Bug 1319563 - Move command line generation to the CommonBackend; r?glandium draft
authorMike Shal <mshal@mozilla.com>
Tue, 08 Nov 2016 16:56:36 -0500
changeset 443148 a53afa33df5ade4be5fdde50f51c333c60b26f73
parent 443147 62e8594733aaa9c708056268780b46ed64a49354
child 443149 aae518d317f6fc80126bd8fe1eb7ce878cd5456a
push id36915
push userbmo:mshal@mozilla.com
push dateWed, 23 Nov 2016 23:38:19 +0000
reviewersglandium
bugs1319563
milestone53.0a1
Bug 1319563 - Move command line generation to the CommonBackend; r?glandium
python/mozbuild/mozbuild/backend/common.py
python/mozbuild/mozbuild/compilation/database.py
python/mozbuild/mozbuild/test/backend/common.py
--- a/python/mozbuild/mozbuild/backend/common.py
+++ b/python/mozbuild/mozbuild/backend/common.py
@@ -19,41 +19,55 @@ from mozbuild.frontend.context import (
     Path,
     RenamedSourcePath,
     VARIABLES,
 )
 from mozbuild.frontend.data import (
     BaseProgram,
     ChromeManifestEntry,
     ConfigFileSubstitution,
+    Defines,
+    DirectoryTraversal,
     ExampleWebIDLInterface,
-    IPDLFile,
+    FinalTargetFiles,
     FinalTargetPreprocessedFiles,
-    FinalTargetFiles,
     GeneratedEventWebIDLFile,
+    GeneratedSources,
     GeneratedWebIDLFile,
+    IPDLFile,
+    Linkable,
+    LocalInclude,
     PreprocessedTestWebIDLFile,
     PreprocessedWebIDLFile,
     SharedLibrary,
+    SimpleProgram,
+    Sources,
     TestManifest,
     TestWebIDLFile,
     UnifiedSources,
+    VariablePassthru,
+    WebIDLFile,
     XPIDLFile,
-    WebIDLFile,
 )
 from mozbuild.jar import (
     DeprecatedJarManifest,
     JarManifestParser,
 )
 from mozbuild.preprocessor import Preprocessor
 from mozpack.chrome.manifest import parse_manifest_line
 
-from collections import defaultdict
+from collections import (
+    defaultdict,
+    OrderedDict,
+)
 
-from mozbuild.util import group_unified_files
+from mozbuild.util import (
+    expand_variables,
+    group_unified_files,
+)
 
 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
 
@@ -223,16 +237,26 @@ class CommonBackend(BuildBackend):
     def _init(self):
         self._idl_manager = XPIDLManager(self.environment)
         self._test_manager = TestManager(self.environment)
         self._webidls = WebIDLCollection()
         self._binaries = BinariesCollection()
         self._configs = set()
         self._ipdl_sources = set()
 
+        self._db = OrderedDict()
+        self._envs = {}
+        self._includes = defaultdict(list)
+        self._defines = defaultdict(list)
+        self._local_flags = defaultdict(dict)
+        self._extra_includes = defaultdict(list)
+        self._gyp_dirs = set()
+        self._dist_include_testing = '-I%s' % mozpath.join(
+            self.environment.topobjdir, 'dist', 'include', 'testing')
+
     def consume_object(self, obj):
         self._configs.add(obj.config)
 
         if isinstance(obj, TestManifest):
             for test in obj.tests:
                 self._test_manager.add(test, obj.flavor, obj.topsrcdir)
             self._test_manager.add_defaults(obj.manifest)
             self._test_manager.add_installs(obj, obj.topsrcdir)
@@ -318,22 +342,69 @@ class CommonBackend(BuildBackend):
             if self.environment.is_artifact_build:
                 return True
 
             if obj.have_unified_mapping:
                 self._write_unified_files(obj.unified_source_mapping, obj.objdir)
             if hasattr(self, '_process_unified_sources'):
                 self._process_unified_sources(obj)
 
