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
13using namespace clang;
14using namespace tooling;
15using namespace dependencies;
16
17DependencyScanningTool::DependencyScanningTool(
18    DependencyScanningService &Service,
19    llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
20    : Worker(Service, std::move(FS)) {}
21
22namespace {
23/// Prints out all of the gathered dependencies into a string.
24class MakeDependencyPrinterConsumer : public DependencyConsumer {
25public:
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
67protected:
68  std::unique_ptr<DependencyOutputOptions> Opts;
69  std::vector<std::string> Dependencies;
70};
71} // anonymous namespace
72
73llvm::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
86llvm::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
141llvm::Expected<TranslationUnitDeps>
142DependencyScanningTool::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
155llvm::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
168TranslationUnitDeps 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
189ModuleDepsGraph 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
204CallbackActionController::~CallbackActionController() {}
205