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