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