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/ADT/StringSet.h"
20#include "llvm/Support/CommandLine.h"
21#include "llvm/Support/FileSystem.h"
22#include "llvm/Support/LockFileManager.h"
23#include "llvm/Support/Path.h"
24#include "llvm/Support/Process.h"
34llvm::cl::opt<bool> DebugModulesBuilder(
35 "debug-modules-builder",
36 llvm::cl::desc(
"Don't remove clangd's built module files for debugging. "
37 "Remember to remove them later after debugging."),
38 llvm::cl::init(
false));
40llvm::cl::opt<unsigned> VersionedModuleFileGCThresholdSeconds(
41 "modules-builder-versioned-gc-threshold-seconds",
42 llvm::cl::desc(
"Delete versioned copy-on-read module files whose last "
43 "access time is older than this many seconds."),
44 llvm::cl::init(3 * 24 * 60 * 60));
67std::string hashStringForCache(llvm::StringRef Content) {
68 return llvm::toHex(
digest(Content));
72 llvm::SmallString<256> Normalized(
Path);
73 llvm::sys::path::remove_dots(Normalized,
true);
82getModuleCacheRoot(
PathRef ModuleUnitFileName,
84 llvm::SmallString<256> Result;
85 if (
auto PI = CDB.getProjectInfo(ModuleUnitFileName);
86 PI && !PI->SourceRoot.empty()) {
87 Result = PI->SourceRoot;
88 llvm::sys::path::append(Result,
".cache",
"clangd",
"modules");
92 if (llvm::sys::path::cache_directory(Result)) {
93 llvm::sys::path::append(Result,
"clangd",
"modules");
97 llvm::sys::path::system_temp_directory(
false, Result);
98 llvm::sys::path::append(Result,
"clangd",
"modules");
105llvm::SmallString<256>
106getModuleCacheLocksDirectory(
PathRef ModuleUnitFileName,
108 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
109 llvm::sys::path::append(Result,
".locks");
113std::string getModuleUnitSourcePathHash(
PathRef ModuleUnitFileName) {
114 return hashStringForCache(normalizePathForCache(ModuleUnitFileName));
117std::string getModuleUnitSourceDirectoryName(
PathRef ModuleUnitFileName) {
118 std::string Result = llvm::sys::path::filename(ModuleUnitFileName).str();
119 Result.push_back(
'-');
120 Result.append(getModuleUnitSourcePathHash(ModuleUnitFileName));
124std::string getCompileCommandStringHash(
const tooling::CompileCommand &Cmd) {
125 std::string SerializedCommand;
126 SerializedCommand.reserve(Cmd.Directory.size() + Cmd.Filename.size() +
127 Cmd.CommandLine.size() * 16);
131 SerializedCommand.append(Cmd.Directory);
132 SerializedCommand.push_back(
'\0');
133 for (
const auto &Arg : Cmd.CommandLine) {
134 SerializedCommand.append(Arg);
135 SerializedCommand.push_back(
'\0');
137 return hashStringForCache(SerializedCommand);
144llvm::SmallString<256>
145getModuleFilesDirectory(
PathRef ModuleUnitFileName,
146 const tooling::CompileCommand &Cmd,
148 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
149 llvm::sys::path::append(Result,
150 getModuleUnitSourceDirectoryName(ModuleUnitFileName),
151 getCompileCommandStringHash(Cmd));
157llvm::SmallString<256>
158getModuleSourceHashLockPath(
PathRef ModuleUnitFileName,
160 llvm::SmallString<256> Result =
161 getModuleCacheLocksDirectory(ModuleUnitFileName, CDB);
162 llvm::sys::path::append(Result,
163 getModuleUnitSourcePathHash(ModuleUnitFileName));
169llvm::SmallString<256> getTemporaryModuleFilePath(
PathRef ModuleFilePath) {
170 llvm::SmallString<256> ResultPattern(ModuleFilePath);
171 ResultPattern.append(
".tmp-%%-%%-%%-%%-%%-%%");
172 llvm::SmallString<256> Result;
173 llvm::sys::fs::createUniquePath(ResultPattern, Result,
178std::string getModuleFileVersionTimestamp() {
179 const auto Now = std::chrono::system_clock::now();
180 const auto Micros = std::chrono::duration_cast<std::chrono::microseconds>(
181 Now.time_since_epoch()) %
182 std::chrono::seconds(1);
183 const std::time_t CalendarTime = std::chrono::system_clock::to_time_t(Now);
186 localtime_s(&LocalTime, &CalendarTime);
188 localtime_r(&CalendarTime, &LocalTime);
191 return llvm::formatv(
"{0:04}{1:02}{2:02}-{3:02}{4:02}{5:02}-{6:06}",
192 LocalTime.tm_year + 1900, LocalTime.tm_mon + 1,
193 LocalTime.tm_mday, LocalTime.tm_hour, LocalTime.tm_min,
194 LocalTime.tm_sec, Micros.count())
198llvm::SmallString<256>
199getCopyOnReadModuleFilePath(
PathRef PublishedModuleFile) {
200 llvm::SmallString<256> Result(PublishedModuleFile);
201 llvm::sys::path::remove_filename(Result);
202 llvm::sys::path::append(
204 llvm::formatv(
"{0}-{1}{2}", llvm::sys::path::stem(PublishedModuleFile),
205 getModuleFileVersionTimestamp(),
206 llvm::sys::path::extension(PublishedModuleFile))
213llvm::Error ensureLockAnchorFileExists(
PathRef LockPath) {
214 llvm::SmallString<256> LockParent(LockPath);
215 llvm::sys::path::remove_filename(LockParent);
216 if (std::error_code EC = llvm::sys::fs::create_directories(LockParent))
217 return llvm::createStringError(llvm::formatv(
218 "Failed to create lock directory {0}: {1}", LockParent, EC.message()));
221 if (std::error_code EC = llvm::sys::fs::openFileForWrite(
222 LockPath, FD, llvm::sys::fs::CD_OpenAlways))
223 return llvm::createStringError(llvm::formatv(
224 "Failed to open lock file anchor {0}: {1}", LockPath, EC.message()));
225 llvm::sys::Process::SafelyCloseFileDescriptor(FD);
226 return llvm::Error::success();
249class ScopedModuleSourceLock {
251 static llvm::Expected<ScopedModuleSourceLock>
252 acquire(
PathRef ModuleUnitFileName,
const GlobalCompilationDatabase &CDB) {
253 constexpr auto LockWaitInterval = std::chrono::seconds(10);
254 llvm::SmallString<256> LockPath =
255 getModuleSourceHashLockPath(ModuleUnitFileName, CDB);
256 if (llvm::Error Err = ensureLockAnchorFileExists(LockPath))
257 return std::move(Err);
259 auto Waited = std::chrono::seconds::zero();
262 auto Lock = std::make_unique<llvm::LockFileManager>(LockPath);
263 auto TryLock = Lock->tryLock();
265 return TryLock.takeError();
267 return ScopedModuleSourceLock(std::move(Lock));
269 switch (Lock->waitForUnlockFor(LockWaitInterval)) {
270 case llvm::WaitForUnlockResult::Success:
271 case llvm::WaitForUnlockResult::OwnerDied:
273 case llvm::WaitForUnlockResult::Timeout:
274 Waited += LockWaitInterval;
275 log(
"Still waiting for module lock {0} after {1}s", LockPath,
279 llvm_unreachable(
"Unhandled lock wait result");
284 explicit ScopedModuleSourceLock(std::unique_ptr<llvm::LockFileManager> Lock)
285 : Lock(std::move(Lock)) {}
287 std::unique_ptr<llvm::LockFileManager> Lock;
291std::string getModuleFilePath(llvm::StringRef ModuleName,
293 llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
294 auto [PrimaryModuleName, PartitionName] = ModuleName.split(
':');
295 llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
296 if (!PartitionName.empty()) {
297 ModuleFilePath.append(
"-");
298 ModuleFilePath.append(PartitionName);
301 ModuleFilePath.append(
".pcm");
302 return std::string(ModuleFilePath);
305std::string getPublishedModuleFilePath(llvm::StringRef ModuleName,
307 return getModuleFilePath(ModuleName, ModuleFilesPrefix);
314 ~FailedPrerequisiteModules()
override =
default;
318 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {}
322 canReuse(
const CompilerInvocation &CI,
323 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override {
327 llvm::StringSet<> getRequiredModuleNames()
const override {
return {}; }
333 ModuleFile(StringRef ModuleName,
PathRef ModuleFilePath)
334 : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
337 ModuleFile() =
delete;
339 ModuleFile(
const ModuleFile &) =
delete;
340 ModuleFile operator=(
const ModuleFile &) =
delete;
343 ModuleFile(ModuleFile &&Other)
344 : ModuleName(std::move(Other.ModuleName)),
345 ModuleFilePath(std::move(Other.ModuleFilePath)) {
346 Other.ModuleName.clear();
347 Other.ModuleFilePath.clear();
350 ModuleFile &operator=(ModuleFile &&Other) {
355 new (
this) ModuleFile(std::move(Other));
358 virtual ~ModuleFile() =
default;
360 StringRef getModuleName()
const {
return ModuleName; }
362 StringRef getModuleFilePath()
const {
return ModuleFilePath; }
365 std::string ModuleName;
366 std::string ModuleFilePath;
370class PrebuiltModuleFile :
public ModuleFile {
377 PrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
378 : ModuleFile(ModuleName, ModuleFilePath) {}
380 static std::shared_ptr<PrebuiltModuleFile> make(StringRef ModuleName,
382 return std::make_shared<PrebuiltModuleFile>(ModuleName, ModuleFilePath,
420class BuiltModuleFile final :
public ModuleFile {
427 BuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
428 : ModuleFile(ModuleName, ModuleFilePath) {}
430 static std::shared_ptr<BuiltModuleFile> make(StringRef ModuleName,
432 return std::make_shared<BuiltModuleFile>(ModuleName, ModuleFilePath,
439class CopyOnReadModuleFile final :
public ModuleFile {
444 CopyOnReadModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
445 : ModuleFile(ModuleName, ModuleFilePath) {}
447 ~CopyOnReadModuleFile()
override {
448 if (!ModuleFilePath.empty() && !DebugModulesBuilder)
449 if (std::error_code EC = llvm::sys::fs::remove(ModuleFilePath))
450 vlog(
"Failed to remove copy-on-read module file {0}: {1}",
451 ModuleFilePath, EC.message());
454 static std::shared_ptr<CopyOnReadModuleFile> make(StringRef ModuleName,
456 return std::make_shared<CopyOnReadModuleFile>(ModuleName, ModuleFilePath,
466 ReusablePrerequisiteModules() =
default;
468 ReusablePrerequisiteModules(
const ReusablePrerequisiteModules &Other) =
470 ReusablePrerequisiteModules &
471 operator=(
const ReusablePrerequisiteModules &) =
default;
472 ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) =
delete;
473 ReusablePrerequisiteModules
474 operator=(ReusablePrerequisiteModules &&) =
delete;
476 ~ReusablePrerequisiteModules()
override =
default;
478 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {
480 for (
const auto &RequiredModule : RequiredModules)
481 Options.PrebuiltModuleFiles.insert_or_assign(
482 RequiredModule->getModuleName().str(),
483 RequiredModule->getModuleFilePath().str());
486 std::string getAsString()
const {
488 llvm::raw_string_ostream OS(Result);
489 for (
const auto &MF : RequiredModules) {
490 OS <<
"-fmodule-file=" << MF->getModuleName() <<
"="
491 << MF->getModuleFilePath() <<
" ";
496 bool canReuse(
const CompilerInvocation &CI,
497 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override;
499 bool isModuleUnitBuilt(llvm::StringRef ModuleName)
const {
500 return BuiltModuleNames.contains(ModuleName);
503 void addModuleFile(std::shared_ptr<const ModuleFile> MF) {
504 BuiltModuleNames.insert(MF->getModuleName());
505 RequiredModules.emplace_back(std::move(MF));
508 void setDirectModuleNames(std::vector<std::string> Names) {
509 DirectModuleNames.insert_range(Names);
512 llvm::StringSet<> getRequiredModuleNames()
const override {
513 return DirectModuleNames;
517 llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
519 llvm::StringSet<> BuiltModuleNames;
521 llvm::StringSet<> DirectModuleNames;
524bool IsModuleFileUpToDate(
PathRef ModuleFilePath,
526 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
527 HeaderSearchOptions HSOpts;
528 RequisiteModules.adjustHeaderSearchOptions(HSOpts);
529 HSOpts.ForceCheckCXX20ModulesInputFiles =
true;
530 HSOpts.ValidateASTInputFilesContent =
true;
532 clang::clangd::IgnoreDiagnostics IgnoreDiags;
533 DiagnosticOptions DiagOpts;
534 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
535 CompilerInstance::createDiagnostics(*VFS, DiagOpts, &IgnoreDiags,
538 LangOptions LangOpts;
539 LangOpts.SkipODRCheckInGMF =
true;
541 FileManager FileMgr(FileSystemOptions(), VFS);
543 SourceManager SourceMgr(*Diags, FileMgr);
545 HeaderSearch HeaderInfo(HSOpts, SourceMgr, *Diags, LangOpts,
548 PreprocessorOptions PPOpts;
549 TrivialModuleLoader ModuleLoader;
550 Preprocessor PP(PPOpts, *Diags, LangOpts, SourceMgr, HeaderInfo,
553 std::shared_ptr<ModuleCache> ModCache = createCrossProcessModuleCache();
554 PCHContainerOperations PCHOperations;
555 CodeGenOptions CodeGenOpts;
557 PP, *ModCache,
nullptr, PCHOperations.getRawReader(),
560 DisableValidationForModuleKind::None,
569 Reader.setListener(
nullptr);
576 return Reader.ReadAST(ModuleFileName::makeExplicit(ModuleFilePath),
577 serialization::MK_MainFile, SourceLocation(),
578 ASTReader::ARR_OutOfDate) == ASTReader::Success;
581bool IsModuleFilesUpToDate(
582 llvm::SmallVector<PathRef> ModuleFilePaths,
584 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
586 ModuleFilePaths, [&RequisiteModules, VFS](
auto ModuleFilePath) {
587 return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
594llvm::Expected<std::shared_ptr<BuiltModuleFile>>
595buildModuleFile(llvm::StringRef ModuleName,
PathRef ModuleUnitFileName,
596 tooling::CompileCommand Cmd,
PathRef ModuleFilePath,
598 const ReusablePrerequisiteModules &BuiltModuleFiles,
599 bool &PublishedExistingModuleFile) {
600 PublishedExistingModuleFile =
false;
601 llvm::SmallString<256> ModuleFilesPrefix(ModuleFilePath);
602 llvm::sys::path::remove_filename(ModuleFilesPrefix);
603 if (std::error_code EC = llvm::sys::fs::create_directories(ModuleFilesPrefix))
604 return llvm::createStringError(
605 llvm::formatv(
"Failed to create module cache directory {0}: {1}",
606 ModuleFilesPrefix, EC.message()));
608 llvm::SmallString<256> TemporaryModuleFilePath =
609 getTemporaryModuleFilePath(ModuleFilePath);
610 auto RemoveTemporaryModuleFile = llvm::scope_exit([&] {
611 if (!TemporaryModuleFilePath.empty() && !DebugModulesBuilder)
612 llvm::sys::fs::remove(TemporaryModuleFilePath);
614 (void)RemoveTemporaryModuleFile;
616 Cmd.Output = TemporaryModuleFilePath.str().str();
620 Inputs.CompileCommand = std::move(Cmd);
625 return llvm::createStringError(
"Failed to build compiler invocation");
627 auto FS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
628 auto Buf = FS->getBufferForFile(Inputs.CompileCommand.Filename);
630 return llvm::createStringError(
"Failed to create buffer");
634 CI->getLangOpts().SkipODRCheckInGMF =
true;
639 CI->getHeaderSearchOpts().ValidateASTInputFilesContent =
true;
641 BuiltModuleFiles.adjustHeaderSearchOptions(CI->getHeaderSearchOpts());
643 CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
646 std::move(*Buf), std::move(FS), IgnoreDiags);
648 return llvm::createStringError(
"Failed to prepare compiler instance");
650 GenerateReducedModuleInterfaceAction Action;
651 Clang->ExecuteAction(Action);
653 if (Clang->getDiagnostics().hasErrorOccurred()) {
655 for (
const auto &Arg : Inputs.CompileCommand.CommandLine) {
661 clangd::vlog(
"Failed to compile {0} with command: {1}", ModuleUnitFileName,
664 std::string BuiltModuleFilesStr = BuiltModuleFiles.getAsString();
665 if (!BuiltModuleFilesStr.empty())
666 clangd::vlog(
"The actual used module files built by clangd is {0}",
667 BuiltModuleFilesStr);
669 return llvm::createStringError(
670 llvm::formatv(
"Failed to compile {0}. Use '--log=verbose' to view "
671 "detailed failure reasons. It is helpful to use "
672 "'--debug-modules-builder' flag to keep the clangd's "
673 "built module files to reproduce the failure for "
674 "debugging. Remember to remove them after debugging.",
675 ModuleUnitFileName));
678 if (std::error_code EC =
679 llvm::sys::fs::rename(TemporaryModuleFilePath, ModuleFilePath)) {
680 if (!llvm::sys::fs::exists(ModuleFilePath))
681 return llvm::createStringError(
682 llvm::formatv(
"Failed to publish module file {0}: {1}",
683 ModuleFilePath, EC.message()));
686 PublishedExistingModuleFile =
true;
690 TemporaryModuleFilePath.clear();
693 return BuiltModuleFile::make(ModuleName, ModuleFilePath);
696llvm::Expected<std::shared_ptr<CopyOnReadModuleFile>>
697copyModuleFileForRead(llvm::StringRef ModuleName,
698 PathRef PublishedModuleFilePath) {
699 llvm::SmallString<256> VersionedModuleFilePath =
700 getCopyOnReadModuleFilePath(PublishedModuleFilePath);
701 if (std::error_code EC = llvm::sys::fs::copy_file(PublishedModuleFilePath,
702 VersionedModuleFilePath))
703 return llvm::createStringError(llvm::formatv(
704 "Failed to copy module file {0} to {1}: {2}", PublishedModuleFilePath,
705 VersionedModuleFilePath, EC.message()));
706 return CopyOnReadModuleFile::make(ModuleName, VersionedModuleFilePath);
709bool ReusablePrerequisiteModules::canReuse(
710 const CompilerInvocation &CI,
711 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
const {
712 if (RequiredModules.empty())
715 llvm::SmallVector<llvm::StringRef> BMIPaths;
716 for (
auto &MF : RequiredModules)
717 BMIPaths.push_back(MF->getModuleFilePath());
718 return IsModuleFilesUpToDate(BMIPaths, *
this, VFS);
744class ModuleFileCache {
746 ModuleFileCache(
const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
747 const GlobalCompilationDatabase &getCDB()
const {
return CDB; }
749 std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName,
750 PathRef ModuleUnitSource,
751 llvm::StringRef CommandHash);
753 void add(StringRef ModuleName, PathRef ModuleUnitSource,
754 llvm::StringRef CommandHash,
755 std::shared_ptr<const ModuleFile> ModuleFile) {
756 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
757 ModuleFiles[cacheKey(ModuleName, ModuleUnitSource, CommandHash)] =
761 void remove(StringRef ModuleName, PathRef ModuleUnitSource,
762 llvm::StringRef CommandHash);
765 static std::string cacheKey(StringRef ModuleName, PathRef ModuleUnitSource,
766 llvm::StringRef CommandHash) {
768 Key.reserve(ModuleName.size() + ModuleUnitSource.size() +
769 CommandHash.size() + 2);
770 Key.append(ModuleName);
774 Key.append(CommandHash);
778 const GlobalCompilationDatabase &CDB;
780 llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
781 std::mutex ModuleFilesMutex;
784std::shared_ptr<const ModuleFile>
785ModuleFileCache::getModule(StringRef ModuleName,
PathRef ModuleUnitSource,
786 llvm::StringRef CommandHash) {
787 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
790 ModuleFiles.find(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
791 if (Iter == ModuleFiles.end())
794 if (
auto Res = Iter->second.lock())
797 ModuleFiles.erase(Iter);
801void ModuleFileCache::remove(StringRef ModuleName,
PathRef ModuleUnitSource,
802 llvm::StringRef CommandHash) {
803 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
804 ModuleFiles.erase(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
807class ModuleNameToSourceCache {
809 std::string getUniqueSourceForModuleName(llvm::StringRef ModuleName) {
810 std::lock_guard<std::mutex> Lock(CacheMutex);
811 auto Iter = ModuleNameToUniqueSourceCache.find(ModuleName);
812 if (Iter != ModuleNameToUniqueSourceCache.end())
817 void addUniqueEntry(llvm::StringRef ModuleName, PathRef Source) {
818 std::lock_guard<std::mutex> Lock(CacheMutex);
819 ModuleNameToUniqueSourceCache[ModuleName] = Source.str();
822 void eraseUniqueEntry(llvm::StringRef ModuleName) {
823 std::lock_guard<std::mutex> Lock(CacheMutex);
824 ModuleNameToUniqueSourceCache.erase(ModuleName);
827 std::string getMultipleSourceForModuleName(llvm::StringRef ModuleName,
828 PathRef RequiredSrcFile) {
829 std::lock_guard<std::mutex> Lock(CacheMutex);
830 auto Outer = ModuleNameToMultipleSourceCache.find(ModuleName);
831 if (Outer == ModuleNameToMultipleSourceCache.end())
834 if (Inner == Outer->second.end())
836 return Inner->second;
839 void addMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile,
841 std::lock_guard<std::mutex> Lock(CacheMutex);
842 ModuleNameToMultipleSourceCache[ModuleName]
847 void eraseMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile) {
848 std::lock_guard<std::mutex> Lock(CacheMutex);
849 auto Outer = ModuleNameToMultipleSourceCache.find(ModuleName);
850 if (Outer == ModuleNameToMultipleSourceCache.end())
853 if (Outer->second.empty())
854 ModuleNameToMultipleSourceCache.erase(Outer);
858 std::mutex CacheMutex;
859 llvm::StringMap<std::string> ModuleNameToUniqueSourceCache;
865 llvm::StringMap<llvm::StringMap<std::string>> ModuleNameToMultipleSourceCache;
868class CachingProjectModules :
public ProjectModules {
870 CachingProjectModules(std::unique_ptr<ProjectModules> MDB,
871 ModuleNameToSourceCache &Cache)
872 : MDB(std::move(MDB)), Cache(Cache) {
873 assert(this->MDB &&
"CachingProjectModules should only be created with a "
874 "valid underlying ProjectModules");
877 std::vector<std::string> getRequiredModules(PathRef File)
override {
878 return MDB->getRequiredModules(File);
881 std::string getModuleNameForSource(PathRef File)
override {
882 return MDB->getModuleNameForSource(File);
885 ModuleNameState getModuleNameState(llvm::StringRef ModuleName)
override {
886 return MDB->getModuleNameState(ModuleName);
889 std::string getSourceForModuleName(llvm::StringRef ModuleName,
890 PathRef RequiredSrcFile)
override {
891 auto ModuleState = MDB->getModuleNameState(ModuleName);
893 if (ModuleState == ModuleNameState::Multiple) {
894 std::string CachedResult =
895 Cache.getMultipleSourceForModuleName(ModuleName, RequiredSrcFile);
899 if (!CachedResult.empty()) {
900 std::string ModuleNameOfCachedSource =
901 MDB->getModuleNameForSource(CachedResult);
902 if (ModuleNameOfCachedSource == ModuleName)
906 Cache.eraseMultipleEntry(ModuleName, RequiredSrcFile);
909 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
911 Cache.addMultipleEntry(ModuleName, RequiredSrcFile, Result);
917 assert(ModuleState == ModuleNameState::Unique ||
918 ModuleState == ModuleNameState::Unknown);
919 std::string CachedResult = Cache.getUniqueSourceForModuleName(ModuleName);
923 if (!CachedResult.empty()) {
924 std::string ModuleNameOfCachedSource =
925 MDB->getModuleNameForSource(CachedResult);
926 if (ModuleNameOfCachedSource == ModuleName)
930 Cache.eraseUniqueEntry(ModuleName);
933 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
935 Cache.addUniqueEntry(ModuleName, Result);
941 std::unique_ptr<ProjectModules> MDB;
942 ModuleNameToSourceCache &Cache;
948llvm::SmallVector<std::string> getAllRequiredModules(
PathRef RequiredSource,
949 CachingProjectModules &MDB,
950 StringRef ModuleName) {
951 llvm::SmallVector<std::string> ModuleNames;
952 llvm::StringSet<> ModuleNamesSet;
954 auto VisitDeps = [&](StringRef ModuleName,
auto Visitor) ->
void {
955 ModuleNamesSet.insert(ModuleName);
957 for (StringRef RequiredModuleName : MDB.getRequiredModules(
958 MDB.getSourceForModuleName(ModuleName, RequiredSource)))
959 if (ModuleNamesSet.insert(RequiredModuleName).second)
960 Visitor(RequiredModuleName, Visitor);
962 ModuleNames.push_back(ModuleName.str());
964 VisitDeps(ModuleName, VisitDeps);
971std::vector<std::string> collectModuleFiles(
PathRef CacheRoot) {
972 std::vector<std::string> Result;
974 for (llvm::sys::fs::recursive_directory_iterator It(CacheRoot, EC), End;
975 It != End && !EC; It.increment(EC)) {
976 if (llvm::sys::path::extension(It->path()) !=
".pcm")
978 Result.push_back(It->path());
981 log(
"Failed to scan module cache directory {0}: {1}", CacheRoot,
987void garbageCollectModuleCache(
PathRef CacheRoot) {
988 for (
const auto &ModuleFilePath : collectModuleFiles(CacheRoot)) {
989 llvm::sys::fs::file_status Status;
990 if (std::error_code EC = llvm::sys::fs::status(ModuleFilePath, Status)) {
991 log(
"Failed to stat cached module file {0} for GC: {1}", ModuleFilePath,
996 llvm::sys::TimePoint<> LastAccess = Status.getLastAccessedTime();
997 llvm::sys::TimePoint<> Now = std::chrono::system_clock::now();
998 if (LastAccess > Now)
1001 std::chrono::duration_cast<std::chrono::seconds>(Now - LastAccess);
1003 std::chrono::seconds(VersionedModuleFileGCThresholdSeconds);
1004 if (Age <= Threshold)
1007 if (!llvm::sys::fs::exists(ModuleFilePath))
1010 constexpr llvm::StringLiteral Reason =
"file older than GC threshold";
1011 if (std::error_code EC = llvm::sys::fs::remove(ModuleFilePath)) {
1012 log(
"Failed to remove cached module file {0} ({1}): {2}", ModuleFilePath,
1013 Reason, EC.message());
1016 log(
"Removed cached module file {0} ({1})", ModuleFilePath, Reason);
1027 return ProjectModulesCache;
1032 getOrBuildModuleFile(
PathRef RequiredSource, StringRef ModuleName,
1034 ReusablePrerequisiteModules &BuiltModuleFiles);
1038 void getPrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleUnitFileName,
1040 ReusablePrerequisiteModules &BuiltModuleFiles);
1043 void garbageCollectModuleCacheForProjectRoot(
PathRef ProjectRoot);
1045 ModuleFileCache Cache;
1046 ModuleNameToSourceCache ProjectModulesCache;
1047 std::mutex GarbageCollectedProjectRootsMutex;
1048 llvm::StringSet<> GarbageCollectedProjectRoots;
1051void ModulesBuilder::ModulesBuilderImpl::
1052 garbageCollectModuleCacheForProjectRoot(
PathRef ProjectRoot) {
1053 if (ProjectRoot.empty())
1055 std::string NormalizedProjectRoot = normalizePathForCache(ProjectRoot);
1059 std::lock_guard<std::mutex> Lock(GarbageCollectedProjectRootsMutex);
1060 if (!GarbageCollectedProjectRoots.insert(NormalizedProjectRoot).second)
1064 llvm::SmallString<256> CacheRoot(ProjectRoot);
1065 llvm::sys::path::append(CacheRoot,
".cache",
"clangd",
"modules");
1066 log(
"Running GC pass for clangd built module files under {0} with age "
1067 "threshold {1} seconds (adjust with --modules-builder-versioned-gc-"
1068 "threshold-seconds)",
1069 CacheRoot, VersionedModuleFileGCThresholdSeconds);
1070 garbageCollectModuleCache(CacheRoot);
1071 log(
"Done running GC pass for clangd built module files under {0}",
1075void ModulesBuilder::ModulesBuilderImpl::getPrebuiltModuleFile(
1076 StringRef ModuleName,
PathRef ModuleUnitFileName,
const ThreadsafeFS &TFS,
1077 ReusablePrerequisiteModules &BuiltModuleFiles) {
1078 auto Cmd = getCDB().getCompileCommand(ModuleUnitFileName);
1084 Inputs.CompileCommand = std::move(*Cmd);
1086 IgnoreDiagnostics IgnoreDiags;
1096 for (
auto &[ModuleName, ModuleFilePath] :
1097 CI->getHeaderSearchOpts().PrebuiltModuleFiles) {
1098 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1102 llvm::SmallString<256> AbsoluteModuleFilePath;
1103 if (llvm::sys::path::is_relative(ModuleFilePath)) {
1104 AbsoluteModuleFilePath = Inputs.CompileCommand.Directory;
1105 llvm::sys::path::append(AbsoluteModuleFilePath, ModuleFilePath);
1107 AbsoluteModuleFilePath = ModuleFilePath;
1109 if (IsModuleFileUpToDate(AbsoluteModuleFilePath, BuiltModuleFiles,
1110 TFS.view(std::nullopt))) {
1111 log(
"Reusing prebuilt module file {0} of module {1} for {2}",
1112 AbsoluteModuleFilePath, ModuleName, ModuleUnitFileName);
1113 BuiltModuleFiles.addModuleFile(
1114 PrebuiltModuleFile::make(ModuleName, AbsoluteModuleFilePath));
1121 CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles) {
1122 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1123 return llvm::Error::success();
1125 std::string ModuleUnitFileName =
1126 MDB.getSourceForModuleName(ModuleName, RequiredSource);
1134 if (ModuleUnitFileName.empty())
1135 return llvm::createStringError(
1136 llvm::formatv(
"Don't get the module unit for module {0}", ModuleName));
1141 getPrebuiltModuleFile(ModuleName, ModuleUnitFileName, TFS, BuiltModuleFiles);
1144 auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName);
1145 for (llvm::StringRef ReqModuleName : ReqModuleNames) {
1146 if (BuiltModuleFiles.isModuleUnitBuilt(ReqModuleName))
1149 std::string ReqFileName =
1150 MDB.getSourceForModuleName(ReqModuleName, RequiredSource);
1151 auto Cmd =
getCDB().getCompileCommand(ReqFileName);
1153 return llvm::createStringError(
1154 llvm::formatv(
"No compile command for {0}", ReqFileName));
1155 if (
auto PI =
getCDB().getProjectInfo(ReqFileName);
1156 PI && !PI->SourceRoot.empty())
1157 garbageCollectModuleCacheForProjectRoot(PI->SourceRoot);
1159 const std::string CommandHash = getCompileCommandStringHash(*Cmd);
1160 const std::string PublishedModuleFilePath = getPublishedModuleFilePath(
1161 ReqModuleName, getModuleFilesDirectory(ReqFileName, *Cmd,
getCDB()));
1165 auto SourceLock = ScopedModuleSourceLock::acquire(ReqFileName,
getCDB());
1167 return SourceLock.takeError();
1169 std::shared_ptr<const ModuleFile> Cached =
1170 Cache.getModule(ReqModuleName, ReqFileName, CommandHash);
1173 if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles,
1174 TFS.
view(std::nullopt))) {
1175 log(
"Reusing module {0} from {1}", ReqModuleName,
1176 Cached->getModuleFilePath());
1177 BuiltModuleFiles.addModuleFile(std::move(Cached));
1180 Cache.remove(ReqModuleName, ReqFileName, CommandHash);
1183 if (llvm::sys::fs::exists(PublishedModuleFilePath)) {
1184 if (IsModuleFileUpToDate(PublishedModuleFilePath, BuiltModuleFiles,
1185 TFS.
view(std::nullopt))) {
1186 log(
"Reusing persistent module {0} from {1}", ReqModuleName,
1187 PublishedModuleFilePath);
1189 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1190 if (llvm::Error Err = Materialized.takeError())
1192 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1193 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1198 std::error_code EC = llvm::sys::fs::remove(PublishedModuleFilePath);
1200 return llvm::createStringError(
1201 llvm::formatv(
"Failed to remove stale module file {0}: {1}",
1202 PublishedModuleFilePath, EC.message()));
1205 bool PublishedExistingModuleFile =
false;
1206 llvm::Expected<std::shared_ptr<BuiltModuleFile>> MF = buildModuleFile(
1207 ReqModuleName, ReqFileName, std::move(*Cmd), PublishedModuleFilePath,
1208 TFS, BuiltModuleFiles, PublishedExistingModuleFile);
1209 if (llvm::Error Err = MF.takeError())
1212 if (PublishedExistingModuleFile &&
1213 !IsModuleFileUpToDate(PublishedModuleFilePath, BuiltModuleFiles,
1214 TFS.
view(std::nullopt))) {
1215 return llvm::createStringError(
1216 llvm::formatv(
"Published module file {0} is stale after lock wait",
1217 PublishedModuleFilePath));
1221 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1222 if (llvm::Error Err = Materialized.takeError())
1225 log(
"Built module {0} to {1}", ReqModuleName,
1226 (*Materialized)->getModuleFilePath());
1227 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1228 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1231 return llvm::Error::success();
1235 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1239 CachingProjectModules CachedMDB(std::move(MDB),
1240 Impl->getProjectModulesCache());
1241 return !CachedMDB.getRequiredModules(
File).empty();
1245 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1249 CachingProjectModules CachedMDB(std::move(MDB),
1250 Impl->getProjectModulesCache());
1251 return CachedMDB.getRequiredModules(
File);
1254std::unique_ptr<PrerequisiteModules>
1257 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1259 elog(
"Failed to get Project Modules information for {0}",
File);
1260 return std::make_unique<FailedPrerequisiteModules>();
1262 CachingProjectModules CachedMDB(std::move(MDB),
1263 Impl->getProjectModulesCache());
1265 std::vector<std::string> RequiredModuleNames =
1266 CachedMDB.getRequiredModules(
File);
1267 if (RequiredModuleNames.empty())
1268 return std::make_unique<ReusablePrerequisiteModules>();
1270 auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
1271 RequiredModules->setDirectModuleNames(RequiredModuleNames);
1272 for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
1274 if (llvm::Error Err = Impl->getOrBuildModuleFile(
1275 File, RequiredModuleName, TFS, CachedMDB, *RequiredModules.get())) {
1276 elog(
"Failed to build module {0}; due to {1}", RequiredModuleName,
1278 return std::make_unique<FailedPrerequisiteModules>();
1282 return std::move(RequiredModules);
1286 Impl = std::make_unique<ModulesBuilderImpl>(CDB);
Provides compilation arguments used for parsing C and C++ files.
const GlobalCompilationDatabase & getCDB() const
ModulesBuilderImpl(const GlobalCompilationDatabase &CDB)
ModuleNameToSourceCache & getProjectModulesCache()
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)
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.
std::string maybeCaseFoldPath(PathRef Path)
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.
FileDigest digest(llvm::StringRef Content)
void vlog(const char *Fmt, Ts &&... Vals)
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)
void log(const char *Fmt, Ts &&... Vals)
llvm::StringRef PathRef
A typedef to represent a ref to file path.
std::string Path
A typedef to represent a file path.
void elog(const char *Fmt, Ts &&... Vals)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//