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"
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));
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));
66std::string hashStringForCache(llvm::StringRef Content) {
67 return llvm::toHex(
digest(Content));
71 llvm::SmallString<256> Normalized(
Path);
72 llvm::sys::path::remove_dots(Normalized,
true);
81getModuleCacheRoot(
PathRef ModuleUnitFileName,
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");
91 if (llvm::sys::path::cache_directory(Result)) {
92 llvm::sys::path::append(Result,
"clangd",
"modules");
96 llvm::sys::path::system_temp_directory(
false, Result);
97 llvm::sys::path::append(Result,
"clangd",
"modules");
104llvm::SmallString<256>
105getModuleCacheLocksDirectory(
PathRef ModuleUnitFileName,
107 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
108 llvm::sys::path::append(Result,
".locks");
112std::string getModuleUnitSourcePathHash(
PathRef ModuleUnitFileName) {
113 return hashStringForCache(normalizePathForCache(ModuleUnitFileName));
116std::string getModuleUnitSourceDirectoryName(
PathRef ModuleUnitFileName) {
117 std::string Result = llvm::sys::path::filename(ModuleUnitFileName).str();
118 Result.push_back(
'-');
119 Result.append(getModuleUnitSourcePathHash(ModuleUnitFileName));
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);
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');
136 return hashStringForCache(SerializedCommand);
143llvm::SmallString<256>
144getModuleFilesDirectory(
PathRef ModuleUnitFileName,
145 const tooling::CompileCommand &Cmd,
147 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
148 llvm::sys::path::append(Result,
149 getModuleUnitSourceDirectoryName(ModuleUnitFileName),
150 getCompileCommandStringHash(Cmd));
156llvm::SmallString<256>
157getModuleSourceHashLockPath(
PathRef ModuleUnitFileName,
159 llvm::SmallString<256> Result =
160 getModuleCacheLocksDirectory(ModuleUnitFileName, CDB);
161 llvm::sys::path::append(Result,
162 getModuleUnitSourcePathHash(ModuleUnitFileName));
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,
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);
185 localtime_s(&LocalTime, &CalendarTime);
187 localtime_r(&CalendarTime, &LocalTime);
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())
197llvm::SmallString<256>
198getCopyOnReadModuleFilePath(
PathRef PublishedModuleFile) {
199 llvm::SmallString<256> Result(PublishedModuleFile);
200 llvm::sys::path::remove_filename(Result);
201 llvm::sys::path::append(
203 llvm::formatv(
"{0}-{1}{2}", llvm::sys::path::stem(PublishedModuleFile),
204 getModuleFileVersionTimestamp(),
205 llvm::sys::path::extension(PublishedModuleFile))
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()));
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();
248class ScopedModuleSourceLock {
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);
258 auto Waited = std::chrono::seconds::zero();
261 auto Lock = std::make_unique<llvm::LockFileManager>(LockPath);
262 auto TryLock = Lock->tryLock();
264 return TryLock.takeError();
266 return ScopedModuleSourceLock(std::move(Lock));
268 switch (Lock->waitForUnlockFor(LockWaitInterval)) {
269 case llvm::WaitForUnlockResult::Success:
270 case llvm::WaitForUnlockResult::OwnerDied:
272 case llvm::WaitForUnlockResult::Timeout:
273 Waited += LockWaitInterval;
274 log(
"Still waiting for module lock {0} after {1}s", LockPath,
278 llvm_unreachable(
"Unhandled lock wait result");
283 explicit ScopedModuleSourceLock(std::unique_ptr<llvm::LockFileManager> Lock)
284 : Lock(std::move(Lock)) {}
286 std::unique_ptr<llvm::LockFileManager> Lock;
290std::string getModuleFilePath(llvm::StringRef ModuleName,
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);
300 ModuleFilePath.append(
".pcm");
301 return std::string(ModuleFilePath);
304std::string getPublishedModuleFilePath(llvm::StringRef ModuleName,
306 return getModuleFilePath(ModuleName, ModuleFilesPrefix);
313 ~FailedPrerequisiteModules()
override =
default;
317 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {}
321 canReuse(
const CompilerInvocation &CI,
322 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override {
330 ModuleFile(StringRef ModuleName,
PathRef ModuleFilePath)
331 : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
334 ModuleFile() =
delete;
336 ModuleFile(
const ModuleFile &) =
delete;
337 ModuleFile operator=(
const ModuleFile &) =
delete;
340 ModuleFile(ModuleFile &&Other)
341 : ModuleName(std::move(Other.ModuleName)),
342 ModuleFilePath(std::move(Other.ModuleFilePath)) {
343 Other.ModuleName.clear();
344 Other.ModuleFilePath.clear();
347 ModuleFile &operator=(ModuleFile &&Other) {
352 new (
this) ModuleFile(std::move(Other));
355 virtual ~ModuleFile() =
default;
357 StringRef getModuleName()
const {
return ModuleName; }
359 StringRef getModuleFilePath()
const {
return ModuleFilePath; }
362 std::string ModuleName;
363 std::string ModuleFilePath;
367class PrebuiltModuleFile :
public ModuleFile {
374 PrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
375 : ModuleFile(ModuleName, ModuleFilePath) {}
377 static std::shared_ptr<PrebuiltModuleFile> make(StringRef ModuleName,
379 return std::make_shared<PrebuiltModuleFile>(ModuleName, ModuleFilePath,
417class BuiltModuleFile final :
public ModuleFile {
424 BuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
425 : ModuleFile(ModuleName, ModuleFilePath) {}
427 static std::shared_ptr<BuiltModuleFile> make(StringRef ModuleName,
429 return std::make_shared<BuiltModuleFile>(ModuleName, ModuleFilePath,
436class CopyOnReadModuleFile final :
public ModuleFile {
441 CopyOnReadModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
442 : ModuleFile(ModuleName, ModuleFilePath) {}
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());
451 static std::shared_ptr<CopyOnReadModuleFile> make(StringRef ModuleName,
453 return std::make_shared<CopyOnReadModuleFile>(ModuleName, ModuleFilePath,
463 ReusablePrerequisiteModules() =
default;
465 ReusablePrerequisiteModules(
const ReusablePrerequisiteModules &Other) =
467 ReusablePrerequisiteModules &
468 operator=(
const ReusablePrerequisiteModules &) =
default;
469 ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) =
delete;
470 ReusablePrerequisiteModules
471 operator=(ReusablePrerequisiteModules &&) =
delete;
473 ~ReusablePrerequisiteModules()
override =
default;
475 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {
477 for (
const auto &RequiredModule : RequiredModules)
478 Options.PrebuiltModuleFiles.insert_or_assign(
479 RequiredModule->getModuleName().str(),
480 RequiredModule->getModuleFilePath().str());
483 std::string getAsString()
const {
485 llvm::raw_string_ostream OS(Result);
486 for (
const auto &MF : RequiredModules) {
487 OS <<
"-fmodule-file=" << MF->getModuleName() <<
"="
488 << MF->getModuleFilePath() <<
" ";
493 bool canReuse(
const CompilerInvocation &CI,
494 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override;
496 bool isModuleUnitBuilt(llvm::StringRef ModuleName)
const {
497 return BuiltModuleNames.contains(ModuleName);
500 void addModuleFile(std::shared_ptr<const ModuleFile> MF) {
501 BuiltModuleNames.insert(MF->getModuleName());
502 RequiredModules.emplace_back(std::move(MF));
506 llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
508 llvm::StringSet<> BuiltModuleNames;
511bool IsModuleFileUpToDate(
PathRef ModuleFilePath,
513 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
514 HeaderSearchOptions HSOpts;
515 RequisiteModules.adjustHeaderSearchOptions(HSOpts);
516 HSOpts.ForceCheckCXX20ModulesInputFiles =
true;
517 HSOpts.ValidateASTInputFilesContent =
true;
519 clang::clangd::IgnoreDiagnostics IgnoreDiags;
520 DiagnosticOptions DiagOpts;
521 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
522 CompilerInstance::createDiagnostics(*VFS, DiagOpts, &IgnoreDiags,
525 LangOptions LangOpts;
526 LangOpts.SkipODRCheckInGMF =
true;
528 FileManager FileMgr(FileSystemOptions(), VFS);
530 SourceManager SourceMgr(*Diags, FileMgr);
532 HeaderSearch HeaderInfo(HSOpts, SourceMgr, *Diags, LangOpts,
535 PreprocessorOptions PPOpts;
536 TrivialModuleLoader ModuleLoader;
537 Preprocessor PP(PPOpts, *Diags, LangOpts, SourceMgr, HeaderInfo,
540 std::shared_ptr<ModuleCache> ModCache = createCrossProcessModuleCache();
541 PCHContainerOperations PCHOperations;
542 CodeGenOptions CodeGenOpts;
544 PP, *ModCache,
nullptr, PCHOperations.getRawReader(),
547 DisableValidationForModuleKind::None,
556 Reader.setListener(
nullptr);
563 return Reader.ReadAST(ModuleFileName::makeExplicit(ModuleFilePath),
564 serialization::MK_MainFile, SourceLocation(),
565 ASTReader::ARR_OutOfDate) == ASTReader::Success;
568bool IsModuleFilesUpToDate(
569 llvm::SmallVector<PathRef> ModuleFilePaths,
571 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
573 ModuleFilePaths, [&RequisiteModules, VFS](
auto ModuleFilePath) {
574 return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
581llvm::Expected<std::shared_ptr<BuiltModuleFile>>
582buildModuleFile(llvm::StringRef ModuleName,
PathRef ModuleUnitFileName,
583 tooling::CompileCommand Cmd,
PathRef ModuleFilePath,
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()));
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);
601 (void)RemoveTemporaryModuleFile;
603 Cmd.Output = TemporaryModuleFilePath.str().str();
607 Inputs.CompileCommand = std::move(Cmd);
612 return llvm::createStringError(
"Failed to build compiler invocation");
614 auto FS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
615 auto Buf = FS->getBufferForFile(Inputs.CompileCommand.Filename);
617 return llvm::createStringError(
"Failed to create buffer");
621 CI->getLangOpts().SkipODRCheckInGMF =
true;
626 CI->getHeaderSearchOpts().ValidateASTInputFilesContent =
true;
628 BuiltModuleFiles.adjustHeaderSearchOptions(CI->getHeaderSearchOpts());
630 CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
633 std::move(*Buf), std::move(FS), IgnoreDiags);
635 return llvm::createStringError(
"Failed to prepare compiler instance");
637 GenerateReducedModuleInterfaceAction Action;
638 Clang->ExecuteAction(Action);
640 if (Clang->getDiagnostics().hasErrorOccurred()) {
642 for (
const auto &Arg : Inputs.CompileCommand.CommandLine) {
648 clangd::vlog(
"Failed to compile {0} with command: {1}", ModuleUnitFileName,
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);
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));
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()));
673 PublishedExistingModuleFile =
true;
677 TemporaryModuleFilePath.clear();
680 return BuiltModuleFile::make(ModuleName, ModuleFilePath);
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);
696bool ReusablePrerequisiteModules::canReuse(
697 const CompilerInvocation &CI,
698 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
const {
699 if (RequiredModules.empty())
702 llvm::SmallVector<llvm::StringRef> BMIPaths;
703 for (
auto &MF : RequiredModules)
704 BMIPaths.push_back(MF->getModuleFilePath());
705 return IsModuleFilesUpToDate(BMIPaths, *
this, VFS);
731class ModuleFileCache {
733 ModuleFileCache(
const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
734 const GlobalCompilationDatabase &getCDB()
const {
return CDB; }
736 std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName,
737 PathRef ModuleUnitSource,
738 llvm::StringRef CommandHash);
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)] =
748 void remove(StringRef ModuleName, PathRef ModuleUnitSource,
749 llvm::StringRef CommandHash);
752 static std::string cacheKey(StringRef ModuleName, PathRef ModuleUnitSource,
753 llvm::StringRef CommandHash) {
755 Key.reserve(ModuleName.size() + ModuleUnitSource.size() +
756 CommandHash.size() + 2);
757 Key.append(ModuleName);
761 Key.append(CommandHash);
765 const GlobalCompilationDatabase &CDB;
767 llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
768 std::mutex ModuleFilesMutex;
771std::shared_ptr<const ModuleFile>
772ModuleFileCache::getModule(StringRef ModuleName,
PathRef ModuleUnitSource,
773 llvm::StringRef CommandHash) {
774 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
777 ModuleFiles.find(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
778 if (Iter == ModuleFiles.end())
781 if (
auto Res = Iter->second.lock())
784 ModuleFiles.erase(Iter);
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));
794class ModuleNameToSourceCache {
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())
804 void addUniqueEntry(llvm::StringRef ModuleName, PathRef Source) {
805 std::lock_guard<std::mutex> Lock(CacheMutex);
806 ModuleNameToUniqueSourceCache[ModuleName] = Source.str();
809 void eraseUniqueEntry(llvm::StringRef ModuleName) {
810 std::lock_guard<std::mutex> Lock(CacheMutex);
811 ModuleNameToUniqueSourceCache.erase(ModuleName);
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())
821 if (Inner == Outer->second.end())
823 return Inner->second;
826 void addMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile,
828 std::lock_guard<std::mutex> Lock(CacheMutex);
829 ModuleNameToMultipleSourceCache[ModuleName]
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())
840 if (Outer->second.empty())
841 ModuleNameToMultipleSourceCache.erase(Outer);
845 std::mutex CacheMutex;
846 llvm::StringMap<std::string> ModuleNameToUniqueSourceCache;
852 llvm::StringMap<llvm::StringMap<std::string>> ModuleNameToMultipleSourceCache;
855class CachingProjectModules :
public ProjectModules {
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");
864 std::vector<std::string> getRequiredModules(PathRef File)
override {
865 return MDB->getRequiredModules(File);
868 std::string getModuleNameForSource(PathRef File)
override {
869 return MDB->getModuleNameForSource(File);
872 ModuleNameState getModuleNameState(llvm::StringRef ModuleName)
override {
873 return MDB->getModuleNameState(ModuleName);
876 std::string getSourceForModuleName(llvm::StringRef ModuleName,
877 PathRef RequiredSrcFile)
override {
878 auto ModuleState = MDB->getModuleNameState(ModuleName);
880 if (ModuleState == ModuleNameState::Multiple) {
881 std::string CachedResult =
882 Cache.getMultipleSourceForModuleName(ModuleName, RequiredSrcFile);
886 if (!CachedResult.empty()) {
887 std::string ModuleNameOfCachedSource =
888 MDB->getModuleNameForSource(CachedResult);
889 if (ModuleNameOfCachedSource == ModuleName)
893 Cache.eraseMultipleEntry(ModuleName, RequiredSrcFile);
896 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
898 Cache.addMultipleEntry(ModuleName, RequiredSrcFile, Result);
904 assert(ModuleState == ModuleNameState::Unique ||
905 ModuleState == ModuleNameState::Unknown);
906 std::string CachedResult = Cache.getUniqueSourceForModuleName(ModuleName);
910 if (!CachedResult.empty()) {
911 std::string ModuleNameOfCachedSource =
912 MDB->getModuleNameForSource(CachedResult);
913 if (ModuleNameOfCachedSource == ModuleName)
917 Cache.eraseUniqueEntry(ModuleName);
920 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
922 Cache.addUniqueEntry(ModuleName, Result);
928 std::unique_ptr<ProjectModules> MDB;
929 ModuleNameToSourceCache &Cache;
935llvm::SmallVector<std::string> getAllRequiredModules(
PathRef RequiredSource,
936 CachingProjectModules &MDB,
937 StringRef ModuleName) {
938 llvm::SmallVector<std::string> ModuleNames;
939 llvm::StringSet<> ModuleNamesSet;
941 auto VisitDeps = [&](StringRef ModuleName,
auto Visitor) ->
void {
942 ModuleNamesSet.insert(ModuleName);
944 for (StringRef RequiredModuleName : MDB.getRequiredModules(
945 MDB.getSourceForModuleName(ModuleName, RequiredSource)))
946 if (ModuleNamesSet.insert(RequiredModuleName).second)
947 Visitor(RequiredModuleName, Visitor);
949 ModuleNames.push_back(ModuleName.str());
951 VisitDeps(ModuleName, VisitDeps);
958std::vector<std::string> collectModuleFiles(
PathRef CacheRoot) {
959 std::vector<std::string> Result;
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")
965 Result.push_back(It->path());
968 log(
"Failed to scan module cache directory {0}: {1}", CacheRoot,
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,
983 llvm::sys::TimePoint<> LastAccess = Status.getLastAccessedTime();
984 llvm::sys::TimePoint<> Now = std::chrono::system_clock::now();
985 if (LastAccess > Now)
988 std::chrono::duration_cast<std::chrono::seconds>(Now - LastAccess);
990 std::chrono::seconds(VersionedModuleFileGCThresholdSeconds);
991 if (Age <= Threshold)
994 if (!llvm::sys::fs::exists(ModuleFilePath))
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());
1003 log(
"Removed cached module file {0} ({1})", ModuleFilePath, Reason);
1014 return ProjectModulesCache;
1019 getOrBuildModuleFile(
PathRef RequiredSource, StringRef ModuleName,
1021 ReusablePrerequisiteModules &BuiltModuleFiles);
1025 void getPrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleUnitFileName,
1027 ReusablePrerequisiteModules &BuiltModuleFiles);
1030 void garbageCollectModuleCacheForProjectRoot(
PathRef ProjectRoot);
1032 ModuleFileCache Cache;
1033 ModuleNameToSourceCache ProjectModulesCache;
1034 std::mutex GarbageCollectedProjectRootsMutex;
1035 llvm::StringSet<> GarbageCollectedProjectRoots;
1038void ModulesBuilder::ModulesBuilderImpl::
1039 garbageCollectModuleCacheForProjectRoot(
PathRef ProjectRoot) {
1040 if (ProjectRoot.empty())
1042 std::string NormalizedProjectRoot = normalizePathForCache(ProjectRoot);
1046 std::lock_guard<std::mutex> Lock(GarbageCollectedProjectRootsMutex);
1047 if (!GarbageCollectedProjectRoots.insert(NormalizedProjectRoot).second)
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}",
1062void ModulesBuilder::ModulesBuilderImpl::getPrebuiltModuleFile(
1063 StringRef ModuleName,
PathRef ModuleUnitFileName,
const ThreadsafeFS &TFS,
1064 ReusablePrerequisiteModules &BuiltModuleFiles) {
1065 auto Cmd = getCDB().getCompileCommand(ModuleUnitFileName);
1071 Inputs.CompileCommand = std::move(*Cmd);
1073 IgnoreDiagnostics IgnoreDiags;
1083 for (
auto &[ModuleName, ModuleFilePath] :
1084 CI->getHeaderSearchOpts().PrebuiltModuleFiles) {
1085 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
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);
1094 AbsoluteModuleFilePath = ModuleFilePath;
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));
1108 CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles) {
1109 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1110 return llvm::Error::success();
1112 std::string ModuleUnitFileName =
1113 MDB.getSourceForModuleName(ModuleName, RequiredSource);
1121 if (ModuleUnitFileName.empty())
1122 return llvm::createStringError(
1123 llvm::formatv(
"Don't get the module unit for module {0}", ModuleName));
1128 getPrebuiltModuleFile(ModuleName, ModuleUnitFileName, TFS, BuiltModuleFiles);
1131 auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName);
1132 for (llvm::StringRef ReqModuleName : ReqModuleNames) {
1133 if (BuiltModuleFiles.isModuleUnitBuilt(ReqModuleName))
1136 std::string ReqFileName =
1137 MDB.getSourceForModuleName(ReqModuleName, RequiredSource);
1138 auto Cmd =
getCDB().getCompileCommand(ReqFileName);
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);
1146 const std::string CommandHash = getCompileCommandStringHash(*Cmd);
1147 const std::string PublishedModuleFilePath = getPublishedModuleFilePath(
1148 ReqModuleName, getModuleFilesDirectory(ReqFileName, *Cmd,
getCDB()));
1152 auto SourceLock = ScopedModuleSourceLock::acquire(ReqFileName,
getCDB());
1154 return SourceLock.takeError();
1156 std::shared_ptr<const ModuleFile> Cached =
1157 Cache.getModule(ReqModuleName, ReqFileName, CommandHash);
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));
1167 Cache.remove(ReqModuleName, ReqFileName, CommandHash);
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);
1176 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1177 if (llvm::Error Err = Materialized.takeError())
1179 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1180 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1185 std::error_code EC = llvm::sys::fs::remove(PublishedModuleFilePath);
1187 return llvm::createStringError(
1188 llvm::formatv(
"Failed to remove stale module file {0}: {1}",
1189 PublishedModuleFilePath, EC.message()));
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())
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));
1208 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1209 if (llvm::Error Err = Materialized.takeError())
1212 log(
"Built module {0} to {1}", ReqModuleName,
1213 (*Materialized)->getModuleFilePath());
1214 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1215 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1218 return llvm::Error::success();
1222 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1226 CachingProjectModules CachedMDB(std::move(MDB),
1227 Impl->getProjectModulesCache());
1228 return !CachedMDB.getRequiredModules(
File).empty();
1231std::unique_ptr<PrerequisiteModules>
1234 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1236 elog(
"Failed to get Project Modules information for {0}",
File);
1237 return std::make_unique<FailedPrerequisiteModules>();
1239 CachingProjectModules CachedMDB(std::move(MDB),
1240 Impl->getProjectModulesCache());
1242 std::vector<std::string> RequiredModuleNames =
1243 CachedMDB.getRequiredModules(
File);
1244 if (RequiredModuleNames.empty())
1245 return std::make_unique<ReusablePrerequisiteModules>();
1247 auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
1248 for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
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,
1254 return std::make_unique<FailedPrerequisiteModules>();
1258 return std::move(RequiredModules);
1262 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)
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++ -*-===//