1 //===--- GlobalCompilationDatabase.cpp ---------------------------*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "GlobalCompilationDatabase.h" 10 #include "FS.h" 11 #include "SourceCode.h" 12 #include "support/Logger.h" 13 #include "support/Path.h" 14 #include "support/ThreadsafeFS.h" 15 #include "clang/Frontend/CompilerInvocation.h" 16 #include "clang/Tooling/ArgumentsAdjusters.h" 17 #include "clang/Tooling/CompilationDatabase.h" 18 #include "clang/Tooling/CompilationDatabasePluginRegistry.h" 19 #include "clang/Tooling/JSONCompilationDatabase.h" 20 #include "llvm/ADT/None.h" 21 #include "llvm/ADT/Optional.h" 22 #include "llvm/ADT/STLExtras.h" 23 #include "llvm/ADT/ScopeExit.h" 24 #include "llvm/ADT/SmallString.h" 25 #include "llvm/Support/FileSystem.h" 26 #include "llvm/Support/FileUtilities.h" 27 #include "llvm/Support/Path.h" 28 #include "llvm/Support/Program.h" 29 #include "llvm/Support/VirtualFileSystem.h" 30 #include <chrono> 31 #include <string> 32 #include <tuple> 33 #include <vector> 34 35 namespace clang { 36 namespace clangd { 37 namespace { 38 39 // Variant of parent_path that operates only on absolute paths. 40 PathRef absoluteParent(PathRef Path) { 41 assert(llvm::sys::path::is_absolute(Path)); 42 #if defined(_WIN32) 43 // llvm::sys says "C:\" is absolute, and its parent is "C:" which is relative. 44 // This unhelpful behavior seems to have been inherited from boost. 45 if (llvm::sys::path::relative_path(Path).empty()) { 46 return PathRef(); 47 } 48 #endif 49 PathRef Result = llvm::sys::path::parent_path(Path); 50 assert(Result.empty() || llvm::sys::path::is_absolute(Result)); 51 return Result; 52 } 53 54 // Runs the given action on all parent directories of filename, starting from 55 // deepest directory and going up to root. Stops whenever action succeeds. 56 void actOnAllParentDirectories(PathRef FileName, 57 llvm::function_ref<bool(PathRef)> Action) { 58 for (auto Path = absoluteParent(FileName); !Path.empty() && !Action(Path); 59 Path = absoluteParent(Path)) 60 ; 61 } 62 63 } // namespace 64 65 tooling::CompileCommand 66 GlobalCompilationDatabase::getFallbackCommand(PathRef File) const { 67 std::vector<std::string> Argv = {"clang"}; 68 // Clang treats .h files as C by default and files without extension as linker 69 // input, resulting in unhelpful diagnostics. 70 // Parsing as Objective C++ is friendly to more cases. 71 auto FileExtension = llvm::sys::path::extension(File); 72 if (FileExtension.empty() || FileExtension == ".h") 73 Argv.push_back("-xobjective-c++-header"); 74 Argv.push_back(std::string(File)); 75 tooling::CompileCommand Cmd(llvm::sys::path::parent_path(File), 76 llvm::sys::path::filename(File), std::move(Argv), 77 /*Output=*/""); 78 Cmd.Heuristic = "clangd fallback"; 79 return Cmd; 80 } 81 82 // Loads and caches the CDB from a single directory. 83 // 84 // This class is threadsafe, which is to say we have independent locks for each 85 // directory we're searching for a CDB. 86 // Loading is deferred until first access. 87 // 88 // The DirectoryBasedCDB keeps a map from path => DirectoryCache. 89 // Typical usage is to: 90 // - 1) determine all the paths that might be searched 91 // - 2) acquire the map lock and get-or-create all the DirectoryCache entries 92 // - 3) release the map lock and query the caches as desired 93 class DirectoryBasedGlobalCompilationDatabase::DirectoryCache { 94 using stopwatch = std::chrono::steady_clock; 95 96 // CachedFile is used to read a CDB file on disk (e.g. compile_commands.json). 97 // It specializes in being able to quickly bail out if the file is unchanged, 98 // which is the common case. 99 // Internally, it stores file metadata so a stat() can verify it's unchanged. 100 // We don't actually cache the content as it's not needed - if the file is 101 // unchanged then the previous CDB is valid. 102 struct CachedFile { 103 CachedFile(llvm::StringRef Parent, llvm::StringRef Rel) { 104 llvm::SmallString<256> Path = Parent; 105 llvm::sys::path::append(Path, Rel); 106 this->Path = Path.str().str(); 107 } 108 std::string Path; 109 size_t Size = NoFileCached; 110 llvm::sys::TimePoint<> ModifiedTime; 111 FileDigest ContentHash; 112 113 static constexpr size_t NoFileCached = -1; 114 115 struct LoadResult { 116 enum { 117 FileNotFound, 118 TransientError, 119 FoundSameData, 120 FoundNewData, 121 } Result; 122 std::unique_ptr<llvm::MemoryBuffer> Buffer; // Set only if FoundNewData 123 }; 124 125 LoadResult load(llvm::vfs::FileSystem &FS, bool HasOldData); 126 }; 127 128 // If we've looked for a CDB here and found none, the time when that happened. 129 // (Atomics make it possible for get() to return without taking a lock) 130 std::atomic<stopwatch::rep> NoCDBAt = { 131 stopwatch::time_point::min().time_since_epoch().count()}; 132 133 // Guards the following cache state. 134 std::mutex Mu; 135 // When was the cache last known to be in sync with disk state? 136 stopwatch::time_point CachePopulatedAt = stopwatch::time_point::min(); 137 // Whether a new CDB has been loaded but not broadcast yet. 138 bool NeedsBroadcast = false; 139 // Last loaded CDB, meaningful if CachePopulatedAt was ever set. 140 // shared_ptr so we can overwrite this when callers are still using the CDB. 141 std::shared_ptr<tooling::CompilationDatabase> CDB; 142 // File metadata for the CDB files we support tracking directly. 143 CachedFile CompileCommandsJson; 144 CachedFile BuildCompileCommandsJson; 145 CachedFile CompileFlagsTxt; 146 // CachedFile member corresponding to CDB. 147 // CDB | ACF | Scenario 148 // null | null | no CDB found, or initial empty cache 149 // set | null | CDB was loaded via generic plugin interface 150 // null | set | found known CDB file, but parsing it failed 151 // set | set | CDB was parsed from a known file 152 CachedFile *ActiveCachedFile = nullptr; 153 154 public: 155 DirectoryCache(llvm::StringRef Path) 156 : CompileCommandsJson(Path, "compile_commands.json"), 157 BuildCompileCommandsJson(Path, "build/compile_commands.json"), 158 CompileFlagsTxt(Path, "compile_flags.txt"), Path(Path) { 159 assert(llvm::sys::path::is_absolute(Path)); 160 } 161 162 // Absolute canonical path that we're the cache for. (Not case-folded). 163 const std::string Path; 164 165 // Get the CDB associated with this directory. 166 // ShouldBroadcast: 167 // - as input, signals whether the caller is willing to broadcast a 168 // newly-discovered CDB. (e.g. to trigger background indexing) 169 // - as output, signals whether the caller should do so. 170 // (If a new CDB is discovered and ShouldBroadcast is false, we mark the 171 // CDB as needing broadcast, and broadcast it next time we can). 172 std::shared_ptr<const tooling::CompilationDatabase> 173 get(const ThreadsafeFS &TFS, bool &ShouldBroadcast, 174 stopwatch::time_point FreshTime, stopwatch::time_point FreshTimeMissing) { 175 // Fast path for common case without taking lock. 176 if (stopwatch::time_point(stopwatch::duration(NoCDBAt.load())) > 177 FreshTimeMissing) { 178 ShouldBroadcast = false; 179 return nullptr; 180 } 181 182 std::lock_guard<std::mutex> Lock(Mu); 183 auto RequestBroadcast = llvm::make_scope_exit([&, OldCDB(CDB.get())] { 184 // If we loaded a new CDB, it should be broadcast at some point. 185 if (CDB != nullptr && CDB.get() != OldCDB) 186 NeedsBroadcast = true; 187 else if (CDB == nullptr) // nothing to broadcast anymore! 188 NeedsBroadcast = false; 189 // If we have something to broadcast, then do so iff allowed. 190 if (!ShouldBroadcast) 191 return; 192 ShouldBroadcast = NeedsBroadcast; 193 NeedsBroadcast = false; 194 }); 195 196 // If our cache is valid, serve from it. 197 if (CachePopulatedAt > FreshTime) 198 return CDB; 199 200 if (/*MayCache=*/load(*TFS.view(/*CWD=*/llvm::None))) { 201 // Use new timestamp, as loading may be slow. 202 CachePopulatedAt = stopwatch::now(); 203 NoCDBAt.store((CDB ? stopwatch::time_point::min() : CachePopulatedAt) 204 .time_since_epoch() 205 .count()); 206 } 207 208 return CDB; 209 } 210 211 private: 212 // Updates `CDB` from disk state. Returns false on failure. 213 bool load(llvm::vfs::FileSystem &FS); 214 }; 215 216 DirectoryBasedGlobalCompilationDatabase::DirectoryCache::CachedFile::LoadResult 217 DirectoryBasedGlobalCompilationDatabase::DirectoryCache::CachedFile::load( 218 llvm::vfs::FileSystem &FS, bool HasOldData) { 219 auto Stat = FS.status(Path); 220 if (!Stat || !Stat->isRegularFile()) { 221 Size = NoFileCached; 222 ContentHash = {}; 223 return {LoadResult::FileNotFound, nullptr}; 224 } 225 // If both the size and mtime match, presume unchanged without reading. 226 if (HasOldData && Stat->getLastModificationTime() == ModifiedTime && 227 Stat->getSize() == Size) 228 return {LoadResult::FoundSameData, nullptr}; 229 auto Buf = FS.getBufferForFile(Path); 230 if (!Buf || (*Buf)->getBufferSize() != Stat->getSize()) { 231 // Don't clear the cache - possible we're seeing inconsistent size as the 232 // file is being recreated. If it ends up identical later, great! 233 // 234 // This isn't a complete solution: if we see a partial file but stat/read 235 // agree on its size, we're ultimately going to have spurious CDB reloads. 236 // May be worth fixing if generators don't write atomically (CMake does). 237 elog("Failed to read {0}: {1}", Path, 238 Buf ? "size changed" : Buf.getError().message()); 239 return {LoadResult::TransientError, nullptr}; 240 } 241 242 FileDigest NewContentHash = digest((*Buf)->getBuffer()); 243 if (HasOldData && NewContentHash == ContentHash) { 244 // mtime changed but data is the same: avoid rebuilding the CDB. 245 ModifiedTime = Stat->getLastModificationTime(); 246 return {LoadResult::FoundSameData, nullptr}; 247 } 248 249 Size = (*Buf)->getBufferSize(); 250 ModifiedTime = Stat->getLastModificationTime(); 251 ContentHash = NewContentHash; 252 return {LoadResult::FoundNewData, std::move(*Buf)}; 253 } 254 255 // Adapt CDB-loading functions to a common interface for DirectoryCache::load(). 256 static std::unique_ptr<tooling::CompilationDatabase> 257 parseJSON(PathRef Path, llvm::StringRef Data, std::string &Error) { 258 if (auto CDB = tooling::JSONCompilationDatabase::loadFromBuffer( 259 Data, Error, tooling::JSONCommandLineSyntax::AutoDetect)) { 260 // FS used for expanding response files. 261 // FIXME: ExpandResponseFilesDatabase appears not to provide the usual 262 // thread-safety guarantees, as the access to FS is not locked! 263 // For now, use the real FS, which is known to be threadsafe (if we don't 264 // use/change working directory, which ExpandResponseFilesDatabase doesn't). 265 auto FS = llvm::vfs::getRealFileSystem(); 266 return tooling::inferTargetAndDriverMode( 267 tooling::inferMissingCompileCommands( 268 expandResponseFiles(std::move(CDB), std::move(FS)))); 269 } 270 return nullptr; 271 } 272 static std::unique_ptr<tooling::CompilationDatabase> 273 parseFixed(PathRef Path, llvm::StringRef Data, std::string &Error) { 274 return tooling::FixedCompilationDatabase::loadFromBuffer( 275 llvm::sys::path::parent_path(Path), Data, Error); 276 } 277 278 bool DirectoryBasedGlobalCompilationDatabase::DirectoryCache::load( 279 llvm::vfs::FileSystem &FS) { 280 dlog("Probing directory {0}", Path); 281 std::string Error; 282 283 // Load from the specially-supported compilation databases (JSON + Fixed). 284 // For these, we know the files they read and cache their metadata so we can 285 // cheaply validate whether they've changed, and hot-reload if they have. 286 // (As a bonus, these are also VFS-clean)! 287 struct CDBFile { 288 CachedFile *File; 289 // Wrapper for {Fixed,JSON}CompilationDatabase::loadFromBuffer. 290 llvm::function_ref<std::unique_ptr<tooling::CompilationDatabase>( 291 PathRef, 292 /*Data*/ llvm::StringRef, 293 /*ErrorMsg*/ std::string &)> 294 Parser; 295 }; 296 for (const auto &Entry : {CDBFile{&CompileCommandsJson, parseJSON}, 297 CDBFile{&BuildCompileCommandsJson, parseJSON}, 298 CDBFile{&CompileFlagsTxt, parseFixed}}) { 299 bool Active = ActiveCachedFile == Entry.File; 300 auto Loaded = Entry.File->load(FS, Active); 301 switch (Loaded.Result) { 302 case CachedFile::LoadResult::FileNotFound: 303 if (Active) { 304 log("Unloaded compilation database from {0}", Entry.File->Path); 305 ActiveCachedFile = nullptr; 306 CDB = nullptr; 307 } 308 // Continue looking at other candidates. 309 break; 310 case CachedFile::LoadResult::TransientError: 311 // File existed but we couldn't read it. Reuse the cache, retry later. 312 return false; // Load again next time. 313 case CachedFile::LoadResult::FoundSameData: 314 assert(Active && "CachedFile may not return 'same data' if !HasOldData"); 315 // This is the critical file, and it hasn't changed. 316 return true; 317 case CachedFile::LoadResult::FoundNewData: 318 // We have a new CDB! 319 CDB = Entry.Parser(Entry.File->Path, Loaded.Buffer->getBuffer(), Error); 320 if (CDB) 321 log("{0} compilation database from {1}", Active ? "Reloaded" : "Loaded", 322 Entry.File->Path); 323 else 324 elog("Failed to load compilation database from {0}: {1}", 325 Entry.File->Path, Error); 326 ActiveCachedFile = Entry.File; 327 return true; 328 } 329 } 330 331 // Fall back to generic handling of compilation databases. 332 // We don't know what files they read, so can't efficiently check whether 333 // they need to be reloaded. So we never do that. 334 // FIXME: the interface doesn't provide a way to virtualize FS access. 335 336 // Don't try these more than once. If we've scanned before, we're done. 337 if (CachePopulatedAt > stopwatch::time_point::min()) 338 return true; 339 for (const auto &Entry : 340 tooling::CompilationDatabasePluginRegistry::entries()) { 341 // Avoid duplicating the special cases handled above. 342 if (Entry.getName() == "fixed-compilation-database" || 343 Entry.getName() == "json-compilation-database") 344 continue; 345 auto Plugin = Entry.instantiate(); 346 if (auto CDB = Plugin->loadFromDirectory(Path, Error)) { 347 log("Loaded compilation database from {0} with plugin {1}", Path, 348 Entry.getName()); 349 this->CDB = std::move(CDB); 350 return true; 351 } 352 // Don't log Error here, it's usually just "couldn't find <file>". 353 } 354 dlog("No compilation database at {0}", Path); 355 return true; 356 } 357 358 DirectoryBasedGlobalCompilationDatabase:: 359 DirectoryBasedGlobalCompilationDatabase(const Options &Opts) 360 : Opts(Opts) { 361 if (Opts.CompileCommandsDir) 362 OnlyDirCache = std::make_unique<DirectoryCache>(*Opts.CompileCommandsDir); 363 } 364 365 DirectoryBasedGlobalCompilationDatabase:: 366 ~DirectoryBasedGlobalCompilationDatabase() = default; 367 368 llvm::Optional<tooling::CompileCommand> 369 DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const { 370 CDBLookupRequest Req; 371 Req.FileName = File; 372 Req.ShouldBroadcast = true; 373 auto Now = std::chrono::steady_clock::now(); 374 Req.FreshTime = Now - Opts.RevalidateAfter; 375 Req.FreshTimeMissing = Now - Opts.RevalidateMissingAfter; 376 377 auto Res = lookupCDB(Req); 378 if (!Res) { 379 log("Failed to find compilation database for {0}", File); 380 return llvm::None; 381 } 382 383 auto Candidates = Res->CDB->getCompileCommands(File); 384 if (!Candidates.empty()) 385 return std::move(Candidates.front()); 386 387 return None; 388 } 389 390 // For platforms where paths are case-insensitive (but case-preserving), 391 // we need to do case-insensitive comparisons and use lowercase keys. 392 // FIXME: Make Path a real class with desired semantics instead. 393 // This class is not the only place this problem exists. 394 // FIXME: Mac filesystems default to case-insensitive, but may be sensitive. 395 396 static std::string maybeCaseFoldPath(PathRef Path) { 397 #if defined(_WIN32) || defined(__APPLE__) 398 return Path.lower(); 399 #else 400 return std::string(Path); 401 #endif 402 } 403 404 static bool pathEqual(PathRef A, PathRef B) { 405 #if defined(_WIN32) || defined(__APPLE__) 406 return A.equals_lower(B); 407 #else 408 return A == B; 409 #endif 410 } 411 412 std::vector<DirectoryBasedGlobalCompilationDatabase::DirectoryCache *> 413 DirectoryBasedGlobalCompilationDatabase::getDirectoryCaches( 414 llvm::ArrayRef<llvm::StringRef> Dirs) const { 415 std::vector<std::string> FoldedDirs; 416 FoldedDirs.reserve(Dirs.size()); 417 for (const auto &Dir : Dirs) { 418 #ifndef NDEBUG 419 if (!llvm::sys::path::is_absolute(Dir)) 420 elog("Trying to cache CDB for relative {0}"); 421 #endif 422 FoldedDirs.push_back(maybeCaseFoldPath(Dir)); 423 } 424 425 std::vector<DirectoryCache *> Ret; 426 Ret.reserve(Dirs.size()); 427 428 std::lock_guard<std::mutex> Lock(DirCachesMutex); 429 for (unsigned I = 0; I < Dirs.size(); ++I) 430 Ret.push_back(&DirCaches.try_emplace(FoldedDirs[I], Dirs[I]).first->second); 431 return Ret; 432 } 433 434 llvm::Optional<DirectoryBasedGlobalCompilationDatabase::CDBLookupResult> 435 DirectoryBasedGlobalCompilationDatabase::lookupCDB( 436 CDBLookupRequest Request) const { 437 assert(llvm::sys::path::is_absolute(Request.FileName) && 438 "path must be absolute"); 439 440 bool ShouldBroadcast = false; 441 DirectoryCache *DirCache = nullptr; 442 std::shared_ptr<const tooling::CompilationDatabase> CDB = nullptr; 443 if (OnlyDirCache) { 444 DirCache = OnlyDirCache.get(); 445 ShouldBroadcast = Request.ShouldBroadcast; 446 CDB = DirCache->get(Opts.TFS, ShouldBroadcast, Request.FreshTime, 447 Request.FreshTimeMissing); 448 } else { 449 // Traverse the canonical version to prevent false positives. i.e.: 450 // src/build/../a.cc can detect a CDB in /src/build if not canonicalized. 451 std::string CanonicalPath = removeDots(Request.FileName); 452 std::vector<llvm::StringRef> SearchDirs; 453 actOnAllParentDirectories(CanonicalPath, [&](PathRef Path) { 454 SearchDirs.push_back(Path); 455 return false; 456 }); 457 for (DirectoryCache *Candidate : getDirectoryCaches(SearchDirs)) { 458 bool CandidateShouldBroadcast = Request.ShouldBroadcast; 459 if ((CDB = Candidate->get(Opts.TFS, CandidateShouldBroadcast, 460 Request.FreshTime, Request.FreshTimeMissing))) { 461 DirCache = Candidate; 462 ShouldBroadcast = CandidateShouldBroadcast; 463 break; 464 } 465 } 466 } 467 468 if (!CDB) 469 return llvm::None; 470 471 CDBLookupResult Result; 472 Result.CDB = std::move(CDB); 473 Result.PI.SourceRoot = DirCache->Path; 474 475 // FIXME: Maybe make the following part async, since this can block 476 // retrieval of compile commands. 477 if (ShouldBroadcast) 478 broadcastCDB(Result); 479 return Result; 480 } 481 482 void DirectoryBasedGlobalCompilationDatabase::broadcastCDB( 483 CDBLookupResult Result) const { 484 vlog("Broadcasting compilation database from {0}", Result.PI.SourceRoot); 485 assert(Result.CDB && "Trying to broadcast an invalid CDB!"); 486 487 std::vector<std::string> AllFiles = Result.CDB->getAllFiles(); 488 // We assume CDB in CompileCommandsDir owns all of its entries, since we don't 489 // perform any search in parent paths whenever it is set. 490 if (OnlyDirCache) { 491 assert(OnlyDirCache->Path == Result.PI.SourceRoot && 492 "Trying to broadcast a CDB outside of CompileCommandsDir!"); 493 OnCommandChanged.broadcast(std::move(AllFiles)); 494 return; 495 } 496 497 // Uniquify all parent directories of all files. 498 llvm::StringMap<bool> DirectoryHasCDB; 499 std::vector<llvm::StringRef> FileAncestors; 500 for (llvm::StringRef File : AllFiles) { 501 actOnAllParentDirectories(File, [&](PathRef Path) { 502 auto It = DirectoryHasCDB.try_emplace(Path); 503 // Already seen this path, and all of its parents. 504 if (!It.second) 505 return true; 506 507 FileAncestors.push_back(It.first->getKey()); 508 return pathEqual(Path, Result.PI.SourceRoot); 509 }); 510 } 511 // Work out which ones have CDBs in them. 512 // Given that we know that CDBs have been moved/generated, don't trust caches. 513 // (This should be rare, so it's OK to add a little latency). 514 constexpr auto IgnoreCache = std::chrono::steady_clock::time_point::max(); 515 auto DirectoryCaches = getDirectoryCaches(FileAncestors); 516 assert(DirectoryCaches.size() == FileAncestors.size()); 517 for (unsigned I = 0; I < DirectoryCaches.size(); ++I) { 518 bool ShouldBroadcast = false; 519 if (DirectoryCaches[I]->get(Opts.TFS, ShouldBroadcast, 520 /*FreshTime=*/IgnoreCache, 521 /*FreshTimeMissing=*/IgnoreCache)) 522 DirectoryHasCDB.find(FileAncestors[I])->setValue(true); 523 } 524 525 std::vector<std::string> GovernedFiles; 526 for (llvm::StringRef File : AllFiles) { 527 // A file is governed by this CDB if lookup for the file would find it. 528 // Independent of whether it has an entry for that file or not. 529 actOnAllParentDirectories(File, [&](PathRef Path) { 530 if (DirectoryHasCDB.lookup(Path)) { 531 if (pathEqual(Path, Result.PI.SourceRoot)) 532 // Make sure listeners always get a canonical path for the file. 533 GovernedFiles.push_back(removeDots(File)); 534 // Stop as soon as we hit a CDB. 535 return true; 536 } 537 return false; 538 }); 539 } 540 541 OnCommandChanged.broadcast(std::move(GovernedFiles)); 542 } 543 544 llvm::Optional<ProjectInfo> 545 DirectoryBasedGlobalCompilationDatabase::getProjectInfo(PathRef File) const { 546 CDBLookupRequest Req; 547 Req.FileName = File; 548 Req.ShouldBroadcast = false; 549 Req.FreshTime = Req.FreshTimeMissing = 550 std::chrono::steady_clock::time_point::min(); 551 auto Res = lookupCDB(Req); 552 if (!Res) 553 return llvm::None; 554 return Res->PI; 555 } 556 557 OverlayCDB::OverlayCDB(const GlobalCompilationDatabase *Base, 558 std::vector<std::string> FallbackFlags, 559 tooling::ArgumentsAdjuster Adjuster) 560 : DelegatingCDB(Base), ArgsAdjuster(std::move(Adjuster)), 561 FallbackFlags(std::move(FallbackFlags)) {} 562 563 llvm::Optional<tooling::CompileCommand> 564 OverlayCDB::getCompileCommand(PathRef File) const { 565 llvm::Optional<tooling::CompileCommand> Cmd; 566 { 567 std::lock_guard<std::mutex> Lock(Mutex); 568 auto It = Commands.find(removeDots(File)); 569 if (It != Commands.end()) 570 Cmd = It->second; 571 } 572 if (!Cmd) 573 Cmd = DelegatingCDB::getCompileCommand(File); 574 if (!Cmd) 575 return llvm::None; 576 if (ArgsAdjuster) 577 Cmd->CommandLine = ArgsAdjuster(Cmd->CommandLine, Cmd->Filename); 578 return Cmd; 579 } 580 581 tooling::CompileCommand OverlayCDB::getFallbackCommand(PathRef File) const { 582 auto Cmd = DelegatingCDB::getFallbackCommand(File); 583 std::lock_guard<std::mutex> Lock(Mutex); 584 Cmd.CommandLine.insert(Cmd.CommandLine.end(), FallbackFlags.begin(), 585 FallbackFlags.end()); 586 if (ArgsAdjuster) 587 Cmd.CommandLine = ArgsAdjuster(Cmd.CommandLine, Cmd.Filename); 588 return Cmd; 589 } 590 591 void OverlayCDB::setCompileCommand( 592 PathRef File, llvm::Optional<tooling::CompileCommand> Cmd) { 593 // We store a canonical version internally to prevent mismatches between set 594 // and get compile commands. Also it assures clients listening to broadcasts 595 // doesn't receive different names for the same file. 596 std::string CanonPath = removeDots(File); 597 { 598 std::unique_lock<std::mutex> Lock(Mutex); 599 if (Cmd) 600 Commands[CanonPath] = std::move(*Cmd); 601 else 602 Commands.erase(CanonPath); 603 } 604 OnCommandChanged.broadcast({CanonPath}); 605 } 606 607 DelegatingCDB::DelegatingCDB(const GlobalCompilationDatabase *Base) 608 : Base(Base) { 609 if (Base) 610 BaseChanged = Base->watch([this](const std::vector<std::string> Changes) { 611 OnCommandChanged.broadcast(Changes); 612 }); 613 } 614 615 DelegatingCDB::DelegatingCDB(std::unique_ptr<GlobalCompilationDatabase> Base) 616 : DelegatingCDB(Base.get()) { 617 BaseOwner = std::move(Base); 618 } 619 620 llvm::Optional<tooling::CompileCommand> 621 DelegatingCDB::getCompileCommand(PathRef File) const { 622 if (!Base) 623 return llvm::None; 624 return Base->getCompileCommand(File); 625 } 626 627 llvm::Optional<ProjectInfo> DelegatingCDB::getProjectInfo(PathRef File) const { 628 if (!Base) 629 return llvm::None; 630 return Base->getProjectInfo(File); 631 } 632 633 tooling::CompileCommand DelegatingCDB::getFallbackCommand(PathRef File) const { 634 if (!Base) 635 return GlobalCompilationDatabase::getFallbackCommand(File); 636 return Base->getFallbackCommand(File); 637 } 638 639 } // namespace clangd 640 } // namespace clang 641