1 //===----------------- ModulesBuilder.cpp ------------------------*- C++-*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "ModulesBuilder.h"
11 #include "support/Logger.h"
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"
24 // Create a path to store module files. Generally it should be:
26 // {TEMP_DIRS}/clangd/module_files/{hashed-file-name}-%%-%%-%%-%%-%%-%%/.
28 // {TEMP_DIRS} is the temporary directory for the system, e.g., "/var/tmp"
31 // '%%' means random value to make the generated path unique.
33 // \param MainFile is used to get the root of the project from global
34 // compilation database.
36 // TODO: Move these module fils out of the temporary directory if the module
37 // files are persistent.
38 llvm::SmallString
<256> getUniqueModuleFilesPath(PathRef MainFile
) {
39 llvm::SmallString
<128> HashedPrefix
= llvm::sys::path::filename(MainFile
);
40 // There might be multiple files with the same name in a project. So appending
41 // the hash value of the full path to make sure they won't conflict.
42 HashedPrefix
+= std::to_string(llvm::hash_value(MainFile
));
44 llvm::SmallString
<256> ResultPattern
;
46 llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/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
,
58 /*MakeAbsolute=*/false);
60 llvm::sys::fs::create_directories(Result
);
64 // Get a unique module file path under \param ModuleFilesPrefix.
65 std::string
getModuleFilePath(llvm::StringRef ModuleName
,
66 PathRef ModuleFilesPrefix
) {
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
);
79 // FailedPrerequisiteModules - stands for the PrerequisiteModules which has
80 // errors happened during the building process.
81 class FailedPrerequisiteModules
: public PrerequisiteModules
{
83 ~FailedPrerequisiteModules() override
= default;
85 // We shouldn't adjust the compilation commands based on
86 // FailedPrerequisiteModules.
87 void adjustHeaderSearchOptions(HeaderSearchOptions
&Options
) const override
{
90 // FailedPrerequisiteModules can never be reused.
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;
107 // The move constructor is needed for llvm::SmallVector.
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
;
138 // ReusablePrerequisiteModules - stands for PrerequisiteModules for which all
139 // the required modules are built successfully. All the module files
140 // are owned by the modules builder.
141 class 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
{
156 // Appending all built module files.
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
;
177 // A helper class to speedup the query if a module is built.
178 llvm::StringSet
<> BuiltModuleNames
;
181 bool 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;
189 clang::clangd::IgnoreDiagnostics IgnoreDiags
;
190 IntrusiveRefCntPtr
<DiagnosticsEngine
> Diags
=
191 CompilerInstance::createDiagnostics(*VFS
, new DiagnosticOptions
,
193 /*ShouldOwnClient=*/false);
195 LangOptions LangOpts
;
196 LangOpts
.SkipODRCheckInGMF
= true;
198 FileManager
FileMgr(FileSystemOptions(), VFS
);
200 SourceManager
SourceMgr(*Diags
, FileMgr
);
202 HeaderSearch
HeaderInfo(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
, /*ASTContext=*/nullptr,
212 PCHOperations
.getRawReader(), {});
214 // We don't need any listener here. By default it will use a validator
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(
226 MF
, /*IncludeSystem=*/false, /*Complain=*/false,
227 [&](const serialization::InputFile
&IF
, bool isSystem
) {
228 if (!IF
.getFile() || IF
.isOutOfDate())
236 bool 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
);
246 /// Build a module file for module with `ModuleName`. The information of built
247 /// module file are stored in \param BuiltModuleFiles.
248 llvm::Expected
<ModuleFile
>
249 buildModuleFile(llvm::StringRef ModuleName
, PathRef ModuleUnitFileName
,
250 const GlobalCompilationDatabase
&CDB
, const ThreadsafeFS
&TFS
,
251 const ReusablePrerequisiteModules
&BuiltModuleFiles
) {
252 // Try cheap operation earlier to boil-out cheaply if there are problems.
253 auto Cmd
= CDB
.getCompileCommand(ModuleUnitFileName
);
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
);
265 Inputs
.CompileCommand
= std::move(*Cmd
);
267 IgnoreDiagnostics IgnoreDiags
;
268 auto CI
= buildCompilerInvocation(Inputs
, IgnoreDiags
);
270 return llvm::createStringError("Failed to build compiler invocation");
272 auto FS
= Inputs
.TFS
->view(Inputs
.CompileCommand
.Directory
);
273 auto Buf
= FS
->getBufferForFile(Inputs
.CompileCommand
.Filename
);
275 return llvm::createStringError("Failed to create buffer");
277 // In clang's driver, we will suppress the check for ODR violation in GMF.
278 // See the implementation of RenderModulesOptions in Clang.cpp.
279 CI
->getLangOpts().SkipODRCheckInGMF
= true;
281 // Hash the contents of input files and store the hash value to the BMI files.
282 // So that we can check if the files are still valid when we want to reuse the
284 CI
->getHeaderSearchOpts().ValidateASTInputFilesContent
= true;
286 BuiltModuleFiles
.adjustHeaderSearchOptions(CI
->getHeaderSearchOpts());
288 CI
->getFrontendOpts().OutputFile
= Inputs
.CompileCommand
.Output
;
290 prepareCompilerInstance(std::move(CI
), /*Preamble=*/nullptr,
291 std::move(*Buf
), std::move(FS
), IgnoreDiags
);
293 return llvm::createStringError("Failed to prepare compiler instance");
295 GenerateReducedModuleInterfaceAction Action
;
296 Clang
->ExecuteAction(Action
);
298 if (Clang
->getDiagnostics().hasErrorOccurred())
299 return llvm::createStringError("Compilation failed");
301 return ModuleFile
{ModuleName
, Inputs
.CompileCommand
.Output
};
304 bool 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
);
316 class 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
;
335 // Mutex to guard accesses to ModuleFiles.
336 std::mutex ModuleFilesMutex
;
339 std::shared_ptr
<const ModuleFile
>
340 ModuleFileCache::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
);
354 void ModuleFileCache::remove(StringRef ModuleName
) {
355 std::lock_guard
<std::mutex
> Lock(ModuleFilesMutex
);
357 ModuleFiles
.erase(ModuleName
);
360 /// Collect the directly and indirectly required module names for \param
361 /// ModuleName in topological order. The \param ModuleName is guaranteed to
362 /// be the last element in \param ModuleNames.
363 llvm::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
);
385 class ModulesBuilder::ModulesBuilderImpl
{
387 ModulesBuilderImpl(const GlobalCompilationDatabase
&CDB
) : Cache(CDB
) {}
389 const GlobalCompilationDatabase
&getCDB() const { return Cache
.getCDB(); }
392 getOrBuildModuleFile(StringRef ModuleName
, const ThreadsafeFS
&TFS
,
394 ReusablePrerequisiteModules
&BuiltModuleFiles
);
397 ModuleFileCache Cache
;
400 llvm::Error
ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile(
401 StringRef ModuleName
, const ThreadsafeFS
&TFS
, ProjectModules
&MDB
,
402 ReusablePrerequisiteModules
&BuiltModuleFiles
) {
403 if (BuiltModuleFiles
.isModuleUnitBuilt(ModuleName
))
404 return llvm::Error::success();
406 PathRef ModuleUnitFileName
= MDB
.getSourceForModuleName(ModuleName
);
407 /// It is possible that we're meeting third party modules (modules whose
408 /// source are not in the project. e.g, the std module may be a third-party
409 /// module for most project) or something wrong with the implementation of
411 /// FIXME: How should we treat third party modules here? If we want to ignore
412 /// third party modules, we should return true instead of false here.
413 /// Currently we simply bail out.
414 if (ModuleUnitFileName
.empty())
415 return llvm::createStringError(
416 llvm::formatv("Don't get the module unit for module {0}", ModuleName
));
418 // Get Required modules in topological order.
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();
449 std::unique_ptr
<PrerequisiteModules
>
450 ModulesBuilder::buildPrerequisiteModulesFor(PathRef File
,
451 const ThreadsafeFS
&TFS
) {
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
) {
464 // Return early if there is any error.
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
,
468 toString(std::move(Err
)));
469 return std::make_unique
<FailedPrerequisiteModules
>();
473 return std::move(RequiredModules
);
476 ModulesBuilder::ModulesBuilder(const GlobalCompilationDatabase
&CDB
) {
477 Impl
= std::make_unique
<ModulesBuilderImpl
>(CDB
);
480 ModulesBuilder::~ModulesBuilder() {}
482 } // namespace clangd