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
12 namespace clang{
13 namespace tooling{
14 namespace dependencies{
15
getAdditionalArgs(std::function<StringRef (ModuleID)> LookupPCMPath,std::function<const ModuleDeps & (ModuleID)> LookupModuleDeps) const16 std::vector<std::string> FullDependencies::getAdditionalArgs(
17 std::function<StringRef(ModuleID)> LookupPCMPath,
18 std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
19 std::vector<std::string> Ret = getAdditionalArgsWithoutModulePaths();
20
21 std::vector<std::string> PCMPaths;
22 std::vector<std::string> ModMapPaths;
23 dependencies::detail::collectPCMAndModuleMapPaths(
24 ClangModuleDeps, LookupPCMPath, LookupModuleDeps, PCMPaths, ModMapPaths);
25 for (const std::string &PCMPath : PCMPaths)
26 Ret.push_back("-fmodule-file=" + PCMPath);
27 for (const std::string &ModMapPath : ModMapPaths)
28 Ret.push_back("-fmodule-map-file=" + ModMapPath);
29
30 return Ret;
31 }
32
33 std::vector<std::string>
getAdditionalArgsWithoutModulePaths() const34 FullDependencies::getAdditionalArgsWithoutModulePaths() const {
35 std::vector<std::string> Args{
36 "-fno-implicit-modules",
37 "-fno-implicit-module-maps",
38 };
39
40 for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) {
41 Args.push_back("-fmodule-file=" + PMD.ModuleName + "=" + PMD.PCMFile);
42 Args.push_back("-fmodule-map-file=" + PMD.ModuleMapFile);
43 }
44
45 return Args;
46 }
47
DependencyScanningTool(DependencyScanningService & Service)48 DependencyScanningTool::DependencyScanningTool(
49 DependencyScanningService &Service)
50 : Worker(Service) {}
51
getDependencyFile(const tooling::CompilationDatabase & Compilations,StringRef CWD)52 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
53 const tooling::CompilationDatabase &Compilations, StringRef CWD) {
54 /// Prints out all of the gathered dependencies into a string.
55 class MakeDependencyPrinterConsumer : public DependencyConsumer {
56 public:
57 void
58 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
59 this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
60 }
61
62 void handleFileDependency(StringRef File) override {
63 Dependencies.push_back(std::string(File));
64 }
65
66 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
67 // Same as `handleModuleDependency`.
68 }
69
70 void handleModuleDependency(ModuleDeps MD) override {
71 // These are ignored for the make format as it can't support the full
72 // set of deps, and handleFileDependency handles enough for implicitly
73 // built modules to work.
74 }
75
76 void handleContextHash(std::string Hash) override {}
77
78 void printDependencies(std::string &S) {
79 assert(Opts && "Handled dependency output options.");
80
81 class DependencyPrinter : public DependencyFileGenerator {
82 public:
83 DependencyPrinter(DependencyOutputOptions &Opts,
84 ArrayRef<std::string> Dependencies)
85 : DependencyFileGenerator(Opts) {
86 for (const auto &Dep : Dependencies)
87 addDependency(Dep);
88 }
89
90 void printDependencies(std::string &S) {
91 llvm::raw_string_ostream OS(S);
92 outputDependencyFile(OS);
93 }
94 };
95
96 DependencyPrinter Generator(*Opts, Dependencies);
97 Generator.printDependencies(S);
98 }
99
100 private:
101 std::unique_ptr<DependencyOutputOptions> Opts;
102 std::vector<std::string> Dependencies;
103 };
104
105 // We expect a single command here because if a source file occurs multiple
106 // times in the original CDB, then `computeDependencies` would run the
107 // `DependencyScanningAction` once for every time the input occured in the
108 // CDB. Instead we split up the CDB into single command chunks to avoid this
109 // behavior.
110 assert(Compilations.getAllCompileCommands().size() == 1 &&
111 "Expected a compilation database with a single command!");
112 std::string Input = Compilations.getAllCompileCommands().front().Filename;
113
114 MakeDependencyPrinterConsumer Consumer;
115 auto Result = Worker.computeDependencies(Input, CWD, Compilations, Consumer);
116 if (Result)
117 return std::move(Result);
118 std::string Output;
119 Consumer.printDependencies(Output);
120 return Output;
121 }
122
123 llvm::Expected<FullDependenciesResult>
getFullDependencies(const tooling::CompilationDatabase & Compilations,StringRef CWD,const llvm::StringSet<> & AlreadySeen)124 DependencyScanningTool::getFullDependencies(
125 const tooling::CompilationDatabase &Compilations, StringRef CWD,
126 const llvm::StringSet<> &AlreadySeen) {
127 class FullDependencyPrinterConsumer : public DependencyConsumer {
128 public:
129 FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
130 : AlreadySeen(AlreadySeen) {}
131
132 void
133 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
134
135 void handleFileDependency(StringRef File) override {
136 Dependencies.push_back(std::string(File));
137 }
138
139 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
140 PrebuiltModuleDeps.emplace_back(std::move(PMD));
141 }
142
143 void handleModuleDependency(ModuleDeps MD) override {
144 ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD);
145 }
146
147 void handleContextHash(std::string Hash) override {
148 ContextHash = std::move(Hash);
149 }
150
151 FullDependenciesResult getFullDependencies() const {
152 FullDependencies FD;
153
154 FD.ID.ContextHash = std::move(ContextHash);
155
156 FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
157
158 for (auto &&M : ClangModuleDeps) {
159 auto &MD = M.second;
160 if (MD.ImportedByMainFile)
161 FD.ClangModuleDeps.push_back(MD.ID);
162 }
163
164 FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
165
166 FullDependenciesResult FDR;
167
168 for (auto &&M : ClangModuleDeps) {
169 // TODO: Avoid handleModuleDependency even being called for modules
170 // we've already seen.
171 if (AlreadySeen.count(M.first))
172 continue;
173 FDR.DiscoveredModules.push_back(std::move(M.second));
174 }
175
176 FDR.FullDeps = std::move(FD);
177 return FDR;
178 }
179
180 private:
181 std::vector<std::string> Dependencies;
182 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
183 std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
184 std::string ContextHash;
185 std::vector<std::string> OutputPaths;
186 const llvm::StringSet<> &AlreadySeen;
187 };
188
189 // We expect a single command here because if a source file occurs multiple
190 // times in the original CDB, then `computeDependencies` would run the
191 // `DependencyScanningAction` once for every time the input occured in the
192 // CDB. Instead we split up the CDB into single command chunks to avoid this
193 // behavior.
194 assert(Compilations.getAllCompileCommands().size() == 1 &&
195 "Expected a compilation database with a single command!");
196 std::string Input = Compilations.getAllCompileCommands().front().Filename;
197
198 FullDependencyPrinterConsumer Consumer(AlreadySeen);
199 llvm::Error Result =
200 Worker.computeDependencies(Input, CWD, Compilations, Consumer);
201 if (Result)
202 return std::move(Result);
203 return Consumer.getFullDependencies();
204 }
205
206 } // end namespace dependencies
207 } // end namespace tooling
208 } // end namespace clang
209