#!/usr/bin/env python3 # 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/.
# This script provides one-line bootstrap support to configure systems to build # the tree. It does so by cloning the repo before calling directly into `mach # bootstrap`.
# Note that this script can't assume anything in particular about the host # Python environment (except that it's run with a sufficiently recent version of # Python 3), so we are restricted to stdlib modules.
import sys
major, minor = sys.version_info[:2] if (major < 3) or (major == 3 and minor < 8):
print( "Bootstrap currently only runs on Python 3.8+." "Please try re-running with python3.8+."
)
sys.exit(1)
import ctypes import os import shutil import subprocess import tempfile from optparse import OptionParser from pathlib import Path
CLONE_MERCURIAL_PULL_FAIL = """
Failed to pull from hg.mozilla.org.
WINDOWS = sys.platform.startswith("win32") or sys.platform.startswith("msys")
VCS_HUMAN_READABLE = { "hg": "Mercurial", "git": "Git",
}
def which(name): """Python implementation of which.
It returns the path of an executable orNoneif it couldn't be found. """
search_dirs = os.environ["PATH"].split(os.pathsep)
potential_names = [name] if WINDOWS:
potential_names.insert(0, name + ".exe")
for path in search_dirs: for executable_name in potential_names:
test = Path(path) / executable_name if test.is_file() and os.access(test, os.X_OK): return test
returnNone
def validate_clone_dest(dest: Path):
dest = dest.resolve()
ifnot dest.exists(): return dest
ifnot dest.is_dir():
print(f"ERROR! Destination {dest} exists but is not a directory.") returnNone
ifnot any(dest.iterdir()): return dest else:
print(f"ERROR! Destination directory {dest} exists but is nonempty.")
print(
f"To re-bootstrap the existing checkout, go into '{dest}' and run './mach bootstrap'."
) returnNone
def input_clone_dest(vcs, no_interactive):
repo_name = "mozilla-unified"
print(f"Cloning into {repo_name} using {VCS_HUMAN_READABLE[vcs]}...") whileTrue:
dest = None ifnot no_interactive:
dest = input(
f"Destination directory for clone (leave empty to use "
f"default destination of {repo_name}): "
).strip() ifnot dest:
dest = repo_name
dest = validate_clone_dest(Path(dest).expanduser()) if dest: return dest if no_interactive: returnNone
def hg_clone_firefox(hg: Path, dest: Path, head_repo, head_rev): # We create an empty repo then modify the config before adding data. # This is necessary to ensure storage settings are optimally # configured.
args = [
str(hg), # The unified repo is generaldelta, so ensure the client is as # well. "--config", "format.generaldelta=true", "init",
str(dest),
]
res = subprocess.call(args) if res:
print("unable to create destination repo; please try cloning manually") returnNone
# Strictly speaking, this could overwrite a config based on a template # the user has installed. Let's pretend this problem doesn't exist # unless someone complains about it. with open(dest / ".hg" / "hgrc", "a") as fh:
fh.write("[paths]\n")
fh.write("default = https://hg.mozilla.org/mozilla-unified\n")
fh.write("\n")
# The server uses aggressivemergedeltas which can blow up delta chain # length. This can cause performance to tank due to delta chains being # too long. Limit the delta chain length to something reasonable # to bound revlog read time.
fh.write("[format]\n")
fh.write("# This is necessary to keep performance in check\n")
fh.write("maxchainlen = 10000\n")
# Pulling a specific revision into an empty repository induces a lot of # load on the Mercurial server, so we always pull from mozilla-unified (which, # when done from an empty repository, is equivalent to a clone), and then pull # the specific revision we want (if we want a specific one, otherwise we just # use the "central" bookmark), at which point it will be an incremental pull, # that the server can process more easily. # This is the same thing that robustcheckout does on automation.
res = subprocess.call(
[str(hg), "pull", "https://hg.mozilla.org/mozilla-unified"], cwd=str(dest)
) ifnot res and head_repo:
res = subprocess.call(
[str(hg), "pull", head_repo, "-r", head_rev], cwd=str(dest)
)
print("") if res:
print(CLONE_MERCURIAL_PULL_FAIL % dest) returnNone
head_rev = head_rev or"central"
print(f'updating to "{head_rev}" - the development head of Gecko and Firefox')
res = subprocess.call([str(hg), "update", "-r", head_rev], cwd=str(dest)) if res:
print(
f"error updating; you will need to `cd {dest} && hg update -r central` " "manually"
) return dest
cinnabar_url = "https://github.com/glandium/git-cinnabar/" # If git-cinnabar isn't installed already, that's fine; we can # download a temporary copy. `mach bootstrap` will install a copy # in the state dir; we don't want to copy all that logic to this # tiny bootstrapping script.
tempdir = Path(tempfile.mkdtemp()) with open(tempdir / "download.py", "wb") as fh:
shutil.copyfileobj(
urlopen(f"{cinnabar_url}/raw/master/download.py"), fh
)
subprocess.check_call(
[sys.executable, str(tempdir / "download.py")],
cwd=str(tempdir),
)
env["PATH"] = str(tempdir) + os.pathsep + env["PATH"]
print( "WARNING! git-cinnabar is required for Firefox development " "with git. After the clone is complete, the bootstrapper " "will ask if you would like to configure git; answer yes, " "and be sure to add git-cinnabar to your PATH according to " "the bootstrapper output."
)
# This will attempt to run as administrator by triggering a UAC prompt # for admin credentials. If "No" is selected, no exclusions are added.
ctypes.windll.shell32.ShellExecuteW(None, "runas", powershell_exe, command, None, 0)
if vcs == "hg":
hg = which("hg") ifnot hg:
print("Mercurial is not installed. Mercurial is required to clone Firefox.") try: # We're going to recommend people install the Mercurial package with # pip3. That will work if `pip3` installs binaries to a location # that's in the PATH, but it might not be. To help out, if we CAN # import "mercurial" (in which case it's already been installed), # offer that as a solution. import mercurial # noqa: F401
print( "Hint: have you made sure that Mercurial is installed to a " "location in your PATH?"
) except ImportError:
print("Try installing hg with `pip3 install Mercurial`.") returnNone
binary = hg else:
binary = which(vcs) ifnot binary:
print("Git is not installed.")
print("Try installing git using your system package manager.") returnNone
dest = input_clone_dest(vcs, no_interactive) ifnot dest: returnNone
if no_interactive: # --no-interactive is a global argument, not a command argument, # so it needs to be specified before "bootstrap" is appended.
args += ["--no-interactive"]
args += ["bootstrap"]
if application_choice:
args += ["--application-choice", application_choice] if no_system_changes:
args += ["--no-system-changes"]
def main(args):
parser = OptionParser()
parser.add_option( "--application-choice",
dest="application_choice",
help='Pass in an application choice (see "APPLICATIONS" in ' "python/mozboot/mozboot/bootstrap.py) instead of using the " "default interactive prompt.",
)
parser.add_option( "--vcs",
dest="vcs",
default="hg",
choices=["git", "hg"],
help="VCS (hg or git) to use for downloading the source code, " "instead of using the default interactive prompt.",
)
parser.add_option( "--no-interactive",
dest="no_interactive",
action="store_true",
help="Answer yes to any (Y/n) interactive prompts.",
)
parser.add_option( "--no-system-changes",
dest="no_system_changes",
action="store_true",
help="Only executes actions that leave the system ""configuration alone.",
)
options, leftover = parser.parse_args(args) try:
srcdir = clone(options) ifnot srcdir: return 1
print("Clone complete.")
print( "If you need to run the tooling bootstrapping again, " "then consider running './mach bootstrap' instead."
) ifnot options.no_interactive:
remove_bootstrap_file = input( "Unless you are going to have more local copies of Firefox source code, " "this 'bootstrap.py' file is no longer needed and can be deleted. " "Clean up the bootstrap.py file? (Y/n)"
) ifnot remove_bootstrap_file:
remove_bootstrap_file = "y" if options.no_interactive or remove_bootstrap_file == "y": try:
Path(sys.argv[0]).unlink() except FileNotFoundError:
print("File could not be found !") return bootstrap(
srcdir,
options.application_choice,
options.no_interactive,
options.no_system_changes,
) except Exception:
print("Could not bootstrap Firefox! Consider filing a bug.") raise
if __name__ == "__main__":
sys.exit(main(sys.argv))
¤ Dauer der Verarbeitung: 0.2 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.