Bug 1828387 - Make toolkit/components/glean buildable outside of a unified build...
[gecko.git] / mach
blobcc5e25b7aa360da11203bdf0cbedc817079e36ae
1 #!/usr/bin/env python3
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 import os
7 import platform
8 import sys
9 import subprocess
10 from textwrap import dedent
12 MIN_PYTHON_VERSION = (3, 7)
13 MAX_PYTHON_VERSION_TO_CONSIDER = (3, 11)
16 def load_mach(dir_path, mach_path):
17     # Defer import of "importlib.util" until after Python version check has happened
18     # so that Python 2 usages fail gracefully.
19     import importlib.util
20     spec = importlib.util.spec_from_file_location('mach_initialize', mach_path)
21     mach_initialize = importlib.util.module_from_spec(spec)
22     spec.loader.exec_module(mach_initialize)
23     return mach_initialize.initialize(dir_path)
26 def check_and_get_mach(dir_path):
27     initialize_paths = (
28         # Run Thunderbird's mach_initialize.py if it exists
29         'comm/build/mach_initialize.py',
30         'build/mach_initialize.py',
31         # test package initialize
32         'tools/mach_initialize.py',
33     )
34     for initialize_path in initialize_paths:
35         mach_path = os.path.join(dir_path, initialize_path)
36         if os.path.isfile(mach_path):
37             return load_mach(dir_path, mach_path)
38     return None
41 def try_alternate_python3_executables(args):
42     for i in range(MIN_PYTHON_VERSION[1], MAX_PYTHON_VERSION_TO_CONSIDER[1] + 1):
43         try:
44             potential_python_binary = f"python3.{i}"
45             if os.name == 'nt':
46                 potential_python_binary += ".exe"
48             out = subprocess.run(
49                 [potential_python_binary, "--version"],
50                 stdout=subprocess.PIPE,
51                 stderr=subprocess.PIPE,
52                 encoding="UTF-8")
54             binary_minor_version = int(out.stdout[9:11].strip("."))
56             if binary_minor_version >= MIN_PYTHON_VERSION[1]:
57                 print(f"We found '{potential_python_binary}' and will attempt to re-run Mach with it.")
58                 os.execvp(potential_python_binary, [potential_python_binary] + ["mach"] + args)
60         except Exception:
61             # We don't really care what goes wrong, just don't let it bubble up
62             # If we can't successfully launch with a different python3 binary
63             # we will just print the normal help messages.
64             pass
67 def main(args):
68     # Ensure we are running Python 3.7+. We run this check as soon as
69     # possible to avoid a cryptic import/usage error.
70     if sys.version_info < MIN_PYTHON_VERSION:
71         print(f"Python {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]}+ is required to run mach.")
72         print("You are running Mach with Python {0}".format(platform.python_version()))
73         try_alternate_python3_executables(args)
74         if sys.platform.startswith("linux"):
75             print(dedent("""
76             See https://firefox-source-docs.mozilla.org/setup/linux_build.html#installingpython
77             for guidance on how to install Python on your system.
78             """).strip())
79         elif sys.platform.startswith("darwin"):
80             print(dedent("""
81             See https://firefox-source-docs.mozilla.org/setup/macos_build.html
82             for guidance on how to prepare your system to build Firefox. Perhaps
83             you need to update Xcode, or install Python using brew?
84             """).strip())
85         elif "MOZILLABUILD" in os.environ and os.environ.get("TERM"):
86             print(dedent("""
87             Python is provided by MozillaBuild; ensure your MozillaBuild installation is
88             up to date. See https://firefox-source-docs.mozilla.org/setup/windows_build.html#install-mozillabuild
89             for details.
90             """).strip())
91         elif sys.platform.startswith("win"):
92             print(dedent("""
93             You probably want to be interacting with Mach from within MozillaBuild, see
94             https://firefox-source-docs.mozilla.org/setup/windows_build.html for details.
95             
96             If you are deliberately using Mach from outside MozillaBuild, then see
97             https://firefox-source-docs.mozilla.org/mach/windows-usage-outside-mozillabuild.html#install-python
98             for guidance on installing native Python on your system.
99             """).strip())
100         else:
101             print(dedent("""
102             We do not have specific instructions for your platform on how to
103             install Python. You may find Pyenv (https://github.com/pyenv/pyenv)
104             helpful, if your system package manager does not provide a way to
105             install a recent enough Python 3.
106             """).strip())
107         sys.exit(1)
109     # XCode python sets __PYVENV_LAUNCHER__, which overrides the executable
110     # used when a python subprocess is created. This is an issue when we want
111     # to run using our virtualenv python executables.
112     # In future Python relases, __PYVENV_LAUNCHER__ will be cleared before
113     # application code (mach) is started.
114     # https://github.com/python/cpython/pull/9516
115     os.environ.pop("__PYVENV_LAUNCHER__", None)
117     mach = check_and_get_mach(os.path.dirname(os.path.realpath(__file__)))
118     if not mach:
119         print('Could not run mach: No mach source directory found.')
120         sys.exit(1)
121     sys.exit(mach.run(args))
124 if __name__ == '__main__':
125     main(sys.argv[1:])