mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
95a7613454
Differential Revision: https://phabricator.services.mozilla.com/D216160
404 lines
14 KiB
Python
404 lines
14 KiB
Python
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
|
# vim: set filetype=python:
|
|
# 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/.
|
|
|
|
|
|
m4 = check_prog(
|
|
"M4",
|
|
(
|
|
"gm4",
|
|
"m4",
|
|
),
|
|
paths=prefer_mozillabuild_path,
|
|
)
|
|
|
|
|
|
@depends(mozconfig)
|
|
def prepare_mozconfig(mozconfig):
|
|
if mozconfig["path"]:
|
|
items = {}
|
|
for key, value in mozconfig["vars"]["added"].items():
|
|
items[key] = (value, "added")
|
|
for key, (old, value) in mozconfig["vars"]["modified"].items():
|
|
items[key] = (value, "modified")
|
|
for t in ("env", "vars"):
|
|
for key in mozconfig[t]["removed"].keys():
|
|
items[key] = (None, "removed " + t)
|
|
return items
|
|
|
|
|
|
@depends("OLD_CONFIGURE", build_project)
|
|
def old_configure(old_configure, build_project):
|
|
if not old_configure:
|
|
die("The OLD_CONFIGURE environment variable must be set")
|
|
|
|
# os.path.abspath in the sandbox will ensure forward slashes on Windows,
|
|
# which is actually necessary because this path actually ends up literally
|
|
# as $0, and backslashes there breaks autoconf's detection of the source
|
|
# directory.
|
|
old_configure = os.path.abspath(old_configure[0])
|
|
if build_project == "js":
|
|
old_configure_dir = os.path.dirname(old_configure)
|
|
if not old_configure_dir.endswith("/js/src"):
|
|
old_configure = os.path.join(
|
|
old_configure_dir, "js", "src", os.path.basename(old_configure)
|
|
)
|
|
return old_configure
|
|
|
|
|
|
@depends(prepare_mozconfig, old_configure_assignments)
|
|
@imports(_from="__builtin__", _import="open")
|
|
@imports(_from="__builtin__", _import="print")
|
|
@imports(_from="mozbuild.shellutil", _import="quote")
|
|
def prepare_configure(mozconfig, old_configure_assignments):
|
|
assignments = {}
|
|
|
|
with open("old-configure.vars", "w") as out:
|
|
log.debug("Injecting the following to old-configure:")
|
|
|
|
def inject(command):
|
|
print(command, file=out) # noqa Python 2vs3
|
|
log.debug("| %s", command)
|
|
|
|
if mozconfig:
|
|
inject("# start of mozconfig values")
|
|
for key, (value, action) in sorted(mozconfig.items()):
|
|
if action.startswith("removed "):
|
|
inject("unset %s # from %s" % (key, action[len("removed ") :]))
|
|
else:
|
|
inject("%s=%s # %s" % (key, quote(value), action))
|
|
assignments[key] = value
|
|
|
|
inject("# end of mozconfig values")
|
|
|
|
for k, v in old_configure_assignments:
|
|
inject("%s=%s" % (k, quote(v)))
|
|
assignments[k] = v
|
|
|
|
return namespace(assignments=assignments)
|
|
|
|
|
|
@template
|
|
def old_configure_options(*options):
|
|
for opt in options:
|
|
option(opt, nargs="*", help="Help missing for old configure options")
|
|
|
|
@dependable
|
|
def all_options():
|
|
return list(options)
|
|
|
|
return depends(
|
|
host_for_sub_configure, target_for_sub_configure, all_options, *options
|
|
)
|
|
|
|
|
|
@old_configure_options(
|
|
"--cache-file",
|
|
"--enable-official-branding",
|
|
"--with-branding",
|
|
"--x-includes",
|
|
"--x-libraries",
|
|
)
|
|
def prepare_configure_options(host, target, all_options, *options):
|
|
# old-configure only supports the options listed in @old_configure_options
|
|
# so we don't need to pass it every single option we've been passed. Only
|
|
# the ones that are not supported by python configure need to.
|
|
options = [
|
|
value.format(name)
|
|
for name, value in zip(all_options, options)
|
|
if value.origin != "default"
|
|
] + [host, target]
|
|
|
|
return namespace(options=options, all_options=all_options)
|
|
|
|
|
|
@template
|
|
def old_configure_for(old_configure_path, extra_env=None):
|
|
if extra_env is None:
|
|
extra_env = dependable(None)
|
|
|
|
@depends(
|
|
prepare_configure,
|
|
prepare_configure_options,
|
|
"--prefix",
|
|
"--includedir",
|
|
"--libdir",
|
|
prefer_mozillabuild_path,
|
|
altered_path,
|
|
extra_env,
|
|
build_environment,
|
|
old_configure_path,
|
|
awk,
|
|
m4,
|
|
shell,
|
|
"--cache-file",
|
|
)
|
|
@imports(_from="__builtin__", _import="compile")
|
|
@imports(_from="__builtin__", _import="open")
|
|
@imports(_from="__builtin__", _import="OSError")
|
|
@imports("glob")
|
|
@imports("itertools")
|
|
@imports("logging")
|
|
@imports("os")
|
|
@imports("re")
|
|
@imports("subprocess")
|
|
@imports("sys")
|
|
@imports(_from="mozbuild.shellutil", _import="quote")
|
|
@imports(_from="mozbuild.shellutil", _import="split")
|
|
@imports(_from="tempfile", _import="NamedTemporaryFile")
|
|
@imports(_from="subprocess", _import="CalledProcessError")
|
|
@imports(_from="__builtin__", _import="exec")
|
|
def old_configure(
|
|
prepare_configure,
|
|
prepare_configure_options,
|
|
prefix,
|
|
includedir,
|
|
libdir,
|
|
prefer_mozillabuild_path,
|
|
altered_path,
|
|
extra_env,
|
|
build_env,
|
|
old_configure,
|
|
awk,
|
|
m4,
|
|
shell,
|
|
cache_file_option,
|
|
):
|
|
if altered_path:
|
|
path = altered_path
|
|
else:
|
|
path = os.pathsep.join(prefer_mozillabuild_path)
|
|
|
|
cxx = prepare_configure.assignments.get("CXX")
|
|
cc = prepare_configure.assignments.get("CC")
|
|
|
|
if cc and cxx:
|
|
if cache_file_option:
|
|
config_cache = cache_file_option[0]
|
|
else:
|
|
config_cache = "config.cache"
|
|
|
|
if os.path.exists(config_cache):
|
|
remove = False
|
|
with open(config_cache, "r") as cfg_cache:
|
|
cxx_pattern = re.compile(
|
|
r"^ac_cv_prog_CXX=\${ac_cv_prog_CXX='(.*)'}$"
|
|
)
|
|
cc_pattern = re.compile(r"^ac_cv_prog_CC=\${ac_cv_prog_CC='(.*)'}$")
|
|
for line in cfg_cache:
|
|
m = cc_pattern.match(line)
|
|
if m and m.group(1) != cc:
|
|
remove = True
|
|
break
|
|
m = cxx_pattern.match(line)
|
|
if m and m.group(1) != cxx:
|
|
remove = True
|
|
break
|
|
|
|
if remove:
|
|
log.info("invalidating config.cache")
|
|
os.remove(config_cache)
|
|
|
|
refresh = True
|
|
if os.path.exists(old_configure):
|
|
mtime = os.path.getmtime(old_configure)
|
|
aclocal = os.path.join(build_env.topsrcdir, "build", "autoconf", "*.m4")
|
|
for input in itertools.chain(
|
|
(
|
|
old_configure + ".in",
|
|
os.path.join(os.path.dirname(old_configure), "aclocal.m4"),
|
|
),
|
|
glob.iglob(aclocal),
|
|
):
|
|
if os.path.getmtime(input) > mtime:
|
|
break
|
|
else:
|
|
refresh = False
|
|
|
|
if refresh:
|
|
autoconf = os.path.join(
|
|
build_env.topsrcdir, "build", "autoconf", "autoconf.sh"
|
|
)
|
|
log.info("Refreshing %s with %s", old_configure, autoconf)
|
|
env = dict(os.environ)
|
|
env["M4"] = m4
|
|
env["AWK"] = awk
|
|
env["AC_MACRODIR"] = os.path.join(build_env.topsrcdir, "build", "autoconf")
|
|
env["PATH"] = path
|
|
|
|
try:
|
|
script = subprocess.check_output(
|
|
[
|
|
shell,
|
|
autoconf,
|
|
"--localdir=%s" % os.path.dirname(old_configure),
|
|
old_configure + ".in",
|
|
],
|
|
# Fix the working directory, so that when m4 is called, that
|
|
# includes of relative paths are deterministically resolved
|
|
# relative to the directory containing old-configure.
|
|
cwd=os.path.dirname(old_configure),
|
|
env=env,
|
|
)
|
|
except CalledProcessError as exc:
|
|
die("autoconf exited with return code {}".format(exc.returncode))
|
|
|
|
if not script:
|
|
die(
|
|
"Generated old-configure is empty! Check that your autoconf 2.13 program works!"
|
|
)
|
|
|
|
# Make old-configure append to config.log, where we put our own log.
|
|
# This could be done with a m4 macro, but it's way easier this way
|
|
script = script.replace(b">./config.log", b">>${CONFIG_LOG=./config.log}")
|
|
|
|
with NamedTemporaryFile(
|
|
mode="wb",
|
|
prefix=os.path.basename(old_configure),
|
|
dir=os.path.dirname(old_configure),
|
|
delete=False,
|
|
) as fh:
|
|
fh.write(script)
|
|
|
|
try:
|
|
os.rename(fh.name, old_configure)
|
|
except OSError:
|
|
try:
|
|
# Likely the file already existed (on Windows). Retry after removing it.
|
|
os.remove(old_configure)
|
|
os.rename(fh.name, old_configure)
|
|
except OSError as e:
|
|
die("Failed re-creating old-configure: %s" % e.message)
|
|
|
|
old_configure_options = {
|
|
"prefix": prefix[0],
|
|
"includedir": includedir[0],
|
|
"libdir": libdir[0],
|
|
}
|
|
cmd = (
|
|
[shell, old_configure]
|
|
+ prepare_configure_options.options
|
|
+ [f"--{k}={v}" for k, v in old_configure_options.items()]
|
|
)
|
|
|
|
env = dict(os.environ)
|
|
|
|
# For debugging purpose, in case it's not what we'd expect.
|
|
log.debug("Running %s", quote(*cmd))
|
|
|
|
# Our logging goes to config.log, the same file old.configure uses.
|
|
# We can't share the handle on the file, so close it.
|
|
logger = logging.getLogger("moz.configure")
|
|
config_log = None
|
|
for handler in logger.handlers:
|
|
if isinstance(handler, logging.FileHandler):
|
|
config_log = handler
|
|
config_log.close()
|
|
logger.removeHandler(config_log)
|
|
env["CONFIG_LOG"] = config_log.baseFilename
|
|
log_size = os.path.getsize(config_log.baseFilename)
|
|
break
|
|
|
|
env["PATH"] = path
|
|
|
|
if extra_env:
|
|
env.update(extra_env)
|
|
|
|
env["OLD_CONFIGURE_VARS"] = os.path.join(
|
|
build_env.topobjdir, "old-configure.vars"
|
|
)
|
|
proc = subprocess.Popen(
|
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env
|
|
)
|
|
while True:
|
|
line = proc.stdout.readline()
|
|
if not line:
|
|
break
|
|
log.info(line.rstrip())
|
|
|
|
ret = proc.wait()
|
|
if ret:
|
|
with log.queue_debug():
|
|
if config_log:
|
|
with open(config_log.baseFilename, "r") as fh:
|
|
fh.seek(log_size)
|
|
for line in fh:
|
|
log.debug(line.rstrip())
|
|
log.error("old-configure failed")
|
|
sys.exit(ret)
|
|
|
|
if config_log:
|
|
# Create a new handler in append mode
|
|
handler = logging.FileHandler(config_log.baseFilename, mode="a", delay=True)
|
|
handler.setFormatter(config_log.formatter)
|
|
logger.addHandler(handler)
|
|
|
|
raw_config = {
|
|
"split": split,
|
|
"unique_list": unique_list,
|
|
}
|
|
with open("config.data", "r") as fh:
|
|
code = compile(fh.read(), "config.data", "exec")
|
|
exec(code, raw_config)
|
|
|
|
# Ensure all the flags known to old-configure appear in the
|
|
# @old_configure_options above.
|
|
all_options = set(prepare_configure_options.all_options)
|
|
for flag in raw_config["flags"]:
|
|
if flag not in all_options:
|
|
die(
|
|
"Missing option in `@old_configure_options` in %s: %s",
|
|
__file__,
|
|
flag,
|
|
)
|
|
|
|
# If the code execution above fails, we want to keep the file around for
|
|
# debugging.
|
|
os.remove("config.data")
|
|
|
|
return namespace(
|
|
**{
|
|
c: [
|
|
(k[1:-1], v[1:-1] if isinstance(v, str) else v)
|
|
for k, v in raw_config[c]
|
|
# Eventually we'll want to filter out all lowercase keys. (bug 1869127)
|
|
# For now, we only filter out the most problematic one that
|
|
# we know is unused.
|
|
if k != " target_cpu "
|
|
]
|
|
for c in ("substs", "defines")
|
|
}
|
|
)
|
|
|
|
return old_configure
|
|
|
|
|
|
old_configure = old_configure_for(old_configure)
|
|
set_config("OLD_CONFIGURE_SUBSTS", old_configure.substs)
|
|
set_config("OLD_CONFIGURE_DEFINES", old_configure.defines)
|
|
|
|
|
|
# Assuming no other option is declared after this function, handle the
|
|
# env options that were injected by mozconfig_options by creating dummy
|
|
# Option instances and having the sandbox's CommandLineHelper handle
|
|
# them. We only do so for options that haven't been declared so far,
|
|
# which should be a proxy for the options that old-configure handles
|
|
# and that we don't know anything about.
|
|
@depends("--help")
|
|
@imports("__sandbox__")
|
|
@imports(_from="mozbuild.configure.options", _import="Option")
|
|
def remaining_mozconfig_options(_):
|
|
helper = __sandbox__._helper
|
|
for arg in list(helper):
|
|
if helper._origins[arg] != "mozconfig":
|
|
continue
|
|
name = arg.split("=", 1)[0]
|
|
if name.isupper() and name not in __sandbox__._options:
|
|
option = Option(env=name, nargs="*", help=name)
|
|
helper.handle(option)
|
|
|
|
|
|
# Please do not add anything after remaining_mozconfig_options()
|