Claude
Skills
Sign in
Back

rust-cross-compile

Included with Lifetime
$97 forever

Cross-compiling Rust crates for mobile (Android, iOS) and other non-host targets. Covers rustup target management, cargo-ndk for Android (NDK toolchain), iOS targets (aarch64-apple-ios, aarch64-apple-ios-sim, x86_64-apple-ios), `cross` for general cross-compile via Docker, lipo for iOS universal binaries, XCFramework packaging, sysroot configuration, dependency cross-compile gotchas (openssl, ring, C deps), and CI matrix patterns. USE WHEN: user mentions "cross-compile rust", "cargo-ndk", "rustup target add", "aarch64-apple-ios", "lipo", "cargo cross", "rust for android", "rust for ios", "Rust XCFramework", "static library mobile" DO NOT USE FOR: Gradle KMP build - use `build-tools/gradle-kmp` DO NOT USE FOR: Pure Rust language - use `languages/rust` DO NOT USE FOR: UniFFI specifics - use `languages/uniffi`

Backend & APIs

What this skill does

# Rust Cross-Compilation for Mobile

> **Deep Knowledge**: Use `mcp__documentation__fetch_docs` with technology: `rust-cross-compile` or `cargo-ndk`.

## Targets Cheat Sheet

| Target | Use case |
|---|---|
| `aarch64-linux-android` | Android arm64 (modern devices) |
| `armv7-linux-androideabi` | Android arm32 (older devices) |
| `i686-linux-android` | Android x86 emulator (32-bit) |
| `x86_64-linux-android` | Android x86_64 emulator |
| `aarch64-apple-ios` | iOS device (iPhone arm64) |
| `aarch64-apple-ios-sim` | iOS Simulator on Apple Silicon Mac |
| `x86_64-apple-ios` | iOS Simulator on Intel Mac (legacy) |
| `aarch64-apple-darwin` | macOS Apple Silicon |
| `x86_64-apple-darwin` | macOS Intel |
| `wasm32-unknown-unknown` | Browser WASM |
| `wasm32-wasip1` | WASM with WASI |
| `aarch64-unknown-linux-gnu` | Linux arm64 (Raspberry Pi 64-bit, ARM servers) |
| `x86_64-unknown-linux-musl` | Linux static-link (Alpine, distroless) |

```bash
rustup target list                              # all
rustup target list --installed
rustup target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios
rustup target add aarch64-linux-android armv7-linux-androideabi x86_64-linux-android
```

## Cargo.toml — Library for FFI

```toml
[package]
name = "bhodl-ffi"
version = "0.1.0"
edition = "2021"

[lib]
crate-type = ["cdylib", "staticlib"]            # both for mobile bundling
name = "bhodl_ffi"

[dependencies]
uniffi = { version = "0.28", features = ["cli"] }
tokio = { version = "1", features = ["rt-multi-thread"] }

[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true                                     # strip symbols for smaller binary
panic = "abort"                                  # smaller; use "unwind" if you catch panics
```

`cdylib` → `.so` (Android), `.dylib` (macOS). `staticlib` → `.a` (iOS, where dynamic libs are restricted).

## Android — `cargo-ndk`

Wraps cargo with the Android NDK toolchain. Replaces older `cargo build --target=...` + manual env var dance.

```bash
# Install
cargo install cargo-ndk

# Set NDK path (or use ANDROID_NDK_HOME env)
export ANDROID_NDK_HOME=$HOME/Android/Sdk/ndk/27.1.12297006

# Build for multiple ABIs
cargo ndk \
    -t arm64-v8a \
    -t armeabi-v7a \
    -t x86_64 \
    -t x86 \
    -o ./jniLibs \                              # output dir
    build --release

# Output structure (matches Android's expected layout):
# jniLibs/
# ├── arm64-v8a/libbhodl_ffi.so
# ├── armeabi-v7a/libbhodl_ffi.so
# ├── x86_64/libbhodl_ffi.so
# └── x86/libbhodl_ffi.so
```

In Gradle (Android module):

```kotlin
android {
    sourceSets["main"].jniLibs.srcDirs("../rust-ffi/jniLibs")
}
```

### NDK Version Pinning

Specify NDK version in `local.properties` or via env:

```properties
# local.properties
ndk.dir=/Users/me/Library/Android/sdk/ndk/27.1.12297006
```

Or in `app/build.gradle.kts`:

```kotlin
android {
    ndkVersion = "27.1.12297006"
}
```

### Android API Level

Set min API level for generated `.so`:

```bash
cargo ndk -t arm64-v8a --platform 26 -o ./jniLibs build --release
# Compatible with Android API 26+ (Android 8.0)
```

Match this to your Android `minSdk`.

## iOS — Native Cargo

iOS uses `staticlib` only (Apple disallows dynamic libs in apps).

