Configured targets#
Declaring a target list#
Declare workspace-wide targets in the workspace Cargo.toml:
[workspace.metadata.cargo-fc]
targets = [
"x86_64-unknown-linux-gnu",
"x86_64-pc-windows-msvc",
"aarch64-apple-darwin",
]Now cargo fc check visits every combination on every target.
Per-package target lists#
A package can override the workspace list, or opt out of it:
[package.metadata.cargo-fc]
# Run this package only on wasm (overrides the workspace list, does not merge).
targets = ["wasm32-unknown-unknown"]The three states:
| Value | Meaning |
|---|---|
| key missing | inherit the workspace target list |
targets = [] | opt out of the workspace list; use the single effective target |
targets = ["…"] | this package’s own list (overrides, does not merge) |
A package-level targets key is a capability statement, not just a sweep default: it also filters an explicit --target <triple>. A package whose own list does not admit the requested triple is skipped with a warning — for targets = [], any triple other than the package’s single effective target — so running one configured target at a time keeps the same package coverage as the full sweep. To deliberately override, combine --target with --no-targets.
targets only selects which targets are visited. The target.'cfg(...)' overrides still shape the feature matrix for each concrete target.
Precedence#
For a command that supports targets, each package’s target is resolved as:
- an explicit Cargo
--target <triple>(replaces the workspace list for the run; a package-leveltargetsstill filters membership as described above), - the package’s
targets, - the workspace
targets, CARGO_BUILD_TARGET,- the host target.
Configured lists intentionally beat
CARGO_BUILD_TARGET. Repository config is the declarative matrix and shouldn’t be silently collapsed by a developer’s ambient environment — this differs from Cargo’s own[build].targetprecedence. To run a single target for one invocation, pass--target <triple>(replaces the workspace list, honoring package-leveltargetsconstraints) or--no-targets(ignore configured lists, fall back to Cargo’s default single target). Combining both ignores package-level constraints too, forcing every selected package onto the explicit target.
The host target runs as a plain build#
When a configured list names the host, cargo fc runs that target the way a bare cargo check would: no --target flag, and plain cargo as the driver.
The flag would change nothing about what is built — Cargo already defaults to the host — but it changes where and how. Output moves from target/debug into target/<triple>/, so an ordinary cargo build in the same workspace can reuse none of it, and Cargo stops applying build.rustflags to build scripts and proc macros, fingerprinting those units differently from every other build in that directory. Listing your host among targets would otherwise cost a second full copy of the host build.
An --aggregate-targets invocation that groups the host with cross targets keeps every --target, the host’s included — dropping one there would narrow what Cargo builds.
To build the host row under target/<triple>/ like every other configured target — for uniform artifact paths in CI, or to keep build.rustflags off build scripts and proc macros — turn the omission off:
[workspace.metadata.cargo-fc]
omit_host_target_flag = falseOr per invocation with --omit-host-target-flag=false. This resolves per package-target like every other flag, and only controls the injected flag; the host row’s driver stays plain cargo unless you set driver yourself.
Which commands expand targets#
Configured targets apply only to commands that accept Cargo’s --target flag. The built-ins check, clippy, build, doc, test, run, and cargo fc matrix get this automatically. Aliases that resolve to a built-in inherit it.
A custom command that doesn’t resolve to a built-in must opt in:
[workspace.metadata.cargo-fc.subcommands.my-custom-cmd]
expand_targets = trueThe same table overrides built-in defaults — for example, lint every target but keep build host-only:
[workspace.metadata.cargo-fc.subcommands.build]
expand_targets = falseWell-known plugins (nextest, audit, deny, machete, udeps, leptos, …) have their capability hint suppressed to avoid noise; this does not grant capability. Opt in or out explicitly with the same subcommands.<name> table. If configured targets exist but the command lacks the capability, cargo fc warns once and falls back to the single effective target; an explicit expand_targets = false is quiet.
testandrunexecute the binary. Thetargetslist is shared by all target-capable commands, butcheck/clippyonly need the target’srustc, whiletest/runrun the produced binary and can’t execute a foreign target. Keep them host-only — narrow with--targetor--no-targets.
Per-target package selection#
Workspace package exclusions can vary by target, using the same cfg(...) selectors and patch semantics:
[workspace.metadata.cargo-fc]
targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"]
[workspace.metadata.cargo-fc.target.'cfg(target_arch = "wasm32")']
exclude_packages = { add = ["native-cli"] }
[workspace.metadata.cargo-fc.target.'cfg(target_os = "linux")']
exclude_packages = { add = ["wasm-app"] }Throughput: --aggregate-targets#
--aggregate-targets batches a combination’s compatible configured targets into a single Cargo invocation (one --target per target) instead of one invocation per target. It’s faster on many-core machines and reports results per target group. It falls back to serial execution for run and pruned summaries. Different resolved drivers or child environments split into separate aggregate invocations.
Each row now covers a combination’s whole target group — note targets = [...] (plural), and one invocation per combination instead of one per target:
$ cargo fc --aggregate-targets --summary-only check --workspace Checking [1/4] app ( targets = [x86_64-unknown-linux-gnu, wasm32-unknown-unknown], features = [] ) Checking [2/4] app ( targets = [x86_64-unknown-linux-gnu, wasm32-unknown-unknown], features = [simd] ) Checking [3/4] app ( targets = [x86_64-unknown-linux-gnu, wasm32-unknown-unknown], features = [simd, std] ) Checking [4/4] app ( targets = [x86_64-unknown-linux-gnu, wasm32-unknown-unknown], features = [std] ) Finished 4 feature combinations for 1 package across 2 targets in 0.00s PASS app ( targets = [x86_64-unknown-linux-gnu, wasm32-unknown-unknown], 0 errors, 0 warnings, features = [] ) PASS app ( targets = [x86_64-unknown-linux-gnu, wasm32-unknown-unknown], 0 errors, 0 warnings, features = [simd] ) PASS app ( targets = [x86_64-unknown-linux-gnu, wasm32-unknown-unknown], 0 errors, 0 warnings, features = [simd, std] ) PASS app ( targets = [x86_64-unknown-linux-gnu, wasm32-unknown-unknown], 0 errors, 0 warnings, features = [std] )