From 94bb90b1da9400142d2b241a5ad5d17e59bafa17 Mon Sep 17 00:00:00 2001 From: Andrew Tran Date: Thu, 11 Jun 2026 10:29:09 -0700 Subject: [PATCH 1/2] runfiles.bash: fallback to using directory for relative paths When using the manifest file for `rlocation` and an entry is a relative path, try to resolve it through deriving `RUNFILES_DIR` and using the directory before concluding that the file does not exist. Fixes #46 --- shell/runfiles/runfiles.bash | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/shell/runfiles/runfiles.bash b/shell/runfiles/runfiles.bash index 3dfe3e2..898ef7f 100644 --- a/shell/runfiles/runfiles.bash +++ b/shell/runfiles/runfiles.bash @@ -128,6 +128,31 @@ function __runfiles_escape_grep() { } export -f __runfiles_escape_grep +# When manifest lookup returns a relative path (e.g., from a symlink created +# with ctx.actions.symlink target_path), the -e existence check in +# runfiles_rlocation_checked fails because the path isn't anchored. This helper +# derives the runfiles directory from the manifest path and retries the lookup +# using the original rlocation key, resolving symlinks through the filesystem. +function __runfiles_try_dir_lookup() { + local dir="" + if [[ "${RUNFILES_MANIFEST_FILE}" == *_manifest \ + && -d "${RUNFILES_MANIFEST_FILE%_manifest}" ]]; then + dir="${RUNFILES_MANIFEST_FILE%_manifest}" + elif [[ "${RUNFILES_MANIFEST_FILE}" == */MANIFEST \ + && -d "${RUNFILES_MANIFEST_FILE%/MANIFEST}" ]]; then + dir="${RUNFILES_MANIFEST_FILE%/MANIFEST}" + fi + if [[ -n "$dir" && -e "$dir/$1" ]]; then + if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then + echo >&2 "INFO[runfiles.bash]: rlocation($1): resolved relative manifest entry via directory ($dir), return" + fi + echo "$dir/$1" + return 0 + fi + return 1 +} +export -f __runfiles_try_dir_lookup + # Prints to stdout the runtime location of a data-dependency. # The optional second argument can be used to specify the canonical name of the # repository whose repository mapping should be used to resolve the repository @@ -447,6 +472,9 @@ function runfiles_rlocation_checked() { echo "$candidate" return 0 fi + if [[ "$prefix_result" != /* ]] && __runfiles_try_dir_lookup "$1"; then + return 0 + fi # At this point, the manifest lookup of prefix has been successful, # but the file at the relative path given by the suffix does not # exist. We do not continue the lookup with a shorter prefix for two @@ -476,6 +504,8 @@ function runfiles_rlocation_checked() { echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($result)" fi echo "$result" + elif [[ "$result" != /* ]] && __runfiles_try_dir_lookup "$1"; then + : else if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($result), but file does not exist" From c7472b9c6d3423e290b222b40f0f5f660eb860be Mon Sep 17 00:00:00 2001 From: Andrew Tran Date: Thu, 18 Jun 2026 11:17:57 -0700 Subject: [PATCH 2/2] Add test for relative path based lookup for runfiles.bash The test simulates manifest entries that are relative and would fail the `-e` check if using only the manifest without the RUNFILES_DIR fallback --- tests/runfiles/runfiles_test.bash | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tests/runfiles/runfiles_test.bash b/tests/runfiles/runfiles_test.bash index f738b3d..1274b04 100755 --- a/tests/runfiles/runfiles_test.bash +++ b/tests/runfiles/runfiles_test.bash @@ -512,6 +512,46 @@ EOF [[ "$(rlocation "repo2+/foo/runfile" "my_module++ext+repo1" || echo failed)" == "$tmpdir/repo2+/runfile" ]] || fail } +function test_manifest_based_rlocation_relative_symlink() { + local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" + + # Create the runfiles directory alongside the manifest. + local runfiles_dir="$tmpdir/foo.runfiles" + mkdir -p "$runfiles_dir/a/b" + touch "$runfiles_dir/a/b/target" + mkdir -p "$runfiles_dir/pkg/dir/sub" + touch "$runfiles_dir/pkg/dir/file" + touch "$runfiles_dir/pkg/dir/sub/file" + + # Manifest entries with relative paths as values, simulating + # ctx.actions.symlink(target_path=...) which produces relative symlinks. + # We don't need to create the asctual symlink here, it just needs to be an + # unresolvable relative path in the manifest that would trigger the fallback + cat > "$tmpdir/foo.runfiles_manifest" << EOF +a/b/target ../relative/target +pkg/dir relative_dir +EOF + + export RUNFILES_DIR= + export RUNFILES_MANIFEST_FILE="$tmpdir/foo.runfiles_manifest" + source "$runfiles_lib_path" + + # Exact match: manifest value is relative, but the file exists in the + # runfiles directory at the original key. __runfiles_try_dir_lookup resolves + # it via the directory. + [[ "$(rlocation a/b/target || echo failed)" == "$runfiles_dir/a/b/target" ]] || fail + + # Prefix match: manifest maps "pkg/dir" to a relative path. Looking up a + # file under that prefix constructs a relative candidate that fails -e, then + # __runfiles_try_dir_lookup resolves via the directory. + [[ "$(rlocation pkg/dir/file || echo failed)" == "$runfiles_dir/pkg/dir/file" ]] || fail + [[ "$(rlocation pkg/dir/sub/file || echo failed)" == "$runfiles_dir/pkg/dir/sub/file" ]] || fail + + # Nonexistent files still return empty even with the fallback. + [[ -z "$(rlocation a/b/nonexistent || echo failed)" ]] || fail + [[ -z "$(rlocation pkg/dir/nonexistent || echo failed)" ]] || fail +} + function test_directory_based_envvars() { export RUNFILES_DIR=mock/runfiles export RUNFILES_MANIFEST_FILE=