Cargo Build Orchestration ↔ rustc Incremental Compilation: When to Invalidate Stale Artifacts?
0 reputation · 20 Jul 2020, 06:57 UTC
0 reputation · 20 Jul 2020, 06:57 UTC
The goal is to determine the exact boundary at which Cargo’s rebuild logic and rustc’s incremental state diverge, causing a stale artifact to be reused. Constraints include changes to the toolchain, RUSTFLAGS, dependency versions, and build‑script side effects that may not be fully captured by Cargo’s fingerprinting. The uncertainty lies in whether Cargo’s current invalidation logic is sufficient for high‑assurance or reproducible builds, or if an explicit clean is required after certain alterations.
Key questions remain:
29775 reputation · 20 Jul 2020, 14:47 UTC
Cargo treats a crate as up‑to‑date only when its fingerprint—comprising rustc version, target triple, profile, enabled features, dependency fingerprints, source file hashes, and the effective RUSTFLAGS—matches the previously stored value. If any of those components changes, Cargo marks the crate dirty and invokes rustc; rustc’s incremental cache is then consulted to speed up that recompilation. When the fingerprint is unchanged, Cargo skips rustc entirely, so no incremental state is consulted.
cargo:rerun-if-changed path changes, or, if no such directive is given, when any file in the package changes.target/<profile>/incremental and is only used when Cargo has already decided to recompile the crate.The only way Cargo can reuse an artifact while rustc’s incremental state is out‑of‑sync is when a change that affects the build is **not** part of Cargo’s fingerprint. Typical sources are:
cargo:rerun-if-changed.rustc calls) that leave incremental caches Cargo does not track.cargo build -v (or CARGO_LOG=cargo::core::compiler::fingerprint=info cargo build) to see which fingerprint component changed and why a crate was marked “Fresh” or “Recompiling”.target/debug/incremental (or the release directory) to confirm whether an incremental cache exists for the crate and is being reused across builds.cargo:rerun-if-changed=path/to/file or cargo:rerun-if-env=VAR directive, then rebuild.cargo clean to remove both fingerprints and incremental caches, then rebuild.Did you observe the build script reading an environment variable or a file without a corresponding cargo:rerun-if-changed or cargo:rerun-if-env declaration? If yes, adding the appropriate directive eliminates the need for a manual clean; if no, the stale artifact is likely due to a toolchain or RUSTFLAGS change that Cargo already tracks, and a clean is only needed after such a change.
Use comments to ask for clarification. Post a solution as an answer.
29,775 reputation · 20 Jul 2020, 14:10 UTC
To clarify the boundary between these two systems: Cargo operates on a crate-level orchestration logic, while rustc's incremental compilation operates on a query-level dependency graph. When Cargo detects a change in RUSTFLAGS or the toolchain, it doesn't just tell rustc to "recompile"; it effectively resets the environment for that crate.
A critical detail for high-assurance builds is that incremental compilation is typically disabled for --release profiles by default. In development builds, if you are troubleshooting potential staleness, you can verify the state by inspecting the target/debug/incremental directory. If Cargo's fingerprinting fails to trigger a rebuild (e.g., due to an undeclared environment variable in a build script), rustc will never be invoked, and the incremental cache will remain untouched and stale.
For those requiring strict reproducibility, the most reliable verification is to ensure incremental = false in the Cargo.toml profile settings, bypassing the query-cache entirely.