--experimental_remote_repo_contents_cache (Bazel 9) is supposed to keep @@rules_req_compile+ as an in-memory overlay and only pull blobs that are actually read. Consumers with that flag on still see this on essentially every command:
DEBUG: Materializing remote repo @@rules_req_compile+
That means the whole module tree is copied onto disk instead of staying in the overlay.
Cause
The overlay can serve individual files. Bazel's own loading (load(), BUILD files) already does that; .bzl files are even prefetched.
module_ctx.path(Label) / repository_ctx.path(Label) / ctx.read(Label) do not. They all go through getPathFromLabel, which unconditionally calls ensureMaterialized on any non-main repo before returning a host path:
https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java
path() is the "give me a native filesystem path" API (the same one execute() uses), so Bazel dumps the entire overlay rather than one blob. read(Label) is implemented as getPath() then readContent, so switching path() to read() does not avoid this.
isMain() skips materialization, so consumer lockfiles in the main repo are fine.
Call sites (1.1.3 / current main)
These all path() labels inside this module, and they run for every consumer (not dev_dependency):
sdist_deps in private/sdist.bzl — parse_requirements_locks → ctx.path(Label("//private:sdist_requirements.txt")).
requirements.parse for @req_compile_deps in MODULE.bazel — path() of the five //3rdparty/requirements.*.txt lockfiles (once in the module extension, again when the hub repo rule runs).
whl_repository in private/whl_repo.bzl — default _compiler = Label("//private:sdist_compiler.py") on every wheel repo. Bazel also walks label attrs up front (enforceLabelAttributes), so this fires even when no sdist is built.
Suggested fix
Do not convert labels in this module into host paths.
load() the sdist lockfile and the req_compile_deps lockfiles as .bzl string constants; parse those strings instead of ctx.path()/ctx.read(Label).
- Drop the default
_compiler file label. When an sdist actually needs building, write the compiler script into the generated wheel repo (repository_ctx.file(...)) and path() that local file (a string path, not a Label into this module).
.bzl load() is the overlay-prefetch path, so it does not materialize.
I have a working consumer-side patch along those lines if useful as a starting point.
--experimental_remote_repo_contents_cache(Bazel 9) is supposed to keep@@rules_req_compile+as an in-memory overlay and only pull blobs that are actually read. Consumers with that flag on still see this on essentially every command:That means the whole module tree is copied onto disk instead of staying in the overlay.
Cause
The overlay can serve individual files. Bazel's own loading (
load(),BUILDfiles) already does that;.bzlfiles are even prefetched.module_ctx.path(Label)/repository_ctx.path(Label)/ctx.read(Label)do not. They all go throughgetPathFromLabel, which unconditionally callsensureMaterializedon any non-main repo before returning a host path:https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/bazel/repository/starlark/StarlarkBaseExternalContext.java
path()is the "give me a native filesystem path" API (the same oneexecute()uses), so Bazel dumps the entire overlay rather than one blob.read(Label)is implemented asgetPath()thenreadContent, so switchingpath()toread()does not avoid this.isMain()skips materialization, so consumer lockfiles in the main repo are fine.Call sites (1.1.3 / current main)
These all
path()labels inside this module, and they run for every consumer (notdev_dependency):sdist_depsinprivate/sdist.bzl—parse_requirements_locks→ctx.path(Label("//private:sdist_requirements.txt")).requirements.parsefor@req_compile_depsinMODULE.bazel—path()of the five//3rdparty/requirements.*.txtlockfiles (once in the module extension, again when the hub repo rule runs).whl_repositoryinprivate/whl_repo.bzl— default_compiler = Label("//private:sdist_compiler.py")on every wheel repo. Bazel also walks label attrs up front (enforceLabelAttributes), so this fires even when no sdist is built.Suggested fix
Do not convert labels in this module into host paths.
load()the sdist lockfile and thereq_compile_depslockfiles as.bzlstring constants; parse those strings instead ofctx.path()/ctx.read(Label)._compilerfile label. When an sdist actually needs building, write the compiler script into the generated wheel repo (repository_ctx.file(...)) andpath()that local file (a string path, not a Label into this module)..bzlload()is the overlay-prefetch path, so it does not materialize.I have a working consumer-side patch along those lines if useful as a starting point.