RustのプログラムをWSLでクロスコンパイルして Raspberry Pi Zero 2 で動かすまで
はじめに
こんにちは。aq2rです。
Rustのプログラムを Raspberry Pi Zero 2 で動かしたいなと思ったのですが、動かすまでに苦労したので残しておこうと思います。多少うろ覚えですが方法だけでなく、クロスコンパイルができるまでの道のりも残しておこうと思うので、
さっさと一番簡単な方法教えて!
という方は その後見つけたもっと簡単な方法 に飛んでください。
対象読者
WSL2上で Raspberry Pi Zero 2 向けにRustプログラムをクロスコンパイルしたい人
クロスコンパイルするまでの道のり
私がクロスコンパイルを行いたいと思った理由は、ラズパイ上で DiscordのBot を動かしたいと思ったからなのですが、Zero 2 だと性能があまり高くないので、ラズパイ上でのコンパイルは時間がかかりすぎてしまい難しかったからです。(試したところ1時間以上たっても終わらなかった気がします)
まず試したのが、
ラズパイもWSLもLinuxだからWSL上でコンパイルしてそれを持っていけばいいんじゃない?
ということで、WSL上でコンパイルしてラズパイに持って行ったのですが... もちろん動きません。
アーキテクチャが違う からでした。
わかっている人にとっては当然でしょという感じだと思うのですが、それすら知りませんでした。
(てっきりOSが同じだったら動くものなのかと...^^;)
いろいろ調べたところ、
- aarch64-unknown-linux-gnu
- aarch64-unknown-linux-musl
このどちらかに、クロスコンパイルすればいいらしい。
aarch64-unknown-linux-gnu 向けにビルドしてみる
$ rustup target add aarch64-unknown-linux-gnu
$ cargo build -r --target aarch64-unknown-linux-gnu
--- stderr
error occurred: Failed to find tool. Is `aarch64-linux-gnu-gcc` installed?
warning: build failed, waiting for other jobs to finish...
あれ、エラー...? どうやらaarch64-linux-gnu-gcc
というものが必要らしい?
$ sudo apt install -y gcc-aarch64-linux-gnu
すると、ビルドが進んだ!と思ったら
error: linking with `cc` failed: exit status: 1
|
= note: LC_ALL="C" PATH= ...
とエラーが発生しました。linker
の設定が必要らしいので、
nano ~/.cargo/config.toml
以下を追記しました。
[target.aarch64-unknown-linux-gnu]
linker = "/usr/bin/aarch64-linux-gnu-gcc"
$ cargo build -r --target aarch64-unknown-linux-gnu
...
Finished `release` profile [optimized] target(s) in 1m 07s
ビルドできました!早速ラズパイに持って行って動かそう!と思いラズパイに持って行ったところ、
$ chmod 755 ./my-app
$ ./my-app
./my-app: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.32' not found (required by ./my-app)
./my-app: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.33' not found (required by ./my-app)
./my-app: /lib/aarch64-linux-gnu/libc.so.6: version `GLIBC_2.34' not found (required by ./my-app)
あれ、動かない...
...-gnu ではなく ...-musl にビルドする
どうやらgnuの方だと環境が違うと動かないことがある? らしいので、muslの方でやってみます。
$ rustup target add aarch64-unknown-linux-musl
nano ~/.cargo/config.toml
でconfigを変更します。
[target.aarch64-unknown-linux-musl]
linker = "/usr/bin/aarch64-linux-gnu-gcc"
$ cargo build --target aarch64-unknown-linux-musl
...
error: linking with `/usr/bin/aarch64-linux-gnu-gcc` failed: exit status: 1
|
= note: LC_ALL="C" PATH="
...
Hello Worldのプログラムはコンパイルできたのですが、discordのbotプログラムはコンパイルできませんでした。 muslのlinker? が必要みたいなので、https://musl.cc/ から入手するか、自分でコンパイルします。ここでは自分でビルドする方法を残しておこうと思います。
aarch64-linux-musl-gcc をビルドする
こちらを使用します。
$ git clone https://github.com/richfelker/musl-cross-make.git
$ cd musl-cross-make
nano config.mak
を使用して以下を書きます。
TARGET = aarch64-unknown-linux-musl
OUTPUT = /opt/cross
ビルドします。
$ sudo make install -j $(nproc)
もし failed: Network is unreachable.
このようなエラーが発生する場合は、
~/.ssh/config
に以下を追記します。
Host *
IPQoS lowdelay
ビルドできたらPATHに追加します。
$ echo 'export PATH="/opt/cross/bin:$PATH"' >> ~/.bashrc
$ source ~/.bashrc
aarch64-unknown-linux-musl-gcc --version
を使用してバージョンが返ってきたら完了です!
rustプログラムをコンパイルする
nano ~/.cargo/config.toml
でconfigを変更して...
[target.aarch64-unknown-linux-musl]
linker = "/opt/cross/bin/aarch64-unknown-linux-musl-gcc"
$ cargo build -r --target aarch64-unknown-linux-musl
...
Finished `release` profile [optimized] target(s) in 1m 19s
無事コンパイルが完了してラズパイ上での動作も確認できました!
その後見つけたもっと簡単な方法
こちらを使用した方法です。
実はこの方法は見かけていたのですが、dockerが必要で、なぜかWSL上でdockerが動かず... こちらで解決したので残しておきます。docker をインストール
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$ echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-compose-plugin
sudo なしで実行できるように変更
$ sudo groupadd docker
$ sudo usermod -aG docker $USER
$ newgrp docker
$ sudo update-alternatives --config iptables
ここで 1 の iptables-legacy
を選択します。
動作するかチェック
$ sudo service docker start
$ docker run hello-world
cross のインストールとクロスコンパイル
$ cargo install cross
$ cross build --release --target aarch64-unknown-linux-musl
これで Raspberry Pi Zero 2 向けにクロスコンパイルすることができました!
Discussion