Mercurial > hg > CbC > CbC_llvm
comparison clang-tools-extra/clangd/QueryDriverDatabase.cpp @ 221:79ff65ed7e25
LLVM12 Original
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 15 Jun 2021 19:15:29 +0900 |
parents | 0572611fdcc8 |
children | 5f17cb93ff66 |
comparison
equal
deleted
inserted
replaced
220:42394fc6a535 | 221:79ff65ed7e25 |
---|---|
25 // implementation would not work with a driver that is not gcc-compatible. | 25 // implementation would not work with a driver that is not gcc-compatible. |
26 // | 26 // |
27 // First argument of the command line received from underlying compilation | 27 // First argument of the command line received from underlying compilation |
28 // database is used as compiler driver path. Due to this arbitrary binary | 28 // database is used as compiler driver path. Due to this arbitrary binary |
29 // execution, this mechanism is not used by default and only executes binaries | 29 // execution, this mechanism is not used by default and only executes binaries |
30 // in the paths that are explicitly whitelisted by the user. | 30 // in the paths that are explicitly included by the user. |
31 | 31 |
32 #include "GlobalCompilationDatabase.h" | 32 #include "GlobalCompilationDatabase.h" |
33 #include "support/Logger.h" | 33 #include "support/Logger.h" |
34 #include "support/Path.h" | 34 #include "support/Path.h" |
35 #include "support/Trace.h" | 35 #include "support/Trace.h" |
36 #include "clang/Basic/Diagnostic.h" | |
37 #include "clang/Basic/TargetInfo.h" | |
38 #include "clang/Basic/TargetOptions.h" | |
36 #include "clang/Driver/Types.h" | 39 #include "clang/Driver/Types.h" |
37 #include "clang/Tooling/CompilationDatabase.h" | 40 #include "clang/Tooling/CompilationDatabase.h" |
38 #include "llvm/ADT/DenseMap.h" | 41 #include "llvm/ADT/DenseMap.h" |
39 #include "llvm/ADT/ScopeExit.h" | 42 #include "llvm/ADT/ScopeExit.h" |
40 #include "llvm/ADT/SmallString.h" | 43 #include "llvm/ADT/SmallString.h" |
54 | 57 |
55 namespace clang { | 58 namespace clang { |
56 namespace clangd { | 59 namespace clangd { |
57 namespace { | 60 namespace { |
58 | 61 |
59 std::vector<std::string> parseDriverOutput(llvm::StringRef Output) { | 62 struct DriverInfo { |
60 std::vector<std::string> SystemIncludes; | 63 std::vector<std::string> SystemIncludes; |
64 std::string Target; | |
65 }; | |
66 | |
67 bool isValidTarget(llvm::StringRef Triple) { | |
68 std::shared_ptr<TargetOptions> TargetOpts(new TargetOptions); | |
69 TargetOpts->Triple = Triple.str(); | |
70 DiagnosticsEngine Diags(new DiagnosticIDs, new DiagnosticOptions, | |
71 new IgnoringDiagConsumer); | |
72 IntrusiveRefCntPtr<TargetInfo> Target = | |
73 TargetInfo::CreateTargetInfo(Diags, TargetOpts); | |
74 return bool(Target); | |
75 } | |
76 | |
77 llvm::Optional<DriverInfo> parseDriverOutput(llvm::StringRef Output) { | |
78 DriverInfo Info; | |
61 const char SIS[] = "#include <...> search starts here:"; | 79 const char SIS[] = "#include <...> search starts here:"; |
62 const char SIE[] = "End of search list."; | 80 const char SIE[] = "End of search list."; |
63 llvm::SmallVector<llvm::StringRef, 8> Lines; | 81 const char TS[] = "Target: "; |
82 llvm::SmallVector<llvm::StringRef> Lines; | |
64 Output.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); | 83 Output.split(Lines, '\n', /*MaxSplit=*/-1, /*KeepEmpty=*/false); |
65 | 84 |
66 auto StartIt = llvm::find_if( | 85 enum { |
67 Lines, [SIS](llvm::StringRef Line) { return Line.trim() == SIS; }); | 86 Initial, // Initial state: searching for target or includes list. |
68 if (StartIt == Lines.end()) { | 87 IncludesExtracting, // Includes extracting. |
88 Done // Includes and target extraction done. | |
89 } State = Initial; | |
90 bool SeenIncludes = false; | |
91 bool SeenTarget = false; | |
92 for (auto *It = Lines.begin(); State != Done && It != Lines.end(); ++It) { | |
93 auto Line = *It; | |
94 switch (State) { | |
95 case Initial: | |
96 if (!SeenIncludes && Line.trim() == SIS) { | |
97 SeenIncludes = true; | |
98 State = IncludesExtracting; | |
99 } else if (!SeenTarget && Line.trim().startswith(TS)) { | |
100 SeenTarget = true; | |
101 llvm::StringRef TargetLine = Line.trim(); | |
102 TargetLine.consume_front(TS); | |
103 // Only detect targets that clang understands | |
104 if (!isValidTarget(TargetLine)) { | |
105 elog("System include extraction: invalid target \"{0}\", ignoring", | |
106 TargetLine); | |
107 } else { | |
108 Info.Target = TargetLine.str(); | |
109 vlog("System include extraction: target extracted: \"{0}\"", | |
110 TargetLine); | |
111 } | |
112 } | |
113 break; | |
114 case IncludesExtracting: | |
115 if (Line.trim() == SIE) { | |
116 State = SeenTarget ? Done : Initial; | |
117 } else { | |
118 Info.SystemIncludes.push_back(Line.trim().str()); | |
119 vlog("System include extraction: adding {0}", Line); | |
120 } | |
121 break; | |
122 default: | |
123 llvm_unreachable("Impossible state of the driver output parser"); | |
124 break; | |
125 } | |
126 } | |
127 if (!SeenIncludes) { | |
69 elog("System include extraction: start marker not found: {0}", Output); | 128 elog("System include extraction: start marker not found: {0}", Output); |
70 return {}; | 129 return llvm::None; |
71 } | 130 } |
72 ++StartIt; | 131 if (State == IncludesExtracting) { |
73 const auto EndIt = | |
74 llvm::find_if(llvm::make_range(StartIt, Lines.end()), | |
75 [SIE](llvm::StringRef Line) { return Line.trim() == SIE; }); | |
76 if (EndIt == Lines.end()) { | |
77 elog("System include extraction: end marker missing: {0}", Output); | 132 elog("System include extraction: end marker missing: {0}", Output); |
78 return {}; | 133 return llvm::None; |
79 } | 134 } |
80 | 135 return std::move(Info); |
81 for (llvm::StringRef Line : llvm::make_range(StartIt, EndIt)) { | 136 } |
82 SystemIncludes.push_back(Line.trim().str()); | 137 |
83 vlog("System include extraction: adding {0}", Line); | 138 llvm::Optional<DriverInfo> |
84 } | 139 extractSystemIncludesAndTarget(llvm::SmallString<128> Driver, |
85 return SystemIncludes; | 140 llvm::StringRef Lang, |
86 } | 141 llvm::ArrayRef<std::string> CommandLine, |
87 | 142 const llvm::Regex &QueryDriverRegex) { |
88 std::vector<std::string> | 143 trace::Span Tracer("Extract system includes and target"); |
89 extractSystemIncludes(PathRef Driver, llvm::StringRef Lang, | 144 |
90 llvm::ArrayRef<std::string> CommandLine, | 145 if (!llvm::sys::path::is_absolute(Driver)) { |
91 llvm::Regex &QueryDriverRegex) { | 146 assert(llvm::none_of( |
92 trace::Span Tracer("Extract system includes"); | 147 Driver, [](char C) { return llvm::sys::path::is_separator(C); })); |
148 auto DriverProgram = llvm::sys::findProgramByName(Driver); | |
149 if (DriverProgram) { | |
150 vlog("System include extraction: driver {0} expanded to {1}", Driver, | |
151 *DriverProgram); | |
152 Driver = *DriverProgram; | |
153 } else { | |
154 elog("System include extraction: driver {0} not found in PATH", Driver); | |
155 return llvm::None; | |
156 } | |
157 } | |
158 | |
93 SPAN_ATTACH(Tracer, "driver", Driver); | 159 SPAN_ATTACH(Tracer, "driver", Driver); |
94 SPAN_ATTACH(Tracer, "lang", Lang); | 160 SPAN_ATTACH(Tracer, "lang", Lang); |
95 | 161 |
96 if (!QueryDriverRegex.match(Driver)) { | 162 if (!QueryDriverRegex.match(Driver)) { |
97 vlog("System include extraction: not whitelisted driver {0}", Driver); | 163 vlog("System include extraction: not allowed driver {0}", Driver); |
98 return {}; | 164 return llvm::None; |
99 } | |
100 | |
101 if (!llvm::sys::fs::exists(Driver)) { | |
102 elog("System include extraction: {0} does not exist.", Driver); | |
103 return {}; | |
104 } | |
105 if (!llvm::sys::fs::can_execute(Driver)) { | |
106 elog("System include extraction: {0} is not executable.", Driver); | |
107 return {}; | |
108 } | 165 } |
109 | 166 |
110 llvm::SmallString<128> StdErrPath; | 167 llvm::SmallString<128> StdErrPath; |
111 if (auto EC = llvm::sys::fs::createTemporaryFile("system-includes", "clangd", | 168 if (auto EC = llvm::sys::fs::createTemporaryFile("system-includes", "clangd", |
112 StdErrPath)) { | 169 StdErrPath)) { |
113 elog("System include extraction: failed to create temporary file with " | 170 elog("System include extraction: failed to create temporary file with " |
114 "error {0}", | 171 "error {0}", |
115 EC.message()); | 172 EC.message()); |
116 return {}; | 173 return llvm::None; |
117 } | 174 } |
118 auto CleanUp = llvm::make_scope_exit( | 175 auto CleanUp = llvm::make_scope_exit( |
119 [&StdErrPath]() { llvm::sys::fs::remove(StdErrPath); }); | 176 [&StdErrPath]() { llvm::sys::fs::remove(StdErrPath); }); |
120 | 177 |
121 llvm::Optional<llvm::StringRef> Redirects[] = { | 178 llvm::Optional<llvm::StringRef> Redirects[] = { |
122 {""}, {""}, llvm::StringRef(StdErrPath)}; | 179 {""}, {""}, llvm::StringRef(StdErrPath)}; |
123 | 180 |
124 llvm::SmallVector<llvm::StringRef, 12> Args = {Driver, "-E", "-x", | 181 llvm::SmallVector<llvm::StringRef> Args = {Driver, "-E", "-x", |
125 Lang, "-", "-v"}; | 182 Lang, "-", "-v"}; |
126 | 183 |
127 // These flags will be preserved | 184 // These flags will be preserved |
128 const llvm::StringRef FlagsToPreserve[] = { | 185 const llvm::StringRef FlagsToPreserve[] = { |
129 "-nostdinc", "--no-standard-includes", "-nostdinc++", "-nobuiltininc"}; | 186 "-nostdinc", "--no-standard-includes", "-nostdinc++", "-nobuiltininc"}; |
130 // Preserves these flags and their values, either as separate args or with an | 187 // Preserves these flags and their values, either as separate args or with an |
141 llvm::find_if(ArgsToPreserve, [&Arg](llvm::StringRef S) { | 198 llvm::find_if(ArgsToPreserve, [&Arg](llvm::StringRef S) { |
142 return Arg.startswith(S); | 199 return Arg.startswith(S); |
143 }); | 200 }); |
144 if (Found == std::end(ArgsToPreserve)) | 201 if (Found == std::end(ArgsToPreserve)) |
145 continue; | 202 continue; |
146 Arg.consume_front(*Found); | 203 Arg = Arg.drop_front(Found->size()); |
147 if (Arg.empty() && I + 1 < E) { | 204 if (Arg.empty() && I + 1 < E) { |
148 Args.push_back(CommandLine[I]); | 205 Args.push_back(CommandLine[I]); |
149 Args.push_back(CommandLine[++I]); | 206 Args.push_back(CommandLine[++I]); |
150 } else if (Arg.startswith("=")) { | 207 } else if (Arg.startswith("=")) { |
151 Args.push_back(CommandLine[I]); | 208 Args.push_back(CommandLine[I]); |
152 } | 209 } |
153 } | 210 } |
154 } | 211 } |
155 | 212 |
213 std::string ErrMsg; | |
156 if (int RC = llvm::sys::ExecuteAndWait(Driver, Args, /*Env=*/llvm::None, | 214 if (int RC = llvm::sys::ExecuteAndWait(Driver, Args, /*Env=*/llvm::None, |
157 Redirects)) { | 215 Redirects, /*SecondsToWait=*/0, |
216 /*MemoryLimit=*/0, &ErrMsg)) { | |
158 elog("System include extraction: driver execution failed with return code: " | 217 elog("System include extraction: driver execution failed with return code: " |
159 "{0}. Args: ['{1}']", | 218 "{0} - '{1}'. Args: [{2}]", |
160 llvm::to_string(RC), llvm::join(Args, "', '")); | 219 llvm::to_string(RC), ErrMsg, printArgv(Args)); |
161 return {}; | 220 return llvm::None; |
162 } | 221 } |
163 | 222 |
164 auto BufOrError = llvm::MemoryBuffer::getFile(StdErrPath); | 223 auto BufOrError = llvm::MemoryBuffer::getFile(StdErrPath); |
165 if (!BufOrError) { | 224 if (!BufOrError) { |
166 elog("System include extraction: failed to read {0} with error {1}", | 225 elog("System include extraction: failed to read {0} with error {1}", |
167 StdErrPath, BufOrError.getError().message()); | 226 StdErrPath, BufOrError.getError().message()); |
168 return {}; | 227 return llvm::None; |
169 } | 228 } |
170 | 229 |
171 auto Includes = parseDriverOutput(BufOrError->get()->getBuffer()); | 230 llvm::Optional<DriverInfo> Info = |
172 log("System include extractor: successfully executed {0}, got includes: " | 231 parseDriverOutput(BufOrError->get()->getBuffer()); |
173 "\"{1}\"", | 232 if (!Info) |
174 Driver, llvm::join(Includes, ", ")); | 233 return llvm::None; |
175 return Includes; | 234 log("System includes extractor: successfully executed {0}\n\tgot includes: " |
235 "\"{1}\"\n\tgot target: \"{2}\"", | |
236 Driver, llvm::join(Info->SystemIncludes, ", "), Info->Target); | |
237 return Info; | |
176 } | 238 } |
177 | 239 |
178 tooling::CompileCommand & | 240 tooling::CompileCommand & |
179 addSystemIncludes(tooling::CompileCommand &Cmd, | 241 addSystemIncludes(tooling::CompileCommand &Cmd, |
180 llvm::ArrayRef<std::string> SystemIncludes) { | 242 llvm::ArrayRef<std::string> SystemIncludes) { |
181 for (llvm::StringRef Include : SystemIncludes) { | 243 for (llvm::StringRef Include : SystemIncludes) { |
182 // FIXME(kadircet): This doesn't work when we have "--driver-mode=cl" | 244 // FIXME(kadircet): This doesn't work when we have "--driver-mode=cl" |
183 Cmd.CommandLine.push_back("-isystem"); | 245 Cmd.CommandLine.push_back("-isystem"); |
184 Cmd.CommandLine.push_back(Include.str()); | 246 Cmd.CommandLine.push_back(Include.str()); |
247 } | |
248 return Cmd; | |
249 } | |
250 | |
251 tooling::CompileCommand &setTarget(tooling::CompileCommand &Cmd, | |
252 const std::string &Target) { | |
253 if (!Target.empty()) { | |
254 // We do not want to override existing target with extracted one. | |
255 for (llvm::StringRef Arg : Cmd.CommandLine) { | |
256 if (Arg == "-target" || Arg.startswith("--target=")) | |
257 return Cmd; | |
258 } | |
259 Cmd.CommandLine.push_back("--target=" + Target); | |
185 } | 260 } |
186 return Cmd; | 261 return Cmd; |
187 } | 262 } |
188 | 263 |
189 /// Converts a glob containing only ** or * into a regex. | 264 /// Converts a glob containing only ** or * into a regex. |
226 } | 301 } |
227 | 302 |
228 /// Extracts system includes from a trusted driver by parsing the output of | 303 /// Extracts system includes from a trusted driver by parsing the output of |
229 /// include search path and appends them to the commands coming from underlying | 304 /// include search path and appends them to the commands coming from underlying |
230 /// compilation database. | 305 /// compilation database. |
231 class QueryDriverDatabase : public GlobalCompilationDatabase { | 306 class QueryDriverDatabase : public DelegatingCDB { |
232 public: | 307 public: |
233 QueryDriverDatabase(llvm::ArrayRef<std::string> QueryDriverGlobs, | 308 QueryDriverDatabase(llvm::ArrayRef<std::string> QueryDriverGlobs, |
234 std::unique_ptr<GlobalCompilationDatabase> Base) | 309 std::unique_ptr<GlobalCompilationDatabase> Base) |
235 : QueryDriverRegex(convertGlobsToRegex(QueryDriverGlobs)), | 310 : DelegatingCDB(std::move(Base)), |
236 Base(std::move(Base)) { | 311 QueryDriverRegex(convertGlobsToRegex(QueryDriverGlobs)) {} |
237 assert(this->Base); | |
238 BaseChanged = | |
239 this->Base->watch([this](const std::vector<std::string> &Changes) { | |
240 OnCommandChanged.broadcast(Changes); | |
241 }); | |
242 } | |
243 | 312 |
244 llvm::Optional<tooling::CompileCommand> | 313 llvm::Optional<tooling::CompileCommand> |
245 getCompileCommand(PathRef File) const override { | 314 getCompileCommand(PathRef File) const override { |
246 auto Cmd = Base->getCompileCommand(File); | 315 auto Cmd = DelegatingCDB::getCompileCommand(File); |
247 if (!Cmd || Cmd->CommandLine.empty()) | 316 if (!Cmd || Cmd->CommandLine.empty()) |
248 return Cmd; | 317 return Cmd; |
249 | 318 |
250 llvm::StringRef Lang; | 319 llvm::StringRef Lang; |
251 for (size_t I = 0, E = Cmd->CommandLine.size(); I < E; ++I) { | 320 for (size_t I = 0, E = Cmd->CommandLine.size(); I < E; ++I) { |
264 } | 333 } |
265 Lang = driver::types::getTypeName(Type); | 334 Lang = driver::types::getTypeName(Type); |
266 } | 335 } |
267 | 336 |
268 llvm::SmallString<128> Driver(Cmd->CommandLine.front()); | 337 llvm::SmallString<128> Driver(Cmd->CommandLine.front()); |
269 llvm::sys::fs::make_absolute(Cmd->Directory, Driver); | 338 if (llvm::any_of(Driver, |
270 auto Key = std::make_pair(Driver.str().str(), Lang.str()); | 339 [](char C) { return llvm::sys::path::is_separator(C); })) |
271 | 340 // Driver is a not a single executable name but instead a path (either |
272 std::vector<std::string> SystemIncludes; | 341 // relative or absolute). |
273 { | 342 llvm::sys::fs::make_absolute(Cmd->Directory, Driver); |
274 std::lock_guard<std::mutex> Lock(Mu); | 343 |
275 | 344 if (auto Info = |
276 auto It = DriverToIncludesCache.find(Key); | 345 QueriedDrivers.get(/*Key=*/(Driver + ":" + Lang).str(), [&] { |
277 if (It != DriverToIncludesCache.end()) | 346 return extractSystemIncludesAndTarget( |
278 SystemIncludes = It->second; | 347 Driver, Lang, Cmd->CommandLine, QueryDriverRegex); |
279 else | 348 })) { |
280 DriverToIncludesCache[Key] = SystemIncludes = extractSystemIncludes( | 349 setTarget(addSystemIncludes(*Cmd, Info->SystemIncludes), Info->Target); |
281 Key.first, Key.second, Cmd->CommandLine, QueryDriverRegex); | 350 } |
282 } | 351 return Cmd; |
283 | |
284 return addSystemIncludes(*Cmd, SystemIncludes); | |
285 } | |
286 | |
287 llvm::Optional<ProjectInfo> getProjectInfo(PathRef File) const override { | |
288 return Base->getProjectInfo(File); | |
289 } | 352 } |
290 | 353 |
291 private: | 354 private: |
292 mutable std::mutex Mu; | 355 // Caches includes extracted from a driver. Key is driver:lang. |
293 // Caches includes extracted from a driver. | 356 Memoize<llvm::StringMap<llvm::Optional<DriverInfo>>> QueriedDrivers; |
294 mutable std::map<std::pair<std::string, std::string>, | 357 llvm::Regex QueryDriverRegex; |
295 std::vector<std::string>> | |
296 DriverToIncludesCache; | |
297 mutable llvm::Regex QueryDriverRegex; | |
298 | |
299 std::unique_ptr<GlobalCompilationDatabase> Base; | |
300 CommandChanged::Subscription BaseChanged; | |
301 }; | 358 }; |
302 } // namespace | 359 } // namespace |
303 | 360 |
304 std::unique_ptr<GlobalCompilationDatabase> | 361 std::unique_ptr<GlobalCompilationDatabase> |
305 getQueryDriverDatabase(llvm::ArrayRef<std::string> QueryDriverGlobs, | 362 getQueryDriverDatabase(llvm::ArrayRef<std::string> QueryDriverGlobs, |