12#include "clang/Frontend/FrontendAction.h"
13#include "clang/Frontend/FrontendActions.h"
14#include "clang/Serialization/ASTReader.h"
15#include "clang/Serialization/InMemoryModuleCache.h"
16#include "llvm/ADT/ScopeExit.h"
39 llvm::SmallString<128> HashedPrefix = llvm::sys::path::filename(
MainFile);
42 HashedPrefix += std::to_string(llvm::hash_value(
MainFile));
44 llvm::SmallString<256> ResultPattern;
46 llvm::sys::path::system_temp_directory(
true,
49 llvm::sys::path::append(ResultPattern,
"clangd");
50 llvm::sys::path::append(ResultPattern,
"module_files");
52 llvm::sys::path::append(ResultPattern, HashedPrefix);
54 ResultPattern.append(
"-%%-%%-%%-%%-%%-%%");
56 llvm::SmallString<256> Result;
57 llvm::sys::fs::createUniquePath(ResultPattern, Result,
60 llvm::sys::fs::create_directories(Result);
65std::string getModuleFilePath(llvm::StringRef ModuleName,
67 llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
68 auto [PrimaryModuleName, PartitionName] = ModuleName.split(
':');
69 llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
70 if (!PartitionName.empty()) {
71 ModuleFilePath.append(
"-");
72 ModuleFilePath.append(PartitionName);
75 ModuleFilePath.append(
".pcm");
76 return std::string(ModuleFilePath);
81class FailedPrerequisiteModules :
public PrerequisiteModules {
83 ~FailedPrerequisiteModules()
override =
default;
87 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {
92 canReuse(
const CompilerInvocation &
CI,
93 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override {
99 ModuleFile(StringRef ModuleName,
PathRef ModuleFilePath)
100 : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
102 ModuleFile() =
delete;
104 ModuleFile(
const ModuleFile &) =
delete;
105 ModuleFile operator=(
const ModuleFile &) =
delete;
108 ModuleFile(ModuleFile &&Other)
109 : ModuleName(std::move(Other.ModuleName)),
110 ModuleFilePath(std::move(Other.ModuleFilePath)) {
111 Other.ModuleName.clear();
112 Other.ModuleFilePath.clear();
115 ModuleFile &operator=(ModuleFile &&Other) {
120 new (
this) ModuleFile(std::move(Other));
125 if (!ModuleFilePath.empty())
126 llvm::sys::fs::remove(ModuleFilePath);
129 StringRef getModuleName()
const {
return ModuleName; }
131 StringRef getModuleFilePath()
const {
return ModuleFilePath; }
134 std::string ModuleName;
135 std::string ModuleFilePath;
141class ReusablePrerequisiteModules :
public PrerequisiteModules {
143 ReusablePrerequisiteModules() =
default;
145 ReusablePrerequisiteModules(
const ReusablePrerequisiteModules &Other) =
147 ReusablePrerequisiteModules &
148 operator=(
const ReusablePrerequisiteModules &) =
default;
149 ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) =
delete;
150 ReusablePrerequisiteModules
151 operator=(ReusablePrerequisiteModules &&) =
delete;
153 ~ReusablePrerequisiteModules()
override =
default;
155 void adjustHeaderSearchOptions(HeaderSearchOptions &Options)
const override {
157 for (
const auto &RequiredModule : RequiredModules)
158 Options.PrebuiltModuleFiles.insert_or_assign(
159 RequiredModule->getModuleName().str(),
160 RequiredModule->getModuleFilePath().str());
163 bool canReuse(
const CompilerInvocation &
CI,
164 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>)
const override;
166 bool isModuleUnitBuilt(llvm::StringRef ModuleName)
const {
167 return BuiltModuleNames.contains(ModuleName);
170 void addModuleFile(std::shared_ptr<const ModuleFile> ModuleFile) {
171 BuiltModuleNames.insert(ModuleFile->getModuleName());
172 RequiredModules.emplace_back(std::move(ModuleFile));
176 llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
178 llvm::StringSet<> BuiltModuleNames;
181bool IsModuleFileUpToDate(
PathRef ModuleFilePath,
182 const PrerequisiteModules &RequisiteModules,
183 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
184 auto HSOpts = std::make_shared<HeaderSearchOptions>();
185 RequisiteModules.adjustHeaderSearchOptions(*HSOpts);
186 HSOpts->ForceCheckCXX20ModulesInputFiles =
true;
187 HSOpts->ValidateASTInputFilesContent =
true;
190 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
191 CompilerInstance::createDiagnostics(*VFS,
new DiagnosticOptions,
195 LangOptions LangOpts;
196 LangOpts.SkipODRCheckInGMF =
true;
198 FileManager FileMgr(FileSystemOptions(), VFS);
200 SourceManager SourceMgr(*Diags, FileMgr);
202 HeaderSearch HeaderInfo(std::move(HSOpts), SourceMgr, *Diags, LangOpts,
205 TrivialModuleLoader ModuleLoader;
206 Preprocessor PP(std::make_shared<PreprocessorOptions>(), *Diags, LangOpts,
207 SourceMgr, HeaderInfo, ModuleLoader);
209 IntrusiveRefCntPtr<InMemoryModuleCache> ModuleCache =
new InMemoryModuleCache;
210 PCHContainerOperations PCHOperations;
211 ASTReader Reader(PP, *ModuleCache,
nullptr,
212 PCHOperations.getRawReader(), {});
216 Reader.setListener(
nullptr);
218 if (Reader.ReadAST(ModuleFilePath, serialization::MK_MainFile,
220 ASTReader::ARR_None) != ASTReader::Success)
223 bool UpToDate =
true;
224 Reader.getModuleManager().visit([&](serialization::ModuleFile &MF) ->
bool {
225 Reader.visitInputFiles(
227 [&](
const serialization::InputFile &IF,
bool isSystem) {
228 if (!IF.getFile() || IF.isOutOfDate())
236bool IsModuleFilesUpToDate(
237 llvm::SmallVector<PathRef> ModuleFilePaths,
238 const PrerequisiteModules &RequisiteModules,
239 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
241 ModuleFilePaths, [&RequisiteModules, VFS](
auto ModuleFilePath) {
242 return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
248llvm::Expected<ModuleFile>
249buildModuleFile(llvm::StringRef ModuleName,
PathRef ModuleUnitFileName,
250 const GlobalCompilationDatabase &CDB,
const ThreadsafeFS &TFS,
251 const ReusablePrerequisiteModules &BuiltModuleFiles) {
255 return llvm::createStringError(
256 llvm::formatv(
"No compile command for {0}", ModuleUnitFileName));
258 llvm::SmallString<256> ModuleFilesPrefix =
259 getUniqueModuleFilesPath(ModuleUnitFileName);
261 Cmd->Output = getModuleFilePath(ModuleName, ModuleFilesPrefix);
270 return llvm::createStringError(
"Failed to build compiler invocation");
275 return llvm::createStringError(
"Failed to create buffer");
279 CI->getLangOpts().SkipODRCheckInGMF =
true;
284 CI->getHeaderSearchOpts().ValidateASTInputFilesContent =
true;
286 BuiltModuleFiles.adjustHeaderSearchOptions(
CI->getHeaderSearchOpts());
293 return llvm::createStringError(
"Failed to prepare compiler instance");
295 GenerateReducedModuleInterfaceAction
Action;
298 if (
Clang->getDiagnostics().hasErrorOccurred())
299 return llvm::createStringError(
"Compilation failed");
304bool ReusablePrerequisiteModules::canReuse(
305 const CompilerInvocation &
CI,
306 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
const {
307 if (RequiredModules.empty())
310 llvm::SmallVector<llvm::StringRef> BMIPaths;
311 for (
auto &MF : RequiredModules)
312 BMIPaths.push_back(MF->getModuleFilePath());
313 return IsModuleFilesUpToDate(BMIPaths, *
this, VFS);
316class ModuleFileCache {
318 ModuleFileCache(
const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
319 const GlobalCompilationDatabase &getCDB()
const {
return CDB; }
321 std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName);
323 void add(StringRef ModuleName, std::shared_ptr<const ModuleFile> ModuleFile) {
324 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
326 ModuleFiles[ModuleName] = ModuleFile;
329 void remove(StringRef ModuleName);
332 const GlobalCompilationDatabase &CDB;
334 llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
336 std::mutex ModuleFilesMutex;
339std::shared_ptr<const ModuleFile>
340ModuleFileCache::getModule(StringRef ModuleName) {
341 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
343 auto Iter = ModuleFiles.find(ModuleName);
344 if (Iter == ModuleFiles.end())
347 if (
auto Res = Iter->second.lock())
350 ModuleFiles.erase(Iter);
354void ModuleFileCache::remove(StringRef ModuleName) {
355 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
357 ModuleFiles.erase(ModuleName);
363llvm::SmallVector<StringRef> getAllRequiredModules(ProjectModules &MDB,
364 StringRef ModuleName) {
365 llvm::SmallVector<llvm::StringRef> ModuleNames;
366 llvm::StringSet<> ModuleNamesSet;
368 auto VisitDeps = [&](StringRef ModuleName,
auto Visitor) ->
void {
369 ModuleNamesSet.insert(ModuleName);
371 for (StringRef RequiredModuleName :
372 MDB.getRequiredModules(MDB.getSourceForModuleName(ModuleName)))
373 if (ModuleNamesSet.insert(RequiredModuleName).second)
374 Visitor(RequiredModuleName, Visitor);
376 ModuleNames.push_back(ModuleName);
378 VisitDeps(ModuleName, VisitDeps);
394 ReusablePrerequisiteModules &BuiltModuleFiles);
397 ModuleFileCache Cache;
402 ReusablePrerequisiteModules &BuiltModuleFiles) {
403 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
404 return llvm::Error::success();
414 if (ModuleUnitFileName.empty())
415 return llvm::createStringError(
416 llvm::formatv(
"Don't get the module unit for module {0}", ModuleName));
419 auto ReqModuleNames = getAllRequiredModules(MDB, ModuleName);
420 for (llvm::StringRef ReqModuleName : ReqModuleNames) {
421 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
424 if (
auto Cached = Cache.getModule(ReqModuleName)) {
425 if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles,
426 TFS.view(std::nullopt))) {
427 log(
"Reusing module {0} from {1}", ModuleName,
428 Cached->getModuleFilePath());
429 BuiltModuleFiles.addModuleFile(std::move(Cached));
432 Cache.remove(ReqModuleName);
435 llvm::Expected<ModuleFile> MF = buildModuleFile(
436 ModuleName, ModuleUnitFileName,
getCDB(), TFS, BuiltModuleFiles);
437 if (llvm::Error Err = MF.takeError())
440 log(
"Built module {0} to {1}", ModuleName, MF->getModuleFilePath());
441 auto BuiltModuleFile = std::make_shared<const ModuleFile>(std::move(*MF));
442 Cache.add(ModuleName, BuiltModuleFile);
443 BuiltModuleFiles.addModuleFile(std::move(BuiltModuleFile));
446 return llvm::Error::success();
449std::unique_ptr<PrerequisiteModules>
452 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(
File);
454 elog(
"Failed to get Project Modules information for {0}",
File);
455 return std::make_unique<FailedPrerequisiteModules>();
458 std::vector<std::string> RequiredModuleNames = MDB->getRequiredModules(
File);
459 if (RequiredModuleNames.empty())
460 return std::make_unique<ReusablePrerequisiteModules>();
462 auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
463 for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
465 if (llvm::Error Err = Impl->getOrBuildModuleFile(
466 RequiredModuleName, TFS, *MDB.get(), *RequiredModules.get())) {
467 elog(
"Failed to build module {0}; due to {1}", RequiredModuleName,
469 return std::make_unique<FailedPrerequisiteModules>();
473 return std::move(RequiredModules);
477 Impl = std::make_unique<ModulesBuilderImpl>(CDB);
std::unique_ptr< CompilerInvocation > CI
Provides compilation arguments used for parsing C and C++ files.
virtual std::optional< tooling::CompileCommand > getCompileCommand(PathRef File) const =0
If there are any known-good commands for building this file, returns one.
const GlobalCompilationDatabase & getCDB() const
ModulesBuilderImpl(const GlobalCompilationDatabase &CDB)
llvm::Error getOrBuildModuleFile(StringRef ModuleName, const ThreadsafeFS &TFS, ProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles)
std::unique_ptr< PrerequisiteModules > buildPrerequisiteModulesFor(PathRef File, const ThreadsafeFS &TFS)
ModulesBuilder(const GlobalCompilationDatabase &CDB)
An interface to query the modules information in the project.
virtual PathRef getSourceForModuleName(llvm::StringRef ModuleName, PathRef RequiredSrcFile=PathRef())=0
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.
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.
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++ -*-===//