xref: /llvm-project/clang-tools-extra/clangd/ModulesBuilder.cpp (revision 6bb5d6ae23cace42bd108ca14e17e863c73bbb5c)
1fe6c2400SChuanqi Xu //===----------------- ModulesBuilder.cpp ------------------------*- C++-*-===//
2fe6c2400SChuanqi Xu //
3fe6c2400SChuanqi Xu // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6c2400SChuanqi Xu // See https://llvm.org/LICENSE.txt for license information.
5fe6c2400SChuanqi Xu // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6c2400SChuanqi Xu //
7fe6c2400SChuanqi Xu //===----------------------------------------------------------------------===//
8fe6c2400SChuanqi Xu 
9fe6c2400SChuanqi Xu #include "ModulesBuilder.h"
10fe6c2400SChuanqi Xu #include "Compiler.h"
11fe6c2400SChuanqi Xu #include "support/Logger.h"
12fe6c2400SChuanqi Xu #include "clang/Frontend/FrontendAction.h"
13fe6c2400SChuanqi Xu #include "clang/Frontend/FrontendActions.h"
14fe6c2400SChuanqi Xu #include "clang/Serialization/ASTReader.h"
15e9b7fe8eSChuanqi Xu #include "clang/Serialization/InMemoryModuleCache.h"
16e385e0d3SChuanqi Xu #include "llvm/ADT/ScopeExit.h"
17e385e0d3SChuanqi Xu #include <queue>
18fe6c2400SChuanqi Xu 
19fe6c2400SChuanqi Xu namespace clang {
20fe6c2400SChuanqi Xu namespace clangd {
21fe6c2400SChuanqi Xu 
22fe6c2400SChuanqi Xu namespace {
23fe6c2400SChuanqi Xu 
24fe6c2400SChuanqi Xu // Create a path to store module files. Generally it should be:
25fe6c2400SChuanqi Xu //
26fe6c2400SChuanqi Xu //   {TEMP_DIRS}/clangd/module_files/{hashed-file-name}-%%-%%-%%-%%-%%-%%/.
27fe6c2400SChuanqi Xu //
28fe6c2400SChuanqi Xu // {TEMP_DIRS} is the temporary directory for the system, e.g., "/var/tmp"
29fe6c2400SChuanqi Xu // or "C:/TEMP".
30fe6c2400SChuanqi Xu //
31fe6c2400SChuanqi Xu // '%%' means random value to make the generated path unique.
32fe6c2400SChuanqi Xu //
33fe6c2400SChuanqi Xu // \param MainFile is used to get the root of the project from global
34fe6c2400SChuanqi Xu // compilation database.
35fe6c2400SChuanqi Xu //
36fe6c2400SChuanqi Xu // TODO: Move these module fils out of the temporary directory if the module
37fe6c2400SChuanqi Xu // files are persistent.
38fe6c2400SChuanqi Xu llvm::SmallString<256> getUniqueModuleFilesPath(PathRef MainFile) {
39fe6c2400SChuanqi Xu   llvm::SmallString<128> HashedPrefix = llvm::sys::path::filename(MainFile);
40fe6c2400SChuanqi Xu   // There might be multiple files with the same name in a project. So appending
41fe6c2400SChuanqi Xu   // the hash value of the full path to make sure they won't conflict.
42fe6c2400SChuanqi Xu   HashedPrefix += std::to_string(llvm::hash_value(MainFile));
43fe6c2400SChuanqi Xu 
44fe6c2400SChuanqi Xu   llvm::SmallString<256> ResultPattern;
45fe6c2400SChuanqi Xu 
46fe6c2400SChuanqi Xu   llvm::sys::path::system_temp_directory(/*erasedOnReboot=*/true,
47fe6c2400SChuanqi Xu                                          ResultPattern);
48fe6c2400SChuanqi Xu 
49fe6c2400SChuanqi Xu   llvm::sys::path::append(ResultPattern, "clangd");
50fe6c2400SChuanqi Xu   llvm::sys::path::append(ResultPattern, "module_files");
51fe6c2400SChuanqi Xu 
52fe6c2400SChuanqi Xu   llvm::sys::path::append(ResultPattern, HashedPrefix);
53fe6c2400SChuanqi Xu 
54fe6c2400SChuanqi Xu   ResultPattern.append("-%%-%%-%%-%%-%%-%%");
55fe6c2400SChuanqi Xu 
56fe6c2400SChuanqi Xu   llvm::SmallString<256> Result;
57fe6c2400SChuanqi Xu   llvm::sys::fs::createUniquePath(ResultPattern, Result,
58fe6c2400SChuanqi Xu                                   /*MakeAbsolute=*/false);
59fe6c2400SChuanqi Xu 
60fe6c2400SChuanqi Xu   llvm::sys::fs::create_directories(Result);
61fe6c2400SChuanqi Xu   return Result;
62fe6c2400SChuanqi Xu }
63fe6c2400SChuanqi Xu 
64fe6c2400SChuanqi Xu // Get a unique module file path under \param ModuleFilesPrefix.
65fe6c2400SChuanqi Xu std::string getModuleFilePath(llvm::StringRef ModuleName,
66fe6c2400SChuanqi Xu                               PathRef ModuleFilesPrefix) {
67fe6c2400SChuanqi Xu   llvm::SmallString<256> ModuleFilePath(ModuleFilesPrefix);
68fe6c2400SChuanqi Xu   auto [PrimaryModuleName, PartitionName] = ModuleName.split(':');
69fe6c2400SChuanqi Xu   llvm::sys::path::append(ModuleFilePath, PrimaryModuleName);
70fe6c2400SChuanqi Xu   if (!PartitionName.empty()) {
71fe6c2400SChuanqi Xu     ModuleFilePath.append("-");
72fe6c2400SChuanqi Xu     ModuleFilePath.append(PartitionName);
73fe6c2400SChuanqi Xu   }
74fe6c2400SChuanqi Xu 
75fe6c2400SChuanqi Xu   ModuleFilePath.append(".pcm");
76fe6c2400SChuanqi Xu   return std::string(ModuleFilePath);
77fe6c2400SChuanqi Xu }
78fe6c2400SChuanqi Xu 
79fe6c2400SChuanqi Xu // FailedPrerequisiteModules - stands for the PrerequisiteModules which has
80fe6c2400SChuanqi Xu // errors happened during the building process.
81fe6c2400SChuanqi Xu class FailedPrerequisiteModules : public PrerequisiteModules {
82fe6c2400SChuanqi Xu public:
83fe6c2400SChuanqi Xu   ~FailedPrerequisiteModules() override = default;
84fe6c2400SChuanqi Xu 
85fe6c2400SChuanqi Xu   // We shouldn't adjust the compilation commands based on
86fe6c2400SChuanqi Xu   // FailedPrerequisiteModules.
87fe6c2400SChuanqi Xu   void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override {
88fe6c2400SChuanqi Xu   }
89fe6c2400SChuanqi Xu 
90fe6c2400SChuanqi Xu   // FailedPrerequisiteModules can never be reused.
91fe6c2400SChuanqi Xu   bool
92fe6c2400SChuanqi Xu   canReuse(const CompilerInvocation &CI,
93fe6c2400SChuanqi Xu            llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) const override {
94fe6c2400SChuanqi Xu     return false;
95fe6c2400SChuanqi Xu   }
96fe6c2400SChuanqi Xu };
97fe6c2400SChuanqi Xu 
98448d8fa8SChuanqi Xu struct ModuleFile {
99448d8fa8SChuanqi Xu   ModuleFile(StringRef ModuleName, PathRef ModuleFilePath)
100448d8fa8SChuanqi Xu       : ModuleName(ModuleName.str()), ModuleFilePath(ModuleFilePath.str()) {}
101448d8fa8SChuanqi Xu 
102448d8fa8SChuanqi Xu   ModuleFile() = delete;
103448d8fa8SChuanqi Xu 
104448d8fa8SChuanqi Xu   ModuleFile(const ModuleFile &) = delete;
105448d8fa8SChuanqi Xu   ModuleFile operator=(const ModuleFile &) = delete;
106448d8fa8SChuanqi Xu 
107448d8fa8SChuanqi Xu   // The move constructor is needed for llvm::SmallVector.
108448d8fa8SChuanqi Xu   ModuleFile(ModuleFile &&Other)
109448d8fa8SChuanqi Xu       : ModuleName(std::move(Other.ModuleName)),
110448d8fa8SChuanqi Xu         ModuleFilePath(std::move(Other.ModuleFilePath)) {
111448d8fa8SChuanqi Xu     Other.ModuleName.clear();
112448d8fa8SChuanqi Xu     Other.ModuleFilePath.clear();
113448d8fa8SChuanqi Xu   }
114448d8fa8SChuanqi Xu 
115448d8fa8SChuanqi Xu   ModuleFile &operator=(ModuleFile &&Other) {
116448d8fa8SChuanqi Xu     if (this == &Other)
117448d8fa8SChuanqi Xu       return *this;
118448d8fa8SChuanqi Xu 
119448d8fa8SChuanqi Xu     this->~ModuleFile();
120448d8fa8SChuanqi Xu     new (this) ModuleFile(std::move(Other));
121448d8fa8SChuanqi Xu     return *this;
122448d8fa8SChuanqi Xu   }
123448d8fa8SChuanqi Xu 
124448d8fa8SChuanqi Xu   ~ModuleFile() {
125448d8fa8SChuanqi Xu     if (!ModuleFilePath.empty())
126448d8fa8SChuanqi Xu       llvm::sys::fs::remove(ModuleFilePath);
127448d8fa8SChuanqi Xu   }
128448d8fa8SChuanqi Xu 
129e385e0d3SChuanqi Xu   StringRef getModuleName() const { return ModuleName; }
130e385e0d3SChuanqi Xu 
131e385e0d3SChuanqi Xu   StringRef getModuleFilePath() const { return ModuleFilePath; }
132e385e0d3SChuanqi Xu 
133e385e0d3SChuanqi Xu private:
134448d8fa8SChuanqi Xu   std::string ModuleName;
135448d8fa8SChuanqi Xu   std::string ModuleFilePath;
136448d8fa8SChuanqi Xu };
137448d8fa8SChuanqi Xu 
138e385e0d3SChuanqi Xu // ReusablePrerequisiteModules - stands for PrerequisiteModules for which all
139e385e0d3SChuanqi Xu // the required modules are built successfully. All the module files
140e385e0d3SChuanqi Xu // are owned by the modules builder.
141e385e0d3SChuanqi Xu class ReusablePrerequisiteModules : public PrerequisiteModules {
142e385e0d3SChuanqi Xu public:
143e385e0d3SChuanqi Xu   ReusablePrerequisiteModules() = default;
144e385e0d3SChuanqi Xu 
145e385e0d3SChuanqi Xu   ReusablePrerequisiteModules(const ReusablePrerequisiteModules &Other) =
146e385e0d3SChuanqi Xu       default;
147e385e0d3SChuanqi Xu   ReusablePrerequisiteModules &
148e385e0d3SChuanqi Xu   operator=(const ReusablePrerequisiteModules &) = default;
149e385e0d3SChuanqi Xu   ReusablePrerequisiteModules(ReusablePrerequisiteModules &&) = delete;
150e385e0d3SChuanqi Xu   ReusablePrerequisiteModules
151e385e0d3SChuanqi Xu   operator=(ReusablePrerequisiteModules &&) = delete;
152e385e0d3SChuanqi Xu 
153e385e0d3SChuanqi Xu   ~ReusablePrerequisiteModules() override = default;
154e385e0d3SChuanqi Xu 
155e385e0d3SChuanqi Xu   void adjustHeaderSearchOptions(HeaderSearchOptions &Options) const override {
156e385e0d3SChuanqi Xu     // Appending all built module files.
157e385e0d3SChuanqi Xu     for (const auto &RequiredModule : RequiredModules)
158e385e0d3SChuanqi Xu       Options.PrebuiltModuleFiles.insert_or_assign(
159e385e0d3SChuanqi Xu           RequiredModule->getModuleName().str(),
160e385e0d3SChuanqi Xu           RequiredModule->getModuleFilePath().str());
161e385e0d3SChuanqi Xu   }
162e385e0d3SChuanqi Xu 
163e385e0d3SChuanqi Xu   bool canReuse(const CompilerInvocation &CI,
164e385e0d3SChuanqi Xu                 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>) const override;
165e385e0d3SChuanqi Xu 
166e385e0d3SChuanqi Xu   bool isModuleUnitBuilt(llvm::StringRef ModuleName) const {
167e385e0d3SChuanqi Xu     return BuiltModuleNames.contains(ModuleName);
168e385e0d3SChuanqi Xu   }
169e385e0d3SChuanqi Xu 
170e385e0d3SChuanqi Xu   void addModuleFile(std::shared_ptr<const ModuleFile> ModuleFile) {
171e385e0d3SChuanqi Xu     BuiltModuleNames.insert(ModuleFile->getModuleName());
172e385e0d3SChuanqi Xu     RequiredModules.emplace_back(std::move(ModuleFile));
173e385e0d3SChuanqi Xu   }
174e385e0d3SChuanqi Xu 
175e385e0d3SChuanqi Xu private:
176e385e0d3SChuanqi Xu   llvm::SmallVector<std::shared_ptr<const ModuleFile>, 8> RequiredModules;
177e385e0d3SChuanqi Xu   // A helper class to speedup the query if a module is built.
178e385e0d3SChuanqi Xu   llvm::StringSet<> BuiltModuleNames;
179e385e0d3SChuanqi Xu };
180e385e0d3SChuanqi Xu 
181e9b7fe8eSChuanqi Xu bool IsModuleFileUpToDate(PathRef ModuleFilePath,
182e9b7fe8eSChuanqi Xu                           const PrerequisiteModules &RequisiteModules,
183e9b7fe8eSChuanqi Xu                           llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
184448d8fa8SChuanqi Xu   auto HSOpts = std::make_shared<HeaderSearchOptions>();
185d68059bcSChuanqi Xu   RequisiteModules.adjustHeaderSearchOptions(*HSOpts);
186448d8fa8SChuanqi Xu   HSOpts->ForceCheckCXX20ModulesInputFiles = true;
187448d8fa8SChuanqi Xu   HSOpts->ValidateASTInputFilesContent = true;
188448d8fa8SChuanqi Xu 
189e9b7fe8eSChuanqi Xu   clang::clangd::IgnoreDiagnostics IgnoreDiags;
190e9b7fe8eSChuanqi Xu   IntrusiveRefCntPtr<DiagnosticsEngine> Diags =
191df9a14d7SKadir Cetinkaya       CompilerInstance::createDiagnostics(*VFS, new DiagnosticOptions,
192df9a14d7SKadir Cetinkaya                                           &IgnoreDiags,
193e9b7fe8eSChuanqi Xu                                           /*ShouldOwnClient=*/false);
194e9b7fe8eSChuanqi Xu 
195e9b7fe8eSChuanqi Xu   LangOptions LangOpts;
196e9b7fe8eSChuanqi Xu   LangOpts.SkipODRCheckInGMF = true;
197e9b7fe8eSChuanqi Xu 
198e9b7fe8eSChuanqi Xu   FileManager FileMgr(FileSystemOptions(), VFS);
199e9b7fe8eSChuanqi Xu 
200e9b7fe8eSChuanqi Xu   SourceManager SourceMgr(*Diags, FileMgr);
201e9b7fe8eSChuanqi Xu 
202*6bb5d6aeSZahira Ammarguellat   HeaderSearch HeaderInfo(std::move(HSOpts), SourceMgr, *Diags, LangOpts,
203e9b7fe8eSChuanqi Xu                           /*Target=*/nullptr);
204e9b7fe8eSChuanqi Xu 
205e9b7fe8eSChuanqi Xu   TrivialModuleLoader ModuleLoader;
206e9b7fe8eSChuanqi Xu   Preprocessor PP(std::make_shared<PreprocessorOptions>(), *Diags, LangOpts,
207e9b7fe8eSChuanqi Xu                   SourceMgr, HeaderInfo, ModuleLoader);
208e9b7fe8eSChuanqi Xu 
209e9b7fe8eSChuanqi Xu   IntrusiveRefCntPtr<InMemoryModuleCache> ModuleCache = new InMemoryModuleCache;
210448d8fa8SChuanqi Xu   PCHContainerOperations PCHOperations;
211e9b7fe8eSChuanqi Xu   ASTReader Reader(PP, *ModuleCache, /*ASTContext=*/nullptr,
212e9b7fe8eSChuanqi Xu                    PCHOperations.getRawReader(), {});
213448d8fa8SChuanqi Xu 
214e9b7fe8eSChuanqi Xu   // We don't need any listener here. By default it will use a validator
215e9b7fe8eSChuanqi Xu   // listener.
216e9b7fe8eSChuanqi Xu   Reader.setListener(nullptr);
217448d8fa8SChuanqi Xu 
218e9b7fe8eSChuanqi Xu   if (Reader.ReadAST(ModuleFilePath, serialization::MK_MainFile,
219e9b7fe8eSChuanqi Xu                      SourceLocation(),
220e9b7fe8eSChuanqi Xu                      ASTReader::ARR_None) != ASTReader::Success)
221448d8fa8SChuanqi Xu     return false;
222448d8fa8SChuanqi Xu 
223448d8fa8SChuanqi Xu   bool UpToDate = true;
224e9b7fe8eSChuanqi Xu   Reader.getModuleManager().visit([&](serialization::ModuleFile &MF) -> bool {
225e9b7fe8eSChuanqi Xu     Reader.visitInputFiles(
226448d8fa8SChuanqi Xu         MF, /*IncludeSystem=*/false, /*Complain=*/false,
227448d8fa8SChuanqi Xu         [&](const serialization::InputFile &IF, bool isSystem) {
228448d8fa8SChuanqi Xu           if (!IF.getFile() || IF.isOutOfDate())
229448d8fa8SChuanqi Xu             UpToDate = false;
230448d8fa8SChuanqi Xu         });
231448d8fa8SChuanqi Xu     return !UpToDate;
232448d8fa8SChuanqi Xu   });
233448d8fa8SChuanqi Xu   return UpToDate;
234448d8fa8SChuanqi Xu }
235448d8fa8SChuanqi Xu 
236448d8fa8SChuanqi Xu bool IsModuleFilesUpToDate(
237448d8fa8SChuanqi Xu     llvm::SmallVector<PathRef> ModuleFilePaths,
238e9b7fe8eSChuanqi Xu     const PrerequisiteModules &RequisiteModules,
239e9b7fe8eSChuanqi Xu     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) {
240e9b7fe8eSChuanqi Xu   return llvm::all_of(
241e9b7fe8eSChuanqi Xu       ModuleFilePaths, [&RequisiteModules, VFS](auto ModuleFilePath) {
242e9b7fe8eSChuanqi Xu         return IsModuleFileUpToDate(ModuleFilePath, RequisiteModules, VFS);
243448d8fa8SChuanqi Xu       });
244448d8fa8SChuanqi Xu }
245448d8fa8SChuanqi Xu 
246e385e0d3SChuanqi Xu /// Build a module file for module with `ModuleName`. The information of built
247e385e0d3SChuanqi Xu /// module file are stored in \param BuiltModuleFiles.
248e385e0d3SChuanqi Xu llvm::Expected<ModuleFile>
249e385e0d3SChuanqi Xu buildModuleFile(llvm::StringRef ModuleName, PathRef ModuleUnitFileName,
250e385e0d3SChuanqi Xu                 const GlobalCompilationDatabase &CDB, const ThreadsafeFS &TFS,
251e385e0d3SChuanqi Xu                 const ReusablePrerequisiteModules &BuiltModuleFiles) {
252fe6c2400SChuanqi Xu   // Try cheap operation earlier to boil-out cheaply if there are problems.
253fe6c2400SChuanqi Xu   auto Cmd = CDB.getCompileCommand(ModuleUnitFileName);
254fe6c2400SChuanqi Xu   if (!Cmd)
255fe6c2400SChuanqi Xu     return llvm::createStringError(
256fe6c2400SChuanqi Xu         llvm::formatv("No compile command for {0}", ModuleUnitFileName));
257fe6c2400SChuanqi Xu 
258e385e0d3SChuanqi Xu   llvm::SmallString<256> ModuleFilesPrefix =
259e385e0d3SChuanqi Xu       getUniqueModuleFilesPath(ModuleUnitFileName);
260fe6c2400SChuanqi Xu 
261fe6c2400SChuanqi Xu   Cmd->Output = getModuleFilePath(ModuleName, ModuleFilesPrefix);
262fe6c2400SChuanqi Xu 
263fe6c2400SChuanqi Xu   ParseInputs Inputs;
264fe6c2400SChuanqi Xu   Inputs.TFS = &TFS;
265fe6c2400SChuanqi Xu   Inputs.CompileCommand = std::move(*Cmd);
266fe6c2400SChuanqi Xu 
267fe6c2400SChuanqi Xu   IgnoreDiagnostics IgnoreDiags;
268fe6c2400SChuanqi Xu   auto CI = buildCompilerInvocation(Inputs, IgnoreDiags);
269fe6c2400SChuanqi Xu   if (!CI)
270fe6c2400SChuanqi Xu     return llvm::createStringError("Failed to build compiler invocation");
271fe6c2400SChuanqi Xu 
272fe6c2400SChuanqi Xu   auto FS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
273fe6c2400SChuanqi Xu   auto Buf = FS->getBufferForFile(Inputs.CompileCommand.Filename);
274fe6c2400SChuanqi Xu   if (!Buf)
275fe6c2400SChuanqi Xu     return llvm::createStringError("Failed to create buffer");
276fe6c2400SChuanqi Xu 
277fe6c2400SChuanqi Xu   // In clang's driver, we will suppress the check for ODR violation in GMF.
278fe6c2400SChuanqi Xu   // See the implementation of RenderModulesOptions in Clang.cpp.
279fe6c2400SChuanqi Xu   CI->getLangOpts().SkipODRCheckInGMF = true;
280fe6c2400SChuanqi Xu 
281fe6c2400SChuanqi Xu   // Hash the contents of input files and store the hash value to the BMI files.
282fe6c2400SChuanqi Xu   // So that we can check if the files are still valid when we want to reuse the
283fe6c2400SChuanqi Xu   // BMI files.
284fe6c2400SChuanqi Xu   CI->getHeaderSearchOpts().ValidateASTInputFilesContent = true;
285fe6c2400SChuanqi Xu 
286fe6c2400SChuanqi Xu   BuiltModuleFiles.adjustHeaderSearchOptions(CI->getHeaderSearchOpts());
287fe6c2400SChuanqi Xu 
288fe6c2400SChuanqi Xu   CI->getFrontendOpts().OutputFile = Inputs.CompileCommand.Output;
289fe6c2400SChuanqi Xu   auto Clang =
290fe6c2400SChuanqi Xu       prepareCompilerInstance(std::move(CI), /*Preamble=*/nullptr,
291fe6c2400SChuanqi Xu                               std::move(*Buf), std::move(FS), IgnoreDiags);
292fe6c2400SChuanqi Xu   if (!Clang)
293fe6c2400SChuanqi Xu     return llvm::createStringError("Failed to prepare compiler instance");
294fe6c2400SChuanqi Xu 
295fe6c2400SChuanqi Xu   GenerateReducedModuleInterfaceAction Action;
296fe6c2400SChuanqi Xu   Clang->ExecuteAction(Action);
297fe6c2400SChuanqi Xu 
298fe6c2400SChuanqi Xu   if (Clang->getDiagnostics().hasErrorOccurred())
299fe6c2400SChuanqi Xu     return llvm::createStringError("Compilation failed");
300fe6c2400SChuanqi Xu 
301e385e0d3SChuanqi Xu   return ModuleFile{ModuleName, Inputs.CompileCommand.Output};
302e385e0d3SChuanqi Xu }
303e385e0d3SChuanqi Xu 
304e385e0d3SChuanqi Xu bool ReusablePrerequisiteModules::canReuse(
305e385e0d3SChuanqi Xu     const CompilerInvocation &CI,
306e385e0d3SChuanqi Xu     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS) const {
307e385e0d3SChuanqi Xu   if (RequiredModules.empty())
308e385e0d3SChuanqi Xu     return true;
309e385e0d3SChuanqi Xu 
310e385e0d3SChuanqi Xu   llvm::SmallVector<llvm::StringRef> BMIPaths;
311e385e0d3SChuanqi Xu   for (auto &MF : RequiredModules)
312e385e0d3SChuanqi Xu     BMIPaths.push_back(MF->getModuleFilePath());
313e385e0d3SChuanqi Xu   return IsModuleFilesUpToDate(BMIPaths, *this, VFS);
314e385e0d3SChuanqi Xu }
315e385e0d3SChuanqi Xu 
316e385e0d3SChuanqi Xu class ModuleFileCache {
317e385e0d3SChuanqi Xu public:
318e385e0d3SChuanqi Xu   ModuleFileCache(const GlobalCompilationDatabase &CDB) : CDB(CDB) {}
319e385e0d3SChuanqi Xu   const GlobalCompilationDatabase &getCDB() const { return CDB; }
320e385e0d3SChuanqi Xu 
321e385e0d3SChuanqi Xu   std::shared_ptr<const ModuleFile> getModule(StringRef ModuleName);
322e385e0d3SChuanqi Xu 
323e385e0d3SChuanqi Xu   void add(StringRef ModuleName, std::shared_ptr<const ModuleFile> ModuleFile) {
324e385e0d3SChuanqi Xu     std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
325e385e0d3SChuanqi Xu 
326e385e0d3SChuanqi Xu     ModuleFiles[ModuleName] = ModuleFile;
327e385e0d3SChuanqi Xu   }
328e385e0d3SChuanqi Xu 
329e385e0d3SChuanqi Xu   void remove(StringRef ModuleName);
330e385e0d3SChuanqi Xu 
331e385e0d3SChuanqi Xu private:
332e385e0d3SChuanqi Xu   const GlobalCompilationDatabase &CDB;
333e385e0d3SChuanqi Xu 
334e385e0d3SChuanqi Xu   llvm::StringMap<std::weak_ptr<const ModuleFile>> ModuleFiles;
335e385e0d3SChuanqi Xu   // Mutex to guard accesses to ModuleFiles.
336e385e0d3SChuanqi Xu   std::mutex ModuleFilesMutex;
337e385e0d3SChuanqi Xu };
338e385e0d3SChuanqi Xu 
339e385e0d3SChuanqi Xu std::shared_ptr<const ModuleFile>
340e385e0d3SChuanqi Xu ModuleFileCache::getModule(StringRef ModuleName) {
341e385e0d3SChuanqi Xu   std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
342e385e0d3SChuanqi Xu 
343e385e0d3SChuanqi Xu   auto Iter = ModuleFiles.find(ModuleName);
344e385e0d3SChuanqi Xu   if (Iter == ModuleFiles.end())
345e385e0d3SChuanqi Xu     return nullptr;
346e385e0d3SChuanqi Xu 
347e385e0d3SChuanqi Xu   if (auto Res = Iter->second.lock())
348e385e0d3SChuanqi Xu     return Res;
349e385e0d3SChuanqi Xu 
350e385e0d3SChuanqi Xu   ModuleFiles.erase(Iter);
351e385e0d3SChuanqi Xu   return nullptr;
352e385e0d3SChuanqi Xu }
353e385e0d3SChuanqi Xu 
354e385e0d3SChuanqi Xu void ModuleFileCache::remove(StringRef ModuleName) {
355e385e0d3SChuanqi Xu   std::lock_guard<std::mutex> Lock(ModuleFilesMutex);
356e385e0d3SChuanqi Xu 
357e385e0d3SChuanqi Xu   ModuleFiles.erase(ModuleName);
358e385e0d3SChuanqi Xu }
359e385e0d3SChuanqi Xu 
360e385e0d3SChuanqi Xu /// Collect the directly and indirectly required module names for \param
361e385e0d3SChuanqi Xu /// ModuleName in topological order. The \param ModuleName is guaranteed to
362e385e0d3SChuanqi Xu /// be the last element in \param ModuleNames.
363e385e0d3SChuanqi Xu llvm::SmallVector<StringRef> getAllRequiredModules(ProjectModules &MDB,
364e385e0d3SChuanqi Xu                                                    StringRef ModuleName) {
365e385e0d3SChuanqi Xu   llvm::SmallVector<llvm::StringRef> ModuleNames;
366e385e0d3SChuanqi Xu   llvm::StringSet<> ModuleNamesSet;
367e385e0d3SChuanqi Xu 
368e385e0d3SChuanqi Xu   auto VisitDeps = [&](StringRef ModuleName, auto Visitor) -> void {
369e385e0d3SChuanqi Xu     ModuleNamesSet.insert(ModuleName);
370e385e0d3SChuanqi Xu 
371e385e0d3SChuanqi Xu     for (StringRef RequiredModuleName :
372e385e0d3SChuanqi Xu          MDB.getRequiredModules(MDB.getSourceForModuleName(ModuleName)))
373e385e0d3SChuanqi Xu       if (ModuleNamesSet.insert(RequiredModuleName).second)
374e385e0d3SChuanqi Xu         Visitor(RequiredModuleName, Visitor);
375e385e0d3SChuanqi Xu 
376e385e0d3SChuanqi Xu     ModuleNames.push_back(ModuleName);
377e385e0d3SChuanqi Xu   };
378e385e0d3SChuanqi Xu   VisitDeps(ModuleName, VisitDeps);
379e385e0d3SChuanqi Xu 
380e385e0d3SChuanqi Xu   return ModuleNames;
381e385e0d3SChuanqi Xu }
382e385e0d3SChuanqi Xu 
383e385e0d3SChuanqi Xu } // namespace
384e385e0d3SChuanqi Xu 
385e385e0d3SChuanqi Xu class ModulesBuilder::ModulesBuilderImpl {
386e385e0d3SChuanqi Xu public:
387e385e0d3SChuanqi Xu   ModulesBuilderImpl(const GlobalCompilationDatabase &CDB) : Cache(CDB) {}
388e385e0d3SChuanqi Xu 
389e385e0d3SChuanqi Xu   const GlobalCompilationDatabase &getCDB() const { return Cache.getCDB(); }
390e385e0d3SChuanqi Xu 
391e385e0d3SChuanqi Xu   llvm::Error
392e385e0d3SChuanqi Xu   getOrBuildModuleFile(StringRef ModuleName, const ThreadsafeFS &TFS,
393e385e0d3SChuanqi Xu                        ProjectModules &MDB,
394e385e0d3SChuanqi Xu                        ReusablePrerequisiteModules &BuiltModuleFiles);
395e385e0d3SChuanqi Xu 
396e385e0d3SChuanqi Xu private:
397e385e0d3SChuanqi Xu   ModuleFileCache Cache;
398e385e0d3SChuanqi Xu };
399e385e0d3SChuanqi Xu 
400e385e0d3SChuanqi Xu llvm::Error ModulesBuilder::ModulesBuilderImpl::getOrBuildModuleFile(
401e385e0d3SChuanqi Xu     StringRef ModuleName, const ThreadsafeFS &TFS, ProjectModules &MDB,
402e385e0d3SChuanqi Xu     ReusablePrerequisiteModules &BuiltModuleFiles) {
403e385e0d3SChuanqi Xu   if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
404e385e0d3SChuanqi Xu     return llvm::Error::success();
405e385e0d3SChuanqi Xu 
406e385e0d3SChuanqi Xu   PathRef ModuleUnitFileName = MDB.getSourceForModuleName(ModuleName);
407e385e0d3SChuanqi Xu   /// It is possible that we're meeting third party modules (modules whose
408e385e0d3SChuanqi Xu   /// source are not in the project. e.g, the std module may be a third-party
409e385e0d3SChuanqi Xu   /// module for most project) or something wrong with the implementation of
410e385e0d3SChuanqi Xu   /// ProjectModules.
411e385e0d3SChuanqi Xu   /// FIXME: How should we treat third party modules here? If we want to ignore
412e385e0d3SChuanqi Xu   /// third party modules, we should return true instead of false here.
413e385e0d3SChuanqi Xu   /// Currently we simply bail out.
414e385e0d3SChuanqi Xu   if (ModuleUnitFileName.empty())
415e385e0d3SChuanqi Xu     return llvm::createStringError(
416e385e0d3SChuanqi Xu         llvm::formatv("Don't get the module unit for module {0}", ModuleName));
417e385e0d3SChuanqi Xu 
418e385e0d3SChuanqi Xu   // Get Required modules in topological order.
419e385e0d3SChuanqi Xu   auto ReqModuleNames = getAllRequiredModules(MDB, ModuleName);
420e385e0d3SChuanqi Xu   for (llvm::StringRef ReqModuleName : ReqModuleNames) {
421e385e0d3SChuanqi Xu     if (BuiltModuleFiles.isModuleUnitBuilt(ModuleName))
422e385e0d3SChuanqi Xu       continue;
423e385e0d3SChuanqi Xu 
424e385e0d3SChuanqi Xu     if (auto Cached = Cache.getModule(ReqModuleName)) {
425e385e0d3SChuanqi Xu       if (IsModuleFileUpToDate(Cached->getModuleFilePath(), BuiltModuleFiles,
426e385e0d3SChuanqi Xu                                TFS.view(std::nullopt))) {
427e385e0d3SChuanqi Xu         log("Reusing module {0} from {1}", ModuleName,
428e385e0d3SChuanqi Xu             Cached->getModuleFilePath());
429e385e0d3SChuanqi Xu         BuiltModuleFiles.addModuleFile(std::move(Cached));
430e385e0d3SChuanqi Xu         continue;
431e385e0d3SChuanqi Xu       }
432e385e0d3SChuanqi Xu       Cache.remove(ReqModuleName);
433e385e0d3SChuanqi Xu     }
434e385e0d3SChuanqi Xu 
435e385e0d3SChuanqi Xu     llvm::Expected<ModuleFile> MF = buildModuleFile(
436e385e0d3SChuanqi Xu         ModuleName, ModuleUnitFileName, getCDB(), TFS, BuiltModuleFiles);
437e385e0d3SChuanqi Xu     if (llvm::Error Err = MF.takeError())
438e385e0d3SChuanqi Xu       return Err;
439e385e0d3SChuanqi Xu 
440e385e0d3SChuanqi Xu     log("Built module {0} to {1}", ModuleName, MF->getModuleFilePath());
441e385e0d3SChuanqi Xu     auto BuiltModuleFile = std::make_shared<const ModuleFile>(std::move(*MF));
442e385e0d3SChuanqi Xu     Cache.add(ModuleName, BuiltModuleFile);
443e385e0d3SChuanqi Xu     BuiltModuleFiles.addModuleFile(std::move(BuiltModuleFile));
444e385e0d3SChuanqi Xu   }
445e385e0d3SChuanqi Xu 
446fe6c2400SChuanqi Xu   return llvm::Error::success();
447fe6c2400SChuanqi Xu }
448fe6c2400SChuanqi Xu 
449fe6c2400SChuanqi Xu std::unique_ptr<PrerequisiteModules>
450fe6c2400SChuanqi Xu ModulesBuilder::buildPrerequisiteModulesFor(PathRef File,
451e385e0d3SChuanqi Xu                                             const ThreadsafeFS &TFS) {
452e385e0d3SChuanqi Xu   std::unique_ptr<ProjectModules> MDB = Impl->getCDB().getProjectModules(File);
453fe6c2400SChuanqi Xu   if (!MDB) {
454fe6c2400SChuanqi Xu     elog("Failed to get Project Modules information for {0}", File);
455fe6c2400SChuanqi Xu     return std::make_unique<FailedPrerequisiteModules>();
456fe6c2400SChuanqi Xu   }
457fe6c2400SChuanqi Xu 
458fe6c2400SChuanqi Xu   std::vector<std::string> RequiredModuleNames = MDB->getRequiredModules(File);
459fe6c2400SChuanqi Xu   if (RequiredModuleNames.empty())
460e385e0d3SChuanqi Xu     return std::make_unique<ReusablePrerequisiteModules>();
461fe6c2400SChuanqi Xu 
462e385e0d3SChuanqi Xu   auto RequiredModules = std::make_unique<ReusablePrerequisiteModules>();
463fe6c2400SChuanqi Xu   for (llvm::StringRef RequiredModuleName : RequiredModuleNames) {
464fe6c2400SChuanqi Xu     // Return early if there is any error.
465e385e0d3SChuanqi Xu     if (llvm::Error Err = Impl->getOrBuildModuleFile(
466e385e0d3SChuanqi Xu             RequiredModuleName, TFS, *MDB.get(), *RequiredModules.get())) {
467fe6c2400SChuanqi Xu       elog("Failed to build module {0}; due to {1}", RequiredModuleName,
468fe6c2400SChuanqi Xu            toString(std::move(Err)));
469fe6c2400SChuanqi Xu       return std::make_unique<FailedPrerequisiteModules>();
470fe6c2400SChuanqi Xu     }
471fe6c2400SChuanqi Xu   }
472fe6c2400SChuanqi Xu 
473fe6c2400SChuanqi Xu   return std::move(RequiredModules);
474fe6c2400SChuanqi Xu }
475fe6c2400SChuanqi Xu 
476e385e0d3SChuanqi Xu ModulesBuilder::ModulesBuilder(const GlobalCompilationDatabase &CDB) {
477e385e0d3SChuanqi Xu   Impl = std::make_unique<ModulesBuilderImpl>(CDB);
478fe6c2400SChuanqi Xu }
479fe6c2400SChuanqi Xu 
480e385e0d3SChuanqi Xu ModulesBuilder::~ModulesBuilder() {}
481e385e0d3SChuanqi Xu 
482fe6c2400SChuanqi Xu } // namespace clangd
483fe6c2400SChuanqi Xu } // namespace clang
484