Rustは、その高い安全性とパフォーマンスから、現代のソフトウェア開発において不可欠なプログラミング言語としての地位を確立しました。

特に、一つのソースコードから異なるオペレーティングシステムやアーキテクチャ向けのバイナリを生成するマルチプラットフォーム開発は、Rustが最も得意とする領域の一つです。

開発者は、自身のローカル環境とは異なる実行環境向けに実行ファイルをビルドする「クロスコンパイル」という手法を頻繁に利用します。

しかし、クロスコンパイルを成功させるためには、ターゲットの追加方法やリンカの設定など、いくつかの重要なステップを正しく理解しておく必要があります。

この記事では、Rustにおけるターゲットの追加手順から、クロスコンパイルをスムーズに進めるための具体的な設定方法までを詳しく解説します。

Rustにおけるターゲット追加とクロスコンパイルの基本

Rustでマルチプラットフォーム向けの開発を行う際、まず理解すべきなのが「ターゲットトリプル」という概念です。

ターゲットトリプルは、arch-vendor-os-abi という形式で記述され、どのCPUアーキテクチャやOS向けにビルドするかを指定します。

例えば、x86_64-unknown-linux-gnu は、64ビットのLinux環境を指し、aarch64-apple-darwin はAppleシリコン搭載のMac環境を指します。

Rustの標準ツールチェーンである rustup を使用することで、これらのターゲットを簡単に管理することができます。

クロスコンパイルの最大の利点は、ターゲットとなる実機が手元になくても、開発環境から直接バイナリを出力できる点にあります。

これにより、CI/CDパイプライン上でWindows、Linux、macOS向けのバイナリを一斉にビルドするといった運用が可能になります。

現在の環境と利用可能なターゲットを確認する

ターゲットを追加する前に、まずは現在の自分の環境にどのターゲットがインストールされているかを確認しましょう。

ターミナルを開き、以下のコマンドを実行することで、インストール済みのターゲットと利用可能なターゲットの一覧が表示されます。

Rust
// インストール済みのターゲットと追加可能なターゲットを確認するコマンド
rustup target list

このコマンドを実行すると、以下のような出力が得られます。

実行結果
aarch64-apple-darwin (installed)
x86_64-apple-darwin
x86_64-pc-windows-msvc
wasm32-unknown-unknown
...

出力結果の中で (installed) と表示されているものが、現在ビルドに使用できるターゲットです。

デフォルトでは、あなたが現在使用しているコンピュータの環境に合わせたターゲットがインストールされています。

rustupを使用して新しいターゲットを追加する

新しいプラットフォーム向けのバイナリを作成するには、rustup target add コマンドを使用してターゲットを追加します。

例えば、Linux環境からWindows向けの実行ファイルを生成したい場合は、以下のコマンドを実行します。

Rust
// Windows向けのターゲット(x86_64-pc-windows-msvc)を追加する
rustup target add x86_64-pc-windows-msvc

このコマンドを実行すると、指定したプラットフォーム用の標準ライブラリのプリコンパイル済みバイナリがダウンロードされます。

同様に、WebAssembly向けのビルドを行いたい場合は、以下のターゲットを追加します。

Rust
// WebAssembly向けのターゲットを追加する
rustup target add wasm32-unknown-unknown

ターゲットの追加自体は非常に簡単ですが、これだけでクロスコンパイルが完了するわけではない点に注意が必要です。

Rustコンパイラ(rustc)はコードをコンパイルできますが、最終的なバイナリを生成するための「リンカ」は、ターゲットOSに合わせたものを用意しなければなりません。

クロスコンパイルに必要なリンカの導入

クロスコンパイルにおける最大の難所は、ターゲット環境に適したリンカとC言語のライブラリ(SDK)を準備することです。

Rustはデフォルトでホスト環境のリンカを使用しようとするため、ターゲットが異なるとリンクエラーが発生します。

例えば、Ubuntu(Linux)上でWindows向けのビルドを行う場合、mingw-w64 といったクロスリンカをシステムにインストールする必要があります。

ターゲットOS必要なリンカ・ツール
Windows (GNU)mingw-w64
Linux (MUSL)musl-tools
AndroidAndroid NDK

macOSからLinux向けのバイナリを作りたい場合は、musl-cross などを利用して環境を構築することが一般的です。

リンカが正しく設定されていないと、「linker ‘cc’ not found」といったエラーに直面することになります。

Cargoの設定ファイルでリンカを指定する

システムにリンカをインストールしたら、Cargoに対して「特定のターゲットの時はこのリンカを使う」という指示を出す必要があります。

プロジェクトのルートディレクトリに .cargo/config.toml というファイルを作成し、設定を記述します。

以下は、Linux上でWindows(GNU)向けにクロスコンパイルする場合の設定例です。

