xref: /freebsd-src/contrib/llvm-project/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp (revision 1ac55f4cb0001fed92329746c730aa9a947c09a5)
1480093f4SDimitry Andric //===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
2a7dea167SDimitry Andric //
3a7dea167SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a7dea167SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5a7dea167SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a7dea167SDimitry Andric //
7a7dea167SDimitry Andric //===----------------------------------------------------------------------===//
8a7dea167SDimitry Andric 
9a7dea167SDimitry Andric #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
10a7dea167SDimitry Andric #include "clang/Frontend/Utils.h"
11bdd1243dSDimitry Andric #include <optional>
12a7dea167SDimitry Andric 
13753f127fSDimitry Andric using namespace clang;
14753f127fSDimitry Andric using namespace tooling;
15753f127fSDimitry Andric using namespace dependencies;
16a7dea167SDimitry Andric 
17bdd1243dSDimitry Andric static std::vector<std::string>
18bdd1243dSDimitry Andric makeTUCommandLineWithoutPaths(ArrayRef<std::string> OriginalCommandLine) {
1981ad6265SDimitry Andric   std::vector<std::string> Args = OriginalCommandLine;
20fe6060f1SDimitry Andric 
2181ad6265SDimitry Andric   Args.push_back("-fno-implicit-modules");
2281ad6265SDimitry Andric   Args.push_back("-fno-implicit-module-maps");
23fe6060f1SDimitry Andric 
2481ad6265SDimitry Andric   // These arguments are unused in explicit compiles.
2581ad6265SDimitry Andric   llvm::erase_if(Args, [](StringRef Arg) {
2681ad6265SDimitry Andric     if (Arg.consume_front("-fmodules-")) {
2781ad6265SDimitry Andric       return Arg.startswith("cache-path=") ||
2881ad6265SDimitry Andric              Arg.startswith("prune-interval=") ||
2981ad6265SDimitry Andric              Arg.startswith("prune-after=") ||
3081ad6265SDimitry Andric              Arg == "validate-once-per-build-session";
3181ad6265SDimitry Andric     }
3281ad6265SDimitry Andric     return Arg.startswith("-fbuild-session-file=");
3381ad6265SDimitry Andric   });
3481ad6265SDimitry Andric 
35fe6060f1SDimitry Andric   return Args;
36fe6060f1SDimitry Andric }
37fe6060f1SDimitry Andric 
38480093f4SDimitry Andric DependencyScanningTool::DependencyScanningTool(
39fcaf7f86SDimitry Andric     DependencyScanningService &Service,
40fcaf7f86SDimitry Andric     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
41fcaf7f86SDimitry Andric     : Worker(Service, std::move(FS)) {}
42a7dea167SDimitry Andric 
43*1ac55f4cSDimitry Andric namespace {
44a7dea167SDimitry Andric /// Prints out all of the gathered dependencies into a string.
45480093f4SDimitry Andric class MakeDependencyPrinterConsumer : public DependencyConsumer {
46a7dea167SDimitry Andric public:
47bdd1243dSDimitry Andric   void handleBuildCommand(Command) override {}
48bdd1243dSDimitry Andric 
49fe6060f1SDimitry Andric   void
50fe6060f1SDimitry Andric   handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
51a7dea167SDimitry Andric     this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
52fe6060f1SDimitry Andric   }
53fe6060f1SDimitry Andric 
54fe6060f1SDimitry Andric   void handleFileDependency(StringRef File) override {
555ffd83dbSDimitry Andric     Dependencies.push_back(std::string(File));
56a7dea167SDimitry Andric   }
57a7dea167SDimitry Andric 
58fe6060f1SDimitry Andric   void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
59fe6060f1SDimitry Andric     // Same as `handleModuleDependency`.
60fe6060f1SDimitry Andric   }
61fe6060f1SDimitry Andric 
62480093f4SDimitry Andric   void handleModuleDependency(ModuleDeps MD) override {
63480093f4SDimitry Andric     // These are ignored for the make format as it can't support the full
64480093f4SDimitry Andric     // set of deps, and handleFileDependency handles enough for implicitly
65480093f4SDimitry Andric     // built modules to work.
66480093f4SDimitry Andric   }
67480093f4SDimitry Andric 
68480093f4SDimitry Andric   void handleContextHash(std::string Hash) override {}
69480093f4SDimitry Andric 
70bdd1243dSDimitry Andric   std::string lookupModuleOutput(const ModuleID &ID,
71bdd1243dSDimitry Andric                                  ModuleOutputKind Kind) override {
72bdd1243dSDimitry Andric     llvm::report_fatal_error("unexpected call to lookupModuleOutput");
73bdd1243dSDimitry Andric   }
74bdd1243dSDimitry Andric 
75a7dea167SDimitry Andric   void printDependencies(std::string &S) {
76fe6060f1SDimitry Andric     assert(Opts && "Handled dependency output options.");
77a7dea167SDimitry Andric 
78a7dea167SDimitry Andric     class DependencyPrinter : public DependencyFileGenerator {
79a7dea167SDimitry Andric     public:
80a7dea167SDimitry Andric       DependencyPrinter(DependencyOutputOptions &Opts,
81a7dea167SDimitry Andric                         ArrayRef<std::string> Dependencies)
82a7dea167SDimitry Andric           : DependencyFileGenerator(Opts) {
83a7dea167SDimitry Andric         for (const auto &Dep : Dependencies)
84a7dea167SDimitry Andric           addDependency(Dep);
85a7dea167SDimitry Andric       }
86a7dea167SDimitry Andric 
87a7dea167SDimitry Andric       void printDependencies(std::string &S) {
88a7dea167SDimitry Andric         llvm::raw_string_ostream OS(S);
89a7dea167SDimitry Andric         outputDependencyFile(OS);
90a7dea167SDimitry Andric       }
91a7dea167SDimitry Andric     };
92a7dea167SDimitry Andric 
93a7dea167SDimitry Andric     DependencyPrinter Generator(*Opts, Dependencies);
94a7dea167SDimitry Andric     Generator.printDependencies(S);
95a7dea167SDimitry Andric   }
96a7dea167SDimitry Andric 
97*1ac55f4cSDimitry Andric protected:
98a7dea167SDimitry Andric   std::unique_ptr<DependencyOutputOptions> Opts;
99a7dea167SDimitry Andric   std::vector<std::string> Dependencies;
100a7dea167SDimitry Andric };
101*1ac55f4cSDimitry Andric } // anonymous namespace
102a7dea167SDimitry Andric 
103*1ac55f4cSDimitry Andric llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
104*1ac55f4cSDimitry Andric     const std::vector<std::string> &CommandLine, StringRef CWD,
105*1ac55f4cSDimitry Andric     std::optional<StringRef> ModuleName) {
1065ffd83dbSDimitry Andric   MakeDependencyPrinterConsumer Consumer;
107349cc55cSDimitry Andric   auto Result =
108349cc55cSDimitry Andric       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
1095ffd83dbSDimitry Andric   if (Result)
1105ffd83dbSDimitry Andric     return std::move(Result);
1115ffd83dbSDimitry Andric   std::string Output;
1125ffd83dbSDimitry Andric   Consumer.printDependencies(Output);
1135ffd83dbSDimitry Andric   return Output;
1145ffd83dbSDimitry Andric }
1155ffd83dbSDimitry Andric 
116*1ac55f4cSDimitry Andric llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
117*1ac55f4cSDimitry Andric     const CompileCommand &Command, StringRef CWD,
118*1ac55f4cSDimitry Andric     std::string &MakeformatOutput, std::string &MakeformatOutputPath) {
119*1ac55f4cSDimitry Andric   class P1689ModuleDependencyPrinterConsumer
120*1ac55f4cSDimitry Andric       : public MakeDependencyPrinterConsumer {
121*1ac55f4cSDimitry Andric   public:
122*1ac55f4cSDimitry Andric     P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
123*1ac55f4cSDimitry Andric                                          const CompileCommand &Command)
124*1ac55f4cSDimitry Andric         : Filename(Command.Filename), Rule(Rule) {
125*1ac55f4cSDimitry Andric       Rule.PrimaryOutput = Command.Output;
126*1ac55f4cSDimitry Andric     }
127*1ac55f4cSDimitry Andric 
128*1ac55f4cSDimitry Andric     void handleProvidedAndRequiredStdCXXModules(
129*1ac55f4cSDimitry Andric         std::optional<P1689ModuleInfo> Provided,
130*1ac55f4cSDimitry Andric         std::vector<P1689ModuleInfo> Requires) override {
131*1ac55f4cSDimitry Andric       Rule.Provides = Provided;
132*1ac55f4cSDimitry Andric       if (Rule.Provides)
133*1ac55f4cSDimitry Andric         Rule.Provides->SourcePath = Filename.str();
134*1ac55f4cSDimitry Andric       Rule.Requires = Requires;
135*1ac55f4cSDimitry Andric     }
136*1ac55f4cSDimitry Andric 
137*1ac55f4cSDimitry Andric     StringRef getMakeFormatDependencyOutputPath() {
138*1ac55f4cSDimitry Andric       if (Opts->OutputFormat != DependencyOutputFormat::Make)
139*1ac55f4cSDimitry Andric         return {};
140*1ac55f4cSDimitry Andric       return Opts->OutputFile;
141*1ac55f4cSDimitry Andric     }
142*1ac55f4cSDimitry Andric 
143*1ac55f4cSDimitry Andric   private:
144*1ac55f4cSDimitry Andric     StringRef Filename;
145*1ac55f4cSDimitry Andric     P1689Rule &Rule;
146*1ac55f4cSDimitry Andric   };
147*1ac55f4cSDimitry Andric 
148*1ac55f4cSDimitry Andric   P1689Rule Rule;
149*1ac55f4cSDimitry Andric   P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
150*1ac55f4cSDimitry Andric   auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer);
151*1ac55f4cSDimitry Andric   if (Result)
152*1ac55f4cSDimitry Andric     return std::move(Result);
153*1ac55f4cSDimitry Andric 
154*1ac55f4cSDimitry Andric   MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
155*1ac55f4cSDimitry Andric   if (!MakeformatOutputPath.empty())
156*1ac55f4cSDimitry Andric     Consumer.printDependencies(MakeformatOutput);
157*1ac55f4cSDimitry Andric   return Rule;
158*1ac55f4cSDimitry Andric }
159*1ac55f4cSDimitry Andric 
1605ffd83dbSDimitry Andric llvm::Expected<FullDependenciesResult>
1615ffd83dbSDimitry Andric DependencyScanningTool::getFullDependencies(
162349cc55cSDimitry Andric     const std::vector<std::string> &CommandLine, StringRef CWD,
163349cc55cSDimitry Andric     const llvm::StringSet<> &AlreadySeen,
164bdd1243dSDimitry Andric     LookupModuleOutputCallback LookupModuleOutput,
165bdd1243dSDimitry Andric     std::optional<StringRef> ModuleName) {
166bdd1243dSDimitry Andric   FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
167bdd1243dSDimitry Andric                                   Worker.shouldEagerLoadModules());
168bdd1243dSDimitry Andric   llvm::Error Result =
169bdd1243dSDimitry Andric       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
170bdd1243dSDimitry Andric   if (Result)
171bdd1243dSDimitry Andric     return std::move(Result);
172bdd1243dSDimitry Andric   return Consumer.takeFullDependencies();
173480093f4SDimitry Andric }
174480093f4SDimitry Andric 
175bdd1243dSDimitry Andric llvm::Expected<FullDependenciesResult>
176bdd1243dSDimitry Andric DependencyScanningTool::getFullDependenciesLegacyDriverCommand(
177bdd1243dSDimitry Andric     const std::vector<std::string> &CommandLine, StringRef CWD,
178bdd1243dSDimitry Andric     const llvm::StringSet<> &AlreadySeen,
179bdd1243dSDimitry Andric     LookupModuleOutputCallback LookupModuleOutput,
180bdd1243dSDimitry Andric     std::optional<StringRef> ModuleName) {
181bdd1243dSDimitry Andric   FullDependencyConsumer Consumer(AlreadySeen, LookupModuleOutput,
182bdd1243dSDimitry Andric                                   Worker.shouldEagerLoadModules());
183bdd1243dSDimitry Andric   llvm::Error Result =
184bdd1243dSDimitry Andric       Worker.computeDependencies(CWD, CommandLine, Consumer, ModuleName);
185bdd1243dSDimitry Andric   if (Result)
186bdd1243dSDimitry Andric     return std::move(Result);
187bdd1243dSDimitry Andric   return Consumer.getFullDependenciesLegacyDriverCommand(CommandLine);
188fe6060f1SDimitry Andric }
189fe6060f1SDimitry Andric 
190bdd1243dSDimitry Andric FullDependenciesResult FullDependencyConsumer::takeFullDependencies() {
191bdd1243dSDimitry Andric   FullDependenciesResult FDR;
192bdd1243dSDimitry Andric   FullDependencies &FD = FDR.FullDeps;
19381ad6265SDimitry Andric 
194fe6060f1SDimitry Andric   FD.ID.ContextHash = std::move(ContextHash);
195bdd1243dSDimitry Andric   FD.FileDeps = std::move(Dependencies);
196bdd1243dSDimitry Andric   FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
197bdd1243dSDimitry Andric   FD.Commands = std::move(Commands);
198480093f4SDimitry Andric 
1995ffd83dbSDimitry Andric   for (auto &&M : ClangModuleDeps) {
2005ffd83dbSDimitry Andric     auto &MD = M.second;
201480093f4SDimitry Andric     if (MD.ImportedByMainFile)
202fe6060f1SDimitry Andric       FD.ClangModuleDeps.push_back(MD.ID);
203bdd1243dSDimitry Andric     // TODO: Avoid handleModuleDependency even being called for modules
204bdd1243dSDimitry Andric     //   we've already seen.
205bdd1243dSDimitry Andric     if (AlreadySeen.count(M.first))
206bdd1243dSDimitry Andric       continue;
207bdd1243dSDimitry Andric     FDR.DiscoveredModules.push_back(std::move(MD));
208bdd1243dSDimitry Andric   }
209bdd1243dSDimitry Andric 
210bdd1243dSDimitry Andric   return FDR;
211bdd1243dSDimitry Andric }
212bdd1243dSDimitry Andric 
213bdd1243dSDimitry Andric FullDependenciesResult
214bdd1243dSDimitry Andric FullDependencyConsumer::getFullDependenciesLegacyDriverCommand(
215bdd1243dSDimitry Andric     const std::vector<std::string> &OriginalCommandLine) const {
216bdd1243dSDimitry Andric   FullDependencies FD;
217bdd1243dSDimitry Andric 
218bdd1243dSDimitry Andric   FD.DriverCommandLine = makeTUCommandLineWithoutPaths(
219bdd1243dSDimitry Andric       ArrayRef<std::string>(OriginalCommandLine).slice(1));
220bdd1243dSDimitry Andric 
221bdd1243dSDimitry Andric   FD.ID.ContextHash = std::move(ContextHash);
222bdd1243dSDimitry Andric 
223bdd1243dSDimitry Andric   FD.FileDeps.assign(Dependencies.begin(), Dependencies.end());
224bdd1243dSDimitry Andric 
225bdd1243dSDimitry Andric   for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps)
226bdd1243dSDimitry Andric     FD.DriverCommandLine.push_back("-fmodule-file=" + PMD.PCMFile);
227bdd1243dSDimitry Andric 
228bdd1243dSDimitry Andric   for (auto &&M : ClangModuleDeps) {
229bdd1243dSDimitry Andric     auto &MD = M.second;
230bdd1243dSDimitry Andric     if (MD.ImportedByMainFile) {
231bdd1243dSDimitry Andric       FD.ClangModuleDeps.push_back(MD.ID);
232bdd1243dSDimitry Andric       auto PCMPath = LookupModuleOutput(MD.ID, ModuleOutputKind::ModuleFile);
233bdd1243dSDimitry Andric       if (EagerLoadModules) {
234bdd1243dSDimitry Andric         FD.DriverCommandLine.push_back("-fmodule-file=" + PCMPath);
235bdd1243dSDimitry Andric       } else {
236bdd1243dSDimitry Andric         FD.DriverCommandLine.push_back("-fmodule-map-file=" +
237bdd1243dSDimitry Andric                                        MD.ClangModuleMapFile);
238bdd1243dSDimitry Andric         FD.DriverCommandLine.push_back("-fmodule-file=" + MD.ID.ModuleName +
239bdd1243dSDimitry Andric                                        "=" + PCMPath);
240bdd1243dSDimitry Andric       }
241bdd1243dSDimitry Andric     }
242480093f4SDimitry Andric   }
243480093f4SDimitry Andric 
244fe6060f1SDimitry Andric   FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
245fe6060f1SDimitry Andric 
2465ffd83dbSDimitry Andric   FullDependenciesResult FDR;
2475ffd83dbSDimitry Andric 
2485ffd83dbSDimitry Andric   for (auto &&M : ClangModuleDeps) {
2495ffd83dbSDimitry Andric     // TODO: Avoid handleModuleDependency even being called for modules
2505ffd83dbSDimitry Andric     //   we've already seen.
2515ffd83dbSDimitry Andric     if (AlreadySeen.count(M.first))
2525ffd83dbSDimitry Andric       continue;
2535ffd83dbSDimitry Andric     FDR.DiscoveredModules.push_back(std::move(M.second));
254480093f4SDimitry Andric   }
255480093f4SDimitry Andric 
2565ffd83dbSDimitry Andric   FDR.FullDeps = std::move(FD);
2575ffd83dbSDimitry Andric   return FDR;
258480093f4SDimitry Andric }
259