Example #1
0
    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',
     })
Example #5
0
 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))
Example #8
0
    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)
Example #14
0
    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')
Example #16
0
    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')
Example #18
0
# 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)
Example #19
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 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
Example #21
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 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
Example #22
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
Example #23
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
Example #24
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
Example #25
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',
                     '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