Dealing with Bazel runfiles is one of the most annoying things about using Bazel. Fortunately, Bazel provides a library to make resolving runfiles from Bash scripts easy.
Properly resolving runfiles is more complicated than it first appears. Many systems
manage runfiles as symbolic links in a runfiles directory, but some use a manifest
file named MANIFEST. To properly resolve a runfile you must be able to handle
both cases.
Within the bazel_tools repsoitory there is a script named runfiles.bash.
It is self-described as a “runfiles lookup library for Bazel-built Bash binaries and tests”.
It provides a function named rlocation() which allows you to resolve the location
of a runfile from any bash script.
To use:
- Add
@bazel_tools//tools/bash/runfilesas a dependency of yoursh_*rule:
sh_binary(
name = "my_binary",
srcs = ["my_binary.sh"],
deps = [
"@bazel_tools//tools/bash/runfiles",
],
)
- Copy-paste the initialization code from
runfiles.bashto th beginning of your script:
# --- begin runfiles.bash initialization v3 ---
# Copy-pasted from the Bazel Bash runfiles library v3.
set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
source "$0.runfiles/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
# --- end runfiles.bash initialization v3 ---
- Use
rlocation()to resolve runfiles locations. Note that runfile paths must start with the name of your workspace:
# Resolve the location of unittest.bash using rlocation
source $(rlocation my_workspace/tools/bash/unittest/unittest.bash) || exit 1
Note: According to Jay Conrod’s blog post Writing Bazel rules: data and runfiles,
the rlocation function is already predeclared in sh_test() rules which means
that loading the runfiles library is not necessary. I verified that this is true
in practice, but I could not find any official documentation about this guarantee.