1357095Sdim//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
2353942Sdim//
3353942Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4353942Sdim// See https://llvm.org/LICENSE.txt for license information.
5353942Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6353942Sdim//
7353942Sdim//===----------------------------------------------------------------------===//
8353942Sdim
9353942Sdim#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
10353942Sdim#include "clang/Frontend/Utils.h"
11357095Sdim#include "llvm/Support/JSON.h"
12353942Sdim
13357095Sdimstatic llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
14357095Sdim  std::vector<llvm::StringRef> Strings;
15357095Sdim  for (auto &&I : Set)
16357095Sdim    Strings.push_back(I.getKey());
17357095Sdim  std::sort(Strings.begin(), Strings.end());
18357095Sdim  return llvm::json::Array(Strings);
19357095Sdim}
20357095Sdim
21353942Sdimnamespace clang{
22353942Sdimnamespace tooling{
23353942Sdimnamespace dependencies{
24353942Sdim
25357095SdimDependencyScanningTool::DependencyScanningTool(
26357095Sdim    DependencyScanningService &Service)
27357095Sdim    : Format(Service.getFormat()), Worker(Service) {
28357095Sdim}
29353942Sdim
30357095Sdimllvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
31357095Sdim    const tooling::CompilationDatabase &Compilations, StringRef CWD) {
32353942Sdim  /// Prints out all of the gathered dependencies into a string.
33357095Sdim  class MakeDependencyPrinterConsumer : public DependencyConsumer {
34353942Sdim  public:
35353942Sdim    void handleFileDependency(const DependencyOutputOptions &Opts,
36353942Sdim                              StringRef File) override {
37353942Sdim      if (!this->Opts)
38353942Sdim        this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
39353942Sdim      Dependencies.push_back(File);
40353942Sdim    }
41353942Sdim
42357095Sdim    void handleModuleDependency(ModuleDeps MD) override {
43357095Sdim      // These are ignored for the make format as it can't support the full
44357095Sdim      // set of deps, and handleFileDependency handles enough for implicitly
45357095Sdim      // built modules to work.
46357095Sdim    }
47357095Sdim
48357095Sdim    void handleContextHash(std::string Hash) override {}
49357095Sdim
50353942Sdim    void printDependencies(std::string &S) {
51353942Sdim      if (!Opts)
52353942Sdim        return;
53353942Sdim
54353942Sdim      class DependencyPrinter : public DependencyFileGenerator {
55353942Sdim      public:
56353942Sdim        DependencyPrinter(DependencyOutputOptions &Opts,
57353942Sdim                          ArrayRef<std::string> Dependencies)
58353942Sdim            : DependencyFileGenerator(Opts) {
59353942Sdim          for (const auto &Dep : Dependencies)
60353942Sdim            addDependency(Dep);
61353942Sdim        }
62353942Sdim
63353942Sdim        void printDependencies(std::string &S) {
64353942Sdim          llvm::raw_string_ostream OS(S);
65353942Sdim          outputDependencyFile(OS);
66353942Sdim        }
67353942Sdim      };
68353942Sdim
69353942Sdim      DependencyPrinter Generator(*Opts, Dependencies);
70353942Sdim      Generator.printDependencies(S);
71353942Sdim    }
72353942Sdim
73353942Sdim  private:
74353942Sdim    std::unique_ptr<DependencyOutputOptions> Opts;
75353942Sdim    std::vector<std::string> Dependencies;
76353942Sdim  };
77353942Sdim
78357095Sdim  class FullDependencyPrinterConsumer : public DependencyConsumer {
79357095Sdim  public:
80357095Sdim    void handleFileDependency(const DependencyOutputOptions &Opts,
81357095Sdim                              StringRef File) override {
82357095Sdim      Dependencies.push_back(File);
83357095Sdim    }
84357095Sdim
85357095Sdim    void handleModuleDependency(ModuleDeps MD) override {
86357095Sdim      ClangModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD);
87357095Sdim    }
88357095Sdim
89357095Sdim    void handleContextHash(std::string Hash) override {
90357095Sdim      ContextHash = std::move(Hash);
91357095Sdim    }
92357095Sdim
93357095Sdim    void printDependencies(std::string &S, StringRef MainFile) {
94357095Sdim      // Sort the modules by name to get a deterministic order.
95357095Sdim      std::vector<StringRef> Modules;
96357095Sdim      for (auto &&Dep : ClangModuleDeps)
97357095Sdim        Modules.push_back(Dep.first);
98357095Sdim      std::sort(Modules.begin(), Modules.end());
99357095Sdim
100357095Sdim      llvm::raw_string_ostream OS(S);
101357095Sdim
102357095Sdim      using namespace llvm::json;
103357095Sdim
104357095Sdim      Array Imports;
105357095Sdim      for (auto &&ModName : Modules) {
106357095Sdim        auto &MD = ClangModuleDeps[ModName];
107357095Sdim        if (MD.ImportedByMainFile)
108357095Sdim          Imports.push_back(MD.ModuleName);
109357095Sdim      }
110357095Sdim
111357095Sdim      Array Mods;
112357095Sdim      for (auto &&ModName : Modules) {
113357095Sdim        auto &MD = ClangModuleDeps[ModName];
114357095Sdim        Object Mod{
115357095Sdim            {"name", MD.ModuleName},
116357095Sdim            {"file-deps", toJSONSorted(MD.FileDeps)},
117357095Sdim            {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
118357095Sdim            {"clang-modulemap-file", MD.ClangModuleMapFile},
119357095Sdim        };
120357095Sdim        Mods.push_back(std::move(Mod));
121357095Sdim      }
122357095Sdim
123357095Sdim      Object O{
124357095Sdim          {"input-file", MainFile},
125357095Sdim          {"clang-context-hash", ContextHash},
126357095Sdim          {"file-deps", Dependencies},
127357095Sdim          {"clang-module-deps", std::move(Imports)},
128357095Sdim          {"clang-modules", std::move(Mods)},
129357095Sdim      };
130357095Sdim
131357095Sdim      S = llvm::formatv("{0:2},\n", Value(std::move(O))).str();
132357095Sdim      return;
133357095Sdim    }
134357095Sdim
135357095Sdim  private:
136357095Sdim    std::vector<std::string> Dependencies;
137357095Sdim    std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
138357095Sdim    std::string ContextHash;
139357095Sdim  };
140357095Sdim
141357095Sdim
142357095Sdim  // We expect a single command here because if a source file occurs multiple
143357095Sdim  // times in the original CDB, then `computeDependencies` would run the
144357095Sdim  // `DependencyScanningAction` once for every time the input occured in the
145357095Sdim  // CDB. Instead we split up the CDB into single command chunks to avoid this
146357095Sdim  // behavior.
147357095Sdim  assert(Compilations.getAllCompileCommands().size() == 1 &&
148357095Sdim         "Expected a compilation database with a single command!");
149357095Sdim  std::string Input = Compilations.getAllCompileCommands().front().Filename;
150357095Sdim
151357095Sdim  if (Format == ScanningOutputFormat::Make) {
152357095Sdim    MakeDependencyPrinterConsumer Consumer;
153357095Sdim    auto Result =
154357095Sdim        Worker.computeDependencies(Input, CWD, Compilations, Consumer);
155357095Sdim    if (Result)
156357095Sdim      return std::move(Result);
157357095Sdim    std::string Output;
158357095Sdim    Consumer.printDependencies(Output);
159357095Sdim    return Output;
160357095Sdim  } else {
161357095Sdim    FullDependencyPrinterConsumer Consumer;
162357095Sdim    auto Result =
163357095Sdim        Worker.computeDependencies(Input, CWD, Compilations, Consumer);
164357095Sdim    if (Result)
165357095Sdim      return std::move(Result);
166357095Sdim    std::string Output;
167357095Sdim    Consumer.printDependencies(Output, Input);
168357095Sdim    return Output;
169357095Sdim  }
170353942Sdim}
171353942Sdim
172353942Sdim} // end namespace dependencies
173353942Sdim} // end namespace tooling
174353942Sdim} // end namespace clang
175