13 #include "clang/Frontend/CompilerInvocation.h"
14 #include "clang/Tooling/ArgumentsAdjusters.h"
15 #include "clang/Tooling/CompilationDatabase.h"
16 #include "llvm/ADT/None.h"
17 #include "llvm/ADT/Optional.h"
18 #include "llvm/ADT/STLExtras.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/Support/FileSystem.h"
21 #include "llvm/Support/FileUtilities.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/Support/Program.h"
38 Path = llvm::sys::path::parent_path(
Path))
44 tooling::CompileCommand
46 std::vector<std::string> Argv = {
"clang"};
50 auto FileExtension = llvm::sys::path::extension(
File);
51 if (FileExtension.empty() || FileExtension ==
".h")
52 Argv.push_back(
"-xobjective-c++-header");
53 Argv.push_back(std::string(
File));
54 tooling::CompileCommand Cmd(llvm::sys::path::parent_path(
File),
55 llvm::sys::path::filename(
File), std::move(Argv),
57 Cmd.Heuristic =
"clangd fallback";
63 llvm::Optional<Path> CompileCommandsDir)
64 : CompileCommandsDir(std::move(CompileCommandsDir)) {}
69 llvm::Optional<tooling::CompileCommand>
73 Req.ShouldBroadcast =
true;
75 auto Res = lookupCDB(Req);
77 log(
"Failed to find compilation database for {0}",
File);
81 auto Candidates = Res->CDB->getCompileCommands(
File);
82 if (!Candidates.empty())
83 return std::move(Candidates.front());
95 #if defined(_WIN32) || defined(__APPLE__)
98 return std::string(
Path);
103 #if defined(_WIN32) || defined(__APPLE__)
104 return A.equals_lower(B);
110 DirectoryBasedGlobalCompilationDatabase::CachedCDB &
111 DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(
PathRef Dir)
const {
114 auto R = CompilationDatabases.try_emplace(
Key);
116 CachedCDB &
Entry = R.first->second;
118 Entry.Path = std::string(Dir);
119 Entry.CDB = tooling::CompilationDatabase::loadFromDirectory(Dir,
Error);
122 if (!CompileCommandsDir && !
Entry.CDB) {
123 llvm::SmallString<256> BuildDir = Dir;
124 llvm::sys::path::append(BuildDir,
"build");
125 if (llvm::sys::fs::is_directory(BuildDir)) {
126 vlog(
"Found candidate build directory {0}", BuildDir);
128 tooling::CompilationDatabase::loadFromDirectory(BuildDir,
Error);
132 log(
"Loaded compilation database from {0}", Dir);
134 return R.first->second;
137 llvm::Optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult>
138 DirectoryBasedGlobalCompilationDatabase::lookupCDB(
139 CDBLookupRequest Request)
const {
140 assert(llvm::sys::path::is_absolute(Request.FileName) &&
141 "path must be absolute");
143 bool ShouldBroadcast =
false;
144 CDBLookupResult Result;
147 std::lock_guard<std::mutex> Lock(Mutex);
148 CachedCDB *
Entry =
nullptr;
149 if (CompileCommandsDir) {
150 Entry = &getCDBInDirLocked(*CompileCommandsDir);
155 actOnAllParentDirectories(
removeDots(Request.FileName),
157 Entry = &getCDBInDirLocked(Path);
158 return Entry->CDB != nullptr;
166 if (Request.ShouldBroadcast && !
Entry->SentBroadcast) {
167 Entry->SentBroadcast =
true;
168 ShouldBroadcast =
true;
171 Result.CDB =
Entry->CDB.get();
172 Result.PI.SourceRoot =
Entry->Path;
178 broadcastCDB(Result);
182 void DirectoryBasedGlobalCompilationDatabase::broadcastCDB(
183 CDBLookupResult Result)
const {
184 assert(Result.CDB &&
"Trying to broadcast an invalid CDB!");
186 std::vector<std::string> AllFiles = Result.CDB->getAllFiles();
189 if (CompileCommandsDir) {
190 assert(*CompileCommandsDir == Result.PI.SourceRoot &&
191 "Trying to broadcast a CDB outside of CompileCommandsDir!");
196 llvm::StringMap<bool> DirectoryHasCDB;
198 std::lock_guard<std::mutex> Lock(Mutex);
200 for (llvm::StringRef
File : AllFiles) {
202 auto It = DirectoryHasCDB.try_emplace(
Path);
207 CachedCDB &
Entry = getCDBInDirLocked(
Path);
208 It.first->second =
Entry.CDB !=
nullptr;
214 std::vector<std::string> GovernedFiles;
215 for (llvm::StringRef
File : AllFiles) {
219 if (DirectoryHasCDB.lookup(
Path)) {
233 llvm::Optional<ProjectInfo>
235 CDBLookupRequest Req;
237 Req.ShouldBroadcast =
false;
238 auto Res = lookupCDB(Req);
245 std::vector<std::string> FallbackFlags,
246 tooling::ArgumentsAdjuster Adjuster)
247 :
Base(
Base), ArgsAdjuster(std::move(Adjuster)),
248 FallbackFlags(std::move(FallbackFlags)) {
250 BaseChanged = Base->watch([
this](
const std::vector<std::string>
Changes) {
255 llvm::Optional<tooling::CompileCommand>
257 llvm::Optional<tooling::CompileCommand> Cmd;
259 std::lock_guard<std::mutex> Lock(Mutex);
261 if (It != Commands.end())
269 Cmd->CommandLine = ArgsAdjuster(Cmd->CommandLine, Cmd->Filename);
276 std::lock_guard<std::mutex> Lock(Mutex);
277 Cmd.CommandLine.insert(Cmd.CommandLine.end(), FallbackFlags.begin(),
278 FallbackFlags.end());
280 Cmd.CommandLine = ArgsAdjuster(Cmd.CommandLine, Cmd.Filename);
285 PathRef File, llvm::Optional<tooling::CompileCommand> Cmd) {
291 std::unique_lock<std::mutex> Lock(Mutex);
293 Commands[CanonPath] = std::move(*Cmd);
295 Commands.erase(CanonPath);