Bigcheese created this revision.
Bigcheese added reviewers: arphaman, kousikk.
Bigcheese added a project: clang.
Herald added subscribers: tschuett, dexonsmith, mgrang, mgorny.

This adds experimental support for extracting a Clang module dependency graph
from a compilation database. The output format is experimental and will change.
It is currently a concatenation of JSON outputs for each compilation. Future
patches will change this to deduplicate modules between compilations.

This patch doesn't implement deduplication as the patch was already getting 
pretty
large. This also doesn't properly support C++20 modules yet.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D69420

Files:
  clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
  clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
  clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
  clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
  clang/lib/Tooling/DependencyScanning/CMakeLists.txt
  clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
  clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
  clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
  clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
  clang/test/ClangScanDeps/modules-full.cpp
  clang/tools/clang-scan-deps/ClangScanDeps.cpp

Index: clang/tools/clang-scan-deps/ClangScanDeps.cpp
===================================================================
--- clang/tools/clang-scan-deps/ClangScanDeps.cpp
+++ clang/tools/clang-scan-deps/ClangScanDeps.cpp
@@ -59,6 +59,17 @@
     llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing),
     llvm::cl::cat(DependencyScannerCategory));
 
+static llvm::cl::opt<ScanningFormat> Format(
+    "format", llvm::cl::desc("The output format for the dependencies"),
+    llvm::cl::values(clEnumValN(ScanningFormat::Make, "make",
+                                "Makefile compatible dep file"),
+                     clEnumValN(ScanningFormat::Full, "experimental-full",
+                                "Full dependency graph suitable"
+                                " for explicitly building modules. This format "
+                                "is experimental and will change.")),
+    llvm::cl::init(ScanningFormat::Make),
+    llvm::cl::cat(DependencyScannerCategory));
+
 llvm::cl::opt<unsigned>
     NumThreads("j", llvm::cl::Optional,
                llvm::cl::desc("Number of worker threads to use (default: use "
@@ -200,7 +211,7 @@
   // Print out the dependency results to STDOUT by default.
   SharedStream DependencyOS(llvm::outs());
 
-  DependencyScanningService Service(ScanMode, ReuseFileManager,
+  DependencyScanningService Service(ScanMode, Format, ReuseFileManager,
                                     SkipExcludedPPRanges);
 #if LLVM_ENABLE_THREADS
   unsigned NumWorkers =
Index: clang/test/ClangScanDeps/modules-full.cpp
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/modules-full.cpp
@@ -0,0 +1,74 @@
+// RUN: rm -rf %t.dir
+// RUN: rm -rf %t.cdb
+// RUN: rm -rf %t.module-cache
+// RUN: mkdir -p %t.dir
+// RUN: cp %s %t.dir/modules_cdb_input.cpp
+// RUN: cp %s %t.dir/modules_cdb_input2.cpp
+// RUN: mkdir %t.dir/Inputs
+// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header.h
+// RUN: cp %S/Inputs/header2.h %t.dir/Inputs/header2.h
+// RUN: cp %S/Inputs/module.modulemap %t.dir/Inputs/module.modulemap
+// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb.json > %t.cdb
+//
+// RUN: echo %t.dir > %t.result
+// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 \
+// RUN:   -mode preprocess-minimized-sources -format experimental-full >> %t.result
+// RUN: cat %t.result | FileCheck --check-prefixes=CHECK %s
+
+#include "header.h"
+
+// CHECK: [[PREFIX:(.*[/\\])+[a-zA-Z0-9.-]+]]
+// CHECK-NEXT:     {
+// CHECK-NEXT:  "clang-context-hash": "[[CONTEXT_HASH:[A-Z0-9]+]]",
+// CHECK-NEXT:  "clang-module-deps": [
+// CHECK-NEXT:    "header1"
+// CHECK-NEXT:  ],
+// CHECK-NEXT:  "clang-modules": [
+// CHECK-NEXT:    {
+// CHECK-NEXT:      "clang-module-deps": [
+// CHECK-NEXT:        "header2"
+// CHECK-NEXT:      ],
+// CHECK-NEXT:      "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",
+// CHECK-NEXT:      "file-deps": [
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header.h"
+// CHECK-NEXT:      ],
+// CHECK-NEXT:      "name": "header1"
+// CHECK-NEXT:    },
+// CHECK-NEXT:    {
+// CHECK-NEXT:      "clang-module-deps": [],
+// CHECK-NEXT:      "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",
+// CHECK-NEXT:      "file-deps": [
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header2.h",
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap"
+// CHECK-NEXT:      ],
+// CHECK-NEXT:      "name": "header2"
+// CHECK-NEXT:    }
+// CHECK-NEXT:  ],
+// CHECK-NEXT:  "file-deps": [
+// CHECK-NEXT:    "header.h"
+// CHECK-NEXT:  ],
+// CHECK-NEXT:  "input-file": "[[PREFIX]]{{[/\\]}}modules_cdb_input2.cpp"
+// CHECK-NEXT:},
+// CHECK-NEXT:{
+// CHECK-NOT:   "clang-context-hash": "[[CONTEXT_HASH]]",
+// CHECK-NEXT:  "clang-context-hash": "{{[A-Z0-9]+}}",
+// CHECK-NEXT:  "clang-module-deps": [
+// CHECK-NEXT:    "header1"
+// CHECK-NEXT:  ],
+// CHECK-NEXT:  "clang-modules": [
+// CHECK-NEXT:    {
+// CHECK-NEXT:      "clang-module-deps": [],
+// CHECK-NEXT:      "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",
+// CHECK-NEXT:      "file-deps": [
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header.h"
+// CHECK-NEXT:      ],
+// CHECK-NEXT:      "name": "header1"
+// CHECK-NEXT:    }
+// CHECK-NEXT:  ],
+// CHECK-NEXT:  "file-deps": [
+// CHECK-NEXT:    "header.h"
+// CHECK-NEXT:  ],
+// CHECK-NEXT:  "input-file": "[[PREFIX]]{{[/\\]}}modules_cdb_input.cpp"
+// CHECK-NEXT:},
Index: clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
===================================================================
--- /dev/null
+++ clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -0,0 +1,109 @@
+//===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
+
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"
+
+using namespace clang;
+using namespace tooling;
+using namespace dependencies;
+
+void ModuleDepCollectorPP::InclusionDirective(
+    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,
+    bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,
+    StringRef SearchPath, StringRef RelativePath, const Module *Imported,
+    SrcMgr::CharacteristicKind FileType) {
+  MDC.MainDeps.push_back(FileName);
+
+  if (!Imported)
+    return;
+
+  MDC.Deps[MDC.ContextHash + Imported->getTopLevelModule()->getFullModuleName()]
+      .MainFile = true;
+  DirectDeps.insert(Imported->getTopLevelModule());
+}
+
+void ModuleDepCollectorPP::EndOfMainFile() {
+  FileID MainFileID = Instance.getSourceManager().getMainFileID();
+  MDC.MainFile =
+      Instance.getSourceManager().getFileEntryForID(MainFileID)->getName();
+
+  for (const Module *M : DirectDeps) {
+    handleTopLevelModule(M);
+  }
+
+  for (auto &&I : MDC.Deps)
+    MDC.Consumer.handleModuleDependency(I.second);
+
+  DependencyOutputOptions Opts;
+  for (auto &&I : MDC.MainDeps)
+    MDC.Consumer.handleFileDependency(Opts, I);
+}
+
+void ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
+  assert(M == M->getTopLevelModule() && "Expected top level module!");
+
+  auto ModI = MDC.Deps.insert(
+      std::make_pair(MDC.ContextHash + M->getFullModuleName(), ModuleDeps{}));
+
+  if (!ModI.first->second.ModuleName.empty())
+    return;
+
+  ModuleDeps &MD = ModI.first->second;
+
+  const FileEntry *ModuleMap = Instance.getPreprocessor()
+                                   .getHeaderSearchInfo()
+                                   .getModuleMap()
+                                   .getContainingModuleMapFile(M);
+
+  MD.ModuleMapFile = ModuleMap ? ModuleMap->getName() : "";
+  MD.ModuleName = M->getFullModuleName();
+  MD.ModulePath = M->getASTFile()->getName();
+  MD.ContextHash = MDC.ContextHash;
+  serialization::ModuleFile *MF =
+      MDC.Instance.getModuleManager()->getModuleManager().lookup(
+          M->getASTFile());
+  MDC.Instance.getModuleManager()->visitInputFiles(
+      *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {
+        MD.FileDeps.insert(IF.getFile()->getName());
+      });
+
+  addAllSubmoduleDeps(M, MD);
+}
+
+void ModuleDepCollectorPP::addAllSubmoduleDeps(const Module *M,
+                                               ModuleDeps &MD) {
+  addModuleDep(M, MD);
+
+  for (const Module *SubM : M->submodules())
+    addAllSubmoduleDeps(SubM, MD);
+}
+
+void ModuleDepCollectorPP::addModuleDep(const Module *M, ModuleDeps &MD) {
+  for (const Module *Import : M->Imports) {
+    if (Import->getTopLevelModule() != M->getTopLevelModule()) {
+      MD.ModuleDeps.insert(Import->getTopLevelModuleName());
+      handleTopLevelModule(Import->getTopLevelModule());
+    }
+  }
+}
+
+ModuleDepCollector::ModuleDepCollector(CompilerInstance &I,
+                                       DependencyConsumer &C)
+    : Instance(I), Consumer(C), ContextHash(I.getInvocation().getModuleHash()) {
+}
+
+void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
+  PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this));
+}
+
+void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
Index: clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -14,6 +14,7 @@
 #include "clang/Frontend/Utils.h"
 #include "clang/Lex/PreprocessorOptions.h"
 #include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
