Bug 1638435 [wpt PR 23644] - Convert all script metadata to text, a=testonly
[gecko.git] / mach
blob284c8c7df842e94667fd689a254b5e263e39466f
1 #!/bin/sh
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
6 # The beginning of this script is both valid POSIX shell and valid Python,
7 # such that the script starts with the shell and is reexecuted with
8 # the right Python.
10 # Embeds a shell script inside a Python triple quote. This pattern is valid
11 # shell because `''':'`, `':'` and `:` are all equivalent, and `:` is a no-op.
12 ''':'
13 py2commands="
14 android
15 awsy-test
16 browsertime
17 check-spidermonkey
18 clang-format
19 cppunittest
20 cramtest
21 crashtest
22 devtools-css-db
23 firefox-ui-functional
24 geckodriver
25 geckodriver-test
26 geckoview-junit
27 gradle
28 gtest
29 hazards
30 jsapi-tests
31 jsshell-bench
32 jstestbrowser
33 jstests
34 marionette-test
35 mochitest
36 mozharness
37 pastebin
38 power
39 prettier-format
40 puppeteer-test
41 python
42 python-test
43 raptor
44 raptor-test
45 reftest
46 release
47 repackage
48 rusttests
49 static-analysis
50 talos-test
51 taskcluster-build-image
52 taskcluster-load-image
53 taskgraph
54 telemetry-tests-client
55 test
56 test-info
57 tps-build
58 valgrind-test
59 visualmetrics
60 web-platform-tests
61 web-platform-tests-update
62 webidl-example
63 webidl-parser-test
64 webrtc-gtest
65 wpt
66 wpt-manifest-update
67 wpt-metadata-merge
68 wpt-metadata-summary
69 wpt-serve
70 wpt-test-paths
71 wpt-unittest
72 wpt-update
73 xpcshell-test
76 run_py() {
77 # Try to run a specific Python interpreter. Fall back to the system
78 # default Python if the specific interpreter couldn't be found.
79 py_executable="$1"
80 shift
81 if which "$py_executable" > /dev/null
82 then
83 exec "$py_executable" "$0" "$@"
84 elif [ "$py_executable" = "python2.7" ]; then
85 exec python "$0" "$@"
86 else
87 echo "This mach command requires $py_executable, which wasn't found on the system!"
88 exit 1
92 first_arg=$1
93 if [ "$first_arg" = "help" ]; then
94 # When running `./mach help <command>`, the correct Python for <command>
95 # needs to be used.
96 first_arg=$2
97 elif [ "$first_arg" = "mach-completion" ]; then
98 # When running `./mach mach-completion /path/to/mach <command>`, the
99 # correct Python for <command> needs to be used.
100 first_arg=$3
103 if [ -z "$first_arg" ]; then
104 # User ran `./mach` or `./mach help`, use Python 3.
105 run_py python3 "$@"
108 case "${first_arg}" in
109 "-"*)
110 # We have global arguments which are tricky to parse from this shell
111 # script. So invoke `mach` with a special --print-command argument to
112 # return the name of the command. This adds extra overhead when using
113 # global arguments, but global arguments are an edge case and this hack
114 # is only needed temporarily for the Python 3 migration. We use Python
115 # 2.7 because using Python 3 hits this error in build tasks:
116 # https://searchfox.org/mozilla-central/rev/c7e8bc4996f9/build/moz.configure/init.configure#319
117 command=`run_py python2.7 --print-command "$@" | tail -n1`
120 # In the common case, the first argument is the command.
121 command=${first_arg};
123 esac
125 # Check for the mach subcommand in the Python 2 commands list and run it
126 # with the correct interpreter.
127 case " $(echo $py2commands) " in
128 *\ $command\ *)
129 run_py python2.7 "$@"
132 run_py python3 "$@"
134 esac
136 # Run Python 3 for everything else.
137 run_py python3 "$@"
140 from __future__ import absolute_import, print_function, unicode_literals
142 import os
143 import sys
145 def ancestors(path):
146 while path:
147 yield path
148 (path, child) = os.path.split(path)
149 if child == "":
150 break
152 def load_mach(dir_path, mach_path):
153 if sys.version_info < (3, 5):
154 import imp
155 mach_bootstrap = imp.load_source('mach_bootstrap', mach_path)
156 else:
157 import importlib.util
158 spec = importlib.util.spec_from_file_location('mach_bootstrap', mach_path)
159 mach_bootstrap = importlib.util.module_from_spec(spec)
160 spec.loader.exec_module(mach_bootstrap)
162 return mach_bootstrap.bootstrap(dir_path)
165 def check_and_get_mach(dir_path):
166 bootstrap_paths = (
167 'build/mach_bootstrap.py',
168 # test package bootstrap
169 'tools/mach_bootstrap.py',
171 for bootstrap_path in bootstrap_paths:
172 mach_path = os.path.join(dir_path, bootstrap_path)
173 if os.path.isfile(mach_path):
174 return load_mach(dir_path, mach_path)
175 return None
178 def setdefaultenv(key, value):
179 """Compatibility shim to ensure the proper string type is used with
180 os.environ for the version of Python being used.
182 encoding = "mbcs" if sys.platform == "win32" else "utf-8"
184 if sys.version_info[0] == 2:
185 if isinstance(key, unicode):
186 key = key.encode(encoding)
187 if isinstance(value, unicode):
188 value = value.encode(encoding)
189 else:
190 if isinstance(key, bytes):
191 key = key.decode(encoding)
192 if isinstance(value, bytes):
193 value = value.decode(encoding)
195 os.environ.setdefault(key, value)
198 def get_mach():
199 # Check whether the current directory is within a mach src or obj dir.
200 for dir_path in ancestors(os.getcwd()):
201 # If we find a "config.status" and "mozinfo.json" file, we are in the objdir.
202 config_status_path = os.path.join(dir_path, 'config.status')
203 mozinfo_path = os.path.join(dir_path, 'mozinfo.json')
204 if os.path.isfile(config_status_path) and os.path.isfile(mozinfo_path):
205 import json
206 info = json.load(open(mozinfo_path))
207 if 'mozconfig' in info:
208 # If the MOZCONFIG environment variable is not already set, set it
209 # to the value from mozinfo.json. This will tell the build system
210 # to look for a config file at the path in $MOZCONFIG rather than
211 # its default locations.
212 setdefaultenv('MOZCONFIG', info['mozconfig'])
214 if 'topsrcdir' in info:
215 # Continue searching for mach_bootstrap in the source directory.
216 dir_path = info['topsrcdir']
218 mach = check_and_get_mach(dir_path)
219 if mach:
220 return mach
222 # If we didn't find a source path by scanning for a mozinfo.json, check
223 # whether the directory containing this script is a source directory. We
224 # follow symlinks so mach can be run even if cwd is outside the srcdir.
225 return check_and_get_mach(os.path.dirname(os.path.realpath(__file__)))
227 def main(args):
228 mach = get_mach()
229 if not mach:
230 print('Could not run mach: No mach source directory found.')
231 sys.exit(1)
232 sys.exit(mach.run(args))
235 if __name__ == '__main__':
236 main(sys.argv[1:])