[8.3.0] Move Bash runfiles libraries out of Bazel (#26304)

The Bash libraries in `@bazel_tools` are replaced with aliases to
`@rules_shell//shell/runfiles`.

This requires skipping load visibility checks for resolved WORKSPACE
files, which are going away anyway.

Closes #24219.

PiperOrigin-RevId: 727843225
Change-Id: I00744bd30c637cae450d63b859b87abf89e56b7c 
(cherry picked from commit b8b91e4f646c8052d93f73293d604e6296e1759a)

Fixes #26303
diff --git a/MODULE.bazel b/MODULE.bazel
index d400120..4efd74d 100644
--- a/MODULE.bazel
+++ b/MODULE.bazel
@@ -33,7 +33,7 @@
 bazel_dep(name = "googletest", version = "1.15.2", repo_name = "com_google_googletest")
 bazel_dep(name = "with_cfg.bzl", version = "0.6.0")
 bazel_dep(name = "abseil-cpp", version = "20240722.0.bcr.2")
-bazel_dep(name = "rules_shell", version = "0.2.0")
+bazel_dep(name = "rules_shell", version = "0.3.0")
 bazel_dep(name = "chicory", version = "1.1.0")
 
 # Depend on apple_support first and then rules_cc so that the Xcode toolchain
diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock
index 49363c78..bc9a652 100644
--- a/MODULE.bazel.lock
+++ b/MODULE.bazel.lock
@@ -213,7 +213,8 @@
     "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
     "https://bcr.bazel.build/modules/rules_python/0.40.0/source.json": "939d4bd2e3110f27bfb360292986bb79fd8dcefb874358ccd6cdaa7bda029320",
     "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
-    "https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95",
+    "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
+    "https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3",
     "https://bcr.bazel.build/modules/rules_swift/1.16.0/MODULE.bazel": "4a09f199545a60d09895e8281362b1ff3bb08bbde69c6fc87aff5b92fcc916ca",
     "https://bcr.bazel.build/modules/rules_swift/1.18.0/MODULE.bazel": "a6aba73625d0dc64c7b4a1e831549b6e375fbddb9d2dde9d80c9de6ec45b24c9",
     "https://bcr.bazel.build/modules/rules_swift/2.1.1/MODULE.bazel": "494900a80f944fc7aa61500c2073d9729dff0b764f0e89b824eb746959bc1046",
diff --git a/src/BUILD b/src/BUILD
index 995c148..a0faacc 100644
--- a/src/BUILD
+++ b/src/BUILD
@@ -649,6 +649,7 @@
         "@rules_license//:WORKSPACE",
         "@rules_pkg//:WORKSPACE",
         "@rules_python//:WORKSPACE",
+        "@rules_shell//shell:rules_bzl",
         "@rules_testing//:LICENSE",
     ] + select({
         "//src/conditions:linux_x86_64": ["@remotejdk%s_linux//:WORKSPACE" % v for v in JDK_VERSIONS],
diff --git a/src/MODULE.tools b/src/MODULE.tools
index 5b45c31..dca7fde 100644
--- a/src/MODULE.tools
+++ b/src/MODULE.tools
@@ -41,7 +41,7 @@
 bazel_dep(name = "rules_java", version = "8.12.0")
 bazel_dep(name = "rules_cc", version = "0.1.1")
 bazel_dep(name = "rules_python", version = "0.40.0")
-bazel_dep(name = "rules_shell", version = "0.2.0")
+bazel_dep(name = "rules_shell", version = "0.3.0")
 # add rules_android
 # add apple_support
 
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BUILD b/src/main/java/com/google/devtools/build/lib/bazel/rules/BUILD
index e973166..96c98da 100644
--- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BUILD
+++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BUILD
@@ -141,6 +141,9 @@
         protobuf_deps()
         load("@rules_java//java:repositories.bzl", "rules_java_toolchains")
         rules_java_toolchains()
+        load("@rules_shell//shell:repositories.bzl", "rules_shell_dependencies", "rules_shell_toolchains")
+        rules_shell_dependencies()
+        rules_shell_toolchains()
     """,
     preamble = """
         load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
@@ -153,6 +156,7 @@
         "rules_cc",  # For auto-load cc rules symbols
         "rules_python",  # For auto-load python rules symbols
         "rules_license",  # for license attestations
+        "rules_shell",  # For auto-load of shell rules symbols
     ],
     use_maybe = 1,
     visibility = ["//:__pkg__"],
diff --git a/src/test/tools/bzlmod/MODULE.bazel.lock b/src/test/tools/bzlmod/MODULE.bazel.lock
index c4559dc..79dcf00 100644
--- a/src/test/tools/bzlmod/MODULE.bazel.lock
+++ b/src/test/tools/bzlmod/MODULE.bazel.lock
@@ -121,7 +121,8 @@
     "https://bcr.bazel.build/modules/rules_python/0.40.0/MODULE.bazel": "9d1a3cd88ed7d8e39583d9ffe56ae8a244f67783ae89b60caafc9f5cf318ada7",
     "https://bcr.bazel.build/modules/rules_python/0.40.0/source.json": "939d4bd2e3110f27bfb360292986bb79fd8dcefb874358ccd6cdaa7bda029320",
     "https://bcr.bazel.build/modules/rules_shell/0.2.0/MODULE.bazel": "fda8a652ab3c7d8fee214de05e7a9916d8b28082234e8d2c0094505c5268ed3c",
-    "https://bcr.bazel.build/modules/rules_shell/0.2.0/source.json": "7f27af3c28037d9701487c4744b5448d26537cc66cdef0d8df7ae85411f8de95",
+    "https://bcr.bazel.build/modules/rules_shell/0.3.0/MODULE.bazel": "de4402cd12f4cc8fda2354fce179fdb068c0b9ca1ec2d2b17b3e21b24c1a937b",
+    "https://bcr.bazel.build/modules/rules_shell/0.3.0/source.json": "c55ed591aa5009401ddf80ded9762ac32c358d2517ee7820be981e2de9756cf3",
     "https://bcr.bazel.build/modules/stardoc/0.5.1/MODULE.bazel": "1a05d92974d0c122f5ccf09291442580317cdd859f07a8655f1db9a60374f9f8",
     "https://bcr.bazel.build/modules/stardoc/0.5.3/MODULE.bazel": "c7f6948dae6999bf0db32c1858ae345f112cacf98f174c7a8bb707e41b974f1c",
     "https://bcr.bazel.build/modules/stardoc/0.5.6/MODULE.bazel": "c43dabc564990eeab55e25ed61c07a1aadafe9ece96a4efabb3f8bf9063b71ef",
diff --git a/tools/bash/runfiles/BUILD b/tools/bash/runfiles/BUILD
index 5186f35..5f91184 100644
--- a/tools/bash/runfiles/BUILD
+++ b/tools/bash/runfiles/BUILD
@@ -1,6 +1,3 @@
-load("@rules_shell//shell:sh_library.bzl", "sh_library")
-load("@rules_shell//shell:sh_test.bzl", "sh_test")
-
 package(default_visibility = ["//visibility:private"])
 
 filegroup(
@@ -19,19 +16,6 @@
     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"],
-)
diff --git a/tools/bash/runfiles/BUILD.tools b/tools/bash/runfiles/BUILD.tools
index 71ad084..8eccb15 100644
--- a/tools/bash/runfiles/BUILD.tools
+++ b/tools/bash/runfiles/BUILD.tools
@@ -1,7 +1,5 @@
-load("@rules_shell//shell:sh_library.bzl", "sh_library")
-
-sh_library(
+alias(
     name = "runfiles",
-    srcs = ["runfiles.bash"],
+    actual = "@rules_shell//shell/runfiles",
     visibility = ["//visibility:public"],
 )
diff --git a/tools/bash/runfiles/runfiles.bash b/tools/bash/runfiles/runfiles.bash
deleted file mode 100644
index 8e1f944..0000000
--- a/tools/bash/runfiles/runfiles.bash
+++ /dev/null
@@ -1,468 +0,0 @@
-# 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.
-
-# Runfiles lookup library for Bazel-built Bash binaries and tests, version 3.
-#
-# VERSION HISTORY:
-# - version 3: Fixes a bug in the init code on macOS and makes the library aware
-#              of Bzlmod repository mappings.
-#   Features:
-#     - With Bzlmod enabled, rlocation now takes the repository mapping of the
-#       Bazel repository containing the calling script into account when
-#       looking up runfiles. The new, optional second argument to rlocation can
-#       be used to specify the canonical name of the Bazel repository to use
-#       instead of this default. The new runfiles_current_repository function
-#       can be used to obtain the canonical name of the N-th caller's Bazel
-#       repository.
-#   Fixed:
-#     - Sourcing a shell script that contains the init code from a shell script
-#       that itself contains the init code no longer fails on macOS.
-#   Compatibility:
-#     - The init script and the runfiles library are backwards and forwards
-#       compatible with version 2.
-# - version 2: Shorter init code.
-#   Features:
-#     - "set -euo pipefail" only at end of init code.
-#       "set -e" breaks the source <path1> || source <path2> || ... scheme on
-#       macOS, because it terminates if path1 does not exist.
-#     - Not exporting any environment variables in init code.
-#       This is now done in runfiles.bash itself.
-#   Compatibility:
-#     - The v1 init code can load the v2 library, i.e. if you have older source
-#       code (still using v1 init) then you can build it with newer Bazel (which
-#       contains the v2 library).
-#     - The reverse is not true: the v2 init code CANNOT load the v1 library,
-#       i.e. if your project (or any of its external dependencies) use v2 init
-#       code, then you need a newer Bazel version (which contains the v2
-#       library).
-# - version 1: Original Bash runfiles library.
-#
-# ENVIRONMENT:
-# - If RUNFILES_LIB_DEBUG=1 is set, the script will print diagnostic messages 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:
-#
-#       # --- 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
-#       # shellcheck disable=SC1090
-#       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 ---
-#
-#
-# 3.  Use rlocation to look up runfile paths.
-#
-#       cat "$(rlocation my_workspace/path/to/my/data.txt)"
-#
-
-if [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
-  if [[ -f "$0.runfiles_manifest" ]]; then
-    export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
-  elif [[ -f "$0.runfiles/MANIFEST" ]]; then
-    export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
-  elif [[ -f "$0.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash" ]]; then
-    export RUNFILES_DIR="$0.runfiles"
-  fi
-fi
-
-case "$(uname -s | tr [:upper:] [:lower:])" in
-msys*|mingw*|cygwin*)
-  # matches an absolute Windows path
-  export _RLOCATION_ISABS_PATTERN="^[a-zA-Z]:[/\\]"
-  # Windows paths are case insensitive and Bazel and MSYS2 capitalize differently, so we can't
-  # assume that all paths are in the same native case.
-  export _RLOCATION_GREP_CASE_INSENSITIVE_ARGS=-i
-  ;;
-*)
-  # matches an absolute Unix path
-  export _RLOCATION_ISABS_PATTERN="^/[^/].*"
-  export _RLOCATION_GREP_CASE_INSENSITIVE_ARGS=
-  ;;
-esac
-
-# Does not exit with a non-zero exit code if no match is found and performs a case-insensitive
-# search on Windows.
-function __runfiles_maybe_grep() {
-  grep $_RLOCATION_GREP_CASE_INSENSITIVE_ARGS "$@" || test $? = 1;
-}
-export -f __runfiles_maybe_grep
-
-# 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
-# part of the provided path. If not specified, the repository of the caller is
-# used.
-function rlocation() {
-  if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-    echo >&2 "INFO[runfiles.bash]: rlocation($1): start"
-  fi
-  if [[ "$1" =~ $_RLOCATION_ISABS_PATTERN ]]; then
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "INFO[runfiles.bash]: rlocation($1): absolute path, return"
-    fi
-    # If the path is absolute, print it as-is.
-    echo "$1"
-    return 0
-  elif [[ "$1" == ../* || "$1" == */.. || "$1" == ./* || "$1" == */./* || "$1" == "*/." || "$1" == *//* ]]; then
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "ERROR[runfiles.bash]: rlocation($1): path is not normalized"
-    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
-  fi
-
-  if [[ -f "$RUNFILES_REPO_MAPPING" ]]; then
-    local -r target_repo_apparent_name=$(echo "$1" | cut -d / -f 1)
-     # Use -s to get an empty remainder if the argument does not contain a slash.
-    # The repo mapping should not be applied to single segment paths, which may
-    # be root symlinks.
-    local -r remainder=$(echo "$1" | cut -s -d / -f 2-)
-    if [[ -n "$remainder" ]]; then
-      if [[ -z "${2+x}" ]]; then
-        local -r source_repo=$(runfiles_current_repository 2)
-      else
-        local -r source_repo=$2
-      fi
-      if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-        echo >&2 "INFO[runfiles.bash]: rlocation($1): looking up canonical name for ($target_repo_apparent_name) from ($source_repo) in ($RUNFILES_REPO_MAPPING)"
-      fi
-      local -r target_repo=$(__runfiles_maybe_grep -m1 "^$source_repo,$target_repo_apparent_name," "$RUNFILES_REPO_MAPPING" | cut -d , -f 3)
-      if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-        echo >&2 "INFO[runfiles.bash]: rlocation($1): canonical name of target repo is ($target_repo)"
-      fi
-      if [[ -n "$target_repo" ]]; then
-        local -r rlocation_path="$target_repo/$remainder"
-      else
-        local -r rlocation_path="$1"
-      fi
-    else
-      local -r rlocation_path="$1"
-    fi
-  else
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "INFO[runfiles.bash]: rlocation($1): not using repository mapping ($RUNFILES_REPO_MAPPING) since it does not exist"
-    fi
-    local -r rlocation_path="$1"
-  fi
-
-  runfiles_rlocation_checked "$rlocation_path"
-}
-export -f rlocation
-
-# Exports the environment variables that subprocesses need in order to use
-# runfiles.
-# If a subprocess is a Bazel-built binary rule that also uses the runfiles
-# libraries under @bazel_tools//tools/<lang>/runfiles, then that binary needs
-# these envvars in order to initialize its own runfiles library.
-function runfiles_export_envvars() {
-  if [[ ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" \
-        && ! -d "${RUNFILES_DIR:-/dev/null}" ]]; then
-    return 1
-  fi
-
-  if [[ ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
-    if [[ -f "$RUNFILES_DIR/MANIFEST" ]]; then
-      export RUNFILES_MANIFEST_FILE="$RUNFILES_DIR/MANIFEST"
-    elif [[ -f "${RUNFILES_DIR}_manifest" ]]; then
-      export RUNFILES_MANIFEST_FILE="${RUNFILES_DIR}_manifest"
-    else
-      export RUNFILES_MANIFEST_FILE=
-    fi
-  elif [[ ! -d "${RUNFILES_DIR:-/dev/null}" ]]; then
-    if [[ "$RUNFILES_MANIFEST_FILE" == */MANIFEST \
-          && -d "${RUNFILES_MANIFEST_FILE%/MANIFEST}" ]]; then
-      export RUNFILES_DIR="${RUNFILES_MANIFEST_FILE%/MANIFEST}"
-      export JAVA_RUNFILES="$RUNFILES_DIR"
-    elif [[ "$RUNFILES_MANIFEST_FILE" == *_manifest \
-          && -d "${RUNFILES_MANIFEST_FILE%_manifest}" ]]; then
-      export RUNFILES_DIR="${RUNFILES_MANIFEST_FILE%_manifest}"
-      export JAVA_RUNFILES="$RUNFILES_DIR"
-    else
-      export RUNFILES_DIR=
-    fi
-  fi
-}
-export -f runfiles_export_envvars
-
-# Returns the canonical name of the Bazel repository containing the script that
-# calls this function.
-# The optional argument N, which defaults to 1, can be used to return the
-# canonical name of the N-th caller instead.
-#
-# Note: This function only works correctly with Bzlmod enabled. Without Bzlmod,
-# its return value is ignored if passed to rlocation.
-function runfiles_current_repository() {
-  local -r idx=${1:-1}
-  local -r raw_caller_path="${BASH_SOURCE[$idx]}"
-  # Make the caller path absolute if needed to handle the case where the script is run directly
-  # from bazel-bin, with working directory a subdirectory of bazel-bin.
-  if [[ "$raw_caller_path" =~ $_RLOCATION_ISABS_PATTERN ]]; then
-    local -r caller_path="$raw_caller_path"
-  else
-    local -r caller_path="$(cd $(dirname "$raw_caller_path"); pwd)/$(basename "$raw_caller_path")"
-  fi
-  if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-    echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): caller's path is ($caller_path)"
-  fi
-
-  local rlocation_path=
-
-  # If the runfiles manifest exists, search for an entry with target the caller's path.
-  if [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
-    # Escape $caller_path for use in the grep regex below. Also replace \ with / since the manifest
-    # uses / as the path separator even on Windows.
-    local -r normalized_caller_path="$(echo "$caller_path" | sed 's|\\\\*|/|g')"
-    local -r escaped_caller_path="$(echo "$normalized_caller_path" | sed 's/[.[\*^$]/\\&/g')"
-    rlocation_path=$(__runfiles_maybe_grep -m1 "^[^ ]* ${escaped_caller_path}$" "${RUNFILES_MANIFEST_FILE}" | cut -d ' ' -f 1)
-    if [[ -z "$rlocation_path" ]]; then
-      if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-        echo >&2 "ERROR[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) is not the target of an entry in the runfiles manifest ($RUNFILES_MANIFEST_FILE)"
-      fi
-      # The binary may also be run directly from bazel-bin or bazel-out.
-      local -r repository=$(echo "$normalized_caller_path" | __runfiles_maybe_grep -E -o '(^|/)(bazel-out/[^/]+/bin|bazel-bin)/external/[^/]+/' | tail -1 | awk -F/ '{print $(NF-1)}')
-      if [[ -n "$repository" ]]; then
-        if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-          echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) lies in repository ($repository) (parsed exec path)"
-        fi
-        echo "$repository"
-      else
-        if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-          echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) lies in the main repository (parsed exec path)"
-        fi
-        echo ""
-      fi
-      return 1
-    else
-      if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-        echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) is the target of ($rlocation_path) in the runfiles manifest"
-      fi
-    fi
-  fi
-
-  # If the runfiles directory exists, check if the caller's path is of the form
-  # $RUNFILES_DIR/rlocation_path and if so, set $rlocation_path.
-  if [[ -z "$rlocation_path" && -d "${RUNFILES_DIR:-/dev/null}" ]]; then
-    normalized_caller_path="$(echo "$caller_path" | sed 's|\\\\*|/|g')"
-    normalized_dir="$(echo "${RUNFILES_DIR%[\/]}" | sed 's|\\\\*|/|g')"
-    if [[ -n "${_RLOCATION_GREP_CASE_INSENSITIVE_ARGS}" ]]; then
-      # When comparing file paths insensitively, also normalize the case of the prefixes.
-      normalized_caller_path=$(echo "$normalized_caller_path" | tr '[:upper:]' '[:lower:]')
-      normalized_dir=$(echo "$normalized_dir" | tr '[:upper:]' '[:lower:]')
-    fi
-    if [[ "$normalized_caller_path" == "$normalized_dir"/* ]]; then
-      rlocation_path=${normalized_caller_path:${#normalized_dir}}
-      rlocation_path=${rlocation_path:1}
-    fi
-    if [[ -z "$rlocation_path" ]]; then
-      if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-        echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) does not lie under the runfiles directory ($normalized_dir)"
-      fi
-      # The only shell script that is not executed from the runfiles directory (if it is populated)
-      # is the sh_binary entrypoint. Parse its path under the execroot, using the last match to
-      # allow for nested execroots (e.g. in Bazel integration tests). The binary may also be run
-      # directly from bazel-bin.
-      local -r repository=$(echo "$normalized_caller_path" | __runfiles_maybe_grep -E -o '(^|/)(bazel-out/[^/]+/bin|bazel-bin)/external/[^/]+/' | tail -1 | awk -F/ '{print $(NF-1)}')
-      if [[ -n "$repository" ]]; then
-        if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-          echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) lies in repository ($repository) (parsed exec path)"
-        fi
-        echo "$repository"
-      else
-        if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-          echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($normalized_caller_path) lies in the main repository (parsed exec path)"
-        fi
-        echo ""
-      fi
-      return 0
-    else
-      if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-        echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($caller_path) has path ($rlocation_path) relative to the runfiles directory ($RUNFILES_DIR)"
-      fi
-    fi
-  fi
-
-  if [[ -z "$rlocation_path" ]]; then
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "ERROR[runfiles.bash]: runfiles_current_repository($idx): cannot determine repository for ($caller_path) since neither the runfiles directory (${RUNFILES_DIR:-}) nor the runfiles manifest (${RUNFILES_MANIFEST_FILE:-}) exist"
-    fi
-    return 1
-  fi
-
-  if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-    echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($caller_path) corresponds to rlocation path ($rlocation_path)"
-  fi
-  # Normalize the rlocation path to be of the form repo/pkg/file.
-  rlocation_path=${rlocation_path#_main/external/}
-  rlocation_path=${rlocation_path#_main/../}
-  local -r repository=$(echo "$rlocation_path" | cut -d / -f 1)
-  if [[ "$repository" == _main ]]; then
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($rlocation_path) lies in the main repository"
-    fi
-    echo ""
-  else
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "INFO[runfiles.bash]: runfiles_current_repository($idx): ($rlocation_path) lies in repository ($repository)"
-    fi
-    echo "$repository"
-  fi
-}
-export -f runfiles_current_repository
-
-function runfiles_rlocation_checked() {
-  # FIXME: If the runfiles lookup fails, the exit code of this function is 0 if
-  #  and only if the runfiles manifest exists. In particular, the exit code
-  #  behavior is not consistent across platforms.
-  if [[ -e "${RUNFILES_DIR:-/dev/null}/$1" ]]; then
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "INFO[runfiles.bash]: rlocation($1): found under RUNFILES_DIR ($RUNFILES_DIR), return"
-    fi
-    echo "${RUNFILES_DIR}/$1"
-  elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "INFO[runfiles.bash]: rlocation($1): looking in RUNFILES_MANIFEST_FILE ($RUNFILES_MANIFEST_FILE)"
-    fi
-    # If the rlocation path contains a space or newline, it needs to be prefixed
-    # with a space and spaces, newlines, and backslashes have to be escaped as
-    # \s, \n, and \b.
-    if [[ "$1" == *" "* || "$1" == *$'\n'* ]]; then
-      local search_prefix=" $(echo -n "$1" | sed 's/\\/\\b/g; s/ /\\s/g')"
-      search_prefix="${search_prefix//$'\n'/\\n}"
-      local escaped=true
-      if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-        echo >&2 "INFO[runfiles.bash]: rlocation($1): using escaped search prefix ($search_prefix)"
-      fi
-    else
-      local search_prefix="$1"
-      local escaped=false
-    fi
-    # The extra space below is added because cut counts from 1.
-    local trim_length=$(echo -n "$search_prefix  " | wc -c | tr -d ' ')
-    # Escape the search prefix for use in the grep regex below *after*
-    # determining the trim length.
-    local result=$(__runfiles_maybe_grep -m1 "^$(echo -n "$search_prefix" | sed 's/[.[\*^$]/\\&/g') " "${RUNFILES_MANIFEST_FILE}" | cut -b "${trim_length}-")
-    if [[ -z "$result" ]]; then
-      # If path references a runfile that lies under a directory that itself
-      # is a runfile, then only the directory is listed in the manifest. Look
-      # up all prefixes of path in the manifest and append the relative path
-      # from the prefix if there is a match.
-      local prefix="$1"
-      local prefix_result=
-      local new_prefix=
-      while true; do
-        new_prefix="${prefix%/*}"
-        [[ "$new_prefix" == "$prefix" ]] && break
-        prefix="$new_prefix"
-        if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-          echo >&2 "INFO[runfiles.bash]: rlocation($1): looking for prefix ($prefix)"
-        fi
-        if [[ "$prefix" == *" "* || "$prefix" == *$'\n'* ]]; then
-          search_prefix=" $(echo -n "$prefix" | sed 's/\\/\\b/g; s/ /\\s/g')"
-          search_prefix="${search_prefix//$'\n'/\\n}"
-          escaped=true
-          if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-            echo >&2 "INFO[runfiles.bash]: rlocation($1): using escaped search prefix ($search_prefix)"
-          fi
-        else
-          search_prefix="$prefix"
-          escaped=false
-        fi
-        # The extra space below is added because cut counts from 1.
-        trim_length=$(echo -n "$search_prefix  " | wc -c)
-        prefix_result=$(__runfiles_maybe_grep -m1 "$(echo -n "$search_prefix" | sed 's/[.[\*^$]/\\&/g') " "${RUNFILES_MANIFEST_FILE}" | cut -b ${trim_length}-)
-        if [[ "$escaped" = true ]]; then
-          prefix_result="${prefix_result//\\n/$'\n'}"
-          prefix_result="${prefix_result//\\b/\\}"
-        fi
-        [[ -z "$prefix_result" ]] && continue
-        local -r candidate="${prefix_result}${1#"${prefix}"}"
-        if [[ -e "$candidate" ]]; then
-          if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-            echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($candidate) via prefix ($prefix)"
-          fi
-          echo "$candidate"
-          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
-        # reasons:
-        # 1. Manifests generated by Bazel never contain a path that is a
-        #    prefix of another path.
-        # 2. Runfiles libraries for other languages do not check for file
-        #    existence and would have returned the non-existent path. It seems
-        #    better to return no path rather than a potentially different,
-        #    non-empty path.
-        if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-          echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($candidate) via prefix ($prefix), but file does not exist"
-        fi
-        break
-      done
-      if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-        echo >&2 "INFO[runfiles.bash]: rlocation($1): not found in manifest"
-      fi
-      echo ""
-    else
-      if [[ "$escaped" = true ]]; then
-        result="${result//\\n/$'\n'}"
-        result="${result//\\b/\\}"
-      fi
-      if [[ -e "$result" ]]; then
-        if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-          echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($result)"
-        fi
-        echo "$result"
-      else
-        if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-          echo >&2 "INFO[runfiles.bash]: rlocation($1): found in manifest as ($result), but file does not exist"
-        fi
-        echo ""
-      fi
-    fi
-  else
-    if [[ "${RUNFILES_LIB_DEBUG:-}" == 1 ]]; then
-      echo >&2 "ERROR[runfiles.bash]: cannot look up runfile \"$1\" " \
-               "(RUNFILES_DIR=\"${RUNFILES_DIR:-}\"," \
-               "RUNFILES_MANIFEST_FILE=\"${RUNFILES_MANIFEST_FILE:-}\")"
-    fi
-    return 1
-  fi
-}
-export -f runfiles_rlocation_checked
-
-export RUNFILES_REPO_MAPPING=$(runfiles_rlocation_checked _repo_mapping 2> /dev/null)
diff --git a/tools/bash/runfiles/runfiles_test.bash b/tools/bash/runfiles/runfiles_test.bash
deleted file mode 100755
index 43697e5..0000000
--- a/tools/bash/runfiles/runfiles_test.bash
+++ /dev/null
@@ -1,457 +0,0 @@
-#!/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 [[ ! -d "${RUNFILES_DIR:-/dev/null}" && ! -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
-    if [[ -f "$0.runfiles_manifest" ]]; then
-      export RUNFILES_MANIFEST_FILE="$0.runfiles_manifest"
-    elif [[ -f "$0.runfiles/MANIFEST" ]]; then
-      export RUNFILES_MANIFEST_FILE="$0.runfiles/MANIFEST"
-    elif [[ -f "$0.runfiles/_main/tools/bash/runfiles/runfiles.bash" ]]; then
-      export RUNFILES_DIR="$0.runfiles"
-    fi
-  fi
-  if [[ -f "${RUNFILES_DIR:-/dev/null}/_main/tools/bash/runfiles/runfiles.bash" ]]; then
-    echo "${RUNFILES_DIR}/_main/tools/bash/runfiles/runfiles.bash"
-  elif [[ -f "${RUNFILES_MANIFEST_FILE:-/dev/null}" ]]; then
-    grep -m1 "^_main/tools/bash/runfiles/runfiles.bash " \
-        "$RUNFILES_MANIFEST_FILE" | cut -d ' ' -f 2-
-  else
-    echo >&2 "ERROR: cannot find //tools/bash/runfiles:runfiles.bash"
-    exit 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
-  if rlocation "foo/../bar" >&/dev/null; then
-    fail
-  fi
-  if rlocation "./foo" >&/dev/null; then
-    fail
-  fi
-  if rlocation "foo/." >&/dev/null; then
-    fail
-  fi
-  if rlocation "foo/./bar" >&/dev/null; then
-    fail
-  fi
-  if rlocation "//foo" >&/dev/null; then
-    fail
-  fi
-  if rlocation "foo//" >&/dev/null; then
-    fail
-  fi
-  if rlocation "foo//bar" >&/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" || echo failed)" == "c:/Foo" ]] || fail
-    [[ "$(rlocation "c:\\Foo" || echo failed)" == "c:\\Foo" ]] || fail
-  else
-    [[ "$(rlocation "/Foo" || echo failed)" == "/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 $tmpdir/c/d
-e/f $tmpdir/g h
-y $tmpdir/y
-c/dir $tmpdir/dir
-unresolved $tmpdir/unresolved
- h/\si $tmpdir/ j k
- h/\s\bi $tmpdir/ j k b
- h/\n\bi $tmpdir/ \bnj k \na
- dir\swith\sspaces $tmpdir/dir with spaces
- space\snewline\nbackslash\b_dir $tmpdir/space newline\nbackslash\ba
-EOF
-  mkdir "${tmpdir}/c"
-  mkdir "${tmpdir}/y"
-  mkdir -p "${tmpdir}/dir/deeply/nested"
-  touch "${tmpdir}/c/d" "${tmpdir}/g h"
-  touch "${tmpdir}/dir/file"
-  ln -s /does/not/exist "${tmpdir}/dir/unresolved"
-  touch "${tmpdir}/dir/deeply/nested/file"
-  touch "${tmpdir}/dir/deeply/nested/file with spaces"
-  ln -s /does/not/exist "${tmpdir}/unresolved"
-  touch "${tmpdir}/ j k"
-  touch "${tmpdir}/ j k b"
-  mkdir -p "${tmpdir}/dir with spaces/nested"
-  touch "${tmpdir}/dir with spaces/nested/file"
-  if ! is_windows; then
-    touch "${tmpdir}/ \nj k "$'\n'a
-    mkdir -p "${tmpdir}/space newline"$'\n'"backslash\a"
-    touch "${tmpdir}/space newline"$'\n'"backslash\a/f i\le"
-  fi
-
-  export RUNFILES_DIR=
-  export RUNFILES_MANIFEST_FILE=$tmpdir/foo.runfiles_manifest
-  source "$runfiles_lib_path"
-
-  [[ -z "$(rlocation a || echo failed)" ]] || fail
-  [[ -z "$(rlocation c/d || echo failed)" ]] || fail
-  [[ "$(rlocation a/b || echo failed)" == "$tmpdir/c/d" ]] || fail
-  [[ "$(rlocation e/f || echo failed)" == "$tmpdir/g h" ]] || fail
-  [[ "$(rlocation y || echo failed)" == "$tmpdir/y" ]] || fail
-  [[ -z "$(rlocation c || echo failed)" ]] || fail
-  [[ -z "$(rlocation c/di || echo failed)" ]] || fail
-  [[ "$(rlocation c/dir || echo failed)" == "$tmpdir/dir" ]] || fail
-  [[ "$(rlocation c/dir/file || echo failed)" == "$tmpdir/dir/file" ]] || fail
-  [[ -z "$(rlocation c/dir/unresolved || echo failed)" ]] || fail
-  [[ "$(rlocation c/dir/deeply/nested/file || echo failed)" == "$tmpdir/dir/deeply/nested/file" ]] || fail
-  [[ "$(rlocation "c/dir/deeply/nested/file with spaces" || echo failed)" == "$tmpdir/dir/deeply/nested/file with spaces" ]] || fail
-  [[ -z "$(rlocation unresolved || echo failed)" ]] || fail
-  [[ "$(rlocation "h/ i" || echo failed)" == "$tmpdir/ j k" ]] || fail
-  [[ "$(rlocation "h/ \i" || echo failed)" == "$tmpdir/ j k b" ]] || fail
-  [[ "$(rlocation "dir with spaces" || echo failed)" == "$tmpdir/dir with spaces" ]] || fail
-  [[ "$(rlocation "dir with spaces/nested/file" || echo failed)" == "$tmpdir/dir with spaces/nested/file" ]] || fail
-  if ! is_windows; then
-    [[ "$(rlocation $'h/\n\\i' || echo failed)" == "$tmpdir/ \nj k "$'\n'a ]] || fail
-    [[ "$(rlocation "space newline"$'\n'"backslash\_dir/f i\le" || echo failed)" == "${tmpdir}/space newline"$'\n'"backslash\a/f i\le" ]] || fail
-  fi
-
-  rm -r "$tmpdir/c/d" "$tmpdir/g h" "$tmpdir/y" "$tmpdir/dir" "$tmpdir/unresolved" "$tmpdir/ j k" "$tmpdir/dir with spaces"
-  if ! is_windows; then
-    rm -r "$tmpdir/ \nj k "$'\n'a "${tmpdir}/space newline"$'\n'"backslash\a"
-    [[ -z "$(rlocation $'h/\n\\i' || echo failed)" ]] || fail
-    [[ -z "$(rlocation "space newline"$'\n'"backslash\_dir/f i\le" || echo failed)" ]] || fail
-  fi
-  [[ -z "$(rlocation a/b || echo failed)" ]] || fail
-  [[ -z "$(rlocation e/f || echo failed)" ]] || fail
-  [[ -z "$(rlocation y || echo failed)" ]] || fail
-  [[ -z "$(rlocation c/dir || echo failed)" ]] || fail
-  [[ -z "$(rlocation c/dir/file || echo failed)" ]] || fail
-  [[ -z "$(rlocation c/dir/deeply/nested/file || echo failed)" ]] || fail
-  [[ -z "$(rlocation "h/ i" || echo failed)" ]] || fail
-  [[ -z "$(rlocation "dir with spaces" || echo failed)" ]] || fail
-  [[ -z "$(rlocation "dir with spaces/nested/file" || echo failed)" ]] || 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
-  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
-}
-
-function test_init_directory_based_runfiles() {
-  local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)"
-
-  export RUNFILES_DIR=${tmpdir}/mock/runfiles
-  export RUNFILES_MANIFEST_FILE=
-  source "$runfiles_lib_path"
-
-  mkdir -p "$RUNFILES_DIR/a"
-  touch "$RUNFILES_DIR/a/b" "$RUNFILES_DIR/c d"
-  [[ "$(rlocation a || echo failed)" == "$RUNFILES_DIR/a" ]] || fail
-  [[ "$(rlocation c/d || echo failed)" == failed ]] || fail
-  [[ "$(rlocation a/b || echo failed)" == "$RUNFILES_DIR/a/b" ]] || fail
-  [[ "$(rlocation "c d" || echo failed)" == "$RUNFILES_DIR/c d" ]] || fail
-  [[ "$(rlocation "c" || echo failed)" == failed ]] || fail
-  rm -r "$RUNFILES_DIR/a" "$RUNFILES_DIR/c d"
-  [[ "$(rlocation a || echo failed)" == failed ]] || fail
-  [[ "$(rlocation a/b || echo failed)" == failed ]] || fail
-  [[ "$(rlocation "c d" || echo failed)" == failed ]] || fail
-}
-
-function test_directory_based_runfiles_with_repo_mapping_from_main() {
-  local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)"
-
-  export RUNFILES_DIR=${tmpdir}/mock/runfiles
-  mkdir -p "$RUNFILES_DIR"
-  cat > "$RUNFILES_DIR/_repo_mapping" <<EOF
-,config.json,config.json+1.2.3
-,my_module,_main
-,my_protobuf,protobuf+3.19.2
-,my_workspace,_main
-protobuf+3.19.2,protobuf,protobuf+3.19.2
-protobuf+3.19.2,config.json,config.json+1.2.3
-EOF
-  export RUNFILES_MANIFEST_FILE=
-  source "$runfiles_lib_path"
-
-  mkdir -p "$RUNFILES_DIR/_main/bar"
-  touch "$RUNFILES_DIR/_main/bar/runfile"
-  mkdir -p "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/de eply/nes ted"
-  touch "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/file"
-  touch "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le"
-  mkdir -p "$RUNFILES_DIR/protobuf+3.19.2/foo"
-  touch "$RUNFILES_DIR/protobuf+3.19.2/foo/runfile"
-  touch "$RUNFILES_DIR/config.json"
-
-  [[ "$(rlocation "my_module/bar/runfile" "" || echo failed)" == "$RUNFILES_DIR/_main/bar/runfile" ]] || fail
-  [[ "$(rlocation "my_workspace/bar/runfile" "" || echo failed)" == "$RUNFILES_DIR/_main/bar/runfile" ]] || fail
-  [[ "$(rlocation "my_protobuf/foo/runfile" "" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/foo/runfile" ]] || fail
-  [[ "$(rlocation "my_protobuf/bar/dir" "" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir" ]] || fail
-  [[ "$(rlocation "my_protobuf/bar/dir/file" "" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/file" ]] || fail
-  [[ "$(rlocation "my_protobuf/bar/dir/de eply/nes ted/fi+le" "" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail
-
-  [[ "$(rlocation "protobuf/foo/runfile" "" || echo failed)" == failed ]] || fail
-  [[ "$(rlocation "protobuf/bar/dir/dir/de eply/nes ted/fi+le" "" || echo failed)" == failed ]] || fail
-
-  [[ "$(rlocation "_main/bar/runfile" "" || echo failed)" == "$RUNFILES_DIR/_main/bar/runfile" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/foo/runfile" "" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/foo/runfile" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir" "" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir/file" "" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/file" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" "" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail
-
-  [[ "$(rlocation "config.json" "" || echo failed)" == "$RUNFILES_DIR/config.json" ]] || fail
-}
-
-function test_directory_based_runfiles_with_repo_mapping_from_other_repo() {
-  local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)"
-
-  export RUNFILES_DIR=${tmpdir}/mock/runfiles
-  mkdir -p "$RUNFILES_DIR"
-  cat > "$RUNFILES_DIR/_repo_mapping" <<EOF
-,config.json,config.json+1.2.3
-,my_module,_main
-,my_protobuf,protobuf+3.19.2
-,my_workspace,_main
-protobuf+3.19.2,protobuf,protobuf+3.19.2
-protobuf+3.19.2,config.json,config.json+1.2.3
-EOF
-  export RUNFILES_MANIFEST_FILE=
-  source "$runfiles_lib_path"
-
-  mkdir -p "$RUNFILES_DIR/_main/bar"
-  touch "$RUNFILES_DIR/_main/bar/runfile"
-  mkdir -p "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/de eply/nes ted"
-  touch "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/file"
-  touch "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le"
-  mkdir -p "$RUNFILES_DIR/protobuf+3.19.2/foo"
-  touch "$RUNFILES_DIR/protobuf+3.19.2/foo/runfile"
-  touch "$RUNFILES_DIR/config.json"
-
-  [[ "$(rlocation "protobuf/foo/runfile" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/foo/runfile" ]] || fail
-  [[ "$(rlocation "protobuf/bar/dir" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir" ]] || fail
-  [[ "$(rlocation "protobuf/bar/dir/file" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/file" ]] || fail
-  [[ "$(rlocation "protobuf/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail
-
-  [[ "$(rlocation "my_module/bar/runfile" "protobuf+3.19.2" || echo failed)" == failed ]] || fail
-  [[ "$(rlocation "my_protobuf/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" == failed ]] || fail
-
-  [[ "$(rlocation "_main/bar/runfile" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/_main/bar/runfile" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/foo/runfile" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/foo/runfile" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir/file" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/file" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail
-
-  [[ "$(rlocation "config.json" "protobuf+3.19.2" || echo failed)" == "$RUNFILES_DIR/config.json" ]] || fail
-}
-
-function test_manifest_based_runfiles_with_repo_mapping_from_main() {
-  local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)"
-
-  cat > "$tmpdir/foo.repo_mapping" <<EOF
-,config.json,config.json+1.2.3
-,my_module,_main
-,my_protobuf,protobuf+3.19.2
-,my_workspace,_main
-protobuf+3.19.2,protobuf,protobuf+3.19.2
-protobuf+3.19.2,config.json,config.json+1.2.3
-EOF
-  export RUNFILES_DIR=
-  export RUNFILES_MANIFEST_FILE="$tmpdir/foo.runfiles_manifest"
-  cat > "$RUNFILES_MANIFEST_FILE" << EOF
-_repo_mapping $tmpdir/foo.repo_mapping
-config.json $tmpdir/config.json
-protobuf+3.19.2/foo/runfile $tmpdir/protobuf+3.19.2/foo/runfile
-_main/bar/runfile $tmpdir/_main/bar/runfile
-protobuf+3.19.2/bar/dir $tmpdir/protobuf+3.19.2/bar/dir
-EOF
-  source "$runfiles_lib_path"
-
-  mkdir -p "$tmpdir/_main/bar"
-  touch "$tmpdir/_main/bar/runfile"
-  mkdir -p "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted"
-  touch "$tmpdir/protobuf+3.19.2/bar/dir/file"
-  touch "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le"
-  mkdir -p "$tmpdir/protobuf+3.19.2/foo"
-  touch "$tmpdir/protobuf+3.19.2/foo/runfile"
-  touch "$tmpdir/config.json"
-
-  [[ "$(rlocation "my_module/bar/runfile" "" || echo failed)" == "$tmpdir/_main/bar/runfile" ]] || fail
-  [[ "$(rlocation "my_workspace/bar/runfile" "" || echo failed)" == "$tmpdir/_main/bar/runfile" ]] || fail
-  [[ "$(rlocation "my_protobuf/foo/runfile" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/foo/runfile" ]] || fail
-  [[ "$(rlocation "my_protobuf/bar/dir" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir" ]] || fail
-  [[ "$(rlocation "my_protobuf/bar/dir/file" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/file" ]] || fail
-  [[ "$(rlocation "my_protobuf/bar/dir/de eply/nes ted/fi+le" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail
-
-  [[ -z "$(rlocation "protobuf/foo/runfile" "" || echo failed)" ]] || fail
-  [[ -z "$(rlocation "protobuf/bar/dir/dir/de eply/nes ted/fi+le" "" || echo failed)" ]] || fail
-
-  [[ "$(rlocation "_main/bar/runfile" "" || echo failed)" == "$tmpdir/_main/bar/runfile" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/foo/runfile" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/foo/runfile" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir/file" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/file" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" "" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail
-
-  [[ "$(rlocation "config.json" "" || echo failed)" == "$tmpdir/config.json" ]] || fail
-}
-
-function test_manifest_based_runfiles_with_repo_mapping_from_other_repo() {
-  local tmpdir="$(mktemp -d $TEST_TMPDIR/tmp.XXXXXXXX)"
-
-  cat > "$tmpdir/foo.repo_mapping" <<EOF
-,config.json,config.json+1.2.3
-,my_module,_main
-,my_protobuf,protobuf+3.19.2
-,my_workspace,_main
-protobuf+3.19.2,protobuf,protobuf+3.19.2
-protobuf+3.19.2,config.json,config.json+1.2.3
-EOF
-  export RUNFILES_DIR=
-  export RUNFILES_MANIFEST_FILE="$tmpdir/foo.runfiles_manifest"
-  cat > "$RUNFILES_MANIFEST_FILE" << EOF
-_repo_mapping $tmpdir/foo.repo_mapping
-config.json $tmpdir/config.json
-protobuf+3.19.2/foo/runfile $tmpdir/protobuf+3.19.2/foo/runfile
-_main/bar/runfile $tmpdir/_main/bar/runfile
-protobuf+3.19.2/bar/dir $tmpdir/protobuf+3.19.2/bar/dir
-EOF
-  source "$runfiles_lib_path"
-
-  mkdir -p "$tmpdir/_main/bar"
-  touch "$tmpdir/_main/bar/runfile"
-  mkdir -p "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted"
-  touch "$tmpdir/protobuf+3.19.2/bar/dir/file"
-  touch "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le"
-  mkdir -p "$tmpdir/protobuf+3.19.2/foo"
-  touch "$tmpdir/protobuf+3.19.2/foo/runfile"
-  touch "$tmpdir/config.json"
-
-  [[ "$(rlocation "protobuf/foo/runfile" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/foo/runfile" ]] || fail
-  [[ "$(rlocation "protobuf/bar/dir" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir" ]] || fail
-  [[ "$(rlocation "protobuf/bar/dir/file" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/file" ]] || fail
-  [[ "$(rlocation "protobuf/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail
-
-  [[ -z "$(rlocation "my_module/bar/runfile" "protobuf+3.19.2" || echo failed)" ]] || fail
-  [[ -z "$(rlocation "my_protobuf/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" ]] || fail
-
-  [[ "$(rlocation "_main/bar/runfile" "protobuf+3.19.2" || echo failed)" == "$tmpdir/_main/bar/runfile" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/foo/runfile" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/foo/runfile" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir/file" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/file" ]] || fail
-  [[ "$(rlocation "protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" "protobuf+3.19.2" || echo failed)" == "$tmpdir/protobuf+3.19.2/bar/dir/de eply/nes ted/fi+le" ]] || fail
-
-  [[ "$(rlocation "config.json" "protobuf+3.19.2" || echo failed)" == "$tmpdir/config.json" ]] || fail
-}
-
-function test_directory_based_envvars() {
-  export RUNFILES_DIR=mock/runfiles
-  export RUNFILES_MANIFEST_FILE=
-  source "$runfiles_lib_path"
-
-  runfiles_export_envvars
-  [[ "${RUNFILES_DIR:-}" == "mock/runfiles" ]] || fail
-  [[ -z "${RUNFILES_MANIFEST_FILE:-}" ]] || fail
-}
-
-function main() {
-  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_FILE="$manifest_file"
-    export RUNFILES_DIR="$dir"
-    if ! ($t); then
-      failure=1
-    fi
-  done
-  return $failure
-}
-
-main
diff --git a/workspace_deps.bzl b/workspace_deps.bzl
index d90ca6f..d39d08b 100644
--- a/workspace_deps.bzl
+++ b/workspace_deps.bzl
@@ -57,10 +57,10 @@
         "urls": ["https://github.com/bazelbuild/rules_pkg/releases/download/0.9.1/rules_pkg-0.9.1.tar.gz"],
     },
     "rules_shell": {
-        "archive": "rules_shell-v0.1.1.tar.gz",
-        "sha256": "0d0c56d01c3c40420bf7bf14d73113f8a92fbd9f5cd13205a3b89f72078f0321",
-        "strip_prefix": "rules_shell-0.1.1",
-        "urls": ["https://github.com/bazelbuild/rules_shell/releases/download/v0.1.1/rules_shell-v0.1.1.tar.gz"],
+        "archive": "rules_shell-v0.3.0.tar.gz",
+        "sha256": "d8cd4a3a91fc1dc68d4c7d6b655f09def109f7186437e3f50a9b60ab436a0c53",
+        "strip_prefix": "rules_shell-0.3.0",
+        "urls": ["https://github.com/bazelbuild/rules_shell/releases/download/v0.3.0/rules_shell-v0.3.0.tar.gz"],
     },
     "rules_testing": {
         "archive": "rules_testing-v0.6.0.tar.gz",