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