#!/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
    firefox-ui-functional
    jsshell-bench
    marionette-test
    jstests
    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 <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 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:])