-        elif isinstance(obj, BaseProgram):
-            self._binaries.programs.append(obj)
+        elif isinstance(obj, Linkable):
+            if isinstance(obj.defines, Defines): # As opposed to HostDefines
+                for d in obj.defines.get_defines():
+                    if d not in self._defines[obj.objdir]:
+                        self._defines[obj.objdir].append(d)
+            self._defines[obj.objdir].extend(obj.lib_defines.get_defines())
+            if isinstance(obj, SimpleProgram):
+                if obj.is_unit_test:
+                    if (self._dist_include_testing not in
+                            self._extra_includes[obj.objdir]):
+                        self._extra_includes[obj.objdir].append(
+                            self._dist_include_testing)
+
+            if isinstance(obj, BaseProgram):
+                self._binaries.programs.append(obj)
+            elif isinstance(obj, SharedLibrary):
+                self._binaries.shared_libraries.append(obj)
+            return False
+
+        elif isinstance(obj, DirectoryTraversal):
+            self._envs[obj.objdir] = obj.config
+            for var in ('STL_FLAGS', 'VISIBILITY_FLAGS', 'WARNINGS_AS_ERRORS'):
+                value = obj.config.substs.get(var)
+                if value:
+                    self._local_flags[obj.objdir][var] = value
             return False
 
-        elif isinstance(obj, SharedLibrary):
-            self._binaries.shared_libraries.append(obj)
+        elif isinstance(obj, (Sources, GeneratedSources)):
+            # For other sources, include each source file.
+            for f in obj.files:
+                self._build_compiler_line(obj.objdir, obj.relativedir, obj.config, f,
+                                          obj.canonical_suffix)
+            return False
+
+        elif isinstance(obj, LocalInclude):
+            self._includes[obj.objdir].append('-I%s' % mozpath.normpath(
+                obj.path.full_path))
+            return False
+
+        elif isinstance(obj, VariablePassthru):
+            if obj.variables.get('IS_GYP_DIR'):
+                self._gyp_dirs.add(obj.objdir)
+            for var in ('MOZBUILD_CFLAGS', 'MOZBUILD_CXXFLAGS',
+                        'MOZBUILD_CMFLAGS', 'MOZBUILD_CMMFLAGS',
+                        'RTL_FLAGS', 'VISIBILITY_FLAGS'):
+                if var in obj.variables:
+                    self._local_flags[obj.objdir][var] = obj.variables[var]
+            if (obj.variables.get('DISABLE_STL_WRAPPING') and
+                    'STL_FLAGS' in self._local_flags[obj.objdir]):
+                del self._local_flags[obj.objdir]['STL_FLAGS']
+            if (obj.variables.get('ALLOW_COMPILER_WARNINGS') and
+                    'WARNINGS_AS_ERRORS' in self._local_flags[obj.objdir]):
+                del self._local_flags[obj.objdir]['WARNINGS_AS_ERRORS']
             return False
 
         else:
             return False
 
         return True
 
     def consume_finished(self):
@@ -388,16 +459,57 @@ class CommonBackend(BuildBackend):
         # Write out a machine-readable file describing binaries.
         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)
 
+        # Finalize compiler command-lines by replacing deferred variables with
+        # their final values.
+        for (directory, filename), cmd in self._db.iteritems():
+            env = self._envs[directory]
+            cmd = list(cmd)
+            cmd.append(filename)
+            local_extra = list(self._extra_includes[directory])
+            if directory not in self._gyp_dirs:
+                for var in (
+                    'NSPR_CFLAGS',
+                    'NSS_CFLAGS',
+                    'MOZ_JPEG_CFLAGS',
+                    'MOZ_PNG_CFLAGS',
+                    'MOZ_ZLIB_CFLAGS',
+                    'MOZ_PIXMAN_CFLAGS',
+                ):
+                    f = env.substs.get(var)
+                    if f:
+                        local_extra.extend(f)
+            variables = {
+                'LOCAL_INCLUDES': self._includes[directory],
+                'DEFINES': self._defines[directory],
+                'EXTRA_INCLUDES': local_extra,
+                'DIST': mozpath.join(env.topobjdir, 'dist'),
+                'DEPTH': env.topobjdir,
+                'MOZILLA_DIR': env.topsrcdir,
+                'topsrcdir': env.topsrcdir,
+                'topobjdir': env.topobjdir,
+            }
+            variables.update(self._local_flags[directory])
+            c = []
+            for a in cmd:
+                a = expand_variables(a, variables).split()
+                if not a:
+                    continue
+                if isinstance(a, types.StringTypes):
+                    c.append(a)
+                else:
+                    c.extend(a)
+            self._db[(directory, filename)] = c
+
     def _handle_webidl_collection(self, webidls):
         if not webidls.all_stems():
             return
 
         bindings_dir = mozpath.join(self.environment.topobjdir, 'dom', 'bindings')
 
         all_inputs = set(webidls.all_static_sources())
         for s in webidls.all_non_static_basenames():
