blob: 39b7c99248d7c1cc0ce08c178135c2f7ccfea1a0 [file] [log] [blame]
# 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
from mozbuild.base import MozbuildObject
from mozbuild.compilation import util
from mozbuild.backend.common import CommonBackend
from mozbuild.frontend.data import (
Sources,
HostSources,
UnifiedSources,
GeneratedSources,
)
from mozbuild.shellutil import quote as shell_quote
from mach.config import ConfigSettings
from mach.logging import LoggingManager
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 = []
# The cache for per-directory flags
self._flags = {}
log_manager = LoggingManager()
self._cmd = MozbuildObject(self.environment.topsrcdir, ConfigSettings(),
log_manager, self.environment.topobjdir)
def consume_object(self, obj):
consumed = CommonBackend.consume_object(self, obj)
if consumed:
return True
if isinstance(obj, Sources) or isinstance(obj, HostSources) or \
isinstance(obj, 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,
isinstance(obj, HostSources))
return True
def consume_finished(self):
CommonBackend.consume_finished(self)
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)
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, False)
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, False)
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, False)
def _get_dir_flags(self, directory):
if directory in self._flags:
return self._flags[directory]
from mozbuild.util import resolve_target_to_make
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.get_flags(self.environment.topobjdir, directory,
build_vars, name)
self._flags[directory] = build_vars
return self._flags[directory]
def _build_db_line(self, objdir, cenv, filename, canonical_suffix, flags, ishost):
# Distinguish between host and target files.
prefix = 'HOST_' if ishost else ''
if canonical_suffix == '.c':
compiler = cenv.substs[prefix + 'CC']
cflags = list(flags['COMPILE_CFLAGS'])
# Add the Objective-C flags if needed.
if filename.endswith('.m'):
cflags.extend(flags['COMPILE_CMFLAGS'])
elif canonical_suffix == '.cpp':
compiler = cenv.substs[prefix + 'CXX']
cflags = list(flags['COMPILE_CXXFLAGS'])
# Add the Objective-C++ flags if needed.
if filename.endswith('.mm'):
cflags.extend(flags['COMPILE_CMMFLAGS'])
else:
return
cmd = compiler.split() + [
'-o', '/dev/null', '-c'
] + cflags + [ filename ]
self._db.append({
'directory': objdir,
'command': ' '.join(shell_quote(a) for a in cmd),
'file': filename
})