12#include "clang/Frontend/FrontendAction.h"
13#include "clang/Frontend/FrontendActions.h"
14#include "clang/Serialization/ASTReader.h"
15#include "clang/Serialization/ModuleCache.h"
16#include "llvm/ADT/ScopeExit.h"
17#include "llvm/Support/CommandLine.h"
26llvm::cl::opt<bool> DebugModulesBuilder(
27 "debug-modules-builder",
28 llvm::cl::desc(
"Don't remove clangd's built module files for debugging. "
29 "Remember to remove them later after debugging."),
30 llvm::cl::init(
false));
46llvm::SmallString<256> getUniqueModuleFilesPath(
PathRef MainFile) {
47 llvm::SmallString<128> HashedPrefix = llvm::sys::path::filename(MainFile);
50 HashedPrefix += std::to_string(llvm::hash_value(MainFile));
52 llvm::SmallString<256> ResultPattern;
54 llvm::sys::path::system_temp_directory(
true,
57 llvm::sys::path::append(ResultPattern,
"clangd");
58 llvm::sys::path::append(ResultPattern,
"module_files");
60 llvm::sys::path::append(ResultPattern, HashedPrefix);
62 ResultPattern.append(
"-%%-%%-%%-%%-%%-%%");
64 llvm::SmallString<256> Result;
65 llvm::sys::fs::createUniquePath(ResultPattern, Result,
68 llvm::sys::fs::create_directories(Result);
73std::string getModuleFilePath(llvm::StringRef ModuleName,
75 llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
76 auto [PrimaryModuleName, PartitionName] = ModuleName.split(
':');
77 llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
78 if (!PartitionName.empty()) {
79 ModuleFilePath.append(
"-");
80 ModuleFilePath.append(PartitionName);
83 ModuleFilePath.append(
".pcm");
84 return std::string(ModuleFilePath);
91 ~FailedPrerequisiteModules()
override =
default;
95 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {
100 canReuse(
const CompilerInvocation &CI,
101 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override {
109 ModuleFile(StringRef ModuleName,
PathRef ModuleFilePath)
110 : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
113 ModuleFile() =
delete;
115 ModuleFile(
const ModuleFile &) =
delete;
116 ModuleFile operator=(
const ModuleFile &) =
delete;
119 ModuleFile(ModuleFile &&Other)
120 : ModuleName(std::move(Other.ModuleName)),
121 ModuleFilePath(std::move(Other.ModuleFilePath)) {
122 Other.ModuleName.clear();
123 Other.ModuleFilePath.clear();
126 ModuleFile &operator=(ModuleFile &&Other) {
131 new (
this) ModuleFile(std::move(Other));
134 virtual ~ModuleFile() =
default;
136 StringRef getModuleName()
const {
return ModuleName; }
138 StringRef getModuleFilePath()
const {
return ModuleFilePath; }
141 std::string ModuleName;
142 std::string ModuleFilePath;
146class PrebuiltModuleFile :
public ModuleFile {
153 PrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
154 : ModuleFile(ModuleName, ModuleFilePath) {}
156 static std::shared_ptr<PrebuiltModuleFile> make(StringRef ModuleName,
158 return std::make_shared<PrebuiltModuleFile>(ModuleName, ModuleFilePath,
164class BuiltModuleFile :
public ModuleFile {
171 BuiltModuleFile(StringRef ModuleName,
PathRef ModuleFilePath, CtorTag)
172 : ModuleFile(ModuleName, ModuleFilePath) {}
174 static std::shared_ptr<BuiltModuleFile> make(StringRef ModuleName,
176 return std::make_shared<BuiltModuleFile>(ModuleName, ModuleFilePath,
180 virtual ~BuiltModuleFile() {
181 if (!ModuleFilePath.empty() && !DebugModulesBuilder)
182 llvm::sys::fs::remove(ModuleFilePath);
191 ReusablePrerequisiteModules() =
default;
193 ReusablePrerequisiteModules(
const ReusablePrerequisiteModules &Other) =
195 ReusablePrerequisiteModules &
196 operator=(
const ReusablePrerequisiteModules &) =
default;
197 ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) =
delete;
198 ReusablePrerequisiteModules
199 operator=(ReusablePrerequisiteModules &&) =
delete;
201 ~ReusablePrerequisiteModules()
override =
default;
203 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {
205 for (
const auto &RequiredModule : RequiredModules)
206 Options.PrebuiltModuleFiles.insert_or_assign(
207 RequiredModule->getModuleName().str(),
208 RequiredModule->getModuleFilePath().str());
211 std::string getAsString()
const {
213 llvm::raw_string_ostream OS(Result);
214 for (
const auto &MF : RequiredModules) {
215 OS <<
"-fmodule-file=" << MF->getModuleName() <<
"="
216 << MF->getModuleFilePath() <<
" ";
221 bool canReuse(
const CompilerInvocation &CI,
222 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override;
224 bool isModuleUnitBuilt(llvm::StringRef ModuleName)
const {
225 return BuiltModuleNames.contains(ModuleName);
228 void addModuleFile(std::shared_ptr<const ModuleFile> MF) {
229 BuiltModuleNames.insert(MF->getModuleName());
230 RequiredModules.emplace_back(std::move(MF));
234 llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
236 llvm::StringSet<> BuiltModuleNames;
239bool IsModuleFileUpToDate(
PathRef ModuleFilePath,
241 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
242 HeaderSearchOptions HSOpts;
243 RequisiteModules.adjustHeaderSearchOptions(HSOpts);
244 HSOpts.ForceCheckCXX20ModulesInputFiles =
true;
245 HSOpts.ValidateASTInputFilesContent =
true;
247 clang::clangd::IgnoreDiagnostics IgnoreDiags;
248 DiagnosticOptions DiagOpts;
249 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
250 CompilerInstance::createDiagnostics(*VFS, DiagOpts, &IgnoreDiags,
253 LangOptions LangOpts;
254 LangOpts.SkipODRCheckInGMF =
true;
256 FileManager FileMgr(FileSystemOptions(), VFS);
258 SourceManager SourceMgr(*Diags, FileMgr);
260 HeaderSearch HeaderInfo(HSOpts, SourceMgr, *Diags, LangOpts,
263 PreprocessorOptions PPOpts;
264 TrivialModuleLoader ModuleLoader;
265 Preprocessor PP(PPOpts, *Diags, LangOpts, SourceMgr, HeaderInfo,
268 IntrusiveRefCntPtr<ModuleCache> ModCache = createCrossProcessModuleCache();
269 PCHContainerOperations PCHOperations;
270 CodeGenOptions CodeGenOpts;
271 ASTReader Reader(PP, *ModCache,
nullptr,
272 PCHOperations.getRawReader(), CodeGenOpts, {});
276 Reader.setListener(
nullptr);
278 if (Reader.ReadAST(ModuleFilePath, serialization::MK_MainFile,
280 ASTReader::ARR_None) != ASTReader::Success)
283 bool UpToDate =
true;
284 Reader.getModuleManager().visit([&](serialization::ModuleFile &MF) ->
bool {
285 Reader.visitInputFiles(
287 [&](
const serialization::InputFile &IF,
bool isSystem) {
288 if (!IF.getFile() || IF.isOutOfDate())
296bool IsModuleFilesUpToDate(
297 llvm::SmallVector<PathRef> ModuleFilePaths,
299 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
301 ModuleFilePaths, [&RequisiteModules, VFS](
auto ModuleFilePath) {
302 return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
308llvm::Expected<std::shared_ptr<BuiltModuleFile>>
309buildModuleFile(llvm::StringRef ModuleName,
PathRef ModuleUnitFileName,
311 const ReusablePrerequisiteModules &BuiltModuleFiles) {
313 auto Cmd = CDB.getCompileCommand(ModuleUnitFileName);
315 return llvm::createStringError(
316 llvm::formatv(
"No compile command for {0}", ModuleUnitFileName));
318 llvm::SmallString<256> ModuleFilesPrefix =
319 getUniqueModuleFilesPath(ModuleUnitFileName);
321 Cmd->Output = getModuleFilePath(ModuleName, ModuleFilesPrefix);
325 Inputs.CompileCommand = std::move(*Cmd);
330 return llvm::createStringError(
"Failed to build compiler invocation");
332 auto FS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
333 auto Buf = FS->getBufferForFile(Inputs.CompileCommand.Filename);
335 return llvm::createStringError(
"Failed to create buffer");
339 CI->getLangOpts().SkipODRCheckInGMF =
true;
344 CI->getHeaderSearchOpts().ValidateASTInputFilesContent =
true;
346 BuiltModuleFiles.adjustHeaderSearchOptions(CI->getHeaderSearchOpts());
348 CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
351 std::move(*Buf), std::move(FS), IgnoreDiags);
353 return llvm::createStringError(
"Failed to prepare compiler instance");
355 GenerateReducedModuleInterfaceAction Action;
356 Clang->ExecuteAction(Action);
358 if (Clang->getDiagnostics().hasErrorOccurred()) {
360 for (
const auto &Arg : Inputs.CompileCommand.CommandLine) {
366 clangd::vlog(
"Failed to compile {0} with command: {1}", ModuleUnitFileName,
369 std::string BuiltModuleFilesStr = BuiltModuleFiles.getAsString();
370 if (!BuiltModuleFilesStr.empty())
371 clangd::vlog(
"The actual used module files built by clangd is {0}",
372 BuiltModuleFilesStr);
374 return llvm::createStringError(
375 llvm::formatv(
"Failed to compile {0}. Use '--log=verbose' to view "
376 "detailed failure reasons. It is helpful to use "
377 "'--debug-modules-builder' flag to keep the clangd's "
378 "built module files to reproduce the failure for "
379 "debugging. Remember to remove them after debugging.",
380 ModuleUnitFileName));
383 return BuiltModuleFile::make(ModuleName, Inputs.CompileCommand.Output);
386bool ReusablePrerequisiteModules::canReuse(
387 const CompilerInvocation &CI,
388 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
const {
389 if (RequiredModules.empty())
392 llvm::SmallVector<llvm::StringRef> BMIPaths;
393 for (
auto &MF : RequiredModules)
394 BMIPaths.push_back(MF->getModuleFilePath());
395 return IsModuleFilesUpToDate(BMIPaths, *
this, VFS);
398class ModuleFileCache {
400 ModuleFileCache(
const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
401 const GlobalCompilationDatabase &getCDB()
const {
return CDB; }
403 std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName);
405 void add(StringRef ModuleName, std::shared_ptr<const ModuleFile> ModuleFile) {
406 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
408 ModuleFiles[ModuleName] = ModuleFile;
411 void remove(StringRef ModuleName);
414 const GlobalCompilationDatabase &CDB;
416 llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
418 std::mutex ModuleFilesMutex;
421std::shared_ptr<const ModuleFile>
422ModuleFileCache::getModule(StringRef ModuleName) {
423 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
425 auto Iter = ModuleFiles.find(ModuleName);
426 if (Iter == ModuleFiles.end())
429 if (
auto Res = Iter->second.lock())
432 ModuleFiles.erase(Iter);
436void ModuleFileCache::remove(StringRef ModuleName) {
437 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
439 ModuleFiles.erase(ModuleName);
442class ModuleNameToSourceCache {
444 std::string getSourceForModuleName(llvm::StringRef ModuleName) {
445 std::lock_guard<std::mutex> Lock(CacheMutex);
446 auto Iter = ModuleNameToSourceCache.find(ModuleName);
447 if (Iter != ModuleNameToSourceCache.end())
452 void addEntry(llvm::StringRef ModuleName, PathRef Source) {
453 std::lock_guard<std::mutex> Lock(CacheMutex);
454 ModuleNameToSourceCache[ModuleName] = Source.str();
457 void eraseEntry(llvm::StringRef ModuleName) {
458 std::lock_guard<std::mutex> Lock(CacheMutex);
459 ModuleNameToSourceCache.erase(ModuleName);
463 std::mutex CacheMutex;
464 llvm::StringMap<std::string> ModuleNameToSourceCache;
467class CachingProjectModules :
public ProjectModules {
469 CachingProjectModules(std::unique_ptr<ProjectModules> MDB,
470 ModuleNameToSourceCache &Cache)
471 : MDB(std::move(MDB)), Cache(Cache) {
472 assert(this->MDB &&
"CachingProjectModules should only be created with a "
473 "valid underlying ProjectModules");
476 std::vector<std::string> getRequiredModules(PathRef File)
override {
477 return MDB->getRequiredModules(File);
480 std::string getModuleNameForSource(PathRef File)
override {
481 return MDB->getModuleNameForSource(File);
484 std::string getSourceForModuleName(llvm::StringRef ModuleName,
485 PathRef RequiredSrcFile)
override {
486 std::string CachedResult = Cache.getSourceForModuleName(ModuleName);
490 if (!CachedResult.empty()) {
491 std::string ModuleNameOfCachedSource =
492 MDB->getModuleNameForSource(CachedResult);
493 if (ModuleNameOfCachedSource == ModuleName)
497 Cache.eraseEntry(ModuleName);
500 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
501 Cache.addEntry(ModuleName, Result);
507 std::unique_ptr<ProjectModules> MDB;
508 ModuleNameToSourceCache &Cache;
514llvm::SmallVector<std::string> getAllRequiredModules(
PathRef RequiredSource,
515 CachingProjectModules &MDB,
516 StringRef ModuleName) {
517 llvm::SmallVector<std::string> ModuleNames;
518 llvm::StringSet<> ModuleNamesSet;
520 auto VisitDeps = [&](StringRef ModuleName,
auto Visitor) ->
void {
521 ModuleNamesSet.insert(ModuleName);
523 for (StringRef RequiredModuleName : MDB.getRequiredModules(
524 MDB.getSourceForModuleName(ModuleName, RequiredSource)))
525 if (ModuleNamesSet.insert(RequiredModuleName).second)
526 Visitor(RequiredModuleName, Visitor);
528 ModuleNames.push_back(ModuleName.str());
530 VisitDeps(ModuleName, VisitDeps);
542 return ProjectModulesCache;
547 getOrBuildModuleFile(
PathRef RequiredSource, StringRef ModuleName,
549 ReusablePrerequisiteModules &BuiltModuleFiles);
553 void getPrebuiltModuleFile(StringRef ModuleName,
PathRef ModuleUnitFileName,
555 ReusablePrerequisiteModules &BuiltModuleFiles);
557 ModuleFileCache Cache;
558 ModuleNameToSourceCache ProjectModulesCache;
561void ModulesBuilder::ModulesBuilderImpl::getPrebuiltModuleFile(
563 ReusablePrerequisiteModules &BuiltModuleFiles) {
564 auto Cmd = getCDB().getCompileCommand(ModuleUnitFileName);
582 for (
auto &[ModuleName, ModuleFilePath] :
583 CI->getHeaderSearchOpts().PrebuiltModuleFiles) {
584 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
587 if (IsModuleFileUpToDate(ModuleFilePath, BuiltModuleFiles,
588 TFS.
view(std::nullopt))) {
589 log(
"Reusing prebuilt module file {0} of module {1} for {2}",
590 ModuleFilePath, ModuleName, ModuleUnitFileName);
591 BuiltModuleFiles.addModuleFile(
592 PrebuiltModuleFile::make(ModuleName, ModuleFilePath));
599 CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles) {
600 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
601 return llvm::Error::success();
603 std::string ModuleUnitFileName =
604 MDB.getSourceForModuleName(ModuleName, RequiredSource);
612 if (ModuleUnitFileName.empty())
613 return llvm::createStringError(
614 llvm::formatv(
"Don't get the module unit for module {0}", ModuleName));
619 getPrebuiltModuleFile(ModuleName, ModuleUnitFileName, TFS, BuiltModuleFiles);
622 auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName);
623 for (llvm::StringRef ReqModuleName : ReqModuleNames) {
624 if (BuiltModuleFiles.isModuleUnitBuilt(ReqModuleName))
627 if (
auto Cached = Cache.getModule(ReqModuleName)) {
628 if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles,
629 TFS.
view(std::nullopt))) {
630 log(
"Reusing module {0} from {1}", ReqModuleName,
631 Cached->getModuleFilePath());
632 BuiltModuleFiles.addModuleFile(std::move(Cached));
635 Cache.remove(ReqModuleName);
638 std::string ReqFileName =
639 MDB.getSourceForModuleName(ReqModuleName, RequiredSource);
640 llvm::Expected<std::shared_ptr<BuiltModuleFile>> MF = buildModuleFile(
641 ReqModuleName, ReqFileName,
getCDB(), TFS, BuiltModuleFiles);
642 if (llvm::Error Err = MF.takeError())
645 log(
"Built module {0} to {1}", ReqModuleName, (*MF)->getModuleFilePath());
646 Cache.add(ReqModuleName, *MF);
647 BuiltModuleFiles.addModuleFile(std::move(*MF));
650 return llvm::Error::success();
653std::unique_ptr<PrerequisiteModules>
656 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
658 elog(
"Failed to get Project Modules information for {0}",
File);
659 return std::make_unique<FailedPrerequisiteModules>();
661 CachingProjectModules CachedMDB(std::move(MDB),
662 Impl->getProjectModulesCache());
664 std::vector<std::string> RequiredModuleNames =
665 CachedMDB.getRequiredModules(
File);
666 if (RequiredModuleNames.empty())
667 return std::make_unique<ReusablePrerequisiteModules>();
669 auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
670 for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
672 if (llvm::Error Err = Impl->getOrBuildModuleFile(
673 File, RequiredModuleName, TFS, CachedMDB, *RequiredModules.get())) {
674 elog(
"Failed to build module {0}; due to {1}", RequiredModuleName,
676 return std::make_unique<FailedPrerequisiteModules>();
680 return std::move(RequiredModules);
684 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)
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::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.
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.
void elog(const char *Fmt, Ts &&... Vals)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//