DependencyScanningTool.cpp revision 360784
160484Sobrien//===- DependencyScanningTool.cpp - clang-scan-deps service ---------------===//
278828Sobrien//
378828Sobrien// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
460484Sobrien// See https://llvm.org/LICENSE.txt for license information.
560484Sobrien// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
660484Sobrien//
760484Sobrien//===----------------------------------------------------------------------===//
860484Sobrien
960484Sobrien#include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
1060484Sobrien#include "clang/Frontend/Utils.h"
1160484Sobrien#include "llvm/Support/JSON.h"
1260484Sobrien
1360484Sobrienstatic llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {
1460484Sobrien  std::vector<llvm::StringRef> Strings;
1560484Sobrien  for (auto &&I : Set)
1660484Sobrien    Strings.push_back(I.getKey());
1760484Sobrien  std::sort(Strings.begin(), Strings.end());
1860484Sobrien  return llvm::json::Array(Strings);
1960484Sobrien}
2060484Sobrien
2160484Sobriennamespace clang{
2260484Sobriennamespace tooling{
2360484Sobriennamespace dependencies{
2460484Sobrien
2560484SobrienDependencyScanningTool::DependencyScanningTool(
2660484Sobrien    DependencyScanningService &Service)
2760484Sobrien    : Format(Service.getFormat()), Worker(Service) {
2860484Sobrien}
2960484Sobrien
3060484Sobrienllvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
3160484Sobrien    const tooling::CompilationDatabase &Compilations, StringRef CWD) {
3260484Sobrien  /// Prints out all of the gathered dependencies into a string.
3360484Sobrien  class MakeDependencyPrinterConsumer : public DependencyConsumer {
3460484Sobrien  public:
3560484Sobrien    void handleFileDependency(const DependencyOutputOptions &Opts,
3660484Sobrien                              StringRef File) override {
3760484Sobrien      if (!this->Opts)
3860484Sobrien        this->Opts = std::make_unique<DependencyOutputOptions>(Opts);
3989857Sobrien      Dependencies.push_back(File);
4089857Sobrien    }
4160484Sobrien
4289857Sobrien    void handleModuleDependency(ModuleDeps MD) override {
4389857Sobrien      // These are ignored for the make format as it can't support the full
4489857Sobrien      // set of deps, and handleFileDependency handles enough for implicitly
4589857Sobrien      // built modules to work.
4689857Sobrien    }
4789857Sobrien
4889857Sobrien    void handleContextHash(std::string Hash) override {}
4989857Sobrien
5089857Sobrien    void printDependencies(std::string &S) {
5189857Sobrien      if (!Opts)
5289857Sobrien        return;
5389857Sobrien
5489857Sobrien      class DependencyPrinter : public DependencyFileGenerator {
5589857Sobrien      public:
5689857Sobrien        DependencyPrinter(DependencyOutputOptions &Opts,
5789857Sobrien                          ArrayRef<std::string> Dependencies)
5889857Sobrien            : DependencyFileGenerator(Opts) {
5989857Sobrien          for (const auto &Dep : Dependencies)
6089857Sobrien            addDependency(Dep);
6189857Sobrien        }
6289857Sobrien
6389857Sobrien        void printDependencies(std::string &S) {
6489857Sobrien          llvm::raw_string_ostream OS(S);
6589857Sobrien          outputDependencyFile(OS);
6689857Sobrien        }
6789857Sobrien      };
6889857Sobrien
6989857Sobrien      DependencyPrinter Generator(*Opts, Dependencies);
7089857Sobrien      Generator.printDependencies(S);
7189857Sobrien    }
7289857Sobrien
7360484Sobrien  private:
7460484Sobrien    std::unique_ptr<DependencyOutputOptions> Opts;
7560484Sobrien    std::vector<std::string> Dependencies;
7677298Sobrien  };
7760484Sobrien
7860484Sobrien  class FullDependencyPrinterConsumer : public DependencyConsumer {
7960484Sobrien  public:
8060484Sobrien    void handleFileDependency(const DependencyOutputOptions &Opts,
8160484Sobrien                              StringRef File) override {
8260484Sobrien      Dependencies.push_back(File);
8360484Sobrien    }
8460484Sobrien
8560484Sobrien    void handleModuleDependency(ModuleDeps MD) override {
8660484Sobrien      ClangModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD);
8760484Sobrien    }
8860484Sobrien
8960484Sobrien    void handleContextHash(std::string Hash) override {
9060484Sobrien      ContextHash = std::move(Hash);
9160484Sobrien    }
9260484Sobrien
9360484Sobrien    void printDependencies(std::string &S, StringRef MainFile) {
9460484Sobrien      // Sort the modules by name to get a deterministic order.
9560484Sobrien      std::vector<StringRef> Modules;
9660484Sobrien      for (auto &&Dep : ClangModuleDeps)
9760484Sobrien        Modules.push_back(Dep.first);
9860484Sobrien      std::sort(Modules.begin(), Modules.end());
9960484Sobrien
10060484Sobrien      llvm::raw_string_ostream OS(S);
10177298Sobrien
10260484Sobrien      using namespace llvm::json;
10389857Sobrien
10489857Sobrien      Array Imports;
10560484Sobrien      for (auto &&ModName : Modules) {
10660484Sobrien        auto &MD = ClangModuleDeps[ModName];
10760484Sobrien        if (MD.ImportedByMainFile)
10889857Sobrien          Imports.push_back(MD.ModuleName);
10960484Sobrien      }
11060484Sobrien
11160484Sobrien      Array Mods;
11260484Sobrien      for (auto &&ModName : Modules) {
11360484Sobrien        auto &MD = ClangModuleDeps[ModName];
11460484Sobrien        Object Mod{
11589857Sobrien            {"name", MD.ModuleName},
11660484Sobrien            {"file-deps", toJSONSorted(MD.FileDeps)},
11789857Sobrien            {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},
11860484Sobrien            {"clang-modulemap-file", MD.ClangModuleMapFile},
11960484Sobrien        };
12060484Sobrien        Mods.push_back(std::move(Mod));
12160484Sobrien      }
12260484Sobrien
12360484Sobrien      Object O{
12460484Sobrien          {"input-file", MainFile},
12560484Sobrien          {"clang-context-hash", ContextHash},
12660484Sobrien          {"file-deps", Dependencies},
12760484Sobrien          {"clang-module-deps", std::move(Imports)},
12860484Sobrien          {"clang-modules", std::move(Mods)},
12960484Sobrien      };
13060484Sobrien
13160484Sobrien      S = llvm::formatv("{0:2},\n", Value(std::move(O))).str();
13260484Sobrien      return;
13360484Sobrien    }
13460484Sobrien
13560484Sobrien  private:
13660484Sobrien    std::vector<std::string> Dependencies;
13760484Sobrien    std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
13889857Sobrien    std::string ContextHash;
13989857Sobrien  };
14060484Sobrien
14160484Sobrien
14260484Sobrien  // We expect a single command here because if a source file occurs multiple
14360484Sobrien  // times in the original CDB, then `computeDependencies` would run the
14489857Sobrien  // `DependencyScanningAction` once for every time the input occured in the
14577298Sobrien  // CDB. Instead we split up the CDB into single command chunks to avoid this
14660484Sobrien  // behavior.
14760484Sobrien  assert(Compilations.getAllCompileCommands().size() == 1 &&
14860484Sobrien         "Expected a compilation database with a single command!");
14960484Sobrien  std::string Input = Compilations.getAllCompileCommands().front().Filename;
15060484Sobrien
15160484Sobrien  if (Format == ScanningOutputFormat::Make) {
15277298Sobrien    MakeDependencyPrinterConsumer Consumer;
15389857Sobrien    auto Result =
15460484Sobrien        Worker.computeDependencies(Input, CWD, Compilations, Consumer);
15560484Sobrien    if (Result)
15660484Sobrien      return std::move(Result);
15760484Sobrien    std::string Output;
15860484Sobrien    Consumer.printDependencies(Output);
15960484Sobrien    return Output;
16060484Sobrien  } else {
16160484Sobrien    FullDependencyPrinterConsumer Consumer;
16260484Sobrien    auto Result =
16360484Sobrien        Worker.computeDependencies(Input, CWD, Compilations, Consumer);
16460484Sobrien    if (Result)
16560484Sobrien      return std::move(Result);
16660484Sobrien    std::string Output;
16760484Sobrien    Consumer.printDependencies(Output, Input);
16860484Sobrien    return Output;
16960484Sobrien  }
17060484Sobrien}
17189857Sobrien
17289857Sobrien} // end namespace dependencies
17389857Sobrien} // end namespace tooling
17460484Sobrien} // end namespace clang
17560484Sobrien