+#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
 #include "clang/Tooling/Tooling.h"
 
 using namespace clang;
@@ -72,9 +73,11 @@
   DependencyScanningAction(
       StringRef WorkingDirectory, DependencyConsumer &Consumer,
       llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,
-      ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings)
+      ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings,
+      ScanningFormat Format)
       : WorkingDirectory(WorkingDirectory), Consumer(Consumer),
-        DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings) {}
+        DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings),
+        Format(Format) {}
 
   bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,
                      FileManager *FileMgr,
@@ -131,9 +134,20 @@
     // We need at least one -MT equivalent for the generator to work.
     if (Opts->Targets.empty())
       Opts->Targets = {"clang-scan-deps dependency"};
-    Compiler.addDependencyCollector(
-        std::make_shared<DependencyConsumerForwarder>(std::move(Opts),
-                                                      Consumer));
+
+    switch (Format) {
+    case ScanningFormat::Make:
+      Compiler.addDependencyCollector(
+          std::make_shared<DependencyConsumerForwarder>(std::move(Opts),
+                                                        Consumer));
+      break;
+    case ScanningFormat::Full:
+      Compiler.addDependencyCollector(
+          std::make_shared<ModuleDepCollector>(Compiler, Consumer));
+      break;
+    }
+
+    Consumer.handleContextHash(Compiler.getInvocation().getModuleHash());
 
     auto Action = std::make_unique<PreprocessOnlyAction>();
     const bool Result = Compiler.ExecuteAction(*Action);
