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