]> git.ipfire.org Git - thirdparty/systemd.git/blame - .ycm_extra_conf.py
journal-remote: shorten code a bit by using strcspn()
[thirdparty/systemd.git] / .ycm_extra_conf.py
CommitLineData
1c5ea591
JX
1#!/usr/bin/env python
2
3# SPDX-License-Identifier: Unlicense
4#
5# Based on the template file provided by the 'YCM-Generator' project authored by
6# Reuben D'Netto.
7# Jiahui Xie has re-reformatted and expanded the original script in accordance
8# to the requirements of the PEP 8 style guide and 'systemd' project,
9# respectively.
10#
11# The original license is preserved as it is.
12#
13#
14# This is free and unencumbered software released into the public domain.
15#
16# Anyone is free to copy, modify, publish, use, compile, sell, or
17# distribute this software, either in source code form or as a compiled
18# binary, for any purpose, commercial or non-commercial, and by any
19# means.
20#
21# In jurisdictions that recognize copyright laws, the author or authors
22# of this software dedicate any and all copyright interest in the
23# software to the public domain. We make this dedication for the benefit
24# of the public at large and to the detriment of our heirs and
25# successors. We intend this dedication to be an overt act of
26# relinquishment in perpetuity of all present and future rights to this
27# software under copyright law.
28#
29# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
32# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
33# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
34# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
35# OTHER DEALINGS IN THE SOFTWARE.
36#
37# For more information, please refer to <http://unlicense.org/>
38
39"""
40YouCompleteMe configuration file tailored to support the 'meson' build system
41used by the 'systemd' project.
42"""
43
44import glob
328b5bc9 45import os
1c5ea591 46import ycm_core
328b5bc9
DR
47
48
50918c2c
JX
49SOURCE_EXTENSIONS = (".C", ".cpp", ".cxx", ".cc", ".c", ".m", ".mm")
50HEADER_EXTENSIONS = (".H", ".h", ".hxx", ".hpp", ".hh")
51
52
328b5bc9 53def DirectoryOfThisScript():
cfbdfa8c
JX
54 """
55 Return the absolute path of the parent directory containing this
56 script.
57 """
1c5ea591
JX
58 return os.path.dirname(os.path.abspath(__file__))
59
60
61def GuessBuildDirectory():
cfbdfa8c
JX
62 """
63 Guess the build directory using the following heuristics:
64
65 1. Returns the current directory of this script plus 'build'
66 subdirectory in absolute path if this subdirectory exists.
67
68 2. Otherwise, probes whether there exists any directory
69 containing '.ninja_log' file two levels above the current directory;
70 returns this single directory only if there is one candidate.
71 """
1c5ea591
JX
72 result = os.path.join(DirectoryOfThisScript(), "build")
73
74 if os.path.exists(result):
75 return result
76
77 result = glob.glob(os.path.join(DirectoryOfThisScript(),
78 "..", "..", "*", ".ninja_log"))
79
80 if not result:
81 return ""
82
83 if 1 != len(result):
84 return ""
85
86 return os.path.split(result[0])[0]
87
88
89def TraverseByDepth(root, include_extensions):
cfbdfa8c
JX
90 """
91 Return a set of child directories of the 'root' containing file
92 extensions specified in 'include_extensions'.
93
94 NOTE:
95 1. The 'root' directory itself is excluded from the result set.
96 2. No subdirectories would be excluded if 'include_extensions' is left
97 to 'None'.
98 3. Each entry in 'include_extensions' must begin with string '.'.
99 """
1c5ea591
JX
100 is_root = True
101 result = set()
102 # Perform a depth first top down traverse of the given directory tree.
103 for root_dir, subdirs, file_list in os.walk(root):
104 if not is_root:
105 # print("Relative Root: ", root_dir)
106 # print(subdirs)
107 if include_extensions:
108 get_ext = os.path.splitext
109 subdir_extensions = {
110 get_ext(f)[-1] for f in file_list if get_ext(f)[-1]
111 }
112 if subdir_extensions & include_extensions:
113 result.add(root_dir)
114 else:
115 result.add(root_dir)
116 else:
117 is_root = False
118
119 return result
120
121
122_project_src_dir = os.path.join(DirectoryOfThisScript(), "src")
123_include_dirs_set = TraverseByDepth(_project_src_dir, frozenset({".h"}))
124flags = [
125 "-x",
126 "c"
127 # The following flags are partially redundant due to the existence of
128 # 'compile_commands.json'.
129 # '-Wall',
130 # '-Wextra',
131 # '-Wfloat-equal',
132 # '-Wpointer-arith',
133 # '-Wshadow',
134 # '-std=gnu99',
135]
136
137for include_dir in _include_dirs_set:
138 flags.append("-I" + include_dir)
139
140# Set this to the absolute path to the folder (NOT the file!) containing the
141# compile_commands.json file to use that instead of 'flags'. See here for
142# more details: http://clang.llvm.org/docs/JSONCompilationDatabase.html
143#
144# You can get CMake to generate this file for you by adding:
145# set( CMAKE_EXPORT_COMPILE_COMMANDS 1 )
146# to your CMakeLists.txt file.
147#
148# Most projects will NOT need to set this to anything; you can just change the
149# 'flags' list of compilation flags. Notice that YCM itself uses that approach.
150compilation_database_folder = GuessBuildDirectory()
151
152if os.path.exists(compilation_database_folder):
153 database = ycm_core.CompilationDatabase(compilation_database_folder)
154else:
155 database = None
156
328b5bc9
DR
157
158def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
cfbdfa8c
JX
159 """
160 Iterate through 'flags' and replace the relative paths prefixed by
161 '-isystem', '-I', '-iquote', '--sysroot=' with absolute paths
162 start with 'working_directory'.
163 """
1c5ea591
JX
164 if not working_directory:
165 return list(flags)
166 new_flags = []
167 make_next_absolute = False
168 path_flags = ["-isystem", "-I", "-iquote", "--sysroot="]
169 for flag in flags:
170 new_flag = flag
171
172 if make_next_absolute:
173 make_next_absolute = False
174 if not flag.startswith("/"):
175 new_flag = os.path.join(working_directory, flag)
176
177 for path_flag in path_flags:
178 if flag == path_flag:
179 make_next_absolute = True
180 break
181
182 if flag.startswith(path_flag):
183 path = flag[len(path_flag):]
184 new_flag = path_flag + os.path.join(working_directory, path)
185 break
186
187 if new_flag:
188 new_flags.append(new_flag)
189 return new_flags
190
191
192def IsHeaderFile(filename):
cfbdfa8c
JX
193 """
194 Check whether 'filename' is considered as a header file.
195 """
1c5ea591 196 extension = os.path.splitext(filename)[1]
50918c2c 197 return extension in HEADER_EXTENSIONS
1c5ea591
JX
198
199
200def GetCompilationInfoForFile(filename):
cfbdfa8c
JX
201 """
202 Helper function to look up compilation info of 'filename' in the 'database'.
203 """
1c5ea591
JX
204 # The compilation_commands.json file generated by CMake does not have
205 # entries for header files. So we do our best by asking the db for flags for
206 # a corresponding source file, if any. If one exists, the flags for that
207 # file should be good enough.
208 if not database:
209 return None
210
211 if IsHeaderFile(filename):
212 basename = os.path.splitext(filename)[0]
213 for extension in SOURCE_EXTENSIONS:
214 replacement_file = basename + extension
215 if os.path.exists(replacement_file):
216 compilation_info = \
217 database.GetCompilationInfoForFile(replacement_file)
218 if compilation_info.compiler_flags_:
219 return compilation_info
220 return None
221 return database.GetCompilationInfoForFile(filename)
222
223
224def FlagsForFile(filename, **kwargs):
cfbdfa8c
JX
225 """
226 Callback function to be invoked by YouCompleteMe in order to get the
227 information necessary to compile 'filename'.
228
229 It returns a dictionary with a single element 'flags'. This element is a
230 list of compiler flags to pass to libclang for the file 'filename'.
231 """
1c5ea591
JX
232 if database:
233 # Bear in mind that compilation_info.compiler_flags_ does NOT return a
234 # python list, but a "list-like" StringVec object
235 compilation_info = GetCompilationInfoForFile(filename)
236 if not compilation_info:
237 return None
238
239 final_flags = MakeRelativePathsInFlagsAbsolute(
240 compilation_info.compiler_flags_,
241 compilation_info.compiler_working_dir_)
242
243 else:
244 relative_to = DirectoryOfThisScript()
245 final_flags = MakeRelativePathsInFlagsAbsolute(flags, relative_to)
246
247 return {
248 "flags": final_flags,
249 "do_cache": True
250 }