#!/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=" awsy-test jsshell-bench mozharness raptor raptor-test telemetry-tests-client test wpt-metadata-merge " # 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 `, the correct Python for # needs to be used. help) echo $2; break;; # When running `./mach mach-completion /path/to/mach `, the # correct Python for 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 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 main(args): # XCode python sets __PYVENV_LAUNCHER__, which overrides the executable # used when a python subprocess is created. This is an issue when we want # to run using our virtualenv python executables. # In future Python relases, __PYVENV_LAUNCHER__ will be cleared before # application code (mach) is started. # https://github.com/python/cpython/pull/9516 os.environ.pop("__PYVENV_LAUNCHER__", None) mach = check_and_get_mach(os.path.dirname(os.path.realpath(__file__))) 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:])