Bug 892973 - Add support for the YouCompleteMe vim plugin; r=gps
authorEhsan Akhgari <ehsan@mozilla.com>
Fri, 24 Apr 2015 15:12:50 -0400
changeset 241325 1bfcd43acd3c9454ff8ed1d1a40907793d5c8e73
parent 241324 fe8d97018a57e96f51a0ad3ab1e423f9e34e7f56
child 241326 c2c74fe15d1e19cd75e2e9685634e54c9c92ceb5
push id28658
push usercbook@mozilla.com
push dateTue, 28 Apr 2015 10:01:27 +0000
treeherdermozilla-central@e0299ad29b85 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgps
bugs892973
milestone40.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 892973 - Add support for the YouCompleteMe vim plugin; r=gps
.ycm_extra_conf.py
build/Makefile.in
build/mach_bootstrap.py
mach
python/mozbuild/mozbuild/compilation/codecomplete.py
new file mode 100644
--- /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
+    }
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -48,16 +48,23 @@ LLDBINIT_OBJDIR := .lldbinit.in
 LLDBINIT_OBJDIR_PATH = $(DEPTH)
 LLDBINIT_OBJDIR_FLAGS += -Dtopsrcdir=$(abspath $(topsrcdir))
 PP_TARGETS += LLDBINIT_OBJDIR
 
 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
 MOZTT_DEST = $(FINAL_TARGET)/fonts
 MOZTT_FILES = $(patsubst external/moztt/%,$(MOZTTDIR)/%,$(filter external/moztt/%,$(subst :, ,$(PRODUCT_COPY_FILES))))
 INSTALL_TARGETS += MOZTT
 endif
 
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -78,16 +78,17 @@ MACH_MODULES = [
     'dom/bindings/mach_commands.py',
     'layout/tools/reftest/mach_commands.py',
     'python/mach_commands.py',
     'python/mach/mach/commands/commandinfo.py',
     'python/compare-locales/mach_commands.py',
     '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',
     'testing/mach_commands.py',
     'testing/taskcluster/mach_commands.py',
     'testing/marionette/mach_commands.py',
     'testing/mochitest/mach_commands.py',
     'testing/xpcshell/mach_commands.py',
--- a/mach
+++ b/mach
@@ -22,25 +22,24 @@ def ancestors(path):
             break
 
 def load_mach(topsrcdir):
     sys.path[0:0] = [os.path.join(topsrcdir, "build")]
     import mach_bootstrap
     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.
         mozinfo_path = os.path.join(dir_path, 'mozinfo.json')
         if os.path.isfile(mozinfo_path):
             import json
             info = json.load(open(mozinfo_path))
             if 'mozconfig' in info and 'MOZCONFIG' not in os.environ:
@@ -51,24 +50,30 @@ def main(args):
                 #
                 # Note: subprocess requires native strings in os.environ on Windows
                 os.environ[b'MOZCONFIG'] = str(info['mozconfig'])
 
             if 'topsrcdir' in info:
                 # 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__':
     if sys.platform == 'win32':
         # This is a complete hack to work around the fact that Windows
         # multiprocessing needs to import the original module (ie: this
         # file), but only works if it has a .py extension.
         #
new file mode 100644
--- /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))
+