def test_dependencies(self): """Test getting dependencies on defines and substs.""" env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self._assert_deps(env, []) self.assertEqual(env.defines["MOZ_FOO"], "1") self._assert_deps(env, ["defines/MOZ_FOO"]) self.assertEqual(env.defines["MOZ_BAR"], "2") self._assert_deps(env, ["defines/MOZ_FOO", "defines/MOZ_BAR"]) # Getting a define again shouldn't add a redundant dependency self.assertEqual(env.defines["MOZ_FOO"], "1") self._assert_deps(env, ["defines/MOZ_FOO", "defines/MOZ_BAR"]) self.assertEqual(env.substs["MOZ_SUBST_1"], "1") self._assert_deps( env, ["defines/MOZ_FOO", "defines/MOZ_BAR", "substs/MOZ_SUBST_1"]) with self.assertRaises(KeyError): _ = env.substs["NON_EXISTENT"] self._assert_deps( env, [ "defines/MOZ_FOO", "defines/MOZ_BAR", "substs/MOZ_SUBST_1", "substs/NON_EXISTENT", ], ) self.assertEqual(env.substs.get("NON_EXISTENT"), None)
def test_dependencies(self): '''Test getting dependencies on defines and substs. ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self._assert_deps(env, []) self.assertEqual(env.defines['MOZ_FOO'], '1') self._assert_deps(env, ['defines/MOZ_FOO']) self.assertEqual(env.defines['MOZ_BAR'], '2') self._assert_deps(env, ['defines/MOZ_FOO', 'defines/MOZ_BAR']) # Getting a define again shouldn't add a redundant dependency self.assertEqual(env.defines['MOZ_FOO'], '1') self._assert_deps(env, ['defines/MOZ_FOO', 'defines/MOZ_BAR']) self.assertEqual(env.substs['MOZ_SUBST_1'], '1') self._assert_deps( env, ['defines/MOZ_FOO', 'defines/MOZ_BAR', 'substs/MOZ_SUBST_1']) with self.assertRaises(KeyError): x = env.substs['NON_EXISTENT'] self._assert_deps(env, [ 'defines/MOZ_FOO', 'defines/MOZ_BAR', 'substs/MOZ_SUBST_1', 'substs/NON_EXISTENT' ]) self.assertEqual(env.substs.get('NON_EXISTENT'), None)
def test_auto_substs(self): '''Test the automatically set values of ACDEFINES, and ALLDEFINES ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs['ACDEFINES'], '-DMOZ_BAR=2 -DMOZ_FOO=1') self.assertEqual(env.defines['ALLDEFINES'], { 'MOZ_BAR': '2', 'MOZ_FOO': '1', })
def test_auto_substs(self): '''Test the automatically set values of ACDEFINES, and ALLDEFINES ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs['ACDEFINES'], '-DMOZ_BAR=2 -DMOZ_FOO=1') self.assertEqual(env.defines['ALLDEFINES'], { 'MOZ_BAR': '2', 'MOZ_FOO': '1', })
def test_auto_substs(self): """Test the automatically set values of ACDEFINES, and ALLDEFINES""" env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs["ACDEFINES"], "-DMOZ_BAR=2 -DMOZ_FOO=1") self.assertEqual( env.defines["ALLDEFINES"], { "MOZ_BAR": "2", "MOZ_FOO": "1", }, )
def test_set_subst(self): '''Test setting a subst ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs['MOZ_SUBST_1'], '1') env.substs['MOZ_SUBST_1'] = 'updated' self.assertEqual(env.substs['MOZ_SUBST_1'], 'updated') # A new environment should pull the result from the file again. newenv = PartialConfigEnvironment(env.topobjdir) self.assertEqual(newenv.substs['MOZ_SUBST_1'], '1')
def test_remove_subst(self): '''Test removing a subst from the config. The file should be overwritten with 'None' ''' env = PartialConfigEnvironment(self._objdir()) path = mozpath.join(env.topobjdir, 'config.statusd', 'substs', 'MYSUBST') myconfig = config.copy() env.write_vars(myconfig) with self.assertRaises(KeyError): x = env.substs['MYSUBST'] self.assertFalse(os.path.exists(path)) myconfig['substs']['MYSUBST'] = 'new' env.write_vars(myconfig) self.assertEqual(env.substs['MYSUBST'], 'new') self.assertTrue(os.path.exists(path)) del myconfig['substs']['MYSUBST'] env.write_vars(myconfig) with self.assertRaises(KeyError): x = env.substs['MYSUBST'] # Now that the subst is gone, the file still needs to be present so that # make can update dependencies correctly. Overwriting the file with # 'None' is the same as deleting it as far as the # PartialConfigEnvironment is concerned, but make can't track a # dependency on a file that doesn't exist. self.assertTrue(os.path.exists(path))
def test_update(self): """Test calling update on the substs or defines pseudo dicts""" env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) mysubsts = {"NEW": "new"} mysubsts.update(env.substs.iteritems()) self.assertEqual(mysubsts["NEW"], "new") self.assertEqual(mysubsts["CPP"], "cpp") mydefines = {"DEBUG": "1"} mydefines.update(env.defines.iteritems()) self.assertEqual(mydefines["DEBUG"], "1") self.assertEqual(mydefines["MOZ_FOO"], "1")
def test_update(self): '''Test calling update on the substs or defines pseudo dicts ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) mysubsts = {'NEW': 'new'} mysubsts.update(env.substs.iteritems()) self.assertEqual(mysubsts['NEW'], 'new') self.assertEqual(mysubsts['CPP'], 'cpp') mydefines = {'DEBUG': '1'} mydefines.update(env.defines.iteritems()) self.assertEqual(mydefines['DEBUG'], '1') self.assertEqual(mydefines['MOZ_FOO'], '1')
def test_update(self): '''Test calling update on the substs or defines pseudo dicts ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) mysubsts = {'NEW': 'new'} mysubsts.update(env.substs.iteritems()) self.assertEqual(mysubsts['NEW'], 'new') self.assertEqual(mysubsts['CPP'], 'cpp') mydefines = {'DEBUG': '1'} mydefines.update(env.defines.iteritems()) self.assertEqual(mydefines['DEBUG'], '1') self.assertEqual(mydefines['MOZ_FOO'], '1')
def test_remove_subst(self): '''Test removing a subst from the config. The file should be overwritten with 'None' ''' env = PartialConfigEnvironment(self._objdir()) path = mozpath.join(env.topobjdir, 'config.statusd', 'substs', 'MYSUBST') myconfig = config.copy() env.write_vars(myconfig) with self.assertRaises(KeyError): x = env.substs['MYSUBST'] self.assertFalse(os.path.exists(path)) myconfig['substs']['MYSUBST'] = 'new' env.write_vars(myconfig) self.assertEqual(env.substs['MYSUBST'], 'new') self.assertTrue(os.path.exists(path)) del myconfig['substs']['MYSUBST'] env.write_vars(myconfig) with self.assertRaises(KeyError): x = env.substs['MYSUBST'] # Now that the subst is gone, the file still needs to be present so that # make can update dependencies correctly. Overwriting the file with # 'None' is the same as deleting it as far as the # PartialConfigEnvironment is concerned, but make can't track a # dependency on a file that doesn't exist. self.assertTrue(os.path.exists(path))
def test_env_override(self): '''Test overriding a subst with an environment variable ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs['MOZ_SUBST_1'], '1') self.assertEqual(env.substs['CPP'], 'cpp') # Reset the environment and set some environment variables. env = PartialConfigEnvironment(env.topobjdir) os.environ['MOZ_SUBST_1'] = 'subst 1 environ' os.environ['CPP'] = 'cpp environ' # The MOZ_SUBST_1 should be overridden by the environment, while CPP is # a special variable and should not. self.assertEqual(env.substs['MOZ_SUBST_1'], 'subst 1 environ') self.assertEqual(env.substs['CPP'], 'cpp')
def test_dependencies(self): '''Test getting dependencies on defines and substs. ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self._assert_deps(env, []) self.assertEqual(env.defines['MOZ_FOO'], '1') self._assert_deps(env, ['defines/MOZ_FOO']) self.assertEqual(env.defines['MOZ_BAR'], '2') self._assert_deps(env, ['defines/MOZ_FOO', 'defines/MOZ_BAR']) # Getting a define again shouldn't add a redundant dependency self.assertEqual(env.defines['MOZ_FOO'], '1') self._assert_deps(env, ['defines/MOZ_FOO', 'defines/MOZ_BAR']) self.assertEqual(env.substs['MOZ_SUBST_1'], '1') self._assert_deps(env, ['defines/MOZ_FOO', 'defines/MOZ_BAR', 'substs/MOZ_SUBST_1']) with self.assertRaises(KeyError): x = env.substs['NON_EXISTENT'] self._assert_deps(env, ['defines/MOZ_FOO', 'defines/MOZ_BAR', 'substs/MOZ_SUBST_1', 'substs/NON_EXISTENT']) self.assertEqual(env.substs.get('NON_EXISTENT'), None)
def test_set_subst(self): """Test setting a subst""" env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs["MOZ_SUBST_1"], "1") env.substs["MOZ_SUBST_1"] = "updated" self.assertEqual(env.substs["MOZ_SUBST_1"], "updated") # A new environment should pull the result from the file again. newenv = PartialConfigEnvironment(env.topobjdir) self.assertEqual(newenv.substs["MOZ_SUBST_1"], "1")
def test_set_subst(self): '''Test setting a subst ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs['MOZ_SUBST_1'], '1') env.substs['MOZ_SUBST_1'] = 'updated' self.assertEqual(env.substs['MOZ_SUBST_1'], 'updated') # A new environment should pull the result from the file again. newenv = PartialConfigEnvironment(env.topobjdir) self.assertEqual(newenv.substs['MOZ_SUBST_1'], '1')
def test_env_override(self): """Test overriding a subst with an environment variable""" env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs["MOZ_SUBST_1"], "1") self.assertEqual(env.substs["CPP"], "cpp") # Reset the environment and set some environment variables. env = PartialConfigEnvironment(env.topobjdir) os.environ["MOZ_SUBST_1"] = "subst 1 environ" os.environ["CPP"] = "cpp environ" # The MOZ_SUBST_1 should be overridden by the environment, while CPP is # a special variable and should not. self.assertEqual(env.substs["MOZ_SUBST_1"], "subst 1 environ") self.assertEqual(env.substs["CPP"], "cpp")
def test_env_override(self): '''Test overriding a subst with an environment variable ''' env = PartialConfigEnvironment(self._objdir()) env.write_vars(config) self.assertEqual(env.substs['MOZ_SUBST_1'], '1') self.assertEqual(env.substs['CPP'], 'cpp') # Reset the environment and set some environment variables. env = PartialConfigEnvironment(env.topobjdir) os.environ['MOZ_SUBST_1'] = 'subst 1 environ' os.environ['CPP'] = 'cpp environ' # The MOZ_SUBST_1 should be overridden by the environment, while CPP is # a special variable and should not. self.assertEqual(env.substs['MOZ_SUBST_1'], 'subst 1 environ') self.assertEqual(env.substs['CPP'], 'cpp')
# 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/. import sys from mozbuild.base import MozbuildObject from mozbuild.backend.configenvironment import PartialConfigEnvironment config = MozbuildObject.from_environment() partial_config = PartialConfigEnvironment(config.topobjdir) for var in ("topsrcdir", "topobjdir"): value = getattr(config, var) setattr(sys.modules[__name__], var, value) for var in ("defines", "substs", "get_dependencies"): value = getattr(partial_config, var) setattr(sys.modules[__name__], var, value)
def process_define_file(output, input): '''Creates the given config header. A config header is generated by taking the corresponding source file and replacing some #define/#undef occurences: "#undef NAME" is turned into "#define NAME VALUE" "#define NAME" is unchanged "#define NAME ORIGINAL_VALUE" is turned into "#define NAME VALUE" "#undef UNKNOWN_NAME" is turned into "/* #undef UNKNOWN_NAME */" Whitespaces are preserved. As a special rule, "#undef ALLDEFINES" is turned into "#define NAME VALUE" for all the defined variables. ''' path = os.path.abspath(input) config = PartialConfigEnvironment(topobjdir) if mozpath.basedir(path, [mozpath.join(topsrcdir, 'js/src')]) and \ not config.substs.get('JS_STANDALONE'): config = PartialConfigEnvironment(mozpath.join(topobjdir, 'js', 'src')) with open(path, 'rU') as input: r = re.compile( '^\s*#\s*(?P<cmd>[a-z]+)(?:\s+(?P<name>\S+)(?:\s+(?P<value>\S+))?)?', re.U) for l in input: m = r.match(l) if m: cmd = m.group('cmd') name = m.group('name') value = m.group('value') if name and cmd != 'endif': if name == 'ALLDEFINES': if cmd == 'define': raise Exception( '`#define ALLDEFINES` is not allowed in a ' 'CONFIGURE_DEFINE_FILE') defines = '\n'.join( sorted('#define %s %s' % (name, val) for name, val in config.defines['ALLDEFINES'].iteritems())) l = l[:m.start('cmd') - 1] \ + defines + l[m.end('name'):] elif name in config.defines: if cmd == 'define' and value: l = l[:m.start('value')] \ + str(config.defines[name]) \ + l[m.end('value'):] elif cmd == 'undef': l = l[:m.start('cmd')] \ + 'define' \ + l[m.end('cmd'):m.end('name')] \ + ' ' \ + str(config.defines[name]) \ + l[m.end('name'):] elif cmd == 'undef': l = '/* ' + l[:m.end('name')] + ' */' + l[m.end('name' ):] output.write(l) deps = {path} deps.update(config.get_dependencies()) return deps
def process_define_file(output, input): '''Creates the given config header. A config header is generated by taking the corresponding source file and replacing some #define/#undef occurences: "#undef NAME" is turned into "#define NAME VALUE" "#define NAME" is unchanged "#define NAME ORIGINAL_VALUE" is turned into "#define NAME VALUE" "#undef UNKNOWN_NAME" is turned into "/* #undef UNKNOWN_NAME */" Whitespaces are preserved. As a special rule, "#undef ALLDEFINES" is turned into "#define NAME VALUE" for all the defined variables. ''' path = os.path.abspath(input) config = PartialConfigEnvironment(topobjdir) if mozpath.basedir(path, [mozpath.join(topsrcdir, 'js/src')]) and \ not config.substs.get('JS_STANDALONE'): config = PartialConfigEnvironment(mozpath.join(topobjdir, 'js', 'src')) with open(path, 'rU') as input: r = re.compile('^\s*#\s*(?P<cmd>[a-z]+)(?:\s+(?P<name>\S+)(?:\s+(?P<value>\S+))?)?', re.U) for l in input: m = r.match(l) if m: cmd = m.group('cmd') name = m.group('name') value = m.group('value') if name: if name == 'ALLDEFINES': if cmd == 'define': raise Exception( '`#define ALLDEFINES` is not allowed in a ' 'CONFIGURE_DEFINE_FILE') def define_for_name(name, val): """WebRTC files like to define WINVER and _WIN32_WINNT via the command line, which raises a mass of macro redefinition warnings. Just handle those macros specially here.""" define = "#define {name} {val}".format(name=name, val=val) if name in ('WINVER', '_WIN32_WINNT'): return '#if !defined({name})\n{define}\n#endif' \ .format(name=name, define=define) return define defines = '\n'.join(sorted( define_for_name(name, val) for name, val in config.defines['ALLDEFINES'].items())) l = l[:m.start('cmd') - 1] \ + defines + l[m.end('name'):] elif cmd == 'define': if value and name in config.defines: l = l[:m.start('value')] \ + str(config.defines[name]) \ + l[m.end('value'):] elif cmd == 'undef': if name in config.defines: l = l[:m.start('cmd')] \ + 'define' \ + l[m.end('cmd'):m.end('name')] \ + ' ' \ + str(config.defines[name]) \ + l[m.end('name'):] else: l = '/* ' + l[:m.end('name')] + ' */' + l[m.end('name'):] output.write(l) deps = {path} deps.update(config.get_dependencies()) return deps
def config_status(config): # Sanitize config data to feed config.status # Ideally, all the backend and frontend code would handle the booleans, but # there are so many things involved, that it's easier to keep config.status # untouched for now. def sanitized_bools(v): if v is True: return '1' if v is False: return '' return v sanitized_config = {} sanitized_config['substs'] = { k: sanitized_bools(v) for k, v in six.iteritems(config) if k not in ('DEFINES', 'non_global_defines', 'TOPSRCDIR', 'TOPOBJDIR', 'CONFIG_STATUS_DEPS') } sanitized_config['defines'] = { k: sanitized_bools(v) for k, v in six.iteritems(config['DEFINES']) } sanitized_config['non_global_defines'] = config['non_global_defines'] sanitized_config['topsrcdir'] = config['TOPSRCDIR'] sanitized_config['topobjdir'] = config['TOPOBJDIR'] sanitized_config['mozconfig'] = config.get('MOZCONFIG') if not check_unicode(sanitized_config): print("Configuration should be all unicode.", file=sys.stderr) print("Please file a bug for the above.", file=sys.stderr) sys.exit(1) # Create config.status. Eventually, we'll want to just do the work it does # here, when we're able to skip configure tests/use cached results/not rely # on autoconf. logging.getLogger('moz.configure').info('Creating config.status') with codecs.open('config.status', 'w', 'utf-8') as fh: fh.write(textwrap.dedent('''\ #!%(python)s # coding=utf-8 from __future__ import unicode_literals ''') % {'python': config['PYTHON']}) for k, v in six.iteritems(sanitized_config): fh.write('%s = %s\n' % (k, indented_repr(v))) fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', " "'non_global_defines', 'substs', 'mozconfig']") if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'): fh.write(textwrap.dedent(''' if __name__ == '__main__': from mozbuild.util import patch_main patch_main() from mozbuild.config_status import config_status args = dict([(name, globals()[name]) for name in __all__]) config_status(**args) ''')) partial_config = PartialConfigEnvironment(config['TOPOBJDIR']) partial_config.write_vars(sanitized_config) # Write out a file so the build backend knows to re-run configure when # relevant Python changes. with open('config_status_deps.in', 'w') as fh: for f in itertools.chain(config['CONFIG_STATUS_DEPS'], iter_modules_in_path(config['TOPOBJDIR'], config['TOPSRCDIR'])): fh.write('%s\n' % mozpath.normpath(f)) # Other things than us are going to run this file, so we need to give it # executable permissions. os.chmod('config.status', 0o755) if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'): from mozbuild.config_status import config_status # Some values in sanitized_config also have more complex types, such as # EnumString, which using when calling config_status would currently # break the build, as well as making it inconsistent with re-running # config.status, for which they are normalized to plain strings via # indented_repr. Likewise for non-dict non-string iterables being # converted to lists. def normalize(obj): if isinstance(obj, dict): return { k: normalize(v) for k, v in six.iteritems(obj) } if isinstance(obj, six.text_type): return six.text_type(obj) if isinstance(obj, Iterable): return [normalize(o) for o in obj] return obj return config_status(args=[], **normalize(sanitized_config)) return 0
def config_status(config): # Sanitize config data to feed config.status # Ideally, all the backend and frontend code would handle the booleans, but # there are so many things involved, that it's easier to keep config.status # untouched for now. def sanitized_bools(v): if v is True: return '1' if v is False: return '' return v sanitized_config = {} sanitized_config['substs'] = { k: sanitized_bools(v) for k, v in config.iteritems() if k not in ('DEFINES', 'non_global_defines', 'TOPSRCDIR', 'TOPOBJDIR', 'ALL_CONFIGURE_PATHS') } sanitized_config['defines'] = { k: sanitized_bools(v) for k, v in config['DEFINES'].iteritems() } sanitized_config['non_global_defines'] = config['non_global_defines'] sanitized_config['topsrcdir'] = config['TOPSRCDIR'] sanitized_config['topobjdir'] = config['TOPOBJDIR'] sanitized_config['mozconfig'] = config.get('MOZCONFIG') # Create config.status. Eventually, we'll want to just do the work it does # here, when we're able to skip configure tests/use cached results/not rely # on autoconf. print("Creating config.status", file=sys.stderr) encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8' with codecs.open('config.status', 'w', encoding) as fh: fh.write( textwrap.dedent('''\ #!%(python)s # coding=%(encoding)s from __future__ import unicode_literals from mozbuild.util import encode encoding = '%(encoding)s' ''') % { 'python': config['PYTHON'], 'encoding': encoding }) # A lot of the build backend code is currently expecting byte # strings and breaks in subtle ways with unicode strings. (bug 1296508) for k, v in sanitized_config.iteritems(): fh.write('%s = encode(%s, encoding)\n' % (k, indented_repr(v))) fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', " "'non_global_defines', 'substs', 'mozconfig']") if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'): fh.write( textwrap.dedent(''' if __name__ == '__main__': from mozbuild.util import patch_main patch_main() from mozbuild.config_status import config_status args = dict([(name, globals()[name]) for name in __all__]) config_status(**args) ''')) partial_config = PartialConfigEnvironment(config['TOPOBJDIR']) partial_config.write_vars(sanitized_config) # Write out a depfile so Make knows to re-run configure when relevant Python # changes. mk = Makefile() rule = mk.create_rule() rule.add_targets(["%s/config.status" % config['TOPOBJDIR']]) rule.add_dependencies( itertools.chain( config['ALL_CONFIGURE_PATHS'], iter_modules_in_path(config['TOPOBJDIR'], config['TOPSRCDIR']))) with open('configure.d', 'w') as fh: mk.dump(fh) # Other things than us are going to run this file, so we need to give it # executable permissions. os.chmod('config.status', 0o755) if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'): from mozbuild.config_status import config_status # Some values in sanitized_config also have more complex types, such as # EnumString, which using when calling config_status would currently # break the build, as well as making it inconsistent with re-running # config.status. Fortunately, EnumString derives from unicode, so it's # covered by converting unicode strings. # A lot of the build backend code is currently expecting byte strings # and breaks in subtle ways with unicode strings. return config_status(args=[], **encode(sanitized_config, encoding)) return 0
def config_status(config, execute=True): # Sanitize config data to feed config.status # Ideally, all the backend and frontend code would handle the booleans, but # there are so many things involved, that it's easier to keep config.status # untouched for now. def sanitize_config(v): if v is True: return "1" if v is False: return "" # Serialize types that look like lists and tuples as lists. if not isinstance(v, (bytes, six.text_type, dict)) and isinstance( v, Iterable): return list(v) return v sanitized_config = {} sanitized_config["substs"] = { k: sanitize_config(v) for k, v in six.iteritems(config) if k not in ( "DEFINES", "TOPSRCDIR", "TOPOBJDIR", "CONFIG_STATUS_DEPS", "OLD_CONFIGURE_SUBSTS", "OLD_CONFIGURE_DEFINES", ) } for k, v in config["OLD_CONFIGURE_SUBSTS"]: sanitized_config["substs"][k] = sanitize_config(v) sanitized_config["defines"] = { k: sanitize_config(v) for k, v in six.iteritems(config["DEFINES"]) } for k, v in config["OLD_CONFIGURE_DEFINES"]: sanitized_config["defines"][k] = sanitize_config(v) sanitized_config["topsrcdir"] = config["TOPSRCDIR"] sanitized_config["topobjdir"] = config["TOPOBJDIR"] sanitized_config["mozconfig"] = config.get("MOZCONFIG") if not check_unicode(sanitized_config): print("Configuration should be all unicode.", file=sys.stderr) print("Please file a bug for the above.", file=sys.stderr) sys.exit(1) # Some values in sanitized_config also have more complex types, such as # EnumString, which using when calling config_status would currently # break the build, as well as making it inconsistent with re-running # config.status, for which they are normalized to plain strings via # indented_repr. Likewise for non-dict non-string iterables being # converted to lists. def normalize(obj): if isinstance(obj, dict): return {k: normalize(v) for k, v in six.iteritems(obj)} if isinstance(obj, six.text_type): return six.text_type(obj) if isinstance(obj, Iterable): return [normalize(o) for o in obj] return obj sanitized_config = normalize(sanitized_config) # Create config.status. Eventually, we'll want to just do the work it does # here, when we're able to skip configure tests/use cached results/not rely # on autoconf. with codecs.open("config.status", "w", "utf-8") as fh: fh.write( textwrap.dedent("""\ #!%(python)s # coding=utf-8 from __future__ import unicode_literals """) % {"python": config["PYTHON3"]}) for k, v in sorted(six.iteritems(sanitized_config)): fh.write("%s = " % k) write_indented_repr(fh, v) fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', " "'substs', 'mozconfig']") if execute: fh.write( textwrap.dedent(""" if __name__ == '__main__': from mozbuild.util import patch_main patch_main() from mozbuild.config_status import config_status args = dict([(name, globals()[name]) for name in __all__]) config_status(**args) """)) partial_config = PartialConfigEnvironment(config["TOPOBJDIR"]) partial_config.write_vars(sanitized_config) # Write out a file so the build backend knows to re-run configure when # relevant Python changes. with io.open("config_status_deps.in", "w", encoding="utf-8", newline="\n") as fh: for f in sorted( itertools.chain( config["CONFIG_STATUS_DEPS"], iter_modules_in_path(config["TOPOBJDIR"], config["TOPSRCDIR"]), )): fh.write("%s\n" % mozpath.normpath(f)) # Other things than us are going to run this file, so we need to give it # executable permissions. os.chmod("config.status", 0o755) if execute: from mozbuild.config_status import config_status return config_status(args=[], **sanitized_config) return 0
def process_define_file(output, input): """Creates the given config header. A config header is generated by taking the corresponding source file and replacing some #define/#undef occurences: "#undef NAME" is turned into "#define NAME VALUE" "#define NAME" is unchanged "#define NAME ORIGINAL_VALUE" is turned into "#define NAME VALUE" "#undef UNKNOWN_NAME" is turned into "/* #undef UNKNOWN_NAME */" Whitespaces are preserved. As a special rule, "#undef ALLDEFINES" is turned into "#define NAME VALUE" for all the defined variables. """ path = os.path.abspath(input) config = PartialConfigEnvironment(topobjdir) if mozpath.basedir(path, [mozpath.join(topsrcdir, "js/src") ]) and not config.substs.get("JS_STANDALONE"): config = PartialConfigEnvironment(mozpath.join(topobjdir, "js", "src")) with open(path, "rU") as input: r = re.compile( "^\s*#\s*(?P<cmd>[a-z]+)(?:\s+(?P<name>\S+)(?:\s+(?P<value>\S+))?)?", re.U) for l in input: m = r.match(l) if m: cmd = m.group("cmd") name = m.group("name") value = m.group("value") if name: if name == "ALLDEFINES": if cmd == "define": raise Exception( "`#define ALLDEFINES` is not allowed in a " "CONFIGURE_DEFINE_FILE") def define_for_name(name, val): """WebRTC files like to define WINVER and _WIN32_WINNT via the command line, which raises a mass of macro redefinition warnings. Just handle those macros specially here.""" define = "#define {name} {val}".format(name=name, val=val) if name in ("WINVER", "_WIN32_WINNT"): return "#if !defined({name})\n{define}\n#endif".format( name=name, define=define) return define defines = "\n".join( sorted( define_for_name(name, val) for name, val in config.defines["ALLDEFINES"].items())) l = l[:m.start("cmd") - 1] + defines + l[m.end("name"):] elif cmd == "define": if value and name in config.defines: l = (l[:m.start("value")] + str(config.defines[name]) + l[m.end("value"):]) elif cmd == "undef": if name in config.defines: l = (l[:m.start("cmd")] + "define" + l[m.end("cmd"):m.end("name")] + " " + str(config.defines[name]) + l[m.end("name"):]) else: l = "/* " + l[:m.end("name" )] + " */" + l[m.end("name"):] output.write(l) deps = {path} deps.update(config.get_dependencies()) return deps
def config_status(config): # Sanitize config data to feed config.status # Ideally, all the backend and frontend code would handle the booleans, but # there are so many things involved, that it's easier to keep config.status # untouched for now. def sanitized_bools(v): if v is True: return '1' if v is False: return '' return v sanitized_config = {} sanitized_config['substs'] = { k: sanitized_bools(v) for k, v in config.iteritems() if k not in ('DEFINES', 'non_global_defines', 'TOPSRCDIR', 'TOPOBJDIR', 'CONFIG_STATUS_DEPS') } sanitized_config['defines'] = { k: sanitized_bools(v) for k, v in config['DEFINES'].iteritems() } sanitized_config['non_global_defines'] = config['non_global_defines'] sanitized_config['topsrcdir'] = config['TOPSRCDIR'] sanitized_config['topobjdir'] = config['TOPOBJDIR'] sanitized_config['mozconfig'] = config.get('MOZCONFIG') # Create config.status. Eventually, we'll want to just do the work it does # here, when we're able to skip configure tests/use cached results/not rely # on autoconf. logging.getLogger('moz.configure').info('Creating config.status') encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8' with codecs.open('config.status', 'w', encoding) as fh: fh.write(textwrap.dedent('''\ #!%(python)s # coding=%(encoding)s from __future__ import unicode_literals from mozbuild.util import encode encoding = '%(encoding)s' ''') % {'python': config['PYTHON'], 'encoding': encoding}) # A lot of the build backend code is currently expecting byte # strings and breaks in subtle ways with unicode strings. (bug 1296508) for k, v in sanitized_config.iteritems(): fh.write('%s = encode(%s, encoding)\n' % (k, indented_repr(v))) fh.write("__all__ = ['topobjdir', 'topsrcdir', 'defines', " "'non_global_defines', 'substs', 'mozconfig']") if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'): fh.write(textwrap.dedent(''' if __name__ == '__main__': from mozbuild.util import patch_main patch_main() from mozbuild.config_status import config_status args = dict([(name, globals()[name]) for name in __all__]) config_status(**args) ''')) partial_config = PartialConfigEnvironment(config['TOPOBJDIR']) partial_config.write_vars(sanitized_config) # Write out a file so the build backend knows to re-run configure when # relevant Python changes. with open('config_status_deps.in', 'w') as fh: for f in itertools.chain(config['CONFIG_STATUS_DEPS'], iter_modules_in_path(config['TOPOBJDIR'], config['TOPSRCDIR'])): fh.write('%s\n' % mozpath.normpath(f)) # Other things than us are going to run this file, so we need to give it # executable permissions. os.chmod('config.status', 0o755) if config.get('MOZ_BUILD_APP') != 'js' or config.get('JS_STANDALONE'): from mozbuild.config_status import config_status # Some values in sanitized_config also have more complex types, such as # EnumString, which using when calling config_status would currently # break the build, as well as making it inconsistent with re-running # config.status. Fortunately, EnumString derives from unicode, so it's # covered by converting unicode strings. # A lot of the build backend code is currently expecting byte strings # and breaks in subtle ways with unicode strings. return config_status(args=[], **encode(sanitized_config, encoding)) return 0
def process_define_file(output, input): '''Creates the given config header. A config header is generated by taking the corresponding source file and replacing some #define/#undef occurences: "#undef NAME" is turned into "#define NAME VALUE" "#define NAME" is unchanged "#define NAME ORIGINAL_VALUE" is turned into "#define NAME VALUE" "#undef UNKNOWN_NAME" is turned into "/* #undef UNKNOWN_NAME */" Whitespaces are preserved. As a special rule, "#undef ALLDEFINES" is turned into "#define NAME VALUE" for all the defined variables. ''' path = os.path.abspath(input) config = PartialConfigEnvironment(topobjdir) if mozpath.basedir(path, [mozpath.join(topsrcdir, 'js/src')]) and \ not config.substs.get('JS_STANDALONE'): config = PartialConfigEnvironment(mozpath.join(topobjdir, 'js', 'src')) with open(path, 'rU') as input: r = re.compile('^\s*#\s*(?P<cmd>[a-z]+)(?:\s+(?P<name>\S+)(?:\s+(?P<value>\S+))?)?', re.U) for l in input: m = r.match(l) if m: cmd = m.group('cmd') name = m.group('name') value = m.group('value') if name: if name == 'ALLDEFINES': if cmd == 'define': raise Exception( '`#define ALLDEFINES` is not allowed in a ' 'CONFIGURE_DEFINE_FILE') defines = '\n'.join(sorted( '#define %s %s' % (name, val) for name, val in config.defines['ALLDEFINES'].iteritems())) l = l[:m.start('cmd') - 1] \ + defines + l[m.end('name'):] elif cmd == 'define': if value and name in config.defines: l = l[:m.start('value')] \ + str(config.defines[name]) \ + l[m.end('value'):] elif cmd == 'undef': if name in config.defines: l = l[:m.start('cmd')] \ + 'define' \ + l[m.end('cmd'):m.end('name')] \ + ' ' \ + str(config.defines[name]) \ + l[m.end('name'):] else: l = '/* ' + l[:m.end('name')] + ' */' + l[m.end('name'):] output.write(l) deps = {path} deps.update(config.get_dependencies()) return deps