generated from pantsbuild/example-python
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pants
executable file
·262 lines (222 loc) · 9.23 KB
/
pants
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
#!/usr/bin/env bash
# Copyright 2020 Pants project contributors (see CONTRIBUTORS.md).
# Licensed under the Apache License, Version 2.0 (see LICENSE).
# =============================== NOTE ===============================
# This ./pants bootstrap script comes from the pantsbuild/setup
# project. It is intended to be checked into your code repository so
# that other developers have the same setup.
#
# Learn more here: https://www.pantsbuild.org/docs/installation
# ====================================================================
set -eou pipefail
PYTHON_BIN_NAME="${PYTHON:-unspecified}"
# Set one of these to specify a non-standard location for this script to read the pants version from.
# NB: This will *not* cause Pants itself to use this location as a config file.
# You can use PANTS_CONFIG_FILES or --pants-config-files to do so.
PANTS_INI=${PANTS_INI:-pants.ini}
PANTS_TOML=${PANTS_TOML:-pants.toml}
# NOTE: To use an unreleased version of Pants from the pantsbuild/pants master branch,
# locate the master branch SHA, set PANTS_SHA=<SHA> in the environment, and run this script as usual.
#
# E.g., PANTS_SHA=725fdaf504237190f6787dda3d72c39010a4c574 ./pants --version
PANTS_BIN_NAME="${PANTS_BIN_NAME:-$0}"
PANTS_HOME="${PANTS_HOME:-${XDG_CACHE_HOME:-$HOME/.cache}/pants/setup}"
# If given a relative path, we fix it to be absolute.
if [[ "$PANTS_HOME" != /* ]]; then
PANTS_HOME="${PWD}/${PANTS_HOME}"
fi
PANTS_BOOTSTRAP="${PANTS_HOME}/bootstrap-$(uname -s)-$(uname -m)"
VENV_VERSION=${VENV_VERSION:-16.4.3}
VENV_PACKAGE=virtualenv-${VENV_VERSION}
VENV_TARBALL=${VENV_PACKAGE}.tar.gz
COLOR_RED="\x1b[31m"
COLOR_GREEN="\x1b[32m"
COLOR_RESET="\x1b[0m"
function log() {
echo -e "$@" 1>&2
}
function die() {
(($# > 0)) && log "${COLOR_RED}$*${COLOR_RESET}"
exit 1
}
function green() {
(($# > 0)) && log "${COLOR_GREEN}$*${COLOR_RESET}"
}
function tempdir {
mktemp -d "$1"/pants.XXXXXX
}
function get_exe_path_or_die {
exe="$1"
if ! command -v "${exe}"; then
die "Could not find ${exe}. Please ensure ${exe} is on your PATH."
fi
}
pants_config_file=""
if [[ -f "${PANTS_INI}" ]]; then
pants_config_file="${PANTS_INI}"
fi
if [[ -f "${PANTS_TOML}" ]]; then
pants_config_file="${PANTS_TOML}"
fi
function get_pants_config_value {
config_key="$1"
optional_space="[[:space:]]*"
if [[ "${pants_config_file}" == *.ini ]]; then
valid_delimiters="[:=]"
prefix="^${config_key}${optional_space}${valid_delimiters}${optional_space}"
sed -ne "/${prefix}/ s#${prefix}##p" "${PANTS_INI}" && return 0
fi
if [[ "${pants_config_file}" == *.toml ]]; then
prefix="^${config_key}${optional_space}=${optional_space}"
raw_value="$(sed -ne "/${prefix}/ s#${prefix}##p" "${PANTS_TOML}")"
echo "${raw_value}" | tr -d \"\' && return 0
fi
return 0
}
function get_python_major_minor_version {
python_exe="$1"
"$python_exe" <<EOF
import sys
major_minor_version = ''.join(str(version_num) for version_num in sys.version_info[0:2])
print(major_minor_version)
EOF
}
# The high-level flow:
# 1.) Resolve the Python interpreter, first reading from the env var $PYTHON,
# then defaulting to Python 3.6+.
# 2.) Resolve the Pants version from config or from the latest stable
# release to PyPI, so that we know what to name the venv (virtual environment) folder and what
# to install with Pip.
# 3.) Check if the venv already exists via a naming convention, and create the venv if not found.
# 4.) Execute Pants with the resolved Python interpreter and venv.
#
# After that, Pants itself will handle making sure any requested plugins
# are installed and up to date.
function determine_pants_version {
if [ -n "${PANTS_SHA:-}" ]; then
# get_version_for_sha will echo the version, thus "returning" it from this function.
get_version_for_sha "$PANTS_SHA"
return
fi
python="$1"
pants_version="$(get_pants_config_value 'pants_version')"
if [[ -z "${pants_version}" ]]; then
pants_version="$(curl -sSL https://pypi.python.org/pypi/pantsbuild.pants/json |
"${python}" -c "import json, sys; print(json.load(sys.stdin)['info']['version'])")"
fi
pants_major_version="$(echo "${pants_version}" | cut -d '.' -f1)"
pants_minor_version="$(echo "${pants_version}" | cut -d '.' -f2)"
if [[ "${pants_major_version}" -eq 1 && "${pants_minor_version}" -le 22 ]]; then
die "This version of the \`./pants\` script does not work with Pants <= 1.22.0. Instead,
either upgrade your \`pants_version\` or use the version of the \`./pants\` script at
https://raw.githubusercontent.com/pantsbuild/setup/d1da382f6de0420940ec6007a39cba87c21075c6/pants."
fi
echo "${pants_version}"
}
function determine_default_python_exe {
for version in '3.6' '3.7' '3.8' '3.9'; do
interpreter_path="$(command -v "python${version}")"
if [[ -z "${interpreter_path}" ]]; then
continue
fi
# Check if the Python version is installed via Pyenv but not activated.
if [[ "$("${interpreter_path}" --version 2>&1 > /dev/null)" == "pyenv: python${version}"* ]]; then
continue
fi
echo "${interpreter_path}" && return 0
done
}
function determine_python_exe {
if [[ "${PYTHON_BIN_NAME}" != 'unspecified' ]]; then
python_bin_name="${PYTHON_BIN_NAME}"
else
python_bin_name="$(determine_default_python_exe)"
if [[ -z "${python_bin_name}" ]]; then
die "No valid Python interpreter found. Pants requires Python 3.6+ to run."
fi
fi
python_exe="$(get_exe_path_or_die "${python_bin_name}")"
major_minor_version="$(get_python_major_minor_version "${python_exe}")"
for valid_version in '36' '37' '38' '39'; do
if [[ "${major_minor_version}" == "${valid_version}" ]]; then
echo "${python_exe}" && return 0
fi
done
die "Invalid Python interpreter version for ${python_exe}. Pants requires Python 3.6+ to run."
}
# TODO(John Sirois): GC race loser tmp dirs leftover from bootstrap_XXX
# functions. Any tmp dir w/o a symlink pointing to it can go.
function bootstrap_venv {
if [[ ! -d "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}" ]]; then
(
mkdir -p "${PANTS_BOOTSTRAP}"
staging_dir=$(tempdir "${PANTS_BOOTSTRAP}")
cd "${staging_dir}"
curl -LO "https://pypi.io/packages/source/v/virtualenv/${VENV_TARBALL}"
tar -xzf "${VENV_TARBALL}"
ln -s "${staging_dir}/${VENV_PACKAGE}" "${staging_dir}/latest"
mv "${staging_dir}/latest" "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}"
) 1>&2
fi
echo "${PANTS_BOOTSTRAP}/${VENV_PACKAGE}"
}
function find_links_url {
local pants_version="$1"
local pants_sha="$2"
echo -n "https://binaries.pantsbuild.org/wheels/pantsbuild.pants/${pants_sha}/${pants_version/+/%2B}/index.html"
}
function get_version_for_sha {
local sha="$1"
# Retrieve the Pants version associated with this commit.
local pants_version
pants_version="$(curl --fail -sL "https://raw.githubusercontent.com/pantsbuild/pants/${sha}/src/python/pants/VERSION")"
# Construct the version as the release version from src/python/pants/VERSION, plus the string `+gitXXXXXXXX`,
# where the XXXXXXXX is the first 8 characters of the SHA.
echo "${pants_version}+git${sha:0:8}"
}
function bootstrap_pants {
pants_version="$1"
python="$2"
pants_sha="${3:-}"
pants_requirement="pantsbuild.pants==${pants_version}"
if [[ -z "${pants_sha}" ]]; then
maybe_find_links=""
else
maybe_find_links="--find-links=$(find_links_url "${pants_version}" "${pants_sha}")"
fi
python_major_minor_version="$(get_python_major_minor_version "${python}")"
target_folder_name="${pants_version}_py${python_major_minor_version}"
if [[ ! -d "${PANTS_BOOTSTRAP}/${target_folder_name}" ]]; then
(
venv_path="$(bootstrap_venv)"
staging_dir=$(tempdir "${PANTS_BOOTSTRAP}")
# shellcheck disable=SC2086
"${python}" "${venv_path}/virtualenv.py" --no-download "${staging_dir}/install" && \
"${staging_dir}/install/bin/pip" install -U pip && \
"${staging_dir}/install/bin/pip" install ${maybe_find_links} --progress-bar off "${pants_requirement}" && \
ln -s "${staging_dir}/install" "${staging_dir}/${target_folder_name}" && \
mv "${staging_dir}/${target_folder_name}" "${PANTS_BOOTSTRAP}/${target_folder_name}" && \
green "New virtual environment successfully created at ${PANTS_BOOTSTRAP}/${target_folder_name}."
) 1>&2
fi
echo "${PANTS_BOOTSTRAP}/${target_folder_name}"
}
# Ensure we operate from the context of the ./pants buildroot.
cd "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
python="$(determine_python_exe)"
pants_version="$(determine_pants_version "${python}")"
pants_dir="$(bootstrap_pants "${pants_version}" "${python}" "${PANTS_SHA:-}")"
pants_python="${pants_dir}/bin/python"
pants_binary="${pants_dir}/bin/pants"
pants_extra_args=""
if [[ -n "${PANTS_SHA:-}" ]]; then
pants_extra_args="${pants_extra_args} --python-repos-repos=$(find_links_url "$pants_version" "$PANTS_SHA")"
fi
# We set the env var no_proxy to '*', to work around an issue with urllib using non
# async-signal-safe syscalls after we fork a process that has already spawned threads.
#
# See https://blog.phusion.nl/2017/10/13/why-ruby-app-servers-break-on-macos-high-sierra-and-what-can-be-done-about-it/
export no_proxy='*'
# shellcheck disable=SC2086
exec "${pants_python}" "${pants_binary}" ${pants_extra_args} \
--pants-bin-name="${PANTS_BIN_NAME}" --pants-version=${pants_version} "$@"