Bug 1735754 [wpt PR 31232] - Make the higher-order comparisons in the structured...
[gecko.git] / configure.py
blob445dd429ee7ded6e52f2c2490ecb26b6e86929b5
1 # This Source Code Form is subject to the terms of the Mozilla Public
2 # License, v. 2.0. If a copy of the MPL was not distributed with this
3 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 from __future__ import absolute_import, print_function, unicode_literals
7 import codecs
8 import errno
9 import io
10 import itertools
11 import logging
12 import os
13 import sys
14 import textwrap
16 from collections.abc import Iterable
18 base_dir = os.path.abspath(os.path.dirname(__file__))
19 sys.path.insert(0, os.path.join(base_dir, "python", "mach"))
20 sys.path.insert(0, os.path.join(base_dir, "python", "mozboot"))
21 sys.path.insert(0, os.path.join(base_dir, "python", "mozbuild"))
22 sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "packaging"))
23 sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "pyparsing"))
24 sys.path.insert(0, os.path.join(base_dir, "third_party", "python", "six"))
25 from mozbuild.configure import (
26 ConfigureSandbox,
27 TRACE,
29 from mozbuild.pythonutil import iter_modules_in_path
30 from mozbuild.backend.configenvironment import PartialConfigEnvironment
31 from mozbuild.util import write_indented_repr
32 import mozpack.path as mozpath
33 import six
36 def main(argv):
37 config = {}
39 if "OLD_CONFIGURE" not in os.environ:
40 os.environ["OLD_CONFIGURE"] = os.path.join(base_dir, "old-configure")
42 sandbox = ConfigureSandbox(config, os.environ, argv)
44 clobber_file = "CLOBBER"
45 if not os.path.exists(clobber_file):
46 # Simply touch the file.
47 with open(clobber_file, "a"):
48 pass
50 if os.environ.get("MOZ_CONFIGURE_TRACE"):
51 sandbox._logger.setLevel(TRACE)
53 sandbox.run(os.path.join(os.path.dirname(__file__), "moz.configure"))
55 if sandbox._help:
56 return 0
58 logging.getLogger("moz.configure").info("Creating config.status")
60 old_js_configure_substs = config.pop("OLD_JS_CONFIGURE_SUBSTS", None)
61 old_js_configure_defines = config.pop("OLD_JS_CONFIGURE_DEFINES", None)
62 if old_js_configure_substs or old_js_configure_defines:
63 js_config = config.copy()
64 pwd = os.getcwd()
65 try:
66 try:
67 os.makedirs("js/src")
68 except OSError as e:
69 if e.errno != errno.EEXIST:
70 raise
72 os.chdir("js/src")
73 js_config["OLD_CONFIGURE_SUBSTS"] = old_js_configure_substs
74 js_config["OLD_CONFIGURE_DEFINES"] = old_js_configure_defines
75 # The build system frontend expects $objdir/js/src/config.status
76 # to have $objdir/js/src as topobjdir.
77 # We want forward slashes on all platforms.
78 js_config["TOPOBJDIR"] += "/js/src"
79 config_status(js_config, execute=False)
80 finally:
81 os.chdir(pwd)
83 return config_status(config)
86 def check_unicode(obj):
87 """Recursively check that all strings in the object are unicode strings."""
88 if isinstance(obj, dict):
89 result = True
90 for k, v in six.iteritems(obj):
91 if not check_unicode(k):
92 print("%s key is not unicode." % k, file=sys.stderr)
93 result = False
94 elif not check_unicode(v):
95 print("%s value is not unicode." % k, file=sys.stderr)
96 result = False
97 return result
98 if isinstance(obj, bytes):
99 return False
100 if isinstance(obj, six.text_type):
101 return True
102 if isinstance(obj, Iterable):
103 return all(check_unicode(o) for o in obj)
104 return True
107 def config_status(config, execute=True):
108 # Sanitize config data to feed config.status
109 # Ideally, all the backend and frontend code would handle the booleans, but
110 # there are so many things involved, that it's easier to keep config.status
111 # untouched for now.
112 def sanitize_config(v):
113 if v is True:
114 return "1"
115 if v is False:
116 return ""
117 # Serialize types that look like lists and tuples as lists.
118 if not isinstance(v, (bytes, six.text_type, dict)) and isinstance(v, Iterable):
119 return list(v)
120 return v
122 sanitized_config = {}
123 sanitized_config["substs"] = {
124 k: sanitize_config(v)
125 for k, v in six.iteritems(config)
126 if k
127 not in (
128 "DEFINES",
129 "TOPSRCDIR",
130 "TOPOBJDIR",
131 "CONFIG_STATUS_DEPS",
132 "OLD_CONFIGURE_SUBSTS",
133 "OLD_CONFIGURE_DEFINES",
136 for k, v in config["OLD_CONFIGURE_SUBSTS"]:
137 sanitized_config["substs"][k] = sanitize_config(v)
138 sanitized_config["defines"] = {
139 k: sanitize_config(v) for k, v in six.iteritems(config["DEFINES"])
141 for k, v in config["OLD_CONFIGURE_DEFINES"]:
142 sanitized_config["defines"][k] = sanitize_config(v)
143 sanitized_config["topsrcdir"] = config["TOPSRCDIR"]
144 sanitized_config["topobjdir"] = config["TOPOBJDIR"]
145 sanitized_config["mozconfig"] = config.get("MOZCONFIG")
147 if not check_unicode(sanitized_config):
148 print("Configuration should be all unicode.", file=sys.stderr)
149 print("Please file a bug for the above.", file=sys.stderr)
150 sys.exit(1)
152 # Some values in sanitized_config also have more complex types, such as
153 # EnumString, which using when calling config_status would currently
154 # break the build, as well as making it inconsistent with re-running
155 # config.status, for which they are normalized to plain strings via
156 # indented_repr. Likewise for non-dict non-string iterables being
157 # converted to lists.
158 def normalize(obj):
159 if isinstance(obj, dict):
160 return {k: normalize(v) for k, v in six.iteritems(obj)}
161 if isinstance(obj, six.text_type):
162 return six.text_type(obj)
163 if isinstance(obj, Iterable):
164 return [normalize(o) for o in obj]
165 return obj
167 sanitized_config = normalize(sanitized_config)
169 # Create config.status. Eventually, we'll want to just do the work it does
170 # here, when we're able to skip configure tests/use cached results/not rely
171 # on autoconf.
172 with codecs.open("config.status", "w", "utf-8") as fh:
173 fh.write(
174 textwrap.dedent(
175 """\
176 #!%(python)s
177 # coding=utf-8
178 from __future__ import unicode_literals
181 % {"python": config["PYTHON3"]}
183 for k, v in sorted(six.iteritems(sanitized_config)):
184 fh.write("%s = " % k)
185 write_indented_repr(fh, v)
186 fh.write(
187 "__all__ = ['topobjdir', 'topsrcdir', 'defines', " "'substs', 'mozconfig']"
190 if execute:
191 fh.write(
192 textwrap.dedent(
194 if __name__ == '__main__':
195 from mozbuild.config_status import config_status
196 args = dict([(name, globals()[name]) for name in __all__])
197 config_status(**args)
202 partial_config = PartialConfigEnvironment(config["TOPOBJDIR"])
203 partial_config.write_vars(sanitized_config)
205 # Write out a file so the build backend knows to re-run configure when
206 # relevant Python changes.
207 with io.open("config_status_deps.in", "w", encoding="utf-8", newline="\n") as fh:
208 for f in sorted(
209 itertools.chain(
210 config["CONFIG_STATUS_DEPS"],
211 iter_modules_in_path(config["TOPOBJDIR"], config["TOPSRCDIR"]),
214 fh.write("%s\n" % mozpath.normpath(f))
216 # Other things than us are going to run this file, so we need to give it
217 # executable permissions.
218 os.chmod("config.status", 0o755)
219 if execute:
220 from mozbuild.config_status import config_status
222 return config_status(args=[], **sanitized_config)
223 return 0
226 if __name__ == "__main__":
227 sys.exit(main(sys.argv))