仕事初めだしRustのクロスコンパイル環境を整えよう(Windows/MacOS/iOS/iPad/Android)


:::note
加筆中（2025/1/6現在）
:::

執筆当時は2025年1月6日(月)ですが、現実を見たくないので有給休暇を取得しています。年末年始でRustクロスコンパイル環境を整備したので、その作業メモです。

# TL;DR
- 参考リポジトリを見たほうが早いです

https://github.com/tk88e/RustCrossCompile

- Rustで書いたドメインロジックをWindows(C#)/MacOS,iOS,iPad(Swift)/Android(Kotlin)で呼び出すための環境整備のメモです
- 言語間の調整というよりもApple SiliconやIntel,ARMなどSoC(System on a Chip)やSiP(System in a Package)の差異を吸収していくイメージです


# この記事について
:::note
本番環境等で実装する場合はすべて自己責任でお願いします
:::

## 目的
- `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`
```rust
#[no_mangle]
pub extern "C" fn  add(a: usize, b: usize) -> usize {
    a + b
}
```

プロジェクト全体は下記`rust`フォルダをご覧ください。

https://github.com/tk88e/RustCrossCompile/tree/main/rust


# Rustに対する設定
まずはドメインロジックとなる`Rust`環境を整備します。  
大きく2つ

1. Rustに命令セットに合わせたビルド環境を整える
1. ソースコードをマングリングしないように設定し(`no_mangle`マクロ等)、FFIを通じて外部から読み込めるようにビルドする

をする目的で環境整備します。


iOS用のクロスコンパイルを説明した[Mozillaのサイト](https://mozilla.github.io/firefox-browser-architecture/experiments/2017-09-06-rust-on-ios.html)を追っていきつつ、その他の不足する情報を適宜追加しています。

## まずは`rustup`をインストール
ツール概要の説明は[公式ページ](https://rust-lang.github.io/rustup/concepts/index.html)に任せます。イメージはRubyの`rbenv`やPythonの`pyenv`といった、ビルド環境を動的に変更できるツールだと思っていただければと思います。

インストールは簡単で、以下のコマンドを実行、ガイダンスに従ってインストールを完了させてください。

```bash
$ curl https://sh.rustup.rs -sSf | sh
```


## 各プラットフォームで動作するようビルド環境の整備する
Rustで開発したコードを各環境で動作させるためにビルドツールを整備します

### Windows環境
:::note 
[The rustup book](https://rust-lang.github.io/rustup/installation/windows.html)の要約です。
:::

この記事では**MSVC ABI**を前提に進めていきます（ABIについて後述）。
以下のコマンドで、msvc用のビルドツールをインストールします。

```bash
$ rustup target add i686-pc-windows-msvc x86_64-pc-windows-msvc aarch64-pc-windows-msvc
```
ところで、Windows環境は2つのABI(Application binary interface)があるため環境構築が少し複雑です。ABIのうち１つは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`等でインストールをお願いします。
ビルドツールのインストール結果が以下のようになっていれば、ひとまず成功です。

```powershell
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`自体のアップデートが走ると自動で実行されるため、念のため...ぐらいの気持ちで実行してください。

```bash
PS> rustup toolchain install stable-msvc
```

以上で、Windows環境でのビルド環境は完了です。

### Apple環境

#### MacOS用

```bash
$ rustup target add aarch64-apple-darwin x86_64-apple-darwin
```
MacBookやMac mini環境で開発している場合は不要です。私のように古いMacBookを使っている場合はIntelチップ環境で動くように`x86_64-apple-darwin`もtargetに追加してください

#### iOS用
```bash
$ 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ができます。
使い方はシンプルで
```bash
$ 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環境
```bash
$ rustup target add aarch64-linux-android x86_64-linux-android
```
ARMかIntelかの違いですね。


# Windows(C#)とRustの連携

:::note alert
Under Construction
:::

## サンプル用のWindowsデスクトップアプリを作る

WinUI3で最小構成で作っていきます。Windows Storeに公開する予定はないので`Unpackage`のみビルドできる環境でつくっています。
また丁寧に作れば`MVVM構成`にすべきですが、わかりやすさ優先でコードビハインドでサンプルを作っています。

# XCode(Swift)とRustの連携
:::note
Rustビルド環境整備とXcodeの環境整備は割愛します。
:::
## まずはおまじない

XCcodeのアップデート等によりビルド環境が変わった場合は、以下のコマンドにより環境を整備します。
```
xcode-select --install
```

## XCodeの操作

大きく3つ実施します。
1. 【MacOS用】Fatライブラリを作成する
1. `module.modulemap`を作成する
1. `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`に格納）

```plaintext module.modulemap
module RustSampleFunctions {
    header "RustSampleFunctions.hh"
    export *
}
```



### `XCFramework`を作成

```bash
$ 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の値をバインディングして足し算の結果を返していれば正常動作です。


# Android Studio(Kotlin)とRustの連携
:::note alert
Under Construction
:::