Mount the repo contents cache under the sandbox's hermetic /tmp (https://github.com/bazelbuild/bazel/pull/29876) ### Description ### Motivation A repo fetched into the repo contents cache is materialized in the output base as a symlink into the cache. When the cache lives under /tmp but outside the output base (e.g. when relocated via `--repository_cache` or `--repo_contents_cache`), the symlink target wasn't made available inside a sandbox that uses a hermetic `/tmp`. Fixes #29649 ### Build API Changes No ### Checklist - [ ] I have added tests for the new use cases (if any). - [ ] I have updated the documentation (if applicable). ### Release Notes RELNOTES: None Closes #29876. PiperOrigin-RevId: 935046365 Change-Id: Idbc024b147665b764582b6a15606508bd9bbf428
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java b/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java index 554bb98..07cb396 100644 --- a/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java +++ b/src/main/java/com/google/devtools/build/lib/runtime/CommandEnvironment.java
@@ -680,6 +680,15 @@ return workspace.getSkyframeExecutor(); } + /** + * Returns the path of the repo contents cache directory, or {@code null} if the repo contents + * cache is disabled. + */ + @Nullable + public Path getRepoContentsCachePath() { + return getSkyframeExecutor().getRepoContentsCachePath(); + } + public SkyframeBuildView getSkyframeBuildView() { return getSkyframeExecutor().getSkyframeBuildView(); }
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 2b80aaa..f70bf30 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java
@@ -61,6 +61,7 @@ import java.time.Duration; import java.util.HashMap; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.SortedMap; import java.util.TreeMap; @@ -184,7 +185,9 @@ // or well-known children of /tmp from the host. // TODO(bazel-team): Review all flags whose path may have to be considered here. return Stream.concat( - Stream.of(sandboxBase, cmdEnv.getOutputBase()), + Stream.concat( + Stream.of(sandboxBase, cmdEnv.getOutputBase()), + Optional.ofNullable(cmdEnv.getRepoContentsCachePath()).stream()), cmdEnv.getPackageLocator().getPathEntries().stream().map(Root::asPath)) .filter(p -> p.startsWith(slashTmp)) // For any path /tmp/dir1/dir2 we encounter, we instead mount /tmp/dir1 (first two
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/ExternalFilesHelper.java b/src/main/java/com/google/devtools/build/lib/skyframe/ExternalFilesHelper.java index 614011f..0947320 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/ExternalFilesHelper.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/ExternalFilesHelper.java
@@ -119,6 +119,15 @@ } /** + * Returns the path of the repo contents cache directory, or {@code null} if the repo contents + * cache is disabled. Fetched repos may be materialized as symlinks into this directory. + */ + @Nullable + public Path getRepoContentsCachePath() { + return repoContentsCachePathSupplier.get(); + } + + /** * The action to take when an external path is encountered. See {@link FileType} for the * definition of "external". */
diff --git a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java index 0b682eb..a3d2ced 100644 --- a/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java +++ b/src/main/java/com/google/devtools/build/lib/skyframe/SkyframeExecutor.java
@@ -2794,6 +2794,15 @@ return packageManager; } + /** + * Returns the path of the repo contents cache directory, or {@code null} if the repo contents + * cache is disabled. + */ + @Nullable + public Path getRepoContentsCachePath() { + return externalFilesHelper.getRepoContentsCachePath(); + } + public QueryTransitivePackagePreloader getQueryTransitivePackagePreloader() { return queryTransitivePackagePreloader; }
diff --git a/src/test/shell/integration/sandboxing_test.sh b/src/test/shell/integration/sandboxing_test.sh index 0855814..eadc733 100755 --- a/src/test/shell/integration/sandboxing_test.sh +++ b/src/test/shell/integration/sandboxing_test.sh
@@ -827,6 +827,58 @@ [[ -f "${temp_dir}/file" ]] || fail "Expected ${temp_dir}/file to exist" } +# Regression test for https://github.com/bazelbuild/bazel/issues/29649 +function test_repo_contents_cache_under_hermetic_tmp { + if ! is_linux; then + echo "Skipping test: hermetic /tmp is only supported in Linux" 1>&2 + return 0 + fi + + if [[ "${PRODUCT_NAME}" != "bazel" ]]; then + echo "Skipping test: repo contents cache is only supported in Bazel" 1>&2 + return 0 + fi + + # Place the repo contents cache directly under /tmp at a location that is not + # contained in the output base. + # Not declared local so that it is still bound when the EXIT trap runs. + repo_contents_cache=$(mktemp -d /tmp/repo_contents_cache.XXXXXX) + trap 'rm -rf ${repo_contents_cache}' EXIT + + cat > repo.bzl <<'EOF' +def _cached_repo_impl(rctx): + rctx.file("BUILD", "exports_files(['data.txt'])") + rctx.file("data.txt", "hello from the cached repo\n") + # Mark the repo as reproducible so it is stored in the repo contents cache + # and materialized as a symlink into it. + return rctx.repo_metadata(reproducible = True) + +cached_repo = repository_rule(implementation = _cached_repo_impl) +EOF + touch BUILD + + cat >> MODULE.bazel <<'EOF' +cached_repo = use_repo_rule("//:repo.bzl", "cached_repo") +cached_repo(name = "cached_repo") +EOF + + mkdir -p pkg + cat > pkg/BUILD <<'EOF' +genrule( + name = "use_cached_repo", + srcs = ["@cached_repo//:data.txt"], + outs = ["out.txt"], + cmd = "cat $(location @cached_repo//:data.txt) > $@", +) +EOF + + bazel build //pkg:use_cached_repo \ + --repo_contents_cache="${repo_contents_cache}" &>"$TEST_log" \ + || fail "Expected build to succeed" + + assert_contains "hello from the cached repo" bazel-genfiles/pkg/out.txt +} + function test_sandbox_reuse_stashes_sandbox() { mkdir pkg cat >pkg/BUILD <<'EOF'