clang-tools 23.0.0git
ModulesBuilder.cpp
Go to the documentation of this file.
1//===----------------- ModulesBuilder.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 "ModulesBuilder.h"
10#include "Compiler.h"
11#include "SourceCode.h"
12#include "support/Logger.h"
13#include "clang/Frontend/FrontendAction.h"
14#include "clang/Frontend/FrontendActions.h"
15#include "clang/Serialization/ASTReader.h"
16#include "clang/Serialization/ModuleCache.h"
17#include "llvm/ADT/ScopeExit.h"
18#include "llvm/ADT/StringExtras.h"
19#include "llvm/Support/CommandLine.h"
20#include "llvm/Support/FileSystem.h"
21#include "llvm/Support/LockFileManager.h"
22#include "llvm/Support/Path.h"
23#include "llvm/Support/Process.h"
24
25#include <chrono>
26#include <ctime>
27
28namespace clang {
29namespace clangd {
30
31namespace {
32
33llvm::cl::opt<bool> DebugModulesBuilder(
34 "debug-modules-builder",
35 llvm::cl::desc("Don't remove clangd's built module files for debugging. "
36 "Remember to remove them later after debugging."),
37 llvm::cl::init(false));
38
39llvm::cl::opt<unsigned> VersionedModuleFileGCThresholdSeconds(
40 "modules-builder-versioned-gc-threshold-seconds",
41 llvm::cl::desc("Delete versioned copy-on-read module files whose last "
42 "access time is older than this many seconds."),
43 llvm::cl::init(3 * 24 * 60 * 60));
44
45//===----------------------------------------------------------------------===//
46// Persistent Module Cache Layout.
47//
48// clangd publishes prerequisite BMIs into a stable on-disk cache so later
49// builders can reuse them across sessions. Cache entries are grouped by a
50// readable module-unit source directory name plus a hash of the normalized
51// source path, and are further separated by a hash of the full compile
52// command, which keeps incompatible BMI variants apart.
53//
54// module-unit source
55// |
56// v
57// cache root
58// |
59// +-- <module-unit-source-name>-<source-hash>
60// |
61// +-- <command-hash>
62// |
63// +-- <primary-module>[-<partition>].pcm
64//===----------------------------------------------------------------------===//
65
66std::string hashStringForCache(llvm::StringRef Content) {
67 return llvm::toHex(digest(Content));
68}
69
70std::string normalizePathForCache(PathRef Path) {
71 llvm::SmallString<256> Normalized(Path);
72 llvm::sys::path::remove_dots(Normalized, /*remove_dot_dot=*/true);
73 return maybeCaseFoldPath(Normalized);
74}
75
76/// Returns the root directory used for persistent module cache storage.
77/// Prefer a project-local cache so different clangd sessions working on the
78/// same source tree can reuse BMIs. Fall back to the user cache directory, and
79/// finally to a non-ephemeral temp directory when no better cache root exists.
80llvm::SmallString<256>
81getModuleCacheRoot(PathRef ModuleUnitFileName,
82 const GlobalCompilationDatabase &CDB) {
83 llvm::SmallString<256> Result;
84 if (auto PI = CDB.getProjectInfo(ModuleUnitFileName);
85 PI && !PI->SourceRoot.empty()) {
86 Result = PI->SourceRoot;
87 llvm::sys::path::append(Result, ".cache", "clangd", "modules");
88 return Result;
89 }
90
91 if (llvm::sys::path::cache_directory(Result)) {
92 llvm::sys::path::append(Result, "clangd", "modules");
93 return Result;
94 }
95
96 llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/false, Result);
97 llvm::sys::path::append(Result, "clangd", "modules");
98 return Result;
99}
100
101/// Returns the directory holding source-scoped lock files for the persistent
102/// module cache. Placing locks beside the cache ensures all builders sharing
103/// the cache also synchronize through the same lock namespace.
104llvm::SmallString<256>
105getModuleCacheLocksDirectory(PathRef ModuleUnitFileName,
106 const GlobalCompilationDatabase &CDB) {
107 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
108 llvm::sys::path::append(Result, ".locks");
109 return Result;
110}
111
112std::string getModuleUnitSourcePathHash(PathRef ModuleUnitFileName) {
113 return hashStringForCache(normalizePathForCache(ModuleUnitFileName));
114}
115
116std::string getModuleUnitSourceDirectoryName(PathRef ModuleUnitFileName) {
117 std::string Result = llvm::sys::path::filename(ModuleUnitFileName).str();
118 Result.push_back('-');
119 Result.append(getModuleUnitSourcePathHash(ModuleUnitFileName));
120 return Result;
121}
122
123std::string getCompileCommandStringHash(const tooling::CompileCommand &Cmd) {
124 std::string SerializedCommand;
125 SerializedCommand.reserve(Cmd.Directory.size() + Cmd.Filename.size() +
126 Cmd.CommandLine.size() * 16);
127 // The module-unit source path is already encoded in the parent cache
128 // directory. Output is rewritten while staging the BMI, so hash only the
129 // semantic compile command to keep the cache key stable across rebuilds.
130 SerializedCommand.append(Cmd.Directory);
131 SerializedCommand.push_back('\0');
132 for (const auto &Arg : Cmd.CommandLine) {
133 SerializedCommand.append(Arg);
134 SerializedCommand.push_back('\0');
135 }
136 return hashStringForCache(SerializedCommand);
137}
138
139/// Returns the directory for a persistent BMI built from a specific module
140/// unit source and compile command. The directory name keeps a readable source
141/// basename alongside the source-hash, and the command-hash keeps incompatible
142/// command lines apart.
143llvm::SmallString<256>
144getModuleFilesDirectory(PathRef ModuleUnitFileName,
145 const tooling::CompileCommand &Cmd,
146 const GlobalCompilationDatabase &CDB) {
147 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
148 llvm::sys::path::append(Result,
149 getModuleUnitSourceDirectoryName(ModuleUnitFileName),
150 getCompileCommandStringHash(Cmd));
151 return Result;
152}
153
154/// Returns the lock file path guarding publication of BMIs for a module unit
155/// source. Builders targeting the same source-hash serialize through this path.
156llvm::SmallString<256>
157getModuleSourceHashLockPath(PathRef ModuleUnitFileName,
158 const GlobalCompilationDatabase &CDB) {
159 llvm::SmallString<256> Result =
160 getModuleCacheLocksDirectory(ModuleUnitFileName, CDB);
161 llvm::sys::path::append(Result,
162 getModuleUnitSourcePathHash(ModuleUnitFileName));
163 return Result;
164}
165
166/// Returns a unique temporary path used to stage a BMI before atomically
167/// publishing it to the stable cache path.
168llvm::SmallString<256> getTemporaryModuleFilePath(PathRef ModuleFilePath) {
169 llvm::SmallString<256> ResultPattern(ModuleFilePath);
170 ResultPattern.append(".tmp-%%-%%-%%-%%-%%-%%");
171 llvm::SmallString<256> Result;
172 llvm::sys::fs::createUniquePath(ResultPattern, Result,
173 /*MakeAbsolute=*/false);
174 return Result;
175}
176
177std::string getModuleFileVersionTimestamp() {
178 const auto Now = std::chrono::system_clock::now();
179 const auto Micros = std::chrono::duration_cast<std::chrono::microseconds>(
180 Now.time_since_epoch()) %
181 std::chrono::seconds(1);
182 const std::time_t CalendarTime = std::chrono::system_clock::to_time_t(Now);
183 std::tm LocalTime;
184#ifdef _WIN32
185 localtime_s(&LocalTime, &CalendarTime);
186#else
187 localtime_r(&CalendarTime, &LocalTime);
188#endif
189
190 return llvm::formatv("{0:04}{1:02}{2:02}-{3:02}{4:02}{5:02}-{6:06}",
191 LocalTime.tm_year + 1900, LocalTime.tm_mon + 1,
192 LocalTime.tm_mday, LocalTime.tm_hour, LocalTime.tm_min,
193 LocalTime.tm_sec, Micros.count())
194 .str();
195}
196
197llvm::SmallString<256>
198getCopyOnReadModuleFilePath(PathRef PublishedModuleFile) {
199 llvm::SmallString<256> Result(PublishedModuleFile);
200 llvm::sys::path::remove_filename(Result);
201 llvm::sys::path::append(
202 Result,
203 llvm::formatv("{0}-{1}{2}", llvm::sys::path::stem(PublishedModuleFile),
204 getModuleFileVersionTimestamp(),
205 llvm::sys::path::extension(PublishedModuleFile))
206 .str());
207 return Result;
208}
209
210/// Ensures the lock anchor file exists before LockFileManager tries to acquire
211/// ownership, creating parent directories as needed.
212llvm::Error ensureLockAnchorFileExists(PathRef LockPath) {
213 llvm::SmallString<256> LockParent(LockPath);
214 llvm::sys::path::remove_filename(LockParent);
215 if (std::error_code EC = llvm::sys::fs::create_directories(LockParent))
216 return llvm::createStringError(llvm::formatv(
217 "Failed to create lock directory {0}: {1}", LockParent, EC.message()));
218
219 int FD = -1;
220 if (std::error_code EC = llvm::sys::fs::openFileForWrite(
221 LockPath, FD, llvm::sys::fs::CD_OpenAlways))
222 return llvm::createStringError(llvm::formatv(
223 "Failed to open lock file anchor {0}: {1}", LockPath, EC.message()));
224 llvm::sys::Process::SafelyCloseFileDescriptor(FD);
225 return llvm::Error::success();
226}
227
228//===----------------------------------------------------------------------===//
229// Persistent Module Cache Locking.
230//
231// Builders targeting the same module-unit source share a source-hash lock.
232// This serializes in-place replacement of stale cache entries and final publish
233// of the stable BMI path, while still allowing unrelated module sources to be
234// built concurrently.
235//
236// builder A builder B
237// | |
238// +---- lock(source) ----->|
239// | |
240// | build/publish BMI | wait
241// | |
242// +---- unlock ----------->|
243// | reuse or rebuild
244//===----------------------------------------------------------------------===//
245
246/// Serializes publication and in-place replacement of persistent BMIs for a
247/// single module-unit source across multiple builders.
248class ScopedModuleSourceLock {
249public:
250 static llvm::Expected<ScopedModuleSourceLock>
251 acquire(PathRef ModuleUnitFileName, const GlobalCompilationDatabase &CDB) {
252 constexpr auto LockWaitInterval = std::chrono::seconds(10);
253 llvm::SmallString<256> LockPath =
254 getModuleSourceHashLockPath(ModuleUnitFileName, CDB);
255 if (llvm::Error Err = ensureLockAnchorFileExists(LockPath))
256 return std::move(Err);
257
258 auto Waited = std::chrono::seconds::zero();
259
260 while (true) {
261 auto Lock = std::make_unique<llvm::LockFileManager>(LockPath);
262 auto TryLock = Lock->tryLock();
263 if (!TryLock)
264 return TryLock.takeError();
265 if (*TryLock)
266 return ScopedModuleSourceLock(std::move(Lock));
267
268 switch (Lock->waitForUnlockFor(LockWaitInterval)) {
269 case llvm::WaitForUnlockResult::Success:
270 case llvm::WaitForUnlockResult::OwnerDied:
271 continue;
272 case llvm::WaitForUnlockResult::Timeout:
273 Waited += LockWaitInterval;
274 log("Still waiting for module lock {0} after {1}s", LockPath,
275 Waited.count());
276 continue;
277 }
278 llvm_unreachable("Unhandled lock wait result");
279 }
280 }
281
282private:
283 explicit ScopedModuleSourceLock(std::unique_ptr<llvm::LockFileManager> Lock)
284 : Lock(std::move(Lock)) {}
285
286 std::unique_ptr<llvm::LockFileManager> Lock;
287};
288
289// Get the stable published module file path under \param ModuleFilesPrefix.
290std::string getModuleFilePath(llvm::StringRef ModuleName,
291 PathRef ModuleFilesPrefix) {
292 llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
293 auto [PrimaryModuleName, PartitionName] = ModuleName.split(':');
294 llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
295 if (!PartitionName.empty()) {
296 ModuleFilePath.append("-");
297 ModuleFilePath.append(PartitionName);
298 }
299
300 ModuleFilePath.append(".pcm");
301 return std::string(ModuleFilePath);
302}
303
304std::string getPublishedModuleFilePath(llvm::StringRef ModuleName,
305 PathRef ModuleFilesPrefix) {
306 return getModuleFilePath(ModuleName, ModuleFilesPrefix);
307}
308
309// FailedPrerequisiteModules - stands for the PrerequisiteModules which has
310// errors happened during the building process.
311class FailedPrerequisiteModules : public PrerequisiteModules {
312public:
313 ~FailedPrerequisiteModules() override = default;
314
315 // We shouldn't adjust the compilation commands based on
316 // FailedPrerequisiteModules.
317 void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override {}
318
319 // FailedPrerequisiteModules can never be reused.
320 bool
321 canReuse(const CompilerInvocation &CI,
322 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) const override {
323 return false;
324 }
325};
326
327/// Represents a reference to a module file (*.pcm).
328class ModuleFile {
329protected:
330 ModuleFile(StringRef ModuleName, PathRef ModuleFilePath)
331 : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
332
333public:
334 ModuleFile() = delete;
335
336 ModuleFile(const ModuleFile &) = delete;
337 ModuleFile operator=(const ModuleFile &) = delete;
338
339 // The move constructor is needed for llvm::SmallVector.
340 ModuleFile(ModuleFile &&Other)
341 : ModuleName(std::move(Other.ModuleName)),
342 ModuleFilePath(std::move(Other.ModuleFilePath)) {
343 Other.ModuleName.clear();
344 Other.ModuleFilePath.clear();
345 }
346
347 ModuleFile &operator=(ModuleFile &&Other) {
348 if (this == &Other)
349 return *this;
350
351 this->~ModuleFile();
352 new (this) ModuleFile(std::move(Other));
353 return *this;
354 }
355 virtual ~ModuleFile() = default;
356
357 StringRef getModuleName() const { return ModuleName; }
358
359 StringRef getModuleFilePath() const { return ModuleFilePath; }
360
361protected:
362 std::string ModuleName;
363 std::string ModuleFilePath;
364};
365
366/// Represents a prebuilt module file which is not owned by us.
367class PrebuiltModuleFile : public ModuleFile {
368private:
369 // private class to make sure the class can only be constructed by member
370 // functions.
371 struct CtorTag {};
372
373public:
374 PrebuiltModuleFile(StringRef ModuleName, PathRef ModuleFilePath, CtorTag)
375 : ModuleFile(ModuleName, ModuleFilePath) {}
376
377 static std::shared_ptr<PrebuiltModuleFile> make(StringRef ModuleName,
378 PathRef ModuleFilePath) {
379 return std::make_shared<PrebuiltModuleFile>(ModuleName, ModuleFilePath,
380 CtorTag{});
381 }
382};
383
384//===----------------------------------------------------------------------===//
385// Module File Ownership and Reuse.
386//
387// PrebuiltModuleFile refers to BMIs supplied directly by the compile command.
388// BuiltModuleFile refers to BMIs produced by clangd and published into the
389// persistent cache. Object lifetime does not control filesystem lifetime for
390// BuiltModuleFile; cache files remain on disk for reuse across builders. The
391// versioned copies handed to clang for actual reads are owned by
392// CopyOnReadModuleFile and are deleted when the last reader releases them.
393//
394// Copy-on-read keeps the published BMI path stable for future builders while
395// avoiding in-place replacement races for active readers. clangd never hands
396// the stable cache entry directly to parsing code. Instead, once a published
397// BMI is known to be up to date, clangd copies it to a versioned sibling path
398// and gives that copy to readers. Rebuilding only mutates the stable cache
399// entry; existing readers keep their own immutable copy until the last
400// shared_ptr reference drops and the copy-on-read file is deleted.
401//
402// compile command ---------> PrebuiltModuleFile
403//
404// clangd build -> publish -> BuiltModuleFile ------------> stable cache path
405// (M.pcm)
406// |
407// +-> copy for read -> CopyOnReadModuleFile
408// (M-<timestamp>.pcm)
409// -> handed to clang readers
410// -> removed on last release
411//
412// later builder -----------> reuse stable cache path ----> copy for read
413//===----------------------------------------------------------------------===//
414
415/// Represents a module file built and published by clangd into its persistent
416/// cache.
417class BuiltModuleFile final : public ModuleFile {
418private:
419 // private class to make sure the class can only be constructed by member
420 // functions.
421 struct CtorTag {};
422
423public:
424 BuiltModuleFile(StringRef ModuleName, PathRef ModuleFilePath, CtorTag)
425 : ModuleFile(ModuleName, ModuleFilePath) {}
426
427 static std::shared_ptr<BuiltModuleFile> make(StringRef ModuleName,
428 PathRef ModuleFilePath) {
429 return std::make_shared<BuiltModuleFile>(ModuleName, ModuleFilePath,
430 CtorTag{});
431 }
432};
433
434/// Represents a versioned copy of a published BMI handed to clangd readers.
435/// The copy is removed when the last reader releases it.
436class CopyOnReadModuleFile final : public ModuleFile {
437private:
438 struct CtorTag {};
439
440public:
441 CopyOnReadModuleFile(StringRef ModuleName, PathRef ModuleFilePath, CtorTag)
442 : ModuleFile(ModuleName, ModuleFilePath) {}
443
444 ~CopyOnReadModuleFile() override {
445 if (!ModuleFilePath.empty() && !DebugModulesBuilder)
446 if (std::error_code EC = llvm::sys::fs::remove(ModuleFilePath))
447 vlog("Failed to remove copy-on-read module file {0}: {1}",
448 ModuleFilePath, EC.message());
449 }
450
451 static std::shared_ptr<CopyOnReadModuleFile> make(StringRef ModuleName,
452 PathRef ModuleFilePath) {
453 return std::make_shared<CopyOnReadModuleFile>(ModuleName, ModuleFilePath,
454 CtorTag{});
455 }
456};
457
458// ReusablePrerequisiteModules - stands for PrerequisiteModules for which all
459// the required modules are built successfully. All the module files
460// are owned by the modules builder.
461class ReusablePrerequisiteModules : public PrerequisiteModules {
462public:
463 ReusablePrerequisiteModules() = default;
464
465 ReusablePrerequisiteModules(const ReusablePrerequisiteModules &Other) =
466 default;
467 ReusablePrerequisiteModules &
468 operator=(const ReusablePrerequisiteModules &) = default;
469 ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) = delete;
470 ReusablePrerequisiteModules
471 operator=(ReusablePrerequisiteModules &&) = delete;
472
473 ~ReusablePrerequisiteModules() override = default;
474
475 void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override {
476 // Appending all built module files.
477 for (const auto &RequiredModule : RequiredModules)
478 Options.PrebuiltModuleFiles.insert_or_assign(
479 RequiredModule->getModuleName().str(),
480 RequiredModule->getModuleFilePath().str());
481 }
482
483 std::string getAsString() const {
484 std::string Result;
485 llvm::raw_string_ostream OS(Result);
486 for (const auto &MF : RequiredModules) {
487 OS << "-fmodule-file=" << MF->getModuleName() << "="
488 << MF->getModuleFilePath() << " ";
489 }
490 return Result;
491 }
492
493 bool canReuse(const CompilerInvocation &CI,
494 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) const override;
495
496 bool isModuleUnitBuilt(llvm::StringRef ModuleName) const {
497 return BuiltModuleNames.contains(ModuleName);
498 }
499
500 void addModuleFile(std::shared_ptr<const ModuleFile> MF) {
501 BuiltModuleNames.insert(MF->getModuleName());
502 RequiredModules.emplace_back(std::move(MF));
503 }
504
505private:
506 llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
507 // A helper class to speedup the query if a module is built.
508 llvm::StringSet<> BuiltModuleNames;
509};
510
511bool IsModuleFileUpToDate(PathRef ModuleFilePath,
512 const PrerequisiteModules &RequisiteModules,
513 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
514 HeaderSearchOptions HSOpts;
515 RequisiteModules.adjustHeaderSearchOptions(HSOpts);
516 HSOpts.ForceCheckCXX20ModulesInputFiles = true;
517 HSOpts.ValidateASTInputFilesContent = true;
518
519 clang::clangd::IgnoreDiagnostics IgnoreDiags;
520 DiagnosticOptions DiagOpts;
521 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
522 CompilerInstance::createDiagnostics(*VFS, DiagOpts, &IgnoreDiags,
523 /*ShouldOwnClient=*/false);
524
525 LangOptions LangOpts;
526 LangOpts.SkipODRCheckInGMF = true;
527
528 FileManager FileMgr(FileSystemOptions(), VFS);
529
530 SourceManager SourceMgr(*Diags, FileMgr);
531
532 HeaderSearch HeaderInfo(HSOpts, SourceMgr, *Diags, LangOpts,
533 /*Target=*/nullptr);
534
535 PreprocessorOptions PPOpts;
536 TrivialModuleLoader ModuleLoader;
537 Preprocessor PP(PPOpts, *Diags, LangOpts, SourceMgr, HeaderInfo,
538 ModuleLoader);
539
540 std::shared_ptr<ModuleCache> ModCache = createCrossProcessModuleCache();
541 PCHContainerOperations PCHOperations;
542 CodeGenOptions CodeGenOpts;
543 ASTReader Reader(
544 PP, *ModCache, /*ASTContext=*/nullptr, PCHOperations.getRawReader(),
545 CodeGenOpts, {},
546 /*isysroot=*/"",
547 /*DisableValidationKind=*/DisableValidationForModuleKind::None,
548 /*AllowASTWithCompilerErrors=*/false,
549 /*AllowConfigurationMismatch=*/false,
550 /*ValidateSystemInputs=*/false,
551 /*ForceValidateUserInputs=*/true,
552 /*ValidateASTInputFilesContent=*/true);
553
554 // We don't need any listener here. By default it will use a validator
555 // listener.
556 Reader.setListener(nullptr);
557
558 // Use ARR_OutOfDate so that ReadAST returns OutOfDate instead of Failure
559 // when input files are modified. This allows us to detect staleness
560 // without treating it as a hard error.
561 // ReadAST will validate all input files internally and return OutOfDate
562 // if any file is modified.
563 return Reader.ReadAST(ModuleFileName::makeExplicit(ModuleFilePath),
564 serialization::MK_MainFile, SourceLocation(),
565 ASTReader::ARR_OutOfDate) == ASTReader::Success;
566}
567
568bool IsModuleFilesUpToDate(
569 llvm::SmallVector<PathRef> ModuleFilePaths,
570 const PrerequisiteModules &RequisiteModules,
571 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
572 return llvm::all_of(
573 ModuleFilePaths, [&RequisiteModules, VFS](auto ModuleFilePath) {
574 return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
575 });
576}
577
578/// Builds a BMI into a temporary file and publishes it to `ModuleFilePath`.
579/// If another builder wins the publish race first, reports that through
580/// `PublishedExistingModuleFile` so the caller can validate and reuse it.
581llvm::Expected<std::shared_ptr<BuiltModuleFile>>
582buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName,
583 tooling::CompileCommand Cmd, PathRef ModuleFilePath,
584 const ThreadsafeFS &TFS,
585 const ReusablePrerequisiteModules &BuiltModuleFiles,
586 bool &PublishedExistingModuleFile) {
587 PublishedExistingModuleFile = false;
588 llvm::SmallString<256> ModuleFilesPrefix(ModuleFilePath);
589 llvm::sys::path::remove_filename(ModuleFilesPrefix);
590 if (std::error_code EC = llvm::sys::fs::create_directories(ModuleFilesPrefix))
591 return llvm::createStringError(
592 llvm::formatv("Failed to create module cache directory {0}: {1}",
593 ModuleFilesPrefix, EC.message()));
594
595 llvm::SmallString<256> TemporaryModuleFilePath =
596 getTemporaryModuleFilePath(ModuleFilePath);
597 auto RemoveTemporaryModuleFile = llvm::scope_exit([&] {
598 if (!TemporaryModuleFilePath.empty() && !DebugModulesBuilder)
599 llvm::sys::fs::remove(TemporaryModuleFilePath);
600 });
601 (void)RemoveTemporaryModuleFile;
602
603 Cmd.Output = TemporaryModuleFilePath.str().str();
604
605 ParseInputs Inputs;
606 Inputs.TFS = &TFS;
607 Inputs.CompileCommand = std::move(Cmd);
608
609 IgnoreDiagnostics IgnoreDiags;
610 auto CI = buildCompilerInvocation(Inputs, IgnoreDiags);
611 if (!CI)
612 return llvm::createStringError("Failed to build compiler invocation");
613
614 auto FS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
615 auto Buf = FS->getBufferForFile(Inputs.CompileCommand.Filename);
616 if (!Buf)
617 return llvm::createStringError("Failed to create buffer");
618
619 // In clang's driver, we will suppress the check for ODR violation in GMF.
620 // See the implementation of RenderModulesOptions in Clang.cpp.
621 CI->getLangOpts().SkipODRCheckInGMF = true;
622
623 // Hash the contents of input files and store the hash value to the BMI files.
624 // So that we can check if the files are still valid when we want to reuse the
625 // BMI files.
626 CI->getHeaderSearchOpts().ValidateASTInputFilesContent = true;
627
628 BuiltModuleFiles.adjustHeaderSearchOptions(CI->getHeaderSearchOpts());
629
630 CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
631 auto Clang =
632 prepareCompilerInstance(std::move(CI), /*Preamble=*/nullptr,
633 std::move(*Buf), std::move(FS), IgnoreDiags);
634 if (!Clang)
635 return llvm::createStringError("Failed to prepare compiler instance");
636
637 GenerateReducedModuleInterfaceAction Action;
638 Clang->ExecuteAction(Action);
639
640 if (Clang->getDiagnostics().hasErrorOccurred()) {
641 std::string Cmds;
642 for (const auto &Arg : Inputs.CompileCommand.CommandLine) {
643 if (!Cmds.empty())
644 Cmds += " ";
645 Cmds += Arg;
646 }
647
648 clangd::vlog("Failed to compile {0} with command: {1}", ModuleUnitFileName,
649 Cmds);
650
651 std::string BuiltModuleFilesStr = BuiltModuleFiles.getAsString();
652 if (!BuiltModuleFilesStr.empty())
653 clangd::vlog("The actual used module files built by clangd is {0}",
654 BuiltModuleFilesStr);
655
656 return llvm::createStringError(
657 llvm::formatv("Failed to compile {0}. Use '--log=verbose' to view "
658 "detailed failure reasons. It is helpful to use "
659 "'--debug-modules-builder' flag to keep the clangd's "
660 "built module files to reproduce the failure for "
661 "debugging. Remember to remove them after debugging.",
662 ModuleUnitFileName));
663 }
664
665 if (std::error_code EC =
666 llvm::sys::fs::rename(TemporaryModuleFilePath, ModuleFilePath)) {
667 if (!llvm::sys::fs::exists(ModuleFilePath))
668 return llvm::createStringError(
669 llvm::formatv("Failed to publish module file {0}: {1}",
670 ModuleFilePath, EC.message()));
671 // Another builder already published the stable cache entry. Drop our
672 // staged BMI and let the caller revalidate the published path.
673 PublishedExistingModuleFile = true;
674 } else {
675 // Rename consumed the staging file into the stable cache path. Clear it so
676 // the scope-exit cleanup does not try to remove the published BMI.
677 TemporaryModuleFilePath.clear();
678 }
679
680 return BuiltModuleFile::make(ModuleName, ModuleFilePath);
681}
682
683llvm::Expected<std::shared_ptr<CopyOnReadModuleFile>>
684copyModuleFileForRead(llvm::StringRef ModuleName,
685 PathRef PublishedModuleFilePath) {
686 llvm::SmallString<256> VersionedModuleFilePath =
687 getCopyOnReadModuleFilePath(PublishedModuleFilePath);
688 if (std::error_code EC = llvm::sys::fs::copy_file(PublishedModuleFilePath,
689 VersionedModuleFilePath))
690 return llvm::createStringError(llvm::formatv(
691 "Failed to copy module file {0} to {1}: {2}", PublishedModuleFilePath,
692 VersionedModuleFilePath, EC.message()));
693 return CopyOnReadModuleFile::make(ModuleName, VersionedModuleFilePath);
694}
695
696bool ReusablePrerequisiteModules::canReuse(
697 const CompilerInvocation &CI,
698 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) const {
699 if (RequiredModules.empty())
700 return true;
701
702 llvm::SmallVector<llvm::StringRef> BMIPaths;
703 for (auto &MF : RequiredModules)
704 BMIPaths.push_back(MF->getModuleFilePath());
705 return IsModuleFilesUpToDate(BMIPaths, *this, VFS);
706}
707
708//===----------------------------------------------------------------------===//
709// In-Memory Module File Cache.
710//
711// This cache deduplicates BMIs within a single builder instance. Its key
712// mirrors the persistent cache layout: module name, module-unit source, and
713// compile command hash. That prevents a builder from reusing a BMI built under
714// a different command line.
715//
716// (module name,
717// module-unit source,
718// command hash)
719// |
720// v
721// ModuleFileCache
722// |
723// +-- hit -> reuse in current builder
724// |
725// +-- miss -> probe persistent cache / rebuild
726//===----------------------------------------------------------------------===//
727
728/// In-memory cache for module files built by clangd. Entries are keyed by
729/// module name, module-unit source, and compile-command hash so persistent BMI
730/// variants do not collide.
731class ModuleFileCache {
732public:
733 ModuleFileCache(const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
734 const GlobalCompilationDatabase &getCDB() const { return CDB; }
735
736 std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName,
737 PathRef ModuleUnitSource,
738 llvm::StringRef CommandHash);
739
740 void add(StringRef ModuleName, PathRef ModuleUnitSource,
741 llvm::StringRef CommandHash,
742 std::shared_ptr<const ModuleFile> ModuleFile) {
743 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
744 ModuleFiles[cacheKey(ModuleName, ModuleUnitSource, CommandHash)] =
745 ModuleFile;
746 }
747
748 void remove(StringRef ModuleName, PathRef ModuleUnitSource,
749 llvm::StringRef CommandHash);
750
751private:
752 static std::string cacheKey(StringRef ModuleName, PathRef ModuleUnitSource,
753 llvm::StringRef CommandHash) {
754 std::string Key;
755 Key.reserve(ModuleName.size() + ModuleUnitSource.size() +
756 CommandHash.size() + 2);
757 Key.append(ModuleName);
758 Key.push_back('\0');
759 Key.append(maybeCaseFoldPath(ModuleUnitSource));
760 Key.push_back('\0');
761 Key.append(CommandHash);
762 return Key;
763 }
764
765 const GlobalCompilationDatabase &CDB;
766
767 llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
768 std::mutex ModuleFilesMutex;
769};
770
771std::shared_ptr<const ModuleFile>
772ModuleFileCache::getModule(StringRef ModuleName, PathRef ModuleUnitSource,
773 llvm::StringRef CommandHash) {
774 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
775
776 auto Iter =
777 ModuleFiles.find(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
778 if (Iter == ModuleFiles.end())
779 return nullptr;
780
781 if (auto Res = Iter->second.lock())
782 return Res;
783
784 ModuleFiles.erase(Iter);
785 return nullptr;
786}
787
788void ModuleFileCache::remove(StringRef ModuleName, PathRef ModuleUnitSource,
789 llvm::StringRef CommandHash) {
790 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
791 ModuleFiles.erase(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
792}
793
794class ModuleNameToSourceCache {
795public:
796 std::string getUniqueSourceForModuleName(llvm::StringRef ModuleName) {
797 std::lock_guard<std::mutex> Lock(CacheMutex);
798 auto Iter = ModuleNameToUniqueSourceCache.find(ModuleName);
799 if (Iter != ModuleNameToUniqueSourceCache.end())
800 return Iter->second;
801 return "";
802 }
803
804 void addUniqueEntry(llvm::StringRef ModuleName, PathRef Source) {
805 std::lock_guard<std::mutex> Lock(CacheMutex);
806 ModuleNameToUniqueSourceCache[ModuleName] = Source.str();
807 }
808
809 void eraseUniqueEntry(llvm::StringRef ModuleName) {
810 std::lock_guard<std::mutex> Lock(CacheMutex);
811 ModuleNameToUniqueSourceCache.erase(ModuleName);
812 }
813
814 std::string getMultipleSourceForModuleName(llvm::StringRef ModuleName,
815 PathRef RequiredSrcFile) {
816 std::lock_guard<std::mutex> Lock(CacheMutex);
817 auto Outer = ModuleNameToMultipleSourceCache.find(ModuleName);
818 if (Outer == ModuleNameToMultipleSourceCache.end())
819 return "";
820 auto Inner = Outer->second.find(maybeCaseFoldPath(RequiredSrcFile));
821 if (Inner == Outer->second.end())
822 return "";
823 return Inner->second;
824 }
825
826 void addMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile,
827 PathRef Source) {
828 std::lock_guard<std::mutex> Lock(CacheMutex);
829 ModuleNameToMultipleSourceCache[ModuleName]
830 [maybeCaseFoldPath(RequiredSrcFile)] =
831 Source.str();
832 }
833
834 void eraseMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile) {
835 std::lock_guard<std::mutex> Lock(CacheMutex);
836 auto Outer = ModuleNameToMultipleSourceCache.find(ModuleName);
837 if (Outer == ModuleNameToMultipleSourceCache.end())
838 return;
839 Outer->second.erase(maybeCaseFoldPath(RequiredSrcFile));
840 if (Outer->second.empty())
841 ModuleNameToMultipleSourceCache.erase(Outer);
842 }
843
844private:
845 std::mutex CacheMutex;
846 llvm::StringMap<std::string> ModuleNameToUniqueSourceCache;
847
848 // Map from module name to a map from required source to module unit source
849 // which declares the corresponding module name.
850 // This looks inefficiency. We can only assume there won't too many duplicated
851 // module names with different module units in a project.
852 llvm::StringMap<llvm::StringMap<std::string>> ModuleNameToMultipleSourceCache;
853};
854
855class CachingProjectModules : public ProjectModules {
856public:
857 CachingProjectModules(std::unique_ptr<ProjectModules> MDB,
858 ModuleNameToSourceCache &Cache)
859 : MDB(std::move(MDB)), Cache(Cache) {
860 assert(this->MDB && "CachingProjectModules should only be created with a "
861 "valid underlying ProjectModules");
862 }
863
864 std::vector<std::string> getRequiredModules(PathRef File) override {
865 return MDB->getRequiredModules(File);
866 }
867
868 std::string getModuleNameForSource(PathRef File) override {
869 return MDB->getModuleNameForSource(File);
870 }
871
872 ModuleNameState getModuleNameState(llvm::StringRef ModuleName) override {
873 return MDB->getModuleNameState(ModuleName);
874 }
875
876 std::string getSourceForModuleName(llvm::StringRef ModuleName,
877 PathRef RequiredSrcFile) override {
878 auto ModuleState = MDB->getModuleNameState(ModuleName);
879
880 if (ModuleState == ModuleNameState::Multiple) {
881 std::string CachedResult =
882 Cache.getMultipleSourceForModuleName(ModuleName, RequiredSrcFile);
883
884 // Verify Cached Result by seeing if the source declaring the same module
885 // as we query.
886 if (!CachedResult.empty()) {
887 std::string ModuleNameOfCachedSource =
888 MDB->getModuleNameForSource(CachedResult);
889 if (ModuleNameOfCachedSource == ModuleName)
890 return CachedResult;
891
892 // Cached Result is invalid. Clear it.
893 Cache.eraseMultipleEntry(ModuleName, RequiredSrcFile);
894 }
895
896 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
897 if (!Result.empty())
898 Cache.addMultipleEntry(ModuleName, RequiredSrcFile, Result);
899 return Result;
900 }
901
902 // For unknown module name state, assume it is unique. This may give user
903 // higher usability.
904 assert(ModuleState == ModuleNameState::Unique ||
905 ModuleState == ModuleNameState::Unknown);
906 std::string CachedResult = Cache.getUniqueSourceForModuleName(ModuleName);
907
908 // Verify Cached Result by seeing if the source declaring the same module
909 // as we query.
910 if (!CachedResult.empty()) {
911 std::string ModuleNameOfCachedSource =
912 MDB->getModuleNameForSource(CachedResult);
913 if (ModuleNameOfCachedSource == ModuleName)
914 return CachedResult;
915
916 // Cached Result is invalid. Clear it.
917 Cache.eraseUniqueEntry(ModuleName);
918 }
919
920 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
921 if (!Result.empty())
922 Cache.addUniqueEntry(ModuleName, Result);
923
924 return Result;
925 }
926
927private:
928 std::unique_ptr<ProjectModules> MDB;
929 ModuleNameToSourceCache &Cache;
930};
931
932/// Collect the directly and indirectly required module names for \param
933/// ModuleName in topological order. The \param ModuleName is guaranteed to
934/// be the last element in \param ModuleNames.
935llvm::SmallVector<std::string> getAllRequiredModules(PathRef RequiredSource,
936 CachingProjectModules &MDB,
937 StringRef ModuleName) {
938 llvm::SmallVector<std::string> ModuleNames;
939 llvm::StringSet<> ModuleNamesSet;
940
941 auto VisitDeps = [&](StringRef ModuleName, auto Visitor) -> void {
942 ModuleNamesSet.insert(ModuleName);
943
944 for (StringRef RequiredModuleName : MDB.getRequiredModules(
945 MDB.getSourceForModuleName(ModuleName, RequiredSource)))
946 if (ModuleNamesSet.insert(RequiredModuleName).second)
947 Visitor(RequiredModuleName, Visitor);
948
949 ModuleNames.push_back(ModuleName.str());
950 };
951 VisitDeps(ModuleName, VisitDeps);
952
953 return ModuleNames;
954}
955
956/// Collects cache roots to scan during constructor-time GC.
957/// Scans one cache root and returns all `.pcm` files under it.
958std::vector<std::string> collectModuleFiles(PathRef CacheRoot) {
959 std::vector<std::string> Result;
960 std::error_code EC;
961 for (llvm::sys::fs::recursive_directory_iterator It(CacheRoot, EC), End;
962 It != End && !EC; It.increment(EC)) {
963 if (llvm::sys::path::extension(It->path()) != ".pcm")
964 continue;
965 Result.push_back(It->path());
966 }
967 if (EC)
968 log("Failed to scan module cache directory {0}: {1}", CacheRoot,
969 EC.message());
970 return Result;
971}
972
973/// Performs one GC pass over a persistent module cache root.
974void garbageCollectModuleCache(PathRef CacheRoot) {
975 for (const auto &ModuleFilePath : collectModuleFiles(CacheRoot)) {
976 llvm::sys::fs::file_status Status;
977 if (std::error_code EC = llvm::sys::fs::status(ModuleFilePath, Status)) {
978 log("Failed to stat cached module file {0} for GC: {1}", ModuleFilePath,
979 EC.message());
980 continue;
981 }
982
983 llvm::sys::TimePoint<> LastAccess = Status.getLastAccessedTime();
984 llvm::sys::TimePoint<> Now = std::chrono::system_clock::now();
985 if (LastAccess > Now)
986 continue;
987 auto Age =
988 std::chrono::duration_cast<std::chrono::seconds>(Now - LastAccess);
989 auto Threshold =
990 std::chrono::seconds(VersionedModuleFileGCThresholdSeconds);
991 if (Age <= Threshold)
992 continue;
993
994 if (!llvm::sys::fs::exists(ModuleFilePath))
995 continue;
996
997 constexpr llvm::StringLiteral Reason = "file older than GC threshold";
998 if (std::error_code EC = llvm::sys::fs::remove(ModuleFilePath)) {
999 log("Failed to remove cached module file {0} ({1}): {2}", ModuleFilePath,
1000 Reason, EC.message());
1001 continue;
1002 }
1003 log("Removed cached module file {0} ({1})", ModuleFilePath, Reason);
1004 }
1005}
1006
1007} // namespace
1008
1010public:
1012
1013 ModuleNameToSourceCache &getProjectModulesCache() {
1014 return ProjectModulesCache;
1015 }
1016 const GlobalCompilationDatabase &getCDB() const { return Cache.getCDB(); }
1017
1018 llvm::Error
1019 getOrBuildModuleFile(PathRef RequiredSource, StringRef ModuleName,
1020 const ThreadsafeFS &TFS, CachingProjectModules &MDB,
1021 ReusablePrerequisiteModules &BuiltModuleFiles);
1022
1023private:
1024 /// Try to get prebuilt module files from the compilation database.
1025 void getPrebuiltModuleFile(StringRef ModuleName, PathRef ModuleUnitFileName,
1026 const ThreadsafeFS &TFS,
1027 ReusablePrerequisiteModules &BuiltModuleFiles);
1028
1029 /// Runs GC once for the cache root owning a project root.
1030 void garbageCollectModuleCacheForProjectRoot(PathRef ProjectRoot);
1031
1032 ModuleFileCache Cache;
1033 ModuleNameToSourceCache ProjectModulesCache;
1034 std::mutex GarbageCollectedProjectRootsMutex;
1035 llvm::StringSet<> GarbageCollectedProjectRoots;
1036};
1037
1038void ModulesBuilder::ModulesBuilderImpl::
1039 garbageCollectModuleCacheForProjectRoot(PathRef ProjectRoot) {
1040 if (ProjectRoot.empty())
1041 return;
1042 std::string NormalizedProjectRoot = normalizePathForCache(ProjectRoot);
1043 {
1044 // If the project root lives in GarbageCollectedProjectRoots, it implies
1045 // we've already started GC on the cache root.
1046 std::lock_guard<std::mutex> Lock(GarbageCollectedProjectRootsMutex);
1047 if (!GarbageCollectedProjectRoots.insert(NormalizedProjectRoot).second)
1048 return;
1049 }
1050
1051 llvm::SmallString<256> CacheRoot(ProjectRoot);
1052 llvm::sys::path::append(CacheRoot, ".cache", "clangd", "modules");
1053 log("Running GC pass for clangd built module files under {0} with age "
1054 "threshold {1} seconds (adjust with --modules-builder-versioned-gc-"
1055 "threshold-seconds)",
1056 CacheRoot, VersionedModuleFileGCThresholdSeconds);
1057 garbageCollectModuleCache(CacheRoot);
1058 log("Done running GC pass for clangd built module files under {0}",
1059 CacheRoot);
1060}
1061
1062void ModulesBuilder::ModulesBuilderImpl::getPrebuiltModuleFile(
1063 StringRef ModuleName, PathRef ModuleUnitFileName, const ThreadsafeFS &TFS,
1064 ReusablePrerequisiteModules &BuiltModuleFiles) {
1065 auto Cmd = getCDB().getCompileCommand(ModuleUnitFileName);
1066 if (!Cmd)
1067 return;
1068
1069 ParseInputs Inputs;
1070 Inputs.TFS = &TFS;
1071 Inputs.CompileCommand = std::move(*Cmd);
1072
1073 IgnoreDiagnostics IgnoreDiags;
1074 auto CI = buildCompilerInvocation(Inputs, IgnoreDiags);
1075 if (!CI)
1076 return;
1077
1078 // We don't need to check if the module files are in ModuleCache or adding
1079 // them to the module cache. As even if the module files are in the module
1080 // cache, we still need to validate them. And it looks not helpful to add them
1081 // to the module cache, since we may always try to get the prebuilt module
1082 // files before building the module files by ourselves.
1083 for (auto &[ModuleName, ModuleFilePath] :
1084 CI->getHeaderSearchOpts().PrebuiltModuleFiles) {
1085 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1086 continue;
1087
1088 // Convert relative path to absolute path based on the compilation directory
1089 llvm::SmallString<256> AbsoluteModuleFilePath;
1090 if (llvm::sys::path::is_relative(ModuleFilePath)) {
1091 AbsoluteModuleFilePath = Inputs.CompileCommand.Directory;
1092 llvm::sys::path::append(AbsoluteModuleFilePath, ModuleFilePath);
1093 } else
1094 AbsoluteModuleFilePath = ModuleFilePath;
1095
1096 if (IsModuleFileUpToDate(AbsoluteModuleFilePath, BuiltModuleFiles,
1097 TFS.view(std::nullopt))) {
1098 log("Reusing prebuilt module file {0} of module {1} for {2}",
1099 AbsoluteModuleFilePath, ModuleName, ModuleUnitFileName);
1100 BuiltModuleFiles.addModuleFile(
1101 PrebuiltModuleFile::make(ModuleName, AbsoluteModuleFilePath));
1102 }
1103 }
1104}
1105
1107 PathRef RequiredSource, StringRef ModuleName, const ThreadsafeFS &TFS,
1108 CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles) {
1109 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1110 return llvm::Error::success();
1111
1112 std::string ModuleUnitFileName =
1113 MDB.getSourceForModuleName(ModuleName, RequiredSource);
1114 /// It is possible that we're meeting third party modules (modules whose
1115 /// source are not in the project. e.g, the std module may be a third-party
1116 /// module for most project) or something wrong with the implementation of
1117 /// ProjectModules.
1118 /// FIXME: How should we treat third party modules here? If we want to ignore
1119 /// third party modules, we should return true instead of false here.
1120 /// Currently we simply bail out.
1121 if (ModuleUnitFileName.empty())
1122 return llvm::createStringError(
1123 llvm::formatv("Don't get the module unit for module {0}", ModuleName));
1124
1125 /// Try to get prebuilt module files from the compilation database first. This
1126 /// helps to avoid building the module files that are already built by the
1127 /// compiler.
1128 getPrebuiltModuleFile(ModuleName, ModuleUnitFileName, TFS, BuiltModuleFiles);
1129
1130 // Get Required modules in topological order.
1131 auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName);
1132 for (llvm::StringRef ReqModuleName : ReqModuleNames) {
1133 if (BuiltModuleFiles.isModuleUnitBuilt(ReqModuleName))
1134 continue;
1135
1136 std::string ReqFileName =
1137 MDB.getSourceForModuleName(ReqModuleName, RequiredSource);
1138 auto Cmd = getCDB().getCompileCommand(ReqFileName);
1139 if (!Cmd)
1140 return llvm::createStringError(
1141 llvm::formatv("No compile command for {0}", ReqFileName));
1142 if (auto PI = getCDB().getProjectInfo(ReqFileName);
1143 PI && !PI->SourceRoot.empty())
1144 garbageCollectModuleCacheForProjectRoot(PI->SourceRoot);
1145
1146 const std::string CommandHash = getCompileCommandStringHash(*Cmd);
1147 const std::string PublishedModuleFilePath = getPublishedModuleFilePath(
1148 ReqModuleName, getModuleFilesDirectory(ReqFileName, *Cmd, getCDB()));
1149
1150 // Keep the source-scoped lock while probing and validating cached BMIs so
1151 // stale-file replacement and final publication stay serialized.
1152 auto SourceLock = ScopedModuleSourceLock::acquire(ReqFileName, getCDB());
1153 if (!SourceLock)
1154 return SourceLock.takeError();
1155
1156 std::shared_ptr<const ModuleFile> Cached =
1157 Cache.getModule(ReqModuleName, ReqFileName, CommandHash);
1158
1159 if (Cached) {
1160 if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles,
1161 TFS.view(std::nullopt))) {
1162 log("Reusing module {0} from {1}", ReqModuleName,
1163 Cached->getModuleFilePath());
1164 BuiltModuleFiles.addModuleFile(std::move(Cached));
1165 continue;
1166 }
1167 Cache.remove(ReqModuleName, ReqFileName, CommandHash);
1168 }
1169
1170 if (llvm::sys::fs::exists(PublishedModuleFilePath)) {
1171 if (IsModuleFileUpToDate(PublishedModuleFilePath, BuiltModuleFiles,
1172 TFS.view(std::nullopt))) {
1173 log("Reusing persistent module {0} from {1}", ReqModuleName,
1174 PublishedModuleFilePath);
1175 auto Materialized =
1176 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1177 if (llvm::Error Err = Materialized.takeError())
1178 return Err;
1179 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1180 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1181 continue;
1182 }
1183
1184 // The persistent module file is stale. Remove it and build a new one.
1185 std::error_code EC = llvm::sys::fs::remove(PublishedModuleFilePath);
1186 if (EC)
1187 return llvm::createStringError(
1188 llvm::formatv("Failed to remove stale module file {0}: {1}",
1189 PublishedModuleFilePath, EC.message()));
1190 }
1191
1192 bool PublishedExistingModuleFile = false;
1193 llvm::Expected<std::shared_ptr<BuiltModuleFile>> MF = buildModuleFile(
1194 ReqModuleName, ReqFileName, std::move(*Cmd), PublishedModuleFilePath,
1195 TFS, BuiltModuleFiles, PublishedExistingModuleFile);
1196 if (llvm::Error Err = MF.takeError())
1197 return Err;
1198
1199 if (PublishedExistingModuleFile &&
1200 !IsModuleFileUpToDate(PublishedModuleFilePath, BuiltModuleFiles,
1201 TFS.view(std::nullopt))) {
1202 return llvm::createStringError(
1203 llvm::formatv("Published module file {0} is stale after lock wait",
1204 PublishedModuleFilePath));
1205 }
1206
1207 auto Materialized =
1208 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1209 if (llvm::Error Err = Materialized.takeError())
1210 return Err;
1211
1212 log("Built module {0} to {1}", ReqModuleName,
1213 (*Materialized)->getModuleFilePath());
1214 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1215 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1216 }
1217
1218 return llvm::Error::success();
1219}
1220
1222 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(File);
1223 if (!MDB)
1224 return false;
1225
1226 CachingProjectModules CachedMDB(std::move(MDB),
1227 Impl->getProjectModulesCache());
1228 return !CachedMDB.getRequiredModules(File).empty();
1229}
1230
1231std::unique_ptr<PrerequisiteModules>
1233 const ThreadsafeFS &TFS) {
1234 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(File);
1235 if (!MDB) {
1236 elog("Failed to get Project Modules information for {0}", File);
1237 return std::make_unique<FailedPrerequisiteModules>();
1238 }
1239 CachingProjectModules CachedMDB(std::move(MDB),
1240 Impl->getProjectModulesCache());
1241
1242 std::vector<std::string> RequiredModuleNames =
1243 CachedMDB.getRequiredModules(File);
1244 if (RequiredModuleNames.empty())
1245 return std::make_unique<ReusablePrerequisiteModules>();
1246
1247 auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
1248 for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
1249 // Return early if there is any error.
1250 if (llvm::Error Err = Impl->getOrBuildModuleFile(
1251 File, RequiredModuleName, TFS, CachedMDB, *RequiredModules.get())) {
1252 elog("Failed to build module {0}; due to {1}", RequiredModuleName,
1253 toString(std::move(Err)));
1254 return std::make_unique<FailedPrerequisiteModules>();
1255 }
1256 }
1257
1258 return std::move(RequiredModules);
1259}
1260
1262 Impl = std::make_unique<ModulesBuilderImpl>(CDB);
1263}
1264
1266
1267} // namespace clangd
1268} // namespace clang
Provides compilation arguments used for parsing C and C++ files.
const GlobalCompilationDatabase & getCDB() const
ModulesBuilderImpl(const GlobalCompilationDatabase &CDB)
llvm::Error getOrBuildModuleFile(PathRef RequiredSource, StringRef ModuleName, const ThreadsafeFS &TFS, CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles)
bool hasRequiredModules(PathRef File)
std::unique_ptr< PrerequisiteModules > buildPrerequisiteModulesFor(PathRef File, const ThreadsafeFS &TFS)
ModulesBuilder(const GlobalCompilationDatabase &CDB)
Store all the needed module files information to parse a single source file.
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > view(std::nullopt_t CWD) const
Obtain a vfs::FileSystem with an arbitrary initial working directory.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
Definition AST.cpp:44
std::string maybeCaseFoldPath(PathRef Path)
Definition Path.cpp:18
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
Definition Compiler.cpp:96
FileDigest digest(llvm::StringRef Content)
void vlog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:72
static const char * toString(OffsetEncoding OE)
std::unique_ptr< CompilerInstance > prepareCompilerInstance(std::unique_ptr< clang::CompilerInvocation > CI, const PrecompiledPreamble *Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, DiagnosticConsumer &DiagsClient)
Definition Compiler.cpp:131
void log(const char *Fmt, Ts &&... Vals)
Definition Logger.h:67
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition Path.h:29
std::string Path
A typedef to represent a file path.
Definition Path.h:26
void elog(const char *Fmt, Ts &&... Vals)
Definition Logger.h:61
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Information required to run clang, e.g. to parse AST or do code completion.
Definition Compiler.h:51
const ThreadsafeFS * TFS
Definition Compiler.h:53