mach
author Marian-Vasile Laza <mlaza@mozilla.com>
Fri, 23 Jul 2021 21:41:10 +0300
changeset 586546 eeb3f6140f6910fb78e3942e868a2c39ba25b766
parent 581764 a1a67efda98eeae6d9bcac77a817015f85e5fe4a
permissions -rwxr-xr-x
Backed out changeset dc28d43a2814 (bug 1721640) for causing ebay Btime raptor failures. CLOSED TREE

#!/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 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" $py_profile_command_args "$0" "$@"
    else
        echo "This mach command requires $py_executable, which wasn't found on the system!"
        case "$py_executable" in
            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
            ;;
        --no-interactive) shift;;
        --log-interval) shift;;
        --log-no-times) shift;;
        -h) shift;;
        --debug-command) shift;;
        --profile-command)
            py_profile_command="1"
            shift;;
        --settings)
            if [ "$#" -lt 2 ]
            then
                echo
                break
            else
                shift 2
            fi
            ;;
        "") echo; break;;
        *) echo $1; break;;
    esac
    done
    return ${py_profile_command}
}

state_dir=${MOZBUILD_STATE_PATH:-~/.mozbuild}
command=$(get_command "$@")
py_profile_command=$?

if [ ${py_profile_command} -eq 0 ]
then
    py_profile_command_args=""
else
    # We would prefer to use an array variable here, but we're limited to POSIX.
    # None of our arguments have quoting or spaces so we can safely interpolate
    # a string instead.
    py_profile_command_args="-m cProfile -o mach_profile_${command}.cProfile"
    echo "Running with --profile-command.  To visualize, use snakeviz:"
    echo "$HOME/.mozbuild/_virtualenvs/mach/bin/python -m pip install snakeviz"
    echo "$HOME/.mozbuild/_virtualenvs/mach/bin/python -m snakeviz mach_profile_${command}.cProfile"
fi

# If MACH_USE_SYSTEM_PYTHON or MOZ_AUTOMATION are set, always use the
# python 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
    py3executable=$state_dir/_virtualenvs/mach/$bin_path/python
else
    py3executable=python3
fi

# Check whether we need to run with the native Python 3 interpreter.
case " $(echo $nativecmds) " in
    *\ $command\ *)
        run_py python3 "$@"
        ;;
esac

# # Use the mach virtualenv's Python 3 for the rest of the commands.
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:])