11#include "clang/DependencyScanning/DependencyScanningService.h"
12#include "clang/Tooling/DependencyScanningTool.h"
33class ModuleDependencyScanner {
35 ModuleDependencyScanner(
36 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
37 const ThreadsafeFS &TFS)
38 : CDB(CDB), Service([&TFS] {
39 dependencies::DependencyScanningServiceOptions Opts;
40 Opts.MakeVFS = [&] {
return TFS.view(std::nullopt); };
41 Opts.Mode = dependencies::ScanningMode::CanonicalPreprocessing;
42 Opts.Format = dependencies::ScanningOutputFormat::P1689;
47 struct ModuleDependencyInfo {
49 std::optional<std::string> ModuleName;
51 std::vector<std::string> RequiredModules;
55 std::optional<ModuleDependencyInfo>
73 PathRef getSourceForModuleName(llvm::StringRef ModuleName)
const;
77 std::vector<std::string>
82 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB;
85 bool GlobalScanned =
false;
87 clang::dependencies::DependencyScanningService Service;
92 llvm::StringMap<std::string> ModuleNameToSource;
95std::optional<ModuleDependencyScanner::ModuleDependencyInfo>
96ModuleDependencyScanner::scan(
PathRef FilePath,
98 auto Candidates = CDB->getCompileCommands(FilePath);
99 if (Candidates.empty())
105 tooling::CompileCommand Cmd = std::move(Candidates.front());
108 Mangler(Cmd, FilePath);
110 using namespace clang::tooling;
112 DependencyScanningTool ScanningTool(Service);
115 llvm::raw_string_ostream OS(S);
116 DiagnosticOptions DiagOpts;
117 DiagOpts.ShowCarets =
false;
118 TextDiagnosticPrinter DiagConsumer(OS, DiagOpts);
120 std::optional<P1689Rule> ScanningResult =
121 ScanningTool.getP1689ModuleDependencyFile(Cmd, Cmd.Directory,
124 if (!ScanningResult) {
125 elog(
"Scanning modules dependencies for {0} failed: {1}", FilePath, S);
129 ModuleDependencyInfo Result;
131 if (ScanningResult->Provides) {
132 Result.ModuleName = ScanningResult->Provides->ModuleName;
134 auto [Iter, Inserted] = ModuleNameToSource.try_emplace(
135 ScanningResult->Provides->ModuleName, FilePath);
137 if (!Inserted && Iter->second != FilePath) {
138 elog(
"Detected multiple source files ({0}, {1}) declaring the same "
140 "Now clangd may find the wrong source in such case.",
141 Iter->second, FilePath, ScanningResult->Provides->ModuleName);
145 for (
auto &Required : ScanningResult->Requires)
146 Result.RequiredModules.push_back(Required.ModuleName);
151void ModuleDependencyScanner::globalScan(
156 for (
auto &File : CDB->getAllFiles())
159 GlobalScanned =
true;
162PathRef ModuleDependencyScanner::getSourceForModuleName(
163 llvm::StringRef ModuleName)
const {
166 "We should only call getSourceForModuleName after calling globalScan()");
168 if (
auto It = ModuleNameToSource.find(ModuleName);
169 It != ModuleNameToSource.end())
175std::vector<std::string> ModuleDependencyScanner::getRequiredModules(
177 auto ScanningResult = scan(File, Mangler);
181 return ScanningResult->RequiredModules;
195 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
197 : Scanner(CDB, TFS) {}
202 return Scanner.getRequiredModules(
File, Mangler);
206 this->Mangler = std::move(Mangler);
212 PathRef RequiredSourceFile)
override {
213 Scanner.globalScan(Mangler);
214 return Scanner.getSourceForModuleName(ModuleName).str();
218 auto ScanningResult = Scanner.scan(
File, Mangler);
219 if (!ScanningResult || !ScanningResult->ModuleName)
222 return *ScanningResult->ModuleName;
226 ModuleDependencyScanner Scanner;
231 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
233 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.
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.