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/.
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.
22 spec = importlib.util.spec_from_file_location("mach_initialize", mach_path)
23 mach_initialize = importlib.util.module_from_spec(spec)
24 spec.loader.exec_module(mach_initialize)
25 return mach_initialize.initialize(dir_path, args)
28 def check_and_get_mach(dir_path, args):
30 # Run Thunderbird's mach_initialize.py if it exists
31 "comm/build/mach_initialize.py",
32 "build/mach_initialize.py",
33 # test package initialize
34 "tools/mach_initialize.py",
36 for initialize_path in initialize_paths:
37 mach_path = os.path.join(dir_path, initialize_path)
38 if os.path.isfile(mach_path):
39 return load_mach(dir_path, mach_path, args)
43 def find_alternate_python3_executables():
44 for i in range(MIN_PYTHON_VERSION[1], MAX_PYTHON_VERSION_TO_CONSIDER[1] + 1):
45 potential_python_binary = f"python3.{i}"
47 potential_python_binary += ".exe"
51 [potential_python_binary, "--version"],
52 stdout=subprocess.PIPE,
53 stderr=subprocess.PIPE,
57 binary_minor_version = int(out.stdout[9:11].strip("."))
59 if binary_minor_version >= MIN_PYTHON_VERSION[1]:
60 yield potential_python_binary
66 def try_alternate_python3_executables(args):
67 for potential_python_binary in find_alternate_python3_executables():
70 f"We found '{potential_python_binary}' and will attempt to re-run Mach with it."
73 potential_python_binary, [potential_python_binary] + ["mach"] + args
76 # We don't really care what goes wrong, just don't let it bubble up
77 # If we can't successfully launch with a different python3 binary
78 # we will just print the normal help messages.
83 # Ensure we are running Python 3.7+. We run this check as soon as
84 # possible to avoid a cryptic import/usage error.
85 if sys.version_info < MIN_PYTHON_VERSION:
87 f"Python {MIN_PYTHON_VERSION[0]}.{MIN_PYTHON_VERSION[1]}+ is required to run mach."
89 print("You are running Mach with Python {0}".format(platform.python_version()))
90 try_alternate_python3_executables(args)
91 if sys.platform.startswith("linux"):
95 See https://firefox-source-docs.mozilla.org/setup/linux_build.html#installingpython
96 for guidance on how to install Python on your system.
100 elif sys.platform.startswith("darwin"):
104 See https://firefox-source-docs.mozilla.org/setup/macos_build.html
105 for guidance on how to prepare your system to build Firefox. Perhaps
106 you need to update Xcode, or install Python using brew?
110 elif "MOZILLABUILD" in os.environ and os.environ.get("TERM"):
114 Python is provided by MozillaBuild; ensure your MozillaBuild installation is
115 up to date. See https://firefox-source-docs.mozilla.org/setup/windows_build.html#install-mozillabuild
120 elif sys.platform.startswith("win"):
124 You probably want to be interacting with Mach from within MozillaBuild, see
125 https://firefox-source-docs.mozilla.org/setup/windows_build.html for details.
127 If you are deliberately using Mach from outside MozillaBuild, then see
128 https://firefox-source-docs.mozilla.org/mach/windows-usage-outside-mozillabuild.html#install-python
129 for guidance on installing native Python on your system.
137 We do not have specific instructions for your platform on how to
138 install Python. You may find Pyenv (https://github.com/pyenv/pyenv)
139 helpful, if your system package manager does not provide a way to
140 install a recent enough Python 3.
146 # XCode python sets __PYVENV_LAUNCHER__, which overrides the executable
147 # used when a python subprocess is created. This is an issue when we want
148 # to run using our virtualenv python executables.
149 # In future Python relases, __PYVENV_LAUNCHER__ will be cleared before
150 # application code (mach) is started.
151 # https://github.com/python/cpython/pull/9516
152 os.environ.pop("__PYVENV_LAUNCHER__", None)
155 mach = check_and_get_mach(os.path.dirname(os.path.realpath(__file__)), args)
157 print("Could not run mach: No mach source directory found.")
159 sys.exit(mach.run(args))
160 except (KeyboardInterrupt, SystemExit):
162 except Exception as e:
163 if sys.version_info >= (
164 MAX_PYTHON_VERSION_TO_CONSIDER[0],
165 MAX_PYTHON_VERSION_TO_CONSIDER[1] + 1,
167 traceback.print_exc()
175 Note that you are running Mach with Python
176 {platform.python_version()}, which is higher than the highest
177 known working version of Python for Mach. Consider running Mach
178 with Python {MAX_PYTHON_VERSION_TO_CONSIDER[0]}.{MAX_PYTHON_VERSION_TO_CONSIDER[1]}
185 alternative = next(find_alternate_python3_executables())
187 print("Running the following command may solve your issue:")
189 print(f" {alternative} {sys.argv[0]} {' '.join(args)}")
191 except StopIteration:
198 if __name__ == "__main__":