| # Copyright 2015 the V8 project authors. All rights reserved. | 
 | # Use of this source code is governed by a BSD-style license that can be | 
 | # found in the LICENSE file. | 
 |  | 
 | # Autocompletion config for YouCompleteMe in V8. | 
 | # | 
 | # USAGE: | 
 | # | 
 | #   1. Install YCM [https://github.com/Valloric/YouCompleteMe] | 
 | #          (Googlers should check out [go/ycm]) | 
 | # | 
 | #   2. Profit | 
 | # | 
 | # | 
 | # Usage notes: | 
 | # | 
 | #   * You must use ninja & clang to build V8. | 
 | # | 
 | #   * You must have run gyp_v8 and built V8 recently. | 
 | # | 
 | # | 
 | # Hacking notes: | 
 | # | 
 | #   * The purpose of this script is to construct an accurate enough command line | 
 | #     for YCM to pass to clang so it can build and extract the symbols. | 
 | # | 
 | #   * Right now, we only pull the -I and -D flags. That seems to be sufficient | 
 | #     for everything I've used it for. | 
 | # | 
 | #   * That whole ninja & clang thing? We could support other configs if someone | 
 | #     were willing to write the correct commands and a parser. | 
 | # | 
 | #   * This has only been tested on gTrusty. | 
 |  | 
 |  | 
 | import os | 
 | import os.path | 
 | import subprocess | 
 | import sys | 
 |  | 
 |  | 
 | # Flags from YCM's default config. | 
 | flags = [ | 
 | '-DUSE_CLANG_COMPLETER', | 
 | '-std=gnu++14', | 
 | '-x', | 
 | 'c++', | 
 | ] | 
 |  | 
 |  | 
 | def PathExists(*args): | 
 |   return os.path.exists(os.path.join(*args)) | 
 |  | 
 |  | 
 | def FindV8SrcFromFilename(filename): | 
 |   """Searches for the root of the V8 checkout. | 
 |  | 
 |   Simply checks parent directories until it finds .gclient and v8/. | 
 |  | 
 |   Args: | 
 |     filename: (String) Path to source file being edited. | 
 |  | 
 |   Returns: | 
 |     (String) Path of 'v8/', or None if unable to find. | 
 |   """ | 
 |   curdir = os.path.normpath(os.path.dirname(filename)) | 
 |   while not (PathExists(curdir, 'v8') and PathExists(curdir, 'v8', 'DEPS') | 
 |              and (PathExists(curdir, '.gclient') | 
 |                   or PathExists(curdir, 'v8', '.git'))): | 
 |     nextdir = os.path.normpath(os.path.join(curdir, '..')) | 
 |     if nextdir == curdir: | 
 |       return None | 
 |     curdir = nextdir | 
 |   return os.path.join(curdir, 'v8') | 
 |  | 
 |  | 
 | def GetClangCommandFromNinjaForFilename(v8_root, filename): | 
 |   """Returns the command line to build |filename|. | 
 |  | 
 |   Asks ninja how it would build the source file. If the specified file is a | 
 |   header, tries to find its companion source file first. | 
 |  | 
 |   Args: | 
 |     v8_root: (String) Path to v8/. | 
 |     filename: (String) Path to source file being edited. | 
 |  | 
 |   Returns: | 
 |     (List of Strings) Command line arguments for clang. | 
 |   """ | 
 |   if not v8_root: | 
 |     return [] | 
 |  | 
 |   # Generally, everyone benefits from including V8's root, because all of | 
 |   # V8's includes are relative to that. | 
 |   v8_flags = ['-I' + os.path.join(v8_root)] | 
 |  | 
 |   # Version of Clang used to compile V8 can be newer then version of | 
 |   # libclang that YCM uses for completion. So it's possible that YCM's libclang | 
 |   # doesn't know about some used warning options, which causes compilation | 
 |   # warnings (and errors, because of '-Werror'); | 
 |   v8_flags.append('-Wno-unknown-warning-option') | 
 |  | 
 |   # Header files can't be built. Instead, try to match a header file to its | 
 |   # corresponding source file. | 
 |   if filename.endswith('.h'): | 
 |     alternates = ['.cc', '.cpp'] | 
 |     for alt_extension in alternates: | 
 |       alt_name = filename[:-2] + alt_extension | 
 |       if os.path.exists(alt_name): | 
 |         filename = alt_name | 
 |         break | 
 |     else: | 
 |       if filename.endswith('-inl.h'): | 
 |         for alt_extension in alternates: | 
 |           alt_name = filename[:-6] + alt_extension | 
 |           if os.path.exists(alt_name): | 
 |             filename = alt_name | 
 |             break; | 
 |         else: | 
 |           # If this is a standalone -inl.h file with no source, the best we can | 
 |           # do is try to use the default flags. | 
 |           return v8_flags | 
 |       else: | 
 |         # If this is a standalone .h file with no source, the best we can do is | 
 |         # try to use the default flags. | 
 |         return v8_flags | 
 |  | 
 |   sys.path.append(os.path.join(v8_root, 'tools', 'ninja')) | 
 |   from ninja_output import GetNinjaOutputDirectory | 
 |   out_dir = os.path.realpath(GetNinjaOutputDirectory(v8_root)) | 
 |  | 
 |   # Ninja needs the path to the source file relative to the output build | 
 |   # directory. | 
 |   rel_filename = os.path.relpath(os.path.realpath(filename), out_dir) | 
 |  | 
 |   # Ask ninja how it would build our source file. | 
 |   p = subprocess.Popen(['ninja', '-v', '-C', out_dir, '-t', | 
 |                         'commands', rel_filename + '^'], | 
 |                        stdout=subprocess.PIPE) | 
 |   stdout, stderr = p.communicate() | 
 |   if p.returncode: | 
 |     return v8_flags | 
 |  | 
 |   # Ninja might execute several commands to build something. We want the last | 
 |   # clang command. | 
 |   clang_line = None | 
 |   for line in reversed(stdout.split('\n')): | 
 |     if 'clang' in line: | 
 |       clang_line = line | 
 |       break | 
 |   else: | 
 |     return v8_flags | 
 |  | 
 |   # Parse flags that are important for YCM's purposes. | 
 |   for flag in clang_line.split(' '): | 
 |     if flag.startswith('-I'): | 
 |       # Relative paths need to be resolved, because they're relative to the | 
 |       # output dir, not the source. | 
 |       if flag[2] == '/': | 
 |         v8_flags.append(flag) | 
 |       else: | 
 |         abs_path = os.path.normpath(os.path.join(out_dir, flag[2:])) | 
 |         v8_flags.append('-I' + abs_path) | 
 |     elif flag.startswith('-std'): | 
 |       v8_flags.append(flag) | 
 |     elif flag.startswith('-') and flag[1] in 'DWFfmO': | 
 |       if flag == '-Wno-deprecated-register' or flag == '-Wno-header-guard': | 
 |         # These flags causes libclang (3.3) to crash. Remove it until things | 
 |         # are fixed. | 
 |         continue | 
 |       v8_flags.append(flag) | 
 |  | 
 |   return v8_flags | 
 |  | 
 |  | 
 | def FlagsForFile(filename): | 
 |   """This is the main entry point for YCM. Its interface is fixed. | 
 |  | 
 |   Args: | 
 |     filename: (String) Path to source file being edited. | 
 |  | 
 |   Returns: | 
 |     (Dictionary) | 
 |       'flags': (List of Strings) Command line flags. | 
 |       'do_cache': (Boolean) True if the result should be cached. | 
 |   """ | 
 |   v8_root = FindV8SrcFromFilename(filename) | 
 |   v8_flags = GetClangCommandFromNinjaForFilename(v8_root, filename) | 
 |   final_flags = flags + v8_flags | 
 |   return { | 
 |     'flags': final_flags, | 
 |     'do_cache': True | 
 |   } |