Cross-compilation in CI#
Scenario: you want to check every feature combination on several target triples, from a single CI job, without a matrix of GitHub Actions jobs.
Declare the targets#
Declare the target list in the workspace Cargo.toml:
# Workspace that checks every feature combination across multiple target triples.
[workspace]
resolver = "2"
members = ["crates/*"]
[workspace.package]
version = "0.1.0"
edition = "2024"
publish = false
[workspace.metadata.cargo-fc]
# Check every feature combination on each of these targets.
targets = [
"x86_64-unknown-linux-gnu",
"wasm32-unknown-unknown",
]
# `check` never links, so plain cargo cross-checks fine without a C toolchain.
# (Cross-compiling native-C deps would use the cargo-zigbuild default instead.)
driver = "cargo"A single cargo fc check then visits every feature combination on every target:
$ cargo fc --summary-only check --workspace Checking [1/8] app ( target = x86_64-unknown-linux-gnu, features = [] ) Checking [2/8] app ( target = x86_64-unknown-linux-gnu, features = [simd] ) Checking [3/8] app ( target = x86_64-unknown-linux-gnu, features = [simd, std] ) Checking [4/8] app ( target = x86_64-unknown-linux-gnu, features = [std] ) Checking [5/8] app ( target = wasm32-unknown-unknown, features = [] ) Checking [6/8] app ( target = wasm32-unknown-unknown, features = [simd] ) Checking [7/8] app ( target = wasm32-unknown-unknown, features = [simd, std] ) Checking [8/8] app ( target = wasm32-unknown-unknown, features = [std] ) Finished 8 feature combinations for 1 package across 2 targets in 0.00s PASS app ( target = x86_64-unknown-linux-gnu, 0 errors, 0 warnings, features = [] ) PASS app ( target = x86_64-unknown-linux-gnu, 0 errors, 0 warnings, features = [simd] ) PASS app ( target = x86_64-unknown-linux-gnu, 0 errors, 0 warnings, features = [simd, std] ) PASS app ( target = x86_64-unknown-linux-gnu, 0 errors, 0 warnings, features = [std] ) PASS app ( target = wasm32-unknown-unknown, 0 errors, 0 warnings, features = [] ) PASS app ( target = wasm32-unknown-unknown, 0 errors, 0 warnings, features = [simd] ) PASS app ( target = wasm32-unknown-unknown, 0 errors, 0 warnings, features = [simd, std] ) PASS app ( target = wasm32-unknown-unknown, 0 errors, 0 warnings, features = [std] )
(Add more triples — aarch64-unknown-linux-gnu, x86_64-pc-windows-msvc, … — to the targets list as needed.)
Keep host-executing commands host-only#
check and clippy only need each target’s rustc, so they cross-compile fine. test and run execute the binary and can’t run a foreign target — keep them host-only:
[workspace.metadata.cargo-fc.subcommands.test]
expand_targets = falseLint everything in one invocation#
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
with:
targets: x86_64-unknown-linux-gnu, aarch64-unknown-linux-gnu, wasm32-unknown-unknown
- uses: romnn/cargo-feature-combinations@main
- run: cargo fc clippyA single cargo fc clippy iterates every configured target and feature combination.
Native-C dependencies#
If a target pulls in native-C build dependencies, cargo fc uses cargo-zigbuild automatically for non-host targets. Install zig and cargo-zigbuild on the runner, or override the driver.
Throughput#
Add --aggregate-targets to batch each combination’s targets into one Cargo invocation on many-core runners:
- run: cargo fc clippy --aggregate-targetsFor fanning targets out across separate jobs instead, see Continuous integration.