]> git.ipfire.org Git - thirdparty/systemd.git/blob - .ycm_extra_conf.py
firstboot: Check if the given shell exists
[thirdparty/systemd.git] / .ycm_extra_conf.py
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 """
40 YouCompleteMe configuration file tailored to support the 'meson' build system
41 used by the 'systemd' project.
42 """
43
44 import glob
45 import os
46 import ycm_core
47
48
49 SOURCE_EXTENSIONS = (".C", ".cpp", ".cxx", ".cc", ".c", ".m", ".mm")
50 HEADER_EXTENSIONS = (".H", ".h", ".hxx", ".hpp", ".hh")
51
52
53 def DirectoryOfThisScript():
54 """
55 Return the absolute path of the parent directory containing this
56 script.
57 """
58 return os.path.dirname(os.path.abspath(__file__))
59
60
61 def GuessBuildDirectory():
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 """
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
89 def TraverseByDepth(root, include_extensions):
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 """
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"}))
124 flags = [
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
137 for 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.
150 compilation_database_folder = GuessBuildDirectory()
151
152 if os.path.exists(compilation_database_folder):
153 database = ycm_core.CompilationDatabase(compilation_database_folder)
154 else:
155 database = None
156
157
158 def MakeRelativePathsInFlagsAbsolute(flags, working_directory):
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 """
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
192 def IsHeaderFile(filename):
193 """
194 Check whether 'filename' is considered as a header file.
195 """
196 extension = os.path.splitext(filename)[1]
197 return extension in HEADER_EXTENSIONS
198
199
200 def GetCompilationInfoForFile(filename):
201 """
202 Helper function to look up compilation info of 'filename' in the 'database'.
203 """
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
224 def FlagsForFile(filename, **kwargs):
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 """
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 }