Bug 1605894 reduce the proliferation of DefaultLoopbackTone to only AudioStreamFlowin...
[gecko.git] / mach
blob676f51ec9d920d1a25648d272c000f828baa6bad
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 import traceback
11 from textwrap import dedent, fill
13 MIN_PYTHON_VERSION = (3, 7)
14 MAX_PYTHON_VERSION_TO_CONSIDER = (3, 11)
17 def load_mach(dir_path, mach_path, args):
18     # Defer import of "importlib.util" until after Python version check has happened
19     # so that Python 2 usages fail gracefully.
20     import importlib.util
21     spec = importlib.util.spec_from_file_location('mach_initialize', mach_path)
22     mach_initialize = importlib.util.module_from_spec(spec)
23     spec.loader.exec_module(mach_initialize)
24     return mach_initialize.initialize(dir_path, args)
27 def check_and_get_mach(dir_path, args):
28     initialize_paths = (
29         # Run Thunderbird's mach_initialize.py if it exists
30         'comm/build/mach_initialize.py',
31         'build/mach_initialize.py',
32         # test package initialize
33         'tools/mach_initialize.py',
34     )
35     for initialize_path in initialize_paths:
36         mach_path = os.path.join(dir_path, initialize_path)
37         if os.path.isfile(mach_path):
38             return load_mach(dir_path, mach_path, args)
39     return None
42 def find_alternate_python3_executables():
43     for i in range(MIN_PYTHON_VERSION[1], MAX_PYTHON_VERSION_TO_CONSIDER[1] + 1):
44         potential_python_binary = f"python3.{i}"
45         if os.name == "nt":
46             potential_python_binary += ".exe"
48         try:
49             out = subprocess.run(
50                 [potential_python_binary, "--version"],
51                 stdout=subprocess.PIPE,
52                 stderr=subprocess.PIPE,
53                 encoding="UTF-8")
55             binary_minor_version = int(out.stdout[9:11].strip("."))
57             if binary_minor_version >= MIN_PYTHON_VERSION[1]:
58                 yield potential_python_binary
60         except Exception:
61             pass
64 def try_alternate_python3_executables(args):
65     for potential_python_binary in find_alternate_python3_executables():
66         try:
67             print(
68                 f"We found '{potential_python_binary}' and will attempt to re-run Mach with it."
69             )
70             os.execvp(
71                 potential_python_binary, [potential_python_binary] + ["mach"] + args
72             )
73         except Exception:
74             # We don't really care what goes wrong, just don't let it bubble up
75             # If we can't successfully launch with a different python3 binary
76             # we will just print the normal help messages.
77             pass
80 def main(args):
81     # Ensure we are running Python 3.7+. We run this check as soon as
82     # possible to avoid a cryptic import/usage error.
83     if sys.version_info < MIN_PYTHON_VERSION:
84         print(f"Python {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]}+ is required to run mach.")
85         print("You are running Mach with Python {0}".format(platform.python_version()))
86         try_alternate_python3_executables(args)
87         if sys.platform.startswith("linux"):
88             print(dedent("""
89             See https://firefox-source-docs.mozilla.org/setup/linux_build.html#installingpython
90             for guidance on how to install Python on your system.
91             """).strip())
92         elif sys.platform.startswith("darwin"):
93             print(dedent("""
94             See https://firefox-source-docs.mozilla.org/setup/macos_build.html
95             for guidance on how to prepare your system to build Firefox. Perhaps
96             you need to update Xcode, or install Python using brew?
97             """).strip())
98         elif "MOZILLABUILD" in os.environ and os.environ.get("TERM"):
99             print(dedent("""
100             Python is provided by MozillaBuild; ensure your MozillaBuild installation is
101             up to date. See https://firefox-source-docs.mozilla.org/setup/windows_build.html#install-mozillabuild
102             for details.
103             """).strip())
104         elif sys.platform.startswith("win"):
105             print(dedent("""
106             You probably want to be interacting with Mach from within MozillaBuild, see
107             https://firefox-source-docs.mozilla.org/setup/windows_build.html for details.
108             
109             If you are deliberately using Mach from outside MozillaBuild, then see
110             https://firefox-source-docs.mozilla.org/mach/windows-usage-outside-mozillabuild.html#install-python
111             for guidance on installing native Python on your system.
112             """).strip())
113         else:
114             print(dedent("""
115             We do not have specific instructions for your platform on how to
116             install Python. You may find Pyenv (https://github.com/pyenv/pyenv)
117             helpful, if your system package manager does not provide a way to
118             install a recent enough Python 3.
119             """).strip())
120         sys.exit(1)
122     # XCode python sets __PYVENV_LAUNCHER__, which overrides the executable
123     # used when a python subprocess is created. This is an issue when we want
124     # to run using our virtualenv python executables.
125     # In future Python relases, __PYVENV_LAUNCHER__ will be cleared before
126     # application code (mach) is started.
127     # https://github.com/python/cpython/pull/9516
128     os.environ.pop("__PYVENV_LAUNCHER__", None)
130     try:
131         mach = check_and_get_mach(os.path.dirname(os.path.realpath(__file__)), args)
132         if not mach:
133             print("Could not run mach: No mach source directory found.")
134             sys.exit(1)
135         sys.exit(mach.run(args))
136     except:
137         if sys.version_info >= (
138             MAX_PYTHON_VERSION_TO_CONSIDER[0],
139             MAX_PYTHON_VERSION_TO_CONSIDER[1] + 1,
140         ):
141             traceback.print_exc()
142             print()
143             print("---")
144             print()
145             print(fill(dedent(f"""\
146                 Note that you are running Mach with Python
147                 {platform.python_version()}, which is higher than the highest
148                 known working version of Python for Mach. Consider running Mach
149                 with Python {MAX_PYTHON_VERSION_TO_CONSIDER[0]}.{MAX_PYTHON_VERSION_TO_CONSIDER[1]}
150                 or lower."""
151             )))
153             try:
154                 alternative = next(find_alternate_python3_executables())
155                 print()
156                 print("Running the following command may solve your issue:")
157                 print()
158                 print(f"    {alternative} {sys.argv[0]} {' '.join(args)}")
159                 print()
160             except StopIteration:
161                 pass
162             sys.exit(1)
163         else:
164             raise
167 if __name__ == '__main__':
168     main(sys.argv[1:])