fix: point runfiles libraries at the manifest when there is no runfiles tree
Where runfiles trees are not built, which is the default on Windows,
RUNFILES_DIR names a directory holding nothing but the manifest, and a runfiles
library has to read that manifest instead. Bazel does not put
RUNFILES_MANIFEST_FILE in a build action's environment
(bazelbuild/bazel#7994), so the launcher exported only RUNFILES_DIR and
@bazel/runfiles fell back to walking a tree that was not there:
RUNFILES_MANIFEST_FILE should have been set but wasn't.
falling back to using runfiles symlinks.
cfg_probe.txt not found in runfiles at _main/js/private/test/js_run_binary/cfg_probe.txt
Export RUNFILES_MANIFEST_FILE from the launcher when the tree was not laid out
and the manifest is really there. //js/private/test/js_run_binary:target_cfg_test
now runs on Windows rather than failing to build, so it is no longer marked
incompatible.
The two RUNFILES_ONLY cases stay marked, with the reason corrected: they assert
that cfg_probe.txt is *absent* from the target bin directory, which only holds
for an action that cannot see files it did not declare. Other targets in that
package do build cfg_probe.txt in the target configuration, so without a sandbox
the probe finds that copy in the execroot. no_copy_to_bin_test's reason is
likewise corrected: it requires a file by a path relative to the entry point,
and require() does not consult a manifest.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
diff --git a/js/private/bash.bzl b/js/private/bash.bzl
index 5341565..80dfd06 100644
--- a/js/private/bash.bzl
+++ b/js/private/bash.bzl
@@ -113,4 +113,18 @@
# Set RUNFILES_DIR if not already set so that tools such as @bazel/runfiles
# can locate runfiles without requiring RUNFILES to be exported.
export RUNFILES_DIR="${RUNFILES_DIR:-$RUNFILES}"
+
+# Where runfiles trees are not built, which is the default on Windows, RUNFILES_DIR names a
+# directory holding nothing but the manifest and a runfiles library has to read that manifest
+# instead. Bazel does not put RUNFILES_MANIFEST_FILE in a build action's environment
+# (https://github.com/bazelbuild/bazel/issues/7994), so point at the manifest here when the tree
+# was not laid out. Only ever when the file is really there: a library that finds neither a tree
+# nor a manifest reports that better than one sent to a path that does not exist.
+if [ -z "${RUNFILES_MANIFEST_FILE:-}" ] && [ ! -d "$RUNFILES/$JS_BINARY__WORKSPACE" ]; then
+ if [ -f "$RUNFILES/MANIFEST" ]; then
+ export RUNFILES_MANIFEST_FILE="$RUNFILES/MANIFEST"
+ elif [ -f "${RUNFILES}_manifest" ]; then
+ export RUNFILES_MANIFEST_FILE="${RUNFILES}_manifest"
+ fi
+fi
"""
diff --git a/js/private/test/js_run_binary/BUILD.bazel b/js/private/test/js_run_binary/BUILD.bazel
index 59035b0..a729119 100644
--- a/js/private/test/js_run_binary/BUILD.bazel
+++ b/js/private/test/js_run_binary/BUILD.bazel
@@ -8,11 +8,12 @@
package(default_testonly = True)
-# find_cfg_probe.mjs locates its probe file with the @bazel/runfiles library, so these cases need a
-# runfiles tree, which Windows does not build by default. Bazel does not set the runfiles
-# environment variables for the tool of a build action either, so without a tree the library has
-# nothing to resolve against.
-_REQUIRES_RUNFILES = select({
+# The RUNFILES_ONLY cases below assert that cfg_probe.txt is *absent* from the target bin directory,
+# which only holds for an action that cannot see files it did not declare -- a sandboxed one. Other
+# targets in this package do build cfg_probe.txt in the target configuration, so on Windows, which
+# has no sandbox, the probe finds that copy sitting in the execroot. What the launcher resolves
+# through the runfiles manifest is not the issue; target_cfg_test below runs on Windows.
+_REQUIRES_SANDBOX = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
})
@@ -41,7 +42,7 @@
name = "runfiles_location",
silent_on_success = False,
stdout = "runfiles_location.txt",
- target_compatible_with = _REQUIRES_RUNFILES,
+ target_compatible_with = _REQUIRES_SANDBOX,
tool = ":find_cfg_probe",
use_execroot_entry_point = False,
)
@@ -50,12 +51,12 @@
name = "runfiles_test",
actual = ":runfiles_location.txt",
expected = "RUNFILES_ONLY",
- target_compatible_with = _REQUIRES_RUNFILES,
+ target_compatible_with = _REQUIRES_SANDBOX,
)
label_capture(
name = "label_capture",
- target_compatible_with = _REQUIRES_RUNFILES,
+ target_compatible_with = _REQUIRES_SANDBOX,
tool = ":find_cfg_probe",
)
@@ -63,7 +64,7 @@
name = "label_capture_test",
actual = ":label_capture_stdout.txt",
expected = "RUNFILES_ONLY",
- target_compatible_with = _REQUIRES_RUNFILES,
+ target_compatible_with = _REQUIRES_SANDBOX,
)
# With use_execroot_entry_point = True, the tool's data dependencies are built
@@ -73,7 +74,6 @@
name = "target_cfg_location",
silent_on_success = False,
stdout = "target_cfg_location.txt",
- target_compatible_with = _REQUIRES_RUNFILES,
tool = ":find_cfg_probe",
use_execroot_entry_point = True,
)
@@ -89,7 +89,6 @@
name = "target_cfg_test",
file1 = ":target_cfg_location.txt",
file2 = ":expected_target_cfg_location.txt",
- target_compatible_with = _REQUIRES_RUNFILES,
)
# Verify that dependencies passed via the srcs parameter are always built for
diff --git a/js/private/test/no_copy_to_bin/BUILD.bazel b/js/private/test/no_copy_to_bin/BUILD.bazel
index ad762ff..3431f8c 100644
--- a/js/private/test/no_copy_to_bin/BUILD.bazel
+++ b/js/private/test/no_copy_to_bin/BUILD.bazel
@@ -7,9 +7,10 @@
data = ["//js/private/test/no_copy_to_bin/subpkg:42.js"],
entry_point = "test_no_copy_to_bin.js",
no_copy_to_bin = ["//js/private/test/no_copy_to_bin/subpkg:42.js"],
- # A file kept out of the bin directory is only laid out next to the program in the runfiles
- # tree, so there is nowhere for the program to require it from on Windows, which does not build
- # one by default.
+ # The program requires this file by a path relative to itself, which node resolves against the
+ # entry point's own directory. A file kept out of the bin directory only sits next to the entry
+ # point in the runfiles tree, and Windows does not build one by default; a runfiles manifest is
+ # no help here since require() does not consult one.
target_compatible_with = select({
"@platforms//os:windows": ["@platforms//:incompatible"],
"//conditions:default": [],
diff --git a/js/private/test/snapshots/launcher.sh b/js/private/test/snapshots/launcher.sh
index d913138..f0059a3 100644
--- a/js/private/test/snapshots/launcher.sh
+++ b/js/private/test/snapshots/launcher.sh
@@ -300,6 +300,20 @@
# can locate runfiles without requiring RUNFILES to be exported.
export RUNFILES_DIR="${RUNFILES_DIR:-$RUNFILES}"
+# Where runfiles trees are not built, which is the default on Windows, RUNFILES_DIR names a
+# directory holding nothing but the manifest and a runfiles library has to read that manifest
+# instead. Bazel does not put RUNFILES_MANIFEST_FILE in a build action's environment
+# (https://github.com/bazelbuild/bazel/issues/7994), so point at the manifest here when the tree
+# was not laid out. Only ever when the file is really there: a library that finds neither a tree
+# nor a manifest reports that better than one sent to a path that does not exist.
+if [ -z "${RUNFILES_MANIFEST_FILE:-}" ] && [ ! -d "$RUNFILES/$JS_BINARY__WORKSPACE" ]; then
+ if [ -f "$RUNFILES/MANIFEST" ]; then
+ export RUNFILES_MANIFEST_FILE="$RUNFILES/MANIFEST"
+ elif [ -f "${RUNFILES}_manifest" ]; then
+ export RUNFILES_MANIFEST_FILE="${RUNFILES}_manifest"
+ fi
+fi
+
JS_BINARY__RUNFILES="$RUNFILES"
export JS_BINARY__RUNFILES