Bug 1670884 [wpt PR 26078] - [:is/:where] Avoid scope-contains-last-matched-element...
[gecko.git] / configure.py
blob5b9c9494e58fb1fdea0717bb7f514ecece0b2b51
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
17 try:
18 from collections.abc import Iterable
19 except ImportError:
20 from collections import Iterable
23 base_dir = os.path.abspath(os.path.dirname(__file__))
24 sys.path.insert(0, os.path.join(base_dir, 'python', 'mozboot'))
25 sys.path.insert(0, os.path.join(base_dir, 'python', 'mozbuild'))
26 sys.path.insert(0, os.path.join(base_dir, 'third_party', 'python', 'six'))
27 from mozbuild.configure import (
28 ConfigureSandbox,
29 TRACE,
31 from mozbuild.pythonutil import iter_modules_in_path
32 from mozbuild.backend.configenvironment import PartialConfigEnvironment
33 from mozbuild.util import (
34 write_indented_repr,
36 import mozpack.path as mozpath
37 import six
40 def main(argv):
41 config = {}
43 sandbox = ConfigureSandbox(config, os.environ, argv)
45 clobber_file = 'CLOBBER'
46 if not os.path.exists(clobber_file):
47 # Simply touch the file.
48 with open(clobber_file, 'a'):
49 pass
51 if os.environ.get('MOZ_CONFIGURE_TRACE'):
52 sandbox._logger.setLevel(TRACE)
54 sandbox.run(os.path.join(os.path.dirname(__file__), 'moz.configure'))
56 if sandbox._help:
57 return 0
59 logging.getLogger('moz.configure').info('Creating config.status')
61 old_js_configure_substs = config.pop('OLD_JS_CONFIGURE_SUBSTS', None)
62 old_js_configure_defines = config.pop('OLD_JS_CONFIGURE_DEFINES', None)
63 if old_js_configure_substs or old_js_configure_defines:
64 js_config = config.copy()
65 pwd = os.getcwd()
66 try:
67 try:
68 os.makedirs('js/src')
69 except OSError as e:
70 if e.errno != errno.EEXIST:
71 raise
73 os.chdir('js/src')
74 js_config['OLD_CONFIGURE_SUBSTS'] = old_js_configure_substs
75 js_config['OLD_CONFIGURE_DEFINES'] = old_js_configure_defines
76 # The build system frontend expects $objdir/js/src/config.status
77 # to have $objdir/js/src as topobjdir.
78 # We want forward slashes on all platforms.
79 js_config['TOPOBJDIR'] += '/js/src'
80 config_status(js_config, execute=False)
81 finally:
82 os.chdir(pwd)
84 return config_status(config)
87 def check_unicode(obj):
88 '''Recursively check that all strings in the object are unicode strings.'''
89 if isinstance(obj, dict):
90 result = True
91 for k, v in six.iteritems(obj):
92 if not check_unicode(k):
93 print("%s key is not unicode." % k, file=sys.stderr)
94 result = False
95 elif not check_unicode(v):
96 print("%s value is not unicode." % k, file=sys.stderr)
97 result = False
98 return result
99 if isinstance(obj, bytes):
100 return False
101 if isinstance(obj, six.text_type):
102 return True
103 if isinstance(obj, Iterable):
104 return all(check_unicode(o) for o in obj)
105 return True
108 def config_status(config, execute=True):
109 # Sanitize config data to feed config.status
110 # Ideally, all the backend and frontend code would handle the booleans, but
111 # there are so many things involved, that it's easier to keep config.status
112 # untouched for now.
113 def sanitize_config(v):
114 if v is True:
115 return '1'
116 if v is False:
117 return ''
118 # Serialize types that look like lists and tuples as lists.
119 if not isinstance(v, (bytes, six.text_type, dict)) and isinstance(v, Iterable):
120 return list(v)
121 return v
123 sanitized_config = {}
124 sanitized_config['substs'] = {
125 k: sanitize_config(v) for k, v in six.iteritems(config)
126 if k not in ('DEFINES', 'TOPSRCDIR', 'TOPOBJDIR', 'CONFIG_STATUS_DEPS',
127 'OLD_CONFIGURE_SUBSTS', 'OLD_CONFIGURE_DEFINES')
129 for k, v in config['OLD_CONFIGURE_SUBSTS']:
130 sanitized_config['substs'][k] = sanitize_config(v)
131 sanitized_config['defines'] = {
132 k: sanitize_config(v) for k, v in six.iteritems(config['DEFINES'])
134 for k, v in config['OLD_CONFIGURE_DEFINES']:
135 sanitized_config['defines'][k] = sanitize_config(v)
136 sanitized_config['topsrcdir'] = config['TOPSRCDIR']
137 sanitized_config['topobjdir'] = config['TOPOBJDIR']
138 sanitized_config['mozconfig'] = config.get('MOZCONFIG')
140 if not check_unicode(sanitized_config):
141 print("Configuration should be all unicode.", file=sys.stderr)
142 print("Please file a bug for the above.", file=sys.stderr)
143 sys.exit(1)
145 # Some values in sanitized_config also have more complex types, such as
146 # EnumString, which using when calling config_status would currently
147 # break the build, as well as making it inconsistent with re-running
148 # config.status, for which they are normalized to plain strings via
149 # indented_repr. Likewise for non-dict non-string iterables being
150 # converted to lists.
151 def normalize(obj):
152 if isinstance(obj, dict):
153 return {
154 k: normalize(v)
155 for k, v in six.iteritems(obj)
157 if isinstance(obj, six.text_type):
158 return six.text_type(obj)
159 if isinstance(obj, Iterable):
160 return [normalize(o) for o in obj]
161 return obj
163 sanitized_config = normalize(sanitized_config)
165 # Create config.status. Eventually, we'll want to just do the work it does
166 # here, when we're able to skip configure tests/use cached results/not rely
167 # on autoconf.
168 with codecs.open('config.status', 'w', 'utf-8') as fh:
169 fh.write(textwrap.dedent('''\
170 #!%(python)s
171 # coding=utf-8
172 from __future__ import unicode_literals
173 ''') % {'python': config['PYTHON3']})
174 for k, v in sorted(six.iteritems(sanitized_config)):
175 fh.write('%s = ' % k)
176 write_indented_repr(fh, v)
177 fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', "
178 "'substs', 'mozconfig']")
180 if execute:
181 fh.write(textwrap.dedent('''
182 if __name__ == '__main__':
183 from mozbuild.util import patch_main
184 patch_main()
185 from mozbuild.config_status import config_status
186 args = dict([(name, globals()[name]) for name in __all__])
187 config_status(**args)
188 '''))
190 partial_config = PartialConfigEnvironment(config['TOPOBJDIR'])
191 partial_config.write_vars(sanitized_config)
193 # Write out a file so the build backend knows to re-run configure when
194 # relevant Python changes.
195 with io.open('config_status_deps.in', 'w', encoding='utf-8',
196 newline='\n') as fh:
197 for f in sorted(
198 itertools.chain(config['CONFIG_STATUS_DEPS'],
199 iter_modules_in_path(config['TOPOBJDIR'],
200 config['TOPSRCDIR']))):
201 fh.write('%s\n' % mozpath.normpath(f))
203 # Other things than us are going to run this file, so we need to give it
204 # executable permissions.
205 os.chmod('config.status', 0o755)
206 if execute:
207 from mozbuild.config_status import config_status
208 return config_status(args=[], **sanitized_config)
209 return 0
212 if __name__ == '__main__':
213 sys.exit(main(sys.argv))