diff options
author | Laszlo Csomor <laszlocsomor@google.com> | 2018-04-16 03:06:20 -0700 |
---|---|---|
committer | Copybara-Service <copybara-piper@google.com> | 2018-04-16 03:07:39 -0700 |
commit | 0c17f0c79d3c1fdcfb9312b5b06486a45a75c002 (patch) | |
tree | 06560a59b7075b50479d7ffe34ed9d57059537e9 /tools/bash | |
parent | 27e0c0e5406f1b480367332b0fcc9fb1e04bdc98 (diff) |
Bash,runfiles: add runfiles library
See https://github.com/bazelbuild/bazel/issues/4460
Change-Id: I35a0b12ac17fe8bfdce3c7ba8c0dfddb35df7e28
Closes #5014.
Change-Id: I35a0b12ac17fe8bfdce3c7ba8c0dfddb35df7e28
PiperOrigin-RevId: 193010997
Diffstat (limited to 'tools/bash')
-rw-r--r-- | tools/bash/runfiles/BUILD | 13 | ||||
-rw-r--r-- | tools/bash/runfiles/runfiles.bash | 113 | ||||
-rw-r--r-- | tools/bash/runfiles/runfiles_test.bash | 181 |
3 files changed, 307 insertions, 0 deletions
diff --git a/tools/bash/runfiles/BUILD b/tools/bash/runfiles/BUILD index 964802ce9b..adec4cef86 100644 --- a/tools/bash/runfiles/BUILD +++ b/tools/bash/runfiles/BUILD @@ -16,10 +16,23 @@ filegroup( name = "embedded_tools", srcs = [ "BUILD.tools", + "runfiles.bash", ], visibility = ["//tools/bash:__pkg__"], ) +sh_library( + name = "runfiles_lib", + testonly = 1, + srcs = ["runfiles.bash"], +) + +sh_test( + name = "runfiles_test", + srcs = ["runfiles_test.bash"], + deps = [":runfiles_lib"], +) + test_suite( name = "windows_tests", tags = [ diff --git a/tools/bash/runfiles/runfiles.bash b/tools/bash/runfiles/runfiles.bash new file mode 100644 index 0000000000..d56a835bfa --- /dev/null +++ b/tools/bash/runfiles/runfiles.bash @@ -0,0 +1,113 @@ +#!/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. + +# This Bash script defines functions to handle sh_binary/sh_test runfiles. +# +# REQUIREMENTS: +# - The RUNFILES_MANIFEST_FILE and/or the RUNFILES_DIR environment variable must +# be set to the absolute path of the runfiles manifest or the +# <rulename>.runfiles directory, respectively. +# - If RUNFILES_MANIFEST_ONLY=1 is set, then RUNFILES_MANIFEST_FILE must be set +# 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. + +case "$(uname -s | tr [:upper:] [:lower:])" in +msys*|mingw*|cygwin*) + # matches an absolute Windows path + _rlocation_isabs_pattern="^[a-zA-Z]:[/\\]" + ;; +*) + # matches an absolute Unix path + _rlocation_isabs_pattern="^/.*" + ;; +esac + +# Prints to stdout the runtime location of a data-dependency. +function rlocation() { + if [[ "$1" =~ $_rlocation_isabs_pattern ]]; then + # If the path is absolute, print it as-is. + echo $1 + elif [[ "$1" =~ \.\. ]]; then + if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then + echo >&2 "ERROR[runfiles.bash]: rlocation($1): contains uplevel reference" + fi + return 1 + elif [[ "$1" == \\* ]]; then + if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then + echo >&2 "ERROR[runfiles.bash]: rlocation($1): absolute path without" \ + "drive name" + fi + return 1 + else + if [[ "${RUNFILES_MANIFEST_ONLY:-}" == 1 ]]; then + if [[ -n "${RUNFILES_MANIFEST_FILE:-}" \ + && -f "${RUNFILES_MANIFEST_FILE}" ]]; then + grep -m1 "^$1 " "${RUNFILES_MANIFEST_FILE}" | cut -d ' ' -f 2- \ + || return 1 + else + if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then + echo >&2 "ERROR[runfiles.bash]: trying to use manifest-based" \ + "runfiles but RUNFILES_MANIFEST_FILE is unset or" \ + "non-existent" \ + "(RUNFILES_MANIFEST_ONLY=\"${RUNFILES_MANIFEST_ONLY:-}\"," \ + "RUNFILES_DIR=\"${RUNFILES_DIR:-}\")" + fi + return 1 + fi + elif [[ -n "${RUNFILES_DIR:-}" && -d "${RUNFILES_DIR}" ]]; then + echo "${RUNFILES_DIR}/$1" + else + if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then + echo >&2 "ERROR[runfiles.bash]: trying to use directory-based" \ + "runfiles but RUNFILES_DIR is unset or non-existent" \ + "(RUNFILES_MANIFEST_ONLY=\"${RUNFILES_MANIFEST_ONLY:-}\"," \ + "RUNFILES_MANIFEST_FILE=\"${RUNFILES_MANIFEST_FILE:-}\")" + fi + return 1 + fi + fi +} +export -f rlocation + +# Exports the environment variables that subprocesses may need to use runfiles. +# If a subprocess is a Bazel-built binary rule that also uses the runfiles +# libraries under @bazel_tools//tools/bash/runfiles, then that binary needs +# these envvars in order to initialize its own runfiles library. +function runfiles_export_envvars() { + if [[ "${RUNFILES_MANIFEST_ONLY:-}" == 1 ]]; then + if [[ -z "${RUNFILES_MANIFEST_FILE:-}" \ + && ! -f "$RUNFILES_MANIFEST_FILE" ]]; then + return 1 + fi + if [[ -z "${RUNFILES_DIR:-}" ]]; then + if [[ "$RUNFILES_MANIFEST_FILE" == */MANIFEST \ + && -d "${RUNFILES_MANIFEST_FILE%/MANIFEST}" ]]; then + export RUNFILES_DIR="${RUNFILES_MANIFEST_FILE%/MANIFEST}" + elif [[ "$RUNFILES_MANIFEST_FILE" == *runfiles_manifest \ + && -d "${RUNFILES_MANIFEST_FILE%_manifest}" ]]; then + export RUNFILES_DIR="${RUNFILES_MANIFEST_FILE%_manifest}" + fi + fi + fi + # No need to define anything if RUNFILES_MANIFEST_ONLY is not 1: it makes no + # difference whether RUNFILES_DIR is defined or not. + + export RUNFILES_MANIFEST_FILE="${RUNFILES_MANIFEST_FILE:-}" + export RUNFILES_DIR="${RUNFILES_DIR:-}" + export JAVA_RUNFILES="${RUNFILES_DIR:-}" +} +export -f runfiles_export_envvars diff --git a/tools/bash/runfiles/runfiles_test.bash b/tools/bash/runfiles/runfiles_test.bash new file mode 100644 index 0000000000..3d6dd8885c --- /dev/null +++ b/tools/bash/runfiles/runfiles_test.bash @@ -0,0 +1,181 @@ +#!/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 + +function _log_base() { + prefix=$1 + shift + echo >&2 "${prefix}[$(basename "${BASH_SOURCE[0]}"):${BASH_LINENO[1]} ($(date "+%H:%M:%S %z"))] $*" +} + +function fail() { + _log_base "FAILED" "$@" + exit 1 +} + +function log_fail() { + # non-fatal version of fail() + _log_base "FAILED" $* +} + +function log_info() { + _log_base "INFO" $* +} + +which uname >&/dev/null || fail "cannot locate GNU coreutils" + +case "$(uname -s | tr [:upper:] [:lower:])" in +msys*|mingw*|cygwin*) + function is_windows() { true; } + ;; +*) + function is_windows() { false; } + ;; +esac + +function find_runfiles_lib() { + # Unset existing definitions of the functions we want to test. + if type rlocation >&/dev/null; then + unset rlocation + unset runfiles_export_envvars + fi + + if [[ "${RUNFILES_MANIFEST_ONLY:-}" == "1" ]]; then + grep -m1 "^io_bazel/tools/bash/runfiles/runfiles.bash" \ + "${RUNFILES_MANIFEST_FILE}" | cut -d ' ' -f 2- + elif [[ -n "${RUNFILES_DIR:-}" && -d "$RUNFILES_DIR" ]]; then + echo "${RUNFILES_DIR}/io_bazel/tools/bash/runfiles/runfiles.bash" + else + echo >&2 "ERROR: cannot find //tools/bash/runfiles:runfiles.bash" + return 1 + fi +} + +function test_rlocation_call_requires_no_envvars() { + export RUNFILES_DIR=mock/runfiles + export RUNFILES_MANIFEST_FILE= + export RUNFILES_MANIFEST_ONLY= + source "$runfiles_lib_path" || fail +} + +function test_rlocation_argument_validation() { + export RUNFILES_DIR= + export RUNFILES_MANIFEST_FILE= + export RUNFILES_MANIFEST_ONLY= + source "$runfiles_lib_path" + + # Test invalid inputs to make sure rlocation catches these. + if (rlocation "foo/.." >&/dev/null); then + fail + fi + if (rlocation "\\foo" >&/dev/null); then + fail + fi +} + +function test_rlocation_abs_path() { + export RUNFILES_DIR= + export RUNFILES_MANIFEST_FILE= + export RUNFILES_MANIFEST_ONLY= + source "$runfiles_lib_path" + + if is_windows; then + [[ "$(rlocation "c:/Foo")" == "c:/Foo" ]] || fail + [[ "$(rlocation "c:\\Foo")" == "c:\\Foo" ]] || fail + else + [[ "$(rlocation "/Foo")" == "/Foo" ]] || fail + fi +} + +function test_init_manifest_based_runfiles() { + local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" + cat > $tmpdir/foo.runfiles_manifest << 'EOF' +a/b c/d +e/f g h +EOF + + export RUNFILES_DIR= + export RUNFILES_MANIFEST_FILE=$tmpdir/foo.runfiles_manifest + export RUNFILES_MANIFEST_ONLY=1 + source "$runfiles_lib_path" + + [[ -z "$(rlocation a)" ]] || fail + [[ "$(rlocation a/b)" == "c/d" ]] || fail + [[ "$(rlocation e/f)" == "g h" ]] || fail + [[ -z "$(rlocation c/d)" ]] || fail +} + +function test_manifest_based_envvars() { + local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" + echo "a b" > $tmpdir/foo.runfiles_manifest + + export RUNFILES_DIR= + export RUNFILES_MANIFEST_FILE=$tmpdir/foo.runfiles_manifest + export RUNFILES_MANIFEST_ONLY=1 + mkdir -p $tmpdir/foo.runfiles + source "$runfiles_lib_path" + + runfiles_export_envvars + [[ "${RUNFILES_DIR:-}" == "$tmpdir/foo.runfiles" ]] || fail + [[ "${RUNFILES_MANIFEST_FILE:-}" == "$tmpdir/foo.runfiles_manifest" ]] || fail + [[ "${RUNFILES_MANIFEST_ONLY:-}" == 1 ]] || fail +} + +function test_init_directory_based_runfiles() { + local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)" + + export RUNFILES_DIR=${tmpdir}/mock/runfiles + export RUNFILES_MANIFEST_FILE= + export RUNFILES_MANIFEST_ONLY= + source "$runfiles_lib_path" + + mkdir -p "$RUNFILES_DIR" + [[ "$(rlocation a)" == */mock/runfiles/a ]] || fail + [[ "$(rlocation a/b)" == *mock/runfiles/a/b ]] || fail +} + +function test_directory_based_envvars() { + export RUNFILES_DIR=mock/runfiles + export RUNFILES_MANIFEST_FILE= + export RUNFILES_MANIFEST_ONLY= + source "$runfiles_lib_path" + + runfiles_export_envvars + [[ "${RUNFILES_DIR:-}" == "mock/runfiles" ]] || fail + [[ -z "${RUNFILES_MANIFEST_FILE:-}" ]] || fail + [[ -z "${RUNFILES_MANIFEST_ONLY:-}" ]] || fail +} + +function main() { + local -r manifest_only="${RUNFILES_MANIFEST_ONLY:-}" + local -r manifest_file="${RUNFILES_MANIFEST_FILE:-}" + local -r dir="${RUNFILES_DIR:-}" + local -r runfiles_lib_path=$(find_runfiles_lib) + + local -r tests=$(declare -F | grep " -f test" | awk '{print $3}') + local failure=0 + for t in $tests; do + export RUNFILES_MANIFEST_ONLY="$manifest_only" + export RUNFILES_MANIFEST_FILE="$manifest_file" + export RUNFILES_DIR="$dir" + if ! ($t); then + failure=1 + fi + done + return $failure +} + +main |