xref: /freebsd/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp (revision 9f23cbd6cae82fd77edfad7173432fa8dccd0a95)
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 #include <optional>
12 
13 using namespace clang;
14 using namespace tooling;
15 using namespace dependencies;
16 
17 static std::vector<std::string>
18 makeTUCommandLineWithoutPaths(ArrayRef<std::string> OriginalCommandLine) {
19   std::vector<std::string> Args = OriginalCommandLine;
20 
21   Args.push_back("-fno-implicit-modules");
22   Args.push_back("-fno-implicit-module-maps");
23 
24   // These arguments are unused in explicit compiles.
25   llvm::erase_if(Args, [](StringRef Arg) {
26     if (Arg.consume_front("-fmodules-")) {
27       return Arg.startswith("cache-path=") ||
28              Arg.startswith("prune-interval=") ||
29              Arg.startswith("prune-after=") ||
30              Arg == "validate-once-per-build-session";
31     }
32     return Arg.startswith("-fbuild-session-file=");
33   });
34 
35   return Args;
36 }
37 
38 DependencyScanningTool::DependencyScanningTool(
39     DependencyScanningService &Service,
40     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
41     : Worker(Service, std::move(FS)) {}
42 
43 namespace {
44 /// Prints out all of the gathered dependencies into a string.
45 class MakeDependencyPrinterConsumer : public DependencyConsumer {
46 public:
47   void handleBuildCommand(Command) override {}
48 
49   void
50   handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
51     this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
52   }
53 
54   void handleFileDependency(StringRef File) override {
55     Dependencies.push_back(std::string(File));
56   }
57 
58   void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
59     // Same as `handleModuleDependency`.
60   }
61 
62   void handleModuleDependency(ModuleDeps MD) override {
63     // These are ignored for the make format as it can't support the full
64     // set of deps, and handleFileDependency handles enough for implicitly
65     // built modules to work.
66   }
67 
68   void handleContextHash(std::string Hash) override {}
69 
70   std::string lookupModuleOutput(const ModuleID &ID,
71                                  ModuleOutputKind Kind) override {
72     llvm::report_fatal_error("unexpected call to lookupModuleOutput");
73   }
74 
75   void printDependencies(std::string &S) {
76     assert(Opts && "Handled dependency output options.");
77 
78     class DependencyPrinter : public DependencyFileGenerator {
79     public:
80       DependencyPrinter(DependencyOutputOptions &Opts,
81                         ArrayRef<std::string> Dependencies)
82           : DependencyFileGenerator(Opts) {
83         for (const auto &Dep : Dependencies)
84           addDependency(Dep);
85       }
86 
87       void printDependencies(std::string &S) {
88         llvm::raw_string_ostream OS(S);
89         outputDependencyFile(OS);
90       }
91     };
92 
93     DependencyPrinter Generator(*Opts, Dependencies);
94     Generator.printDependencies(S);
95   }
96 
97 protected:
98   std::unique_ptr<DependencyOutputOptions> Opts;
99   std::vector<std::string> Dependencies;
100 };
101 } // anonymous namespace
102 
103 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
104     const std::vector<std::string> &CommandLine, StringRef CWD,
105     std::optional<StringRef> ModuleName) {
106   MakeDependencyPrinterConsumer Consumer;
107   auto Result =
108       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
109   if (Result)
110     return std::move(Result);
111   std::string Output;
112   Consumer.printDependencies(Output);
113   return Output;
114 }
115 
116 llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
117     const CompileCommand &Command, StringRef CWD,
118     std::string &MakeformatOutput, std::string &MakeformatOutputPath) {
119   class P1689ModuleDependencyPrinterConsumer
120       : public MakeDependencyPrinterConsumer {
121   public:
122     P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
123                                          const CompileCommand &Command)
124         : Filename(Command.Filename), Rule(Rule) {
125       Rule.PrimaryOutput = Command.Output;
126     }
127 
128     void handleProvidedAndRequiredStdCXXModules(
129         std::optional<P1689ModuleInfo> Provided,
130         std::vector<P1689ModuleInfo> Requires) override {
131       Rule.Provides = Provided;
132       if (Rule.Provides)
133         Rule.Provides->SourcePath = Filename.str();
134       Rule.Requires = Requires;
135     }
136 
137     StringRef getMakeFormatDependencyOutputPath() {
138       if (Opts->OutputFormat != DependencyOutputFormat::Make)
139         return {};
140       return Opts->OutputFile;
141     }
142 
143   private:
144     StringRef Filename;
145     P1689Rule &Rule;
146   };
147 
148   P1689Rule Rule;
149   P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
150   auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer);
151   if (Result)
152     return std::move(Result);
153 
154   MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
155   if (!MakeformatOutputPath.empty())
156     Consumer.printDependencies(MakeformatOutput);
157   return Rule;
158 }
159 
160 llvm::Expected<FullDependenciesResult>
161 DependencyScanningTool::getFullDependencies(
162     const std::vector<std::string> &CommandLine, StringRef CWD,
163     const llvm::StringSet<> &AlreadySeen,
164     LookupModuleOutputCallback LookupModuleOutput,
165     std::optional<StringRef> ModuleName) {
166   FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
167                                   Worker.shouldEagerLoadModules());
168   llvm::Error Result =
169       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
170   if (Result)
171     return std::move(Result);
172   return Consumer.takeFullDependencies();
173 }
174 
175 llvm::Expected<FullDependenciesResult>
176 DependencyScanningTool::getFullDependenciesLegacyDriverCommand(
177     const std::vector<std::string> &CommandLine, StringRef CWD,
178     const llvm::StringSet<> &AlreadySeen,
179     LookupModuleOutputCallback LookupModuleOutput,
180     std::optional<StringRef> ModuleName) {
181   FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
182                                   Worker.shouldEagerLoadModules());
183   llvm::Error Result =
184       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
185   if (Result)
186     return std::move(Result);
187   return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine);
188 }
189 
190 FullDependenciesResult FullDependencyConsumer::takeFullDependencies() {
191   FullDependenciesResult FDR;
192   FullDependencies &FD = FDR.FullDeps;
193 
194   FD.ID.ContextHash = std::move(ContextHash);
195   FD.FileDeps = std::move(Dependencies);
196   FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
197   FD.Commands = std::move(Commands);
198 
199   for (auto &&M : ClangModuleDeps) {
200     auto &MD = M.second;
201     if (MD.ImportedByMainFile)
202       FD.ClangModuleDeps.push_back(MD.ID);
203     // TODO: Avoid handleModuleDependency even being called for modules
204     //   we've already seen.
205     if (AlreadySeen.count(M.first))
206       continue;
207     FDR.DiscoveredModules.push_back(std::move(MD));
208   }
209 
210   return FDR;
211 }
212 
213 FullDependenciesResult
214 FullDependencyConsumer::getFullDependenciesLegacyDriverCommand(
215     const std::vector<std::string> &OriginalCommandLine) const {
216   FullDependencies FD;
217 
218   FD.DriverCommandLine = makeTUCommandLineWithoutPaths(
219       ArrayRef<std::string>(OriginalCommandLine).slice(1));
220 
221   FD.ID.ContextHash = std::move(ContextHash);
222 
223   FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
224 
225   for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
226     FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile);
227 
228   for (auto &&M : ClangModuleDeps) {
229     auto &MD = M.second;
230     if (MD.ImportedByMainFile) {
231       FD.ClangModuleDeps.push_back(MD.ID);
232       auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile);
233       if (EagerLoadModules) {
234         FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath);
235       } else {
236         FD.DriverCommandLine.push_back("-fmodule-map-file=" +
237                                        MD.ClangModuleMapFile);
238         FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName +
239                                        "=" + PCMPath);
240       }
241     }
242   }
243 
244   FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
245 
246   FullDependenciesResult FDR;
247 
248   for (auto &&M : ClangModuleDeps) {
249     // TODO: Avoid handleModuleDependency even being called for modules
250     //   we've already seen.
251     if (AlreadySeen.count(M.first))
252       continue;
253     FDR.DiscoveredModules.push_back(std::move(M.second));
254   }
255 
256   FDR.FullDeps = std::move(FD);
257   return FDR;
258 }
259