【Rust】別ターゲット用にプログラムをクロスコンパイルする (original) (raw)

ここ最近、毎日Rustを使ってRaspberry Pi用プログラムを書いている。センサー・デバイスから取得した計測データをSQLiteデータベースに保存して、同時にWebSocket経由でサーバーへ転送するようなものだ。ターゲットはRaspberry Piだが、Rustプログラムの開発ホスト機としてMacを使っている。

前記事にRustのインストールと開発環境の構築手順を書いたが、

blog.ketus-ix.work

この記事の中で、下のようなRustコンポーネントの一覧を取得するコマンドを紹介した。

% rustup component list cargo-x86_64-apple-darwin (installed) clippy-x86_64-apple-darwin (installed) llvm-bitcode-linker-x86_64-apple-darwin llvm-tools-x86_64-apple-darwin rls-x86_64-apple-darwin rust-analysis-x86_64-apple-darwin rust-analyzer-x86_64-apple-darwin rust-docs-x86_64-apple-darwin (installed) rust-src rust-std-aarch64-apple-darwin rust-std-aarch64-apple-ios rust-std-aarch64-apple-ios-sim rust-std-aarch64-linux-android rust-std-aarch64-pc-windows-gnullvm rust-std-aarch64-pc-windows-msvc rust-std-aarch64-unknown-fuchsia rust-std-aarch64-unknown-linux-gnu rust-std-aarch64-unknown-linux-musl rust-std-aarch64-unknown-linux-ohos rust-std-aarch64-unknown-none rust-std-aarch64-unknown-none-softfloat rust-std-aarch64-unknown-uefi rust-std-arm-linux-androideabi rust-std-arm-unknown-linux-gnueabi rust-std-arm-unknown-linux-gnueabihf rust-std-arm-unknown-linux-musleabi rust-std-arm-unknown-linux-musleabihf rust-std-armebv7r-none-eabi rust-std-armebv7r-none-eabihf rust-std-armv5te-unknown-linux-gnueabi rust-std-armv5te-unknown-linux-musleabi rust-std-armv7-linux-androideabi rust-std-armv7-unknown-linux-gnueabi rust-std-armv7-unknown-linux-gnueabihf rust-std-armv7-unknown-linux-musleabi rust-std-armv7-unknown-linux-musleabihf rust-std-armv7-unknown-linux-ohos rust-std-armv7a-none-eabi rust-std-armv7r-none-eabi rust-std-armv7r-none-eabihf rust-std-i586-pc-windows-msvc rust-std-i586-unknown-linux-gnu rust-std-i586-unknown-linux-musl rust-std-i686-linux-android rust-std-i686-pc-windows-gnu rust-std-i686-pc-windows-gnullvm rust-std-i686-pc-windows-msvc rust-std-i686-unknown-freebsd rust-std-i686-unknown-linux-gnu rust-std-i686-unknown-linux-musl rust-std-i686-unknown-uefi rust-std-loongarch64-unknown-linux-gnu rust-std-loongarch64-unknown-none rust-std-loongarch64-unknown-none-softfloat rust-std-nvptx64-nvidia-cuda rust-std-powerpc-unknown-linux-gnu rust-std-powerpc64-unknown-linux-gnu rust-std-powerpc64le-unknown-linux-gnu rust-std-riscv32i-unknown-none-elf rust-std-riscv32im-unknown-none-elf rust-std-riscv32imac-unknown-none-elf rust-std-riscv32imafc-unknown-none-elf rust-std-riscv32imc-unknown-none-elf rust-std-riscv64gc-unknown-linux-gnu rust-std-riscv64gc-unknown-none-elf rust-std-riscv64imac-unknown-none-elf rust-std-s390x-unknown-linux-gnu rust-std-sparc64-unknown-linux-gnu rust-std-sparcv9-sun-solaris rust-std-thumbv6m-none-eabi rust-std-thumbv7em-none-eabi rust-std-thumbv7em-none-eabihf rust-std-thumbv7m-none-eabi rust-std-thumbv7neon-linux-androideabi rust-std-thumbv7neon-unknown-linux-gnueabihf rust-std-thumbv8m.base-none-eabi rust-std-thumbv8m.main-none-eabi rust-std-thumbv8m.main-none-eabihf rust-std-wasm32-unknown-emscripten rust-std-wasm32-unknown-unknown rust-std-wasm32-wasi rust-std-wasm32-wasip1 rust-std-wasm32-wasip1-threads rust-std-x86_64-apple-darwin (installed) rust-std-x86_64-apple-ios rust-std-x86_64-fortanix-unknown-sgx rust-std-x86_64-linux-android rust-std-x86_64-pc-solaris rust-std-x86_64-pc-windows-gnu rust-std-x86_64-pc-windows-gnullvm rust-std-x86_64-pc-windows-msvc rust-std-x86_64-unknown-freebsd rust-std-x86_64-unknown-fuchsia rust-std-x86_64-unknown-illumos rust-std-x86_64-unknown-linux-gnu rust-std-x86_64-unknown-linux-gnux32 rust-std-x86_64-unknown-linux-musl rust-std-x86_64-unknown-linux-ohos rust-std-x86_64-unknown-netbsd rust-std-x86_64-unknown-none rust-std-x86_64-unknown-redox rust-std-x86_64-unknown-uefi rustc-x86_64-apple-darwin (installed) rustc-dev-aarch64-apple-darwin rustc-dev-aarch64-pc-windows-msvc rustc-dev-aarch64-unknown-linux-gnu rustc-dev-aarch64-unknown-linux-musl rustc-dev-arm-unknown-linux-gnueabi rustc-dev-arm-unknown-linux-gnueabihf rustc-dev-armv7-unknown-linux-gnueabihf rustc-dev-i686-pc-windows-gnu rustc-dev-i686-pc-windows-msvc rustc-dev-i686-unknown-linux-gnu rustc-dev-loongarch64-unknown-linux-gnu rustc-dev-powerpc-unknown-linux-gnu rustc-dev-powerpc64-unknown-linux-gnu rustc-dev-powerpc64le-unknown-linux-gnu rustc-dev-riscv64gc-unknown-linux-gnu rustc-dev-s390x-unknown-linux-gnu rustc-dev-x86_64-apple-darwin rustc-dev-x86_64-pc-windows-gnu rustc-dev-x86_64-pc-windows-msvc rustc-dev-x86_64-unknown-freebsd rustc-dev-x86_64-unknown-illumos rustc-dev-x86_64-unknown-linux-gnu rustc-dev-x86_64-unknown-linux-musl rustc-dev-x86_64-unknown-netbsd rustc-docs-x86_64-unknown-linux-gnu rustfmt-x86_64-apple-darwin (installed)

