150
|
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
|
221
|
16 std::vector<std::string> FullDependencies::getAdditionalArgs(
|
|
17 std::function<StringRef(ModuleID)> LookupPCMPath,
|
|
18 std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
|
223
|
19 std::vector<std::string> Ret = getAdditionalArgsWithoutModulePaths();
|
150
|
20
|
221
|
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);
|
150
|
29
|
|
30 return Ret;
|
|
31 }
|
|
32
|
221
|
33 std::vector<std::string>
|
|
34 FullDependencies::getAdditionalArgsWithoutModulePaths() const {
|
223
|
35 std::vector<std::string> Args{
|
221
|
36 "-fno-implicit-modules",
|
|
37 "-fno-implicit-module-maps",
|
|
38 };
|
223
|
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;
|
221
|
46 }
|
|
47
|
150
|
48 DependencyScanningTool::DependencyScanningTool(
|
|
49 DependencyScanningService &Service)
|
|
50 : Worker(Service) {}
|
|
51
|
|
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:
|
223
|
57 void
|
|
58 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
|
|
59 this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
|
|
60 }
|
|
61
|
|
62 void handleFileDependency(StringRef File) override {
|
150
|
63 Dependencies.push_back(std::string(File));
|
|
64 }
|
|
65
|
223
|
66 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
|
|
67 // Same as `handleModuleDependency`.
|
|
68 }
|
|
69
|
150
|
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) {
|
223
|
79 assert(Opts && "Handled dependency output options.");
|
150
|
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>
|
|
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
|
223
|
132 void
|
|
133 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
|
|
134
|
|
135 void handleFileDependency(StringRef File) override {
|
150
|
136 Dependencies.push_back(std::string(File));
|
|
137 }
|
|
138
|
223
|
139 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
|
|
140 PrebuiltModuleDeps.emplace_back(std::move(PMD));
|
|
141 }
|
|
142
|
150
|
143 void handleModuleDependency(ModuleDeps MD) override {
|
221
|
144 ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD);
|
150
|
145 }
|
|
146
|
|
147 void handleContextHash(std::string Hash) override {
|
|
148 ContextHash = std::move(Hash);
|
|
149 }
|
|
150
|
|
151 FullDependenciesResult getFullDependencies() const {
|
|
152 FullDependencies FD;
|
|
153
|
221
|
154 FD.ID.ContextHash = std::move(ContextHash);
|
150
|
155
|
|
156 FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
|
|
157
|
|
158 for (auto &&M : ClangModuleDeps) {
|
|
159 auto &MD = M.second;
|
|
160 if (MD.ImportedByMainFile)
|
221
|
161 FD.ClangModuleDeps.push_back(MD.ID);
|
150
|
162 }
|
|
163
|
223
|
164 FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
|
|
165
|
150
|
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;
|
223
|
182 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
|
150
|
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
|