Bug 902825 - Allow generating dist/include/mozconfig/*.h; r?glandium
Using the defines from the config environment, we can generate a set of
header files in dist/include/mozconfig/ so that source files can
include them to get access to specific configure values. Instead of
rebuilding the whole world when a single define changes (due to its
presence in mozilla-config.h), we can recompile only the set of files
that actually depend on its value.
This begins the process by generating the individual headers from all of
the non-global defines in moz.configure.
A non-global config item must have the following properties:
1) For defines: They are used in C/C++ code by doing #include
"mozconfig/DEFINE_NAME.h" or have a suitable CONFIGURE_SUBST_FILE with
those defines (eg: xpcom/xpcom-config.h)
2) For substs: They are not used in Makefile.in's or included .mk files,
because the values are not exported to autoconf.mk
If a variable is used in both set_define() and set_config(), then both
properties must apply for it to be marked non-global.
MozReview-Commit-ID: 22jiYaamSo2
--- a/python/mozbuild/mozbuild/backend/common.py
+++ b/python/mozbuild/mozbuild/backend/common.py
@@ -43,16 +43,24 @@ from mozbuild.jar import (
DeprecatedJarManifest,
JarManifestParser,
)
from mozbuild.preprocessor import Preprocessor
from mozpack.chrome.manifest import parse_manifest_line
from mozbuild.util import group_unified_files
+MOZCONFIG_HEADER = '''/* THIS IS AN AUTOGENERATED FILE. DO NOT EDIT */
+
+#ifndef {header_guard}
+#define {header_guard}
+{define_var}
+#endif
+'''
+
class XPIDLManager(object):
"""Helps manage XPCOM IDLs in the context of the build system."""
def __init__(self, config):
self.config = config
self.topsrcdir = config.topsrcdir
self.topobjdir = config.topobjdir
self.idls = {}
@@ -327,16 +335,20 @@ class CommonBackend(BuildBackend):
files_per_unified_file=16))
self._write_unified_files(unified_source_mapping, ipdl_dir, poison_windows_h=False)
self._handle_ipdl_sources(ipdl_dir, sorted_ipdl_sources, unified_source_mapping)
for config in self._configs:
self.backend_input_files.add(config.source)
+ # Generate the mozconfig/*.h headers for individual dependencies on
+ # configure defines.
+ self._generate_mozconfig_headers()
+
# Write out a machine-readable file describing binaries.
topobjdir = self.environment.topobjdir
with self._write_file(mozpath.join(topobjdir, 'binaries.json')) as fh:
d = {
'shared_libraries': [s.to_dict() for s in self._binaries.shared_libraries],
'programs': [p.to_dict() for p in self._binaries.programs],
}
json.dump(d, fh, sort_keys=True, indent=4)
@@ -519,8 +531,32 @@ class CommonBackend(BuildBackend):
FinalTargetPreprocessedFiles(jar_context, files_pp))
for m in jarinfo.chrome_manifests:
entry = parse_manifest_line(
mozpath.dirname(jarinfo.name),
m.replace('%', mozpath.basename(jarinfo.name) + '/'))
self.consume_object(ChromeManifestEntry(
jar_context, '%s.manifest' % jarinfo.name, entry))
+
+ def _write_mozconfig_header(self, include_dir, define, value=None):
+ path = mozpath.join(include_dir, define + '.h')
+ if value is not None:
+ define_var = '#define {define} {value}'.format(
+ define = define,
+ value = value,
+ )
+ else:
+ define_var = ''
+
+ with self._write_file(path) as fh:
+ fh.write(MOZCONFIG_HEADER.format(
+ header_guard = 'MOZCONFIG_%s_H' % define,
+ define_var = define_var,
+ ))
+
+ def _generate_mozconfig_headers(self):
+ include_dir = mozpath.join(self.environment.topobjdir, 'dist/include/mozconfig')
+
+ for var in self.environment.non_global_config:
+ if var in self.environment.alldefines:
+ v = self.environment.defines.get(var)
+ self._write_mozconfig_header(include_dir, var, v)
--- a/python/mozbuild/mozbuild/test/backend/common.py
+++ b/python/mozbuild/mozbuild/test/backend/common.py
@@ -164,17 +164,43 @@ CONFIGS = defaultdict(lambda: {
'prog-lib-c-only': {
'defines': {},
'non_global_config': [],
'substs': {
'COMPILE_ENVIRONMENT': '1',
'LIB_SUFFIX': '.a',
'BIN_SUFFIX': '',
},
- }
+ },
+ 'mozconfig-header': {
+ 'defines': {
+ 'MOZ_FOO': '1',
+ 'MOZ_BAR': '2',
+ 'MOZ_BAZ': '3',
+ 'MOZ_CONST': '4',
+ 'MOZ_NON_GLOBAL_1': 'ng1',
+ },
+ 'alldefines': {
+ 'MOZ_FOO',
+ 'MOZ_BAR',
+ 'MOZ_BAZ',
+ 'MOZ_CONST',
+ 'MOZ_NON_GLOBAL_1',
+ },
+ 'non_global_config': [
+ 'MOZ_FOO',
+ 'MOZ_BAR',
+ 'MOZ_CONST',
+ 'MOZ_NONGLOBAL_1',
+ ],
+ 'substs': {
+ 'MOZ_FOO': '1',
+ 'MOZ_SUBST': 'subst',
+ },
+ },
})
class BackendTester(unittest.TestCase):
def setUp(self):
self._old_env = dict(os.environ)
os.environ.pop('MOZ_OBJDIR', None)
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/data/mozconfig-header/moz.build
@@ -0,0 +1,1 @@
+# Stub file for testing mozconfig defines
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/test_mozconfig_header.py
@@ -0,0 +1,63 @@
+# 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/.
+
+from __future__ import unicode_literals
+
+import os
+import stat
+
+import mozpack.path as mozpath
+from mozunit import main
+
+from mozbuild.backend.recursivemake import RecursiveMakeBackend
+from mozbuild.test.backend.common import BackendTester
+
+
+class TestMozconfigHeader(BackendTester):
+ def _check_header(self, env, name, exists, value=None):
+ header = mozpath.join(env.topobjdir, 'dist', 'include', 'mozconfig','%s.h' % name)
+ self.assertEqual(os.path.exists(header), exists)
+ if value:
+ with open(header) as fh:
+ lines = fh.readlines()
+ self.assertIn('#define %s %s\n' % (name, value), lines)
+
+ return os.stat(header)[stat.ST_MTIME]
+
+ def test_headers_written(self):
+ """Ensure all mozconfig_headers are written out."""
+ env = self._consume('mozconfig-header', RecursiveMakeBackend)
+
+ # MOZ_FOO, MOZ_BAR, and MOZ_CONST are the mozconfig headers from the
+ # config. MOZ_BAZ is a define but not a mozconfig_header
+ self._check_header(env, 'MOZ_FOO', True, 1)
+ self._check_header(env, 'MOZ_BAR', True, 2)
+ old_ts = self._check_header(env, 'MOZ_CONST', True, 4)
+ self._check_header(env, 'MOZ_BAZ', False)
+ self._check_header(env, 'MOZ_NEW', False)
+
+ # Now MOZ_BAR is removed from non_global_config, MOZ_FOO is changed,
+ # MOZ_CONST remains the same, and MOZ_NEW is added.
+ env.defines = {d: env.defines[d] for d in env.defines.keys() if d != 'MOZ_BAR'}
+ env.defines['MOZ_FOO'] = 'newfoo'
+ env.defines['MOZ_NEW'] = 'new'
+ env.non_global_config = [
+ 'MOZ_FOO',
+ 'MOZ_CONST',
+ 'MOZ_NEW',
+ 'MOZ_NON_GLOBAL_1',
+ ]
+ env.alldefines.add('MOZ_NEW')
+ self._consume('mozconfig-header', RecursiveMakeBackend, env=env)
+ self._check_header(env, 'MOZ_FOO', True, 'newfoo')
+ self._check_header(env, 'MOZ_BAR', False)
+ new_ts = self._check_header(env, 'MOZ_CONST', True, 4)
+ self._check_header(env, 'MOZ_BAZ', False)
+ self._check_header(env, 'MOZ_NEW', True, 'new')
+
+ # Assert that the file representing an unchanged define is not touched.
+ self.assertEqual(old_ts, new_ts)
+
+if __name__ == '__main__':
+ main()
--- a/python/mozbuild/mozbuild/test/python.ini
+++ b/python/mozbuild/mozbuild/test/python.ini
@@ -1,13 +1,14 @@
[action/test_buildlist.py]
[action/test_generate_browsersearch.py]
[action/test_package_fennec_apk.py]
[backend/test_build.py]
[backend/test_configenvironment.py]
+[backend/test_mozconfig_header.py]
[backend/test_partialconfigenvironment.py]
[backend/test_recursivemake.py]
[backend/test_test_manifest.py]
[backend/test_visualstudio.py]
[codecoverage/test_lcov_rewrite.py]
[compilation/test_warnings.py]
[configure/lint.py]
[configure/test_checks_configure.py]