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 using namespace clang;
13 using namespace tooling;
14 using namespace dependencies;
15
getCommandLine(llvm::function_ref<std::string (const ModuleID &,ModuleOutputKind)> LookupModuleOutput) const16 std::vector<std::string> FullDependencies::getCommandLine(
17 llvm::function_ref<std::string(const ModuleID &, ModuleOutputKind)>
18 LookupModuleOutput) const {
19 std::vector<std::string> Ret = getCommandLineWithoutModulePaths();
20
21 for (ModuleID MID : ClangModuleDeps) {
22 auto PCM = LookupModuleOutput(MID, ModuleOutputKind::ModuleFile);
23 Ret.push_back("-fmodule-file=" + PCM);
24 }
25
26 return Ret;
27 }
28
29 std::vector<std::string>
getCommandLineWithoutModulePaths() const30 FullDependencies::getCommandLineWithoutModulePaths() const {
31 std::vector<std::string> Args = OriginalCommandLine;
32
33 Args.push_back("-fno-implicit-modules");
34 Args.push_back("-fno-implicit-module-maps");
35 for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
36 Args.push_back("-fmodule-file=" + PMD.PCMFile);
37
38 // These arguments are unused in explicit compiles.
39 llvm::erase_if(Args, [](StringRef Arg) {
40 if (Arg.consume_front("-fmodules-")) {
41 return Arg.startswith("cache-path=") ||
42 Arg.startswith("prune-interval=") ||
43 Arg.startswith("prune-after=") ||
44 Arg == "validate-once-per-build-session";
45 }
46 return Arg.startswith("-fbuild-session-file=");
47 });
48
49 return Args;
50 }
51
DependencyScanningTool(DependencyScanningService & Service,llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)52 DependencyScanningTool::DependencyScanningTool(
53 DependencyScanningService &Service,
54 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
55 : Worker(Service, std::move(FS)) {}
56
getDependencyFile(const std::vector<std::string> & CommandLine,StringRef CWD,llvm::Optional<StringRef> ModuleName)57 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
58 const std::vector<std::string> &CommandLine, StringRef CWD,
59 llvm::Optional<StringRef> ModuleName) {
60 /// Prints out all of the gathered dependencies into a string.
61 class MakeDependencyPrinterConsumer : public DependencyConsumer {
62 public:
63 void
64 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
65 this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
66 }
67
68 void handleFileDependency(StringRef File) override {
69 Dependencies.push_back(std::string(File));
70 }
71
72 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
73 // Same as `handleModuleDependency`.
74 }
75
76 void handleModuleDependency(ModuleDeps MD) override {
77 // These are ignored for the make format as it can't support the full
78 // set of deps, and handleFileDependency handles enough for implicitly
79 // built modules to work.
80 }
81
82 void handleContextHash(std::string Hash) override {}
83
84 void printDependencies(std::string &S) {
85 assert(Opts && "Handled dependency output options.");
86
87 class DependencyPrinter : public DependencyFileGenerator {
88 public:
89 DependencyPrinter(DependencyOutputOptions &Opts,
90 ArrayRef<std::string> Dependencies)
91 : DependencyFileGenerator(Opts) {
92 for (const auto &Dep : Dependencies)
93 addDependency(Dep);
94 }
95
96 void printDependencies(std::string &S) {
97 llvm::raw_string_ostream OS(S);
98 outputDependencyFile(OS);
99 }
100 };
101
102 DependencyPrinter Generator(*Opts, Dependencies);
103 Generator.printDependencies(S);
104 }
105
106 private:
107 std::unique_ptr<DependencyOutputOptions> Opts;
108 std::vector<std::string> Dependencies;
109 };
110
111 MakeDependencyPrinterConsumer Consumer;
112 auto Result =
113 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
114 if (Result)
115 return std::move(Result);
116 std::string Output;
117 Consumer.printDependencies(Output);
118 return Output;
119 }
120
121 llvm::Expected<FullDependenciesResult>
getFullDependencies(const std::vector<std::string> & CommandLine,StringRef CWD,const llvm::StringSet<> & AlreadySeen,llvm::Optional<StringRef> ModuleName)122 DependencyScanningTool::getFullDependencies(
123 const std::vector<std::string> &CommandLine, StringRef CWD,
124 const llvm::StringSet<> &AlreadySeen,
125 llvm::Optional<StringRef> ModuleName) {
126 class FullDependencyPrinterConsumer : public DependencyConsumer {
127 public:
128 FullDependencyPrinterConsumer(const llvm::StringSet<> &AlreadySeen)
129 : AlreadySeen(AlreadySeen) {}
130
131 void
132 handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {}
133
134 void handleFileDependency(StringRef File) override {
135 Dependencies.push_back(std::string(File));
136 }
137
138 void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
139 PrebuiltModuleDeps.emplace_back(std::move(PMD));
140 }
141
142 void handleModuleDependency(ModuleDeps MD) override {
143 ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD);
144 }
145
146 void handleContextHash(std::string Hash) override {
147 ContextHash = std::move(Hash);
148 }
149
150 FullDependenciesResult getFullDependencies(
151 const std::vector<std::string> &OriginalCommandLine) const {
152 FullDependencies FD;
153
154 FD.OriginalCommandLine =
155 ArrayRef<std::string>(OriginalCommandLine).slice(1);
156
157 FD.ID.ContextHash = std::move(ContextHash);
158
159 FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
160
161 for (auto &&M : ClangModuleDeps) {
162 auto &MD = M.second;
163 if (MD.ImportedByMainFile)
164 FD.ClangModuleDeps.push_back(MD.ID);
165 }
166
167 FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
168
169 FullDependenciesResult FDR;
170
171 for (auto &&M : ClangModuleDeps) {
172 // TODO: Avoid handleModuleDependency even being called for modules
173 // we've already seen.
174 if (AlreadySeen.count(M.first))
175 continue;
176 FDR.DiscoveredModules.push_back(std::move(M.second));
177 }
178
179 FDR.FullDeps = std::move(FD);
180 return FDR;
181 }
182
183 private:
184 std::vector<std::string> Dependencies;
185 std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
186 llvm::MapVector<std::string, ModuleDeps, llvm::StringMap<unsigned>> ClangModuleDeps;
187 std::string ContextHash;
188 std::vector<std::string> OutputPaths;
189 const llvm::StringSet<> &AlreadySeen;
190 };
191
192 FullDependencyPrinterConsumer Consumer(AlreadySeen);
193 llvm::Error Result =
194 Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
195 if (Result)
196 return std::move(Result);
197 return Consumer.getFullDependencies(CommandLine);
198 }
199