forked from mozilla/gecko-dev
-
Notifications
You must be signed in to change notification settings - Fork 1
/
mach
executable file
·256 lines (227 loc) · 7.53 KB
/
mach
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
#!/bin/sh
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
# The beginning of this script is both valid POSIX shell and valid Python,
# such that the script starts with the shell and is reexecuted with
# the right Python.
# Embeds a shell script inside a Python triple quote. This pattern is valid
# shell because `''':'`, `':'` and `:` are all equivalent, and `:` is a no-op.
''':'
# Commands that are to be run with Python 2.
py2commands="
android
awsy-test
crashtest
firefox-ui-functional
geckodriver
geckodriver-test
jsshell-bench
marionette-test
jstestbrowser
jstests
mozharness
prettier-format
raptor
raptor-test
reftest
talos-test
telemetry-tests-client
test
web-platform-tests
web-platform-tests-update
wpt
wpt-metadata-merge
wpt-serve
wpt-update
"
# Commands that are to be run with the system Python 3 instead of the
# virtualenv.
nativecmds="
bootstrap
create-mach-environment
install-moz-phab
"
run_py() {
# Try to run a specific Python interpreter.
py_executable="$1"
shift
if command -v "$py_executable" > /dev/null
then
exec "$py_executable" "$0" "$@"
else
echo "This mach command requires $py_executable, which wasn't found on the system!"
case "$py_executable" in
python2.7|python3) ;;
*)
echo "Consider running 'mach bootstrap' or 'mach create-mach-environment' to create the mach virtualenvs, or set MACH_USE_SYSTEM_PYTHON to use the system Python installation over a virtualenv."
;;
esac
exit 1
fi
}
get_command() {
# Parse the name of the mach command out of the arguments. This is necessary
# in the presence of global mach arguments that come before the name of the
# command, e.g. `mach -v build`. We dispatch to the correct Python
# interpreter depending on the command.
while true; do
case $1 in
-v|--verbose) shift;;
-l|--log-file)
if [ "$#" -lt 2 ]
then
echo
break
else
shift 2
fi
;;
--log-interval) shift;;
--log-no-times) shift;;
-h) shift;;
--debug-command) shift;;
--settings)
if [ "$#" -lt 2 ]
then
echo
break
else
shift 2
fi
;;
# When running `./mach help <command>`, the correct Python for <command>
# needs to be used.
help) echo $2; break;;
# When running `./mach mach-completion /path/to/mach <command>`, the
# correct Python for <command> needs to be used.
mach-completion) echo $3; break;;
"") echo; break;;
*) echo $1; break;;
esac
done
}
state_dir=${MOZBUILD_STATE_PATH:-~/.mozbuild}
command=$(get_command "$@")
# If MACH_USE_SYSTEM_PYTHON or MOZ_AUTOMATION are set, always use the
# python{2.7,3} executables and not the virtualenv locations.
if [ -z ${MACH_USE_SYSTEM_PYTHON} ] && [ -z ${MOZ_AUTOMATION} ]
then
case "$OSTYPE" in
cygwin|msys|win32) bin_path=Scripts;;
*) bin_path=bin;;
esac
py2executable=$state_dir/_virtualenvs/mach_py2/$bin_path/python
py3executable=$state_dir/_virtualenvs/mach/$bin_path/python
else
py2executable=python2.7
py3executable=python3
fi
# Check whether we need to run with the native Python 3 interpreter.
case " $(echo $nativecmds) " in
*\ $command\ *)
run_py python3 "$@"
;;
esac
# Check for the mach subcommand in the Python 2 commands list and run it
# with the correct interpreter.
case " $(echo $py2commands) " in
*\ $command\ *)
run_py "$py2executable" "$@"
;;
*)
if [ -z ${MACH_PY2} ]
then
run_py "$py3executable" "$@"
else
if [ $command != "python-test" ]
then
echo "MACH_PY2 is only valid for mach python-test; please unset MACH_PY2 to continue."
exit 1
fi
run_py "$py2executable" "$@"
fi
;;
esac
# Run Python 3 for everything else.
run_py "$py3executable" "$@"
'''
from __future__ import absolute_import, print_function, unicode_literals
import os
import sys
def ancestors(path):
while path:
yield path
(path, child) = os.path.split(path)
if child == "":
break
def load_mach(dir_path, mach_path):
if sys.version_info < (3, 5):
import imp
mach_bootstrap = imp.load_source('mach_bootstrap', mach_path)
else:
import importlib.util
spec = importlib.util.spec_from_file_location('mach_bootstrap', mach_path)
mach_bootstrap = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mach_bootstrap)
return mach_bootstrap.bootstrap(dir_path)
def check_and_get_mach(dir_path):
bootstrap_paths = (
'build/mach_bootstrap.py',
# test package bootstrap
'tools/mach_bootstrap.py',
)
for bootstrap_path in bootstrap_paths:
mach_path = os.path.join(dir_path, bootstrap_path)
if os.path.isfile(mach_path):
return load_mach(dir_path, mach_path)
return None
def setdefaultenv(key, value):
"""Compatibility shim to ensure the proper string type is used with
os.environ for the version of Python being used.
"""
encoding = "mbcs" if sys.platform == "win32" else "utf-8"
if sys.version_info[0] == 2:
if isinstance(key, unicode):
key = key.encode(encoding)
if isinstance(value, unicode):
value = value.encode(encoding)
else:
if isinstance(key, bytes):
key = key.decode(encoding)
if isinstance(value, bytes):
value = value.decode(encoding)
os.environ.setdefault(key, value)
def get_mach():
# Check whether the current directory is within a mach src or obj dir.
for dir_path in ancestors(os.getcwd()):
# If we find a "config.status" and "mozinfo.json" file, we are in the objdir.
config_status_path = os.path.join(dir_path, 'config.status')
mozinfo_path = os.path.join(dir_path, 'mozinfo.json')
if os.path.isfile(config_status_path) and os.path.isfile(mozinfo_path):
import json
info = json.load(open(mozinfo_path))
if 'mozconfig' in info:
# If the MOZCONFIG environment variable is not already set, set it
# to the value from mozinfo.json. This will tell the build system
# to look for a config file at the path in $MOZCONFIG rather than
# its default locations.
setdefaultenv('MOZCONFIG', info['mozconfig'])
if 'topsrcdir' in info:
# Continue searching for mach_bootstrap in the source directory.
dir_path = info['topsrcdir']
mach = check_and_get_mach(dir_path)
if mach:
return mach
# If we didn't find a source path by scanning for a mozinfo.json, check
# whether the directory containing this script is a source directory. We
# follow symlinks so mach can be run even if cwd is outside the srcdir.
return check_and_get_mach(os.path.dirname(os.path.realpath(__file__)))
def main(args):
mach = get_mach()
if not mach:
print('Could not run mach: No mach source directory found.')
sys.exit(1)
sys.exit(mach.run(args))
if __name__ == '__main__':
main(sys.argv[1:])