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)
39 Service(dependencies::ScanningMode::CanonicalPreprocessing,
40 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::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;
109 llvm::SmallString<128> FilePathDir(FilePath);
110 llvm::sys::path::remove_filename(FilePathDir);
111 DependencyScanningTool ScanningTool(Service, TFS.
view(FilePathDir));
114 llvm::raw_string_ostream OS(S);
115 DiagnosticOptions DiagOpts;
116 DiagOpts.ShowCarets =
false;
117 TextDiagnosticPrinter DiagConsumer(OS, DiagOpts);
119 std::optional<P1689Rule> ScanningResult =
120 ScanningTool.getP1689ModuleDependencyFile(Cmd, Cmd.Directory,
123 if (!ScanningResult) {
124 elog(
"Scanning modules dependencies for {0} failed: {1}", FilePath, S);
128 ModuleDependencyInfo Result;
130 if (ScanningResult->Provides) {
131 Result.ModuleName = ScanningResult->Provides->ModuleName;
133 auto [Iter, Inserted] = ModuleNameToSource.try_emplace(
134 ScanningResult->Provides->ModuleName, FilePath);
136 if (!Inserted && Iter->second != FilePath) {
137 elog(
"Detected multiple source files ({0}, {1}) declaring the same "
139 "Now clangd may find the wrong source in such case.",
140 Iter->second, FilePath, ScanningResult->Provides->ModuleName);
144 for (
auto &Required : ScanningResult->Requires)
145 Result.RequiredModules.push_back(Required.ModuleName);
150void ModuleDependencyScanner::globalScan(
155 for (
auto &File : CDB->getAllFiles())
158 GlobalScanned =
true;
161PathRef ModuleDependencyScanner::getSourceForModuleName(
162 llvm::StringRef ModuleName)
const {
165 "We should only call getSourceForModuleName after calling globalScan()");
167 if (
auto It = ModuleNameToSource.find(ModuleName);
168 It != ModuleNameToSource.end())
174std::vector<std::string> ModuleDependencyScanner::getRequiredModules(
176 auto ScanningResult = scan(File, Mangler);
180 return ScanningResult->RequiredModules;
194 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
196 : Scanner(CDB, TFS) {}
201 return Scanner.getRequiredModules(
File, Mangler);
205 this->Mangler = std::move(Mangler);
211 PathRef RequiredSourceFile)
override {
212 Scanner.globalScan(Mangler);
213 return Scanner.getSourceForModuleName(ModuleName).str();
217 auto ScanningResult = Scanner.scan(
File, Mangler);
218 if (!ScanningResult || !ScanningResult->ModuleName)
221 return *ScanningResult->ModuleName;
225 ModuleDependencyScanner Scanner;
230 std::shared_ptr<const clang::tooling::CompilationDatabase> CDB,
232 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.