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"
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));
43llvm::cl::opt<unsigned> VersionedModuleFileGCThresholdSeconds(
44 "modules-builder-versioned-gc-threshold-seconds",
45 llvm::cl::desc(
"Delete versioned copy-on-read module files whose last "
46 "access time is older than this many seconds."),
47 llvm::cl::init(3 * 24 * 60 * 60));
70std::string hashStringForCache(llvm::StringRef Content) {
71 return llvm::toHex(
digest(Content));
75 llvm::SmallString<256> Normalized(
Path);
76 llvm::sys::path::remove_dots(Normalized,
true);
85getModuleCacheRoot(
PathRef ModuleUnitFileName,
87 llvm::SmallString<256> Result;
88 if (
auto PI = CDB.getProjectInfo(ModuleUnitFileName);
89 PI && !PI->SourceRoot.empty()) {
90 Result = PI->SourceRoot;
91 llvm::sys::path::append(Result,
".cache",
"clangd",
"modules");
95 if (llvm::sys::path::cache_directory(Result)) {
96 llvm::sys::path::append(Result,
"clangd",
"modules");
100 llvm::sys::path::system_temp_directory(
false, Result);
101 llvm::sys::path::append(Result,
"clangd",
"modules");
108llvm::SmallString<256>
109getModuleCacheLocksDirectory(
PathRef ModuleUnitFileName,
111 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
112 llvm::sys::path::append(Result,
".locks");
116std::string getModuleUnitSourcePathHash(
PathRef ModuleUnitFileName) {
117 return hashStringForCache(normalizePathForCache(ModuleUnitFileName));
120std::string getModuleUnitSourceDirectoryName(
PathRef ModuleUnitFileName) {
121 std::string Result = llvm::sys::path::filename(ModuleUnitFileName).str();
122 Result.push_back(
'-');
123 Result.append(getModuleUnitSourcePathHash(ModuleUnitFileName));
127std::string getCompileCommandStringHash(
const tooling::CompileCommand &Cmd) {
128 std::string SerializedCommand;
129 SerializedCommand.reserve(Cmd.Directory.size() + Cmd.Filename.size() +
130 Cmd.CommandLine.size() * 16);
134 SerializedCommand.append(Cmd.Directory);
135 SerializedCommand.push_back(
'\0');
136 for (
const auto &Arg : Cmd.CommandLine) {
137 SerializedCommand.append(Arg);
138 SerializedCommand.push_back(
'\0');
140 return hashStringForCache(SerializedCommand);
147llvm::SmallString<256>
148getModuleFilesDirectory(
PathRef ModuleUnitFileName,
149 const tooling::CompileCommand &Cmd,
151 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
152 llvm::sys::path::append(Result,
153 getModuleUnitSourceDirectoryName(ModuleUnitFileName),
154 getCompileCommandStringHash(Cmd));
160llvm::SmallString<256>
161getModuleSourceHashLockPath(
PathRef ModuleUnitFileName,
163 llvm::SmallString<256> Result =
164 getModuleCacheLocksDirectory(ModuleUnitFileName, CDB);
165 llvm::sys::path::append(Result,
166 getModuleUnitSourcePathHash(ModuleUnitFileName));
172llvm::SmallString<256> getTemporaryModuleFilePath(
PathRef ModuleFilePath) {
173 llvm::SmallString<256> ResultPattern(ModuleFilePath);
174 ResultPattern.append(
".tmp-%%-%%-%%-%%-%%-%%");
175 llvm::SmallString<256> Result;
176 llvm::sys::fs::createUniquePath(ResultPattern, Result,
181std::string getModuleFileVersionTimestamp() {
182 const auto Now = std::chrono::system_clock::now();
183 const auto Micros = std::chrono::duration_cast<std::chrono::microseconds>(
184 Now.time_since_epoch()) %
185 std::chrono::seconds(1);
186 const std::time_t CalendarTime = std::chrono::system_clock::to_time_t(Now);
189 localtime_s(&LocalTime, &CalendarTime);
191 localtime_r(&CalendarTime, &LocalTime);
194 return llvm::formatv(
"{0:04}{1:02}{2:02}-{3:02}{4:02}{5:02}-{6:06}",
195 LocalTime.tm_year + 1900, LocalTime.tm_mon + 1,
196 LocalTime.tm_mday, LocalTime.tm_hour, LocalTime.tm_min,
197 LocalTime.tm_sec, Micros.count())
201llvm::SmallString<256>
202getCopyOnReadModuleFilePath(
PathRef PublishedModuleFile) {
203 llvm::SmallString<256> Result(PublishedModuleFile);
204 llvm::sys::path::remove_filename(Result);
205 llvm::sys::path::append(
207 llvm::formatv(
"{0}-{1}{2}", llvm::sys::path::stem(PublishedModuleFile),
208 getModuleFileVersionTimestamp(),
209 llvm::sys::path::extension(PublishedModuleFile))
216llvm::Error ensureLockAnchorFileExists(
PathRef LockPath) {
217 llvm::SmallString<256> LockParent(LockPath);
218 llvm::sys::path::remove_filename(LockParent);
219 if (std::error_code EC = llvm::sys::fs::create_directories(LockParent))
220 return llvm::createStringError(llvm::formatv(
221 "Failed to create lock directory {0}: {1}", LockParent, EC.message()));
224 if (std::error_code EC = llvm::sys::fs::openFileForWrite(
225 LockPath, FD, llvm::sys::fs::CD_OpenAlways))
226 return llvm::createStringError(llvm::formatv(
227 "Failed to open lock file anchor {0}: {1}", LockPath, EC.message()));
228 llvm::sys::Process::SafelyCloseFileDescriptor(FD);
229 return llvm::Error::success();
252class ScopedModuleSourceLock {
254 static llvm::Expected<ScopedModuleSourceLock>
255 acquire(
PathRef ModuleUnitFileName,
const GlobalCompilationDatabase &CDB) {
256 constexpr auto LockWaitInterval = std::chrono::seconds(10);
257 llvm::SmallString<256> LockPath =
258 getModuleSourceHashLockPath(ModuleUnitFileName, CDB);
259 if (llvm::Error Err = ensureLockAnchorFileExists(LockPath))
260 return std::move(Err);
262 auto Waited = std::chrono::seconds::zero();
265 auto Lock = std::make_unique<llvm::LockFileManager>(LockPath);
266 auto TryLock = Lock->tryLock();
268 return TryLock.takeError();
270 return ScopedModuleSourceLock(std::move(Lock));
272 switch (Lock->waitForUnlockFor(LockWaitInterval)) {
273 case llvm::WaitForUnlockResult::Success:
274 case llvm::WaitForUnlockResult::OwnerDied:
276 case llvm::WaitForUnlockResult::Timeout:
277 Waited += LockWaitInterval;
278 log(
"Still waiting for module lock {0} after {1}s", LockPath,
282 llvm_unreachable(
"Unhandled lock wait result");
287 explicit ScopedModuleSourceLock(std::unique_ptr<llvm::LockFileManager> Lock)
288 : Lock(std::move(Lock)) {}
290 std::unique_ptr<llvm::LockFileManager> Lock;
294std::string getModuleFilePath(llvm::StringRef ModuleName,
296 llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
297 auto [PrimaryModuleName, PartitionName] = ModuleName.split(
':');
298 llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
299 if (!PartitionName.empty()) {
300 ModuleFilePath.append(
"-");
301 ModuleFilePath.append(PartitionName);
304 ModuleFilePath.append(
".pcm");
305 return std::string(ModuleFilePath);
308std::string getPublishedModuleFilePath(llvm::StringRef ModuleName,
310 return getModuleFilePath(ModuleName, ModuleFilesPrefix);
317 ~FailedPrerequisiteModules()
override =
default;
321 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {}
325 canReuse(
const CompilerInvocation &CI,
326 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override {
330 llvm::StringSet<> getRequiredModuleNames()
const override {
return {}; }
336 ModuleFile(StringRef ModuleName,
PathRef ModuleFilePath)
337 : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
340 ModuleFile() =
delete;
342 ModuleFile(
const ModuleFile &) =
delete;
343 ModuleFile operator=(
const ModuleFile &) =
delete;
346 ModuleFile(ModuleFile &&Other)
347 : ModuleName(std::move(Other.ModuleName)),
348 ModuleFilePath(std::move(Other.ModuleFilePath)) {
349 Other.ModuleName.clear();
350 Other.ModuleFilePath.clear();
353 ModuleFile &operator=(ModuleFile &&Other) {
358 new (
this) ModuleFile(std::move(Other));
361 virtual ~ModuleFile() =
default;
363 StringRef getModuleName()
const {
return ModuleName; }
365 StringRef getModuleFilePath()
const {
return ModuleFilePath; }
368 std::string ModuleName;
369 std::string ModuleFilePath;
373class PrebuiltModuleFile :
public ModuleFile {
380 PrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
381 : ModuleFile(ModuleName, ModuleFilePath) {}
383 static std::shared_ptr<PrebuiltModuleFile> make(StringRef ModuleName,
385 return std::make_shared<PrebuiltModuleFile>(ModuleName, ModuleFilePath,
423class BuiltModuleFile final :
public ModuleFile {
430 BuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
431 : ModuleFile(ModuleName, ModuleFilePath) {}
433 static std::shared_ptr<BuiltModuleFile> make(StringRef ModuleName,
435 return std::make_shared<BuiltModuleFile>(ModuleName, ModuleFilePath,
442class CopyOnReadModuleFile final :
public ModuleFile {
447 CopyOnReadModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
448 : ModuleFile(ModuleName, ModuleFilePath) {}
450 ~CopyOnReadModuleFile()
override {
451 if (!ModuleFilePath.empty() && !DebugModulesBuilder)
452 if (std::error_code EC = llvm::sys::fs::remove(ModuleFilePath))
453 vlog(
"Failed to remove copy-on-read module file {0}: {1}",
454 ModuleFilePath, EC.message());
457 static std::shared_ptr<CopyOnReadModuleFile> make(StringRef ModuleName,
459 return std::make_shared<CopyOnReadModuleFile>(ModuleName, ModuleFilePath,
469 ReusablePrerequisiteModules() =
default;
471 ReusablePrerequisiteModules(
const ReusablePrerequisiteModules &Other) =
473 ReusablePrerequisiteModules &
474 operator=(
const ReusablePrerequisiteModules &) =
default;
475 ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) =
delete;
476 ReusablePrerequisiteModules
477 operator=(ReusablePrerequisiteModules &&) =
delete;
479 ~ReusablePrerequisiteModules()
override =
default;
481 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {
483 for (
const auto &RequiredModule : RequiredModules)
484 Options.PrebuiltModuleFiles.insert_or_assign(
485 RequiredModule->getModuleName().str(),
486 RequiredModule->getModuleFilePath().str());
489 std::string getAsString()
const {
491 llvm::raw_string_ostream OS(Result);
492 for (
const auto &MF : RequiredModules) {
493 OS <<
"-fmodule-file=" << MF->getModuleName() <<
"="
494 << MF->getModuleFilePath() <<
" ";
499 bool canReuse(
const CompilerInvocation &CI,
500 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override;
502 bool isModuleUnitBuilt(llvm::StringRef ModuleName)
const {
503 return BuiltModuleNames.contains(ModuleName);
506 void addModuleFile(std::shared_ptr<const ModuleFile> MF) {
507 BuiltModuleNames.insert(MF->getModuleName());
508 RequiredModules.emplace_back(std::move(MF));
511 void setDirectModuleNames(std::vector<std::string> Names) {
512 DirectModuleNames.insert_range(Names);
515 llvm::StringSet<> getRequiredModuleNames()
const override {
516 return DirectModuleNames;
520 llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
522 llvm::StringSet<> BuiltModuleNames;
524 llvm::StringSet<> DirectModuleNames;
527bool IsModuleFileUpToDate(
PathRef ModuleFilePath,
529 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
530 HeaderSearchOptions HSOpts;
531 RequisiteModules.adjustHeaderSearchOptions(HSOpts);
532 HSOpts.ForceCheckCXX20ModulesInputFiles =
true;
533 HSOpts.ValidateASTInputFilesContent =
true;
535 clang::clangd::IgnoreDiagnostics IgnoreDiags;
536 DiagnosticOptions DiagOpts;
537 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
538 CompilerInstance::createDiagnostics(*VFS, DiagOpts, &IgnoreDiags,
541 LangOptions LangOpts;
542 LangOpts.SkipODRCheckInGMF =
true;
544 FileManager FileMgr(FileSystemOptions(), VFS);
546 SourceManager SourceMgr(*Diags, FileMgr);
548 HeaderSearch HeaderInfo(HSOpts, SourceMgr, *Diags, LangOpts,
551 PreprocessorOptions PPOpts;
552 TrivialModuleLoader ModuleLoader;
553 Preprocessor PP(PPOpts, *Diags, LangOpts, SourceMgr, HeaderInfo,
556 std::shared_ptr<ModuleCache> ModCache = createCrossProcessModuleCache();
557 PCHContainerOperations PCHOperations;
558 CodeGenOptions CodeGenOpts;
560 PP, *ModCache,
nullptr, PCHOperations.getRawReader(),
563 DisableValidationForModuleKind::None,
572 Reader.setListener(
nullptr);
579 return Reader.ReadAST(ModuleFileName::makeExplicit(ModuleFilePath),
580 serialization::MK_MainFile, SourceLocation(),
581 ASTReader::ARR_OutOfDate) == ASTReader::Success;
584bool IsModuleFilesUpToDate(
585 llvm::SmallVector<PathRef> ModuleFilePaths,
587 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
589 ModuleFilePaths, [&RequisiteModules, VFS](
auto ModuleFilePath) {
590 return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
597llvm::Expected<std::shared_ptr<BuiltModuleFile>>
598buildModuleFile(llvm::StringRef ModuleName,
PathRef ModuleUnitFileName,
599 tooling::CompileCommand Cmd,
PathRef ModuleFilePath,
601 const ReusablePrerequisiteModules &BuiltModuleFiles,
602 bool &PublishedExistingModuleFile) {
603 PublishedExistingModuleFile =
false;
604 llvm::SmallString<256> ModuleFilesPrefix(ModuleFilePath);
605 llvm::sys::path::remove_filename(ModuleFilesPrefix);
606 if (std::error_code EC = llvm::sys::fs::create_directories(ModuleFilesPrefix))
607 return llvm::createStringError(
608 llvm::formatv(
"Failed to create module cache directory {0}: {1}",
609 ModuleFilesPrefix, EC.message()));
611 llvm::SmallString<256> TemporaryModuleFilePath =
612 getTemporaryModuleFilePath(ModuleFilePath);
613 auto RemoveTemporaryModuleFile = llvm::scope_exit([&] {
614 if (!TemporaryModuleFilePath.empty() && !DebugModulesBuilder)
615 llvm::sys::fs::remove(TemporaryModuleFilePath);
617 (void)RemoveTemporaryModuleFile;
619 Cmd.Output = TemporaryModuleFilePath.str().str();
623 Inputs.CompileCommand = std::move(Cmd);
628 return llvm::createStringError(
"Failed to build compiler invocation");
630 auto FS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
631 auto Buf = FS->getBufferForFile(Inputs.CompileCommand.Filename);
633 return llvm::createStringError(
"Failed to create buffer");
637 CI->getLangOpts().SkipODRCheckInGMF =
true;
642 CI->getHeaderSearchOpts().ValidateASTInputFilesContent =
true;
644 BuiltModuleFiles.adjustHeaderSearchOptions(CI->getHeaderSearchOpts());
646 CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
649 std::move(*Buf), std::move(FS), IgnoreDiags);
651 return llvm::createStringError(
"Failed to prepare compiler instance");
653 GenerateReducedModuleInterfaceAction Action;
654 Clang->ExecuteAction(Action);
656 if (Clang->getDiagnostics().hasErrorOccurred()) {
658 for (
const auto &Arg : Inputs.CompileCommand.CommandLine) {
664 clangd::vlog(
"Failed to compile {0} with command: {1}", ModuleUnitFileName,
667 std::string BuiltModuleFilesStr = BuiltModuleFiles.getAsString();
668 if (!BuiltModuleFilesStr.empty())
669 clangd::vlog(
"The actual used module files built by clangd is {0}",
670 BuiltModuleFilesStr);
672 return llvm::createStringError(
673 llvm::formatv(
"Failed to compile {0}. Use '--log=verbose' to view "
674 "detailed failure reasons. It is helpful to use "
675 "'--debug-modules-builder' flag to keep the clangd's "
676 "built module files to reproduce the failure for "
677 "debugging. Remember to remove them after debugging.",
678 ModuleUnitFileName));
681 if (std::error_code EC =
682 llvm::sys::fs::rename(TemporaryModuleFilePath, ModuleFilePath)) {
683 if (!llvm::sys::fs::exists(ModuleFilePath))
684 return llvm::createStringError(
685 llvm::formatv(
"Failed to publish module file {0}: {1}",
686 ModuleFilePath, EC.message()));
689 PublishedExistingModuleFile =
true;
693 TemporaryModuleFilePath.clear();
696 return BuiltModuleFile::make(ModuleName, ModuleFilePath);
699llvm::Expected<std::shared_ptr<CopyOnReadModuleFile>>
700copyModuleFileForRead(llvm::StringRef ModuleName,
701 PathRef PublishedModuleFilePath) {
702 llvm::SmallString<256> VersionedModuleFilePath =
703 getCopyOnReadModuleFilePath(PublishedModuleFilePath);
704 if (std::error_code EC = llvm::sys::fs::copy_file(PublishedModuleFilePath,
705 VersionedModuleFilePath))
706 return llvm::createStringError(llvm::formatv(
707 "Failed to copy module file {0} to {1}: {2}", PublishedModuleFilePath,
708 VersionedModuleFilePath, EC.message()));
709 return CopyOnReadModuleFile::make(ModuleName, VersionedModuleFilePath);
712bool ReusablePrerequisiteModules::canReuse(
713 const CompilerInvocation &CI,
714 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
const {
715 if (RequiredModules.empty())
718 llvm::SmallVector<llvm::StringRef> BMIPaths;
719 for (
auto &MF : RequiredModules)
720 BMIPaths.push_back(MF->getModuleFilePath());
721 return IsModuleFilesUpToDate(BMIPaths, *
this, VFS);
747class ModuleFileCache {
749 ModuleFileCache(
const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
750 const GlobalCompilationDatabase &getCDB()
const {
return CDB; }
752 std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName,
753 PathRef ModuleUnitSource,
754 llvm::StringRef CommandHash);
756 void add(StringRef ModuleName, PathRef ModuleUnitSource,
757 llvm::StringRef CommandHash,
758 std::shared_ptr<const ModuleFile> ModuleFile) {
759 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
760 ModuleFiles[cacheKey(ModuleName, ModuleUnitSource, CommandHash)] =
764 void remove(StringRef ModuleName, PathRef ModuleUnitSource,
765 llvm::StringRef CommandHash);
768 static std::string cacheKey(StringRef ModuleName, PathRef ModuleUnitSource,
769 llvm::StringRef CommandHash) {
771 Key.reserve(ModuleName.size() + ModuleUnitSource.size() +
772 CommandHash.size() + 2);
773 Key.append(ModuleName);
777 Key.append(CommandHash);
781 const GlobalCompilationDatabase &CDB;
783 llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
784 std::mutex ModuleFilesMutex;
787std::shared_ptr<const ModuleFile>
788ModuleFileCache::getModule(StringRef ModuleName,
PathRef ModuleUnitSource,
789 llvm::StringRef CommandHash) {
790 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
793 ModuleFiles.find(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
794 if (Iter == ModuleFiles.end())
797 if (
auto Res = Iter->second.lock())
800 ModuleFiles.erase(Iter);
804void ModuleFileCache::remove(StringRef ModuleName,
PathRef ModuleUnitSource,
805 llvm::StringRef CommandHash) {
806 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
807 ModuleFiles.erase(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
810class ModuleNameToSourceCache {
812 std::string getUniqueSourceForModuleName(llvm::StringRef ModuleName) {
813 std::lock_guard<std::mutex> Lock(CacheMutex);
814 auto Iter = ModuleNameToUniqueSourceCache.find(ModuleName);
815 if (Iter != ModuleNameToUniqueSourceCache.end())
820 void addUniqueEntry(llvm::StringRef ModuleName, PathRef Source) {
821 std::lock_guard<std::mutex> Lock(CacheMutex);
822 ModuleNameToUniqueSourceCache[ModuleName] = Source.str();
825 void eraseUniqueEntry(llvm::StringRef ModuleName) {
826 std::lock_guard<std::mutex> Lock(CacheMutex);
827 ModuleNameToUniqueSourceCache.erase(ModuleName);
830 std::string getMultipleSourceForModuleName(llvm::StringRef ModuleName,
831 PathRef RequiredSrcFile) {
832 std::lock_guard<std::mutex> Lock(CacheMutex);
833 auto Outer = ModuleNameToMultipleSourceCache.find(ModuleName);
834 if (Outer == ModuleNameToMultipleSourceCache.end())
837 if (Inner == Outer->second.end())
839 return Inner->second;
842 void addMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile,
844 std::lock_guard<std::mutex> Lock(CacheMutex);
845 ModuleNameToMultipleSourceCache[ModuleName]
850 void eraseMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile) {
851 std::lock_guard<std::mutex> Lock(CacheMutex);
852 auto Outer = ModuleNameToMultipleSourceCache.find(ModuleName);
853 if (Outer == ModuleNameToMultipleSourceCache.end())
856 if (Outer->second.empty())
857 ModuleNameToMultipleSourceCache.erase(Outer);
861 std::mutex CacheMutex;
862 llvm::StringMap<std::string> ModuleNameToUniqueSourceCache;
868 llvm::StringMap<llvm::StringMap<std::string>> ModuleNameToMultipleSourceCache;
877 namespace types = clang::driver::types;
879 types::lookupTypeForExtension(llvm::sys::path::extension(
File).substr(1));
880 return Lang == types::TY_CXXModule;
886class ObservedModuleFiles {
888 explicit ObservedModuleFiles(
const GlobalCompilationDatabase &CDB)
891 bool add(PathRef File) {
892 std::lock_guard<std::mutex> Lock(Mutex);
896 bool remove(PathRef File) {
897 std::lock_guard<std::mutex> Lock(Mutex);
901 std::vector<Path> sourcesFor(PathRef File)
const {
902 auto PI = CDB.getProjectInfo(File);
903 if (!PI || PI->SourceRoot.empty())
907 std::vector<Path> Observed;
909 std::lock_guard<std::mutex> Lock(Mutex);
910 Observed.reserve(Sources.size());
911 for (
const auto &Source : Sources)
912 Observed.push_back(Source.second);
915 std::vector<Path> Result;
916 for (
const auto &Source : Observed) {
917 auto SourcePI = CDB.getProjectInfo(Source);
919 Result.push_back(Source);
925 const GlobalCompilationDatabase &CDB;
926 mutable std::mutex Mutex;
927 llvm::StringMap<Path> Sources;
930class CachingProjectModules :
public ProjectModules {
932 CachingProjectModules(std::unique_ptr<ProjectModules> MDB,
933 ModuleNameToSourceCache &Cache,
934 const ObservedModuleFiles &ObservedFiles)
935 : MDB(std::move(MDB)), Cache(Cache), ObservedFiles(ObservedFiles) {
936 assert(this->MDB &&
"CachingProjectModules should only be created with a "
937 "valid underlying ProjectModules");
940 std::vector<std::string> getRequiredModules(PathRef File)
override {
941 return MDB->getRequiredModules(File);
944 std::string getModuleNameForSource(PathRef File)
override {
945 return MDB->getModuleNameForSource(File);
948 ModuleNameState getModuleNameState(llvm::StringRef ModuleName)
override {
949 return MDB->getModuleNameState(ModuleName);
952 std::string getSourceForModuleName(llvm::StringRef ModuleName,
953 PathRef RequiredSrcFile)
override {
954 auto ModuleState = MDB->getModuleNameState(ModuleName);
956 if (ModuleState == ModuleNameState::Multiple) {
957 std::string CachedResult =
958 Cache.getMultipleSourceForModuleName(ModuleName, RequiredSrcFile);
962 if (!CachedResult.empty()) {
963 std::string ModuleNameOfCachedSource =
964 MDB->getModuleNameForSource(CachedResult);
965 if (ModuleNameOfCachedSource == ModuleName)
969 Cache.eraseMultipleEntry(ModuleName, RequiredSrcFile);
972 auto Result = findSourceForModuleName(ModuleName, RequiredSrcFile);
975 Cache.addMultipleEntry(ModuleName, RequiredSrcFile, *Result);
981 assert(ModuleState == ModuleNameState::Unique ||
982 ModuleState == ModuleNameState::Unknown);
983 std::string CachedResult = Cache.getUniqueSourceForModuleName(ModuleName);
987 if (!CachedResult.empty()) {
988 std::string ModuleNameOfCachedSource =
989 MDB->getModuleNameForSource(CachedResult);
990 if (ModuleNameOfCachedSource == ModuleName)
994 Cache.eraseUniqueEntry(ModuleName);
997 auto Result = findSourceForModuleName(ModuleName, RequiredSrcFile);
1000 Cache.addUniqueEntry(ModuleName, *Result);
1005 std::optional<std::string> findSourceForModuleName(llvm::StringRef ModuleName,
1006 PathRef RequiredSrcFile) {
1007 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
1008 if (!Result.empty())
1010 for (
const auto &Source : ObservedFiles.sourcesFor(RequiredSrcFile))
1011 if (MDB->getModuleNameForSource(Source) == ModuleName)
1013 return std::nullopt;
1016 std::unique_ptr<ProjectModules> MDB;
1017 ModuleNameToSourceCache &Cache;
1018 const ObservedModuleFiles &ObservedFiles;
1024llvm::SmallVector<std::string> getAllRequiredModules(
PathRef RequiredSource,
1025 CachingProjectModules &MDB,
1026 StringRef ModuleName) {
1027 llvm::SmallVector<std::string> ModuleNames;
1028 llvm::StringSet<> ModuleNamesSet;
1030 auto VisitDeps = [&](StringRef ModuleName,
auto Visitor) ->
void {
1031 ModuleNamesSet.insert(ModuleName);
1033 for (StringRef RequiredModuleName : MDB.getRequiredModules(
1034 MDB.getSourceForModuleName(ModuleName, RequiredSource)))
1035 if (ModuleNamesSet.insert(RequiredModuleName).second)
1036 Visitor(RequiredModuleName, Visitor);
1038 ModuleNames.push_back(ModuleName.str());
1040 VisitDeps(ModuleName, VisitDeps);
1047std::vector<std::string> collectModuleFiles(
PathRef CacheRoot) {
1048 std::vector<std::string> Result;
1050 for (llvm::sys::fs::recursive_directory_iterator It(CacheRoot, EC), End;
1051 It != End && !EC; It.increment(EC)) {
1052 if (llvm::sys::path::extension(It->path()) !=
".pcm")
1054 Result.push_back(It->path());
1057 log(
"Failed to scan module cache directory {0}: {1}", CacheRoot,
1063void garbageCollectModuleCache(
PathRef CacheRoot) {
1064 for (
const auto &ModuleFilePath : collectModuleFiles(CacheRoot)) {
1065 llvm::sys::fs::file_status Status;
1066 if (std::error_code EC = llvm::sys::fs::status(ModuleFilePath, Status)) {
1067 log(
"Failed to stat cached module file {0} for GC: {1}", ModuleFilePath,
1072 llvm::sys::TimePoint<> LastAccess = Status.getLastAccessedTime();
1073 llvm::sys::TimePoint<> Now = std::chrono::system_clock::now();
1074 if (LastAccess > Now)
1077 std::chrono::duration_cast<std::chrono::seconds>(Now - LastAccess);
1079 std::chrono::seconds(VersionedModuleFileGCThresholdSeconds);
1080 if (Age <= Threshold)
1083 if (!llvm::sys::fs::exists(ModuleFilePath))
1086 constexpr llvm::StringLiteral Reason =
"file older than GC threshold";
1087 if (std::error_code EC = llvm::sys::fs::remove(ModuleFilePath)) {
1088 log(
"Failed to remove cached module file {0} ({1}): {2}", ModuleFilePath,
1089 Reason, EC.message());
1092 log(
"Removed cached module file {0} ({1})", ModuleFilePath, Reason);
1101 : Cache(CDB), ObservedFiles(CDB) {}
1104 return ProjectModulesCache;
1110 return isCXXModuleFile(
File) && ObservedFiles.add(
File);
1114 const llvm::StringRef
File =
Event.uri.file();
1115 switch (
Event.type) {
1117 return isCXXModuleFile(
File) && ObservedFiles.add(
File);
1119 if (!isCXXModuleFile(
File))
1121 ObservedFiles.add(
File);
1124 return ObservedFiles.remove(
File);
1126 llvm_unreachable(
"Unhandled FileChangeType");
1130 getOrBuildModuleFile(
PathRef RequiredSource, StringRef ModuleName,
1132 ReusablePrerequisiteModules &BuiltModuleFiles);
1136 void getPrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleUnitFileName,
1138 ReusablePrerequisiteModules &BuiltModuleFiles);
1141 void garbageCollectModuleCacheForProjectRoot(
PathRef ProjectRoot);
1143 ModuleFileCache Cache;
1144 ObservedModuleFiles ObservedFiles;
1145 ModuleNameToSourceCache ProjectModulesCache;
1146 std::mutex GarbageCollectedProjectRootsMutex;
1147 llvm::StringSet<> GarbageCollectedProjectRoots;
1150void ModulesBuilder::ModulesBuilderImpl::
1151 garbageCollectModuleCacheForProjectRoot(
PathRef ProjectRoot) {
1152 if (ProjectRoot.empty())
1154 std::string NormalizedProjectRoot = normalizePathForCache(ProjectRoot);
1158 std::lock_guard<std::mutex> Lock(GarbageCollectedProjectRootsMutex);
1159 if (!GarbageCollectedProjectRoots.insert(NormalizedProjectRoot).second)
1163 llvm::SmallString<256> CacheRoot(ProjectRoot);
1164 llvm::sys::path::append(CacheRoot,
".cache",
"clangd",
"modules");
1165 log(
"Running GC pass for clangd built module files under {0} with age "
1166 "threshold {1} seconds (adjust with --modules-builder-versioned-gc-"
1167 "threshold-seconds)",
1168 CacheRoot, VersionedModuleFileGCThresholdSeconds);
1169 garbageCollectModuleCache(CacheRoot);
1170 log(
"Done running GC pass for clangd built module files under {0}",
1174void ModulesBuilder::ModulesBuilderImpl::getPrebuiltModuleFile(
1175 StringRef ModuleName,
PathRef ModuleUnitFileName,
const ThreadsafeFS &TFS,
1176 ReusablePrerequisiteModules &BuiltModuleFiles) {
1177 auto Cmd = getCDB().getCompileCommand(ModuleUnitFileName);
1183 Inputs.CompileCommand = std::move(*Cmd);
1185 IgnoreDiagnostics IgnoreDiags;
1195 for (
auto &[ModuleName, ModuleFilePath] :
1196 CI->getHeaderSearchOpts().PrebuiltModuleFiles) {
1197 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1201 llvm::SmallString<256> AbsoluteModuleFilePath;
1202 if (llvm::sys::path::is_relative(ModuleFilePath)) {
1203 AbsoluteModuleFilePath = Inputs.CompileCommand.Directory;
1204 llvm::sys::path::append(AbsoluteModuleFilePath, ModuleFilePath);
1206 AbsoluteModuleFilePath = ModuleFilePath;
1208 if (IsModuleFileUpToDate(AbsoluteModuleFilePath, BuiltModuleFiles,
1209 TFS.view(std::nullopt))) {
1210 log(
"Reusing prebuilt module file {0} of module {1} for {2}",
1211 AbsoluteModuleFilePath, ModuleName, ModuleUnitFileName);
1212 BuiltModuleFiles.addModuleFile(
1213 PrebuiltModuleFile::make(ModuleName, AbsoluteModuleFilePath));
1220 CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles) {
1221 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1222 return llvm::Error::success();
1224 std::string ModuleUnitFileName =
1225 MDB.getSourceForModuleName(ModuleName, RequiredSource);
1233 if (ModuleUnitFileName.empty())
1234 return llvm::createStringError(
1235 llvm::formatv(
"Don't get the module unit for module {0}", ModuleName));
1240 getPrebuiltModuleFile(ModuleName, ModuleUnitFileName, TFS, BuiltModuleFiles);
1243 auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName);
1244 for (llvm::StringRef ReqModuleName : ReqModuleNames) {
1245 if (BuiltModuleFiles.isModuleUnitBuilt(ReqModuleName))
1248 std::string ReqFileName =
1249 MDB.getSourceForModuleName(ReqModuleName, RequiredSource);
1250 auto Cmd =
getCDB().getCompileCommand(ReqFileName);
1252 return llvm::createStringError(
1253 llvm::formatv(
"No compile command for {0}", ReqFileName));
1254 if (
auto PI =
getCDB().getProjectInfo(ReqFileName);
1255 PI && !PI->SourceRoot.empty())
1256 garbageCollectModuleCacheForProjectRoot(PI->SourceRoot);
1258 const std::string CommandHash = getCompileCommandStringHash(*Cmd);
1259 const std::string PublishedModuleFilePath = getPublishedModuleFilePath(
1260 ReqModuleName, getModuleFilesDirectory(ReqFileName, *Cmd,
getCDB()));
1264 auto SourceLock = ScopedModuleSourceLock::acquire(ReqFileName,
getCDB());
1266 return SourceLock.takeError();
1268 std::shared_ptr<const ModuleFile> Cached =
1269 Cache.getModule(ReqModuleName, ReqFileName, CommandHash);
1272 if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles,
1273 TFS.
view(std::nullopt))) {
1274 log(
"Reusing module {0} from {1}", ReqModuleName,
1275 Cached->getModuleFilePath());
1276 BuiltModuleFiles.addModuleFile(std::move(Cached));
1279 Cache.remove(ReqModuleName, ReqFileName, CommandHash);
1282 if (llvm::sys::fs::exists(PublishedModuleFilePath)) {
1283 if (IsModuleFileUpToDate(PublishedModuleFilePath, BuiltModuleFiles,
1284 TFS.
view(std::nullopt))) {
1285 log(
"Reusing persistent module {0} from {1}", ReqModuleName,
1286 PublishedModuleFilePath);
1288 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1289 if (llvm::Error Err = Materialized.takeError())
1291 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1292 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1297 std::error_code EC = llvm::sys::fs::remove(PublishedModuleFilePath);
1299 return llvm::createStringError(
1300 llvm::formatv(
"Failed to remove stale module file {0}: {1}",
1301 PublishedModuleFilePath, EC.message()));
1304 bool PublishedExistingModuleFile =
false;
1305 llvm::Expected<std::shared_ptr<BuiltModuleFile>> MF = buildModuleFile(
1306 ReqModuleName, ReqFileName, std::move(*Cmd), PublishedModuleFilePath,
1307 TFS, BuiltModuleFiles, PublishedExistingModuleFile);
1308 if (llvm::Error Err = MF.takeError())
1311 if (PublishedExistingModuleFile &&
1312 !IsModuleFileUpToDate(PublishedModuleFilePath, BuiltModuleFiles,
1313 TFS.
view(std::nullopt))) {
1314 return llvm::createStringError(
1315 llvm::formatv(
"Published module file {0} is stale after lock wait",
1316 PublishedModuleFilePath));
1320 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1321 if (llvm::Error Err = Materialized.takeError())
1324 log(
"Built module {0} to {1}", ReqModuleName,
1325 (*Materialized)->getModuleFilePath());
1326 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1327 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1330 return llvm::Error::success();
1334 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1338 CachingProjectModules CachedMDB(
1339 std::move(MDB), Impl->getProjectModulesCache(), Impl->getObservedFiles());
1340 return !CachedMDB.getRequiredModules(
File).empty();
1344 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1348 CachingProjectModules CachedMDB(
1349 std::move(MDB), Impl->getProjectModulesCache(), Impl->getObservedFiles());
1350 return CachedMDB.getRequiredModules(
File);
1353std::unique_ptr<PrerequisiteModules>
1356 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1358 elog(
"Failed to get Project Modules information for {0}",
File);
1359 return std::make_unique<FailedPrerequisiteModules>();
1361 CachingProjectModules CachedMDB(
1362 std::move(MDB), Impl->getProjectModulesCache(), Impl->getObservedFiles());
1364 std::vector<std::string> RequiredModuleNames =
1365 CachedMDB.getRequiredModules(
File);
1366 if (RequiredModuleNames.empty())
1367 return std::make_unique<ReusablePrerequisiteModules>();
1369 auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
1370 RequiredModules->setDirectModuleNames(RequiredModuleNames);
1371 for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
1373 if (llvm::Error Err = Impl->getOrBuildModuleFile(
1374 File, RequiredModuleName, TFS, CachedMDB, *RequiredModules.get())) {
1375 elog(
"Failed to build module {0}; due to {1}", RequiredModuleName,
1377 return std::make_unique<FailedPrerequisiteModules>();
1381 return std::move(RequiredModules);
1385 Impl = std::make_unique<ModulesBuilderImpl>(CDB);
1391 return Impl->observeSourcePath(
File);
1395 return Impl->onFileEvent(
Event);
An Event<T> allows events of type T to be broadcast to listeners.
Provides compilation arguments used for parsing C and C++ files.
const GlobalCompilationDatabase & getCDB() const
ModulesBuilderImpl(const GlobalCompilationDatabase &CDB)
const ObservedModuleFiles & getObservedFiles() const
bool observeSourcePath(PathRef File)
ModuleNameToSourceCache & getProjectModulesCache()
llvm::Error getOrBuildModuleFile(PathRef RequiredSource, StringRef ModuleName, const ThreadsafeFS &TFS, CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles)
bool onFileEvent(const FileEvent &Event)
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.
@ Created
The file got created.
@ Deleted
The file got deleted.
@ Changed
The file got changed.
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++ -*-===//