1 # -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
2 # vim: set filetype=python:
3 # This Source Code Form is subject to the terms of the Mozilla Public
4 # License, v. 2.0. If a copy of the MPL was not distributed with this
5 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
14 paths=prefer_mozillabuild_path,
19 def prepare_mozconfig(mozconfig):
22 for key, value in mozconfig["vars"]["added"].items():
23 items[key] = (value, "added")
24 for key, (old, value) in mozconfig["vars"]["modified"].items():
25 items[key] = (value, "modified")
26 for t in ("env", "vars"):
27 for key in mozconfig[t]["removed"].keys():
28 items[key] = (None, "removed " + t)
32 @depends("OLD_CONFIGURE", build_project)
33 def old_configure(old_configure, build_project):
35 die("The OLD_CONFIGURE environment variable must be set")
37 # os.path.abspath in the sandbox will ensure forward slashes on Windows,
38 # which is actually necessary because this path actually ends up literally
39 # as $0, and backslashes there breaks autoconf's detection of the source
41 old_configure = os.path.abspath(old_configure[0])
42 if build_project == "js":
43 old_configure_dir = os.path.dirname(old_configure)
44 if not old_configure_dir.endswith("/js/src"):
45 old_configure = os.path.join(
46 old_configure_dir, "js", "src", os.path.basename(old_configure)
51 @depends(prepare_mozconfig, old_configure_assignments)
52 @imports(_from="__builtin__", _import="open")
53 @imports(_from="__builtin__", _import="print")
54 @imports(_from="mozbuild.shellutil", _import="quote")
55 def prepare_configure(mozconfig, old_configure_assignments):
58 with open("old-configure.vars", "w") as out:
59 log.debug("Injecting the following to old-configure:")
62 print(command, file=out) # noqa Python 2vs3
63 log.debug("| %s", command)
66 inject("# start of mozconfig values")
67 for key, (value, action) in sorted(mozconfig.items()):
68 if action.startswith("removed "):
69 inject("unset %s # from %s" % (key, action[len("removed ") :]))
71 inject("%s=%s # %s" % (key, quote(value), action))
72 assignments[key] = value
74 inject("# end of mozconfig values")
76 for k, v in old_configure_assignments:
77 inject("%s=%s" % (k, quote(v)))
80 return namespace(assignments=assignments)
84 def old_configure_options(*options):
86 option(opt, nargs="*", help="Help missing for old configure options")
93 host_for_sub_configure, target_for_sub_configure, all_options, *options
97 @old_configure_options(
100 "--enable-official-branding",
105 "--with-distribution-id",
106 "--with-macbundlename-prefix",
110 def prepare_configure_options(host, target, all_options, *options):
111 # old-configure only supports the options listed in @old_configure_options
112 # so we don't need to pass it every single option we've been passed. Only
113 # the ones that are not supported by python configure need to.
116 for name, value in zip(all_options, options)
117 if value.origin != "default"
120 return namespace(options=options, all_options=all_options)
124 def old_configure_for(old_configure_path, extra_env=None):
125 if extra_env is None:
126 extra_env = dependable(None)
130 prepare_configure_options,
131 prefer_mozillabuild_path,
141 @imports(_from="__builtin__", _import="compile")
142 @imports(_from="__builtin__", _import="open")
143 @imports(_from="__builtin__", _import="OSError")
145 @imports("itertools")
149 @imports("subprocess")
151 @imports(_from="mozbuild.shellutil", _import="quote")
152 @imports(_from="mozbuild.shellutil", _import="split")
153 @imports(_from="tempfile", _import="NamedTemporaryFile")
154 @imports(_from="subprocess", _import="CalledProcessError")
155 @imports(_from="__builtin__", _import="exec")
158 prepare_configure_options,
159 prefer_mozillabuild_path,
172 path = os.pathsep.join(prefer_mozillabuild_path)
174 cxx = prepare_configure.assignments.get("CXX")
175 cc = prepare_configure.assignments.get("CC")
178 if cache_file_option:
179 config_cache = cache_file_option[0]
181 config_cache = "config.cache"
183 if os.path.exists(config_cache):
185 with open(config_cache, "r") as cfg_cache:
186 cxx_pattern = re.compile(
187 r"^ac_cv_prog_CXX=\${ac_cv_prog_CXX='(.*)'}$"
189 cc_pattern = re.compile(r"^ac_cv_prog_CC=\${ac_cv_prog_CC='(.*)'}$")
190 for line in cfg_cache:
191 m = cc_pattern.match(line)
192 if m and m.group(1) != cc:
195 m = cxx_pattern.match(line)
196 if m and m.group(1) != cxx:
201 log.info("invalidating config.cache")
202 os.remove(config_cache)
205 if os.path.exists(old_configure):
206 mtime = os.path.getmtime(old_configure)
207 aclocal = os.path.join(build_env.topsrcdir, "build", "autoconf", "*.m4")
208 for input in itertools.chain(
210 old_configure + ".in",
211 os.path.join(os.path.dirname(old_configure), "aclocal.m4"),
215 if os.path.getmtime(input) > mtime:
221 autoconf = os.path.join(
222 build_env.topsrcdir, "build", "autoconf", "autoconf.sh"
224 log.info("Refreshing %s with %s", old_configure, autoconf)
225 env = dict(os.environ)
228 env["AC_MACRODIR"] = os.path.join(build_env.topsrcdir, "build", "autoconf")
232 script = subprocess.check_output(
236 "--localdir=%s" % os.path.dirname(old_configure),
237 old_configure + ".in",
239 # Fix the working directory, so that when m4 is called, that
240 # includes of relative paths are deterministically resolved
241 # relative to the directory containing old-configure.
242 cwd=os.path.dirname(old_configure),
245 except CalledProcessError as exc:
246 die("autoconf exited with return code {}".format(exc.returncode))
250 "Generated old-configure is empty! Check that your autoconf 2.13 program works!"
253 # Make old-configure append to config.log, where we put our own log.
254 # This could be done with a m4 macro, but it's way easier this way
255 script = script.replace(b">./config.log", b">>${CONFIG_LOG=./config.log}")
257 with NamedTemporaryFile(
259 prefix=os.path.basename(old_configure),
260 dir=os.path.dirname(old_configure),
266 os.rename(fh.name, old_configure)
269 # Likely the file already existed (on Windows). Retry after removing it.
270 os.remove(old_configure)
271 os.rename(fh.name, old_configure)
273 die("Failed re-creating old-configure: %s" % e.message)
275 cmd = [shell, old_configure] + prepare_configure_options.options
277 env = dict(os.environ)
279 # For debugging purpose, in case it's not what we'd expect.
280 log.debug("Running %s", quote(*cmd))
282 # Our logging goes to config.log, the same file old.configure uses.
283 # We can't share the handle on the file, so close it.
284 logger = logging.getLogger("moz.configure")
286 for handler in logger.handlers:
287 if isinstance(handler, logging.FileHandler):
290 logger.removeHandler(config_log)
291 env["CONFIG_LOG"] = config_log.baseFilename
292 log_size = os.path.getsize(config_log.baseFilename)
298 env.update(extra_env)
300 env["OLD_CONFIGURE_VARS"] = os.path.join(
301 build_env.topobjdir, "old-configure.vars"
303 proc = subprocess.Popen(
304 cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env
307 line = proc.stdout.readline()
310 log.info(line.rstrip())
314 with log.queue_debug():
316 with open(config_log.baseFilename, "r") as fh:
319 log.debug(line.rstrip())
320 log.error("old-configure failed")
324 # Create a new handler in append mode
325 handler = logging.FileHandler(config_log.baseFilename, mode="a", delay=True)
326 handler.setFormatter(config_log.formatter)
327 logger.addHandler(handler)
331 "unique_list": unique_list,
333 with open("config.data", "r") as fh:
334 code = compile(fh.read(), "config.data", "exec")
335 exec(code, raw_config)
337 # Ensure all the flags known to old-configure appear in the
338 # @old_configure_options above.
339 all_options = set(prepare_configure_options.all_options)
340 for flag in raw_config["flags"]:
341 if flag not in all_options:
343 "Missing option in `@old_configure_options` in %s: %s",
348 # If the code execution above fails, we want to keep the file around for
350 os.remove("config.data")
355 (k[1:-1], v[1:-1] if isinstance(v, str) else v)
356 for k, v in raw_config[c]
357 # Eventually we'll want to filter out all lowercase keys. (bug 1869127)
358 # For now, we only filter out the most problematic one that
360 if k != " target_cpu "
362 for c in ("substs", "defines")
369 old_configure = old_configure_for(old_configure)
370 set_config("OLD_CONFIGURE_SUBSTS", old_configure.substs)
371 set_config("OLD_CONFIGURE_DEFINES", old_configure.defines)
374 # Assuming no other option is declared after this function, handle the
375 # env options that were injected by mozconfig_options by creating dummy
376 # Option instances and having the sandbox's CommandLineHelper handle
377 # them. We only do so for options that haven't been declared so far,
378 # which should be a proxy for the options that old-configure handles
379 # and that we don't know anything about.
381 @imports("__sandbox__")
382 @imports(_from="mozbuild.configure.options", _import="Option")
383 def remaining_mozconfig_options(_):
384 helper = __sandbox__._helper
385 for arg in list(helper):
386 if helper._origins[arg] != "mozconfig":
388 name = arg.split("=", 1)[0]
389 if name.isupper() and name not in __sandbox__._options:
390 option = Option(env=name, nargs="*", help=name)
391 helper.handle(option)
394 # Please do not add anything after remaining_mozconfig_options()