@@ -620,9 +732,9 @@ class CommonBackend(BuildBackend):
         cmdline.append('$(WARNINGS_AS_ERRORS)')
         cmdline.append('$(MOZBUILD_%s)' % self.CFLAGS[canonical_suffix])
         if canonical_suffix == '.m':
             append_var('OS_COMPILE_CMFLAGS')
             cmdline.append('$(MOZBUILD_CMFLAGS)')
         elif canonical_suffix == '.mm':
             append_var('OS_COMPILE_CMMFLAGS')
             cmdline.append('$(MOZBUILD_CMMFLAGS)')
-        return cmdline
+        self._db[(objdir, filename)] = cmdline
--- a/python/mozbuild/mozbuild/compilation/database.py
+++ b/python/mozbuild/mozbuild/compilation/database.py
@@ -1,196 +1,76 @@
 # 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/.
 
 # This modules provides functionality for dealing with code completion.
 
 import os
-import types
 
 from mozbuild.compilation import util
 from mozbuild.backend.common import CommonBackend
-from mozbuild.frontend.data import (
-    Sources,
-    GeneratedSources,
-    DirectoryTraversal,
-    Defines,
-    Linkable,
-    LocalInclude,
-    VariablePassthru,
-    SimpleProgram,
-)
 from mozbuild.shellutil import (
     quote as shell_quote,
 )
-from mozbuild.util import expand_variables
-import mozpack.path as mozpath
-from collections import (
-    defaultdict,
-    OrderedDict,
-)
 
 
 class CompileDBBackend(CommonBackend):
     def _init(self):
         CommonBackend._init(self)
         if not util.check_top_objdir(self.environment.topobjdir):
             raise Exception()
 
-        # The database we're going to dump out to.
-        self._db = OrderedDict()
-
-        # The cache for per-directory flags
-        self._flags = {}
-
-        self._envs = {}
-        self._includes = defaultdict(list)
-        self._defines = defaultdict(list)
-        self._local_flags = defaultdict(dict)
-        self._extra_includes = defaultdict(list)
-        self._gyp_dirs = set()
-        self._dist_include_testing = '-I%s' % mozpath.join(
-            self.environment.topobjdir, 'dist', 'include', 'testing')
-
     def consume_object(self, obj):
         # Those are difficult directories, that will be handled later.
         if obj.relativedir in (
                 'build/unix/elfhack',
                 'build/unix/elfhack/inject',
                 'build/clang-plugin',
                 'build/clang-plugin/tests',
                 'security/sandbox/win/wow_helper',
                 'toolkit/crashreporter/google-breakpad/src/common'):
             return True
 
-        consumed = CommonBackend.consume_object(self, obj)
-
-        if consumed:
-            return True
-
-        if isinstance(obj, DirectoryTraversal):
-            self._envs[obj.objdir] = obj.config
-            for var in ('STL_FLAGS', 'VISIBILITY_FLAGS', 'WARNINGS_AS_ERRORS'):
-                value = obj.config.substs.get(var)
-                if value:
-                    self._local_flags[obj.objdir][var] = value
-
-        elif isinstance(obj, (Sources, GeneratedSources)):
-            # For other sources, include each source file.
-            for f in obj.files:
-                self._build_db_line(obj.objdir, obj.relativedir, obj.config, f,
-                                    obj.canonical_suffix)
-
-        elif isinstance(obj, LocalInclude):
-            self._includes[obj.objdir].append('-I%s' % mozpath.normpath(
-                obj.path.full_path))
-
-        elif isinstance(obj, Linkable):
-            if isinstance(obj.defines, Defines): # As opposed to HostDefines
-                for d in obj.defines.get_defines():
-                    if d not in self._defines[obj.objdir]:
-                        self._defines[obj.objdir].append(d)
-            self._defines[obj.objdir].extend(obj.lib_defines.get_defines())
-            if isinstance(obj, SimpleProgram) and obj.is_unit_test:
-                if (self._dist_include_testing not in
-                        self._extra_includes[obj.objdir]):
-                    self._extra_includes[obj.objdir].append(
-                        self._dist_include_testing)
-
-        elif isinstance(obj, VariablePassthru):
-            if obj.variables.get('IS_GYP_DIR'):
-                self._gyp_dirs.add(obj.objdir)
-            for var in ('MOZBUILD_CFLAGS', 'MOZBUILD_CXXFLAGS',
-                        'MOZBUILD_CMFLAGS', 'MOZBUILD_CMMFLAGS',
-                        'RTL_FLAGS', 'VISIBILITY_FLAGS'):
-                if var in obj.variables:
-                    self._local_flags[obj.objdir][var] = obj.variables[var]
-            if (obj.variables.get('DISABLE_STL_WRAPPING') and
-                    'STL_FLAGS' in self._local_flags[obj.objdir]):
-                del self._local_flags[obj.objdir]['STL_FLAGS']
-            if (obj.variables.get('ALLOW_COMPILER_WARNINGS') and
-                    'WARNINGS_AS_ERRORS' in self._local_flags[obj.objdir]):
-                del self._local_flags[obj.objdir]['WARNINGS_AS_ERRORS']
+        CommonBackend.consume_object(self, obj)
 
         return True
 
     def consume_finished(self):
         CommonBackend.consume_finished(self)
 
         db = []
 
         for (directory, filename), cmd in self._db.iteritems():
