-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
pants
executable file
·214 lines (183 loc) · 7.57 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
#!/usr/bin/env bash
# Copyright 2015 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 and is intended to be checked into your code repository so
# that any developer can check out your code and be building it with
# Pants with no prior setup needed.
#
# It requires your version of pants to be v1.23.0 or newer.
#
# You can learn more here: https://pants.readme.io/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}
PANTS_BIN_NAME="${PANTS_BIN_NAME:-$0}"
PANTS_HOME="${PANTS_HOME:-${XDG_CACHE_HOME:-$HOME/.cache}/pants/setup}"
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 {
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 16 ]]; then
die "This version of the \`./pants\` script does not work with Pants <= 1.16.0. Instead,
either upgrade your \`pants_version\` or use the version of the \`./pants\` script at
https://raw.githubusercontent.com/pantsbuild/setup/3bb006e4a792ae83d7059ea76c0372ece7c1069d/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 bootstrap_pants {
pants_version="$1"
python="$2"
pants_requirement="pantsbuild.pants==${pants_version}"
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}")
"${python}" "${venv_path}/virtualenv.py" --no-download "${staging_dir}/install"
"${staging_dir}/install/bin/pip" install -U pip
"${staging_dir}/install/bin/pip" install --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}")"
# 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='*'
exec "${pants_dir}/bin/python" "${pants_dir}/bin/pants" --pants-bin-name="${PANTS_BIN_NAME}" "$@"