@@ -147,12 +161,14 @@
   DependencyConsumer &Consumer;
   llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;
   ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;
+  ScanningFormat Format;
 };
 
 } // end anonymous namespace
 
 DependencyScanningWorker::DependencyScanningWorker(
-    DependencyScanningService &Service) {
+    DependencyScanningService &Service)
+    : Format(Service.getFormat()) {
   DiagOpts = new DiagnosticOptions();
   PCHContainerOps = std::make_shared<PCHContainerOperations>();
   RealFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem());
@@ -195,7 +211,7 @@
     Tool.setPrintErrorMessage(false);
     Tool.setDiagnosticConsumer(&DC);
     DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS,
-                                    PPSkipMappings.get());
+                                    PPSkipMappings.get(), Format);
     return !Tool.run(&Action);
   });
 }
Index: clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -8,18 +8,30 @@
 
 #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"
 #include "clang/Frontend/Utils.h"
+#include "llvm/Support/JSON.h"
+
+static llvm::json::Array toJSON(const llvm::StringSet<> &Set) {
+  llvm::json::Array Ret;
+  for (auto &&I : Set) {
+    Ret.push_back(std::string(I.getKey()));
+  }
+  return Ret;
+}
 
 namespace clang{
 namespace tooling{
 namespace dependencies{
 
-DependencyScanningTool::DependencyScanningTool(DependencyScanningService &Service,
-const tooling::CompilationDatabase &Compilations) : Worker(Service), Compilations(Compilations) {}
+DependencyScanningTool::DependencyScanningTool(
+    DependencyScanningService &Service,
+    const tooling::CompilationDatabase &Compilations)
+    : Format(Service.getFormat()), Worker(Service), Compilations(Compilations) {
+}
 
 llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(const std::string &Input,
                                               StringRef CWD) {
   /// Prints out all of the gathered dependencies into a string.
-  class DependencyPrinterConsumer : public DependencyConsumer {
+  class MakeDependencyPrinterConsumer : public DependencyConsumer {
   public:
     void handleFileDependency(const DependencyOutputOptions &Opts,
                               StringRef File) override {
@@ -28,6 +40,14 @@
       Dependencies.push_back(File);
     }
 
+    void handleModuleDependency(ModuleDeps MD) override {
+      // These are ignored for the make format as it can't support the full
+      // set of deps, and handleFileDependency handles enough for implicitly
+      // built modules to work.
+    }
+
+    void handleContextHash(std::string Hash) override {}
+
     void printDependencies(std::string &S) {
       if (!Opts)
         return;
@@ -56,14 +76,88 @@
     std::vector<std::string> Dependencies;
   };
 
-  DependencyPrinterConsumer Consumer;
-  auto Result =
-      Worker.computeDependencies(Input, CWD, Compilations, Consumer);
-  if (Result)
-    return std::move(Result);
-  std::string Output;
-  Consumer.printDependencies(Output);
-  return Output;
+  class FullDependencyPrinterConsumer : public DependencyConsumer {
+  public:
+    void handleFileDependency(const DependencyOutputOptions &Opts,
+                              StringRef File) override {
+      Dependencies.push_back(File);
+    }
+
+    void handleModuleDependency(ModuleDeps MD) override {
+      ModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD);
+    }
+
+    void handleContextHash(std::string Hash) override {
+      ContextHash = std::move(Hash);
+    }
+
+    void printDependencies(std::string &S, StringRef MainFile) {
+      // Sort the modules by name to get a deterministic order.
+      std::vector<StringRef> Modules;
+      for (auto &&Dep : ModuleDeps)
+        Modules.push_back(Dep.first);
+      std::sort(Modules.begin(), Modules.end());
+
+      llvm::raw_string_ostream OS(S);
+
+      using namespace llvm::json;
+
+      Array Imports;
+      for (auto &&ModName : Modules) {
+        auto &MD = ModuleDeps[ModName];
+        if (MD.MainFile)
+          Imports.push_back(MD.ModuleName);
+      }
+
+      Array Mods;
+      for (auto &&ModName : Modules) {
+        auto &MD = ModuleDeps[ModName];
+        Object Mod{
+            {"name", MD.ModuleName},
+            {"file-deps", toJSON(MD.FileDeps)},
+            {"clang-module-deps", toJSON(MD.ModuleDeps)},
+            {"clang-modulemap-file", MD.ModuleMapFile},
+        };
+        Mods.push_back(std::move(Mod));
+      }
+
+      Object O{
+          {"input-file", MainFile},
+          {"clang-context-hash", ContextHash},
+          {"file-deps", Dependencies},
+          {"clang-module-deps", std::move(Imports)},
+          {"clang-modules", std::move(Mods)},
+      };
+
+      S = llvm::formatv("{0:2},\n", Value(std::move(O))).str();
+      return;
+    }
+
+  private:
+    std::vector<std::string> Dependencies;
+    std::unordered_map<std::string, ModuleDeps> ModuleDeps;
+    std::string ContextHash;
+  };
+
+  if (Format == ScanningFormat::Make) {
+    MakeDependencyPrinterConsumer Consumer;
+    auto Result =
+        Worker.computeDependencies(Input, CWD, Compilations, Consumer);
+    if (Result)
+      return std::move(Result);
+    std::string Output;
+    Consumer.printDependencies(Output);
+    return Output;
+  } else {
+    FullDependencyPrinterConsumer Consumer;
+    auto Result =
+        Worker.computeDependencies(Input, CWD, Compilations, Consumer);
+    if (Result)
+      return std::move(Result);
+    std::string Output;
+    Consumer.printDependencies(Output, Input);
+    return Output;
+  }
 }
 
 } // end namespace dependencies
Index: clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
+++ clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp
@@ -13,7 +13,8 @@
 using namespace dependencies;
 
 DependencyScanningService::DependencyScanningService(ScanningMode Mode,
+                                                     ScanningFormat Format,
                                                      bool ReuseFileManager,
                                                      bool SkipExcludedPPRanges)
-    : Mode(Mode), ReuseFileManager(ReuseFileManager),
+    : Mode(Mode), Format(Format), ReuseFileManager(ReuseFileManager),
       SkipExcludedPPRanges(SkipExcludedPPRanges) {}
Index: clang/lib/Tooling/DependencyScanning/CMakeLists.txt
===================================================================
--- clang/lib/Tooling/DependencyScanning/CMakeLists.txt
+++ clang/lib/Tooling/DependencyScanning/CMakeLists.txt
@@ -8,6 +8,7 @@
   DependencyScanningService.cpp
   DependencyScanningWorker.cpp
   DependencyScanningTool.cpp
+  ModuleDepCollector.cpp
 
   DEPENDS
   ClangDriverOptions
Index: clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
===================================================================
--- /dev/null
+++ clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -0,0 +1,91 @@
+//===- ModuleDepCollector.h - Callbacks to collect deps ---------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H
+#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H
+
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/Utils.h"
+#include "clang/Lex/HeaderSearch.h"
+#include "clang/Lex/PPCallbacks.h"
+#include "clang/Serialization/ASTReader.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/StringSet.h"
+#include "llvm/Support/raw_ostream.h"
+
+#include <string>
+
+namespace clang {
+namespace tooling {
+namespace dependencies {
+
+class DependencyConsumer;
+
+struct ModuleDeps {
+  std::string ModuleName;
+  std::string ModuleMapFile;
+  std::string ModulePath;
+  std::string ContextHash;
+  llvm::StringSet<> FileDeps;
+  llvm::StringSet<> ModuleDeps;
+  bool MainFile = false;
+};
+
+class ModuleDepCollector;
+
+class ModuleDepCollectorPP final : public PPCallbacks {
+public:
+  ModuleDepCollectorPP(CompilerInstance &I, ModuleDepCollector &MDC)
+      : Instance(I), MDC(MDC) {}
+
+  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
+                          StringRef FileName, bool IsAngled,
+                          CharSourceRange FilenameRange, const FileEntry *File,
+                          StringRef SearchPath, StringRef RelativePath,
+                          const Module *Imported,
+                          SrcMgr::CharacteristicKind FileType) override;
+
+  void EndOfMainFile() override;
+
+private:
+  CompilerInstance &Instance;
+  ModuleDepCollector &MDC;
+  llvm::DenseSet<const Module *> DirectDeps;
+
+  void handleTopLevelModule(const Module *M);
+  void addAllSubmoduleDeps(const Module *M, ModuleDeps &MD);
+  void addModuleDep(const Module *M, ModuleDeps &MD);
+
+  void addDirectDependencies(const Module *Mod);
+};
+
+class ModuleDepCollector final : public DependencyCollector {
+public:
+  ModuleDepCollector(CompilerInstance &I, DependencyConsumer &C);
+
+  void attachToPreprocessor(Preprocessor &PP) override;
+  void attachToASTReader(ASTReader &R) override;
+
+private:
+  friend ModuleDepCollectorPP;
+
+  CompilerInstance &Instance;
+  DependencyConsumer &Consumer;
+  std::string MainFile;
+  std::string ContextHash;
+  std::vector<std::string> MainDeps;
+  std::unordered_map<std::string, ModuleDeps> Deps;
+};
+
+} // end namespace dependencies
+} // end namespace tooling
+} // end namespace clang
+
+#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H
Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
+++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
@@ -15,6 +15,8 @@
 #include "clang/Frontend/PCHContainerOperations.h"
 #include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h"
 #include "clang/Tooling/CompilationDatabase.h"
+#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"
+#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/FileSystem.h"
 #include <string>
@@ -26,7 +28,6 @@
 namespace tooling {
 namespace dependencies {
 
-class DependencyScanningService;
 class DependencyScanningWorkerFilesystem;
 
 class DependencyConsumer {
@@ -36,7 +37,9 @@
   virtual void handleFileDependency(const DependencyOutputOptions &Opts,
                                     StringRef Filename) = 0;
 
-  // FIXME: Add support for reporting modular dependencies.
+  virtual void handleModuleDependency(ModuleDeps MD) = 0;
+
+  virtual void handleContextHash(std::string Hash) = 0;
 };
 
 /// An individual dependency scanning worker that is able to run on its own
@@ -73,6 +76,7 @@
   /// The file manager that is reused accross multiple invocations by this
   /// worker. If null, the file manager will not be reused.
   llvm::IntrusiveRefCntPtr<FileManager> Files;
+  ScanningFormat Format;
 };
 
 } // end namespace dependencies
Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
+++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
@@ -37,6 +37,7 @@
   llvm::Expected<std::string> getDependencyFile(const std::string &Input, StringRef CWD);
 
 private:
+  const ScanningFormat Format;
   DependencyScanningWorker Worker;
   const tooling::CompilationDatabase &Compilations;
 };
Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
+++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h
@@ -30,15 +30,28 @@
   MinimizedSourcePreprocessing
 };
 
+/// The format that is output by the dependency scanner.
+enum class ScanningFormat {
+  /// This is the Makefile compatible dep format.
+  Make,
+
+  /// This outputs the full module dependency graph suitable for use for
+  /// explicitly building modules.
+  Full,
+};
+
 /// The dependency scanning service contains the shared state that is used by
 /// the invidual dependency scanning workers.
 class DependencyScanningService {
 public:
-  DependencyScanningService(ScanningMode Mode, bool ReuseFileManager = true,
+  DependencyScanningService(ScanningMode Mode, ScanningFormat Format,
+                            bool ReuseFileManager = true,
                             bool SkipExcludedPPRanges = true);
 
   ScanningMode getMode() const { return Mode; }
 
+  ScanningFormat getFormat() const { return Format; }
+
   bool canReuseFileManager() const { return ReuseFileManager; }
 
   bool canSkipExcludedPPRanges() const { return SkipExcludedPPRanges; }
@@ -49,6 +62,7 @@
 
 private:
   const ScanningMode Mode;
+  const ScanningFormat Format;
   const bool ReuseFileManager;
   /// Set to true to use the preprocessor optimization that skips excluded PP
   /// ranges by bumping the buffer pointer in the lexer instead of lexing the
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to