You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
In AI and Worktrees Are Filling Our Disks: Kache Storage, Measured we benchmark kache on Firefox: a cold build in one worktree populates the cache, then a warm build in a second worktree at a different absolute path restores from it. For that warm build to actually hit, the two checkouts have to produce identical compiler inputs, and stock Firefox does not: its build system writes absolute checkout paths into build outputs. To get Firefox to fully use kache we apply two patches before running the benchmark, declared in scenario.toml. This post explains what they do and why they are needed.
Both patches address the same root problem. kache normalizes paths that appear in compiler command lines, environment variables and dep files, but it cannot rewrite bytes inside a generated source file or a compiled rlib. When a build script bakes /home/user/clone-a/... into a file that later becomes a compiler input, two clones at different paths produce genuinely different inputs, and no key normalization on the cache side can (or should) paper over that. The only correct fix is at the source: stop baking absolute paths into build outputs. These are reproducible-build fixes in spirit, the same class of change Firefox has already made elsewhere (MOZ_BUILD_DATE for example), and the same issues would bite any distributed or cross-machine compile cache, not just kache.
This removes the biggest cross-checkout divergence cascade. The mozbuild helper crate is generated at build time by generate_buildconfig.py, which bakes TOPOBJDIR and TOPSRCDIR into the crate as const &str literals, along with NSPR_CFLAGS/NSS_CFLAGS arrays containing absolute -I paths. Those literals end up in the compiled mozbuild rlib, so the rlib is byte-different in every checkout. Dozens of build scripts (neqo-crypto, ohttp, nss-gk-api, mtu, gkrust, the style bindings, the profiler API and more) depend on mozbuild, so that one divergent rlib poisons their dependency hashes and everything downstream of them.
The patch changes mozbuild to read the paths from the environment when a build script runs (MOZ_TOPOBJDIR, and MOZ_TOPSRCDIR newly exported in rust.mk) instead of baking them in at generation time. The TOPOBJDIR/TOPSRCDIR constants become topobjdir()/topsrcdir() functions, the NSPR/NSS cflags are derived from the objdir at build-script runtime, and the objdir_path!/srcdir_path! macros switch to env!(). With that, mozbuild's rlib is byte-identical across checkouts and the checkout-specific paths live only in environment variables, which kache can normalize (the KACHE_PATH_ONLY_ENV_VARS and KACHE_BASE_DIR settings in the benchmark's mozconfig pair with this).
Four of the affected crates are vendored (mtu, neqo-crypto, nss-gk-api, ohttp), and editing a vendored build.rs trips cargo's vendor checksums, so the patch also updates their .cargo-checksum.json. That part is a bench-only necessity; an upstream submission would regenerate the checksums through mach vendor.
Two build scripts generate Rust source containing include_str!/include_bytes! with absolute paths: webrender's build.rs writes shaders.rs referencing every GLSL shader by absolute path, and khronos_api's build.rs writes webgl_exts.rs referencing the WebGL extension XML files the same way. The generated .rs file is a hashed compiler input, so two checkouts produce different bytes and these crates miss across clones even though the included content is identical.
The fix is small: emit the paths relative to OUT_DIR, which is where the generated file lives and what include_str!/include_bytes! resolve against anyway. The generated source becomes byte-identical across checkouts and nothing about the build changes, since the included files' contents are hashed separately. khronos_api is vendored, so this patch carries the same .cargo-checksum.json update as above.
These patches don't inflate the numbers
Nothing is skipped or faked. Every compile still runs in full on the cold build, and the warm build still recompiles anything whose real inputs changed. The patches only remove checkout-specific bytes from inputs so identical work is recognizable as identical. Without them the benchmark would mostly measure Firefox's path-reproducibility gaps rather than the cache, and anyone pointing sccache or a similar tool at Firefox across machines or checkout paths would need the same fixes. Both changes are upstreamable in principle.
Covering the other failure mode
The patched benchmark covers the spatial axis: the same pinned revision built at two different paths. There is a second, independent failure mode on the temporal axis: the same checkout, source moving forward in time, as in the daily "build, pull, rebuild" workflow. In case something drifts there, we are adding one more nightly check for it in #479: a permanent pull benchmark that builds vanilla Firefox at one revision, checks out the next day's revision in the same worktree, rebuilds, and tracks how much of the cache survives. It deliberately runs on unpatched Firefox with none of the pins described above, so it measures kache against Firefox exactly as users build it and catches temporal regressions independently of these patches.
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
In AI and Worktrees Are Filling Our Disks: Kache Storage, Measured we benchmark kache on Firefox: a cold build in one worktree populates the cache, then a warm build in a second worktree at a different absolute path restores from it. For that warm build to actually hit, the two checkouts have to produce identical compiler inputs, and stock Firefox does not: its build system writes absolute checkout paths into build outputs. To get Firefox to fully use kache we apply two patches before running the benchmark, declared in scenario.toml. This post explains what they do and why they are needed.
Both patches address the same root problem. kache normalizes paths that appear in compiler command lines, environment variables and dep files, but it cannot rewrite bytes inside a generated source file or a compiled rlib. When a build script bakes
/home/user/clone-a/...into a file that later becomes a compiler input, two clones at different paths produce genuinely different inputs, and no key normalization on the cache side can (or should) paper over that. The only correct fix is at the source: stop baking absolute paths into build outputs. These are reproducible-build fixes in spirit, the same class of change Firefox has already made elsewhere (MOZ_BUILD_DATEfor example), and the same issues would bite any distributed or cross-machine compile cache, not just kache.firefox-mozbuild-path-clean.patch
This removes the biggest cross-checkout divergence cascade. The
mozbuildhelper crate is generated at build time bygenerate_buildconfig.py, which bakesTOPOBJDIRandTOPSRCDIRinto the crate asconst &strliterals, along withNSPR_CFLAGS/NSS_CFLAGSarrays containing absolute-Ipaths. Those literals end up in the compiledmozbuildrlib, so the rlib is byte-different in every checkout. Dozens of build scripts (neqo-crypto,ohttp,nss-gk-api,mtu,gkrust, the style bindings, the profiler API and more) depend onmozbuild, so that one divergent rlib poisons their dependency hashes and everything downstream of them.The patch changes
mozbuildto read the paths from the environment when a build script runs (MOZ_TOPOBJDIR, andMOZ_TOPSRCDIRnewly exported inrust.mk) instead of baking them in at generation time. TheTOPOBJDIR/TOPSRCDIRconstants becometopobjdir()/topsrcdir()functions, the NSPR/NSS cflags are derived from the objdir at build-script runtime, and theobjdir_path!/srcdir_path!macros switch toenv!(). With that,mozbuild's rlib is byte-identical across checkouts and the checkout-specific paths live only in environment variables, which kache can normalize (theKACHE_PATH_ONLY_ENV_VARSandKACHE_BASE_DIRsettings in the benchmark's mozconfig pair with this).Four of the affected crates are vendored (
mtu,neqo-crypto,nss-gk-api,ohttp), and editing a vendoredbuild.rstrips cargo's vendor checksums, so the patch also updates their.cargo-checksum.json. That part is a bench-only necessity; an upstream submission would regenerate the checksums throughmach vendor.firefox-generated-source-relative.patch
Two build scripts generate Rust source containing
include_str!/include_bytes!with absolute paths: webrender'sbuild.rswritesshaders.rsreferencing every GLSL shader by absolute path, andkhronos_api'sbuild.rswriteswebgl_exts.rsreferencing the WebGL extension XML files the same way. The generated.rsfile is a hashed compiler input, so two checkouts produce different bytes and these crates miss across clones even though the included content is identical.The fix is small: emit the paths relative to
OUT_DIR, which is where the generated file lives and whatinclude_str!/include_bytes!resolve against anyway. The generated source becomes byte-identical across checkouts and nothing about the build changes, since the included files' contents are hashed separately.khronos_apiis vendored, so this patch carries the same.cargo-checksum.jsonupdate as above.These patches don't inflate the numbers
Nothing is skipped or faked. Every compile still runs in full on the cold build, and the warm build still recompiles anything whose real inputs changed. The patches only remove checkout-specific bytes from inputs so identical work is recognizable as identical. Without them the benchmark would mostly measure Firefox's path-reproducibility gaps rather than the cache, and anyone pointing sccache or a similar tool at Firefox across machines or checkout paths would need the same fixes. Both changes are upstreamable in principle.
Covering the other failure mode
The patched benchmark covers the spatial axis: the same pinned revision built at two different paths. There is a second, independent failure mode on the temporal axis: the same checkout, source moving forward in time, as in the daily "build, pull, rebuild" workflow. In case something drifts there, we are adding one more nightly check for it in #479: a permanent pull benchmark that builds vanilla Firefox at one revision, checks out the next day's revision in the same worktree, rebuilds, and tracks how much of the cache survives. It deliberately runs on unpatched Firefox with none of the pins described above, so it measures kache against Firefox exactly as users build it and catches temporal regressions independently of these patches.
All reactions