Bash,runfiles: add tests
The runfiles library can now discover where the
runfiles are.
Also remove the shebang line from the runfiles
library. This script should never by executed on
its own but be sourced by other Bash scripts.
See https://github.com/bazelbuild/bazel/issues/4460
RELNOTES[NEW]: Bash,runfiles: use the new platform-independent library in `@bazel_tools//tools/bash/runfiles` to access runfiles (data-dependencies). See https://github.com/bazelbuild/bazel/blob/master/tools/bash/runfiles/runfiles.bash for usage information.
Closes #5071.
Change-Id: I94557133e76ecc927646d109e2611d711c363cdc
PiperOrigin-RevId: 193923870
diff --git a/src/test/py/bazel/runfiles_test.py b/src/test/py/bazel/runfiles_test.py
index fb2b0e1..86c40b9 100644
--- a/src/test/py/bazel/runfiles_test.py
+++ b/src/test/py/bazel/runfiles_test.py
@@ -32,21 +32,23 @@
"\n".join(stderr))
def _AssertRunfilesLibraryInBazelToolsRepo(self, family, lang_name):
- for s, t in [
- ("WORKSPACE.mock", "WORKSPACE"),
- ("foo/BUILD.mock", "foo/BUILD"),
- ("foo/foo.py", "foo/foo.py"),
- ("foo/Foo.java", "foo/Foo.java"),
- ("foo/datadep/hello.txt", "foo/datadep/hello.txt"),
- ("bar/BUILD.mock", "bar/BUILD"),
- ("bar/bar.py", "bar/bar.py"),
- ("bar/bar-py-data.txt", "bar/bar-py-data.txt"),
- ("bar/Bar.java", "bar/Bar.java"),
- ("bar/bar-java-data.txt", "bar/bar-java-data.txt"),
- ]:
+ for s, t, exe in [
+ ("WORKSPACE.mock", "WORKSPACE", False),
+ ("foo/BUILD.mock", "foo/BUILD", False),
+ ("foo/foo.py", "foo/foo.py", True),
+ ("foo/Foo.java", "foo/Foo.java", False),
+ ("foo/foo.sh", "foo/foo.sh", True),
+ ("foo/datadep/hello.txt", "foo/datadep/hello.txt", False),
+ ("bar/BUILD.mock", "bar/BUILD", False),
+ ("bar/bar.py", "bar/bar.py", True),
+ ("bar/bar-py-data.txt", "bar/bar-py-data.txt", False),
+ ("bar/Bar.java", "bar/Bar.java", False),
+ ("bar/bar-java-data.txt", "bar/bar-java-data.txt", False),
+ ("bar/bar.sh", "bar/bar.sh", True),
+ ("bar/bar-sh-data.txt", "bar/bar-sh-data.txt", False)]:
self.CopyFile(
- self.Rlocation(
- "io_bazel/src/test/py/bazel/testdata/runfiles_test/" + s), t)
+ self.Rlocation("io_bazel/src/test/py/bazel/testdata/runfiles_test/" +
+ s), t, exe)
exit_code, stdout, stderr = self.RunBazel(["info", "bazel-bin"])
self.AssertExitCode(exit_code, 0, stderr)
@@ -65,7 +67,7 @@
exit_code, stdout, stderr = self.RunProgram(
[bin_path], env_add={"TEST_SRCDIR": "__ignore_me__"})
self.AssertExitCode(exit_code, 0, stderr)
- if len(stdout) != 6:
+ if len(stdout) != 8:
self.fail("stdout: %s" % stdout)
self.assertEqual(stdout[0], "Hello %s Foo!" % lang_name)
@@ -79,7 +81,8 @@
self.assertEqual(lines[0], "world")
i = 2
- for lang in [("py", "Python", "bar.py"), ("java", "Java", "Bar.java")]:
+ for lang in [("py", "Python", "bar.py"), ("java", "Java", "Bar.java"),
+ ("sh", "Bash", "bar.sh")]:
self.assertEqual(stdout[i], "Hello %s Bar!" % lang[1])
six.assertRegex(self, stdout[i + 1],
"^rloc=.*/bar/bar-%s-data.txt" % lang[0])
@@ -99,28 +102,34 @@
def testJavaRunfilesLibraryInBazelToolsRepo(self):
self._AssertRunfilesLibraryInBazelToolsRepo("java", "Java")
+ def testBashRunfilesLibraryInBazelToolsRepo(self):
+ self._AssertRunfilesLibraryInBazelToolsRepo("sh", "Bash")
+
def testRunfilesLibrariesFindRunfilesWithoutEnvvars(self):
- for s, t in [
- ("WORKSPACE.mock", "WORKSPACE"),
- ("bar/BUILD.mock", "bar/BUILD"),
- ("bar/bar.py", "bar/bar.py"),
- ("bar/bar-py-data.txt", "bar/bar-py-data.txt"),
- ("bar/Bar.java", "bar/Bar.java"),
- ("bar/bar-java-data.txt", "bar/bar-java-data.txt"),
+ for s, t, exe in [
+ ("WORKSPACE.mock", "WORKSPACE", False),
+ ("bar/BUILD.mock", "bar/BUILD", False),
+ ("bar/bar.py", "bar/bar.py", True),
+ ("bar/bar-py-data.txt", "bar/bar-py-data.txt", False),
+ ("bar/Bar.java", "bar/Bar.java", False),
+ ("bar/bar-java-data.txt", "bar/bar-java-data.txt", False),
+ ("bar/bar.sh", "bar/bar.sh", True),
+ ("bar/bar-sh-data.txt", "bar/bar-sh-data.txt", False),
]:
self.CopyFile(
- self.Rlocation(
- "io_bazel/src/test/py/bazel/testdata/runfiles_test/" + s), t)
+ self.Rlocation("io_bazel/src/test/py/bazel/testdata/runfiles_test/" +
+ s), t, exe)
exit_code, stdout, stderr = self.RunBazel(["info", "bazel-bin"])
self.AssertExitCode(exit_code, 0, stderr)
bazel_bin = stdout[0]
exit_code, _, stderr = self.RunBazel(
- ["build", "//bar:bar-py", "//bar:bar-java"])
+ ["build", "//bar:bar-py", "//bar:bar-java", "//bar:bar-sh"])
self.AssertExitCode(exit_code, 0, stderr)
- for lang in [("py", "Python", "bar.py"), ("java", "Java", "Bar.java")]:
+ for lang in [("py", "Python", "bar.py"), ("java", "Java", "Bar.java"),
+ ("sh", "Bash", "bar.sh")]:
if test_base.TestBase.IsWindows():
bin_path = os.path.join(bazel_bin, "bar/bar-%s.exe" % lang[0])
else:
@@ -151,25 +160,28 @@
self.assertEqual(lines[0], "data for " + lang[2])
def testRunfilesLibrariesFindRunfilesWithRunfilesManifestEnvvar(self):
- for s, t in [
- ("WORKSPACE.mock", "WORKSPACE"),
- ("bar/BUILD.mock", "bar/BUILD"),
+ for s, t, exe in [
+ ("WORKSPACE.mock", "WORKSPACE", False),
+ ("bar/BUILD.mock", "bar/BUILD", False),
# Note: do not test Python here, because py_binary always needs a
# runfiles tree, even on Windows, because it needs __init__.py files in
# every directory where there may be importable modules, so Bazel always
# needs to create a runfiles tree for py_binary.
- ("bar/Bar.java", "bar/Bar.java"),
- ("bar/bar-java-data.txt", "bar/bar-java-data.txt"),
+ ("bar/Bar.java", "bar/Bar.java", False),
+ ("bar/bar-java-data.txt", "bar/bar-java-data.txt", False),
+ ("bar/bar.sh", "bar/bar.sh", True),
+ ("bar/bar-sh-data.txt", "bar/bar-sh-data.txt", False),
]:
self.CopyFile(
- self.Rlocation(
- "io_bazel/src/test/py/bazel/testdata/runfiles_test/" + s), t)
+ self.Rlocation("io_bazel/src/test/py/bazel/testdata/runfiles_test/" +
+ s), t, exe)
exit_code, stdout, stderr = self.RunBazel(["info", "bazel-bin"])
self.AssertExitCode(exit_code, 0, stderr)
bazel_bin = stdout[0]
- for lang in [("java", "Java")]: # TODO(laszlocsomor): add "cc" when ready.
+ for lang in [("java", "Java"),
+ ("sh", "Bash")]: # TODO(laszlocsomor): add "cc" when ready.
exit_code, _, stderr = self.RunBazel([
"build", "--experimental_enable_runfiles=no", "//bar:bar-" + lang[0]
])
diff --git a/src/test/py/bazel/testdata/runfiles_test/bar/BUILD.mock b/src/test/py/bazel/testdata/runfiles_test/bar/BUILD.mock
index b6d52f7..ab97f8d 100644
--- a/src/test/py/bazel/testdata/runfiles_test/bar/BUILD.mock
+++ b/src/test/py/bazel/testdata/runfiles_test/bar/BUILD.mock
@@ -15,3 +15,10 @@
main_class = "Bar",
deps = ["@bazel_tools//tools/runfiles:java-runfiles"],
)
+
+sh_binary(
+ name = "bar-sh",
+ srcs = ["bar.sh"],
+ data = ["bar-sh-data.txt"],
+ deps = ["@bazel_tools//tools/bash/runfiles"],
+)
diff --git a/src/test/py/bazel/testdata/runfiles_test/bar/bar-sh-data.txt b/src/test/py/bazel/testdata/runfiles_test/bar/bar-sh-data.txt
new file mode 100644
index 0000000..88c9856
--- /dev/null
+++ b/src/test/py/bazel/testdata/runfiles_test/bar/bar-sh-data.txt
@@ -0,0 +1 @@
+data for bar.sh
diff --git a/src/test/py/bazel/testdata/runfiles_test/bar/bar.sh b/src/test/py/bazel/testdata/runfiles_test/bar/bar.sh
new file mode 100755
index 0000000..26d75fa
--- /dev/null
+++ b/src/test/py/bazel/testdata/runfiles_test/bar/bar.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+# Copyright 2018 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+# --- begin runfiles.bash initialization ---
+if [[ "${RUNFILES_MANIFEST_ONLY:-}" != 1 && -z "${RUNFILES_DIR:-}" ]]; then
+ if [[ -f "$0.runfiles_manifest" ]]; then
+ export RUNFILES_MANIFEST_ONLY=1
+ export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+ elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+ export RUNFILES_MANIFEST_ONLY=1
+ export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+ elif [[ -d "$0.runfiles" ]]; then
+ export RUNFILES_DIR="$0.runfiles"
+ fi
+fi
+if [[ "${RUNFILES_MANIFEST_ONLY:-}" == 1 && -f "${RUNFILES_MANIFEST_FILE:-}" ]]; then
+ source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+elif [[ -n "${RUNFILES_DIR:-}" && -d "${RUNFILES_DIR}" ]]; then
+ source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+else
+ echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+ exit 1
+fi
+# --- end runfiles.bash initialization ---
+
+echo "Hello Bash Bar!"
+echo "rloc=$(rlocation "foo_ws/bar/bar-sh-data.txt")"
diff --git a/src/test/py/bazel/testdata/runfiles_test/foo/BUILD.mock b/src/test/py/bazel/testdata/runfiles_test/foo/BUILD.mock
index 48d3108..f35b840 100644
--- a/src/test/py/bazel/testdata/runfiles_test/foo/BUILD.mock
+++ b/src/test/py/bazel/testdata/runfiles_test/foo/BUILD.mock
@@ -3,8 +3,9 @@
srcs = ["foo.py"],
data = [
"datadep/hello.txt",
- "//bar:bar-py",
"//bar:bar-java",
+ "//bar:bar-py",
+ "//bar:bar-sh",
],
main = "foo.py",
deps = ["@bazel_tools//tools/python/runfiles"],
@@ -17,7 +18,20 @@
"datadep/hello.txt",
"//bar:bar-py",
"//bar:bar-java",
+ "//bar:bar-sh",
],
main_class = "Foo",
deps = ["@bazel_tools//tools/runfiles:java-runfiles"],
)
+
+sh_binary(
+ name = "runfiles-sh",
+ srcs = ["foo.sh"],
+ data = [
+ "datadep/hello.txt",
+ "//bar:bar-java",
+ "//bar:bar-py",
+ "//bar:bar-sh",
+ ],
+ deps = ["@bazel_tools//tools/bash/runfiles"],
+)
diff --git a/src/test/py/bazel/testdata/runfiles_test/foo/Foo.java b/src/test/py/bazel/testdata/runfiles_test/foo/Foo.java
index 44a0184..3571de1 100644
--- a/src/test/py/bazel/testdata/runfiles_test/foo/Foo.java
+++ b/src/test/py/bazel/testdata/runfiles_test/foo/Foo.java
@@ -28,7 +28,7 @@
Runfiles r = Runfiles.create();
System.out.println("rloc=" + r.rlocation("foo_ws/foo/datadep/hello.txt"));
- for (String lang : new String[] {"py", "java"}) {
+ for (String lang : new String[] {"py", "java", "sh"}) {
String path = r.rlocation(childBinaryName(lang));
if (path == null || path.isEmpty()) {
throw new IOException("cannot find child binary for " + lang);
diff --git a/src/test/py/bazel/testdata/runfiles_test/foo/foo.py b/src/test/py/bazel/testdata/runfiles_test/foo/foo.py
index 141c67f..d526531 100644
--- a/src/test/py/bazel/testdata/runfiles_test/foo/foo.py
+++ b/src/test/py/bazel/testdata/runfiles_test/foo/foo.py
@@ -53,7 +53,7 @@
else:
env = {}
env.update(r.EnvVars())
- for lang in ["py", "java"]:
+ for lang in ["py", "java", "sh"]:
p = subprocess.Popen(
[r.Rlocation(ChildBinaryName(lang))],
env=env,
diff --git a/src/test/py/bazel/testdata/runfiles_test/foo/foo.sh b/src/test/py/bazel/testdata/runfiles_test/foo/foo.sh
new file mode 100755
index 0000000..6a89cfd
--- /dev/null
+++ b/src/test/py/bazel/testdata/runfiles_test/foo/foo.sh
@@ -0,0 +1,82 @@
+#!/bin/bash
+# Copyright 2018 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -euo pipefail
+# --- begin runfiles.bash initialization ---
+if [[ "${RUNFILES_MANIFEST_ONLY:-}" != 1 && -z "${RUNFILES_DIR:-}" ]]; then
+ if [[ -f "$0.runfiles_manifest" ]]; then
+ export RUNFILES_MANIFEST_ONLY=1
+ export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+ elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+ export RUNFILES_MANIFEST_ONLY=1
+ export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+ elif [[ -d "$0.runfiles" ]]; then
+ export RUNFILES_DIR="$0.runfiles"
+ fi
+fi
+if [[ "${RUNFILES_MANIFEST_ONLY:-}" == 1 && -f "${RUNFILES_MANIFEST_FILE:-}" ]]; then
+ source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+ "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+elif [[ -n "${RUNFILES_DIR:-}" && -d "${RUNFILES_DIR}" ]]; then
+ source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+else
+ echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+ exit 1
+fi
+# --- end runfiles.bash initialization ---
+
+if ! type rlocation >&/dev/null; then
+ echo >&2 "ERROR: rlocation is undefined"
+ exit 1
+fi
+
+case "$(uname -s | tr [:upper:] [:lower:])" in
+msys*|mingw*|cygwin*)
+ function is_windows() { true ; }
+ ;;
+*)
+ function is_windows() { false ; }
+ ;;
+esac
+
+function child_binary_name() {
+ local lang=$1
+ if is_windows; then
+ echo "foo_ws/bar/bar-${lang}.exe"
+ else
+ echo "foo_ws/bar/bar-${lang}"
+ fi
+}
+
+function main() {
+ echo "Hello Bash Foo!"
+ echo "rloc=$(rlocation "foo_ws/foo/datadep/hello.txt")"
+
+ # Run a subprocess, propagate the runfiles envvar to it. The subprocess will
+ # use this process's runfiles manifest or runfiles directory.
+ runfiles_export_envvars
+ if is_windows; then
+ export SYSTEMROOT="${SYSTEMROOT:-}"
+ fi
+ for lang in py java sh; do
+ child_bin="$(rlocation "$(child_binary_name $lang)")"
+ if ! "$child_bin"; then
+ echo >&2 "ERROR: error running bar-$lang"
+ exit 1
+ fi
+ done
+}
+
+main
diff --git a/tools/bash/runfiles/BUILD.tools b/tools/bash/runfiles/BUILD.tools
index 9fcf6ea..3b43a75 100644
--- a/tools/bash/runfiles/BUILD.tools
+++ b/tools/bash/runfiles/BUILD.tools
@@ -1,2 +1,5 @@
-# This package will host the Bash runfiles library.
-# See https://github.com/bazelbuild/bazel/issues/4460
+sh_library(
+ name = "runfiles",
+ srcs = ["runfiles.bash"],
+ visibility = ["//visibility:public"],
+)
diff --git a/tools/bash/runfiles/runfiles.bash b/tools/bash/runfiles/runfiles.bash
index d56a835..6894a8b 100644
--- a/tools/bash/runfiles/runfiles.bash
+++ b/tools/bash/runfiles/runfiles.bash
@@ -1,5 +1,3 @@
-#!/bin/bash
-#
# Copyright 2018 The Bazel Authors. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
@@ -24,6 +22,44 @@
# to the absolute path of the runfiles manifest. RUNFILES_DIR may be unset in
# this case.
# - If RUNFILES_LIB_DEBUG=1 is set, the script will print errors to stderr.
+#
+# USAGE:
+# 1. Depend on this runfiles library from your build rule:
+#
+# sh_binary(
+# name = "my_binary",
+# ...
+# deps = ["@bazel_tools//tools/bash/runfiles"],
+# )
+#
+# 2. Source the runfiles library.
+# The runfiles library itself defines rlocation which you would need to look
+# up the library's runtime location, thus we have a chicken-and-egg problem.
+# Insert the following code snippet to the top of your main script:
+#
+# set -euo pipefail
+# # --- begin runfiles.bash initialization ---
+# if [[ "${RUNFILES_MANIFEST_ONLY:-}" != 1 && -z "${RUNFILES_DIR:-}" ]]; then
+# if [[ -f "$0.runfiles_manifest" ]]; then
+# export RUNFILES_MANIFEST_ONLY=1
+# export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
+# elif [[ -f "$0.runfiles/MANIFEST" ]]; then
+# export RUNFILES_MANIFEST_ONLY=1
+# export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
+# elif [[ -d "$0.runfiles" ]]; then
+# export RUNFILES_DIR="$0.runfiles"
+# fi
+# fi
+# if [[ "${RUNFILES_MANIFEST_ONLY:-}" == 1 && -f "${RUNFILES_MANIFEST_FILE:-}" ]]; then
+# source "$(grep -m1 "^bazel_tools/tools/bash/runfiles/runfiles.bash " \
+# "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-)"
+# elif [[ -n "${RUNFILES_DIR:-}" && -d "${RUNFILES_DIR}" ]]; then
+# source "${RUNFILES_DIR}/bazel_tools/tools/bash/runfiles/runfiles.bash"
+# else
+# echo >&2 "ERROR: cannot find @bazel_tools//tools/bash/runfiles:runfiles.bash"
+# exit 1
+# fi
+# # --- end runfiles.bash initialization ---
case "$(uname -s | tr [:upper:] [:lower:])" in
msys*|mingw*|cygwin*)
diff --git a/tools/bash/runfiles/runfiles_test.bash b/tools/bash/runfiles/runfiles_test.bash
old mode 100644
new mode 100755