build(cache): share sccache across worktrees with mode-aware wrappers - #128
Conversation
The cargo-cache wrappers only enabled sccache for release builds, and it was not even installed locally, so every fingerprint bust recompiled Servo at full price in every worktree and profile tree (measured: 343 GB target in the root checkout, 268 GB in one worktree, 41k stale files in debug/deps). sccache and incremental compilation are mutually exclusive (sccache 0.17 hard-errors on the env var and on arg-level -Cincremental), so the wrappers now pick a mode per command: - build/test/bench and release-like profiles: RUSTC_WRAPPER=sccache with CARGO_INCREMENTAL=0 and a bounded cache (75G default) shared by every worktree under the hypercolor cache root. On Windows this also routes cl.exe through sccache (cc + CMake launchers), which caches mozangle's ANGLE builds. - run/check/clippy: Cargo incremental. rustc metadata units are not cacheable by sccache, and an edit-rebuild of hypercolor-core measures ~45s non-incremental vs ~11s incremental, so the edit loops keep incremental. Iteration-shaped recipes (test-crate, test-one, app, the Windows dev daemon build) pin this via HYPERCOLOR_ITERATE=1. rust-lld becomes the local linker on x86_64-pc-windows-msvc; the shared .cargo/config.toml stays portable for CI and release. Disk growth gets bounds: the cpu-smoke feature shape builds into its own target/cpu-smoke lane instead of churning the daily tree, and new just recipes (disk, gc, gc-worktrees, gc-deep) report and sweep stale artifacts with cargo-sweep. Verified on Windows: mode selection per subcommand, sccache fill and cache hits across a cargo clean, CI env compat (HYPERCOLOR_FORCE_SCCACHE and explicit CARGO_INCREMENTAL are respected). Co-Authored-By: Nova (Claude Fable 5) <noreply@anthropic.com>
The verification pass on the sccache topology change returned PASS with two majors and five minors; this lands the fixes. rust-lld is now gated off release-like builds so shipped Windows artifacts (CI sidecars, the installer, app bundles built outside the wrapper) all keep link.exe; the fast linker remains for dev iteration. The caching doc's CI section claimed a mozilla-actions sccache setup that has never existed in .github; it now documents reality: CI runs rust-cache with CARGO_INCREMENTAL=0 and no sccache, and the wrappers preserve that environment unchanged. Robustness fixes from the same report: nextest joins the sccache subcommand list (CI's test runner, and local nextest runs deserve the cache); the sh wrapper's bare-invocation default is injected before mode detection so it routes like an explicit workspace build; an ambient sccache RUSTC_WRAPPER is dropped in incremental mode instead of hard-failing every compile; the ps1 --profile scan can no longer unset an earlier --release match; and the housekeeping recipes use plain sh lines instead of shebang blocks, which resolve bash from PATH and can land on WSL bash on Windows. just disk survives empty target dirs, and just disk/gc are verified live (gc: cargo sweep --installed plus --time 14, clean no-op on a fresh tree). Co-Authored-By: Nova (Claude Fable 5) <noreply@anthropic.com>
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughBuild wrappers now select between ChangesBuild caching and iteration behavior
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Developer
participant BuildWrapper
participant Cargo
participant Sccache
Developer->>BuildWrapper: run a build, test, or development recipe
BuildWrapper->>BuildWrapper: inspect command, profile, and environment
BuildWrapper->>Sccache: configure compiler cache when selected
BuildWrapper->>Cargo: set incremental mode and execute command
Cargo-->>Developer: produce build or test result
Poem
✨ Finishing Touches📝 Generate docstrings
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
🗄️ One compile cache for every worktree
💡 What this is
Sharing one Cargo target dir between worktrees is the wrong primitive for this repo: Cargo's target lock is coarse, so two agents building in parallel would serialize, and alternating feature shapes would thrash the shared tree. Instead, every worktree keeps its own target dir and the sharing happens one layer down — a single bounded sccache under
~/.cache/hypercolor/sccachethat every worktree's compiles hit. A second worktree's cold build becomes mostly cache hits without any cross-worktree locking.The wrappers previously enabled sccache only for release builds (and it was not installed on the primary dev machine at all), so every fingerprint bust — toolchain bump, lockfile change, profile switch, new worktree — recompiled Servo at full price, per profile tree, per worktree.
🎯 The invariant
sccache 0.17 hard-errors the moment it sees incremental compilation, both via the
CARGO_INCREMENTALenv var and via arg-level-Cincremental(verified by probe; there is no passthrough). The property to anchor on: no code path in either wrapper can select sccache and leave incremental enabled in the same invocation. Everything else in the wrapper diff is plumbing around that exclusion.🛠️ How it works
The wrappers pick a mode per command, from the cargo subcommand and profile flags:
cargo build/test/bench/nextest, any--release/--profile release|benchCARGO_INCREMENTAL=0cargo runcargo check/clippy--emit=metadataunits at alldeny,doc, …)Incremental mode also drops an ambient sccache
RUSTC_WRAPPERif one leaks in from the environment, so the sccache+incremental hard-error is unreachable from any wrapper path.scripts/cargo-cache-build.ps1— subcommand detection, the mode split,SCCACHE_CACHE_SIZE(default75G,HYPERCOLOR_SCCACHE_SIZEto override), and Windows C/C++ caching:CC/CXX=sccache clfor cc-rs consumers (mozangle's ANGLE build, the largest repeat C++ cost at 12 GB of stale build output on the audited machine) andCMAKE_*_COMPILER_LAUNCHERfor CMake consumers (turbojpeg). Also setsrust-lldas the MSVC linker viaCARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKERfor non-release builds only — env-only so the committed.cargo/config.tomlstays portable, and gated off release-like profiles so shipped artifacts (CI sidecars, the installer, app bundles built outside this wrapper) all keeplink.exe.scripts/cargo-cache-build.sh— the same mode logic for Linux/macOS. The C/C++ ccache routing it already had is unchanged.CARGO_INCREMENTALalways wins;HYPERCOLOR_NO_SCCACHE=1(session) andHYPERCOLOR_ITERATE=1(per invocation) force incremental mode;HYPERCOLOR_FORCE_SCCACHE=1forces sccache mode. CI does not run sccache at all today (verification grep: zero sccache references in.github/, none preinstalled on hosted runners) — it setsCARGO_INCREMENTAL=0globally, which both old and new wrappers preserve identically, so CI behavior is bit-for-bit unchanged. The doc's CI section previously claimed a CI sccache setup that never existed; it now describes reality, and the future CI sccache lane is a filed sidequest.just test-crate,test-one,app, and the daemon build insidescripts/dev-windows.ps1exportHYPERCOLOR_ITERATE=1, because those are edit loops even though their subcommands aretest/build. Alternating one profile tree between modes rebuilds only workspace crates (~50s measured), never dependencies.justfiledisk hygiene —just disk(usage report incl. sccache stats),just gc(cargo-sweep: orphaned toolchains, then >14-day artifacts),just gc-worktrees(the same sweep across every worktree lane),just gc-deep(also drops incremental state and the cpu-smoke lane).just e2e-build-cpunow builds intotarget/cpu-smokeso the--no-default-featuresfeature unification stops churning the daily tree; an ambientCARGO_TARGET_DIR(CI) still wins.docs/development/SERVO_BUILD_CACHING.md— documents the mode split, the cross-worktree topology, and the disk bounds.flowchart LR A[worktree A\ntarget/] -->|compile units| S[(sccache\n75G bounded)] B[worktree B\ntarget/] -->|compile units| S C[worktree C\ntarget/] -->|compile units| S A -.-> M[(mozbuild state)] B -.-> M A --> X[target/cpu-smoke\nisolated feature shape]🧪 Validation
RUSTC_WRAPPER=sccache+CARGO_INCREMENTAL=1→sccache: incremental compilation is prohibited(exit 101); same with the env unset and dev-profile-Cincrementalargs. This is the measured basis for the mode split.hypercolor-core --features servo, warm deps, same machine: 44.8s withCARGO_INCREMENTAL=0vs 11.4s incremental.cargo build -p hypercolor-typesin sccache mode → exit 0, sccache stats 15 misses (cold fill); aftercargo clean, rebuild → 11 cache hits (the 5–8 non-cacheable calls are proc-macro crates, which sccache can never cache).cargo check→ incremental mode banner, exit 0.cargo run --help→ incremental mode banner.cargo test -p hypercolor-types --no-run→ sccache mode banner,Finishedin 17.6s.nextestadded to the sccache list, the sh no-args default moved ahead of mode detection, ambientRUSTC_WRAPPERdropped in incremental mode, the ps1--profilescan made monotonic, andjust diskhardened against empty targets.--releasebuild → sccache mode with no rust-lld line; dev build → sccache mode + rust-lld; ambientRUSTC_WRAPPER=sccache+cargo check→ "unset ambient sccache RUSTC_WRAPPER" + incremental mode. PowerShell parser 0 errors,bash -nclean,just --listexit 0,just diskandjust gcrun live.sccache clpath end to end, and a full daemon build — the firstjust daemonon this branch proves the C++ cache path, and the first non-release build after the linker change re-runs build scripts and relinks once (one-time warm-up, cold C++ cache included).🔍 What reviewers should focus on
+toolchain, the no-args default) — a wrong subcommand read routes a command into the wrong mode. Known nit, deliberately unfixed: a value-taking flag before the subcommand (cargo --config x.y build) mis-detects and fails safe into incremental mode; no caller does this.HYPERCOLOR_FORCE_SCCACHE,HYPERCOLOR_NO_SCCACHE,HYPERCOLOR_ITERATE, and a pre-setCARGO_INCREMENTAL, especially against CI's globalCARGO_INCREMENTAL=0.CC='sccache cl'on MSVC relies on cc-rs's known-wrapper splitting; if any build script mishandles it, the C/C++ launcher lines are the first thing to pull.📌 Follow-ups (deliberate non-fixes)
just verifylink time and deps disk; consolidation to one harness per crate + cargo-nextest is filed as a Sibyl sidequest.🤖 Generated with Claude Code
Summary by CodeRabbit