.ycm_extra_conf.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. # Copyright 2015 the V8 project authors. All rights reserved.
  2. # Use of this source code is governed by a BSD-style license that can be
  3. # found in the LICENSE file.
  4. # Autocompletion config for YouCompleteMe in V8.
  5. #
  6. # USAGE:
  7. #
  8. # 1. Install YCM [https://github.com/Valloric/YouCompleteMe]
  9. # (Googlers should check out [go/ycm])
  10. #
  11. # 2. Profit
  12. #
  13. #
  14. # Usage notes:
  15. #
  16. # * You must use ninja & clang to build V8.
  17. #
  18. # * You must have run gyp_v8 and built V8 recently.
  19. #
  20. #
  21. # Hacking notes:
  22. #
  23. # * The purpose of this script is to construct an accurate enough command line
  24. # for YCM to pass to clang so it can build and extract the symbols.
  25. #
  26. # * Right now, we only pull the -I and -D flags. That seems to be sufficient
  27. # for everything I've used it for.
  28. #
  29. # * That whole ninja & clang thing? We could support other configs if someone
  30. # were willing to write the correct commands and a parser.
  31. #
  32. # * This has only been tested on gTrusty.
  33. import os
  34. import os.path
  35. import subprocess
  36. import sys
  37. # Flags from YCM's default config.
  38. flags = [
  39. '-DUSE_CLANG_COMPLETER',
  40. '-std=gnu++14',
  41. '-x',
  42. 'c++',
  43. ]
  44. def PathExists(*args):
  45. return os.path.exists(os.path.join(*args))
  46. def FindV8SrcFromFilename(filename):
  47. """Searches for the root of the V8 checkout.
  48. Simply checks parent directories until it finds .gclient and v8/.
  49. Args:
  50. filename: (String) Path to source file being edited.
  51. Returns:
  52. (String) Path of 'v8/', or None if unable to find.
  53. """
  54. curdir = os.path.normpath(os.path.dirname(filename))
  55. while not (PathExists(curdir, 'v8') and PathExists(curdir, 'v8', 'DEPS')
  56. and (PathExists(curdir, '.gclient')
  57. or PathExists(curdir, 'v8', '.git'))):
  58. nextdir = os.path.normpath(os.path.join(curdir, '..'))
  59. if nextdir == curdir:
  60. return None
  61. curdir = nextdir
  62. return os.path.join(curdir, 'v8')
  63. def GetClangCommandFromNinjaForFilename(v8_root, filename):
  64. """Returns the command line to build |filename|.
  65. Asks ninja how it would build the source file. If the specified file is a
  66. header, tries to find its companion source file first.
  67. Args:
  68. v8_root: (String) Path to v8/.
  69. filename: (String) Path to source file being edited.
  70. Returns:
  71. (List of Strings) Command line arguments for clang.
  72. """
  73. if not v8_root:
  74. return []
  75. # Generally, everyone benefits from including V8's root, because all of
  76. # V8's includes are relative to that.
  77. v8_flags = ['-I' + os.path.join(v8_root)]
  78. # Version of Clang used to compile V8 can be newer then version of
  79. # libclang that YCM uses for completion. So it's possible that YCM's libclang
  80. # doesn't know about some used warning options, which causes compilation
  81. # warnings (and errors, because of '-Werror');
  82. v8_flags.append('-Wno-unknown-warning-option')
  83. # Header files can't be built. Instead, try to match a header file to its
  84. # corresponding source file.
  85. if filename.endswith('.h'):
  86. base = filename[:-6] if filename.endswith('-inl.h') else filename[:-2]
  87. for alternate in [base + e for e in ['.cc', '.cpp']]:
  88. if os.path.exists(alternate):
  89. filename = alternate
  90. break
  91. else:
  92. # If this is a standalone .h file with no source, we ask ninja for the
  93. # compile flags of some generic cc file ('src/utils/utils.cc'). This
  94. # should contain most/all of the interesting flags for other targets too.
  95. filename = os.path.join(v8_root, 'src', 'utils', 'utils.cc')
  96. sys.path.append(os.path.join(v8_root, 'tools', 'vim'))
  97. from ninja_output import GetNinjaOutputDirectory
  98. out_dir = os.path.realpath(GetNinjaOutputDirectory(v8_root))
  99. # Ninja needs the path to the source file relative to the output build
  100. # directory.
  101. rel_filename = os.path.relpath(os.path.realpath(filename), out_dir)
  102. # Ask ninja how it would build our source file.
  103. p = subprocess.Popen(['ninja', '-v', '-C', out_dir, '-t',
  104. 'commands', rel_filename + '^'],
  105. stdout=subprocess.PIPE)
  106. stdout, stderr = p.communicate()
  107. if p.returncode:
  108. return v8_flags
  109. # Ninja might execute several commands to build something. We want the last
  110. # clang command.
  111. clang_line = None
  112. for line in reversed(stdout.decode('utf-8').splitlines()):
  113. if 'clang' in line:
  114. clang_line = line
  115. break
  116. else:
  117. return v8_flags
  118. # Parse flags that are important for YCM's purposes.
  119. for flag in clang_line.split(' '):
  120. if flag.startswith('-I'):
  121. # Relative paths need to be resolved, because they're relative to the
  122. # output dir, not the source.
  123. if flag[2] == '/':
  124. v8_flags.append(flag)
  125. else:
  126. abs_path = os.path.normpath(os.path.join(out_dir, flag[2:]))
  127. v8_flags.append('-I' + abs_path)
  128. elif flag.startswith('-std'):
  129. v8_flags.append(flag)
  130. elif flag.startswith('-') and flag[1] in 'DWFfmO':
  131. if flag == '-Wno-deprecated-register' or flag == '-Wno-header-guard':
  132. # These flags causes libclang (3.3) to crash. Remove it until things
  133. # are fixed.
  134. continue
  135. v8_flags.append(flag)
  136. return v8_flags
  137. def FlagsForFile(filename):
  138. """This is the main entry point for YCM. Its interface is fixed.
  139. Args:
  140. filename: (String) Path to source file being edited.
  141. Returns:
  142. (Dictionary)
  143. 'flags': (List of Strings) Command line flags.
  144. 'do_cache': (Boolean) True if the result should be cached.
  145. """
  146. v8_root = FindV8SrcFromFilename(filename)
  147. v8_flags = GetClangCommandFromNinjaForFilename(v8_root, filename)
  148. final_flags = flags + v8_flags
  149. return {
  150. 'flags': final_flags,
  151. 'do_cache': True
  152. }