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));
49constexpr bool ReusePrebuiltModuleFileDefault =
false;
51constexpr bool ReusePrebuiltModuleFileDefault =
true;
54llvm::cl::opt<bool> ReusePrebuiltModuleFile(
55 "reuse-prebuilt-module-file",
56 llvm::cl::desc(
"Whether or not to reuse prebuilt module files from existing build "
58 llvm::cl::init(ReusePrebuiltModuleFileDefault));
60llvm::cl::opt<unsigned> VersionedModuleFileGCThresholdSeconds(
61 "modules-builder-versioned-gc-threshold-seconds",
62 llvm::cl::desc(
"Delete versioned copy-on-read module files whose last "
63 "access time is older than this many seconds."),
64 llvm::cl::init(3 * 24 * 60 * 60));
87std::string hashStringForCache(llvm::StringRef Content) {
88 return llvm::toHex(
digest(Content));
92 llvm::SmallString<256> Normalized(
Path);
93 llvm::sys::path::remove_dots(Normalized,
true);
101llvm::SmallString<256>
102getModuleCacheRoot(
PathRef ModuleUnitFileName,
104 llvm::SmallString<256> Result;
105 if (
auto PI = CDB.getProjectInfo(ModuleUnitFileName);
106 PI && !PI->SourceRoot.empty()) {
107 Result = PI->SourceRoot;
108 llvm::sys::path::append(Result,
".cache",
"clangd",
"modules");
112 if (llvm::sys::path::cache_directory(Result)) {
113 llvm::sys::path::append(Result,
"clangd",
"modules");
117 llvm::sys::path::system_temp_directory(
false, Result);
118 llvm::sys::path::append(Result,
"clangd",
"modules");
125llvm::SmallString<256>
126getModuleCacheLocksDirectory(
PathRef ModuleUnitFileName,
128 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
129 llvm::sys::path::append(Result,
".locks");
133std::string getModuleUnitSourcePathHash(
PathRef ModuleUnitFileName) {
134 return hashStringForCache(normalizePathForCache(ModuleUnitFileName));
137std::string getModuleUnitSourceDirectoryName(
PathRef ModuleUnitFileName) {
138 std::string Result = llvm::sys::path::filename(ModuleUnitFileName).str();
139 Result.push_back(
'-');
140 Result.append(getModuleUnitSourcePathHash(ModuleUnitFileName));
144std::string getCompileCommandStringHash(
const tooling::CompileCommand &Cmd) {
145 std::string SerializedCommand;
146 SerializedCommand.reserve(Cmd.Directory.size() + Cmd.Filename.size() +
147 Cmd.CommandLine.size() * 16);
151 SerializedCommand.append(Cmd.Directory);
152 SerializedCommand.push_back(
'\0');
153 for (
const auto &Arg : Cmd.CommandLine) {
154 SerializedCommand.append(Arg);
155 SerializedCommand.push_back(
'\0');
157 return hashStringForCache(SerializedCommand);
164llvm::SmallString<256>
165getModuleFilesDirectory(
PathRef ModuleUnitFileName,
166 const tooling::CompileCommand &Cmd,
168 llvm::SmallString<256> Result = getModuleCacheRoot(ModuleUnitFileName, CDB);
169 llvm::sys::path::append(Result,
170 getModuleUnitSourceDirectoryName(ModuleUnitFileName),
171 getCompileCommandStringHash(Cmd));
177llvm::SmallString<256>
178getModuleSourceHashLockPath(
PathRef ModuleUnitFileName,
180 llvm::SmallString<256> Result =
181 getModuleCacheLocksDirectory(ModuleUnitFileName, CDB);
182 llvm::sys::path::append(Result,
183 getModuleUnitSourcePathHash(ModuleUnitFileName));
189llvm::SmallString<256> getTemporaryModuleFilePath(
PathRef ModuleFilePath) {
190 llvm::SmallString<256> ResultPattern(ModuleFilePath);
191 ResultPattern.append(
".tmp-%%-%%-%%-%%-%%-%%");
192 llvm::SmallString<256> Result;
193 llvm::sys::fs::createUniquePath(ResultPattern, Result,
198std::string getModuleFileVersionTimestamp() {
199 const auto Now = std::chrono::system_clock::now();
200 const auto Micros = std::chrono::duration_cast<std::chrono::microseconds>(
201 Now.time_since_epoch()) %
202 std::chrono::seconds(1);
203 const std::time_t CalendarTime = std::chrono::system_clock::to_time_t(Now);
206 localtime_s(&LocalTime, &CalendarTime);
208 localtime_r(&CalendarTime, &LocalTime);
211 return llvm::formatv(
"{0:04}{1:02}{2:02}-{3:02}{4:02}{5:02}-{6:06}",
212 LocalTime.tm_year + 1900, LocalTime.tm_mon + 1,
213 LocalTime.tm_mday, LocalTime.tm_hour, LocalTime.tm_min,
214 LocalTime.tm_sec, Micros.count())
218llvm::SmallString<256>
219getCopyOnReadModuleFilePath(
PathRef PublishedModuleFile) {
220 llvm::SmallString<256> Result(PublishedModuleFile);
221 llvm::sys::path::remove_filename(Result);
222 llvm::sys::path::append(
224 llvm::formatv(
"{0}-{1}{2}", llvm::sys::path::stem(PublishedModuleFile),
225 getModuleFileVersionTimestamp(),
226 llvm::sys::path::extension(PublishedModuleFile))
233llvm::Error ensureLockAnchorFileExists(
PathRef LockPath) {
234 llvm::SmallString<256> LockParent(LockPath);
235 llvm::sys::path::remove_filename(LockParent);
236 if (std::error_code EC = llvm::sys::fs::create_directories(LockParent))
237 return llvm::createStringError(llvm::formatv(
238 "Failed to create lock directory {0}: {1}", LockParent, EC.message()));
241 if (std::error_code EC = llvm::sys::fs::openFileForWrite(
242 LockPath, FD, llvm::sys::fs::CD_OpenAlways))
243 return llvm::createStringError(llvm::formatv(
244 "Failed to open lock file anchor {0}: {1}", LockPath, EC.message()));
245 llvm::sys::Process::SafelyCloseFileDescriptor(FD);
246 return llvm::Error::success();
269class ScopedModuleSourceLock {
271 static llvm::Expected<ScopedModuleSourceLock>
272 acquire(
PathRef ModuleUnitFileName,
const GlobalCompilationDatabase &CDB) {
273 constexpr auto LockWaitInterval = std::chrono::seconds(10);
274 llvm::SmallString<256> LockPath =
275 getModuleSourceHashLockPath(ModuleUnitFileName, CDB);
276 if (llvm::Error Err = ensureLockAnchorFileExists(LockPath))
277 return std::move(Err);
279 auto Waited = std::chrono::seconds::zero();
282 auto Lock = std::make_unique<llvm::LockFileManager>(LockPath);
283 auto TryLock = Lock->tryLock();
285 return TryLock.takeError();
287 return ScopedModuleSourceLock(std::move(Lock));
289 switch (Lock->waitForUnlockFor(LockWaitInterval)) {
290 case llvm::WaitForUnlockResult::Success:
291 case llvm::WaitForUnlockResult::OwnerDied:
293 case llvm::WaitForUnlockResult::Timeout:
294 Waited += LockWaitInterval;
295 log(
"Still waiting for module lock {0} after {1}s", LockPath,
299 llvm_unreachable(
"Unhandled lock wait result");
304 explicit ScopedModuleSourceLock(std::unique_ptr<llvm::LockFileManager> Lock)
305 : Lock(std::move(Lock)) {}
307 std::unique_ptr<llvm::LockFileManager> Lock;
311std::string getModuleFilePath(llvm::StringRef ModuleName,
313 llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
314 auto [PrimaryModuleName, PartitionName] = ModuleName.split(
':');
315 llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
316 if (!PartitionName.empty()) {
317 ModuleFilePath.append(
"-");
318 ModuleFilePath.append(PartitionName);
321 ModuleFilePath.append(
".pcm");
322 return std::string(ModuleFilePath);
325std::string getPublishedModuleFilePath(llvm::StringRef ModuleName,
327 return getModuleFilePath(ModuleName, ModuleFilesPrefix);
334 ~FailedPrerequisiteModules()
override =
default;
338 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {}
342 canReuse(
const CompilerInvocation &CI,
343 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override {
347 llvm::StringSet<> getRequiredModuleNames()
const override {
return {}; }
353 ModuleFile(StringRef ModuleName,
PathRef ModuleFilePath)
354 : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
357 ModuleFile() =
delete;
359 ModuleFile(
const ModuleFile &) =
delete;
360 ModuleFile operator=(
const ModuleFile &) =
delete;
363 ModuleFile(ModuleFile &&Other)
364 : ModuleName(std::move(Other.ModuleName)),
365 ModuleFilePath(std::move(Other.ModuleFilePath)) {
366 Other.ModuleName.clear();
367 Other.ModuleFilePath.clear();
370 ModuleFile &operator=(ModuleFile &&Other) {
375 new (
this) ModuleFile(std::move(Other));
378 virtual ~ModuleFile() =
default;
380 StringRef getModuleName()
const {
return ModuleName; }
382 StringRef getModuleFilePath()
const {
return ModuleFilePath; }
385 std::string ModuleName;
386 std::string ModuleFilePath;
390class PrebuiltModuleFile :
public ModuleFile {
397 PrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
398 : ModuleFile(ModuleName, ModuleFilePath) {}
400 static std::shared_ptr<PrebuiltModuleFile> make(StringRef ModuleName,
402 return std::make_shared<PrebuiltModuleFile>(ModuleName, ModuleFilePath,
440class BuiltModuleFile final :
public ModuleFile {
447 BuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
448 : ModuleFile(ModuleName, ModuleFilePath) {}
450 static std::shared_ptr<BuiltModuleFile> make(StringRef ModuleName,
452 return std::make_shared<BuiltModuleFile>(ModuleName, ModuleFilePath,
459class CopyOnReadModuleFile final :
public ModuleFile {
464 CopyOnReadModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
465 : ModuleFile(ModuleName, ModuleFilePath) {}
467 ~CopyOnReadModuleFile()
override {
468 if (!ModuleFilePath.empty() && !DebugModulesBuilder)
469 if (std::error_code EC = llvm::sys::fs::remove(ModuleFilePath))
470 vlog(
"Failed to remove copy-on-read module file {0}: {1}",
471 ModuleFilePath, EC.message());
474 static std::shared_ptr<CopyOnReadModuleFile> make(StringRef ModuleName,
476 return std::make_shared<CopyOnReadModuleFile>(ModuleName, ModuleFilePath,
486 ReusablePrerequisiteModules() =
default;
488 ReusablePrerequisiteModules(
const ReusablePrerequisiteModules &Other) =
490 ReusablePrerequisiteModules &
491 operator=(
const ReusablePrerequisiteModules &) =
default;
492 ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) =
delete;
493 ReusablePrerequisiteModules
494 operator=(ReusablePrerequisiteModules &&) =
delete;
496 ~ReusablePrerequisiteModules()
override =
default;
498 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {
500 for (
const auto &RequiredModule : RequiredModules)
501 Options.PrebuiltModuleFiles.insert_or_assign(
502 RequiredModule->getModuleName().str(),
503 RequiredModule->getModuleFilePath().str());
506 std::string getAsString()
const {
508 llvm::raw_string_ostream OS(Result);
509 for (
const auto &MF : RequiredModules) {
510 OS <<
"-fmodule-file=" << MF->getModuleName() <<
"="
511 << MF->getModuleFilePath() <<
" ";
516 bool canReuse(
const CompilerInvocation &CI,
517 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override;
519 bool isModuleUnitBuilt(llvm::StringRef ModuleName)
const {
520 return BuiltModuleNames.contains(ModuleName);
523 void addModuleFile(std::shared_ptr<const ModuleFile> MF) {
524 BuiltModuleNames.insert(MF->getModuleName());
525 RequiredModules.emplace_back(std::move(MF));
528 void setDirectModuleNames(std::vector<std::string> Names) {
529 DirectModuleNames.insert_range(Names);
532 llvm::StringSet<> getRequiredModuleNames()
const override {
533 return DirectModuleNames;
537 llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
539 llvm::StringSet<> BuiltModuleNames;
541 llvm::StringSet<> DirectModuleNames;
544bool IsModuleFileUpToDate(
PathRef ModuleFilePath,
546 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
547 HeaderSearchOptions HSOpts;
548 RequisiteModules.adjustHeaderSearchOptions(HSOpts);
549 HSOpts.ForceCheckCXX20ModulesInputFiles =
true;
550 HSOpts.ValidateASTInputFilesContent =
true;
552 clang::clangd::IgnoreDiagnostics IgnoreDiags;
553 DiagnosticOptions DiagOpts;
554 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
555 CompilerInstance::createDiagnostics(*VFS, DiagOpts, &IgnoreDiags,
558 LangOptions LangOpts;
559 LangOpts.SkipODRCheckInGMF =
true;
561 FileManager FileMgr(FileSystemOptions(), VFS);
563 SourceManager SourceMgr(*Diags, FileMgr);
565 HeaderSearch HeaderInfo(HSOpts, SourceMgr, *Diags, LangOpts,
568 PreprocessorOptions PPOpts;
569 TrivialModuleLoader ModuleLoader;
570 Preprocessor PP(PPOpts, *Diags, LangOpts, SourceMgr, HeaderInfo,
573 std::shared_ptr<ModuleCache> ModCache = createCrossProcessModuleCache();
574 PCHContainerOperations PCHOperations;
575 CodeGenOptions CodeGenOpts;
577 PP, *ModCache,
nullptr, PCHOperations.getRawReader(),
580 DisableValidationForModuleKind::None,
589 Reader.setListener(
nullptr);
596 return Reader.ReadAST(ModuleFileName::makeExplicit(ModuleFilePath),
597 serialization::MK_MainFile, SourceLocation(),
598 ASTReader::ARR_OutOfDate) == ASTReader::Success;
601bool IsModuleFilesUpToDate(
602 llvm::SmallVector<PathRef> ModuleFilePaths,
604 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
606 ModuleFilePaths, [&RequisiteModules, VFS](
auto ModuleFilePath) {
607 return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
614llvm::Expected<std::shared_ptr<BuiltModuleFile>>
615buildModuleFile(llvm::StringRef ModuleName,
PathRef ModuleUnitFileName,
616 tooling::CompileCommand Cmd,
PathRef ModuleFilePath,
618 const ReusablePrerequisiteModules &BuiltModuleFiles,
619 bool &PublishedExistingModuleFile) {
620 PublishedExistingModuleFile =
false;
621 llvm::SmallString<256> ModuleFilesPrefix(ModuleFilePath);
622 llvm::sys::path::remove_filename(ModuleFilesPrefix);
623 if (std::error_code EC = llvm::sys::fs::create_directories(ModuleFilesPrefix))
624 return llvm::createStringError(
625 llvm::formatv(
"Failed to create module cache directory {0}: {1}",
626 ModuleFilesPrefix, EC.message()));
628 llvm::SmallString<256> TemporaryModuleFilePath =
629 getTemporaryModuleFilePath(ModuleFilePath);
630 auto RemoveTemporaryModuleFile = llvm::scope_exit([&] {
631 if (!TemporaryModuleFilePath.empty() && !DebugModulesBuilder)
632 llvm::sys::fs::remove(TemporaryModuleFilePath);
634 (void)RemoveTemporaryModuleFile;
636 Cmd.Output = TemporaryModuleFilePath.str().str();
640 Inputs.CompileCommand = std::move(Cmd);
645 return llvm::createStringError(
"Failed to build compiler invocation");
647 auto FS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
648 auto Buf = FS->getBufferForFile(Inputs.CompileCommand.Filename);
650 return llvm::createStringError(
"Failed to create buffer");
654 CI->getLangOpts().SkipODRCheckInGMF =
true;
659 CI->getHeaderSearchOpts().ValidateASTInputFilesContent =
true;
661 BuiltModuleFiles.adjustHeaderSearchOptions(CI->getHeaderSearchOpts());
663 CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
666 std::move(*Buf), std::move(FS), IgnoreDiags);
668 return llvm::createStringError(
"Failed to prepare compiler instance");
670 GenerateReducedModuleInterfaceAction Action;
671 Clang->ExecuteAction(Action);
673 if (Clang->getDiagnostics().hasErrorOccurred()) {
675 for (
const auto &Arg : Inputs.CompileCommand.CommandLine) {
681 clangd::vlog(
"Failed to compile {0} with command: {1}", ModuleUnitFileName,
684 std::string BuiltModuleFilesStr = BuiltModuleFiles.getAsString();
685 if (!BuiltModuleFilesStr.empty())
686 clangd::vlog(
"The actual used module files built by clangd is {0}",
687 BuiltModuleFilesStr);
689 return llvm::createStringError(
690 llvm::formatv(
"Failed to compile {0}. Use '--log=verbose' to view "
691 "detailed failure reasons. It is helpful to use "
692 "'--debug-modules-builder' flag to keep the clangd's "
693 "built module files to reproduce the failure for "
694 "debugging. Remember to remove them after debugging.",
695 ModuleUnitFileName));
698 if (std::error_code EC =
699 llvm::sys::fs::rename(TemporaryModuleFilePath, ModuleFilePath)) {
700 if (!llvm::sys::fs::exists(ModuleFilePath))
701 return llvm::createStringError(
702 llvm::formatv(
"Failed to publish module file {0}: {1}",
703 ModuleFilePath, EC.message()));
706 PublishedExistingModuleFile =
true;
710 TemporaryModuleFilePath.clear();
713 return BuiltModuleFile::make(ModuleName, ModuleFilePath);
716llvm::Expected<std::shared_ptr<CopyOnReadModuleFile>>
717copyModuleFileForRead(llvm::StringRef ModuleName,
718 PathRef PublishedModuleFilePath) {
719 llvm::SmallString<256> VersionedModuleFilePath =
720 getCopyOnReadModuleFilePath(PublishedModuleFilePath);
721 if (std::error_code EC = llvm::sys::fs::copy_file(PublishedModuleFilePath,
722 VersionedModuleFilePath))
723 return llvm::createStringError(llvm::formatv(
724 "Failed to copy module file {0} to {1}: {2}", PublishedModuleFilePath,
725 VersionedModuleFilePath, EC.message()));
726 return CopyOnReadModuleFile::make(ModuleName, VersionedModuleFilePath);
729bool ReusablePrerequisiteModules::canReuse(
730 const CompilerInvocation &CI,
731 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
const {
732 if (RequiredModules.empty())
735 llvm::SmallVector<llvm::StringRef> BMIPaths;
736 for (
auto &MF : RequiredModules)
737 BMIPaths.push_back(MF->getModuleFilePath());
738 return IsModuleFilesUpToDate(BMIPaths, *
this, VFS);
764class ModuleFileCache {
766 ModuleFileCache(
const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
767 const GlobalCompilationDatabase &getCDB()
const {
return CDB; }
769 std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName,
770 PathRef ModuleUnitSource,
771 llvm::StringRef CommandHash);
773 void add(StringRef ModuleName, PathRef ModuleUnitSource,
774 llvm::StringRef CommandHash,
775 std::shared_ptr<const ModuleFile> ModuleFile) {
776 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
777 ModuleFiles[cacheKey(ModuleName, ModuleUnitSource, CommandHash)] =
781 void remove(StringRef ModuleName, PathRef ModuleUnitSource,
782 llvm::StringRef CommandHash);
785 static std::string cacheKey(StringRef ModuleName, PathRef ModuleUnitSource,
786 llvm::StringRef CommandHash) {
788 Key.reserve(ModuleName.size() + ModuleUnitSource.size() +
789 CommandHash.size() + 2);
790 Key.append(ModuleName);
794 Key.append(CommandHash);
798 const GlobalCompilationDatabase &CDB;
800 llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
801 std::mutex ModuleFilesMutex;
804std::shared_ptr<const ModuleFile>
805ModuleFileCache::getModule(StringRef ModuleName,
PathRef ModuleUnitSource,
806 llvm::StringRef CommandHash) {
807 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
810 ModuleFiles.find(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
811 if (Iter == ModuleFiles.end())
814 if (
auto Res = Iter->second.lock())
817 ModuleFiles.erase(Iter);
821void ModuleFileCache::remove(StringRef ModuleName,
PathRef ModuleUnitSource,
822 llvm::StringRef CommandHash) {
823 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
824 ModuleFiles.erase(cacheKey(ModuleName, ModuleUnitSource, CommandHash));
827class ModuleNameToSourceCache {
829 std::string getUniqueSourceForModuleName(llvm::StringRef ModuleName) {
830 std::lock_guard<std::mutex> Lock(CacheMutex);
831 auto Iter = ModuleNameToUniqueSourceCache.find(ModuleName);
832 if (Iter != ModuleNameToUniqueSourceCache.end())
837 void addUniqueEntry(llvm::StringRef ModuleName, PathRef Source) {
838 std::lock_guard<std::mutex> Lock(CacheMutex);
839 ModuleNameToUniqueSourceCache[ModuleName] = Source.str();
842 void eraseUniqueEntry(llvm::StringRef ModuleName) {
843 std::lock_guard<std::mutex> Lock(CacheMutex);
844 ModuleNameToUniqueSourceCache.erase(ModuleName);
847 std::string getMultipleSourceForModuleName(llvm::StringRef ModuleName,
848 PathRef RequiredSrcFile) {
849 std::lock_guard<std::mutex> Lock(CacheMutex);
850 auto Outer = ModuleNameToMultipleSourceCache.find(ModuleName);
851 if (Outer == ModuleNameToMultipleSourceCache.end())
854 if (Inner == Outer->second.end())
856 return Inner->second;
859 void addMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile,
861 std::lock_guard<std::mutex> Lock(CacheMutex);
862 ModuleNameToMultipleSourceCache[ModuleName]
867 void eraseMultipleEntry(llvm::StringRef ModuleName, PathRef RequiredSrcFile) {
868 std::lock_guard<std::mutex> Lock(CacheMutex);
869 auto Outer = ModuleNameToMultipleSourceCache.find(ModuleName);
870 if (Outer == ModuleNameToMultipleSourceCache.end())
873 if (Outer->second.empty())
874 ModuleNameToMultipleSourceCache.erase(Outer);
878 std::mutex CacheMutex;
879 llvm::StringMap<std::string> ModuleNameToUniqueSourceCache;
885 llvm::StringMap<llvm::StringMap<std::string>> ModuleNameToMultipleSourceCache;
894 namespace types = clang::driver::types;
896 types::lookupTypeForExtension(llvm::sys::path::extension(
File).substr(1));
897 return Lang == types::TY_CXXModule;
903class ObservedModuleFiles {
905 explicit ObservedModuleFiles(
const GlobalCompilationDatabase &CDB)
908 bool add(PathRef File) {
909 std::lock_guard<std::mutex> Lock(Mutex);
913 bool remove(PathRef File) {
914 std::lock_guard<std::mutex> Lock(Mutex);
918 std::vector<Path> sourcesFor(PathRef File)
const {
919 auto PI = CDB.getProjectInfo(File);
920 if (!PI || PI->SourceRoot.empty())
924 std::vector<Path> Observed;
926 std::lock_guard<std::mutex> Lock(Mutex);
927 Observed.reserve(Sources.size());
928 for (
const auto &Source : Sources)
929 Observed.push_back(Source.second);
932 std::vector<Path> Result;
933 for (
const auto &Source : Observed) {
934 auto SourcePI = CDB.getProjectInfo(Source);
936 Result.push_back(Source);
942 const GlobalCompilationDatabase &CDB;
943 mutable std::mutex Mutex;
944 llvm::StringMap<Path> Sources;
947class CachingProjectModules :
public ProjectModules {
949 CachingProjectModules(std::unique_ptr<ProjectModules> MDB,
950 ModuleNameToSourceCache &Cache,
951 const ObservedModuleFiles &ObservedFiles)
952 : MDB(std::move(MDB)), Cache(Cache), ObservedFiles(ObservedFiles) {
953 assert(this->MDB &&
"CachingProjectModules should only be created with a "
954 "valid underlying ProjectModules");
957 std::vector<std::string> getRequiredModules(PathRef File)
override {
958 return MDB->getRequiredModules(File);
961 std::string getModuleNameForSource(PathRef File)
override {
962 return MDB->getModuleNameForSource(File);
965 ModuleNameState getModuleNameState(llvm::StringRef ModuleName)
override {
966 return MDB->getModuleNameState(ModuleName);
969 std::string getSourceForModuleName(llvm::StringRef ModuleName,
970 PathRef RequiredSrcFile)
override {
971 auto ModuleState = MDB->getModuleNameState(ModuleName);
973 if (ModuleState == ModuleNameState::Multiple) {
974 std::string CachedResult =
975 Cache.getMultipleSourceForModuleName(ModuleName, RequiredSrcFile);
979 if (!CachedResult.empty()) {
980 std::string ModuleNameOfCachedSource =
981 MDB->getModuleNameForSource(CachedResult);
982 if (ModuleNameOfCachedSource == ModuleName)
986 Cache.eraseMultipleEntry(ModuleName, RequiredSrcFile);
989 auto Result = findSourceForModuleName(ModuleName, RequiredSrcFile);
992 Cache.addMultipleEntry(ModuleName, RequiredSrcFile, *Result);
998 assert(ModuleState == ModuleNameState::Unique ||
999 ModuleState == ModuleNameState::Unknown);
1000 std::string CachedResult = Cache.getUniqueSourceForModuleName(ModuleName);
1004 if (!CachedResult.empty()) {
1005 std::string ModuleNameOfCachedSource =
1006 MDB->getModuleNameForSource(CachedResult);
1007 if (ModuleNameOfCachedSource == ModuleName)
1008 return CachedResult;
1011 Cache.eraseUniqueEntry(ModuleName);
1014 auto Result = findSourceForModuleName(ModuleName, RequiredSrcFile);
1017 Cache.addUniqueEntry(ModuleName, *Result);
1022 std::optional<std::string> findSourceForModuleName(llvm::StringRef ModuleName,
1023 PathRef RequiredSrcFile) {
1024 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
1025 if (!Result.empty())
1027 for (
const auto &Source : ObservedFiles.sourcesFor(RequiredSrcFile))
1028 if (MDB->getModuleNameForSource(Source) == ModuleName)
1030 return std::nullopt;
1033 std::unique_ptr<ProjectModules> MDB;
1034 ModuleNameToSourceCache &Cache;
1035 const ObservedModuleFiles &ObservedFiles;
1041llvm::SmallVector<std::string> getAllRequiredModules(
PathRef RequiredSource,
1042 CachingProjectModules &MDB,
1043 StringRef ModuleName) {
1044 llvm::SmallVector<std::string> ModuleNames;
1045 llvm::StringSet<> ModuleNamesSet;
1047 auto VisitDeps = [&](StringRef ModuleName,
auto Visitor) ->
void {
1048 ModuleNamesSet.insert(ModuleName);
1050 for (StringRef RequiredModuleName : MDB.getRequiredModules(
1051 MDB.getSourceForModuleName(ModuleName, RequiredSource)))
1052 if (ModuleNamesSet.insert(RequiredModuleName).second)
1053 Visitor(RequiredModuleName, Visitor);
1055 ModuleNames.push_back(ModuleName.str());
1057 VisitDeps(ModuleName, VisitDeps);
1064std::vector<std::string> collectModuleFiles(
PathRef CacheRoot) {
1065 std::vector<std::string> Result;
1067 for (llvm::sys::fs::recursive_directory_iterator It(CacheRoot, EC), End;
1068 It != End && !EC; It.increment(EC)) {
1069 if (llvm::sys::path::extension(It->path()) !=
".pcm")
1071 Result.push_back(It->path());
1074 log(
"Failed to scan module cache directory {0}: {1}", CacheRoot,
1080void garbageCollectModuleCache(
PathRef CacheRoot) {
1081 for (
const auto &ModuleFilePath : collectModuleFiles(CacheRoot)) {
1082 llvm::sys::fs::file_status Status;
1083 if (std::error_code EC = llvm::sys::fs::status(ModuleFilePath, Status)) {
1084 log(
"Failed to stat cached module file {0} for GC: {1}", ModuleFilePath,
1089 llvm::sys::TimePoint<> LastAccess = Status.getLastAccessedTime();
1090 llvm::sys::TimePoint<> Now = std::chrono::system_clock::now();
1091 if (LastAccess > Now)
1094 std::chrono::duration_cast<std::chrono::seconds>(Now - LastAccess);
1096 std::chrono::seconds(VersionedModuleFileGCThresholdSeconds);
1097 if (Age <= Threshold)
1100 if (!llvm::sys::fs::exists(ModuleFilePath))
1103 constexpr llvm::StringLiteral Reason =
"file older than GC threshold";
1104 if (std::error_code EC = llvm::sys::fs::remove(ModuleFilePath)) {
1105 log(
"Failed to remove cached module file {0} ({1}): {2}", ModuleFilePath,
1106 Reason, EC.message());
1109 log(
"Removed cached module file {0} ({1})", ModuleFilePath, Reason);
1118 : Cache(CDB), ObservedFiles(CDB) {}
1121 return ProjectModulesCache;
1127 return isCXXModuleFile(
File) && ObservedFiles.add(
File);
1131 const llvm::StringRef
File =
Event.uri.file();
1132 switch (
Event.type) {
1134 return isCXXModuleFile(
File) && ObservedFiles.add(
File);
1136 if (!isCXXModuleFile(
File))
1138 ObservedFiles.add(
File);
1141 return ObservedFiles.remove(
File);
1143 llvm_unreachable(
"Unhandled FileChangeType");
1147 getOrBuildModuleFile(
PathRef RequiredSource, StringRef ModuleName,
1149 ReusablePrerequisiteModules &BuiltModuleFiles);
1153 void getPrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleUnitFileName,
1155 ReusablePrerequisiteModules &BuiltModuleFiles);
1158 void garbageCollectModuleCacheForProjectRoot(
PathRef ProjectRoot);
1160 ModuleFileCache Cache;
1161 ObservedModuleFiles ObservedFiles;
1162 ModuleNameToSourceCache ProjectModulesCache;
1163 std::mutex GarbageCollectedProjectRootsMutex;
1164 llvm::StringSet<> GarbageCollectedProjectRoots;
1167void ModulesBuilder::ModulesBuilderImpl::
1168 garbageCollectModuleCacheForProjectRoot(
PathRef ProjectRoot) {
1169 if (ProjectRoot.empty())
1171 std::string NormalizedProjectRoot = normalizePathForCache(ProjectRoot);
1175 std::lock_guard<std::mutex> Lock(GarbageCollectedProjectRootsMutex);
1176 if (!GarbageCollectedProjectRoots.insert(NormalizedProjectRoot).second)
1180 llvm::SmallString<256> CacheRoot(ProjectRoot);
1181 llvm::sys::path::append(CacheRoot,
".cache",
"clangd",
"modules");
1182 log(
"Running GC pass for clangd built module files under {0} with age "
1183 "threshold {1} seconds (adjust with --modules-builder-versioned-gc-"
1184 "threshold-seconds)",
1185 CacheRoot, VersionedModuleFileGCThresholdSeconds);
1186 garbageCollectModuleCache(CacheRoot);
1187 log(
"Done running GC pass for clangd built module files under {0}",
1191void ModulesBuilder::ModulesBuilderImpl::getPrebuiltModuleFile(
1192 StringRef ModuleName,
PathRef ModuleUnitFileName,
const ThreadsafeFS &TFS,
1193 ReusablePrerequisiteModules &BuiltModuleFiles) {
1194 if (!ReusePrebuiltModuleFile)
1197 auto Cmd = getCDB().getCompileCommand(ModuleUnitFileName);
1203 Inputs.CompileCommand = std::move(*Cmd);
1205 IgnoreDiagnostics IgnoreDiags;
1215 for (
auto &[ModuleName, ModuleFilePath] :
1216 CI->getHeaderSearchOpts().PrebuiltModuleFiles) {
1217 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1221 llvm::SmallString<256> AbsoluteModuleFilePath;
1222 if (llvm::sys::path::is_relative(ModuleFilePath)) {
1223 AbsoluteModuleFilePath = Inputs.CompileCommand.Directory;
1224 llvm::sys::path::append(AbsoluteModuleFilePath, ModuleFilePath);
1226 AbsoluteModuleFilePath = ModuleFilePath;
1228 if (IsModuleFileUpToDate(AbsoluteModuleFilePath, BuiltModuleFiles,
1229 TFS.view(std::nullopt))) {
1230 log(
"Reusing prebuilt module file {0} of module {1} for {2}",
1231 AbsoluteModuleFilePath, ModuleName, ModuleUnitFileName);
1232 BuiltModuleFiles.addModuleFile(
1233 PrebuiltModuleFile::make(ModuleName, AbsoluteModuleFilePath));
1240 CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles) {
1241 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
1242 return llvm::Error::success();
1244 std::string ModuleUnitFileName =
1245 MDB.getSourceForModuleName(ModuleName, RequiredSource);
1253 if (ModuleUnitFileName.empty())
1254 return llvm::createStringError(
1255 llvm::formatv(
"Don't get the module unit for module {0}", ModuleName));
1260 getPrebuiltModuleFile(ModuleName, ModuleUnitFileName, TFS, BuiltModuleFiles);
1263 auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName);
1264 for (llvm::StringRef ReqModuleName : ReqModuleNames) {
1265 if (BuiltModuleFiles.isModuleUnitBuilt(ReqModuleName))
1268 std::string ReqFileName =
1269 MDB.getSourceForModuleName(ReqModuleName, RequiredSource);
1270 auto Cmd =
getCDB().getCompileCommand(ReqFileName);
1272 return llvm::createStringError(
1273 llvm::formatv(
"No compile command for {0}", ReqFileName));
1274 if (
auto PI =
getCDB().getProjectInfo(ReqFileName);
1275 PI && !PI->SourceRoot.empty())
1276 garbageCollectModuleCacheForProjectRoot(PI->SourceRoot);
1278 const std::string CommandHash = getCompileCommandStringHash(*Cmd);
1279 const std::string PublishedModuleFilePath = getPublishedModuleFilePath(
1280 ReqModuleName, getModuleFilesDirectory(ReqFileName, *Cmd,
getCDB()));
1284 auto SourceLock = ScopedModuleSourceLock::acquire(ReqFileName,
getCDB());
1286 return SourceLock.takeError();
1288 std::shared_ptr<const ModuleFile> Cached =
1289 Cache.getModule(ReqModuleName, ReqFileName, CommandHash);
1292 if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles,
1293 TFS.
view(std::nullopt))) {
1294 log(
"Reusing module {0} from {1}", ReqModuleName,
1295 Cached->getModuleFilePath());
1296 BuiltModuleFiles.addModuleFile(std::move(Cached));
1299 Cache.remove(ReqModuleName, ReqFileName, CommandHash);
1302 if (llvm::sys::fs::exists(PublishedModuleFilePath)) {
1303 if (IsModuleFileUpToDate(PublishedModuleFilePath, BuiltModuleFiles,
1304 TFS.
view(std::nullopt))) {
1305 log(
"Reusing persistent module {0} from {1}", ReqModuleName,
1306 PublishedModuleFilePath);
1308 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1309 if (llvm::Error Err = Materialized.takeError())
1311 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1312 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1317 std::error_code EC = llvm::sys::fs::remove(PublishedModuleFilePath);
1319 return llvm::createStringError(
1320 llvm::formatv(
"Failed to remove stale module file {0}: {1}",
1321 PublishedModuleFilePath, EC.message()));
1324 bool PublishedExistingModuleFile =
false;
1325 llvm::Expected<std::shared_ptr<BuiltModuleFile>> MF = buildModuleFile(
1326 ReqModuleName, ReqFileName, std::move(*Cmd), PublishedModuleFilePath,
1327 TFS, BuiltModuleFiles, PublishedExistingModuleFile);
1328 if (llvm::Error Err = MF.takeError())
1331 if (PublishedExistingModuleFile &&
1332 !IsModuleFileUpToDate(PublishedModuleFilePath, BuiltModuleFiles,
1333 TFS.
view(std::nullopt))) {
1334 return llvm::createStringError(
1335 llvm::formatv(
"Published module file {0} is stale after lock wait",
1336 PublishedModuleFilePath));
1340 copyModuleFileForRead(ReqModuleName, PublishedModuleFilePath);
1341 if (llvm::Error Err = Materialized.takeError())
1344 log(
"Built module {0} to {1}", ReqModuleName,
1345 (*Materialized)->getModuleFilePath());
1346 Cache.add(ReqModuleName, ReqFileName, CommandHash, *Materialized);
1347 BuiltModuleFiles.addModuleFile(std::move(*Materialized));
1350 return llvm::Error::success();
1354 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1358 CachingProjectModules CachedMDB(
1359 std::move(MDB), Impl->getProjectModulesCache(), Impl->getObservedFiles());
1360 return !CachedMDB.getRequiredModules(
File).empty();
1364 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1368 CachingProjectModules CachedMDB(
1369 std::move(MDB), Impl->getProjectModulesCache(), Impl->getObservedFiles());
1370 return CachedMDB.getRequiredModules(
File);
1373std::unique_ptr<PrerequisiteModules>
1376 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
1378 elog(
"Failed to get Project Modules information for {0}",
File);
1379 return std::make_unique<FailedPrerequisiteModules>();
1381 CachingProjectModules CachedMDB(
1382 std::move(MDB), Impl->getProjectModulesCache(), Impl->getObservedFiles());
1384 std::vector<std::string> RequiredModuleNames =
1385 CachedMDB.getRequiredModules(
File);
1386 if (RequiredModuleNames.empty())
1387 return std::make_unique<ReusablePrerequisiteModules>();
1389 auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
1390 RequiredModules->setDirectModuleNames(RequiredModuleNames);
1391 for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
1393 if (llvm::Error Err = Impl->getOrBuildModuleFile(
1394 File, RequiredModuleName, TFS, CachedMDB, *RequiredModules.get())) {
1395 elog(
"Failed to build module {0}; due to {1}", RequiredModuleName,
1397 return std::make_unique<FailedPrerequisiteModules>();
1401 return std::move(RequiredModules);
1405 Impl = std::make_unique<ModulesBuilderImpl>(CDB);
1411 return Impl->observeSourcePath(
File);
1415 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++ -*-===//