Bug 1892041 - Part 1: Update test262 features. r=spidermonkey-reviewers,dminor
[gecko.git] / build / moz.configure / old.configure
bloba37fd265a274a0d8d837821211b9047de3ee1c65
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/.
8 m4 = check_prog(
9     "M4",
10     (
11         "gm4",
12         "m4",
13     ),
14     paths=prefer_mozillabuild_path,
18 @depends(mozconfig)
19 def prepare_mozconfig(mozconfig):
20     if mozconfig["path"]:
21         items = {}
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)
29         return items
32 @depends("OLD_CONFIGURE", build_project)
33 def old_configure(old_configure, build_project):
34     if not old_configure:
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
40     # directory.
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)
47             )
48     return 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):
56     assignments = {}
58     with open("old-configure.vars", "w") as out:
59         log.debug("Injecting the following to old-configure:")
61         def inject(command):
62             print(command, file=out)  # noqa Python 2vs3
63             log.debug("| %s", command)
65         if mozconfig:
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 ") :]))
70                 else:
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)))
78             assignments[k] = v
80     return namespace(assignments=assignments)
83 @template
84 def old_configure_options(*options):
85     for opt in options:
86         option(opt, nargs="*", help="Help missing for old configure options")
88     @dependable
89     def all_options():
90         return list(options)
92     return depends(
93         host_for_sub_configure, target_for_sub_configure, all_options, *options
94     )
97 @old_configure_options(
98     "--cache-file",
99     "--datadir",
100     "--enable-official-branding",
101     "--includedir",
102     "--libdir",
103     "--prefix",
104     "--with-branding",
105     "--with-distribution-id",
106     "--with-macbundlename-prefix",
107     "--x-includes",
108     "--x-libraries",
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.
114     options = [
115         value.format(name)
116         for name, value in zip(all_options, options)
117         if value.origin != "default"
118     ] + [host, target]
120     return namespace(options=options, all_options=all_options)
123 @template
124 def old_configure_for(old_configure_path, extra_env=None):
125     if extra_env is None:
126         extra_env = dependable(None)
128     @depends(
129         prepare_configure,
130         prepare_configure_options,
131         prefer_mozillabuild_path,
132         altered_path,
133         extra_env,
134         build_environment,
135         old_configure_path,
136         awk,
137         m4,
138         shell,
139         "--cache-file",
140     )
141     @imports(_from="__builtin__", _import="compile")
142     @imports(_from="__builtin__", _import="open")
143     @imports(_from="__builtin__", _import="OSError")
144     @imports("glob")
145     @imports("itertools")
146     @imports("logging")
147     @imports("os")
148     @imports("re")
149     @imports("subprocess")
150     @imports("sys")
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")
156     def old_configure(
157         prepare_configure,
158         prepare_configure_options,
159         prefer_mozillabuild_path,
160         altered_path,
161         extra_env,
162         build_env,
163         old_configure,
164         awk,
165         m4,
166         shell,
167         cache_file_option,
168     ):
169         if altered_path:
170             path = altered_path
171         else:
172             path = os.pathsep.join(prefer_mozillabuild_path)
174         cxx = prepare_configure.assignments.get("CXX")
175         cc = prepare_configure.assignments.get("CC")
177         if cc and cxx:
178             if cache_file_option:
179                 config_cache = cache_file_option[0]
180             else:
181                 config_cache = "config.cache"
183             if os.path.exists(config_cache):
184                 remove = False
185                 with open(config_cache, "r") as cfg_cache:
186                     cxx_pattern = re.compile(
187                         r"^ac_cv_prog_CXX=\${ac_cv_prog_CXX='(.*)'}$"
188                     )
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:
193                             remove = True
194                             break
195                         m = cxx_pattern.match(line)
196                         if m and m.group(1) != cxx:
197                             remove = True
198                             break
200                 if remove:
201                     log.info("invalidating config.cache")
202                     os.remove(config_cache)
204         refresh = True
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(
209                 (
210                     old_configure + ".in",
211                     os.path.join(os.path.dirname(old_configure), "aclocal.m4"),
212                 ),
213                 glob.iglob(aclocal),
214             ):
215                 if os.path.getmtime(input) > mtime:
216                     break
217             else:
218                 refresh = False
220         if refresh:
221             autoconf = os.path.join(
222                 build_env.topsrcdir, "build", "autoconf", "autoconf.sh"
223             )
224             log.info("Refreshing %s with %s", old_configure, autoconf)
225             env = dict(os.environ)
226             env["M4"] = m4
227             env["AWK"] = awk
228             env["AC_MACRODIR"] = os.path.join(build_env.topsrcdir, "build", "autoconf")
229             env["PATH"] = path
231             try:
232                 script = subprocess.check_output(
233                     [
234                         shell,
235                         autoconf,
236                         "--localdir=%s" % os.path.dirname(old_configure),
237                         old_configure + ".in",
238                     ],
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),
243                     env=env,
244                 )
245             except CalledProcessError as exc:
246                 die("autoconf exited with return code {}".format(exc.returncode))
248             if not script:
249                 die(
250                     "Generated old-configure is empty! Check that your autoconf 2.13 program works!"
251                 )
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(
258                 mode="wb",
259                 prefix=os.path.basename(old_configure),
260                 dir=os.path.dirname(old_configure),
261                 delete=False,
262             ) as fh:
263                 fh.write(script)
265             try:
266                 os.rename(fh.name, old_configure)
267             except OSError:
268                 try:
269                     # Likely the file already existed (on Windows). Retry after removing it.
270                     os.remove(old_configure)
271                     os.rename(fh.name, old_configure)
272                 except OSError as e:
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")
285         config_log = None
286         for handler in logger.handlers:
287             if isinstance(handler, logging.FileHandler):
288                 config_log = handler
289                 config_log.close()
290                 logger.removeHandler(config_log)
291                 env["CONFIG_LOG"] = config_log.baseFilename
292                 log_size = os.path.getsize(config_log.baseFilename)
293                 break
295         env["PATH"] = path
297         if extra_env:
298             env.update(extra_env)
300         env["OLD_CONFIGURE_VARS"] = os.path.join(
301             build_env.topobjdir, "old-configure.vars"
302         )
303         proc = subprocess.Popen(
304             cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env
305         )
306         while True:
307             line = proc.stdout.readline()
308             if not line:
309                 break
310             log.info(line.rstrip())
312         ret = proc.wait()
313         if ret:
314             with log.queue_debug():
315                 if config_log:
316                     with open(config_log.baseFilename, "r") as fh:
317                         fh.seek(log_size)
318                         for line in fh:
319                             log.debug(line.rstrip())
320                 log.error("old-configure failed")
321             sys.exit(ret)
323         if config_log:
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)
329         raw_config = {
330             "split": split,
331             "unique_list": unique_list,
332         }
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:
342                 die(
343                     "Missing option in `@old_configure_options` in %s: %s",
344                     __file__,
345                     flag,
346                 )
348         # If the code execution above fails, we want to keep the file around for
349         # debugging.
350         os.remove("config.data")
352         return namespace(
353             **{
354                 c: [
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
359                     # we know is unused.
360                     if k != " target_cpu "
361                 ]
362                 for c in ("substs", "defines")
363             }
364         )
366     return old_configure
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.
380 @depends("--help")
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":
387             continue
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()