Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions shell/runfiles/runfiles.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand Down
40 changes: 40 additions & 0 deletions tests/runfiles/runfiles_test.bash
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down