зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1224450 - Make the CompileDB derive its commands from the moz.build data. r=gps
The moz.build data is now sufficient to, with some convolution, generate the same compilation database that recursing the tree with the showbuild target does. The resulting code is not the prettiest, and exposes the shortcomings of the current moz.build data model. It is however a first step towards fixing those shortcomings, because they are now more clearly identified. This was validated on all platforms on try by checking the output of mach build-backend -b CompileDB -d -n is empty when backing out the patch after running mach build-backend -b CompileDB once.
This commit is contained in:
Родитель
93c78646c2
Коммит
9e3dec34bf
|
@ -5,6 +5,7 @@
|
|||
# This modules provides functionality for dealing with code completion.
|
||||
|
||||
import os
|
||||
import types
|
||||
|
||||
from mozbuild.base import MozbuildObject
|
||||
from mozbuild.compilation import util
|
||||
|
@ -14,13 +15,25 @@ from mozbuild.frontend.data import (
|
|||
HostSources,
|
||||
UnifiedSources,
|
||||
GeneratedSources,
|
||||
DirectoryTraversal,
|
||||
Defines,
|
||||
Linkable,
|
||||
LocalInclude,
|
||||
VariablePassthru,
|
||||
SimpleProgram,
|
||||
)
|
||||
from mozbuild.shellutil import (
|
||||
split as shell_split,
|
||||
quote as shell_quote,
|
||||
)
|
||||
from mozbuild.util import expand_variables
|
||||
from mach.config import ConfigSettings
|
||||
from mach.logging import LoggingManager
|
||||
import mozpack.path as mozpath
|
||||
from collections import (
|
||||
defaultdict,
|
||||
OrderedDict,
|
||||
)
|
||||
|
||||
|
||||
class CompileDBBackend(CommonBackend):
|
||||
|
@ -30,7 +43,7 @@ class CompileDBBackend(CommonBackend):
|
|||
raise Exception()
|
||||
|
||||
# The database we're going to dump out to.
|
||||
self._db = []
|
||||
self._db = OrderedDict()
|
||||
|
||||
# The cache for per-directory flags
|
||||
self._flags = {}
|
||||
|
@ -39,6 +52,14 @@ class CompileDBBackend(CommonBackend):
|
|||
self._cmd = MozbuildObject(self.environment.topsrcdir, ConfigSettings(),
|
||||
log_manager, self.environment.topobjdir)
|
||||
|
||||
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.
|
||||
|
@ -56,98 +77,186 @@ class CompileDBBackend(CommonBackend):
|
|||
if consumed:
|
||||
return True
|
||||
|
||||
# We ignore host compilations for now, the code doesn't handle them
|
||||
# properly.
|
||||
if isinstance(obj, (Sources, GeneratedSources)):
|
||||
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:
|
||||
flags = self._get_dir_flags(obj.objdir)
|
||||
self._build_db_line(obj.objdir, self.environment, f,
|
||||
obj.canonical_suffix, flags)
|
||||
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']
|
||||
|
||||
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),
|
||||
'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(self._db, jsonout, indent=0)
|
||||
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:
|
||||
flags = self._get_dir_flags(obj.objdir)
|
||||
self._build_db_line(obj.objdir, self.environment, f[0],
|
||||
obj.canonical_suffix, flags)
|
||||
self._build_db_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):
|
||||
flags = self._get_dir_flags(ipdl_dir)
|
||||
for f in unified_ipdl_cppsrcs_mapping:
|
||||
self._build_db_line(ipdl_dir, self.environment, f[0],
|
||||
'.cpp', flags)
|
||||
self._build_db_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):
|
||||
flags = self._get_dir_flags(bindings_dir)
|
||||
for f in unified_source_mapping:
|
||||
self._build_db_line(bindings_dir, self.environment, f[0],
|
||||
'.cpp', flags)
|
||||
self._build_db_line(bindings_dir, None, self.environment, f[0],
|
||||
'.cpp')
|
||||
|
||||
def _get_dir_flags(self, directory):
|
||||
if directory in self._flags:
|
||||
return self._flags[directory]
|
||||
COMPILERS = {
|
||||
'.c': 'CC',
|
||||
'.cpp': 'CXX',
|
||||
'.m': 'CC',
|
||||
'.mm': 'CXX',
|
||||
}
|
||||
|
||||
from mozbuild.util import resolve_target_to_make
|
||||
CFLAGS = {
|
||||
'.c': 'CFLAGS',
|
||||
'.cpp': 'CXXFLAGS',
|
||||
'.m': 'CFLAGS',
|
||||
'.mm': 'CXXFLAGS',
|
||||
}
|
||||
|
||||
make_dir, make_target = resolve_target_to_make(self.environment.topobjdir, directory)
|
||||
if make_dir is None and make_target is None:
|
||||
raise Exception('Cannot figure out the make dir and target for ' + directory)
|
||||
|
||||
build_vars = util.get_build_vars(directory, self._cmd)
|
||||
|
||||
# We only care about the following build variables.
|
||||
for name in ('COMPILE_CFLAGS', 'COMPILE_CXXFLAGS',
|
||||
'COMPILE_CMFLAGS', 'COMPILE_CMMFLAGS'):
|
||||
if name not in build_vars:
|
||||
continue
|
||||
|
||||
build_vars[name] = util.sanitize_cflags(shell_split(build_vars[name]))
|
||||
|
||||
self._flags[directory] = build_vars
|
||||
return self._flags[directory]
|
||||
|
||||
def _build_db_line(self, objdir, cenv, filename, canonical_suffix, flags):
|
||||
if canonical_suffix in ('.c', '.m'):
|
||||
compiler = cenv.substs['CC']
|
||||
cflags = list(flags['COMPILE_CFLAGS'])
|
||||
# Add the Objective-C flags if needed.
|
||||
if canonical_suffix == '.m':
|
||||
cflags.extend(flags['COMPILE_CMFLAGS'])
|
||||
elif canonical_suffix in ('.cpp', '.mm'):
|
||||
compiler = cenv.substs['CXX']
|
||||
cflags = list(flags['COMPILE_CXXFLAGS'])
|
||||
# Add the Objective-C++ flags if needed.
|
||||
if canonical_suffix == '.mm':
|
||||
cflags.extend(flags['COMPILE_CMMFLAGS'])
|
||||
else:
|
||||
def _build_db_line(self, objdir, reldir, cenv, filename, canonical_suffix):
|
||||
if canonical_suffix not in self.COMPILERS:
|
||||
return
|
||||
db = self._db.setdefault((objdir, filename),
|
||||
cenv.substs[self.COMPILERS[canonical_suffix]].split() +
|
||||
['-o', '/dev/null', '-c'])
|
||||
reldir = reldir or mozpath.relpath(objdir, cenv.topobjdir)
|
||||
|
||||
cmd = compiler.split() + [
|
||||
'-o', '/dev/null', '-c'
|
||||
] + cflags + [ filename ]
|
||||
def append_var(name):
|
||||
value = cenv.substs.get(name)
|
||||
if not value:
|
||||
return
|
||||
if isinstance(value, types.StringTypes):
|
||||
value = value.split()
|
||||
db.extend(value)
|
||||
|
||||
self._db.append({
|
||||
'directory': objdir,
|
||||
'command': ' '.join(shell_quote(a) for a in cmd),
|
||||
'file': filename
|
||||
})
|
||||
if canonical_suffix in ('.mm', '.cpp'):
|
||||
db.append('$(STL_FLAGS)')
|
||||
|
||||
db.extend((
|
||||
'$(VISIBILITY_FLAGS)',
|
||||
'$(DEFINES)',
|
||||
'-I%s' % mozpath.join(cenv.topsrcdir, reldir),
|
||||
'-I%s' % objdir,
|
||||
'$(LOCAL_INCLUDES)',
|
||||
'-I%s/dist/include' % cenv.topobjdir,
|
||||
'$(EXTRA_INCLUDES)',
|
||||
))
|
||||
append_var('DSO_CFLAGS')
|
||||
append_var('DSO_PIC_CFLAGS')
|
||||
if canonical_suffix in ('.c', '.cpp'):
|
||||
db.append('$(RTL_FLAGS)')
|
||||
append_var('OS_COMPILE_%s' % self.CFLAGS[canonical_suffix])
|
||||
append_var('OS_CPPFLAGS')
|
||||
append_var('OS_%s' % self.CFLAGS[canonical_suffix])
|
||||
append_var('MOZ_DEBUG_FLAGS')
|
||||
append_var('MOZ_OPTIMIZE_FLAGS')
|
||||
append_var('MOZ_FRAMEPTR_FLAGS')
|
||||
db.append('$(WARNINGS_AS_ERRORS)')
|
||||
db.append('$(MOZBUILD_%s)' % self.CFLAGS[canonical_suffix])
|
||||
if canonical_suffix == '.m':
|
||||
append_var('OS_COMPILE_CMFLAGS')
|
||||
db.append('$(MOZBUILD_CMFLAGS)')
|
||||
elif canonical_suffix == '.mm':
|
||||
append_var('OS_COMPILE_CMMFLAGS')
|
||||
db.append('$(MOZBUILD_CMMFLAGS)')
|
||||
|
|
Загрузка…
Ссылка в новой задаче