diff --git a/.ycm_extra_conf.py b/.ycm_extra_conf.py new file mode 100644 index 00000000000..332911ab744 --- /dev/null +++ b/.ycm_extra_conf.py @@ -0,0 +1,27 @@ +# 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/. + +import imp +import os +from StringIO import StringIO +import shlex + +path = os.path.join(os.path.dirname(__file__), 'mach') + +if not os.path.exists(path): + path = os.path.join(os.path.dirname(__file__), 'config.status') + config = imp.load_module('_buildconfig', open(path), path, ('', 'r', imp.PY_SOURCE)) + path = os.path.join(config.topsrcdir, 'mach') +mach_module = imp.load_module('_mach', open(path), path, ('', 'r', imp.PY_SOURCE)) + +def FlagsForFile(filename): + mach = mach_module.get_mach() + out = StringIO() + out.encoding = None + mach.run(['compileflags', filename], stdout=out, stderr=out) + + return { + 'flags': shlex.split(out.getvalue()), + 'do_cache': True + } diff --git a/build/Makefile.in b/build/Makefile.in index 5c6b151edca..61edfde57ad 100644 --- a/build/Makefile.in +++ b/build/Makefile.in @@ -53,6 +53,13 @@ LLDBINIT_FINAL_TARGET_FILES := $(DEPTH)/.lldbinit LLDBINIT_FINAL_TARGET_DEST = $(FINAL_TARGET) INSTALL_TARGETS += LLDBINIT_FINAL_TARGET +# Put the .ycm_extra_conf.py file at the root of the objdir. It is used by +# the vim plugin YouCompleteMe. +YCM_FILES := $(topsrcdir)/.ycm_extra_conf.py +YCM_DEST := $(abspath $(DEPTH)) +YCM_TARGET := export +INSTALL_TARGETS += YCM + ifdef MOZTTDIR # Install the Firefox OS fonts. include $(MOZTTDIR)/fonts.mk diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py index d04ad7586b3..aa51125214b 100644 --- a/build/mach_bootstrap.py +++ b/build/mach_bootstrap.py @@ -83,6 +83,7 @@ MACH_MODULES = [ 'python/mozboot/mozboot/mach_commands.py', 'python/mozbuild/mozbuild/mach_commands.py', 'python/mozbuild/mozbuild/backend/mach_commands.py', + 'python/mozbuild/mozbuild/compilation/codecomplete.py', 'python/mozbuild/mozbuild/frontend/mach_commands.py', 'services/common/tests/mach_commands.py', 'testing/luciddream/mach_commands.py', diff --git a/mach b/mach index 2814f6fa666..f196d368f86 100755 --- a/mach +++ b/mach @@ -27,15 +27,14 @@ def load_mach(topsrcdir): return mach_bootstrap.bootstrap(topsrcdir) -def check_and_run_mach(dir_path, args): +def check_and_get_mach(dir_path): # If we find the mach bootstrap module, we are in the srcdir. mach_path = os.path.join(dir_path, 'build/mach_bootstrap.py') if os.path.isfile(mach_path): - mach = load_mach(dir_path) - sys.exit(mach.run(args)) + return load_mach(dir_path) + return None - -def main(args): +def get_mach(): # Check whether the current directory is within a mach src or obj dir. for dir_path in ancestors(os.getcwd()): # If we find a "mozinfo.json" file, we are in the objdir. @@ -56,14 +55,20 @@ def main(args): # Continue searching for mach_bootstrap in the source directory. dir_path = info['topsrcdir'] - check_and_run_mach(dir_path, args) + mach = check_and_get_mach(dir_path) + if mach: + return mach # If we didn't find a source path by scanning for a mozinfo.json, check # whether the directory containing this script is a source directory. - check_and_run_mach(os.path.dirname(__file__), args) + return check_and_get_mach(os.path.dirname(__file__)) - print('Could not run mach: No mach source directory found.') - sys.exit(1) +def main(args): + mach = get_mach() + if not mach: + print('Could not run mach: No mach source directory found.') + sys.exit(1) + sys.exit(mach.run(args)) if __name__ == '__main__': diff --git a/python/mozbuild/mozbuild/compilation/codecomplete.py b/python/mozbuild/mozbuild/compilation/codecomplete.py new file mode 100644 index 00000000000..276011da067 --- /dev/null +++ b/python/mozbuild/mozbuild/compilation/codecomplete.py @@ -0,0 +1,85 @@ +# 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 mach.decorators import ( + CommandArgument, + CommandProvider, + Command, +) + +from mozbuild.base import MachCommandBase + +@CommandProvider +class Introspection(MachCommandBase): + """Instropection commands.""" + + @Command('compileflags', category='devenv', + description='Display the compilation flags for a given source file') + @CommandArgument('what', default=None, + help='Source file to display compilation flags for') + def compileflags(self, what): + from mozbuild.util import resolve_target_to_make + import shlex + + top_make = os.path.join(self.topobjdir, 'Makefile') + if not os.path.exists(top_make): + print('Your tree has not been built yet. Please run ' + '|mach build| with no arguments.') + return 1 + + path_arg = self._wrap_path_argument(what) + + make_dir, make_target = resolve_target_to_make(self.topobjdir, + path_arg.relpath()) + + if make_dir is None and make_target is None: + return 1 + + build_vars = {} + + def on_line(line): + elements = [s.strip() for s in line.split('=', 1)] + + if len(elements) != 2: + return + + build_vars[elements[0]] = elements[1] + + try: + old_logger = self.log_manager.replace_terminal_handler(None) + self._run_make(directory=make_dir, target='showbuild', log=False, + print_directory=False, allow_parallel=False, silent=True, + line_handler=on_line) + finally: + self.log_manager.replace_terminal_handler(old_logger) + + if what.endswith('.c'): + name = 'COMPILE_CFLAGS' + else: + name = 'COMPILE_CXXFLAGS' + + if name not in build_vars: + return + + flags = ['-isystem', '-I', '-include', '-MF'] + new_args = [] + path = os.path.join(self.topobjdir, make_dir) + for arg in shlex.split(build_vars[name]): + if new_args and new_args[-1] in flags: + arg = os.path.normpath(os.path.join(path, arg)) + else: + flag = [(f, arg[len(f):]) for f in flags + ['--sysroot='] + if arg.startswith(f)] + if flag: + flag, val = flag[0] + if val: + arg = flag + os.path.normpath(os.path.join(path, val)) + new_args.append(arg) + + print(' '.join(new_args)) +