xref: /aosp_15_r20/external/webrtc/third_party/crc32c/src/.ycm_extra_conf.py (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1# Copyright 2017 The CRC32C 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"""YouCompleteMe configuration that interprets a .clang_complete file.
5
6This module implementes the YouCompleteMe configuration API documented at:
7https://github.com/ycm-core/ycmd#ycm_extra_confpy-specification
8
9The implementation loads and processes a .clang_complete file, documented at:
10https://github.com/xavierd/clang_complete/blob/master/README.md
11"""
12
13import os
14
15# Flags added to the list in .clang_complete.
16BASE_FLAGS = [
17    '-Werror',  # Unlike clang_complete, YCM can also be used as a linter.
18    '-DUSE_CLANG_COMPLETER',  # YCM needs this.
19    '-xc++',  # YCM needs this to avoid compiling headers as C code.
20]
21
22# Clang flags that take in paths.
23# See https://clang.llvm.org/docs/ClangCommandLineReference.html
24PATH_FLAGS = [
25    '-isystem',
26    '-I',
27    '-iquote',
28    '--sysroot='
29]
30
31
32def DirectoryOfThisScript():
33  """Returns the absolute path to the directory containing this script."""
34  return os.path.dirname(os.path.abspath(__file__))
35
36
37def MakeRelativePathsInFlagsAbsolute(flags, build_root):
38  """Expands relative paths in a list of Clang command-line flags.
39
40  Args:
41    flags: The list of flags passed to Clang.
42    build_root: The current directory when running the Clang compiler. Should be
43        an absolute path.
44
45  Returns:
46    A list of flags with relative paths replaced by absolute paths.
47  """
48  new_flags = []
49  make_next_absolute = False
50  for flag in flags:
51    new_flag = flag
52
53    if make_next_absolute:
54      make_next_absolute = False
55      if not flag.startswith('/'):
56        new_flag = os.path.join(build_root, flag)
57
58    for path_flag in PATH_FLAGS:
59      if flag == path_flag:
60        make_next_absolute = True
61        break
62
63      if flag.startswith(path_flag):
64        path = flag[len(path_flag):]
65        new_flag = path_flag + os.path.join(build_root, path)
66        break
67
68    if new_flag:
69      new_flags.append(new_flag)
70  return new_flags
71
72
73def FindNearest(target, path, build_root):
74  """Looks for a file with a specific name closest to a project path.
75
76  This is similar to the logic used by a version-control system (like git) to
77  find its configuration directory (.git) based on the current directory when a
78  command is invoked.
79
80  Args:
81    target: The file name to search for.
82    path: The directory where the search starts. The search will explore the
83        given directory's ascendants using the parent relationship. Should be an
84        absolute path.
85    build_root: A directory that acts as a fence for the search. If the search
86        reaches this directory, it will not advance to its parent. Should be an
87        absolute path.
88
89  Returns:
90    The path to a file with the desired name. None if the search failed.
91  """
92  candidate = os.path.join(path, target)
93  if os.path.isfile(candidate):
94    return candidate
95
96  if path == build_root:
97    return None
98
99  parent = os.path.dirname(path)
100  if parent == path:
101    return None
102
103  return FindNearest(target, parent, build_root)
104
105
106def FlagsForClangComplete(file_path, build_root):
107  """Reads the .clang_complete flags for a source file.
108
109  Args:
110    file_path: The path to the source file. Should be inside the project. Used
111      to locate the relevant .clang_complete file.
112    build_root: The current directory when running the Clang compiler for this
113        file. Should be an absolute path.
114
115  Returns:
116    A list of strings, where each element is a Clang command-line flag.
117  """
118  clang_complete_path = FindNearest('.clang_complete', file_path, build_root)
119  if clang_complete_path is None:
120    return None
121  clang_complete_flags = open(clang_complete_path, 'r').read().splitlines()
122  return clang_complete_flags
123
124
125def FlagsForFile(filename, **kwargs):
126  """Implements the YouCompleteMe API."""
127
128  # kwargs can be used to pass 'client_data' to the YCM configuration. This
129  # configuration script does not need any extra information, so
130  # pylint: disable=unused-argument
131
132  build_root = DirectoryOfThisScript()
133  file_path = os.path.realpath(filename)
134
135  flags = BASE_FLAGS
136  clang_flags = FlagsForClangComplete(file_path, build_root)
137  if clang_flags:
138    flags += clang_flags
139
140  final_flags = MakeRelativePathsInFlagsAbsolute(flags, build_root)
141
142  return {'flags': final_flags}
143