<div dir="ltr">I XFAILED the test on Windows in 52194350cfe. The [/\\] regexes might need to match two slashes, and the PREFIX variable ends up having inconsistent slash direction.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Wed, Oct 30, 2019 at 3:27 PM Michael Spencer via cfe-commits <<a href="mailto:cfe-commits@lists.llvm.org">cfe-commits@lists.llvm.org</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><br>
Author: Michael Spencer<br>
Date: 2019-10-30T15:27:27-07:00<br>
New Revision: 33a745e6fe7e81d3793f7831d2832aa0785ef327<br>
<br>
URL: <a href="https://github.com/llvm/llvm-project/commit/33a745e6fe7e81d3793f7831d2832aa0785ef327" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/33a745e6fe7e81d3793f7831d2832aa0785ef327</a><br>
DIFF: <a href="https://github.com/llvm/llvm-project/commit/33a745e6fe7e81d3793f7831d2832aa0785ef327.diff" rel="noreferrer" target="_blank">https://github.com/llvm/llvm-project/commit/33a745e6fe7e81d3793f7831d2832aa0785ef327.diff</a><br>
<br>
LOG: [clang][clang-scan-deps] Add support for extracting full module dependencies.<br>
<br>
This is a recommit of d8a4ef0e685c with the nondeterminism fixed.<br>
<br>
This adds experimental support for extracting a Clang module dependency graph<br>
from a compilation database. The output format is experimental and will change.<br>
It is currently a concatenation of JSON outputs for each compilation. Future<br>
patches will change this to deduplicate modules between compilations.<br>
<br>
Differential Revision: <a href="https://reviews.llvm.org/D69420" rel="noreferrer" target="_blank">https://reviews.llvm.org/D69420</a><br>
<br>
Added: <br>
    clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h<br>
    clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp<br>
    clang/test/ClangScanDeps/modules-full.cpp<br>
<br>
Modified: <br>
    clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h<br>
    clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h<br>
    clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h<br>
    clang/lib/Tooling/DependencyScanning/CMakeLists.txt<br>
    clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp<br>
    clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp<br>
    clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp<br>
    clang/tools/clang-scan-deps/ClangScanDeps.cpp<br>
<br>
Removed: <br>
<br>
<br>
<br>
################################################################################<br>
diff  --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h<br>
index fd8ed80b143c..76edf150dbee 100644<br>
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h<br>
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningService.h<br>
@@ -30,15 +30,30 @@ enum class ScanningMode {<br>
   MinimizedSourcePreprocessing<br>
 };<br>
