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