Use when the user cross-compiles to another target triple, uses `rustup target add`, uses the `cross` crate, configures per-target linker via `.cargo/config.toml`, or guards code with `cfg(target_os = "...")`. Prevents missing target triple installation, picking wrong target for the use case (musl vs gnu, msvc vs gnu on Windows), and using `cfg` without `cfg_attr` for selective attributes. Covers: target triple format (arch-vendor-os-env), common triples (x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, aarch64-apple-darwin, x86_64-pc-windows-msvc, x86_64-pc-windows-gnu, wasm32-unknown-unknown, wasm32-wasi), `rustup target add`, `cargo build --target <triple>`, `cross` crate (container-based), conditional compilation `cfg(target_os = "...", target_arch = "...", target_pointer_width = "...", target_feature = "...")`, `cfg_attr`, `.cargo/config.toml` for per-target linker / runner. Keywords: "cross-compile", "cross compile", "target triple", "rustup target add", "cargo build --target", "cross crate", "musl
Installer avec Codex ou Claude Copiez ce prompt, collez-le dans Codex, Claude ou un autre assistant, puis laissez-le vérifier la page du skill et l'installer pour vous.
Une commande directe contourne le prompt de vérification. Examinez la source avant de l'exécuter.
Use when the user cross-compiles to another target triple, uses `rustup target add`, uses the `cross` crate, configures per-target linker via `.cargo/config.toml`, or guards code with `cfg(target_os = "...")`. Prevents missing target triple installation, picking wrong target for the use case (musl vs gnu, msvc vs gnu on Windows), and using `cfg` without `cfg_attr` for selective attributes. Covers: target triple format (arch-vendor-os-env), common triples (x86_64-unknown-linux-gnu, x86_64-unknown-linux-musl, aarch64-apple-darwin, x86_64-pc-windows-msvc, x86_64-pc-windows-gnu, wasm32-unknown-unknown, wasm32-wasi), `rustup target add`, `cargo build --target <triple>`, `cross` crate (container-based), conditional compilation `cfg(target_os = "...", target_arch = "...", target_pointer_width = "...", target_feature = "...")`, `cfg_attr`, `.cargo/config.toml` for per-target linker / runner. Keywords: "cross-compile", "cross compile", "target triple", "rustup target add", "cargo build --target", "cross crate", "musl static", "musl gnu", "msvc gnu", wasm32, "wasm32-unknown-unknown", "wasm32-wasi", "cfg target_os", "cfg target_arch", "target_pointer_width", "target_feature", cfg_attr, ".cargo/config.toml", linker, runner, "static linking".
license
MIT
compatibility
Designed for Claude Code. Requires Rust 1.85+, edition 2024.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
rust-impl-cross-compile
The cross-compilation skill: how to build a Rust crate for a target other than the host. Covers target triple format and common triples, rustup target add to install standard library artifacts, cargo build --target <triple>, the cross crate for container-based toolchains, conditional compilation via cfg(target_os = ...) and related predicates, the cfg_attr attribute for selective attribute emission, and per-target linker / runner configuration via .cargo/config.toml.
Cross-references: [[rust-impl-cargo-project]] (single-crate Cargo.toml + profiles), [[rust-impl-build-scripts]] (build.rs that detects target via env vars), [[rust-impl-ffi-bindgen]] (cross-compile C deps and adjust sysroot), [[rust-core-toolchain]] (rustup channels, components, target lists).
When to use this skill
User runs cargo build and gets linker 'cc' not found or error: target may not be installed after switching --target.
User wants a portable static Linux binary and asks musl vs gnu.
User builds for Windows from Linux and asks pc-windows-msvc vs pc-windows-gnu.
User builds for macOS ARM (Apple Silicon) from x86_64 host.
User asks how to build a .wasm module and asks wasm32-unknown-unknown vs wasm32-wasip1.
User writes platform-conditional code with #[cfg(target_os = "...")] or cfg!(...).
User wants a single attribute (e.g. #[repr(C)], #[link(...)]) to appear only on one target.
User configures a custom linker for a target via [target.<triple>] linker = "..." in .cargo/config.toml.
User asks about the cross tool because installing native toolchains is tedious.
For host-only profile / lints / dependency tuning see [[rust-impl-cargo-project]]. For build-time target detection in build.rs see [[rust-impl-build-scripts]].
Target triple format
Rust target triples follow the shape <arch>-<vendor>-<os>-<env> where each field is a fixed identifier (see platform-support):
NOTE: wasm32-wasi was renamed to wasm32-wasip1 in Rust 1.81 (rust181). wasm32-wasi still works as a legacy alias but new code should use wasm32-wasip1 (or wasm32-wasip2 for WASIp2).
Common target triples
Decision rules per use case. Tier classification is from platform-support: Tier 1 has CI guarantees + host tools, Tier 2 with-host-tools has guaranteed std + tools, Tier 2 without-host-tools has guaranteed std only, Tier 3 is best-effort.
Target Triple
Tier
When to use it
x86_64-unknown-linux-gnu
1
Default 64-bit Linux. Dynamic linking against glibc 2.17+.
aarch64-unknown-linux-gnu
1
ARM64 Linux servers (AWS Graviton, RPi 64-bit, etc.).
x86_64-unknown-linux-musl
2-host
Fully static Linux binary, no glibc dependency.
aarch64-unknown-linux-musl
2-host
Static ARM64 Linux binary.
aarch64-apple-darwin
1
macOS on Apple Silicon (M1+).
x86_64-apple-darwin
1
macOS on Intel.
x86_64-pc-windows-msvc
1
Windows native, MSVC toolchain (needs Visual Studio Build Tools).
x86_64-pc-windows-gnu
1
Windows via MinGW-w64 (cross-compile-friendly from Linux).
aarch64-pc-windows-msvc
1
Windows on ARM64.
wasm32-unknown-unknown
2
Browser WebAssembly. No OS. Requires wasm-bindgen for browser glue.
wasm32-wasip1
2
WebAssembly with WASI Preview 1 (filesystem, env, clock).
Need a portable static binary that runs on any Linux distro (no glibc dep)?
YES -> *-unknown-linux-musl
NO -> *-unknown-linux-gnu (default, dynamic glibc, smaller binary, glibc 2.17+)
Will the binary load shared libraries with dlopen at runtime?
YES -> *-gnu (musl has known dlopen quirks)
NO -> either is fine, choose by static vs dynamic preference
Is the binary going into a `FROM scratch` Docker image?
YES -> *-musl is the only sane choice
Windows: msvc vs gnu
Compiling on Windows with Visual Studio Build Tools installed?
YES -> *-pc-windows-msvc (recommended, matches Microsoft ecosystem)
Cross-compiling from Linux / macOS to Windows?
*-pc-windows-gnu is easier (just install mingw-w64 cross gcc).
*-pc-windows-msvc also works via `cargo-xwin` or LLVM tooling but heavier setup.
Linking against a precompiled `.lib` from a vendor?
Match the vendor's ABI. MSVC libs require msvc; MinGW libs require gnu.
WebAssembly: unknown vs wasi
Browser target with JS interop? -> wasm32-unknown-unknown + wasm-bindgen.
CLI / serverless WASI runtime (wasmtime, wasmer)? -> wasm32-wasip1 (or wasip2 for component model).
Pure compute, no I/O, no DOM? -> wasm32-unknown-unknown (smaller, no OS shims).
Cross-compile workflow
Step 1 : install the target standard library
rustup target add x86_64-unknown-linux-musl
rustup target add installs ONLY the precompiled standard library for the target (rustup-cross). It does NOT install a linker, libc, or any platform SDK. The host toolchain (compiler) is reused; only libstd / libcore / liballoc are added.
List available targets : rustup target list. List installed : rustup target list --installed. Remove : rustup target remove <triple>.
Step 2 : ensure a target-compatible linker exists
Target
Linker needed on Linux host
x86_64-unknown-linux-musl
musl-gcc (Alpine native) or via cross
aarch64-unknown-linux-gnu
gcc-aarch64-linux-gnu (Debian/Ubuntu)
aarch64-unknown-linux-musl
musl-cross toolchain or cross
x86_64-pc-windows-gnu
gcc-mingw-w64-x86-64
x86_64-pc-windows-msvc
MSVC SDK + cargo-xwin, or cross
aarch64-apple-darwin
macOS SDK (must build on macOS host or use osxcross)
wasm32-unknown-unknown
none required, LLVM ships its own.
wasm32-wasip1
none required.
NEVER expect rustup target add to install gcc / clang / lld. ALWAYS install the host-side cross toolchain separately, or delegate to cross.
Step 3 : point Cargo at the linker via .cargo/config.toml
# .cargo/config.toml (project-local or in $CARGO_HOME)[target.aarch64-unknown-linux-gnu]linker = "aarch64-linux-gnu-gcc"[target.x86_64-unknown-linux-musl]linker = "musl-gcc"[target.x86_64-pc-windows-gnu]linker = "x86_64-w64-mingw32-gcc"
Now bare cargo build cross-builds without --target. NEVER do this in a library crate (forces all downstream users to install that target).
The cross tool (container-based)
cross (cross-rs) is a community wrapper that runs cargo inside a per-target Docker / Podman image. Each image bundles the cross linker + libc + sysroot, so the host needs only Docker.
Host toolchain setup is awkward (musl-cross on Debian, mingw-w64 on macOS, etc.).
You build many targets in CI and want hermetic, reproducible toolchains.
You want Windows / Android / BSD targets without installing each SDK.
NEVER use cross for aarch64-apple-darwin from a non-macOS host : Apple's SDK license forbids redistribution, so no Docker image exists. ALWAYS build Darwin targets on macOS hardware or via macOS CI runners.
cross reads Cross.toml for per-target image overrides, mounted volumes, and pre-build hooks. See [[references/methods.md]] for syntax.
Conditional compilation with cfg
cfg predicates select code that is compiled only when the predicate holds (reference-cfg). All cfg keys are evaluated at compile time against the current target.
Use all(...), any(...), not(...) for boolean composition. NEVER nest cfg attributes (#[cfg(a)] #[cfg(b)]) when you mean all ; while it works, cfg(all(a, b)) makes intent explicit.
Runtime cfg!(...) macro
ifcfg!(target_os = "linux") {
// executed only on Linux. Code is still type-checked on every target.
}
cfg!(...) returns a bool constant. Both arms of the if are compiled on every target, so they must type-check everywhere. For mutually exclusive code, prefer #[cfg(...)] on items.
cfg_attr for conditional attributes
#[cfg_attr(predicate, attr1, attr2, ...)] emits the listed attributes ONLY when the predicate is true ([reference-cfg]).
#[cfg_attr(target_os = "linux", repr(C))]structHeader {
magic: u32,
flags: u32,
}
// On Linux, expands to: #[repr(C)] struct Header { ... }// On other targets, no repr attribute is applied.#[cfg_attr(not(test), inline)]fnhot_path() { /* ... */ }
ALWAYS use cfg_attr when an attribute itself should be conditional. NEVER copy-paste the whole item under #[cfg(...)] just to vary one attribute.
rustup target list --installed | grep <triple> shows the target.
Linker is on PATH or pinned in .cargo/config.toml.
cargo build --target <triple> exits 0.
file target/<triple>/release/<bin> reports the expected ELF / PE / Wasm format.
For musl : ldd target/<triple>/release/<bin> reports not a dynamic executable.
For Wasm : wasm-objdump -h <bin>.wasm lists sections without errors.
Platform-conditional code paths were exercised on the actual target (or under an emulator) ; cfg! only verifies the predicate, not the runtime correctness.
Reference links
[[references/methods.md]] : full rustup / cargo / cross command catalogue, all [target.<triple>] keys, full cfg-key list with valid values, env-var equivalents.
[[references/examples.md]] : end-to-end recipes (musl static, Windows MinGW from Linux, macOS ARM, WASI, Cortex-M no_std, Cross.toml, GitHub Actions matrix).
[[references/anti-patterns.md]] : missing rustup target add, choosing musl for the wrong reason, wasm32-unknown-unknown for WASI workloads, hard-coding linker path, missing cfg_attr, untested conditional code paths.