1fe6c2400SChuanqi Xu //===------------------ ProjectModules.h -------------------------*- 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 "ProjectModules.h" 10fe6c2400SChuanqi Xu #include "support/Logger.h" 11fe6c2400SChuanqi Xu #include "clang/Tooling/DependencyScanning/DependencyScanningService.h" 12fe6c2400SChuanqi Xu #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h" 13fe6c2400SChuanqi Xu 14fe6c2400SChuanqi Xu namespace clang::clangd { 15fe6c2400SChuanqi Xu namespace { 16fe6c2400SChuanqi Xu /// A scanner to query the dependency information for C++20 Modules. 17fe6c2400SChuanqi Xu /// 18fe6c2400SChuanqi Xu /// The scanner can scan a single file with `scan(PathRef)` member function 19fe6c2400SChuanqi Xu /// or scan the whole project with `globalScan(vector<PathRef>)` member 20fe6c2400SChuanqi Xu /// function. See the comments of `globalScan` to see the details. 21fe6c2400SChuanqi Xu /// 22fe6c2400SChuanqi Xu /// The ModuleDependencyScanner can get the directly required module names for a 23fe6c2400SChuanqi Xu /// specific source file. Also the ModuleDependencyScanner can get the source 24fe6c2400SChuanqi Xu /// file declaring the primary module interface for a specific module name. 25fe6c2400SChuanqi Xu /// 26fe6c2400SChuanqi Xu /// IMPORTANT NOTE: we assume that every module unit is only declared once in a 27fe6c2400SChuanqi Xu /// source file in the project. But the assumption is not strictly true even 28fe6c2400SChuanqi Xu /// besides the invalid projects. The language specification requires that every 29fe6c2400SChuanqi Xu /// module unit should be unique in a valid program. But a project can contain 30fe6c2400SChuanqi Xu /// multiple programs. Then it is valid that we can have multiple source files 31fe6c2400SChuanqi Xu /// declaring the same module in a project as long as these source files don't 32fe6c2400SChuanqi Xu /// interfere with each other. 33fe6c2400SChuanqi Xu class ModuleDependencyScanner { 34fe6c2400SChuanqi Xu public: 35fe6c2400SChuanqi Xu ModuleDependencyScanner( 36fe6c2400SChuanqi Xu std::shared_ptr<const clang::tooling::CompilationDatabase> CDB, 37fe6c2400SChuanqi Xu const ThreadsafeFS &TFS) 38fe6c2400SChuanqi Xu : CDB(CDB), TFS(TFS), 39fe6c2400SChuanqi Xu Service(tooling::dependencies::ScanningMode::CanonicalPreprocessing, 40fe6c2400SChuanqi Xu tooling::dependencies::ScanningOutputFormat::P1689) {} 41fe6c2400SChuanqi Xu 42fe6c2400SChuanqi Xu /// The scanned modules dependency information for a specific source file. 43fe6c2400SChuanqi Xu struct ModuleDependencyInfo { 44fe6c2400SChuanqi Xu /// The name of the module if the file is a module unit. 45fe6c2400SChuanqi Xu std::optional<std::string> ModuleName; 46fe6c2400SChuanqi Xu /// A list of names for the modules that the file directly depends. 47fe6c2400SChuanqi Xu std::vector<std::string> RequiredModules; 48fe6c2400SChuanqi Xu }; 49fe6c2400SChuanqi Xu 50fe6c2400SChuanqi Xu /// Scanning the single file specified by \param FilePath. 51*2b0e2255SPetr Polezhaev std::optional<ModuleDependencyInfo> 52*2b0e2255SPetr Polezhaev scan(PathRef FilePath, const ProjectModules::CommandMangler &Mangler); 53fe6c2400SChuanqi Xu 54fe6c2400SChuanqi Xu /// Scanning every source file in the current project to get the 55fe6c2400SChuanqi Xu /// <module-name> to <module-unit-source> map. 56fe6c2400SChuanqi Xu /// TODO: We should find an efficient method to get the <module-name> 57fe6c2400SChuanqi Xu /// to <module-unit-source> map. We can make it either by providing 58fe6c2400SChuanqi Xu /// a global module dependency scanner to monitor every file. Or we 59fe6c2400SChuanqi Xu /// can simply require the build systems (or even the end users) 60fe6c2400SChuanqi Xu /// to provide the map. 61*2b0e2255SPetr Polezhaev void globalScan(const ProjectModules::CommandMangler &Mangler); 62fe6c2400SChuanqi Xu 63fe6c2400SChuanqi Xu /// Get the source file from the module name. Note that the language 64fe6c2400SChuanqi Xu /// guarantees all the module names are unique in a valid program. 65fe6c2400SChuanqi Xu /// This function should only be called after globalScan. 66fe6c2400SChuanqi Xu /// 67fe6c2400SChuanqi Xu /// TODO: We should handle the case that there are multiple source files 68fe6c2400SChuanqi Xu /// declaring the same module. 69fe6c2400SChuanqi Xu PathRef getSourceForModuleName(llvm::StringRef ModuleName) const; 70fe6c2400SChuanqi Xu 71fe6c2400SChuanqi Xu /// Return the direct required modules. Indirect required modules are not 72fe6c2400SChuanqi Xu /// included. 73*2b0e2255SPetr Polezhaev std::vector<std::string> 74*2b0e2255SPetr Polezhaev getRequiredModules(PathRef File, 75*2b0e2255SPetr Polezhaev const ProjectModules::CommandMangler &Mangler); 76fe6c2400SChuanqi Xu 77fe6c2400SChuanqi Xu private: 78fe6c2400SChuanqi Xu std::shared_ptr<const clang::tooling::CompilationDatabase> CDB; 79fe6c2400SChuanqi Xu const ThreadsafeFS &TFS; 80fe6c2400SChuanqi Xu 81fe6c2400SChuanqi Xu // Whether the scanner has scanned the project globally. 82fe6c2400SChuanqi Xu bool GlobalScanned = false; 83fe6c2400SChuanqi Xu 84fe6c2400SChuanqi Xu clang::tooling::dependencies::DependencyScanningService Service; 85fe6c2400SChuanqi Xu 86fe6c2400SChuanqi Xu // TODO: Add a scanning cache. 87fe6c2400SChuanqi Xu 88fe6c2400SChuanqi Xu // Map module name to source file path. 89fe6c2400SChuanqi Xu llvm::StringMap<std::string> ModuleNameToSource; 90fe6c2400SChuanqi Xu }; 91fe6c2400SChuanqi Xu 92fe6c2400SChuanqi Xu std::optional<ModuleDependencyScanner::ModuleDependencyInfo> 93*2b0e2255SPetr Polezhaev ModuleDependencyScanner::scan(PathRef FilePath, 94*2b0e2255SPetr Polezhaev const ProjectModules::CommandMangler &Mangler) { 95fe6c2400SChuanqi Xu auto Candidates = CDB->getCompileCommands(FilePath); 96fe6c2400SChuanqi Xu if (Candidates.empty()) 97fe6c2400SChuanqi Xu return std::nullopt; 98fe6c2400SChuanqi Xu 99fe6c2400SChuanqi Xu // Choose the first candidates as the compile commands as the file. 100fe6c2400SChuanqi Xu // Following the same logic with 101fe6c2400SChuanqi Xu // DirectoryBasedGlobalCompilationDatabase::getCompileCommand. 102fe6c2400SChuanqi Xu tooling::CompileCommand Cmd = std::move(Candidates.front()); 103fe6c2400SChuanqi Xu 104*2b0e2255SPetr Polezhaev if (Mangler) 105*2b0e2255SPetr Polezhaev Mangler(Cmd, FilePath); 106fe6c2400SChuanqi Xu 107fe6c2400SChuanqi Xu using namespace clang::tooling::dependencies; 108fe6c2400SChuanqi Xu 109fe6c2400SChuanqi Xu llvm::SmallString<128> FilePathDir(FilePath); 110fe6c2400SChuanqi Xu llvm::sys::path::remove_filename(FilePathDir); 111fe6c2400SChuanqi Xu DependencyScanningTool ScanningTool(Service, TFS.view(FilePathDir)); 112fe6c2400SChuanqi Xu 113fe6c2400SChuanqi Xu llvm::Expected<P1689Rule> ScanningResult = 114fe6c2400SChuanqi Xu ScanningTool.getP1689ModuleDependencyFile(Cmd, Cmd.Directory); 115fe6c2400SChuanqi Xu 116fe6c2400SChuanqi Xu if (auto E = ScanningResult.takeError()) { 117fe6c2400SChuanqi Xu elog("Scanning modules dependencies for {0} failed: {1}", FilePath, 118fe6c2400SChuanqi Xu llvm::toString(std::move(E))); 119fe6c2400SChuanqi Xu return std::nullopt; 120fe6c2400SChuanqi Xu } 121fe6c2400SChuanqi Xu 122fe6c2400SChuanqi Xu ModuleDependencyInfo Result; 123fe6c2400SChuanqi Xu 124fe6c2400SChuanqi Xu if (ScanningResult->Provides) { 125fe6c2400SChuanqi Xu ModuleNameToSource[ScanningResult->Provides->ModuleName] = FilePath; 126fe6c2400SChuanqi Xu Result.ModuleName = ScanningResult->Provides->ModuleName; 127fe6c2400SChuanqi Xu } 128fe6c2400SChuanqi Xu 129fe6c2400SChuanqi Xu for (auto &Required : ScanningResult->Requires) 130fe6c2400SChuanqi Xu Result.RequiredModules.push_back(Required.ModuleName); 131fe6c2400SChuanqi Xu 132fe6c2400SChuanqi Xu return Result; 133fe6c2400SChuanqi Xu } 134fe6c2400SChuanqi Xu 135*2b0e2255SPetr Polezhaev void ModuleDependencyScanner::globalScan( 136*2b0e2255SPetr Polezhaev const ProjectModules::CommandMangler &Mangler) { 137fe6c2400SChuanqi Xu for (auto &File : CDB->getAllFiles()) 138*2b0e2255SPetr Polezhaev scan(File, Mangler); 139fe6c2400SChuanqi Xu 140fe6c2400SChuanqi Xu GlobalScanned = true; 141fe6c2400SChuanqi Xu } 142fe6c2400SChuanqi Xu 143fe6c2400SChuanqi Xu PathRef ModuleDependencyScanner::getSourceForModuleName( 144fe6c2400SChuanqi Xu llvm::StringRef ModuleName) const { 145fe6c2400SChuanqi Xu assert( 146fe6c2400SChuanqi Xu GlobalScanned && 147fe6c2400SChuanqi Xu "We should only call getSourceForModuleName after calling globalScan()"); 148fe6c2400SChuanqi Xu 149fe6c2400SChuanqi Xu if (auto It = ModuleNameToSource.find(ModuleName); 150fe6c2400SChuanqi Xu It != ModuleNameToSource.end()) 151fe6c2400SChuanqi Xu return It->second; 152fe6c2400SChuanqi Xu 153fe6c2400SChuanqi Xu return {}; 154fe6c2400SChuanqi Xu } 155fe6c2400SChuanqi Xu 156*2b0e2255SPetr Polezhaev std::vector<std::string> ModuleDependencyScanner::getRequiredModules( 157*2b0e2255SPetr Polezhaev PathRef File, const ProjectModules::CommandMangler &Mangler) { 158*2b0e2255SPetr Polezhaev auto ScanningResult = scan(File, Mangler); 159fe6c2400SChuanqi Xu if (!ScanningResult) 160fe6c2400SChuanqi Xu return {}; 161fe6c2400SChuanqi Xu 162fe6c2400SChuanqi Xu return ScanningResult->RequiredModules; 163fe6c2400SChuanqi Xu } 164fe6c2400SChuanqi Xu } // namespace 165fe6c2400SChuanqi Xu 166fe6c2400SChuanqi Xu /// TODO: The existing `ScanningAllProjectModules` is not efficient. See the 167fe6c2400SChuanqi Xu /// comments in ModuleDependencyScanner for detail. 168fe6c2400SChuanqi Xu /// 169fe6c2400SChuanqi Xu /// In the future, we wish the build system can provide a well design 170fe6c2400SChuanqi Xu /// compilation database for modules then we can query that new compilation 171fe6c2400SChuanqi Xu /// database directly. Or we need to have a global long-live scanner to detect 172fe6c2400SChuanqi Xu /// the state of each file. 173fe6c2400SChuanqi Xu class ScanningAllProjectModules : public ProjectModules { 174fe6c2400SChuanqi Xu public: 175fe6c2400SChuanqi Xu ScanningAllProjectModules( 176fe6c2400SChuanqi Xu std::shared_ptr<const clang::tooling::CompilationDatabase> CDB, 177fe6c2400SChuanqi Xu const ThreadsafeFS &TFS) 178fe6c2400SChuanqi Xu : Scanner(CDB, TFS) {} 179fe6c2400SChuanqi Xu 180fe6c2400SChuanqi Xu ~ScanningAllProjectModules() override = default; 181fe6c2400SChuanqi Xu 182fe6c2400SChuanqi Xu std::vector<std::string> getRequiredModules(PathRef File) override { 183*2b0e2255SPetr Polezhaev return Scanner.getRequiredModules(File, Mangler); 184*2b0e2255SPetr Polezhaev } 185*2b0e2255SPetr Polezhaev 186*2b0e2255SPetr Polezhaev void setCommandMangler(CommandMangler Mangler) override { 187*2b0e2255SPetr Polezhaev this->Mangler = std::move(Mangler); 188fe6c2400SChuanqi Xu } 189fe6c2400SChuanqi Xu 190fe6c2400SChuanqi Xu /// RequiredSourceFile is not used intentionally. See the comments of 191fe6c2400SChuanqi Xu /// ModuleDependencyScanner for detail. 192fe6c2400SChuanqi Xu PathRef 193fe6c2400SChuanqi Xu getSourceForModuleName(llvm::StringRef ModuleName, 194fe6c2400SChuanqi Xu PathRef RequiredSourceFile = PathRef()) override { 195*2b0e2255SPetr Polezhaev Scanner.globalScan(Mangler); 196fe6c2400SChuanqi Xu return Scanner.getSourceForModuleName(ModuleName); 197fe6c2400SChuanqi Xu } 198fe6c2400SChuanqi Xu 199fe6c2400SChuanqi Xu private: 200fe6c2400SChuanqi Xu ModuleDependencyScanner Scanner; 201*2b0e2255SPetr Polezhaev CommandMangler Mangler; 202fe6c2400SChuanqi Xu }; 203fe6c2400SChuanqi Xu 204fe6c2400SChuanqi Xu std::unique_ptr<ProjectModules> scanningProjectModules( 205fe6c2400SChuanqi Xu std::shared_ptr<const clang::tooling::CompilationDatabase> CDB, 206fe6c2400SChuanqi Xu const ThreadsafeFS &TFS) { 207fe6c2400SChuanqi Xu return std::make_unique<ScanningAllProjectModules>(CDB, TFS); 208fe6c2400SChuanqi Xu } 209fe6c2400SChuanqi Xu 210fe6c2400SChuanqi Xu } // namespace clang::clangd 211