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