この出力情報から、コマンド "rustup" によって複数ターゲット用のクロスコンパイラをインストールできることが判る。

Rustでは公式サイトによってクロスコンパイラが配布されており、別ターゲットを対象としたクロスプラットフォーム開発環境が簡単に構築できるようになっている。これはRustの大きな利点であり、最近のプロジェクト(特に組み込み系)でRustが採用されるケースが増える要因となっている。

Rustを使うと、クロスコンパイル環境さえ構築すれば、Mac, WindowsLinux,組み込みLinux搭載機、Androidモバイル機、さらにOS無しマイコン環境さえも一つのプロジェクトで対応できるようになる。

本記事では、Rustによるクロスコンパイル環境の構築方法を紹介する。

ターゲット機としてRaspberry Pi OS 64-bit(Raspberry Pi 4用)を想定しているが、コンパイラやリンカの設定方法が変わるだけで、Linux搭載機がターゲットなら同じ手順でクロスコンパイル環境を構築できる。

cross パッケージによるクロスコンパイル

cross パッケージのインストール

% cargo install cross

プロジェクトのクロスビルド

% cd PROJECT_DIR % cross build --target aarch64-unknown-linux-gnu

ビルドによって生成された実行可能ファイルはプロジェクト・ディレクリ内の以下のパスに置かれる。

target/aarch64-unknown-linux-gnu/debug/<project_name>

target/aarch64-unknown-linux-gnu/release/<project_name>

コマンド "cross build" はDockerコンテナを利用してプログラムのクロスコンパイルを行っており、Dockerがインストール済みで、"docker" コマンドが非root権限ユーザーによって実行できる状態であることが本コマンドを使える前提条件となる。

プロジェクト・プログラムの実行

cargo環境に cross パッケージを導入済みの場合、別ターゲット用にビルドしたプログラムを開発ホスト機上で実行することができる。

% cross run --target aarch64-unknown-linux-gnu

このコマンドはQEMUを使ってプログラムをエミュレート実行している。

ターゲット用リンカを使うクロスコンパイル

ターゲット用リンカのインストール

% brew tap messense/macos-cross-toolchains % brew install aarch64-unknown-linux-gnu

$ sudo apt install gcc-aarch64-linux-gnu

ターゲット用ツールチェインのインストール

% rustup target add aarch64-unknown-linux-gnu

ターゲット用ビルド設定の作成

プロジェクト・ディレクトリ内に以下のような内容の.cargo/config.tomlというファイルを作成する。

% cd PROJECT_DIR % mkdir .cargo % vi .cargo/config.toml [target.aarch64-unknown-linux-gnu] linker = "aarch64-linux-gnu-gcc"

プロジェクトのクロスビルド

上記の手順操作が済んでいる状態で、プロジェクト・ディレクトリ内で下のようなコマンドを実行すると、当該プログラムを指定ターゲット用にビルドすることができる。

% cargo build --target aarch64-unknown-linux-gnu

zigbuild パッケージによるクロスコンパイル

Zigというプログラミング言語が在るが、このZigにLLVMベース・ツールチェインが統合されたZig ccというものがある。

zig.guide

このZig ccをcargo環境で使えるようにした cargo-zigbuild というRustパッケージが存在する。

github.com

この cargo-zigbuild パッケージを利用すると、Rustのクロスコンパイル環境を構築できる。

zigbuild パッケージのインスール

% brew install zig % cargo install cargo-zigbuild

$ snap install zig --classic --beta $ cargo install cargo-zigbuild

プロジェクトのクロスビルド

% cd PROJECT_DIR % cargo zigbuild --target aarch64-unknown-linux-gnu

【補足説明】

クロスビルドによって生成したRustプログラムの実行可能ファイルをターゲット機へ転送するには、SSH経由で行うのが良く利用される方法だろう。

scpを使う方法

% scp target/aarch64-unknown-linux-gnu/debug/hello_rust @://tmp/

rsyncを使う方法

% rsync -av -e ssh target/aarch64-unknown-linux-gnu/debug/hello_rust @://tmp/

転送したプログラムの動作確認は、ターゲット機のコンソールから直接実行するか、あるいは、以下のようにSSH経由でターゲット機にログインした上で転送済みの実行可能ファイルを起動すれば良い。

% ssh @ % cd /tmp % ./hello_rust Hello, world!

【参照リンク】

qiita.com

qiita.com

zenn.dev