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 DependencyScanningTool::DependencyScanningTool(
18     DependencyScanningService &Service,
19     llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
20     : Worker(Service, std::move(FS)) {}
21 
22 namespace {
23 /// Prints out all of the gathered dependencies into a string.
24 class MakeDependencyPrinterConsumer : public DependencyConsumer {
25 public:
26   void handleBuildCommand(Command) override {}
27 
28   void
29   handleDependencyOutputOpts(const DependencyOutputOptions &Opts) override {
30     this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
31   }
32 
33   void handleFileDependency(StringRef File) override {
34     Dependencies.push_back(std::string(File));
35   }
36 
37   // These are ignored for the make format as it can't support the full
38   // set of deps, and handleFileDependency handles enough for implicitly
39   // built modules to work.
40   void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {}
41   void handleModuleDependency(ModuleDeps MD) override {}
42   void handleDirectModuleDependency(ModuleID ID) override {}
43   void handleContextHash(std::string Hash) override {}
44 
45   void printDependencies(std::string &S) {
46     assert(Opts && "Handled dependency output options.");
47 
48     class DependencyPrinter : public DependencyFileGenerator {
49     public:
50       DependencyPrinter(DependencyOutputOptions &Opts,
51                         ArrayRef<std::string> Dependencies)
52           : DependencyFileGenerator(Opts) {
53         for (const auto &Dep : Dependencies)
54           addDependency(Dep);
55       }
56 
57       void printDependencies(std::string &S) {
58         llvm::raw_string_ostream OS(S);
59         outputDependencyFile(OS);
60       }
61     };
62 
63     DependencyPrinter Generator(*Opts, Dependencies);
64     Generator.printDependencies(S);
65   }
66 
67 protected:
68   std::unique_ptr<DependencyOutputOptions> Opts;
69   std::vector<std::string> Dependencies;
70 };
71 } // anonymous namespace
72 
73 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
74     const std::vector<std::string> &CommandLine, StringRef CWD) {
75   MakeDependencyPrinterConsumer Consumer;
76   CallbackActionController Controller(nullptr);
77   auto Result =
78       Worker.computeDependencies(CWD, CommandLine, Consumer, Controller);
79   if (Result)
80     return std::move(Result);
81   std::string Output;
82   Consumer.printDependencies(Output);
83   return Output;
84 }
85 
86 llvm::Expected<P1689Rule> DependencyScanningTool::getP1689ModuleDependencyFile(
87     const CompileCommand &Command, StringRef CWD, std::string &MakeformatOutput,
88     std::string &MakeformatOutputPath) {
89   class P1689ModuleDependencyPrinterConsumer
90       : public MakeDependencyPrinterConsumer {
91   public:
92     P1689ModuleDependencyPrinterConsumer(P1689Rule &Rule,
93                                          const CompileCommand &Command)
94         : Filename(Command.Filename), Rule(Rule) {
95       Rule.PrimaryOutput = Command.Output;
96     }
97 
98     void handleProvidedAndRequiredStdCXXModules(
99         std::optional<P1689ModuleInfo> Provided,
100         std::vector<P1689ModuleInfo> Requires) override {
101       Rule.Provides = Provided;
102       if (Rule.Provides)
103         Rule.Provides->SourcePath = Filename.str();
104       Rule.Requires = Requires;
105     }
106 
107     StringRef getMakeFormatDependencyOutputPath() {
108       if (Opts->OutputFormat != DependencyOutputFormat::Make)
109         return {};
110       return Opts->OutputFile;
111     }
112 
113   private:
114     StringRef Filename;
115     P1689Rule &Rule;
116   };
117 
118   class P1689ActionController : public DependencyActionController {
119   public:
120     // The lookupModuleOutput is for clang modules. P1689 format don't need it.
121     std::string lookupModuleOutput(const ModuleID &,
122                                    ModuleOutputKind Kind) override {
123       return "";
124     }
125   };
126 
127   P1689Rule Rule;
128   P1689ModuleDependencyPrinterConsumer Consumer(Rule, Command);
129   P1689ActionController Controller;
130   auto Result = Worker.computeDependencies(CWD, Command.CommandLine, Consumer,
131                                            Controller);
132   if (Result)
133     return std::move(Result);
134 
135   MakeformatOutputPath = Consumer.getMakeFormatDependencyOutputPath();
136   if (!MakeformatOutputPath.empty())
137     Consumer.printDependencies(MakeformatOutput);
138   return Rule;
139 }
140 
141 llvm::Expected<TranslationUnitDeps>
142 DependencyScanningTool::getTranslationUnitDependencies(
143     const std::vector<std::string> &CommandLine, StringRef CWD,
144     const llvm::DenseSet<ModuleID> &AlreadySeen,
145     LookupModuleOutputCallback LookupModuleOutput) {
146   FullDependencyConsumer Consumer(AlreadySeen);
147   CallbackActionController Controller(LookupModuleOutput);
148   llvm::Error Result =
149       Worker.computeDependencies(CWD, CommandLine, Consumer, Controller);
150   if (Result)
151     return std::move(Result);
152   return Consumer.takeTranslationUnitDeps();
153 }
154 
155 llvm::Expected<ModuleDepsGraph> DependencyScanningTool::getModuleDependencies(
156     StringRef ModuleName, const std::vector<std::string> &CommandLine,
157     StringRef CWD, const llvm::DenseSet<ModuleID> &AlreadySeen,
158     LookupModuleOutputCallback LookupModuleOutput) {
159   FullDependencyConsumer Consumer(AlreadySeen);
160   CallbackActionController Controller(LookupModuleOutput);
161   llvm::Error Result = Worker.computeDependencies(CWD, CommandLine, Consumer,
162                                                   Controller, ModuleName);
163   if (Result)
164     return std::move(Result);
165   return Consumer.takeModuleGraphDeps();
166 }
167 
168 TranslationUnitDeps FullDependencyConsumer::takeTranslationUnitDeps() {
169   TranslationUnitDeps TU;
170 
171   TU.ID.ContextHash = std::move(ContextHash);
172   TU.FileDeps = std::move(Dependencies);
173   TU.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
174   TU.Commands = std::move(Commands);
175 
176   for (auto &&M : ClangModuleDeps) {
177     auto &MD = M.second;
178     // TODO: Avoid handleModuleDependency even being called for modules
179     //   we've already seen.
180     if (AlreadySeen.count(M.first))
181       continue;
182     TU.ModuleGraph.push_back(std::move(MD));
183   }
184   TU.ClangModuleDeps = std::move(DirectModuleDeps);
185 
186   return TU;
187 }
188 
189 ModuleDepsGraph FullDependencyConsumer::takeModuleGraphDeps() {
190   ModuleDepsGraph ModuleGraph;
191 
192   for (auto &&M : ClangModuleDeps) {
193     auto &MD = M.second;
194     // TODO: Avoid handleModuleDependency even being called for modules
195     //   we've already seen.
196     if (AlreadySeen.count(M.first))
197       continue;
198     ModuleGraph.push_back(std::move(MD));
199   }
200 
201   return ModuleGraph;
202 }
203 
204 CallbackActionController::~CallbackActionController() {}
205