1//===----------------- ModulesBuilder.cpp ------------------------*- C++-*-===//
2//
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
6//
7//===----------------------------------------------------------------------===//
8
9#include "ModulesBuilder.h"
10#include "Compiler.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/ModuleCache.h"
16#include "llvm/ADT/ScopeExit.h"
17#include <queue>
18
19namespace clang {
20namespace clangd {
21
22namespace {
23
24// Create a path to store module files. Generally it should be:
25//
26// {TEMP_DIRS}/clangd/module_files/{hashed-file-name}-%%-%%-%%-%%-%%-%%/.
27//
28// {TEMP_DIRS} is the temporary directory for the system, e.g., "/var/tmp"
29// or "C:/TEMP".
30//
31// '%%' means random value to make the generated path unique.
32//
33// \param MainFile is used to get the root of the project from global
34// compilation database.
35//
36// TODO: Move these module fils out of the temporary directory if the module
37// files are persistent.
38llvm::SmallString<256> getUniqueModuleFilesPath(PathRef MainFile) {
39 llvm::SmallString<128> HashedPrefix = llvm::sys::path::filename(path: 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(val: llvm::hash_value(S: MainFile));
43
44 llvm::SmallString<256> ResultPattern;
45
46 llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/true,
47 result&: ResultPattern);
48
49 llvm::sys::path::append(path&: ResultPattern, a: "clangd");
50 llvm::sys::path::append(path&: ResultPattern, a: "module_files");
51
52 llvm::sys::path::append(path&: ResultPattern, a: HashedPrefix);
53
54 ResultPattern.append(RHS: "-%%-%%-%%-%%-%%-%%");
55
56 llvm::SmallString<256> Result;
57 llvm::sys::fs::createUniquePath(Model: ResultPattern, ResultPath&: Result,
58 /*MakeAbsolute=*/false);
59
60 llvm::sys::fs::create_directories(path: Result);
61 return Result;
62}
63
64// Get a unique module file path under \param ModuleFilesPrefix.
65std::string getModuleFilePath(llvm::StringRef ModuleName,
66 PathRef ModuleFilesPrefix) {
67 llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
68 auto [PrimaryModuleName, PartitionName] = ModuleName.split(Separator: ':');
69 llvm::sys::path::append(path&: ModuleFilePath, a: PrimaryModuleName);
70 if (!PartitionName.empty()) {
71 ModuleFilePath.append(RHS: "-");
72 ModuleFilePath.append(RHS: PartitionName);
73 }
74
75 ModuleFilePath.append(RHS: ".pcm");
76 return std::string(ModuleFilePath);
77}
78
79// FailedPrerequisiteModules - stands for the PrerequisiteModules which has
80// errors happened during the building process.
81class FailedPrerequisiteModules : public PrerequisiteModules {
82public:
83 ~FailedPrerequisiteModules() override = default;
84
85 // We shouldn't adjust the compilation commands based on
86 // FailedPrerequisiteModules.
87 void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override {
88 }
89
90 // FailedPrerequisiteModules can never be reused.
91 bool
92 canReuse(const CompilerInvocation &CI,
93 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) const override {
94 return false;
95 }
96};
97
98struct ModuleFile {
99 ModuleFile(StringRef ModuleName, PathRef ModuleFilePath)
100 : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
101
102 ModuleFile() = delete;
103
104 ModuleFile(const ModuleFile &) = delete;
105 ModuleFile operator=(const ModuleFile &) = delete;
106
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();
113 }
114
115 ModuleFile &operator=(ModuleFile &&Other) {
116 if (this == &Other)
117 return *this;
118
119 this->~ModuleFile();
120 new (this) ModuleFile(std::move(Other));
121 return *this;
122 }
123
124 ~ModuleFile() {
125 if (!ModuleFilePath.empty())
126 llvm::sys::fs::remove(path: ModuleFilePath);
127 }
128
129 StringRef getModuleName() const { return ModuleName; }
130
131 StringRef getModuleFilePath() const { return ModuleFilePath; }
132
133private:
134 std::string ModuleName;
135 std::string ModuleFilePath;
136};
137
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.
141class ReusablePrerequisiteModules : public PrerequisiteModules {
142public:
143 ReusablePrerequisiteModules() = default;
144
145 ReusablePrerequisiteModules(const ReusablePrerequisiteModules &Other) =
146 default;
147 ReusablePrerequisiteModules &
148 operator=(const ReusablePrerequisiteModules &) = default;
149 ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) = delete;
150 ReusablePrerequisiteModules
151 operator=(ReusablePrerequisiteModules &&) = delete;
152
153 ~ReusablePrerequisiteModules() override = default;
154
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 k: RequiredModule->getModuleName().str(),
160 obj: RequiredModule->getModuleFilePath().str());
161 }
162
163 std::string getAsString() const {
164 std::string Result;
165 llvm::raw_string_ostream OS(Result);
166 for (const auto &ModuleFile : RequiredModules) {
167 OS << "-fmodule-file=" << ModuleFile->getModuleName() << "="
168 << ModuleFile->getModuleFilePath() << " ";
169 }
170 return Result;
171 }
172
173 bool canReuse(const CompilerInvocation &CI,
174 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) const override;
175
176 bool isModuleUnitBuilt(llvm::StringRef ModuleName) const {
177 return BuiltModuleNames.contains(key: ModuleName);
178 }
179
180 void addModuleFile(std::shared_ptr<const ModuleFile> ModuleFile) {
181 BuiltModuleNames.insert(key: ModuleFile->getModuleName());
182 RequiredModules.emplace_back(Args: std::move(ModuleFile));
183 }
184
185private:
186 llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
187 // A helper class to speedup the query if a module is built.
188 llvm::StringSet<> BuiltModuleNames;
189};
190
191bool IsModuleFileUpToDate(PathRef ModuleFilePath,
192 const PrerequisiteModules &RequisiteModules,
193 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
194 HeaderSearchOptions HSOpts;
195 RequisiteModules.adjustHeaderSearchOptions(Options&: HSOpts);
196 HSOpts.ForceCheckCXX20ModulesInputFiles = true;
197 HSOpts.ValidateASTInputFilesContent = true;
198
199 clang::clangd::IgnoreDiagnostics IgnoreDiags;
200 DiagnosticOptions DiagOpts;
201 IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
202 CompilerInstance::createDiagnostics(VFS&: *VFS, Opts&: DiagOpts, Client: &IgnoreDiags,
203 /*ShouldOwnClient=*/false);
204
205 LangOptions LangOpts;
206 LangOpts.SkipODRCheckInGMF = true;
207
208 FileManager FileMgr(FileSystemOptions(), VFS);
209
210 SourceManager SourceMgr(*Diags, FileMgr);
211
212 HeaderSearch HeaderInfo(HSOpts, SourceMgr, *Diags, LangOpts,
213 /*Target=*/nullptr);
214
215 PreprocessorOptions PPOpts;
216 TrivialModuleLoader ModuleLoader;
217 Preprocessor PP(PPOpts, *Diags, LangOpts, SourceMgr, HeaderInfo,
218 ModuleLoader);
219
220 IntrusiveRefCntPtr<ModuleCache> ModCache = createCrossProcessModuleCache();
221 PCHContainerOperations PCHOperations;
222 ASTReader Reader(PP, *ModCache, /*ASTContext=*/nullptr,
223 PCHOperations.getRawReader(), {});
224
225 // We don't need any listener here. By default it will use a validator
226 // listener.
227 Reader.setListener(nullptr);
228
229 if (Reader.ReadAST(FileName: ModuleFilePath, Type: serialization::MK_MainFile,
230 ImportLoc: SourceLocation(),
231 ClientLoadCapabilities: ASTReader::ARR_None) != ASTReader::Success)
232 return false;
233
234 bool UpToDate = true;
235 Reader.getModuleManager().visit(Visitor: [&](serialization::ModuleFile &MF) -> bool {
236 Reader.visitInputFiles(
237 MF, /*IncludeSystem=*/false, /*Complain=*/false,
238 Visitor: [&](const serialization::InputFile &IF, bool isSystem) {
239 if (!IF.getFile() || IF.isOutOfDate())
240 UpToDate = false;
241 });
242 return !UpToDate;
243 });
244 return UpToDate;
245}
246
247bool IsModuleFilesUpToDate(
248 llvm::SmallVector<PathRef> ModuleFilePaths,
249 const PrerequisiteModules &RequisiteModules,
250 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
251 return llvm::all_of(
252 Range&: ModuleFilePaths, P: [&RequisiteModules, VFS](auto ModuleFilePath) {
253 return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
254 });
255}
256
257/// Build a module file for module with `ModuleName`. The information of built
258/// module file are stored in \param BuiltModuleFiles.
259llvm::Expected<ModuleFile>
260buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName,
261 const GlobalCompilationDatabase &CDB, const ThreadsafeFS &TFS,
262 const ReusablePrerequisiteModules &BuiltModuleFiles) {
263 // Try cheap operation earlier to boil-out cheaply if there are problems.
264 auto Cmd = CDB.getCompileCommand(File: ModuleUnitFileName);
265 if (!Cmd)
266 return llvm::createStringError(
267 S: llvm::formatv(Fmt: "No compile command for {0}", Vals&: ModuleUnitFileName));
268
269 llvm::SmallString<256> ModuleFilesPrefix =
270 getUniqueModuleFilesPath(MainFile: ModuleUnitFileName);
271
272 Cmd->Output = getModuleFilePath(ModuleName, ModuleFilesPrefix);
273
274 ParseInputs Inputs;
275 Inputs.TFS = &TFS;
276 Inputs.CompileCommand = std::move(*Cmd);
277
278 IgnoreDiagnostics IgnoreDiags;
279 auto CI = buildCompilerInvocation(Inputs, D&: IgnoreDiags);
280 if (!CI)
281 return llvm::createStringError(Fmt: "Failed to build compiler invocation");
282
283 auto FS = Inputs.TFS->view(CWD: Inputs.CompileCommand.Directory);
284 auto Buf = FS->getBufferForFile(Name: Inputs.CompileCommand.Filename);
285 if (!Buf)
286 return llvm::createStringError(Fmt: "Failed to create buffer");
287
288 // In clang's driver, we will suppress the check for ODR violation in GMF.
289 // See the implementation of RenderModulesOptions in Clang.cpp.
290 CI->getLangOpts().SkipODRCheckInGMF = true;
291
292 // Hash the contents of input files and store the hash value to the BMI files.
293 // So that we can check if the files are still valid when we want to reuse the
294 // BMI files.
295 CI->getHeaderSearchOpts().ValidateASTInputFilesContent = true;
296
297 BuiltModuleFiles.adjustHeaderSearchOptions(Options&: CI->getHeaderSearchOpts());
298
299 CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
300 auto Clang =
301 prepareCompilerInstance(std::move(CI), /*Preamble=*/nullptr,
302 MainFile: std::move(*Buf), std::move(FS), IgnoreDiags);
303 if (!Clang)
304 return llvm::createStringError(Fmt: "Failed to prepare compiler instance");
305
306 GenerateReducedModuleInterfaceAction Action;
307 Clang->ExecuteAction(Act&: Action);
308
309 if (Clang->getDiagnostics().hasErrorOccurred()) {
310 std::string Cmds;
311 for (const auto &Arg : Inputs.CompileCommand.CommandLine) {
312 if (!Cmds.empty())
313 Cmds += " ";
314 Cmds += Arg;
315 }
316
317 clangd::vlog(Fmt: "Failed to compile {0} with command: {1}.", Vals&: ModuleUnitFileName,
318 Vals&: Cmds);
319
320 std::string BuiltModuleFilesStr = BuiltModuleFiles.getAsString();
321 if (!BuiltModuleFilesStr.empty())
322 clangd::vlog(Fmt: "The actual used module files built by clangd is {0}",
323 Vals&: BuiltModuleFilesStr);
324
325 return llvm::createStringError(
326 S: llvm::formatv(Fmt: "Failed to compile {0}. Use '--log=verbose' to view "
327 "detailed failure reasons.",
328 Vals&: ModuleUnitFileName));
329 }
330
331 return ModuleFile{ModuleName, Inputs.CompileCommand.Output};
332}
333
334bool ReusablePrerequisiteModules::canReuse(
335 const CompilerInvocation &CI,
336 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) const {
337 if (RequiredModules.empty())
338 return true;
339
340 llvm::SmallVector<llvm::StringRef> BMIPaths;
341 for (auto &MF : RequiredModules)
342 BMIPaths.push_back(Elt: MF->getModuleFilePath());
343 return IsModuleFilesUpToDate(ModuleFilePaths: BMIPaths, RequisiteModules: *this, VFS);
344}
345
346class ModuleFileCache {
347public:
348 ModuleFileCache(const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
349 const GlobalCompilationDatabase &getCDB() const { return CDB; }
350
351 std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName);
352
353 void add(StringRef ModuleName, std::shared_ptr<const ModuleFile> ModuleFile) {
354 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
355
356 ModuleFiles[ModuleName] = ModuleFile;
357 }
358
359 void remove(StringRef ModuleName);
360
361private:
362 const GlobalCompilationDatabase &CDB;
363
364 llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
365 // Mutex to guard accesses to ModuleFiles.
366 std::mutex ModuleFilesMutex;
367};
368
369std::shared_ptr<const ModuleFile>
370ModuleFileCache::getModule(StringRef ModuleName) {
371 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
372
373 auto Iter = ModuleFiles.find(Key: ModuleName);
374 if (Iter == ModuleFiles.end())
375 return nullptr;
376
377 if (auto Res = Iter->second.lock())
378 return Res;
379
380 ModuleFiles.erase(I: Iter);
381 return nullptr;
382}
383
384void ModuleFileCache::remove(StringRef ModuleName) {
385 std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
386
387 ModuleFiles.erase(Key: ModuleName);
388}
389
390class ModuleNameToSourceCache {
391public:
392 std::string getSourceForModuleName(llvm::StringRef ModuleName) {
393 std::lock_guard<std::mutex> Lock(CacheMutex);
394 auto Iter = ModuleNameToSourceCache.find(Key: ModuleName);
395 if (Iter != ModuleNameToSourceCache.end())
396 return Iter->second;
397 return "";
398 }
399
400 void addEntry(llvm::StringRef ModuleName, PathRef Source) {
401 std::lock_guard<std::mutex> Lock(CacheMutex);
402 ModuleNameToSourceCache[ModuleName] = Source.str();
403 }
404
405 void eraseEntry(llvm::StringRef ModuleName) {
406 std::lock_guard<std::mutex> Lock(CacheMutex);
407 ModuleNameToSourceCache.erase(Key: ModuleName);
408 }
409
410private:
411 std::mutex CacheMutex;
412 llvm::StringMap<std::string> ModuleNameToSourceCache;
413};
414
415class CachingProjectModules : public ProjectModules {
416public:
417 CachingProjectModules(std::unique_ptr<ProjectModules> MDB,
418 ModuleNameToSourceCache &Cache)
419 : MDB(std::move(MDB)), Cache(Cache) {
420 assert(this->MDB && "CachingProjectModules should only be created with a "
421 "valid underlying ProjectModules");
422 }
423
424 std::vector<std::string> getRequiredModules(PathRef File) override {
425 return MDB->getRequiredModules(File);
426 }
427
428 std::string getModuleNameForSource(PathRef File) override {
429 return MDB->getModuleNameForSource(File);
430 }
431
432 std::string getSourceForModuleName(llvm::StringRef ModuleName,
433 PathRef RequiredSrcFile) override {
434 std::string CachedResult = Cache.getSourceForModuleName(ModuleName);
435
436 // Verify Cached Result by seeing if the source declaring the same module
437 // as we query.
438 if (!CachedResult.empty()) {
439 std::string ModuleNameOfCachedSource =
440 MDB->getModuleNameForSource(File: CachedResult);
441 if (ModuleNameOfCachedSource == ModuleName)
442 return CachedResult;
443
444 // Cached Result is invalid. Clear it.
445 Cache.eraseEntry(ModuleName);
446 }
447
448 auto Result = MDB->getSourceForModuleName(ModuleName, RequiredSrcFile);
449 Cache.addEntry(ModuleName, Source: Result);
450
451 return Result;
452 }
453
454private:
455 std::unique_ptr<ProjectModules> MDB;
456 ModuleNameToSourceCache &Cache;
457};
458
459/// Collect the directly and indirectly required module names for \param
460/// ModuleName in topological order. The \param ModuleName is guaranteed to
461/// be the last element in \param ModuleNames.
462llvm::SmallVector<std::string> getAllRequiredModules(PathRef RequiredSource,
463 CachingProjectModules &MDB,
464 StringRef ModuleName) {
465 llvm::SmallVector<std::string> ModuleNames;
466 llvm::StringSet<> ModuleNamesSet;
467
468 auto VisitDeps = [&](StringRef ModuleName, auto Visitor) -> void {
469 ModuleNamesSet.insert(key: ModuleName);
470
471 for (StringRef RequiredModuleName : MDB.getRequiredModules(
472 File: MDB.getSourceForModuleName(ModuleName, RequiredSrcFile: RequiredSource)))
473 if (ModuleNamesSet.insert(key: RequiredModuleName).second)
474 Visitor(RequiredModuleName, Visitor);
475
476 ModuleNames.push_back(Elt: ModuleName.str());
477 };
478 VisitDeps(ModuleName, VisitDeps);
479
480 return ModuleNames;
481}
482
483} // namespace
484
485class ModulesBuilder::ModulesBuilderImpl {
486public:
487 ModulesBuilderImpl(const GlobalCompilationDatabase &CDB) : Cache(CDB) {}
488
489 ModuleNameToSourceCache &getProjectModulesCache() {
490 return ProjectModulesCache;
491 }
492 const GlobalCompilationDatabase &getCDB() const { return Cache.getCDB(); }
493
494 llvm::Error
495 getOrBuildModuleFile(PathRef RequiredSource, StringRef ModuleName,
496 const ThreadsafeFS &TFS, CachingProjectModules &MDB,
497 ReusablePrerequisiteModules &BuiltModuleFiles);
498
499private:
500 ModuleFileCache Cache;
501 ModuleNameToSourceCache ProjectModulesCache;
502};
503
504llvm::Error ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile(
505 PathRef RequiredSource, StringRef ModuleName, const ThreadsafeFS &TFS,
506 CachingProjectModules &MDB, ReusablePrerequisiteModules &BuiltModuleFiles) {
507 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
508 return llvm::Error::success();
509
510 std::string ModuleUnitFileName =
511 MDB.getSourceForModuleName(ModuleName, RequiredSrcFile: RequiredSource);
512 /// It is possible that we're meeting third party modules (modules whose
513 /// source are not in the project. e.g, the std module may be a third-party
514 /// module for most project) or something wrong with the implementation of
515 /// ProjectModules.
516 /// FIXME: How should we treat third party modules here? If we want to ignore
517 /// third party modules, we should return true instead of false here.
518 /// Currently we simply bail out.
519 if (ModuleUnitFileName.empty())
520 return llvm::createStringError(
521 S: llvm::formatv(Fmt: "Don't get the module unit for module {0}", Vals&: ModuleName));
522
523 // Get Required modules in topological order.
524 auto ReqModuleNames = getAllRequiredModules(RequiredSource, MDB, ModuleName);
525 for (llvm::StringRef ReqModuleName : ReqModuleNames) {
526 if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName: ReqModuleName))
527 continue;
528
529 if (auto Cached = Cache.getModule(ModuleName: ReqModuleName)) {
530 if (IsModuleFileUpToDate(ModuleFilePath: Cached->getModuleFilePath(), RequisiteModules: BuiltModuleFiles,
531 VFS: TFS.view(CWD: std::nullopt))) {
532 log(Fmt: "Reusing module {0} from {1}", Vals&: ReqModuleName,
533 Vals: Cached->getModuleFilePath());
534 BuiltModuleFiles.addModuleFile(ModuleFile: std::move(Cached));
535 continue;
536 }
537 Cache.remove(ModuleName: ReqModuleName);
538 }
539
540 std::string ReqFileName =
541 MDB.getSourceForModuleName(ModuleName: ReqModuleName, RequiredSrcFile: RequiredSource);
542 llvm::Expected<ModuleFile> MF = buildModuleFile(
543 ModuleName: ReqModuleName, ModuleUnitFileName: ReqFileName, CDB: getCDB(), TFS, BuiltModuleFiles);
544 if (llvm::Error Err = MF.takeError())
545 return Err;
546
547 log(Fmt: "Built module {0} to {1}", Vals&: ReqModuleName, Vals: MF->getModuleFilePath());
548 auto BuiltModuleFile = std::make_shared<const ModuleFile>(args: std::move(*MF));
549 Cache.add(ModuleName: ReqModuleName, ModuleFile: BuiltModuleFile);
550 BuiltModuleFiles.addModuleFile(ModuleFile: std::move(BuiltModuleFile));
551 }
552
553 return llvm::Error::success();
554}
555
556std::unique_ptr<PrerequisiteModules>
557ModulesBuilder::buildPrerequisiteModulesFor(PathRef File,
558 const ThreadsafeFS &TFS) {
559 std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(File);
560 if (!MDB) {
561 elog(Fmt: "Failed to get Project Modules information for {0}", Vals&: File);
562 return std::make_unique<FailedPrerequisiteModules>();
563 }
564 CachingProjectModules CachedMDB(std::move(MDB),
565 Impl->getProjectModulesCache());
566
567 std::vector<std::string> RequiredModuleNames =
568 CachedMDB.getRequiredModules(File);
569 if (RequiredModuleNames.empty())
570 return std::make_unique<ReusablePrerequisiteModules>();
571
572 auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
573 for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
574 // Return early if there is any error.
575 if (llvm::Error Err = Impl->getOrBuildModuleFile(
576 RequiredSource: File, ModuleName: RequiredModuleName, TFS, MDB&: CachedMDB, BuiltModuleFiles&: *RequiredModules.get())) {
577 elog(Fmt: "Failed to build module {0}; due to {1}", Vals&: RequiredModuleName,
578 Vals: toString(E: std::move(Err)));
579 return std::make_unique<FailedPrerequisiteModules>();
580 }
581 }
582
583 return std::move(RequiredModules);
584}
585
586ModulesBuilder::ModulesBuilder(const GlobalCompilationDatabase &CDB) {
587 Impl = std::make_unique<ModulesBuilderImpl>(args: CDB);
588}
589
590ModulesBuilder::~ModulesBuilder() {}
591
592} // namespace clangd
593} // namespace clang
594

source code of clang-tools-extra/clangd/ModulesBuilder.cpp