Philipp Wollermann | a5afe95 | 2016-06-21 14:58:09 +0000 | [diff] [blame] | 1 | #!/bin/bash |
Brian Silverman | ba04b2d | 2016-01-19 16:46:10 +0000 | [diff] [blame] | 2 | |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 3 | # Copyright 2019 The Bazel Authors. All rights reserved. |
Brian Silverman | ba04b2d | 2016-01-19 16:46:10 +0000 | [diff] [blame] | 4 | # |
| 5 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 6 | # you may not use this file except in compliance with the License. |
| 7 | # You may obtain a copy of the License at |
| 8 | # |
| 9 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 10 | # |
| 11 | # Unless required by applicable law or agreed to in writing, software |
| 12 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 14 | # See the License for the specific language governing permissions and |
| 15 | # limitations under the License. |
| 16 | |
Philipp Wollermann | a5afe95 | 2016-06-21 14:58:09 +0000 | [diff] [blame] | 17 | set -eu |
| 18 | |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 19 | # This is a script which can be installed as your "bazel" binary instead of the |
| 20 | # real Bazel binary. When called, it tries to determine and run the correct |
| 21 | # Bazel version for a given project and forwards all arguments to it. |
| 22 | # |
| 23 | # You can specify which Bazel version to use using these methods: |
| 24 | # 1. Set $USE_BAZEL_VERSION to a version number |
| 25 | # (e.g. export USE_BAZEL_VERSION=1.0.0). |
| 26 | # 2. Add a .bazelversion file that contains a version number next to your |
Geoffrey Martin-Noble | 219eac8 | 2021-01-19 02:51:11 -0800 | [diff] [blame] | 27 | # WORKSPACE[.bazel] file. |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 28 | # 3. Otherwise, the latest Bazel version will be used. |
| 29 | # |
| 30 | # This wrapper only recognizes Bazel versions installed next to itself, thus |
| 31 | # if you install this wrapper as /usr/bin/bazel, you'll have to install binaries |
| 32 | # for individual Bazel binaries as e.g. /usr/bin/bazel-1.0.0. |
| 33 | # |
| 34 | # In addition, if an executable called "tools/bazel" is found in the current |
| 35 | # workspace, this script will not directly execute Bazel, but instead store |
| 36 | # the path to the real Bazel executable in the environment variable BAZEL_REAL |
gkorlam | 90fe1b2 | 2022-04-13 08:31:50 -0700 | [diff] [blame] | 37 | # and then execute the "tools/bazel" wrapper script. The location of the wrapper |
| 38 | # script relative to the workspace can be changed with the $BAZEL_WRAPPER |
| 39 | # environment variable. |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 40 | # |
| 41 | # In contrast to Bazelisk, this script does not download anything from the |
| 42 | # internet and instead relies on the local system to provide Bazel binaries. |
| 43 | |
| 44 | function color() { |
| 45 | # Usage: color "31;5" "string" |
| 46 | # Some valid values for color: |
| 47 | # - 5 blink, 1 strong, 4 underlined |
| 48 | # - fg: 31 red, 32 green, 33 yellow, 34 blue, 35 purple, 36 cyan, 37 white |
| 49 | # - bg: 40 black, 41 red, 44 blue, 45 purple |
| 50 | printf '\033[%sm%s\033[0m\n' "$@" |
| 51 | } |
Brian Silverman | ba04b2d | 2016-01-19 16:46:10 +0000 | [diff] [blame] | 52 | |
Brian Silverman | ba04b2d | 2016-01-19 16:46:10 +0000 | [diff] [blame] | 53 | # `readlink -f` that works on OSX too. |
| 54 | function get_realpath() { |
| 55 | if [ "$(uname -s)" == "Darwin" ]; then |
| 56 | local queue="$1" |
| 57 | if [[ "${queue}" != /* ]] ; then |
| 58 | # Make sure we start with an absolute path. |
| 59 | queue="${PWD}/${queue}" |
| 60 | fi |
| 61 | local current="" |
| 62 | while [ -n "${queue}" ]; do |
| 63 | # Removing a trailing /. |
| 64 | queue="${queue#/}" |
| 65 | # Pull the first path segment off of queue. |
| 66 | local segment="${queue%%/*}" |
| 67 | # If this is the last segment. |
| 68 | if [[ "${queue}" != */* ]] ; then |
| 69 | segment="${queue}" |
| 70 | queue="" |
| 71 | else |
| 72 | # Remove that first segment. |
| 73 | queue="${queue#*/}" |
| 74 | fi |
| 75 | local link="${current}/${segment}" |
| 76 | if [ -h "${link}" ] ; then |
| 77 | link="$(readlink "${link}")" |
| 78 | queue="${link}/${queue}" |
| 79 | if [[ "${link}" == /* ]] ; then |
| 80 | current="" |
| 81 | fi |
| 82 | else |
| 83 | current="${link}" |
| 84 | fi |
| 85 | done |
| 86 | |
| 87 | echo "${current}" |
| 88 | else |
| 89 | readlink -f "$1" |
| 90 | fi |
| 91 | } |
| 92 | |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 93 | function get_workspace_root() { |
| 94 | workspace_dir="${PWD}" |
| 95 | while [[ "${workspace_dir}" != / ]]; do |
Geoffrey Martin-Noble | 219eac8 | 2021-01-19 02:51:11 -0800 | [diff] [blame] | 96 | if [[ -e "${workspace_dir}/WORKSPACE" || -e "${workspace_dir}/WORKSPACE.bazel" ]]; then |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 97 | readonly workspace_dir |
| 98 | return |
Dan Fabulich | ccaee70 | 2016-12-02 16:23:06 +0000 | [diff] [blame] | 99 | fi |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 100 | workspace_dir="$(dirname "${workspace_dir}")" |
| 101 | done |
| 102 | readonly workspace_dir="" |
| 103 | } |
Dan Fabulich | ccaee70 | 2016-12-02 16:23:06 +0000 | [diff] [blame] | 104 | |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 105 | get_workspace_root |
Dan Fabulich | ccaee70 | 2016-12-02 16:23:06 +0000 | [diff] [blame] | 106 | |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 107 | readonly wrapper_dir="$(dirname "$(get_realpath "${BASH_SOURCE[0]}")")" |
| 108 | readonly os_arch_suffix="$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)" |
| 109 | |
| 110 | function get_bazel_version() { |
philwo | 59d7864 | 2020-02-11 07:56:46 -0800 | [diff] [blame] | 111 | bazel_version="" |
| 112 | |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 113 | if [[ -n ${USE_BAZEL_VERSION:-} ]]; then |
philwo | 59d7864 | 2020-02-11 07:56:46 -0800 | [diff] [blame] | 114 | reason="specified in \$USE_BAZEL_VERSION" |
| 115 | bazel_version="${USE_BAZEL_VERSION}" |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 116 | elif [[ -e "${workspace_dir}/.bazelversion" ]]; then |
philwo | 59d7864 | 2020-02-11 07:56:46 -0800 | [diff] [blame] | 117 | reason="specified in ${workspace_dir}/.bazelversion" |
| 118 | # The "read" can fail if the .bazelversion file does not contain a final |
| 119 | # newline character. It will still correctly assign the read data to the |
| 120 | # variable. Of course it can also fail due to real I/O errors. Because we do |
| 121 | # validation of the read data later, we can just ignore the error here. |
| 122 | # Alternatives like 'bazel_version=$(head -1 ...)' are not better either, |
| 123 | # because bash does not care about the exit code of processes used in |
| 124 | # command substitution. |
| 125 | read -r bazel_version < "${workspace_dir}/.bazelversion" || true |
Dan Fabulich | ccaee70 | 2016-12-02 16:23:06 +0000 | [diff] [blame] | 126 | fi |
philwo | 59d7864 | 2020-02-11 07:56:46 -0800 | [diff] [blame] | 127 | |
| 128 | # If we read an empty string or the magic word "latest" from .bazelversion or |
| 129 | # $USE_BAZEL_VERSION, then we should use the latest available stable version. |
| 130 | # This mimics the behavior of Bazelisk. |
| 131 | if [[ -z $bazel_version || $bazel_version == "latest" ]]; then |
| 132 | if [[ -x "${wrapper_dir}/bazel-real" ]]; then |
| 133 | reason="${reason:-"automatically selected bazel-real"}" |
| 134 | bazel_version="real" |
| 135 | else |
| 136 | # Find the latest Bazel version installed on the system. |
| 137 | reason="${reason:-"automatically selected latest available version"}" |
| 138 | bazel_version="$(basename "$(find -H "${wrapper_dir}" -maxdepth 1 -name 'bazel-[0-9]*-${os_arch_suffix}' -type f | sort -V | tail -n 1)")" |
| 139 | if [[ -z $bazel_version ]]; then |
| 140 | bazel_version="$(basename "$(find -H "${wrapper_dir}" -maxdepth 1 -name 'bazel-[0-9]*' -type f | sort -V | tail -n 1)")" |
| 141 | fi |
| 142 | # Remove the "bazel-" prefix from the file name. |
| 143 | bazel_version="${bazel_version#"bazel-"}" |
| 144 | fi |
| 145 | fi |
| 146 | |
| 147 | readonly reason |
| 148 | readonly bazel_version |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 149 | } |
| 150 | |
| 151 | get_bazel_version |
| 152 | |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 153 | BAZEL_REAL="${wrapper_dir}/bazel-${bazel_version}-${os_arch_suffix}" |
philwo | 84eae2f | 2019-12-17 02:19:49 -0800 | [diff] [blame] | 154 | |
| 155 | # Try without the architecture suffix. |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 156 | if [[ ! -x ${BAZEL_REAL} ]]; then |
| 157 | BAZEL_REAL="${wrapper_dir}/bazel-${bazel_version}" |
| 158 | fi |
| 159 | |
philwo | 84eae2f | 2019-12-17 02:19:49 -0800 | [diff] [blame] | 160 | # Last try: Maybe `bazel-real` is actually the requested correct version? |
| 161 | readonly bazel_real_path="${wrapper_dir}/bazel-real" |
| 162 | if [[ ! -x ${BAZEL_REAL} && -x ${bazel_real_path} ]]; then |
| 163 | # Note that "bazel --version" is very fast and doesn't start the Bazel server, |
| 164 | # as opposed to "bazel version". |
| 165 | readonly bazel_real_version="$("${bazel_real_path}" --version | grep '^bazel ' | cut -d' ' -f2)" |
| 166 | if [[ $bazel_real_version == $bazel_version ]]; then |
| 167 | BAZEL_REAL="${bazel_real_path}" |
| 168 | fi |
| 169 | fi |
| 170 | |
gkorlam | 90fe1b2 | 2022-04-13 08:31:50 -0700 | [diff] [blame] | 171 | # If the repository contains a checked-in executable set by $BAZEL_WRAPPER |
| 172 | # (defaults to tools/bazel), we assume that they know what they're doing and |
| 173 | # have their own way of versioning Bazel. Thus, we don't have to print our |
| 174 | # helpful messages or error out in case we couldn't find a binary. |
| 175 | BAZEL_WRAPPER=${BAZEL_WRAPPER:-"tools/bazel"} |
| 176 | readonly wrapper="${workspace_dir}/${BAZEL_WRAPPER}" |
Philipp Wollermann | 2a8cc70 | 2020-01-28 04:09:42 -0800 | [diff] [blame] | 177 | if [[ -x "$wrapper" && -f "$wrapper" ]]; then |
| 178 | export BAZEL_REAL |
| 179 | exec -a "$0" "${wrapper}" "$@" |
| 180 | fi |
| 181 | |
| 182 | if [[ -z $bazel_version ]]; then |
| 183 | color "31" "ERROR: No installed Bazel version found, cannot continue." |
| 184 | (echo "" |
| 185 | echo "Bazel binaries have to be installed in ${wrapper_dir}, but none were found.") 2>&1 |
| 186 | exit 1 |
| 187 | fi |
| 188 | |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 189 | if [[ ! -x $BAZEL_REAL ]]; then |
| 190 | color "31" "ERROR: The project you're trying to build requires Bazel ${bazel_version} (${reason}), but it wasn't found in ${wrapper_dir}." |
| 191 | |
| 192 | long_binary_name="bazel-${bazel_version}-${os_arch_suffix}" |
| 193 | |
| 194 | if [[ -x $(command -v apt-get) && $wrapper_dir == "/usr/bin" ]]; then |
| 195 | (echo "" |
| 196 | echo "You can install the required Bazel version via apt:" |
| 197 | echo " sudo apt update && sudo apt install bazel-${bazel_version}" |
| 198 | echo "" |
| 199 | echo "If this doesn't work, check Bazel's installation instructions for help:" |
fwe | f1e6c65 | 2022-04-13 11:26:36 -0700 | [diff] [blame] | 200 | echo " https://bazel.build/install/ubuntu") 2>&1 |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 201 | else |
| 202 | (echo "" |
Marc Plano-Lesay | d5ae460 | 2019-12-17 02:54:45 -0800 | [diff] [blame] | 203 | echo "Bazel binaries for all official releases can be downloaded from here:" |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 204 | echo " https://github.com/bazelbuild/bazel/releases") 2>&1 |
| 205 | |
| 206 | if [[ -x $(command -v curl) && -w $wrapper_dir ]]; then |
| 207 | (echo "" |
| 208 | echo "You can download the required version directly using this command:" |
Xiaoyi Shi | 47890ca | 2020-07-21 07:13:28 -0700 | [diff] [blame] | 209 | echo " (cd \"${wrapper_dir}\" && curl -fLO https://releases.bazel.build/${bazel_version}/release/${long_binary_name} && chmod +x ${long_binary_name})") 2>&1 |
philwo | d3f8efc | 2019-11-29 02:54:34 -0800 | [diff] [blame] | 210 | elif [[ -x $(command -v wget) && -w $wrapper_dir ]]; then |
| 211 | (echo "" |
| 212 | echo "You can download the required version directly using this command:" |
| 213 | echo " (cd \"${wrapper_dir}\" && wget https://releases.bazel.build/${bazel_version}/release/${long_binary_name} && chmod +x ${long_binary_name})") 2>&1 |
| 214 | else |
| 215 | (echo "" |
| 216 | echo "Please put the downloaded Bazel binary into this location:" |
| 217 | echo " ${wrapper_dir}/${long_binary_name}") 2>&1 |
| 218 | fi |
| 219 | fi |
| 220 | exit 1 |
| 221 | fi |
| 222 | |
Brian Silverman | ba04b2d | 2016-01-19 16:46:10 +0000 | [diff] [blame] | 223 | exec -a "$0" "${BAZEL_REAL}" "$@" |