1//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
10#include "clang/Frontend/Utils.h"
11#include <optional>
12
13using namespace clang;
14using namespace tooling;
15using namespace dependencies;
16
17DependencyScanningTool::DependencyScanningTool(
18 DependencyScanningService &Service,
19 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
20 : Worker(Service, std::move(FS)) {}
21
22namespace {
23/// Prints out all of the gathered dependencies into a string.
24class MakeDependencyPrinterConsumer : public DependencyConsumer {
25public:
26 void handleBuildCommand(Command) override {}
27
28 void
29 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
30 this->Opts = std::make_unique<DependencyOutputOptions>(args: Opts);
31 }
32
33 void handleFileDependency(StringRef File) override {
34 Dependencies.push_back(x: std::string(File));
35 }
36
37 // These are ignored for the make format as it can't support the full
38 // set of deps, and handleFileDependency handles enough for implicitly
39 // built modules to work.
40 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
41 void handleModuleDependency(ModuleDeps MD) override {}
42 void handleDirectModuleDependency(ModuleID ID) override {}
43 void handleContextHash(std::string Hash) override {}
44
45 void printDependencies(std::string &S) {
46 assert(Opts && "Handled dependency output options.");
47
48 class DependencyPrinter : public DependencyFileGenerator {
49 public:
50 DependencyPrinter(DependencyOutputOptions &Opts,
51 ArrayRef<std::string> Dependencies)
52 : DependencyFileGenerator(Opts) {
53 for (const auto &Dep : Dependencies)
54 addDependency(Filename: Dep);
55 }
56
57 void printDependencies(std::string &S) {
58 llvm::raw_string_ostream OS(S);
59 outputDependencyFile(OS);
60 }
61 };
62
63 DependencyPrinter Generator(*Opts, Dependencies);
64 Generator.printDependencies(S);
65 }
66
67protected:
68 std::unique_ptr<DependencyOutputOptions> Opts;
69 std::vector<std::string> Dependencies;
70};
71} // anonymous namespace
72
73llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
74 const std::vector<std::string> &CommandLine, StringRef CWD) {
75 MakeDependencyPrinterConsumer Consumer;
76 CallbackActionController Controller(nullptr);
77 auto Result =
78 Worker.computeDependencies(WorkingDirectory: CWD, CommandLine, Consumer, Controller);
79 if (Result)
80 return std::move(Result);
81 std::string Output;
82 Consumer.printDependencies(S&: Output);
83 return Output;
84}
85
86llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
87 const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput,
88 std::string &MakeformatOutputPath) {
89 class P1689ModuleDependencyPrinterConsumer
90 : public MakeDependencyPrinterConsumer {
91 public:
92 P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
93 const CompileCommand &Command)
94 : Filename(Command.Filename), Rule(Rule) {
95 Rule.PrimaryOutput = Command.Output;
96 }
97
98 void handleProvidedAndRequiredStdCXXModules(
99 std::optional<P1689ModuleInfo> Provided,
100 std::vector<P1689ModuleInfo> Requires) override {
101 Rule.Provides = Provided;
102 if (Rule.Provides)
103 Rule.Provides->SourcePath = Filename.str();
104 Rule.Requires = Requires;
105 }
106
107 StringRef getMakeFormatDependencyOutputPath() {
108 if (Opts->OutputFormat != DependencyOutputFormat::Make)
109 return {};
110 return Opts->OutputFile;
111 }
112
113 private:
114 StringRef Filename;
115 P1689Rule &Rule;
116 };
117
118 class P1689ActionController : public DependencyActionController {
119 public:
120 // The lookupModuleOutput is for clang modules. P1689 format don't need it.
121 std::string lookupModuleOutput(const ModuleID &,
122 ModuleOutputKind Kind) override {
123 return "";
124 }
125 };
126
127 P1689Rule Rule;
128 P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
129 P1689ActionController Controller;
130 auto Result = Worker.computeDependencies(WorkingDirectory: CWD, CommandLine: Command.CommandLine, Consumer,
131 Controller);
132 if (Result)
133 return std::move(Result);
134
135 MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
136 if (!MakeformatOutputPath.empty())
137 Consumer.printDependencies(S&: MakeformatOutput);
138 return Rule;
139}
140
141llvm::Expected<TranslationUnitDeps>
142DependencyScanningTool::getTranslationUnitDependencies(
143 const std::vector<std::string> &CommandLine, StringRef CWD,
144 const llvm::DenseSet<ModuleID> &AlreadySeen,
145 LookupModuleOutputCallback LookupModuleOutput) {
146 FullDependencyConsumer Consumer(AlreadySeen);
147 CallbackActionController Controller(LookupModuleOutput);
148 llvm::Error Result =
149 Worker.computeDependencies(WorkingDirectory: CWD, CommandLine, Consumer, Controller);
150 if (Result)
151 return std::move(Result);
152 return Consumer.takeTranslationUnitDeps();
153}
154
155llvm::Expected<ModuleDepsGraph> DependencyScanningTool::getModuleDependencies(
156 StringRef ModuleName, const std::vector<std::string> &CommandLine,
157 StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen,
158 LookupModuleOutputCallback LookupModuleOutput) {
159 FullDependencyConsumer Consumer(AlreadySeen);
160 CallbackActionController Controller(LookupModuleOutput);
161 llvm::Error Result = Worker.computeDependencies(WorkingDirectory: CWD, CommandLine, Consumer,
162 Controller, ModuleName);
163 if (Result)
164 return std::move(Result);
165 return Consumer.takeModuleGraphDeps();
166}
167
168TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() {
169 TranslationUnitDeps TU;
170
171 TU.ID.ContextHash = std::move(ContextHash);
172 TU.FileDeps = std::move(Dependencies);
173 TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
174 TU.Commands = std::move(Commands);
175
176 for (auto &&M : ClangModuleDeps) {
177 auto &MD = M.second;
178 // TODO: Avoid handleModuleDependency even being called for modules
179 // we've already seen.
180 if (AlreadySeen.count(V: M.first))
181 continue;
182 TU.ModuleGraph.push_back(x: std::move(MD));
183 }
184 TU.ClangModuleDeps = std::move(DirectModuleDeps);
185
186 return TU;
187}
188
189ModuleDepsGraph FullDependencyConsumer::takeModuleGraphDeps() {
190 ModuleDepsGraph ModuleGraph;
191
192 for (auto &&M : ClangModuleDeps) {
193 auto &MD = M.second;
194 // TODO: Avoid handleModuleDependency even being called for modules
195 // we've already seen.
196 if (AlreadySeen.count(V: M.first))
197 continue;
198 ModuleGraph.push_back(x: std::move(MD));
199 }
200
201 return ModuleGraph;
202}
203
204CallbackActionController::~CallbackActionController() {}
205