| # 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 |
| }) |
| |