LoginSignup
45
40

More than 5 years have passed since last update.

dockerによるRustのARMクロスコンパイル環境の構築

Posted at

RasPi用のRustバイナリをクロスコンパイルする環境が欲しかったので
dockerでその環境を構築して誰でも利用できるようにdocker hubに登録した

ビルトと実行について

TL;DR

ビルドコマンド

dockerがインストールされている環境で以下のコマンドを実行する

docker run -it --rm -v ${PWD}:/source yasuyuky/rust-arm cargo build --release --target=arm-unknown-linux-gnueabihf

ビルド結果格納先: target/arm-unknown-linux-gnueabihf/release/

ビルド方法(もう少し長い版)

dockerが実行可能な環境で以下のようにすればARM上で実行可能なバイナリが作成可能。

Rustのプロジェクトディレクトリに移動して

docker run -it --rm -v ${PWD}:/source yasuyuky:rust-arm

するとdockerコンテナ内のbashに入るので以下のように
--target=arm-unknown-linux-gnueabihfをつけて
cargo buildするだけでARM用のバイナリができる。

cargo build --release --target=arm-unknown-linux-gnueabihf

target/arm-unknown-linux-gnueabihf/release/ 以下にバイナリが出来る。

イメージ内に入らずに実行

docker imageの中に入らずに実行する事も可能。

docker run -it --rm -v ${PWD}:/source yasuyuky/rust-arm cargo build --release --target=arm-unknown-linux-gnueabihf

ローカル側のhistoryにも残るのでこちらの方が実用的かもしれない。

rustcを直接使う場合

rustcを直接使う場合は以下のようにlinkerを指定してやる必要がある。

rustc foo.rs --target=arm-unknown-linux-gnueabihf -C linker=arm-linux-gnueabihf-gcc-4.8

確認

$ file target/arm-unknown-linux-gnueabihf/release/foo
target/arm-unknown-linux-gnueabihf/release/foo: ELF 32-bit LSB shared object, ARM, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, not stripped

fileコマンドでARM用のバイナリである事を確認できる。

実行

RasPiはRaspbian OSで使っている方が多いと思われるが、
残念ながら現在('15/7/27)ダウンロードで配布されているRaspbian OSは
debian:wheezyベースとなっており、今回の方法で作ったバイナリでは
libcのバージョンが古いため実行できない

jessieベースにアップグレードしましょう。

構築までの長い道のり

そもそもの動機はMac上でコンパイルしてRasPi上にポンと置いておけば動くバイナリが
構築できる環境が欲しかった。

Goあたりはクロスコンパイルがとても簡単で、
OSX上で直接ARM上で動くlinuxのバイナリがコンパイル出来る。

Rustもcrosscompile用のドキュメントがあるので
goほどではないにしろ何かしらそのようなできるだろうと思ったら予想以上に手強かった。

結局Mac上でクロスプラットフォームのgnu toolchainのインストールがとても大変だという事で
直接Mac OS上でarm-unknown-linux-gnueabihfをターゲットにするのは諦め、
dockerでうごかしたコンテナ上でコンパイルすることにした。

docker images構築

Dockerfileは以下のリポジトリに公開してある
https://github.com/yasuyuky/docker-rust-arm

Dockerfileの工夫

一番最初に作ったイメージでは初めて本格的にdockerを触ったため
素朴にDockerfileを書いてビルドした結果
巨大な4.5GBもあるdocker imageが出来上がってしまった。

具体的には以下のような問題点があった。

  • 今では&&でつなげているところを素朴に全部RUNしていた
  • curlでソース持ってきてるところをgitチェックアウトしていた
  • インストール後のソースのクリーンアップをしていなかった
  • 余分なパッケージが入っていた

その後、上記の部分を改善する事により約900MBまでシュリンクできた。

まだ工夫の余地はあるかもしれないが、全部つなげて1レイヤーに
してしまうみたいなのはdockerの良さも消してしまう部分があるので
バランスが大事だと思う。

内容自体は変わった事はしてないが、.cargo/configをルートに置いて
CargoにARM用のリンカを使うように指示している。

docker build

上記Dockerfileを任意のディレクトリに置いて

docker build .

すればdocker imageが出来上がる。(時間がかかる。)

Successfully built xxxxxxxxxxxx

と言われたら成功。

docker run -it --rm -v${PWD}:/source xxxxxxxxxxxx

builtしたイメージのハッシュを指定してイメージ内に入れる。

各オプションについて簡単に説明すると
-itはインタラクティブに色々するためのオプション。
--rmをつけないとシェルを抜けた後にコンテナプロセスが残ってしまうので、つける事推奨。
-vオプションはローカルのディレクトリをイメージ内にマウントするためのオプション。

Docker hub automated build

Docker hub にはgithubとかbitbucketとかに上げた
Dockerfileから自動的にdocker imageを生成する
automated buildという機能がある。

automated buildを使えばどんなDockerfileが使われているか確認でき、
広く使ってもらってもらうのには有効。

一方で、ビルド時間には2時間という制限があり、
rustのソースからのコンパイルのような時間のかかるビルドは
制限時間にひっかからないように工夫が必要だった。

今回上げたイメージも、当初は2時間制限にひっかかり
できるだけ無駄な処理をしないよう削るなどした。

制限時間はクリアできたが、現状100分以上かかっててギリギリなので今後
rustのコンパイル時間がさらに伸びた場合は対策を考えないといけないかもしれない。

まとめ

Rustのクロスコンパイル環境をdockerで構築した。
当初はワークアラウンドとして考えた策だったが、作ってみると

  • キッティングとしてもdocker(boot2docker)インストールするだけで楽
  • Windows環境などでも普通に使えそう
  • CIするときも同じイメージつかえそう

というメリットがある事に気づいた。

今回初めてdockerを本格的に使ったが、とてもよくできていて
今後Rustに限らず、クロスコンパイル環境の構築が難しいものに
ついてdockerを積極的に採用していってもいいかなと思えた。

45
40
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
45
40