<br>
+/// The format that is output by the dependency scanner.<br>
+enum class ScanningOutputFormat {<br>
+  /// This is the Makefile compatible dep format. This will include all of the<br>
+  /// deps necessary for an implicit modules build, but won't include any<br>
+  /// intermodule dependency information.<br>
+  Make,<br>
+<br>
+  /// This outputs the full module dependency graph suitable for use for<br>
+  /// explicitly building modules.<br>
+  Full,<br>
+};<br>
+<br>
 /// The dependency scanning service contains the shared state that is used by<br>
 /// the invidual dependency scanning workers.<br>
 class DependencyScanningService {<br>
 public:<br>
-  DependencyScanningService(ScanningMode Mode, bool ReuseFileManager = true,<br>
+  DependencyScanningService(ScanningMode Mode, ScanningOutputFormat Format,<br>
+                            bool ReuseFileManager = true,<br>
                             bool SkipExcludedPPRanges = true);<br>
<br>
   ScanningMode getMode() const { return Mode; }<br>
<br>
+  ScanningOutputFormat getFormat() const { return Format; }<br>
+<br>
   bool canReuseFileManager() const { return ReuseFileManager; }<br>
<br>
   bool canSkipExcludedPPRanges() const { return SkipExcludedPPRanges; }<br>
@@ -49,6 +64,7 @@ class DependencyScanningService {<br>
<br>
 private:<br>
   const ScanningMode Mode;<br>
+  const ScanningOutputFormat Format;<br>
   const bool ReuseFileManager;<br>
   /// Set to true to use the preprocessor optimization that skips excluded PP<br>
   /// ranges by bumping the buffer pointer in the lexer instead of lexing the<br>
<br>
diff  --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h<br>
index c950cbe167cd..78b49e4fa0c5 100644<br>
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h<br>
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h<br>
@@ -40,6 +40,7 @@ class DependencyScanningTool {<br>
                                                 StringRef CWD);<br>
<br>
 private:<br>
+  const ScanningOutputFormat Format;<br>
   DependencyScanningWorker Worker;<br>
   const tooling::CompilationDatabase &Compilations;<br>
 };<br>
<br>
diff  --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h<br>
index 45c9fb4f029d..689119330c41 100644<br>
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h<br>
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h<br>
@@ -15,6 +15,8 @@<br>
 #include "clang/Frontend/PCHContainerOperations.h"<br>
 #include "clang/Lex/PreprocessorExcludedConditionalDirectiveSkipMapping.h"<br>
 #include "clang/Tooling/CompilationDatabase.h"<br>
+#include "clang/Tooling/DependencyScanning/DependencyScanningService.h"<br>
+#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"<br>
 #include "llvm/Support/Error.h"<br>
 #include "llvm/Support/FileSystem.h"<br>
 #include <string><br>
@@ -26,7 +28,6 @@ class DependencyOutputOptions;<br>
 namespace tooling {<br>
 namespace dependencies {<br>
<br>
-class DependencyScanningService;<br>
 class DependencyScanningWorkerFilesystem;<br>
<br>
 class DependencyConsumer {<br>
@@ -36,7 +37,9 @@ class DependencyConsumer {<br>
   virtual void handleFileDependency(const DependencyOutputOptions &Opts,<br>
                                     StringRef Filename) = 0;<br>
<br>
-  // FIXME: Add support for reporting modular dependencies.<br>
+  virtual void handleModuleDependency(ModuleDeps MD) = 0;<br>
+<br>
+  virtual void handleContextHash(std::string Hash) = 0;<br>
 };<br>
<br>
 /// An individual dependency scanning worker that is able to run on its own<br>
@@ -73,6 +76,7 @@ class DependencyScanningWorker {<br>
   /// The file manager that is reused accross multiple invocations by this<br>
   /// worker. If null, the file manager will not be reused.<br>
   llvm::IntrusiveRefCntPtr<FileManager> Files;<br>
+  ScanningOutputFormat Format;<br>
 };<br>
<br>
 } // end namespace dependencies<br>
<br>
diff  --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h<br>
new file mode 100644<br>
index 000000000000..7a9fc276fcaa<br>
--- /dev/null<br>
+++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h<br>
@@ -0,0 +1,94 @@<br>
+//===- ModuleDepCollector.h - Callbacks to collect deps ---------*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#ifndef LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H<br>
+#define LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H<br>
+<br>
+#include "clang/Basic/LLVM.h"<br>
+#include "clang/Basic/SourceManager.h"<br>
+#include "clang/Frontend/Utils.h"<br>
+#include "clang/Lex/HeaderSearch.h"<br>
+#include "clang/Lex/PPCallbacks.h"<br>
+#include "clang/Serialization/ASTReader.h"<br>
+#include "llvm/ADT/DenseMap.h"<br>
+#include "llvm/ADT/StringSet.h"<br>
+#include "llvm/Support/raw_ostream.h"<br>
+<br>
+#include <string><br>
+<br>
+namespace clang {<br>
+namespace tooling {<br>
+namespace dependencies {<br>
+<br>
+class DependencyConsumer;<br>
+<br>
+struct ModuleDeps {<br>
+  std::string ModuleName;<br>
+  std::string ClangModuleMapFile;<br>
+  std::string ModulePCMPath;<br>
+  std::string ContextHash;<br>
+  llvm::StringSet<> FileDeps;<br>
+  llvm::StringSet<> ClangModuleDeps;<br>
+  bool ImportedByMainFile = false;<br>
+};<br>
+<br>
+class ModuleDepCollector;<br>
+<br>
+class ModuleDepCollectorPP final : public PPCallbacks {<br>
+public:<br>
+  ModuleDepCollectorPP(CompilerInstance &I, ModuleDepCollector &MDC)<br>
+      : Instance(I), MDC(MDC) {}<br>
+<br>
+  void FileChanged(SourceLocation Loc, FileChangeReason Reason,<br>
+                   SrcMgr::CharacteristicKind FileType,<br>
+                   FileID PrevFID) override;<br>
+  void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,<br>
+                          StringRef FileName, bool IsAngled,<br>
+                          CharSourceRange FilenameRange, const FileEntry *File,<br>
+                          StringRef SearchPath, StringRef RelativePath,<br>
+                          const Module *Imported,<br>
+                          SrcMgr::CharacteristicKind FileType) override;<br>
+<br>
+  void EndOfMainFile() override;<br>
+<br>
+private:<br>
+  CompilerInstance &Instance;<br>
+  ModuleDepCollector &MDC;<br>
+  llvm::DenseSet<const Module *> DirectDeps;<br>
+<br>
+  void handleTopLevelModule(const Module *M);<br>
+  void addAllSubmoduleDeps(const Module *M, ModuleDeps &MD);<br>
+  void addModuleDep(const Module *M, ModuleDeps &MD);<br>
+<br>
+  void addDirectDependencies(const Module *Mod);<br>
+};<br>
+<br>
+class ModuleDepCollector final : public DependencyCollector {<br>
+public:<br>
+  ModuleDepCollector(CompilerInstance &I, DependencyConsumer &C);<br>
+<br>
+  void attachToPreprocessor(Preprocessor &PP) override;<br>
+  void attachToASTReader(ASTReader &R) override;<br>
+<br>
+private:<br>
+  friend ModuleDepCollectorPP;<br>
+<br>
+  CompilerInstance &Instance;<br>
+  DependencyConsumer &Consumer;<br>
+  std::string MainFile;<br>
+  std::string ContextHash;<br>
+  std::vector<std::string> MainDeps;<br>
+  std::unordered_map<std::string, ModuleDeps> Deps;<br>
+};<br>
+<br>
+} // end namespace dependencies<br>
+} // end namespace tooling<br>
+} // end namespace clang<br>
+<br>
+#endif // LLVM_CLANG_TOOLING_DEPENDENCY_SCANNING_MODULE_DEP_COLLECTOR_H<br>
<br>
diff  --git a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt<br>
index 05e1aa54f8d4..c6fe207ab2f2 100644<br>
--- a/clang/lib/Tooling/DependencyScanning/CMakeLists.txt<br>
+++ b/clang/lib/Tooling/DependencyScanning/CMakeLists.txt<br>
@@ -8,6 +8,7 @@ add_clang_library(clangDependencyScanning<br>
   DependencyScanningService.cpp<br>
   DependencyScanningWorker.cpp<br>
   DependencyScanningTool.cpp<br>
+  ModuleDepCollector.cpp<br>
<br>
   DEPENDS<br>
   ClangDriverOptions<br>
<br>
diff  --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp<br>
index e5cebe381000..93bb0cde439d 100644<br>
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp<br>
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningService.cpp<br>
@@ -12,8 +12,8 @@ using namespace clang;<br>
 using namespace tooling;<br>
 using namespace dependencies;<br>
<br>
-DependencyScanningService::DependencyScanningService(ScanningMode Mode,<br>
-                                                     bool ReuseFileManager,<br>
-                                                     bool SkipExcludedPPRanges)<br>
-    : Mode(Mode), ReuseFileManager(ReuseFileManager),<br>
+DependencyScanningService::DependencyScanningService(<br>
+    ScanningMode Mode, ScanningOutputFormat Format, bool ReuseFileManager,<br>
+    bool SkipExcludedPPRanges)<br>
+    : Mode(Mode), Format(Format), ReuseFileManager(ReuseFileManager),<br>
       SkipExcludedPPRanges(SkipExcludedPPRanges) {}<br>
<br>
diff  --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp<br>
index d2af1a9d110c..bffd7c338124 100644<br>
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp<br>
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp<br>
@@ -8,6 +8,15 @@<br>
<br>
 #include "clang/Tooling/DependencyScanning/DependencyScanningTool.h"<br>
 #include "clang/Frontend/Utils.h"<br>
+#include "llvm/Support/JSON.h"<br>
+<br>
+static llvm::json::Array toJSONSorted(const llvm::StringSet<> &Set) {<br>
+  std::vector<llvm::StringRef> Strings;<br>
+  for (auto &&I : Set)<br>
+    Strings.push_back(I.getKey());<br>
+  std::sort(Strings.begin(), Strings.end());<br>
+  return llvm::json::Array(Strings);<br>
+}<br>
<br>
 namespace clang{<br>
 namespace tooling{<br>
@@ -16,13 +25,14 @@ namespace dependencies{<br>
 DependencyScanningTool::DependencyScanningTool(<br>
     DependencyScanningService &Service,<br>
     const tooling::CompilationDatabase &Compilations)<br>
-    : Worker(Service), Compilations(Compilations) {}<br>
+    : Format(Service.getFormat()), Worker(Service), Compilations(Compilations) {<br>
+}<br>
<br>
 llvm::Expected<std::string><br>
 DependencyScanningTool::getDependencyFile(const std::string &Input,<br>
                                           StringRef CWD) {<br>
   /// Prints out all of the gathered dependencies into a string.<br>
-  class DependencyPrinterConsumer : public DependencyConsumer {<br>
+  class MakeDependencyPrinterConsumer : public DependencyConsumer {<br>
   public:<br>
     void handleFileDependency(const DependencyOutputOptions &Opts,<br>
                               StringRef File) override {<br>
@@ -31,6 +41,14 @@ DependencyScanningTool::getDependencyFile(const std::string &Input,<br>
       Dependencies.push_back(File);<br>
     }<br>
<br>
+    void handleModuleDependency(ModuleDeps MD) override {<br>
+      // These are ignored for the make format as it can't support the full<br>
+      // set of deps, and handleFileDependency handles enough for implicitly<br>
+      // built modules to work.<br>
+    }<br>
+<br>
+    void handleContextHash(std::string Hash) override {}<br>
+<br>
     void printDependencies(std::string &S) {<br>
       if (!Opts)<br>
         return;<br>
@@ -59,14 +77,88 @@ DependencyScanningTool::getDependencyFile(const std::string &Input,<br>
     std::vector<std::string> Dependencies;<br>
   };<br>
<br>
-  DependencyPrinterConsumer Consumer;<br>
-  auto Result =<br>
-      Worker.computeDependencies(Input, CWD, Compilations, Consumer);<br>
-  if (Result)<br>
-    return std::move(Result);<br>
-  std::string Output;<br>
-  Consumer.printDependencies(Output);<br>
-  return Output;<br>
+  class FullDependencyPrinterConsumer : public DependencyConsumer {<br>
+  public:<br>
+    void handleFileDependency(const DependencyOutputOptions &Opts,<br>
+                              StringRef File) override {<br>
+      Dependencies.push_back(File);<br>
+    }<br>
+<br>
+    void handleModuleDependency(ModuleDeps MD) override {<br>
+      ModuleDeps[MD.ContextHash + MD.ModuleName] = std::move(MD);<br>
+    }<br>
+<br>
+    void handleContextHash(std::string Hash) override {<br>
+      ContextHash = std::move(Hash);<br>
+    }<br>
+<br>
+    void printDependencies(std::string &S, StringRef MainFile) {<br>
+      // Sort the modules by name to get a deterministic order.<br>
+      std::vector<StringRef> Modules;<br>
+      for (auto &&Dep : ModuleDeps)<br>
+        Modules.push_back(Dep.first);<br>
+      std::sort(Modules.begin(), Modules.end());<br>
+<br>
+      llvm::raw_string_ostream OS(S);<br>
+<br>
+      using namespace llvm::json;<br>
+<br>
+      Array Imports;<br>
+      for (auto &&ModName : Modules) {<br>
+        auto &MD = ModuleDeps[ModName];<br>
+        if (MD.ImportedByMainFile)<br>
+          Imports.push_back(MD.ModuleName);<br>
+      }<br>
+<br>
+      Array Mods;<br>
+      for (auto &&ModName : Modules) {<br>
+        auto &MD = ModuleDeps[ModName];<br>
+        Object Mod{<br>
+            {"name", MD.ModuleName},<br>
+            {"file-deps", toJSONSorted(MD.FileDeps)},<br>
+            {"clang-module-deps", toJSONSorted(MD.ClangModuleDeps)},<br>
+            {"clang-modulemap-file", MD.ClangModuleMapFile},<br>
+        };<br>
+        Mods.push_back(std::move(Mod));<br>
+      }<br>
+<br>
+      Object O{<br>
+          {"input-file", MainFile},<br>
+          {"clang-context-hash", ContextHash},<br>
+          {"file-deps", Dependencies},<br>
+          {"clang-module-deps", std::move(Imports)},<br>
+          {"clang-modules", std::move(Mods)},<br>
+      };<br>
+<br>
+      S = llvm::formatv("{0:2},\n", Value(std::move(O))).str();<br>
+      return;<br>
+    }<br>
+<br>
+  private:<br>
+    std::vector<std::string> Dependencies;<br>
+    std::unordered_map<std::string, ModuleDeps> ModuleDeps;<br>
+    std::string ContextHash;<br>
+  };<br>
+<br>
+  if (Format == ScanningOutputFormat::Make) {<br>
+    MakeDependencyPrinterConsumer Consumer;<br>
+    auto Result =<br>
+        Worker.computeDependencies(Input, CWD, Compilations, Consumer);<br>
+    if (Result)<br>
+      return std::move(Result);<br>
+    std::string Output;<br>
+    Consumer.printDependencies(Output);<br>
+    return Output;<br>
+  } else {<br>
+    FullDependencyPrinterConsumer Consumer;<br>
+    auto Result =<br>
+        Worker.computeDependencies(Input, CWD, Compilations, Consumer);<br>
+    if (Result)<br>
+      return std::move(Result);<br>
+    std::string Output;<br>
+    Consumer.printDependencies(Output, Input);<br>
+    return Output;<br>
+  }<br>
 }<br>
<br>
 } // end namespace dependencies<br>
<br>
diff  --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp<br>
index f382c202f8c2..edf2cf8bd70f 100644<br>
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp<br>
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp<br>
@@ -14,6 +14,7 @@<br>
 #include "clang/Frontend/Utils.h"<br>
 #include "clang/Lex/PreprocessorOptions.h"<br>
 #include "clang/Tooling/DependencyScanning/DependencyScanningService.h"<br>
+#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"<br>
 #include "clang/Tooling/Tooling.h"<br>
<br>
 using namespace clang;<br>
@@ -72,9 +73,11 @@ class DependencyScanningAction : public tooling::ToolAction {<br>
   DependencyScanningAction(<br>
       StringRef WorkingDirectory, DependencyConsumer &Consumer,<br>
       llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS,<br>
-      ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings)<br>
+      ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings,<br>
+      ScanningOutputFormat Format)<br>
       : WorkingDirectory(WorkingDirectory), Consumer(Consumer),<br>
-        DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings) {}<br>
+        DepFS(std::move(DepFS)), PPSkipMappings(PPSkipMappings),<br>
+        Format(Format) {}<br>
<br>
   bool runInvocation(std::shared_ptr<CompilerInvocation> Invocation,<br>
                      FileManager *FileMgr,<br>
@@ -131,9 +134,20 @@ class DependencyScanningAction : public tooling::ToolAction {<br>
     // We need at least one -MT equivalent for the generator to work.<br>
     if (Opts->Targets.empty())<br>
       Opts->Targets = {"clang-scan-deps dependency"};<br>
-    Compiler.addDependencyCollector(<br>
-        std::make_shared<DependencyConsumerForwarder>(std::move(Opts),<br>
-                                                      Consumer));<br>
+<br>
+    switch (Format) {<br>
+    case ScanningOutputFormat::Make:<br>
+      Compiler.addDependencyCollector(<br>
+          std::make_shared<DependencyConsumerForwarder>(std::move(Opts),<br>
+                                                        Consumer));<br>
+      break;<br>
+    case ScanningOutputFormat::Full:<br>
+      Compiler.addDependencyCollector(<br>
+          std::make_shared<ModuleDepCollector>(Compiler, Consumer));<br>
+      break;<br>
+    }<br>
+<br>
+    Consumer.handleContextHash(Compiler.getInvocation().getModuleHash());<br>
<br>
     auto Action = std::make_unique<PreprocessOnlyAction>();<br>
     const bool Result = Compiler.ExecuteAction(*Action);<br>
@@ -147,12 +161,14 @@ class DependencyScanningAction : public tooling::ToolAction {<br>
   DependencyConsumer &Consumer;<br>
   llvm::IntrusiveRefCntPtr<DependencyScanningWorkerFilesystem> DepFS;<br>
   ExcludedPreprocessorDirectiveSkipMapping *PPSkipMappings;<br>
+  ScanningOutputFormat Format;<br>
 };<br>
<br>
 } // end anonymous namespace<br>
<br>
 DependencyScanningWorker::DependencyScanningWorker(<br>
-    DependencyScanningService &Service) {<br>
+    DependencyScanningService &Service)<br>
+    : Format(Service.getFormat()) {<br>
   DiagOpts = new DiagnosticOptions();<br>
   PCHContainerOps = std::make_shared<PCHContainerOperations>();<br>
   RealFS = new ProxyFileSystemWithoutChdir(llvm::vfs::getRealFileSystem());<br>
@@ -195,7 +211,7 @@ llvm::Error DependencyScanningWorker::computeDependencies(<br>
     Tool.setPrintErrorMessage(false);<br>
     Tool.setDiagnosticConsumer(&DC);<br>
     DependencyScanningAction Action(WorkingDirectory, Consumer, DepFS,<br>
-                                    PPSkipMappings.get());<br>
+                                    PPSkipMappings.get(), Format);<br>
     return !Tool.run(&Action);<br>
   });<br>
 }<br>
<br>
diff  --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp<br>
new file mode 100644<br>
index 000000000000..7f20ec7056c6<br>
--- /dev/null<br>
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp<br>
@@ -0,0 +1,136 @@<br>
+//===- ModuleDepCollector.cpp - Callbacks to collect deps -------*- C++ -*-===//<br>
+//<br>
+//                     The LLVM Compiler Infrastructure<br>
+//<br>
+// This file is distributed under the University of Illinois Open Source<br>
+// License. See LICENSE.TXT for details.<br>
+//<br>
+//===----------------------------------------------------------------------===//<br>
+<br>
+#include "clang/Tooling/DependencyScanning/ModuleDepCollector.h"<br>
+<br>
+#include "clang/Frontend/CompilerInstance.h"<br>
+#include "clang/Lex/Preprocessor.h"<br>
+#include "clang/Tooling/DependencyScanning/DependencyScanningWorker.h"<br>
+<br>
+using namespace clang;<br>
+using namespace tooling;<br>
+using namespace dependencies;<br>
+<br>
+void ModuleDepCollectorPP::FileChanged(SourceLocation Loc,<br>
+                                       FileChangeReason Reason,<br>
+                                       SrcMgr::CharacteristicKind FileType,<br>
+                                       FileID PrevFID) {<br>
+  if (Reason != PPCallbacks::EnterFile)<br>
+    return;<br>
+<br>
+  SourceManager &SM = Instance.getSourceManager();<br>
+<br>
+  // Dependency generation really does want to go all the way to the<br>
+  // file entry for a source location to find out what is depended on.<br>
+  // We do not want #line markers to affect dependency generation!<br>
+  Optional<FileEntryRef> File =<br>
+      SM.getFileEntryRefForID(SM.getFileID(SM.getExpansionLoc(Loc)));<br>
+  if (!File)<br>
+    return;<br>
+<br>
+  StringRef FileName =<br>
+      llvm::sys::path::remove_leading_dotslash(File->getName());<br>
+<br>
+  MDC.MainDeps.push_back(FileName);<br>
+}<br>
+<br>
+void ModuleDepCollectorPP::InclusionDirective(<br>
+    SourceLocation HashLoc, const Token &IncludeTok, StringRef FileName,<br>
+    bool IsAngled, CharSourceRange FilenameRange, const FileEntry *File,<br>
+    StringRef SearchPath, StringRef RelativePath, const Module *Imported,<br>
+    SrcMgr::CharacteristicKind FileType) {<br>
+  if (!File && !Imported) {<br>
+    // This is a non-modular include that HeaderSearch failed to find. Add it<br>
+    // here as `FileChanged` will never see it.<br>
+    MDC.MainDeps.push_back(FileName);<br>
+  }<br>
+<br>
+  if (!Imported)<br>
+    return;<br>
+<br>
+  MDC.Deps[MDC.ContextHash + Imported->getTopLevelModule()->getFullModuleName()]<br>
+      .ImportedByMainFile = true;<br>
+  DirectDeps.insert(Imported->getTopLevelModule());<br>
+}<br>
+<br>
+void ModuleDepCollectorPP::EndOfMainFile() {<br>
+  FileID MainFileID = Instance.getSourceManager().getMainFileID();<br>
+  MDC.MainFile =<br>
+      Instance.getSourceManager().getFileEntryForID(MainFileID)->getName();<br>
+<br>
+  for (const Module *M : DirectDeps) {<br>
+    handleTopLevelModule(M);<br>
+  }<br>
+<br>
+  for (auto &&I : MDC.Deps)<br>
+    MDC.Consumer.handleModuleDependency(I.second);<br>
+<br>
+  DependencyOutputOptions Opts;<br>
+  for (auto &&I : MDC.MainDeps)<br>
+    MDC.Consumer.handleFileDependency(Opts, I);<br>
+}<br>
+<br>
+void ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {<br>
+  assert(M == M->getTopLevelModule() && "Expected top level module!");<br>
+<br>
+  auto ModI = MDC.Deps.insert(<br>
+      std::make_pair(MDC.ContextHash + M->getFullModuleName(), ModuleDeps{}));<br>
+<br>
+  if (!ModI.first->second.ModuleName.empty())<br>
+    return;<br>
+<br>
+  ModuleDeps &MD = ModI.first->second;<br>
+<br>
+  const FileEntry *ModuleMap = Instance.getPreprocessor()<br>
+                                   .getHeaderSearchInfo()<br>
+                                   .getModuleMap()<br>
+                                   .getContainingModuleMapFile(M);<br>
+<br>
+  MD.ClangModuleMapFile = ModuleMap ? ModuleMap->getName() : "";<br>
+  MD.ModuleName = M->getFullModuleName();<br>
+  MD.ModulePCMPath = M->getASTFile()->getName();<br>
+  MD.ContextHash = MDC.ContextHash;<br>
+  serialization::ModuleFile *MF =<br>
+      MDC.Instance.getModuleManager()->getModuleManager().lookup(<br>
+          M->getASTFile());<br>
+  MDC.Instance.getModuleManager()->visitInputFiles(<br>
+      *MF, true, true, [&](const serialization::InputFile &IF, bool isSystem) {<br>
+        MD.FileDeps.insert(IF.getFile()->getName());<br>
+      });<br>
+<br>
+  addAllSubmoduleDeps(M, MD);<br>
+}<br>
+<br>
+void ModuleDepCollectorPP::addAllSubmoduleDeps(const Module *M,<br>
+                                               ModuleDeps &MD) {<br>
+  addModuleDep(M, MD);<br>
+<br>
+  for (const Module *SubM : M->submodules())<br>
+    addAllSubmoduleDeps(SubM, MD);<br>
+}<br>
+<br>
+void ModuleDepCollectorPP::addModuleDep(const Module *M, ModuleDeps &MD) {<br>
+  for (const Module *Import : M->Imports) {<br>
+    if (Import->getTopLevelModule() != M->getTopLevelModule()) {<br>
+      MD.ClangModuleDeps.insert(Import->getTopLevelModuleName());<br>
+      handleTopLevelModule(Import->getTopLevelModule());<br>
+    }<br>
+  }<br>
+}<br>
+<br>
+ModuleDepCollector::ModuleDepCollector(CompilerInstance &I,<br>
+                                       DependencyConsumer &C)<br>
+    : Instance(I), Consumer(C), ContextHash(I.getInvocation().getModuleHash()) {<br>
+}<br>
+<br>
+void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {<br>
+  PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this));<br>
+}<br>
+<br>
+void ModuleDepCollector::attachToASTReader(ASTReader &R) {}<br>
<br>
diff  --git a/clang/test/ClangScanDeps/modules-full.cpp b/clang/test/ClangScanDeps/modules-full.cpp<br>
new file mode 100644<br>
index 000000000000..f8bff06d8f74<br>
--- /dev/null<br>
+++ b/clang/test/ClangScanDeps/modules-full.cpp<br>
@@ -0,0 +1,74 @@<br>
+// RUN: rm -rf %t.dir<br>
+// RUN: rm -rf %t.cdb<br>
+// RUN: rm -rf %t.module-cache<br>
+// RUN: mkdir -p %t.dir<br>
+// RUN: cp %s %t.dir/modules_cdb_input.cpp<br>
+// RUN: cp %s %t.dir/modules_cdb_input2.cpp<br>
+// RUN: mkdir %t.dir/Inputs<br>
+// RUN: cp %S/Inputs/header.h %t.dir/Inputs/header.h<br>
+// RUN: cp %S/Inputs/header2.h %t.dir/Inputs/header2.h<br>
+// RUN: cp %S/Inputs/module.modulemap %t.dir/Inputs/module.modulemap<br>
+// RUN: sed -e "s|DIR|%/t.dir|g" %S/Inputs/modules_cdb.json > %t.cdb<br>
+//<br>
+// RUN: echo %t.dir > %t.result<br>
+// RUN: clang-scan-deps -compilation-database %t.cdb -j 1 \<br>
+// RUN:   -mode preprocess-minimized-sources -format experimental-full >> %t.result<br>
+// RUN: cat %t.result | FileCheck --check-prefixes=CHECK %s<br>
+<br>
+#include "header.h"<br>
+<br>
+// CHECK: [[PREFIX:(.*[/\\])+[a-zA-Z0-9.-]+]]<br>
+// CHECK-NEXT:     {<br>
+// CHECK-NEXT:  "clang-context-hash": "[[CONTEXT_HASH:[A-Z0-9]+]]",<br>
+// CHECK-NEXT:  "clang-module-deps": [<br>
+// CHECK-NEXT:    "header1"<br>
+// CHECK-NEXT:  ],<br>
+// CHECK-NEXT:  "clang-modules": [<br>
+// CHECK-NEXT:    {<br>
+// CHECK-NEXT:      "clang-module-deps": [<br>
+// CHECK-NEXT:        "header2"<br>
+// CHECK-NEXT:      ],<br>
+// CHECK-NEXT:      "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",<br>
+// CHECK-NEXT:      "file-deps": [<br>
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header.h",<br>
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap"<br>
+// CHECK-NEXT:      ],<br>
+// CHECK-NEXT:      "name": "header1"<br>
+// CHECK-NEXT:    },<br>
+// CHECK-NEXT:    {<br>
+// CHECK-NEXT:      "clang-module-deps": [],<br>
+// CHECK-NEXT:      "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",<br>
+// CHECK-NEXT:      "file-deps": [<br>
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header2.h",<br>
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap"<br>
+// CHECK-NEXT:      ],<br>
+// CHECK-NEXT:      "name": "header2"<br>
+// CHECK-NEXT:    }<br>
+// CHECK-NEXT:  ],<br>
+// CHECK-NEXT:  "file-deps": [<br>
+// CHECK-NEXT:    "[[PREFIX]]{{[/\\]}}modules_cdb_input2.cpp"<br>
+// CHECK-NEXT:  ],<br>
+// CHECK-NEXT:  "input-file": "[[PREFIX]]{{[/\\]}}modules_cdb_input2.cpp"<br>
+// CHECK-NEXT:},<br>
+// CHECK-NEXT:{<br>
+// CHECK-NOT:   "clang-context-hash": "[[CONTEXT_HASH]]",<br>
+// CHECK-NEXT:  "clang-context-hash": "{{[A-Z0-9]+}}",<br>
+// CHECK-NEXT:  "clang-module-deps": [<br>
+// CHECK-NEXT:    "header1"<br>
+// CHECK-NEXT:  ],<br>
+// CHECK-NEXT:  "clang-modules": [<br>
+// CHECK-NEXT:    {<br>
+// CHECK-NEXT:      "clang-module-deps": [],<br>
+// CHECK-NEXT:      "clang-modulemap-file": "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap",<br>
+// CHECK-NEXT:      "file-deps": [<br>
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}header.h",<br>
+// CHECK-NEXT:        "[[PREFIX]]{{[/\\]}}Inputs{{[/\\]}}module.modulemap"<br>
+// CHECK-NEXT:      ],<br>
+// CHECK-NEXT:      "name": "header1"<br>
+// CHECK-NEXT:    }<br>
+// CHECK-NEXT:  ],<br>
+// CHECK-NEXT:  "file-deps": [<br>
+// CHECK-NEXT:    "[[PREFIX]]{{[/\\]}}modules_cdb_input.cpp"<br>
+// CHECK-NEXT:  ],<br>
+// CHECK-NEXT:  "input-file": "[[PREFIX]]{{[/\\]}}modules_cdb_input.cpp"<br>
+// CHECK-NEXT:},<br>
<br>
diff  --git a/clang/tools/clang-scan-deps/ClangScanDeps.cpp b/clang/tools/clang-scan-deps/ClangScanDeps.cpp<br>
index d57983ed1664..a6abb4a9600b 100644<br>
--- a/clang/tools/clang-scan-deps/ClangScanDeps.cpp<br>
+++ b/clang/tools/clang-scan-deps/ClangScanDeps.cpp<br>
@@ -59,6 +59,17 @@ static llvm::cl::opt<ScanningMode> ScanMode(<br>
     llvm::cl::init(ScanningMode::MinimizedSourcePreprocessing),<br>
     llvm::cl::cat(DependencyScannerCategory));<br>
<br>
+static llvm::cl::opt<ScanningOutputFormat> Format(<br>
+    "format", llvm::cl::desc("The output format for the dependencies"),<br>
+    llvm::cl::values(clEnumValN(ScanningOutputFormat::Make, "make",<br>
+                                "Makefile compatible dep file"),<br>
+                     clEnumValN(ScanningOutputFormat::Full, "experimental-full",<br>
+                                "Full dependency graph suitable"<br>
+                                " for explicitly building modules. This format "<br>
+                                "is experimental and will change.")),<br>
+    llvm::cl::init(ScanningOutputFormat::Make),<br>
+    llvm::cl::cat(DependencyScannerCategory));<br>
+<br>
 llvm::cl::opt<unsigned><br>
     NumThreads("j", llvm::cl::Optional,<br>
                llvm::cl::desc("Number of worker threads to use (default: use "<br>
@@ -200,7 +211,7 @@ int main(int argc, const char **argv) {<br>
   // Print out the dependency results to STDOUT by default.<br>
   SharedStream DependencyOS(llvm::outs());<br>
<br>
-  DependencyScanningService Service(ScanMode, ReuseFileManager,<br>
+  DependencyScanningService Service(ScanMode, Format, ReuseFileManager,<br>
                                     SkipExcludedPPRanges);<br>
 #if LLVM_ENABLE_THREADS<br>
   unsigned NumWorkers =<br>
<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@lists.llvm.org" target="_blank">cfe-commits@lists.llvm.org</a><br>
<a href="https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits" rel="noreferrer" target="_blank">https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits</a><br>
</blockquote></div>