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/. 5 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. 9 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 14# Commands that are to be run with the system Python 3 instead of the 15# virtualenv. 16nativecmds=" 17 bootstrap 18 create-mach-environment 19 install-moz-phab 20" 21 22run_py() { 23 # Try to run a specific Python interpreter. 24 py_executable="$1" 25 shift 26 if command -v "$py_executable" > /dev/null 27 then 28 exec "$py_executable" $py_profile_command_args "$0" "$@" 29 else 30 echo "This mach command requires $py_executable, which wasn't found on the system!" 31 case "$py_executable" in 32 python3) ;; 33 *) 34 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." 35 ;; 36 esac 37 exit 1 38 fi 39} 40 41get_command() { 42 # Parse the name of the mach command out of the arguments. This is necessary 43 # in the presence of global mach arguments that come before the name of the 44 # command, e.g. `mach -v build`. We dispatch to the correct Python 45 # interpreter depending on the command. 46 while true; do 47 case $1 in 48 -v|--verbose) shift;; 49 -l|--log-file) 50 if [ "$#" -lt 2 ] 51 then 52 echo 53 break 54 else 55 shift 2 56 fi 57 ;; 58 --no-interactive) shift;; 59 --log-interval) shift;; 60 --log-no-times) shift;; 61 -h) shift;; 62 --debug-command) shift;; 63 --profile-command) 64 py_profile_command="1" 65 shift;; 66 --settings) 67 if [ "$#" -lt 2 ] 68 then 69 echo 70 break 71 else 72 shift 2 73 fi 74 ;; 75 "") echo; break;; 76 *) echo $1; break;; 77 esac 78 done 79 return ${py_profile_command} 80} 81 82state_dir=${MOZBUILD_STATE_PATH:-~/.mozbuild} 83command=$(get_command "$@") 84py_profile_command=$? 85 86if [ ${py_profile_command} -eq 0 ] 87then 88 py_profile_command_args="" 89else 90 # We would prefer to use an array variable here, but we're limited to POSIX. 91 # None of our arguments have quoting or spaces so we can safely interpolate 92 # a string instead. 93 py_profile_command_args="-m cProfile -o mach_profile_${command}.cProfile" 94 echo "Running with --profile-command. To visualize, use snakeviz:" 95 echo "$HOME/.mozbuild/_virtualenvs/mach/bin/python -m pip install snakeviz" 96 echo "$HOME/.mozbuild/_virtualenvs/mach/bin/python -m snakeviz mach_profile_${command}.cProfile" 97fi 98 99# If MACH_USE_SYSTEM_PYTHON or MOZ_AUTOMATION are set, always use the 100# python 3 executables and not the virtualenv locations. 101if [ -z ${MACH_USE_SYSTEM_PYTHON} ] && [ -z ${MOZ_AUTOMATION} ] 102then 103 case "$OSTYPE" in 104 cygwin|msys|win32) bin_path=Scripts;; 105 *) bin_path=bin;; 106 esac 107 py3executable=$state_dir/_virtualenvs/mach/$bin_path/python 108else 109 py3executable=python3 110fi 111 112# Check whether we need to run with the native Python 3 interpreter. 113case " $(echo $nativecmds) " in 114 *\ $command\ *) 115 run_py python3 "$@" 116 ;; 117esac 118 119# # Use the mach virtualenv's Python 3 for the rest of the commands. 120run_py "$py3executable" "$@" 121''' 122 123from __future__ import absolute_import, print_function, unicode_literals 124 125import os 126import sys 127 128def load_mach(dir_path, mach_path): 129 if sys.version_info < (3, 5): 130 import imp 131 mach_bootstrap = imp.load_source('mach_bootstrap', mach_path) 132 else: 133 import importlib.util 134 spec = importlib.util.spec_from_file_location('mach_bootstrap', mach_path) 135 mach_bootstrap = importlib.util.module_from_spec(spec) 136 spec.loader.exec_module(mach_bootstrap) 137 138 return mach_bootstrap.bootstrap(dir_path) 139 140 141def check_and_get_mach(dir_path): 142 bootstrap_paths = ( 143 'build/mach_bootstrap.py', 144 # test package bootstrap 145 'tools/mach_bootstrap.py', 146 ) 147 for bootstrap_path in bootstrap_paths: 148 mach_path = os.path.join(dir_path, bootstrap_path) 149 if os.path.isfile(mach_path): 150 return load_mach(dir_path, mach_path) 151 return None 152 153 154def main(args): 155 # XCode python sets __PYVENV_LAUNCHER__, which overrides the executable 156 # used when a python subprocess is created. This is an issue when we want 157 # to run using our virtualenv python executables. 158 # In future Python relases, __PYVENV_LAUNCHER__ will be cleared before 159 # application code (mach) is started. 160 # https://github.com/python/cpython/pull/9516 161 os.environ.pop("__PYVENV_LAUNCHER__", None) 162 163 mach = check_and_get_mach(os.path.dirname(os.path.realpath(__file__))) 164 if not mach: 165 print('Could not run mach: No mach source directory found.') 166 sys.exit(1) 167 sys.exit(mach.run(args)) 168 169 170if __name__ == '__main__': 171 main(sys.argv[1:]) 172