TOML
[target.x86_64-pc-windows-gnu]
linker = "x86_64-w64-mingw32-gcc"
ar = "x86_64-w64-mingw32-gcc-ar"

このように設定することで、cargo build --target x86_64-pc-windows-gnu を実行した際に、適切なリンカが呼び出されるようになります。

また、特定のプラットフォームでのみ有効にしたいコンパイルオプションなども、このファイル内で管理することが可能です。

環境変数 CARGO_TARGET_<triple>_LINKER を使用して指定する方法もありますが、プロジェクトごとに管理できる config.toml の利用を推奨します。

特定のターゲットを指定してビルドを実行する

準備が整ったら、実際にターゲットを指定してビルドを行いましょう。

通常のビルドコマンドに --target フラグを付け、追加したターゲットトリプルを指定するだけです。

Rust
// 指定したターゲットでリリースビルドを行う
cargo build --release --target x86_64-unknown-linux-musl

ビルドが成功すると、target/ ディレクトリの中にターゲット名の付いたサブディレクトリが作成されます。

実行結果
target/
└── x86_64-unknown-linux-musl/
    └── release/
        └── my_project (実行ファイル)

生成されたバイナリが正しいプラットフォーム向けであるかは、file コマンドなどで確認できます。

例えば、Linux向けのバイナリであれば、ELF形式の実行ファイルとして認識されるはずです。

cross:Dockerを利用した簡単なクロスコンパイル環境

手動でのリンカ設定が複雑に感じる場合や、依存するCライブラリが多い場合は、cross というツールを使用するのが非常に便利です。

cross は内部でDockerコンテナを使用しており、ターゲットごとに最適化されたビルド環境を自動的に構築してくれます。

まずは、Cargoを介して cross をインストールしましょう。

Rust
// crossツールのインストール
cargo install cross

使い方は cargo コマンドとほぼ同じで、コマンドの先頭を cross に置き換えるだけです。

Rust
// crossを使用してAndroid向けにビルドする
cross build --target aarch64-linux-android

Dockerさえインストールされていれば、リンカやライブラリの依存関係に悩まされることなく、多くのターゲット向けにビルドが可能になります。

特に、組み込みLinuxや古いバージョンのGLIBCをターゲットにする場合、cross は非常に強力な味方となります。

WebAssembly(WASM)へのターゲット追加と活用

Rustが広く使われる大きな理由の一つに、WebAssemblyへの強力なサポートがあります。

前述の通り wasm32-unknown-unknown ターゲットを追加することで、ブラウザ上で動作するバイナリを生成できます。

WASMの場合、通常のOS向けのバイナリとは異なり、リンカの制約が少ないという特徴があります。

しかし、ブラウザのAPIにアクセスしたり、JavaScriptと連携したりするためには wasm-pack というツールを併用するのが一般的です。

Rust
// WebAssemblyプロジェクトのビルド
wasm-pack build --target web

これにより、.wasm ファイルだけでなく、JavaScriptから読み込むためのグルーコード(接着剤となるコード)も自動生成されます。

サーバーサイドだけでなく、クライアントサイドのロジックもRustで記述できることは、モダンなウェブ開発における大きな強みです。

よくあるトラブルと解決策

クロスコンパイルを行っていると、特定のクレート(ライブラリ)のコンパイルでエラーが出ることがあります。

特によくあるのが、OpenSSLpkg-config など、システム側のCライブラリに依存しているケースです。

これらの依存関係を解消するには、ターゲット環境用のライブラリパスを正しく通すか、あるいは純粋なRust実装の代替ライブラリ(例:rustls)に切り替えることを検討してください。

また、動的リンクによるGLIBCのバージョン不一致エラーを避けるために、musl ターゲットを使用して静的リンクを行う手法も有効です。

静的リンクされたバイナリは、ターゲットOS上のライブラリに依存せず動作するため、デプロイが非常に容易になります。

常にターゲットとする環境のアーキテクチャ(x86_64かARMか)と、標準Cライブラリの種類(glibcかmuslか)を意識することが成功の鍵です。

まとめ

Rustにおけるターゲット追加とクロスコンパイルは、マルチプラットフォーム展開を強力にサポートする機能です。

rustup target add によるターゲットの追加から始まり、適切なリンカの設定を行うことで、あらゆる環境向けのバイナリを一つの開発環境から生み出せます。

設定が複雑な場合は cross のようなDockerベースのツールを積極的に活用し、開発の効率化を図りましょう。

一度クロスコンパイルの仕組みを構築してしまえば、Rustの持つポータビリティを最大限に活かした開発が可能になります。

まずは、手近なプラットフォームをターゲットに追加し、ビルドが通る喜びを体験してみてください。