11cb0ef41Sopenharmony_ci# Copyright 2015 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci# Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci# found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci# Autocompletion config for YouCompleteMe in V8.
61cb0ef41Sopenharmony_ci#
71cb0ef41Sopenharmony_ci# USAGE:
81cb0ef41Sopenharmony_ci#
91cb0ef41Sopenharmony_ci#   1. Install YCM [https://github.com/Valloric/YouCompleteMe]
101cb0ef41Sopenharmony_ci#          (Googlers should check out [go/ycm])
111cb0ef41Sopenharmony_ci#
121cb0ef41Sopenharmony_ci#   2. Profit
131cb0ef41Sopenharmony_ci#
141cb0ef41Sopenharmony_ci#
151cb0ef41Sopenharmony_ci# Usage notes:
161cb0ef41Sopenharmony_ci#
171cb0ef41Sopenharmony_ci#   * You must use ninja & clang to build V8.
181cb0ef41Sopenharmony_ci#
191cb0ef41Sopenharmony_ci#   * You must have run gyp_v8 and built V8 recently.
201cb0ef41Sopenharmony_ci#
211cb0ef41Sopenharmony_ci#
221cb0ef41Sopenharmony_ci# Hacking notes:
231cb0ef41Sopenharmony_ci#
241cb0ef41Sopenharmony_ci#   * The purpose of this script is to construct an accurate enough command line
251cb0ef41Sopenharmony_ci#     for YCM to pass to clang so it can build and extract the symbols.
261cb0ef41Sopenharmony_ci#
271cb0ef41Sopenharmony_ci#   * Right now, we only pull the -I and -D flags. That seems to be sufficient
281cb0ef41Sopenharmony_ci#     for everything I've used it for.
291cb0ef41Sopenharmony_ci#
301cb0ef41Sopenharmony_ci#   * That whole ninja & clang thing? We could support other configs if someone
311cb0ef41Sopenharmony_ci#     were willing to write the correct commands and a parser.
321cb0ef41Sopenharmony_ci#
331cb0ef41Sopenharmony_ci#   * This has only been tested on gTrusty.
341cb0ef41Sopenharmony_ci
351cb0ef41Sopenharmony_ci
361cb0ef41Sopenharmony_ciimport os
371cb0ef41Sopenharmony_ciimport os.path
381cb0ef41Sopenharmony_ciimport subprocess
391cb0ef41Sopenharmony_ciimport sys
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_ci
421cb0ef41Sopenharmony_ci# Flags from YCM's default config.
431cb0ef41Sopenharmony_ciflags = [
441cb0ef41Sopenharmony_ci'-DUSE_CLANG_COMPLETER',
451cb0ef41Sopenharmony_ci'-std=gnu++14',
461cb0ef41Sopenharmony_ci'-x',
471cb0ef41Sopenharmony_ci'c++',
481cb0ef41Sopenharmony_ci]
491cb0ef41Sopenharmony_ci
501cb0ef41Sopenharmony_ci
511cb0ef41Sopenharmony_cidef PathExists(*args):
521cb0ef41Sopenharmony_ci  return os.path.exists(os.path.join(*args))
531cb0ef41Sopenharmony_ci
541cb0ef41Sopenharmony_ci
551cb0ef41Sopenharmony_cidef FindV8SrcFromFilename(filename):
561cb0ef41Sopenharmony_ci  """Searches for the root of the V8 checkout.
571cb0ef41Sopenharmony_ci
581cb0ef41Sopenharmony_ci  Simply checks parent directories until it finds .gclient and v8/.
591cb0ef41Sopenharmony_ci
601cb0ef41Sopenharmony_ci  Args:
611cb0ef41Sopenharmony_ci    filename: (String) Path to source file being edited.
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ci  Returns:
641cb0ef41Sopenharmony_ci    (String) Path of 'v8/', or None if unable to find.
651cb0ef41Sopenharmony_ci  """
661cb0ef41Sopenharmony_ci  curdir = os.path.normpath(os.path.dirname(filename))
671cb0ef41Sopenharmony_ci  while not (PathExists(curdir, 'v8') and PathExists(curdir, 'v8', 'DEPS')
681cb0ef41Sopenharmony_ci             and (PathExists(curdir, '.gclient')
691cb0ef41Sopenharmony_ci                  or PathExists(curdir, 'v8', '.git'))):
701cb0ef41Sopenharmony_ci    nextdir = os.path.normpath(os.path.join(curdir, '..'))
711cb0ef41Sopenharmony_ci    if nextdir == curdir:
721cb0ef41Sopenharmony_ci      return None
731cb0ef41Sopenharmony_ci    curdir = nextdir
741cb0ef41Sopenharmony_ci  return os.path.join(curdir, 'v8')
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ci
771cb0ef41Sopenharmony_cidef GetClangCommandFromNinjaForFilename(v8_root, filename):
781cb0ef41Sopenharmony_ci  """Returns the command line to build |filename|.
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_ci  Asks ninja how it would build the source file. If the specified file is a
811cb0ef41Sopenharmony_ci  header, tries to find its companion source file first.
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci  Args:
841cb0ef41Sopenharmony_ci    v8_root: (String) Path to v8/.
851cb0ef41Sopenharmony_ci    filename: (String) Path to source file being edited.
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci  Returns:
881cb0ef41Sopenharmony_ci    (List of Strings) Command line arguments for clang.
891cb0ef41Sopenharmony_ci  """
901cb0ef41Sopenharmony_ci  if not v8_root:
911cb0ef41Sopenharmony_ci    return []
921cb0ef41Sopenharmony_ci
931cb0ef41Sopenharmony_ci  # Generally, everyone benefits from including V8's root, because all of
941cb0ef41Sopenharmony_ci  # V8's includes are relative to that.
951cb0ef41Sopenharmony_ci  v8_flags = ['-I' + os.path.join(v8_root)]
961cb0ef41Sopenharmony_ci
971cb0ef41Sopenharmony_ci  # Version of Clang used to compile V8 can be newer then version of
981cb0ef41Sopenharmony_ci  # libclang that YCM uses for completion. So it's possible that YCM's libclang
991cb0ef41Sopenharmony_ci  # doesn't know about some used warning options, which causes compilation
1001cb0ef41Sopenharmony_ci  # warnings (and errors, because of '-Werror');
1011cb0ef41Sopenharmony_ci  v8_flags.append('-Wno-unknown-warning-option')
1021cb0ef41Sopenharmony_ci
1031cb0ef41Sopenharmony_ci  # Header files can't be built. Instead, try to match a header file to its
1041cb0ef41Sopenharmony_ci  # corresponding source file.
1051cb0ef41Sopenharmony_ci  if filename.endswith('.h'):
1061cb0ef41Sopenharmony_ci    base = filename[:-6] if filename.endswith('-inl.h') else filename[:-2]
1071cb0ef41Sopenharmony_ci    for alternate in [base + e for e in ['.cc', '.cpp']]:
1081cb0ef41Sopenharmony_ci      if os.path.exists(alternate):
1091cb0ef41Sopenharmony_ci        filename = alternate
1101cb0ef41Sopenharmony_ci        break
1111cb0ef41Sopenharmony_ci    else:
1121cb0ef41Sopenharmony_ci      # If this is a standalone .h file with no source, we ask ninja for the
1131cb0ef41Sopenharmony_ci      # compile flags of some generic cc file ('src/utils/utils.cc'). This
1141cb0ef41Sopenharmony_ci      # should contain most/all of the interesting flags for other targets too.
1151cb0ef41Sopenharmony_ci      filename = os.path.join(v8_root, 'src', 'utils', 'utils.cc')
1161cb0ef41Sopenharmony_ci
1171cb0ef41Sopenharmony_ci  sys.path.append(os.path.join(v8_root, 'tools', 'vim'))
1181cb0ef41Sopenharmony_ci  from ninja_output import GetNinjaOutputDirectory
1191cb0ef41Sopenharmony_ci  out_dir = os.path.realpath(GetNinjaOutputDirectory(v8_root))
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci  # Ninja needs the path to the source file relative to the output build
1221cb0ef41Sopenharmony_ci  # directory.
1231cb0ef41Sopenharmony_ci  rel_filename = os.path.relpath(os.path.realpath(filename), out_dir)
1241cb0ef41Sopenharmony_ci
1251cb0ef41Sopenharmony_ci  # Ask ninja how it would build our source file.
1261cb0ef41Sopenharmony_ci  p = subprocess.Popen(['ninja', '-v', '-C', out_dir, '-t',
1271cb0ef41Sopenharmony_ci                        'commands', rel_filename + '^'],
1281cb0ef41Sopenharmony_ci                       stdout=subprocess.PIPE)
1291cb0ef41Sopenharmony_ci  stdout, stderr = p.communicate()
1301cb0ef41Sopenharmony_ci  if p.returncode:
1311cb0ef41Sopenharmony_ci    return v8_flags
1321cb0ef41Sopenharmony_ci
1331cb0ef41Sopenharmony_ci  # Ninja might execute several commands to build something. We want the last
1341cb0ef41Sopenharmony_ci  # clang command.
1351cb0ef41Sopenharmony_ci  clang_line = None
1361cb0ef41Sopenharmony_ci  for line in reversed(stdout.decode('utf-8').splitlines()):
1371cb0ef41Sopenharmony_ci    if 'clang' in line:
1381cb0ef41Sopenharmony_ci      clang_line = line
1391cb0ef41Sopenharmony_ci      break
1401cb0ef41Sopenharmony_ci  else:
1411cb0ef41Sopenharmony_ci    return v8_flags
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_ci  # Parse flags that are important for YCM's purposes.
1441cb0ef41Sopenharmony_ci  for flag in clang_line.split(' '):
1451cb0ef41Sopenharmony_ci    if flag.startswith('-I'):
1461cb0ef41Sopenharmony_ci      # Relative paths need to be resolved, because they're relative to the
1471cb0ef41Sopenharmony_ci      # output dir, not the source.
1481cb0ef41Sopenharmony_ci      if flag[2] == '/':
1491cb0ef41Sopenharmony_ci        v8_flags.append(flag)
1501cb0ef41Sopenharmony_ci      else:
1511cb0ef41Sopenharmony_ci        abs_path = os.path.normpath(os.path.join(out_dir, flag[2:]))
1521cb0ef41Sopenharmony_ci        v8_flags.append('-I' + abs_path)
1531cb0ef41Sopenharmony_ci    elif flag.startswith('-std'):
1541cb0ef41Sopenharmony_ci      v8_flags.append(flag)
1551cb0ef41Sopenharmony_ci    elif flag.startswith('-') and flag[1] in 'DWFfmO':
1561cb0ef41Sopenharmony_ci      if flag == '-Wno-deprecated-register' or flag == '-Wno-header-guard':
1571cb0ef41Sopenharmony_ci        # These flags causes libclang (3.3) to crash. Remove it until things
1581cb0ef41Sopenharmony_ci        # are fixed.
1591cb0ef41Sopenharmony_ci        continue
1601cb0ef41Sopenharmony_ci      v8_flags.append(flag)
1611cb0ef41Sopenharmony_ci
1621cb0ef41Sopenharmony_ci  return v8_flags
1631cb0ef41Sopenharmony_ci
1641cb0ef41Sopenharmony_ci
1651cb0ef41Sopenharmony_cidef FlagsForFile(filename):
1661cb0ef41Sopenharmony_ci  """This is the main entry point for YCM. Its interface is fixed.
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_ci  Args:
1691cb0ef41Sopenharmony_ci    filename: (String) Path to source file being edited.
1701cb0ef41Sopenharmony_ci
1711cb0ef41Sopenharmony_ci  Returns:
1721cb0ef41Sopenharmony_ci    (Dictionary)
1731cb0ef41Sopenharmony_ci      'flags': (List of Strings) Command line flags.
1741cb0ef41Sopenharmony_ci      'do_cache': (Boolean) True if the result should be cached.
1751cb0ef41Sopenharmony_ci  """
1761cb0ef41Sopenharmony_ci  v8_root = FindV8SrcFromFilename(filename)
1771cb0ef41Sopenharmony_ci  v8_flags = GetClangCommandFromNinjaForFilename(v8_root, filename)
1781cb0ef41Sopenharmony_ci  final_flags = flags + v8_flags
1791cb0ef41Sopenharmony_ci  return {
1801cb0ef41Sopenharmony_ci    'flags': final_flags,
1811cb0ef41Sopenharmony_ci    'do_cache': True
1821cb0ef41Sopenharmony_ci  }
183