```bash
# Device
cargo build --release --target aarch64-apple-ios

# Simulator (Apple Silicon Mac)
cargo build --release --target aarch64-apple-ios-sim

# Simulator (Intel Mac, legacy)
cargo build --release --target x86_64-apple-ios

# Output:
# target/aarch64-apple-ios/release/libbhodl_ffi.a
# target/aarch64-apple-ios-sim/release/libbhodl_ffi.a
# target/x86_64-apple-ios/release/libbhodl_ffi.a
```

### Universal Simulator Library (lipo)

Combine sim arm64 + sim x86_64 into single `.a` for unified simulator support:

```bash
mkdir -p target/universal/release
lipo -create \
    target/aarch64-apple-ios-sim/release/libbhodl_ffi.a \
    target/x86_64-apple-ios/release/libbhodl_ffi.a \
    -output target/universal/release/libbhodl_ffi.a

# Verify
lipo -info target/universal/release/libbhodl_ffi.a
# → arm64 x86_64
```

### XCFramework (Recommended Distribution)

Bundle device + simulator binaries with headers into single artifact:

```bash
mkdir -p Bhodl.xcframework
xcodebuild -create-xcframework \
    -library target/aarch64-apple-ios/release/libbhodl_ffi.a \
        -headers ./bindings/ios/include \
    -library target/universal/release/libbhodl_ffi.a \
        -headers ./bindings/ios/include \
    -output Bhodl.xcframework
```

Drop `Bhodl.xcframework` into Xcode project or vendor via Swift Package Manager.

For UniFFI: see `languages/uniffi/quick-ref/kmp-bindings.md` for the full pipeline.

## `cross` — General-Purpose Cross-Compilation

For Linux/Windows/etc. targets without manual toolchain setup. Uses Docker to provide pre-built sysroots.

```bash
# Install
cargo install cross --git https://github.com/cross-rs/cross

# Build for ARM Linux
cross build --target aarch64-unknown-linux-gnu --release

# Build for musl (static, Alpine-friendly)
cross build --target x86_64-unknown-linux-musl --release

# Build for Windows from Linux
cross build --target x86_64-pc-windows-gnu --release
```

`Cross.toml` for custom config:

```toml
[target.aarch64-unknown-linux-gnu]
image = "ghcr.io/cross-rs/aarch64-unknown-linux-gnu:main"
pre-build = [
    "apt-get update",
    "apt-get install -y libssl-dev:arm64",
]

[build.env]
passthrough = ["CARGO_TERM_COLOR"]
```

`cross` requires Docker/Podman. Doesn't work for Apple targets (those need Xcode toolchain on macOS).

## C Dependency Pitfalls

Crates depending on C libraries (openssl, sqlite, libsodium) often need extra config when cross-compiling.

### OpenSSL

```toml
# Avoid system openssl on iOS / Android — use vendored
[dependencies]
openssl = { version = "0.10", features = ["vendored"] }
```

Or switch to **rustls** (pure Rust):

```toml
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls"] }
```

### SQLCipher (rusqlite)

```toml
[dependencies]
rusqlite = { version = "0.32", features = ["bundled-sqlcipher"] }
```

`bundled-sqlcipher` compiles SQLCipher from source, statically linked — no system dep.

### libsodium

```toml
[dependencies]
dryoc = "0.7"            # pure Rust, no C dependency, easiest to cross-compile
```

If you must use libsodium-sys:

```toml
[dependencies]
libsodium-sys-stable = { version = "1.20", features = ["fetch-latest"] }
```

The `fetch-latest` feature builds libsodium from source (no system dep).

## Build Profiles for Mobile

```toml
[profile.release]
opt-level = "z"          # optimize for size (vs "3" for speed)
lto = true               # link-time optimization
codegen-units = 1        # better optimization, slower build
strip = true             # remove debug symbols
panic = "abort"          # smaller binary; no unwinding stacks

[profile.release-with-debug]
inherits = "release"
debug = true             # for profiling release builds
strip = false
```

For smallest mobile binary:

```toml
[profile.release]
opt-level = "z"
lto = "fat"
codegen-units = 1
strip = "symbols"
panic = "abort"
overflow-checks = false  # don't include checks (default in release)
```

Trade-off: slower compile, smaller binary, less debugging info.

## Linker Configuration

`.cargo/config.toml` (per-project):

```toml
[target.aarch64-linux-android]
linker = "aarch64-linux-android26-clang"        # set by cargo-ndk

[target.aarch64-apple-ios]
rustflags = ["-C", "link-arg=-Wl,-application_extension"]   # for app extensions

[target.x86_64-unknown-linux-musl]
linker = "x86_64-linux-musl-gcc"
```

`cargo-ndk` sets the right linker automatically when building Android targets.

## Build Caching

### sccache (incremental cross-target cache)

```bash
cargo install sccache
export RUSTC_WRAPPER=sccache
```

Speeds up cross-compile when targets share intermediate artifacts.

### Target-specific build dirs

```bash
# Avoid clobbering host-target cache
cargo ndk ... build --target-dir target/android
cargo bu

Related in Backend & APIs