36 #include "clang/Driver/Types.h"
37 #include "clang/Tooling/CompilationDatabase.h"
38 #include "llvm/ADT/DenseMap.h"
39 #include "llvm/ADT/ScopeExit.h"
40 #include "llvm/ADT/SmallString.h"
41 #include "llvm/ADT/StringExtras.h"
42 #include "llvm/ADT/StringRef.h"
43 #include "llvm/ADT/iterator_range.h"
44 #include "llvm/Support/FileSystem.h"
45 #include "llvm/Support/MemoryBuffer.h"
46 #include "llvm/Support/Path.h"
47 #include "llvm/Support/Program.h"
48 #include "llvm/Support/Regex.h"
49 #include "llvm/Support/ScopedPrinter.h"
59 std::vector<std::string> parseDriverOutput(llvm::StringRef
Output) {
60 std::vector<std::string> SystemIncludes;
61 const char SIS[] =
"#include <...> search starts here:";
62 const char SIE[] =
"End of search list.";
63 llvm::SmallVector<llvm::StringRef, 8>
Lines;
66 auto StartIt = llvm::find_if(
67 Lines, [SIS](llvm::StringRef
Line) {
return Line.trim() == SIS; });
68 if (StartIt ==
Lines.end()) {
69 elog(
"System include extraction: start marker not found: {0}",
Output);
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);
81 for (llvm::StringRef
Line : llvm::make_range(StartIt, EndIt)) {
82 SystemIncludes.push_back(
Line.trim().str());
83 vlog(
"System include extraction: adding {0}",
Line);
85 return SystemIncludes;
88 std::vector<std::string>
89 extractSystemIncludes(
PathRef Driver, llvm::StringRef Lang,
91 const llvm::Regex &QueryDriverRegex) {
92 trace::Span
Tracer(
"Extract system includes");
96 if (!QueryDriverRegex.match(Driver)) {
97 vlog(
"System include extraction: not allowed driver {0}", Driver);
101 if (!llvm::sys::fs::exists(Driver)) {
102 elog(
"System include extraction: {0} does not exist.", Driver);
105 if (!llvm::sys::fs::can_execute(Driver)) {
106 elog(
"System include extraction: {0} is not executable.", Driver);
110 llvm::SmallString<128> StdErrPath;
111 if (
auto EC = llvm::sys::fs::createTemporaryFile(
"system-includes",
"clangd",
113 elog(
"System include extraction: failed to create temporary file with "
118 auto CleanUp = llvm::make_scope_exit(
119 [&StdErrPath]() { llvm::sys::fs::remove(StdErrPath); });
121 llvm::Optional<llvm::StringRef> Redirects[] = {
122 {
""}, {
""}, llvm::StringRef(StdErrPath)};
124 llvm::SmallVector<llvm::StringRef, 12> Args = {Driver,
"-E",
"-x",
128 const llvm::StringRef FlagsToPreserve[] = {
129 "-nostdinc",
"--no-standard-includes",
"-nostdinc++",
"-nobuiltininc"};
132 const llvm::StringRef ArgsToPreserve[] = {
"--sysroot",
"-isysroot"};
136 if (llvm::any_of(FlagsToPreserve,
137 [&Arg](llvm::StringRef S) {
return S == Arg; })) {
141 llvm::find_if(ArgsToPreserve, [&Arg](llvm::StringRef S) {
142 return Arg.startswith(S);
144 if (Found == std::end(ArgsToPreserve))
146 Arg = Arg.drop_front(Found->size());
147 if (Arg.empty() && I + 1 <
E) {
150 }
else if (Arg.startswith(
"=")) {
156 if (
int RC = llvm::sys::ExecuteAndWait(Driver, Args,
llvm::None,
158 elog(
"System include extraction: driver execution failed with return code: "
159 "{0}. Args: ['{1}']",
160 llvm::to_string(RC),
llvm::join(Args,
"', '"));
164 auto BufOrError = llvm::MemoryBuffer::getFile(StdErrPath);
166 elog(
"System include extraction: failed to read {0} with error {1}",
167 StdErrPath, BufOrError.getError().message());
171 auto Includes = parseDriverOutput(BufOrError->get()->getBuffer());
172 log(
"System include extractor: successfully executed {0}, got includes: "
178 tooling::CompileCommand &
179 addSystemIncludes(tooling::CompileCommand &Cmd,
180 llvm::ArrayRef<std::string> SystemIncludes) {
181 for (llvm::StringRef Include : SystemIncludes) {
183 Cmd.CommandLine.push_back(
"-isystem");
184 Cmd.CommandLine.push_back(Include.str());
190 std::string convertGlobToRegex(llvm::StringRef Glob) {
192 llvm::raw_string_ostream RegStream(RegText);
194 for (
size_t I = 0,
E = Glob.size(); I <
E; ++I) {
195 if (Glob[I] ==
'*') {
196 if (I + 1 <
E && Glob[I + 1] ==
'*') {
203 RegStream <<
"[^/]*";
206 RegStream << llvm::Regex::escape(Glob.substr(I, 1));
215 llvm::Regex convertGlobsToRegex(llvm::ArrayRef<std::string> Globs) {
216 assert(!Globs.empty() &&
"Globs cannot be empty!");
217 std::vector<std::string> RegTexts;
218 RegTexts.reserve(Globs.size());
219 for (llvm::StringRef Glob : Globs)
220 RegTexts.push_back(convertGlobToRegex(Glob));
223 assert(Reg.isValid(RegTexts.front()) &&
224 "Created an invalid regex from globs");
231 class QueryDriverDatabase :
public GlobalCompilationDatabase {
233 QueryDriverDatabase(llvm::ArrayRef<std::string> QueryDriverGlobs,
234 std::unique_ptr<GlobalCompilationDatabase>
Base)
235 : QueryDriverRegex(convertGlobsToRegex(QueryDriverGlobs)),
239 this->Base->watch([
this](
const std::vector<std::string> &
Changes) {
240 OnCommandChanged.broadcast(
Changes);
244 llvm::Optional<tooling::CompileCommand>
246 auto Cmd =
Base->getCompileCommand(
File);
247 if (!Cmd || Cmd->CommandLine.empty())
250 llvm::StringRef Lang;
251 for (
size_t I = 0,
E = Cmd->CommandLine.size(); I <
E; ++I) {
252 llvm::StringRef Arg = Cmd->CommandLine[I];
253 if (Arg ==
"-x" && I + 1 <
E)
254 Lang = Cmd->CommandLine[I + 1];
255 else if (Arg.startswith(
"-x"))
256 Lang = Arg.drop_front(2).trim();
259 llvm::StringRef Ext = llvm::sys::path::extension(
File).trim(
'.');
260 auto Type = driver::types::lookupTypeForExtension(Ext);
261 if (
Type == driver::types::TY_INVALID) {
262 elog(
"System include extraction: invalid file type for {0}", Ext);
265 Lang = driver::types::getTypeName(
Type);
268 llvm::SmallString<128> Driver(Cmd->CommandLine.front());
271 std::vector<std::string> SystemIncludes =
272 QueriedDrivers.get((Driver +
":" + Lang).str(), [&] {
273 return extractSystemIncludes(Driver, Lang, Cmd->CommandLine,
277 return addSystemIncludes(*Cmd, SystemIncludes);
280 llvm::Optional<ProjectInfo> getProjectInfo(
PathRef File)
const override {
286 Memoize<llvm::StringMap<std::vector<std::string>>> QueriedDrivers;
287 llvm::Regex QueryDriverRegex;
289 std::unique_ptr<GlobalCompilationDatabase>
Base;
290 CommandChanged::Subscription BaseChanged;
294 std::unique_ptr<GlobalCompilationDatabase>
296 std::unique_ptr<GlobalCompilationDatabase>
Base) {
297 assert(
Base &&
"Null base to SystemIncludeExtractor");
298 if (QueryDriverGlobs.empty())
300 return std::make_unique<QueryDriverDatabase>(QueryDriverGlobs,