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 260371 1bfcd43acd3c9454ff8ed1d1a40907793d5c8e73
parent 260370 fe8d97018a57e96f51a0ad3ab1e423f9e34e7f56
child 260375 c2c74fe15d1e19cd75e2e9685634e54c9c92ceb5
push idunknown
push userunknown
push dateunknown
reviewersgps
bugs892973
milestone40.0a1
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))
+