Cargo Workflows
Purpose
Guide agents through Cargo workspaces, feature management, build scripts (build.rs ), CI integration, incremental compilation, and the Cargo tool ecosystem.
Triggers
-
"How do I set up a Cargo workspace with multiple crates?"
-
"How do features work in Cargo?"
-
"How do I write a build.rs script?"
-
"How do I speed up Cargo builds in CI?"
-
"How do I audit my Rust dependencies?"
-
"What is cargo nextest and should I use it?"
Workflow
- Workspace setup
my-project/ ├── Cargo.toml # Workspace root ├── Cargo.lock # Single lock file for all members ├── crates/ │ ├── core/ │ │ └── Cargo.toml │ ├── cli/ │ │ └── Cargo.toml │ └── server/ │ └── Cargo.toml └── tools/ └── codegen/ └── Cargo.toml
Workspace root Cargo.toml
[workspace] members = [ "crates/core", "crates/cli", "crates/server", "tools/codegen", ] resolver = "2" # Feature resolver v2 (required for edition 2021)
Shared dependency versions (workspace.dependencies)
[workspace.dependencies] serde = { version = "1", features = ["derive"] } tokio = { version = "1", features = ["full"] } anyhow = "1"
Shared profile settings
[profile.release] lto = "thin" codegen-units = 1
Member Cargo.toml
[package] name = "myapp-core" version.workspace = true edition.workspace = true
[dependencies] serde.workspace = true # Inherit from workspace anyhow.workspace = true
- Feature flags
[features] default = ["std"]
Simple flag
std = []
Feature that enables another feature
full = ["std", "async", "serde-support"]
Feature with optional dependency
async = ["dep:tokio"] serde-support = ["dep:serde", "serde/derive"]
[dependencies] tokio = { version = "1", optional = true } serde = { version = "1", optional = true }
Build with specific features
cargo build --features "async,serde-support"
Build with no default features
cargo build --no-default-features
Build with all features
cargo build --all-features
Check feature combinations
cargo check --no-default-features cargo check --all-features
Feature gotchas:
-
Features are additive: once enabled anywhere in the dependency graph, they stay enabled
-
resolver = "2" prevents feature leakage between dev-dependencies and regular deps
-
Use dep:optional_dep syntax (edition 2021) to avoid implicit feature creation
- Build scripts (build.rs)
// build.rs (at crate root, runs before compilation) use std::env; use std::path::PathBuf;
fn main() { // Re-run if these files change println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=wrapper.h"); println!("cargo:rerun-if-env-changed=MY_LIB_PATH");
// Link a system library
println!("cargo:rustc-link-lib=mylib");
println!("cargo:rustc-link-search=/usr/local/lib");
// Pass a cfg flag to Rust code
let target = env::var("TARGET").unwrap();
if target.contains("linux") {
println!("cargo:rustc-cfg=target_os_linux");
}
// Set environment variable for downstream crates
println!("cargo:rustc-env=MY_GENERATED_VAR=value");
// Generate bindings with bindgen
let bindings = bindgen::Builder::default()
.header("wrapper.h")
.generate()
.expect("Unable to generate bindings");
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings.write_to_file(out_path.join("bindings.rs")).unwrap();
}
println! directive Effect
cargo:rerun-if-changed=FILE
Re-run build script if file changes
cargo:rerun-if-env-changed=VAR
Re-run if env var changes
cargo:rustc-link-lib=NAME
Link library
cargo:rustc-link-search=PATH
Add library search path
cargo:rustc-cfg=FLAG
Enable #[cfg(FLAG)] in code
cargo:rustc-env=KEY=VAL
Set env!("KEY") at compile time
cargo:warning=MSG
Emit build warning
- Incremental builds and CI caching
GitHub Actions with sccache
- uses: Swatinem/rust-cache@v2 with: cache-on-failure: true shared-key: "release-build"
Or manual cache
- uses: actions/cache@v3 with: path: | ~/.cargo/registry/index/ ~/.cargo/registry/cache/ ~/.cargo/git/db/ target/ key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
Warm cache locally
cargo fetch # Download all deps without building cargo build --tests # Build everything including test bins
Check if incremental hurts release builds (it often does)
[profile.release] incremental = false # Default; leave false for release
- cargo nextest (faster test runner)
Install
cargo install cargo-nextest
Run tests (parallel by default, better output)
cargo nextest run
Run with specific filter
cargo nextest run test_name_pattern
List tests without running
cargo nextest list
Use in CI (JUnit output)
cargo nextest run --profile ci
nextest.toml :
[profile.ci] fail-fast = false test-threads = "num-cpus" retries = { backoff = "exponential", count = 2, delay = "1s" }
[profile.default] test-threads = "num-cpus"
- Dependency management and auditing
Check for security advisories
cargo install cargo-audit cargo audit
Deny specific licenses, duplicates, advisories
cargo install cargo-deny cargo deny check
Check for unused dependencies
cargo install cargo-machete cargo machete
Update dependencies
cargo update # Update to compatible versions cargo update -p serde # Update single package cargo upgrade # Update to latest (cargo-edit)
deny.toml :
[licenses] allow = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause"] deny = ["GPL-2.0", "AGPL-3.0"]
[bans] multiple-versions = "warn" deny = [{ name = "openssl", reason = "Use rustls instead" }]
[advisories] ignore = [] # List advisory IDs to ignore
- Useful cargo commands
Build only specific binary
cargo build --bin myapp
Build only specific example
cargo build --example myexample
Run with arguments
cargo run -- --flag arg1 arg2
Expand macros (for debugging proc macros)
cargo install cargo-expand cargo expand module::path
Tree of dependencies
cargo tree cargo tree --duplicates # Show crates with multiple versions cargo tree -i serde # Who depends on serde?
Cargo.toml metadata
cargo metadata --format-version 1 | jq '.packages[].name'
For workspace patterns and dependency resolution details, see references/workspace-patterns.md.
Related skills
-
Use skills/rust/rustc-basics for compiler flags and profile configuration
-
Use skills/rust/rust-debugging for debugging Cargo-built binaries
-
Use skills/rust/rust-ffi for build.rs with C library bindings
-
Use skills/build-systems/cmake when integrating Rust into a CMake build