Add $USE_BAZEL_VERSION and .bazelversion support to Bazel's wrapper script.
Also simplify the installation instructions for Ubuntu and how to
RELNOTES: Bazel's Debian package and the binary installer now include an improved wrapper that understands `<WORKSPACE>/.bazelversion` files and the `$USE_BAZEL_VERSION` environment variable. This is similar to what Bazelisk offers (https://github.com/bazelbuild/bazelisk#how-does-bazelisk-know-which-bazel-version-to-run-and-where-to-get-it-from), except that it works offline and integrates with apt-get.
PiperOrigin-RevId: 283031899
diff --git a/scripts/packages/bazel.sh b/scripts/packages/bazel.sh
index b75b2e6..635c059 100755
--- a/scripts/packages/bazel.sh
+++ b/scripts/packages/bazel.sh
@@ -1,6 +1,6 @@
#!/bin/bash
-# Copyright 2015 The Bazel Authors. All rights reserved.
+# Copyright 2019 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.
@@ -16,10 +16,37 @@
set -eu
-# This is a script which is installed instead of the real Bazel binary.
-# It looks for a tools/bazel executable next to the containing WORKSPACE
-# file and runs that. If that's not found, it runs the real Bazel binary which
-# is installed next to this script as bazel-real.
+# This is a script which can be installed as your "bazel" binary instead of the
+# real Bazel binary. When called, it tries to determine and run the correct
+# Bazel version for a given project and forwards all arguments to it.
+#
+# You can specify which Bazel version to use using these methods:
+# 1. Set $USE_BAZEL_VERSION to a version number
+# (e.g. export USE_BAZEL_VERSION=1.0.0).
+# 2. Add a .bazelversion file that contains a version number next to your
+# WORKSPACE file.
+# 3. Otherwise, the latest Bazel version will be used.
+#
+# This wrapper only recognizes Bazel versions installed next to itself, thus
+# if you install this wrapper as /usr/bin/bazel, you'll have to install binaries
+# for individual Bazel binaries as e.g. /usr/bin/bazel-1.0.0.
+#
+# In addition, if an executable called "tools/bazel" is found in the current
+# workspace, this script will not directly execute Bazel, but instead store
+# the path to the real Bazel executable in the environment variable BAZEL_REAL
+# and then execute the "tools/bazel" wrapper script.
+#
+# In contrast to Bazelisk, this script does not download anything from the
+# internet and instead relies on the local system to provide Bazel binaries.
+
+function color() {
+ # Usage: color "31;5" "string"
+ # Some valid values for color:
+ # - 5 blink, 1 strong, 4 underlined
+ # - fg: 31 red, 32 green, 33 yellow, 34 blue, 35 purple, 36 cyan, 37 white
+ # - bg: 40 black, 41 red, 44 blue, 45 purple
+ printf '\033[%sm%s\033[0m\n' "$@"
+}
# `readlink -f` that works on OSX too.
function get_realpath() {
@@ -61,29 +88,99 @@
fi
}
-BAZEL_REAL="$(dirname "$(get_realpath "${BASH_SOURCE[0]}")")/bazel-real"
-export BAZEL_REAL
-
-WORKSPACE_DIR="${PWD}"
-while [[ "${WORKSPACE_DIR}" != / ]]; do
- if [[ -e "${WORKSPACE_DIR}/WORKSPACE" ]]; then
- break;
+function get_workspace_root() {
+ workspace_dir="${PWD}"
+ while [[ "${workspace_dir}" != / ]]; do
+ if [[ -e "${workspace_dir}/WORKSPACE" ]]; then
+ readonly workspace_dir
+ return
fi
- WORKSPACE_DIR="$(dirname "${WORKSPACE_DIR}")"
-done
-readonly WORKSPACE_DIR
+ workspace_dir="$(dirname "${workspace_dir}")"
+ done
+ readonly workspace_dir=""
+}
-if [[ -e "${WORKSPACE_DIR}/WORKSPACE" ]]; then
- readonly WRAPPER="${WORKSPACE_DIR}/tools/bazel"
+get_workspace_root
- if [[ -x "${WRAPPER}" && ! -d "${WRAPPER}" ]]; then
- exec -a "$0" "${WRAPPER}" "$@"
+readonly wrapper_dir="$(dirname "$(get_realpath "${BASH_SOURCE[0]}")")"
+readonly os_arch_suffix="$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)"
+
+function get_bazel_version() {
+ if [[ -n ${USE_BAZEL_VERSION:-} ]]; then
+ readonly reason="specified in \$USE_BAZEL_VERSION"
+ readonly bazel_version="${USE_BAZEL_VERSION}"
+ elif [[ -e "${workspace_dir}/.bazelversion" ]]; then
+ readonly reason="specified in ${workspace_dir}/.bazelversion"
+ read -r bazel_version < "${workspace_dir}/.bazelversion"
+ readonly bazel_version="${bazel_version}"
+ elif [[ -x "${wrapper_dir}/bazel-real" ]]; then
+ readonly reason="automatically selected bazel-real"
+ readonly bazel_version="real"
+ else
+ # Find the latest Bazel version installed on the system.
+ readonly reason="automatically selected latest available version"
+ bazel_version="$(basename "$(find -H "${wrapper_dir}" -maxdepth 1 -name 'bazel-[0-9]*-${os_arch_suffix}' -type f | sort -V | tail -n 1)")"
+ if [[ -z $bazel_version ]]; then
+ bazel_version="$(basename "$(find -H "${wrapper_dir}" -maxdepth 1 -name 'bazel-[0-9]*' -type f | sort -V | tail -n 1)")"
+ fi
+ # Remove the "bazel-" prefix from the file name.
+ bazel_version="${bazel_version#"bazel-"}"
+ readonly bazel_version
fi
+}
+
+get_bazel_version
+
+if [[ -z $bazel_version ]]; then
+ color "31" "ERROR: No installed Bazel version found, cannot continue."
+ (echo ""
+ echo "Bazel binaries have to be installed in ${wrapper_dir}, but none were found.") 2>&1
+ exit 1
fi
-if [[ ! -x "${BAZEL_REAL}" ]]; then
- echo "Failed to find underlying Bazel executable at ${BAZEL_REAL}" >&2
- exit 1
+BAZEL_REAL="${wrapper_dir}/bazel-${bazel_version}-${os_arch_suffix}"
+if [[ ! -x ${BAZEL_REAL} ]]; then
+ BAZEL_REAL="${wrapper_dir}/bazel-${bazel_version}"
+fi
+
+if [[ ! -x $BAZEL_REAL ]]; then
+ color "31" "ERROR: The project you're trying to build requires Bazel ${bazel_version} (${reason}), but it wasn't found in ${wrapper_dir}."
+
+ long_binary_name="bazel-${bazel_version}-${os_arch_suffix}"
+
+ if [[ -x $(command -v apt-get) && $wrapper_dir == "/usr/bin" ]]; then
+ (echo ""
+ echo "You can install the required Bazel version via apt:"
+ echo " sudo apt update && sudo apt install bazel-${bazel_version}"
+ echo ""
+ echo "If this doesn't work, check Bazel's installation instructions for help:"
+ echo " https://docs.bazel.build/versions/master/install-ubuntu.html") 2>&1
+ else
+ (echo ""
+ echo "Bazel binaries for all official releaeses can be downloaded from here:"
+ echo " https://github.com/bazelbuild/bazel/releases") 2>&1
+
+ if [[ -x $(command -v curl) && -w $wrapper_dir ]]; then
+ (echo ""
+ echo "You can download the required version directly using this command:"
+ echo " (cd \"${wrapper_dir}\" && curl -LO https://releases.bazel.build/${bazel_version}/release/${long_binary_name} && chmod +x ${long_binary_name})") 2>&1
+ elif [[ -x $(command -v wget) && -w $wrapper_dir ]]; then
+ (echo ""
+ echo "You can download the required version directly using this command:"
+ echo " (cd \"${wrapper_dir}\" && wget https://releases.bazel.build/${bazel_version}/release/${long_binary_name} && chmod +x ${long_binary_name})") 2>&1
+ else
+ (echo ""
+ echo "Please put the downloaded Bazel binary into this location:"
+ echo " ${wrapper_dir}/${long_binary_name}") 2>&1
+ fi
+ fi
+ exit 1
+fi
+
+readonly wrapper="${workspace_dir}/tools/bazel"
+if [[ -x "$wrapper" ]]; then
+ export BAZEL_REAL
+ exec -a "$0" "${wrapper}" "$@"
fi
exec -a "$0" "${BAZEL_REAL}" "$@"