blob: c971ebc6971daa8eb35f7fc28d1d562333cba9aa [file] [log] [blame]
Philipp Wollermanna5afe952016-06-21 14:58:09 +00001#!/bin/bash
Brian Silvermanba04b2d2016-01-19 16:46:10 +00002
philwod3f8efc2019-11-29 02:54:34 -08003# Copyright 2019 The Bazel Authors. All rights reserved.
Brian Silvermanba04b2d2016-01-19 16:46:10 +00004#
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 Wollermanna5afe952016-06-21 14:58:09 +000017set -eu
18
philwod3f8efc2019-11-29 02:54:34 -080019# 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-Noble219eac82021-01-19 02:51:11 -080027# WORKSPACE[.bazel] file.
philwod3f8efc2019-11-29 02:54:34 -080028# 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
gkorlam90fe1b22022-04-13 08:31:50 -070037# 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.
philwod3f8efc2019-11-29 02:54:34 -080040#
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
44function 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 Silvermanba04b2d2016-01-19 16:46:10 +000052
Brian Silvermanba04b2d2016-01-19 16:46:10 +000053# `readlink -f` that works on OSX too.
54function 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
philwod3f8efc2019-11-29 02:54:34 -080093function get_workspace_root() {
94 workspace_dir="${PWD}"
95 while [[ "${workspace_dir}" != / ]]; do
Geoffrey Martin-Noble219eac82021-01-19 02:51:11 -080096 if [[ -e "${workspace_dir}/WORKSPACE" || -e "${workspace_dir}/WORKSPACE.bazel" ]]; then
philwod3f8efc2019-11-29 02:54:34 -080097 readonly workspace_dir
98 return
Dan Fabulichccaee702016-12-02 16:23:06 +000099 fi
philwod3f8efc2019-11-29 02:54:34 -0800100 workspace_dir="$(dirname "${workspace_dir}")"
101 done
102 readonly workspace_dir=""
103}
Dan Fabulichccaee702016-12-02 16:23:06 +0000104
philwod3f8efc2019-11-29 02:54:34 -0800105get_workspace_root
Dan Fabulichccaee702016-12-02 16:23:06 +0000106
philwod3f8efc2019-11-29 02:54:34 -0800107readonly wrapper_dir="$(dirname "$(get_realpath "${BASH_SOURCE[0]}")")"
108readonly os_arch_suffix="$(uname -s | tr '[:upper:]' '[:lower:]')-$(uname -m)"
109
110function get_bazel_version() {
philwo59d78642020-02-11 07:56:46 -0800111 bazel_version=""
112
philwod3f8efc2019-11-29 02:54:34 -0800113 if [[ -n ${USE_BAZEL_VERSION:-} ]]; then
philwo59d78642020-02-11 07:56:46 -0800114 reason="specified in \$USE_BAZEL_VERSION"
115 bazel_version="${USE_BAZEL_VERSION}"
philwod3f8efc2019-11-29 02:54:34 -0800116 elif [[ -e "${workspace_dir}/.bazelversion" ]]; then
philwo59d78642020-02-11 07:56:46 -0800117 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 Fabulichccaee702016-12-02 16:23:06 +0000126 fi
philwo59d78642020-02-11 07:56:46 -0800127
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
philwod3f8efc2019-11-29 02:54:34 -0800149}
150
151get_bazel_version
152
philwod3f8efc2019-11-29 02:54:34 -0800153BAZEL_REAL="${wrapper_dir}/bazel-${bazel_version}-${os_arch_suffix}"
philwo84eae2f2019-12-17 02:19:49 -0800154
155# Try without the architecture suffix.
philwod3f8efc2019-11-29 02:54:34 -0800156if [[ ! -x ${BAZEL_REAL} ]]; then
157 BAZEL_REAL="${wrapper_dir}/bazel-${bazel_version}"
158fi
159
philwo84eae2f2019-12-17 02:19:49 -0800160# Last try: Maybe `bazel-real` is actually the requested correct version?
161readonly bazel_real_path="${wrapper_dir}/bazel-real"
162if [[ ! -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
169fi
170
gkorlam90fe1b22022-04-13 08:31:50 -0700171# 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.
175BAZEL_WRAPPER=${BAZEL_WRAPPER:-"tools/bazel"}
176readonly wrapper="${workspace_dir}/${BAZEL_WRAPPER}"
Philipp Wollermann2a8cc702020-01-28 04:09:42 -0800177if [[ -x "$wrapper" && -f "$wrapper" ]]; then
178 export BAZEL_REAL
179 exec -a "$0" "${wrapper}" "$@"
180fi
181
182if [[ -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
187fi
188
philwod3f8efc2019-11-29 02:54:34 -0800189if [[ ! -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:"
fwef1e6c652022-04-13 11:26:36 -0700200 echo " https://bazel.build/install/ubuntu") 2>&1
philwod3f8efc2019-11-29 02:54:34 -0800201 else
202 (echo ""
Marc Plano-Lesayd5ae4602019-12-17 02:54:45 -0800203 echo "Bazel binaries for all official releases can be downloaded from here:"
philwod3f8efc2019-11-29 02:54:34 -0800204 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 Shi47890ca2020-07-21 07:13:28 -0700209 echo " (cd \"${wrapper_dir}\" && curl -fLO https://releases.bazel.build/${bazel_version}/release/${long_binary_name} && chmod +x ${long_binary_name})") 2>&1
philwod3f8efc2019-11-29 02:54:34 -0800210 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
221fi
222
Brian Silvermanba04b2d2016-01-19 16:46:10 +0000223exec -a "$0" "${BAZEL_REAL}" "$@"