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