以前Mac用のクロスコンパイラ環境を紹介しました。
しかし、Windows環境でもクロスコンパイルしたい場合があるので、Windows上にもクロスコンパイラ環境を作りました。そのときのクロスコンパイラ環境の構築手順をまとめたので参考になれば幸いです。
ただ、Windowsといっても実際はWSL上のUbuntu20.04で構築するのですが。
前提条件
構築手順に入る前に、まず下記の前提条件を満たしている必要があります。
- ラズパイとWindowsが通信できる(USBやWi-Fi経由で)
- WindowsにWSLがインストール済み
- WSL上にUbuntuがインストール済み
条件を満たしている方は以降の構築手順に進んでください。
WSLのインストール、WSL上へのUbuntuインストールが済んでいない方は、下記を参考にWSLのインストールをしてください。
ラズパイ用C/C++のクロスコンパイラ環境構築手順
クロスコンパイラ環境の構築手順を解説します。手順は大きく3つに分類されます。
- aptで環境構築に必要なツールのインストール
- ラズパイからPCへライブラリ等をコピー
- コンパイル用スクリプト作成
手順1:aptで環境構築に必要なツールのインストール
WSL(Ubuntu)のターミナルを開いて下記コマンドを実行します。
sudo apt install binutils-arm-linux-gnueabihf clang
このコマンドによって以下の2つのツールがインストールされます。
- binutils-arm-linux-gnueabihf:ARM用プログラミングツール
- clang:コンパイラ基板ツール
また、以降の手順で"rsync"というコマンドを使うのですが、Ubuntuであればプリインストールされているはずです。しかし、何らかの理由で"rsync"コマンドがない場合は、下記コマンドで"rsync"コマンドもインストールしてください。
sudo apt install rsync
手順2:ラズパイからPCへライブラリ等をコピー
次にラズパイからコンパイル時に必要なライブラリ等を取得するのですが、まずはその取得先とするディレクトリを作成します。(実行するディレクトリは任意ですが、ユーザーホームが分かりやすいかと)
mkdir raspbian
続けてWSL(Ubuntu)のターミナル上で下記コマンドを実行します。
rsync -rzLRP --safe-links --append \
pi@raspberrypi.local:/usr/lib/arm-linux-gnueabihf \
pi@raspberrypi.local:/usr/lib/gcc/arm-linux-gnueabihf \
pi@raspberrypi.local:/usr/include \
pi@raspberrypi.local:/lib/arm-linux-gnueabihf \
raspbian/sysroot
このコマンドによってラズパイからライブラリ等をPCへコピーします。それなりに時間がかかるので気長に待ちましょう。
手順3:コンパイル用スクリプト作成
手順2まででコンパイルできる状態になりましたが、コンパイルのコマンドが長いのでスクリプト化しておきます。
まず、WSL(Ubuntu)のターミナル上で下記コマンドを実行して、スクリプトを書き込むファイルを開きます。(viで開いていますが、お好みのエディタで開いて頂いても良いです)
mkdir -p raspbian/prebuilt/bin
vi raspbian/prebuilt/bin/arm-linux-gnueabihf-clang
"arm-linux-gnueabihf-clang"が開かれたら、下記をコピペして保存し、閉じてください。
#!/bin/bash
BASE=$(dirname $0)
SYSROOT="${BASE}/../../sysroot"
TARGET=arm-linux-gnueabihf
ARCH=armv6
COMPILER_PATH="${SYSROOT}/usr/lib/gcc/${TARGET}/8"
exec env COMPILER_PATH="${COMPILER_PATH}" \
"clang" --target=${TARGET} \
-march="${ARCH}" \
--sysroot="${SYSROOT}" \
-L"${COMPILER_PATH}" \
"$@"
5行目の"ARCH=armv6"の部分は『ラズパイZero』の場合の値です。もし他のラズパイを使うのであれば、そのラズパイのARMに合わせて値を変えてください。たとえば『ラズパイ4』用にコンパイルを行う場合は"ARCH=armv8"に変えます。
あとは作成したスクリプトファイルを実行可能にするために下記コマンドを実行します。
chmod +x raspbian/prebuilt/bin/arm-linux-gnueabihf-clang
以上でクロスコンパイラ環境の構築は完了です。
ついでに"arm-linux-gnueabihf-clang"にパスを通しておくと楽なので、ユーザーホームにある".bashrc"に下記を追加します。(もしユーザーホーム以外の場所で環境構築した場合はパスを変更してください)
export PATH=~/raspbian/prebuilt/bin:$PATH
追加したら忘れずに下記コマンドを実行してPATHを反映させましょう。
source ~/.bashrc
コンパイル方法と動作確認
構築したクロスコンパイラで実際にコンパイルしてみましょう。とりあえずお馴染みのHelloWorldを用意します。
#include <stdio.h>
int main(void)
{
printf("Hello World!\n");
return 0;
}
"helloworld.c"の用意ができたらWSL(Ubuntu)上のターミナルで下記コマンドを実行してコンパイルします。
arm-linux-gnueabihf-clang helloworld.c
コンパイルが成功すると"a.out"ファイルが生成されます。
次に生成された"a.out"をラズパイに転送します。WSL(Ubuntu)上のターミナルで下記コマンドを実行するとラズパイのホームディレクトリ直下に転送されます。
scp a.out pi@raspberrypi.local:~/
あとはラズパイにSSH接続してラズパイのターミナルを開き、ホームディレクトリで"a.out"を実行すると"Hello World!"が出力されるはずです。
pi@raspberrypi:~ $ ./a.out
Hello World!
まとめ
Windowsでも簡単にクロスコンパイラ環境が構築できました。サクサクっとコンパイルしてラズパイ上でバシバシ動かしてやりましょう!
コメント
コメント一覧 (8件)
ラズパイ勉強中さん、コメントありがとうございます。
これは入力する文字列になります。
もっといえば、ライブラリの取得先であるラズパイのホスト名となります。
もし、ラズパイのホスト名が不明である場合、ラズパイのIPアドレスでもOKだと思います。
お忙しいところ、回答をありがとうございます。
Linuxシェルのscpコマンドやrsyncコマンドなどにおけるリモートのファイルの指定形式だったのですね。
初歩的な質問ですみませんでした。
開発環境構築の流れについて少し頭が整理できました。
・コンパイラ自体は開発環境で動作する実行ファイルである。
ラズパイ上にあるコンパイラは、Windows上のWSLに構築する開発環境にコピーしても動かないので、ラズパイからコピーせず、
開発環境で動くように手順1の方法でインストールする。
・コンパイルのターゲットは、リモートであるラズパイ。
・ライブラリはすでにターゲットの実行形式にコンパイル済みなので、
なんら中身を弄ることなくコピーして使える。したがって手順2でリモートからローカルへコピー。
今のところこのような感じかなと考えています。
手順1の
sudo apt install binutils-arm-linux-gnueabihf clang
ではインストールに失敗したので、
WSL2にraspberry piのクロスコンパイル環境をインストールするでググって
llvm.shを利用してインストールしてみたのですが、それでも
~/raspbian/sysroot/usr/lib/gcc/arm-linux-gnueabihf/8
の中身が空っぽでした。
~/raspbian/sysroot/usr/lib/gcc/arm-linux-gnueabihf/10
には色々ファイルが入っているので、
手順3のarm-linux-gnueabihf-clangの上記箇所8を10に変更しました。
それでもclangが探すライブラリの場所が違っているのか、コンパイル時にライブラリが見つからないエラーが出るので、
cd ~/raspbian/sysroot/lib/arm-linux-gnueabihf
cp -r * ../
のような感じで、別のディレクトリにあるライブラリをまるごと親ディレクトリにコピーしました。
一部移動で良いのでしょうが、どれを移動すれば良いか分からなかったので。
結果、ラズパイでHello World!は出来ましたが、他のソースもコンパイル成功するか分かりません。
たぶん不具合出そう。
とにかく分からな過ぎて、勉強することが凄く多いです。
ラズパイからコピーしたライブラリはたぶんgcc用だと思うのですが、clangでそのまま使えるのかとか。
ライブラリのディレクトリ構造を確認しなければならないのかとか、
ターゲットのコンパイルバージョン?が8から10に変わったのかどうか。また、10にしたらどうすれば良いのかとか。
あと英語も勉強しなくてはかなり厳しい。
ラズパイ勉強中さん、コメントありがとうございます。
それぞれについて回答していきますね。
ラズパイ勉強中さんが仰る通り、コンパイラ自体は実行環境で動作する実行ファイルになります。今回であれば、Windows上で動作するため、ほぼx86用に生成されたコンパイラが必要になります。
このコンパイラがクロスコンパイラであり、x86上で動作しながらARM用のバイナリを吐き出すことができます。なお、ラズパイ上で動作しているコンパイラは、ARM用に生成されたコンパイラであり、基本的にARM用のバイナリを吐き出すことができます。
ラズパイ勉強中さんが仰る通りです。
ラズパイ勉強中さんが仰る通りです。
Raspbianのバージョンアップにより、取り込んでいるarm-linux-gnueabihfのバージョンが8から10に変わったようですね。
有益な情報をありがとうございます。
こちらはちょっと不明ですね…
ライブラリが見つからないエラーについて、少し調べてみましたが詳細は不明でした。ライブラリが更新されたことによりパス探索が変わったのかもしれません。
以下のコマンドのように、sysroot配下のlibディレクトリ下に、ld-linux-armhf.so.3のシンボリックリンクを作ることで、ライブラリが見つからないエラーは対処できると思います。ご参考までに
gcc, clang間のバイナリ互換性についてですが、以下のTopicが参考になります。
https://stackoverflow.com/questions/3217513/are-llvm-gcc-and-clang-binary-compatible-with-gcc-particularly-mingw-gcc-on-w
こちらでは、C言語であればgcc, clang間で互換性が保たれていることが回答されています。なお、C++については本来は互換性が保たれない可能性があるが、試したところ使えたそうです。
すみません。手順2のrsyncコマンドの引数ですが、
pi@raspberrypi.local:という文字列はプロンプトでしょうか、入力する文字列でしょうか。