11#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
12#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
33class ModuleDependencyScanner {
35 ModuleDependencyScanner(
36 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
37 const ThreadsafeFS &TFS)
39 Service(tooling::dependencies::ScanningMode::CanonicalPreprocessing,
40 tooling::dependencies::ScanningOutputFormat::P1689) {}
43 struct ModuleDependencyInfo {
45 std::optional<std::string> ModuleName;
47 std::vector<std::string> RequiredModules;
51 std::optional<ModuleDependencyInfo>
69 PathRef getSourceForModuleName(llvm::StringRef ModuleName)
const;
73 std::vector<std::string>
78 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB;
79 const ThreadsafeFS &TFS;
82 bool GlobalScanned =
false;
84 clang::tooling::dependencies::DependencyScanningService Service;
89 llvm::StringMap<std::string> ModuleNameToSource;
92std::optional<ModuleDependencyScanner::ModuleDependencyInfo>
93ModuleDependencyScanner::scan(
PathRef FilePath,
95 auto Candidates = CDB->getCompileCommands(FilePath);
96 if (Candidates.empty())
102 tooling::CompileCommand Cmd = std::move(Candidates.front());
105 Mangler(Cmd, FilePath);
107 using namespace clang::tooling::dependencies;
109 llvm::SmallString<128> FilePathDir(FilePath);
110 llvm::sys::path::remove_filename(FilePathDir);
111 DependencyScanningTool ScanningTool(Service, TFS.
view(FilePathDir));
113 llvm::Expected<P1689Rule> ScanningResult =
114 ScanningTool.getP1689ModuleDependencyFile(Cmd, Cmd.Directory);
116 if (
auto E = ScanningResult.takeError()) {
117 elog(
"Scanning modules dependencies for {0} failed: {1}", FilePath,
118 llvm::toString(std::move(E)));
122 ModuleDependencyInfo Result;
124 if (ScanningResult->Provides) {
125 Result.ModuleName = ScanningResult->Provides->ModuleName;
127 auto [Iter, Inserted] = ModuleNameToSource.try_emplace(
128 ScanningResult->Provides->ModuleName, FilePath);
130 if (!Inserted && Iter->second != FilePath) {
131 elog(
"Detected multiple source files ({0}, {1}) declaring the same "
133 "Now clangd may find the wrong source in such case.",
134 Iter->second, FilePath, ScanningResult->Provides->ModuleName);
138 for (
auto &Required : ScanningResult->Requires)
139 Result.RequiredModules.push_back(Required.ModuleName);
144void ModuleDependencyScanner::globalScan(
149 for (
auto &File : CDB->getAllFiles())
152 GlobalScanned =
true;
155PathRef ModuleDependencyScanner::getSourceForModuleName(
156 llvm::StringRef ModuleName)
const {
159 "We should only call getSourceForModuleName after calling globalScan()");
161 if (
auto It = ModuleNameToSource.find(ModuleName);
162 It != ModuleNameToSource.end())
168std::vector<std::string> ModuleDependencyScanner::getRequiredModules(
170 auto ScanningResult = scan(File, Mangler);
174 return ScanningResult->RequiredModules;
188 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
190 : Scanner(CDB, TFS) {}
195 return Scanner.getRequiredModules(
File, Mangler);
199 this->Mangler = std::move(Mangler);
205 PathRef RequiredSourceFile)
override {
206 Scanner.globalScan(Mangler);
207 return Scanner.getSourceForModuleName(ModuleName).str();
211 auto ScanningResult = Scanner.scan(
File, Mangler);
212 if (!ScanningResult || !ScanningResult->ModuleName)
215 return *ScanningResult->ModuleName;
219 ModuleDependencyScanner Scanner;
224 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
226 return std::make_unique<ScanningAllProjectModules>(CDB, TFS);
void elog(const char *Fmt, Ts &&... Vals)
An interface to query the modules information in the project.
llvm::unique_function< void(tooling::CompileCommand &, PathRef) const > CommandMangler
void setCommandMangler(CommandMangler Mangler) override
ScanningAllProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
std::string getModuleNameForSource(PathRef File) override
std::vector< std::string > getRequiredModules(PathRef File) override
std::string getSourceForModuleName(llvm::StringRef ModuleName, PathRef RequiredSourceFile) override
RequiredSourceFile is not used intentionally.
~ScanningAllProjectModules() override=default
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > view(std::nullopt_t CWD) const
Obtain a vfs::FileSystem with an arbitrary initial working directory.
FIXME: Skip testing on windows temporarily due to the different escaping code mode.
std::unique_ptr< ProjectModules > scanningProjectModules(std::shared_ptr< const clang::tooling::CompilationDatabase > CDB, const ThreadsafeFS &TFS)
Providing modules information for the project by scanning every file.
llvm::StringRef PathRef
A typedef to represent a ref to file path.