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