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