-            env = self._envs[directory]
-            cmd = list(cmd)
-            cmd.append(filename)
-            local_extra = list(self._extra_includes[directory])
-            if directory not in self._gyp_dirs:
-                for var in (
-                    'NSPR_CFLAGS',
-                    'NSS_CFLAGS',
-                    'MOZ_JPEG_CFLAGS',
-                    'MOZ_PNG_CFLAGS',
-                    'MOZ_ZLIB_CFLAGS',
-                    'MOZ_PIXMAN_CFLAGS',
-                ):
-                    f = env.substs.get(var)
-                    if f:
-                        local_extra.extend(f)
-            variables = {
-                'LOCAL_INCLUDES': self._includes[directory],
-                'DEFINES': self._defines[directory],
-                'EXTRA_INCLUDES': local_extra,
-                'DIST': mozpath.join(env.topobjdir, 'dist'),
-                'DEPTH': env.topobjdir,
-                'MOZILLA_DIR': env.topsrcdir,
-                'topsrcdir': env.topsrcdir,
-                'topobjdir': env.topobjdir,
-            }
-            variables.update(self._local_flags[directory])
-            c = []
-            for a in cmd:
-                a = expand_variables(a, variables).split()
-                if not a:
-                    continue
-                if isinstance(a, types.StringTypes):
-                    c.append(a)
-                else:
-                    c.extend(a)
             db.append({
                 'directory': directory,
-                'command': ' '.join(shell_quote(a) for a in c),
+                'command': ' '.join(shell_quote(a) for a in cmd),
                 'file': filename,
             })
 
         import json
         # Output the database (a JSON file) to objdir/compile_commands.json
         outputfile = os.path.join(self.environment.topobjdir, 'compile_commands.json')
         with self._write_file(outputfile) as jsonout:
             json.dump(db, jsonout, indent=0)
 
     def _process_unified_sources(self, obj):
         # For unified sources, only include the unified source file.
         # Note that unified sources are never used for host sources.
         for f in obj.unified_source_mapping:
-            self._build_db_line(obj.objdir, obj.relativedir, obj.config, f[0],
-                                obj.canonical_suffix)
+            self._build_compiler_line(obj.objdir, obj.relativedir, obj.config, f[0],
+                                      obj.canonical_suffix)
 
     def _handle_idl_manager(self, idl_manager):
         pass
 
     def _handle_ipdl_sources(self, ipdl_dir, sorted_ipdl_sources,
                              unified_ipdl_cppsrcs_mapping):
         for f in unified_ipdl_cppsrcs_mapping:
-            self._build_db_line(ipdl_dir, None, self.environment, f[0],
-                                '.cpp')
+            self._build_compiler_line(ipdl_dir, None, self.environment, f[0],
+                                      '.cpp')
 
     def _handle_webidl_build(self, bindings_dir, unified_source_mapping,
                              webidls, expected_build_output_files,
                              global_define_files):
         for f in unified_source_mapping:
-            self._build_db_line(bindings_dir, None, self.environment, f[0],
-                                '.cpp')
-
-    def _build_db_line(self, objdir, reldir, cenv, filename, canonical_suffix):
-        cmdline = self._build_compiler_line(objdir, reldir, cenv, filename, canonical_suffix)
-        if cmdline:
-            self._db[(objdir, filename)] = cmdline
+            self._build_compiler_line(bindings_dir, None, self.environment, f[0],
+                                      '.cpp')
--- a/python/mozbuild/mozbuild/test/backend/common.py
+++ b/python/mozbuild/mozbuild/test/backend/common.py
@@ -53,16 +53,18 @@ CONFIGS = defaultdict(lambda: {
         },
     },
     'sources': {
         'defines': {},
         'non_global_defines': [],
         'substs': {
             'LIB_PREFIX': 'lib',
             'LIB_SUFFIX': 'a',
+            'CC': 'gcc',
+            'CXX': 'g++',
         },
     },
     'stub0': {
         'defines': {
             'MOZ_TRUE_1': '1',
             'MOZ_TRUE_2': '1',
         },
         'non_global_defines': [