仕事初めだしRustのクロスコンパイル環境を整えよう(Windows/MacOS/iOS/iPad/Android)
執筆当時は2025年1月6日(月)ですが、現実を見たくないので有給休暇を取得しています。年末年始でRustクロスコンパイル環境を整備したので、その作業メモです。
TL;DR
- 参考リポジトリを見たほうが早いです
- Rustで書いたドメインロジックをWindows(C#)/MacOS,iOS,iPad(Swift)/Android(Kotlin)で呼び出すための環境整備のメモです
- 言語間の調整というよりもApple SiliconやIntel,ARMなどSoC(System on a Chip)やSiP(System in a Package)の差異を吸収していくイメージです
この記事について
目的
-
RustでドメインロジックをUI/UXはOS純正言語(WinUI3/SwiftUI/kotlin Compose等)を使ってクロスコンパイル環境を実現する - 標準ツール・最小モジュールで連携する
前提条件
- Static Librayによる静的リンクで連携します
- Rust開発をしたことがある、Visual Studio、Xcode、Android Studioを触ったことがある開発者を想定しています
- わかりやすさを優先するため極力標準機能を使って環境設定します。したがって
cbindgenなどのプラグインは使用しません。
開発環境
- Windows11 Professional
- MacBook Pro (Intelチップ)
% sw_vers
ProductName: macOS
ProductVersion: 15.2
BuildVersion: 24C101
% sysctl machdep.cpu.brand_string
machdep.cpu.brand_string: Intel(R) Core(TM) i5-8257U CPU @ 1.40GHz
- Rust
$ rustc --version
rustc 1.81.0 (eeb90cda1 2024-09-04)
サンプルコード
引数を足すだけの非常にシンプルなものとなっています。
lib.rs
#[no_mangle]
pub extern "C" fn add(a: usize, b: usize) -> usize {
a + b
}
プロジェクト全体は下記rustフォルダをご覧ください。
Rustに対する設定
まずはドメインロジックとなるRust環境を整備します。
大きく2つ
- Rustに命令セットに合わせたビルド環境を整える
- ソースコードをマングリングしないように設定し(
no_mangleマクロ等)、FFIを通じて外部から読み込めるようにビルドする
をする目的で環境整備します。
iOS用のクロスコンパイルを説明したMozillaのサイトを追っていきつつ、その他の不足する情報を適宜追加しています。
まずはrustupをインストール
ツール概要の説明は公式ページに任せます。イメージはRubyのrbenvやPythonのpyenvといった、ビルド環境を動的に変更できるツールだと思っていただければと思います。
インストールは簡単で、以下のコマンドを実行、ガイダンスに従ってインストールを完了させてください。
$ curl https://sh.rustup.rs -sSf | sh
各プラットフォームで動作するようビルド環境の整備する
Rustで開発したコードを各環境で動作させるためにビルドツールを整備します
Windows環境
この記事ではMSVC ABIを前提に進めていきます(ABIについて後述)。
以下のコマンドで、msvc用のビルドツールをインストールします。
$ rustup target add i686-pc-windows-msvc x86_64-pc-windows-msvc aarch64-pc-windows-msvc
ところで、Windows環境は2つのABI(Application binary interface)があるため環境構築が少し複雑です。ABIのうち1つはVisual Studioで使用されているnative (MSVC) ABIと、もう1つはGNU ABIです。Rustのデフォルトは前者(MSVC ABI)のため、WindowsでRustを動かすにはVisual Studioのインストールが必要です。
もし、Visual Studioがインストールできない環境であれば
- MSVC v143 - VS 2022 C++ x64/x86 build tools (Latest)
- Windows 11 SDK (10.0.22621.0)
をwinget等でインストールをお願いします。
ビルドツールのインストール結果が以下のようになっていれば、ひとまず成功です。
PS> rustup target list | grep msvc
aarch64-pc-windows-msvc (installed)
arm64ec-pc-windows-msvc
i586-pc-windows-msvc
i686-pc-windows-msvc (installed)
x86_64-pc-windows-msvc (installed)
次に、CPUクロスコンパイルのためにツールチェーンをインストールします。この操作はWindows機以外では失敗するのと、rustup自体のアップデートが走ると自動で実行されるため、念のため...ぐらいの気持ちで実行してください。
PS> rustup toolchain install stable-msvc
以上で、Windows環境でのビルド環境は完了です。
Apple環境
MacOS用
$ rustup target add aarch64-apple-darwin x86_64-apple-darwin
MacBookやMac mini環境で開発している場合は不要です。私のように古いMacBookを使っている場合はIntelチップ環境で動くようにx86_64-apple-darwinもtargetに追加してください
iOS用
$ rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
ざっくりいうとaarch64-apple-iosは、Apple Silicon用のビルド環境、aarch64-apple-ios-simとx86_64-apple-iosはシミュレータ用の環境。イメージとしては、Releaseビルドは前者、Debugビルドは後者の静的リンクを使う感じです。
XCFramework環境を整える
XCFrameworkとは、Xcode11から利用開始となった、バイナリフレームワーク配布のため新たにサポートされた形式で、XCFrameworkはエミュレータやデバイスなどの複数のプラットフォームを同じバンドルに入れられます。lipoを使わずUniversal Compileができます。
使い方はシンプルで
$ xcodebuild -create-xcframework \
-library target/aarch64-apple-darwin/release/[StaticLinker].a \
-header [HeaderFolder]
-library target/aarch64-apple-darwin/release/[StaticLinker].a \
-header [HeaderFolder]
-output build/[OutputFileName].xcframework
といった要領で、xcodebuild -create-xcframeworkに-library、-header、-outputのパラメータを渡してあげるだけです。正常終了するとInfo.plistが含まれたフォルダが生成されます。
Android環境
$ rustup target add aarch64-linux-android x86_64-linux-android
ARMかIntelかの違いですね。
Windows(C#)とRustの連携
サンプル用のWindowsデスクトップアプリを作る
WinUI3で最小構成で作っていきます。Windows Storeに公開する予定はないのでUnpackageのみビルドできる環境でつくっています。
また丁寧に作ればMVVM構成にすべきですが、わかりやすさ優先でコードビハインドでサンプルを作っています。
XCode(Swift)とRustの連携
まずはおまじない
XCcodeのアップデート等によりビルド環境が変わった場合は、以下のコマンドにより環境を整備します。
xcode-select --install
XCodeの操作
大きく3つ実施します。
- 【MacOS用】Fatライブラリを作成する
-
module.modulemapを作成する -
XCFrameworkを作成する
Fatライブラリを作成する
命令セットが異なるライブラリを追加すると大量のWarningが出るため、実行環境のCPUを自動検知するようFatライブラリを作ります。
lipo -create \
-output target/release/apple-darwin-librust_sample_functions.a \
target/aarch64-apple-darwin/release/librust_sample_functions.a \
target/x86_64-apple-darwin/release/librust_sample_functions.a
iOS用はARM SoCで同じなため作成不要です。
※ただし、筆者のようにIntel CPUのMacBookではシミュレータはx86_64で動作するので、x86_64-apple-iosのビルドツールでビルドした静的リンクをロードするようにしてください
module.modulemapを作成する
XCodeでObj-Cフレームワークとして認識してもらうためにmodulemapを作ります。メリットとしてはSwiftでimportできるようになります。
今回はmodule.modulemapの名称で作成し、次項(XCFrameworkを作成)で-headersパラメータで指定するパスに格納します(本記事では target/includesに格納)
module RustSampleFunctions {
header "RustSampleFunctions.hh"
export *
}
XCFrameworkを作成
$ cargo build
$ xcodebuild -create-xcframework \
-library target/release/apple-darwin-librust_sample_functions.a \
-headers target/includes \
-library target/aarch64-apple-ios/release/librust_sample_functions.a \
-headers target/includes \
-library target/aarch64-apple-ios-sim/release/librust_sample_functions.a\
-headers target/includes \
-output build/RustSampleFunctions.xcframework
最終的にRustSampleFunctions.xcframeworkという名称のフォルダができていれば大丈夫です。
実行結果(iOSはシミュレータ)
TextFieldの値をバインディングして足し算の結果を返していれば正常動作です。
Discussion