This revision was landed with ongoing or failed builds.
This revision was automatically updated to reflect the committed changes.
Closed by commit rG4629554f0b66: [clang][deps] Handle modular dependencies 
present in PCH (authored by jansvoboda11).

Changed prior to commit:
  https://reviews.llvm.org/D103526?vs=350247&id=351806#toc

Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D103526/new/

https://reviews.llvm.org/D103526

Files:
  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/DependencyScanningTool.cpp
  clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
  clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
  clang/test/ClangScanDeps/Inputs/modules-pch/cdb_pch.json
  clang/test/ClangScanDeps/Inputs/modules-pch/cdb_tu_with_common.json
  clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_1.h
  clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_2.h
  clang/test/ClangScanDeps/Inputs/modules-pch/mod_pch.h
  clang/test/ClangScanDeps/Inputs/modules-pch/mod_tu_with_common.h
  clang/test/ClangScanDeps/Inputs/modules-pch/module.modulemap
  clang/test/ClangScanDeps/Inputs/modules-pch/pch.h
  clang/test/ClangScanDeps/Inputs/modules-pch/tu_with_common.c
  clang/test/ClangScanDeps/modules-pch.c

Index: clang/test/ClangScanDeps/modules-pch.c
===================================================================
--- clang/test/ClangScanDeps/modules-pch.c
+++ clang/test/ClangScanDeps/modules-pch.c
@@ -1,10 +1,124 @@
 // RUN: rm -rf %t && mkdir %t
 // RUN: cp %S/Inputs/modules-pch/* %t
 
+// Scan dependencies of the PCH:
+//
+// RUN: sed "s|DIR|%/t|g" %S/Inputs/modules-pch/cdb_pch.json > %t/cdb.json
+// RUN: echo -%t > %t/result_pch.json
+// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full \
+// RUN:   -generate-modules-path-args -module-files-dir %t/build -mode preprocess >> %t/result_pch.json
+// RUN: cat %t/result_pch.json | sed 's:\\\\\?:/:g' | FileCheck %s -check-prefix=CHECK-PCH
+//
+// CHECK-PCH:      -[[PREFIX:.*]]
+// CHECK-PCH-NEXT: {
+// CHECK-PCH-NEXT:   "modules": [
+// CHECK-PCH-NEXT:     {
+// CHECK-PCH-NEXT:       "clang-module-deps": [],
+// CHECK-PCH-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-PCH-NEXT:       "command-line": [
+// CHECK-PCH-NEXT:         "-cc1"
+// CHECK-PCH:              "-emit-module"
+// CHECK-PCH:              "-fmodules"
+// CHECK-PCH:              "-fmodule-name=ModCommon1"
+// CHECK-PCH:              "-fno-implicit-modules"
+// CHECK-PCH:            ],
+// CHECK-PCH-NEXT:       "context-hash": "[[HASH_MOD_COMMON_1:.*]]",
+// CHECK-PCH-NEXT:       "file-deps": [
+// CHECK-PCH-NEXT:         "[[PREFIX]]/mod_common_1.h",
+// CHECK-PCH-NEXT:         "[[PREFIX]]/module.modulemap"
+// CHECK-PCH-NEXT:       ],
+// CHECK-PCH-NEXT:       "name": "ModCommon1"
+// CHECK-PCH-NEXT:     },
+// CHECK-PCH-NEXT:     {
+// CHECK-PCH-NEXT:       "clang-module-deps": [],
+// CHECK-PCH-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-PCH-NEXT:       "command-line": [
+// CHECK-PCH-NEXT:         "-cc1"
+// CHECK-PCH:              "-emit-module"
+// CHECK-PCH:              "-fmodules"
+// CHECK-PCH:              "-fmodule-name=ModCommon2"
+// CHECK-PCH:              "-fno-implicit-modules"
+// CHECK-PCH:            ],
+// CHECK-PCH-NEXT:       "context-hash": "[[HASH_MOD_COMMON_2:.*]]",
+// CHECK-PCH-NEXT:       "file-deps": [
+// CHECK-PCH-NEXT:         "[[PREFIX]]/mod_common_2.h",
+// CHECK-PCH-NEXT:         "[[PREFIX]]/module.modulemap"
+// CHECK-PCH-NEXT:       ],
+// CHECK-PCH-NEXT:       "name": "ModCommon2"
+// CHECK-PCH-NEXT:     },
+// CHECK-PCH-NEXT:     {
+// CHECK-PCH-NEXT:       "clang-module-deps": [
+// CHECK-PCH-NEXT:         {
+// CHECK-PCH-NEXT:           "context-hash": "[[HASH_MOD_COMMON_2]]",
+// CHECK-PCH-NEXT:           "module-name": "ModCommon2"
+// CHECK-PCH-NEXT:         }
+// CHECK-PCH-NEXT:       ],
+// CHECK-PCH-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-PCH-NEXT:       "command-line": [
+// CHECK-PCH-NEXT:         "-cc1"
+// CHECK-PCH:              "-fmodule-map-file=[[PREFIX]]/module.modulemap"
+// CHECK-PCH:              "-emit-module"
+// CHECK-PCH:              "-fmodule-file=[[PREFIX]]/build/[[HASH_MOD_COMMON_2]]/ModCommon2-{{.*}}.pcm"
+// CHECK-PCH:              "-fmodules"
+// CHECK-PCH:              "-fmodule-name=ModPCH"
+// CHECK-PCH:              "-fno-implicit-modules"
+// CHECK-PCH:            ],
+// CHECK-PCH-NEXT:       "context-hash": "[[HASH_MOD_PCH:.*]]",
+// CHECK-PCH-NEXT:       "file-deps": [
+// CHECK-PCH-NEXT:         "[[PREFIX]]/mod_pch.h",
+// CHECK-PCH-NEXT:         "[[PREFIX]]/module.modulemap"
+// CHECK-PCH-NEXT:       ],
+// CHECK-PCH-NEXT:       "name": "ModPCH"
+// CHECK-PCH-NEXT:     }
+// CHECK-PCH-NEXT:   ],
+// CHECK-PCH-NEXT:   "translation-units": [
+// CHECK-PCH-NEXT:     {
+// CHECK-PCH-NEXT:       "clang-context-hash": "[[HASH_PCH:.*]]",
+// CHECK-PCH-NEXT:       "clang-module-deps": [
+// CHECK-PCH-NEXT:         {
+// CHECK-PCH-NEXT:           "context-hash": "[[HASH_MOD_COMMON_1]]",
+// CHECK-PCH-NEXT:           "module-name": "ModCommon1"
+// CHECK-PCH-NEXT:         },
+// CHECK-PCH-NEXT:         {
+// CHECK-PCH-NEXT:           "context-hash": "[[HASH_MOD_PCH]]",
+// CHECK-PCH-NEXT:           "module-name": "ModPCH"
+// CHECK-PCH-NEXT:         }
+// CHECK-PCH-NEXT:       ],
+// CHECK-PCH-NEXT:       "command-line": [
+// CHECK-PCH-NEXT:         "-fno-implicit-modules",
+// CHECK-PCH-NEXT:         "-fno-implicit-module-maps",
+// CHECK-PCH-DAG:          "-fmodule-file=[[PREFIX]]/build/[[HASH_MOD_COMMON_1]]/ModCommon1-{{.*}}.pcm",
+// CHECK-PCH-DAG:          "-fmodule-file=[[PREFIX]]/build/[[HASH_MOD_COMMON_2]]/ModCommon2-{{.*}}.pcm",
+// CHECK-PCH-DAG:          "-fmodule-file=[[PREFIX]]/build/[[HASH_MOD_PCH]]/ModPCH-{{.*}}.pcm",
+// CHECK-PCH-NEXT:         "-fmodule-map-file=[[PREFIX]]/module.modulemap",
+// CHECK-PCH-NEXT:         "-fmodule-map-file=[[PREFIX]]/module.modulemap",
+// CHECK-PCH-NEXT:         "-fmodule-map-file=[[PREFIX]]/module.modulemap"
+// CHECK-PCH-NEXT:       ],
+// CHECK-PCH-NEXT:       "file-deps": [
+// CHECK-PCH-NEXT:         "[[PREFIX]]/pch.h"
+// CHECK-PCH-NEXT:       ],
+// CHECK-PCH-NEXT:       "input-file": "[[PREFIX]]/pch.h"
+// CHECK-PCH-NEXT:     }
+// CHECK-PCH-NEXT:   ]
+// CHECK-PCH-NEXT: }
+
 // Explicitly build the PCH:
 //
+// RUN: tail -n +2 %t/result_pch.json > %t/result_pch_stripped.json
+// RUN: %python %S/../../utils/module-deps-to-rsp.py %t/result_pch_stripped.json \
+// RUN:   --module-name=ModCommon1 > %t/mod_common_1.cc1.rsp
+// RUN: %python %S/../../utils/module-deps-to-rsp.py %t/result_pch_stripped.json \
+// RUN:   --module-name=ModCommon2 > %t/mod_common_2.cc1.rsp
+// RUN: %python %S/../../utils/module-deps-to-rsp.py %t/result_pch_stripped.json \
+// RUN:   --module-name=ModPCH > %t/mod_pch.cc1.rsp
+// RUN: %python %S/../../utils/module-deps-to-rsp.py %t/result_pch_stripped.json \
+// RUN:   --tu-index=0 > %t/pch.rsp
+//
+// RUN: %clang @%t/mod_common_1.cc1.rsp
+// RUN: %clang @%t/mod_common_2.cc1.rsp
+// RUN: %clang @%t/mod_pch.cc1.rsp
 // RUN: %clang -x c-header %t/pch.h -fmodules -gmodules -fimplicit-module-maps \
-// RUN:   -fmodules-cache-path=%t/cache -o %t/pch.h.gch
+// RUN:   -fmodules-cache-path=%t/cache -o %t/pch.h.gch @%t/pch.rsp
 
 // Scan dependencies of the TU:
 //
@@ -58,3 +172,84 @@
 // CHECK-TU-NEXT:     }
 // CHECK-TU-NEXT:   ]
 // CHECK-TU-NEXT: }
+
+// Explicitly build the TU:
+//
+// RUN: tail -n +2 %t/result_tu.json > %t/result_tu_stripped.json
+// RUN: %python %S/../../utils/module-deps-to-rsp.py %t/result_tu_stripped.json \
+// RUN:   --module-name=ModTU > %t/mod_tu.cc1.rsp
+// RUN: %python %S/../../utils/module-deps-to-rsp.py %t/result_tu_stripped.json \
+// RUN:   --tu-index=0 > %t/tu.rsp
+//
+// RUN: %clang @%t/mod_tu.cc1.rsp
+// RUN: %clang -fsyntax-only %t/tu.c -fmodules -gmodules -fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/cache -include %t/pch.h -o %t/tu.o @%t/tu.rsp
+
+// Scan dependencies of the TU that has common modules with the PCH:
+//
+// RUN: sed "s|DIR|%/t|g" %S/Inputs/modules-pch/cdb_tu_with_common.json > %t/cdb.json
+// RUN: echo -%t > %t/result_tu_with_common.json
+// RUN: clang-scan-deps -compilation-database %t/cdb.json -format experimental-full \
+// RUN:   -generate-modules-path-args -module-files-dir %t/build -mode preprocess >> %t/result_tu_with_common.json
+// RUN: cat %t/result_tu_with_common.json | sed 's:\\\\\?:/:g' | FileCheck %s -check-prefix=CHECK-TU-WITH-COMMON
+//
+// CHECK-TU-WITH-COMMON:      -[[PREFIX:.*]]
+// CHECK-TU-WITH-COMMON-NEXT: {
+// CHECK-TU-WITH-COMMON-NEXT:   "modules": [
+// CHECK-TU-WITH-COMMON-NEXT:     {
+// CHECK-TU-WITH-COMMON-NEXT:       "clang-module-deps": [],
+// CHECK-TU-WITH-COMMON-NEXT:       "clang-modulemap-file": "[[PREFIX]]/module.modulemap",
+// CHECK-TU-WITH-COMMON-NEXT:       "command-line": [
+// CHECK-TU-WITH-COMMON-NEXT:         "-cc1",
+// CHECK-TU-WITH-COMMON:              "-fmodule-map-file=[[PREFIX]]/module.modulemap"
+// CHECK-TU-WITH-COMMON:              "-emit-module",
+// CHECK-TU-WITH-COMMON:              "-fmodule-file=[[PREFIX]]/build/{{.*}}/ModCommon1-{{.*}}.pcm",
+// CHECK-TU-WITH-COMMON:              "-fmodule-name=ModTUWithCommon",
+// CHECK-TU-WITH-COMMON:              "-fno-implicit-modules",
+// CHECK-TU-WITH-COMMON:            ],
+// CHECK-TU-WITH-COMMON-NEXT:       "context-hash": "[[HASH_MOD_TU_WITH_COMMON:.*]]",
+// CHECK-TU-WITH-COMMON-NEXT:       "file-deps": [
+// CHECK-TU-WITH-COMMON-NEXT:         "[[PREFIX]]/mod_tu_with_common.h",
+// CHECK-TU-WITH-COMMON-NEXT:         "[[PREFIX]]/module.modulemap"
+// CHECK-TU-WITH-COMMON-NEXT:       ],
+// CHECK-TU-WITH-COMMON-NEXT:       "name": "ModTUWithCommon"
+// CHECK-TU-WITH-COMMON-NEXT:     }
+// CHECK-TU-WITH-COMMON-NEXT:   ],
+// CHECK-TU-WITH-COMMON-NEXT:   "translation-units": [
+// CHECK-TU-WITH-COMMON-NEXT:     {
+// CHECK-TU-WITH-COMMON-NEXT:       "clang-context-hash": "[[HASH_TU_WITH_COMMON:.*]]",
+// CHECK-TU-WITH-COMMON-NEXT:       "clang-module-deps": [
+// CHECK-TU-WITH-COMMON-NEXT:         {
+// CHECK-TU-WITH-COMMON-NEXT:           "context-hash": "[[HASH_MOD_TU_WITH_COMMON]]",
+// CHECK-TU-WITH-COMMON-NEXT:           "module-name": "ModTUWithCommon"
+// CHECK-TU-WITH-COMMON-NEXT:         }
+// CHECK-TU-WITH-COMMON-NEXT:       ],
+// CHECK-TU-WITH-COMMON-NEXT:       "command-line": [
+// CHECK-TU-WITH-COMMON-NEXT:         "-fno-implicit-modules",
+// CHECK-TU-WITH-COMMON-NEXT:         "-fno-implicit-module-maps",
+// FIXME: Figure out why we need `=ModCommon2` here for Clang to pick up the PCM.
+// CHECK-TU-WITH-COMMON-NEXT:         "-fmodule-file=ModCommon2=[[PREFIX]]/build/{{.*}}/ModCommon2-{{.*}}.pcm",
+// CHECK-TU-WITH-COMMON-NEXT:         "-fmodule-map-file=[[PREFIX]]/module.modulemap"
+// CHECK-TU-WITH-COMMON-NEXT:         "-fmodule-file=[[PREFIX]]/build/[[HASH_MOD_TU_WITH_COMMON]]/ModTUWithCommon-{{.*}}.pcm",
+// CHECK-TU-WITH-COMMON-NEXT:         "-fmodule-map-file=[[PREFIX]]/module.modulemap"
+// CHECK-TU-WITH-COMMON-NEXT:       ],
+// CHECK-TU-WITH-COMMON-NEXT:       "file-deps": [
+// CHECK-TU-WITH-COMMON-NEXT:         "[[PREFIX]]/tu_with_common.c",
+// CHECK-TU-WITH-COMMON-NEXT:         "[[PREFIX]]/pch.h.gch"
+// CHECK-TU-WITH-COMMON-NEXT:       ],
+// CHECK-TU-WITH-COMMON-NEXT:       "input-file": "[[PREFIX]]/tu_with_common.c"
+// CHECK-TU-WITH-COMMON-NEXT:     }
+// CHECK-TU-WITH-COMMON-NEXT:   ]
+// CHECK-TU-WITH-COMMON-NEXT: }
+
+// Explicitly build the TU that has common modules with the PCH:
+//
+// RUN: tail -n +2 %t/result_tu_with_common.json > %t/result_tu_with_common_stripped.json
+// RUN: %python %S/../../utils/module-deps-to-rsp.py %t/result_tu_with_common_stripped.json \
+// RUN:   --module-name=ModTUWithCommon > %t/mod_tu_with_common.cc1.rsp
+// RUN: %python %S/../../utils/module-deps-to-rsp.py %t/result_tu_with_common_stripped.json \
+// RUN:   --tu-index=0 > %t/tu_with_common.rsp
+//
+// RUN: %clang @%t/mod_tu_with_common.cc1.rsp
+// RUN: %clang -fsyntax-only %t/tu_with_common.c -fmodules -gmodules -fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/cache -include %t/pch.h -o %t/tu_with_common.o @%t/tu_with_common.rsp
Index: clang/test/ClangScanDeps/Inputs/modules-pch/tu_with_common.c
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/Inputs/modules-pch/tu_with_common.c
@@ -0,0 +1,4 @@
+// tu_with_common.c
+
+#include "mod_common_2.h"
+#include "mod_tu_with_common.h"
Index: clang/test/ClangScanDeps/Inputs/modules-pch/pch.h
===================================================================
--- clang/test/ClangScanDeps/Inputs/modules-pch/pch.h
+++ clang/test/ClangScanDeps/Inputs/modules-pch/pch.h
@@ -1 +1,4 @@
 // pch.h
+
+#include "mod_common_1.h"
+#include "mod_pch.h"
Index: clang/test/ClangScanDeps/Inputs/modules-pch/module.modulemap
===================================================================
--- clang/test/ClangScanDeps/Inputs/modules-pch/module.modulemap
+++ clang/test/ClangScanDeps/Inputs/modules-pch/module.modulemap
@@ -1,3 +1,19 @@
+module ModCommon1 {
+    header "mod_common_1.h"
+}
+
+module ModCommon2 {
+    header "mod_common_2.h"
+}
+
+module ModPCH {
+    header "mod_pch.h"
+}
+
 module ModTU {
     header "mod_tu.h"
 }
+
+module ModTUWithCommon {
+    header "mod_tu_with_common.h"
+}
Index: clang/test/ClangScanDeps/Inputs/modules-pch/mod_tu_with_common.h
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/Inputs/modules-pch/mod_tu_with_common.h
@@ -0,0 +1,3 @@
+// mod_tu_with_common.h
+
+#include "mod_common_1.h"
Index: clang/test/ClangScanDeps/Inputs/modules-pch/mod_pch.h
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/Inputs/modules-pch/mod_pch.h
@@ -0,0 +1,3 @@
+// mod_pch.h
+
+#include "mod_common_2.h"
Index: clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_2.h
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_2.h
@@ -0,0 +1 @@
+// mod_common_2.h
Index: clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_1.h
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_1.h
@@ -0,0 +1 @@
+// mod_common_1.h
Index: clang/test/ClangScanDeps/Inputs/modules-pch/cdb_tu_with_common.json
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/Inputs/modules-pch/cdb_tu_with_common.json
@@ -0,0 +1,7 @@
+[
+  {
+    "directory": "DIR",
+    "command": "clang -fsyntax-only DIR/tu_with_common.c -fmodules -gmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -include DIR/pch.h -o DIR/tu_with_common.o",
+    "file": "DIR/tu_with_common.c"
+  }
+]
Index: clang/test/ClangScanDeps/Inputs/modules-pch/cdb_pch.json
===================================================================
--- /dev/null
+++ clang/test/ClangScanDeps/Inputs/modules-pch/cdb_pch.json
@@ -0,0 +1,7 @@
+[
+  {
+    "directory": "DIR",
+    "command": "clang -x c-header DIR/pch.h -fmodules -gmodules -fimplicit-module-maps -fmodules-cache-path=DIR/cache -o DIR/pch.h.gch",
+    "file": "DIR/pch.h"
+  }
+]
Index: clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -18,11 +18,10 @@
 using namespace tooling;
 using namespace dependencies;
 
-static CompilerInvocation
-makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps,
-                                         const CompilerInvocation &Invocation) {
+CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
+    const ModuleDeps &Deps) const {
   // Make a deep copy of the invocation.
-  CompilerInvocation CI(Invocation);
+  CompilerInvocation CI(Instance.getInvocation());
 
   // Remove options incompatible with explicit module build.
   CI.getFrontendOpts().Inputs.clear();
@@ -34,6 +33,17 @@
 
   CI.getLangOpts()->ImplicitModules = false;
 
+  // Report the prebuilt modules this module uses.
+  for (const auto &PrebuiltModule : Deps.PrebuiltModuleDeps) {
+    CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModule.PCMFile);
+    CI.getFrontendOpts().ModuleMapFiles.push_back(PrebuiltModule.ModuleMapFile);
+  }
+
+  // Restore the original set of prebuilt module files.
+  CI.getHeaderSearchOpts().PrebuiltModuleFiles = OriginalPrebuiltModuleFiles;
+
+  CI.getPreprocessorOpts().ImplicitPCHInclude.clear();
+
   return CI;
 }
 
@@ -148,7 +158,11 @@
     return;
 
   const Module *TopLevelModule = Imported->getTopLevelModule();
-  DirectModularDeps.insert(TopLevelModule);
+
+  if (MDC.isPrebuiltModule(TopLevelModule))
+    DirectPrebuiltModularDeps.insert(TopLevelModule);
+  else
+    DirectModularDeps.insert(TopLevelModule);
 }
 
 void ModuleDepCollectorPP::EndOfMainFile() {
@@ -167,6 +181,9 @@
 
   for (auto &&I : MDC.FileDeps)
     MDC.Consumer.handleFileDependency(*MDC.Opts, I);
+
+  for (auto &&I : DirectPrebuiltModularDeps)
+    MDC.Consumer.handlePrebuiltModuleDependency(PrebuiltModuleDep{I});
 }
 
 ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
@@ -206,8 +223,12 @@
         MD.FileDeps.insert(IF.getFile()->getName());
       });
 
-  MD.Invocation =
-      makeInvocationForModuleBuildWithoutPaths(MD, Instance.getInvocation());
+  // Add direct prebuilt module dependencies now, so that we can use them when
+  // creating a CompilerInvocation and computing context hash for this
+  // ModuleDeps instance.
+  addDirectPrebuiltModuleDeps(M, MD);
+
+  MD.Invocation = MDC.makeInvocationForModuleBuildWithoutPaths(MD);
   MD.ID.ContextHash = MD.Invocation.getModuleHash();
 
   llvm::DenseSet<const Module *> AddedModules;
@@ -216,6 +237,14 @@
   return MD.ID;
 }
 
+void ModuleDepCollectorPP::addDirectPrebuiltModuleDeps(const Module *M,
+                                                       ModuleDeps &MD) {
+  for (const Module *Import : M->Imports)
+    if (Import->getTopLevelModule() != M->getTopLevelModule())
+      if (MDC.isPrebuiltModule(Import))
+        MD.PrebuiltModuleDeps.emplace_back(Import);
+}
+
 void ModuleDepCollectorPP::addAllSubmoduleDeps(
     const Module *M, ModuleDeps &MD,
     llvm::DenseSet<const Module *> &AddedModules) {
@@ -229,7 +258,8 @@
     const Module *M, ModuleDeps &MD,
     llvm::DenseSet<const Module *> &AddedModules) {
   for (const Module *Import : M->Imports) {
-    if (Import->getTopLevelModule() != M->getTopLevelModule()) {
+    if (Import->getTopLevelModule() != M->getTopLevelModule() &&
+        !MDC.isPrebuiltModule(Import)) {
       ModuleID ImportID = handleTopLevelModule(Import->getTopLevelModule());
       if (AddedModules.insert(Import->getTopLevelModule()).second)
         MD.ClangModuleDeps.push_back(ImportID);
@@ -239,11 +269,25 @@
 
 ModuleDepCollector::ModuleDepCollector(
     std::unique_ptr<DependencyOutputOptions> Opts, CompilerInstance &I,
-    DependencyConsumer &C)
-    : Instance(I), Consumer(C), Opts(std::move(Opts)) {}
+    DependencyConsumer &C,
+    std::map<std::string, std::string, std::less<>> OriginalPrebuiltModuleFiles)
+    : Instance(I), Consumer(C), Opts(std::move(Opts)),
+      OriginalPrebuiltModuleFiles(std::move(OriginalPrebuiltModuleFiles)) {}
 
 void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
   PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this));
 }
 
 void ModuleDepCollector::attachToASTReader(ASTReader &R) {}
+
+bool ModuleDepCollector::isPrebuiltModule(const Module *M) {
+  std::string Name(M->getTopLevelModuleName());
+  const auto &PrebuiltModuleFiles =
+      Instance.getHeaderSearchOpts().PrebuiltModuleFiles;
+  auto PrebuiltModuleFileIt = PrebuiltModuleFiles.find(Name);
+  if (PrebuiltModuleFileIt == PrebuiltModuleFiles.end())
+    return false;
+  assert("Prebuilt module came from the expected AST file" &&
+         PrebuiltModuleFileIt->second == M->getASTFile()->getName());
+  return true;
+}
Index: clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -45,6 +45,23 @@
   DependencyConsumer &C;
 };
 
+/// A listener that collects the names and paths to imported modules.
+class ImportCollectingListener : public ASTReaderListener {
+public:
+  ImportCollectingListener(
+      std::map<std::string, std::string> &PrebuiltModuleFiles)
+      : PrebuiltModuleFiles(PrebuiltModuleFiles) {}
+
+  bool needsImportVisitation() const override { return true; }
+
+  void visitImport(StringRef ModuleName, StringRef Filename) override {
+    PrebuiltModuleFiles[std::string(ModuleName)] = std::string(Filename);
+  }
+
+private:
+  std::map<std::string, std::string> &PrebuiltModuleFiles;
+};
+
 /// A clang tool that runs the preprocessor in a mode that's optimized for
 /// dependency scanning for the given compiler invocation.
 class DependencyScanningAction : public tooling::ToolAction {
@@ -103,6 +120,25 @@
     Compiler.setFileManager(FileMgr);
     Compiler.createSourceManager(*FileMgr);
 
+    std::map<std::string, std::string> PrebuiltModuleFiles;
+    if (!Compiler.getPreprocessorOpts().ImplicitPCHInclude.empty()) {
+      /// Collect the modules that were prebuilt as part of the PCH.
+      ImportCollectingListener Listener(PrebuiltModuleFiles);
+      ASTReader::readASTFileControlBlock(
+          Compiler.getPreprocessorOpts().ImplicitPCHInclude,
+          Compiler.getFileManager(), Compiler.getPCHContainerReader(),
+          /*FindModuleFileExtensions=*/false, Listener,
+          /*ValidateDiagnosticOptions=*/false);
+    }
+    /// Make a backup of the original prebuilt module file arguments.
+    std::map<std::string, std::string, std::less<>> OrigPrebuiltModuleFiles =
+        Compiler.getHeaderSearchOpts().PrebuiltModuleFiles;
+    /// Configure the compiler with discovered prebuilt modules. This will
+    /// prevent the implicit build of duplicate modules and force reuse of
+    /// existing prebuilt module files instead.
+    Compiler.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
+        PrebuiltModuleFiles.begin(), PrebuiltModuleFiles.end());
+
     // Create the dependency collector that will collect the produced
     // dependencies.
     //
@@ -124,7 +160,8 @@
       break;
     case ScanningOutputFormat::Full:
       Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>(
-          std::move(Opts), Compiler, Consumer));
+          std::move(Opts), Compiler, Consumer,
+          std::move(OrigPrebuiltModuleFiles)));
       break;
     }
 
Index: clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
===================================================================
--- clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -16,10 +16,7 @@
 std::vector<std::string> FullDependencies::getAdditionalArgs(
     std::function<StringRef(ModuleID)> LookupPCMPath,
     std::function<const ModuleDeps &(ModuleID)> LookupModuleDeps) const {
-  std::vector<std::string> Ret{
-      "-fno-implicit-modules",
-      "-fno-implicit-module-maps",
-  };
+  std::vector<std::string> Ret = getAdditionalArgsWithoutModulePaths();
 
   std::vector<std::string> PCMPaths;
   std::vector<std::string> ModMapPaths;
@@ -35,10 +32,17 @@
 
 std::vector<std::string>
 FullDependencies::getAdditionalArgsWithoutModulePaths() const {
-  return {
+  std::vector<std::string> Args{
       "-fno-implicit-modules",
       "-fno-implicit-module-maps",
   };
+
+  for (const PrebuiltModuleDep &PMD : PrebuiltModuleDeps) {
+    Args.push_back("-fmodule-file=" + PMD.ModuleName + "=" + PMD.PCMFile);
+    Args.push_back("-fmodule-map-file=" + PMD.ModuleMapFile);
+  }
+
+  return Args;
 }
 
 DependencyScanningTool::DependencyScanningTool(
@@ -57,6 +61,10 @@
       Dependencies.push_back(std::string(File));
     }
 
+    void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
+      // Same as `handleModuleDependency`.
+    }
+
     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
@@ -125,6 +133,10 @@
       Dependencies.push_back(std::string(File));
     }
 
+    void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) override {
+      PrebuiltModuleDeps.emplace_back(std::move(PMD));
+    }
+
     void handleModuleDependency(ModuleDeps MD) override {
       ClangModuleDeps[MD.ID.ContextHash + MD.ID.ModuleName] = std::move(MD);
     }
@@ -146,6 +158,8 @@
           FD.ClangModuleDeps.push_back(MD.ID);
       }
 
+      FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
+
       FullDependenciesResult FDR;
 
       for (auto &&M : ClangModuleDeps) {
@@ -162,6 +176,7 @@
 
   private:
     std::vector<std::string> Dependencies;
+    std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
     std::unordered_map<std::string, ModuleDeps> ClangModuleDeps;
     std::string ContextHash;
     std::vector<std::string> OutputPaths;
Index: clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
+++ clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -29,6 +29,18 @@
 
 class DependencyConsumer;
 
+/// Modular dependency that has already been built prior to the dependency scan.
+struct PrebuiltModuleDep {
+  std::string ModuleName;
+  std::string PCMFile;
+  std::string ModuleMapFile;
+
+  explicit PrebuiltModuleDep(const Module *M)
+      : ModuleName(M->getTopLevelModuleName()),
+        PCMFile(M->getASTFile()->getName()),
+        ModuleMapFile(M->PresumedModuleMapFile) {}
+};
+
 /// This is used to identify a specific module.
 struct ModuleID {
   /// The name of the module. This may include `:` for C++20 module partitions,
@@ -74,6 +86,10 @@
   /// on, not including transitive dependencies.
   llvm::StringSet<> FileDeps;
 
+  /// A collection of prebuilt modular dependencies this module directly depends
+  /// on, not including transitive dependencies.
+  std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
+
   /// A list of module identifiers this module directly depends on, not
   /// including transitive dependencies.
   ///
@@ -150,9 +166,15 @@
   ModuleDepCollector &MDC;
   /// Working set of direct modular dependencies.
   llvm::DenseSet<const Module *> DirectModularDeps;
+  /// Working set of direct modular dependencies that have already been built.
+  llvm::DenseSet<const Module *> DirectPrebuiltModularDeps;
 
   void handleImport(const Module *Imported);
 
+  /// Adds direct modular dependencies that have already been built to the
+  /// ModuleDeps instance.
+  void addDirectPrebuiltModuleDeps(const Module *M, ModuleDeps &MD);
+
   /// Traverses the previously collected direct modular dependencies to discover
   /// transitive modular dependencies and fills the parent \c ModuleDepCollector
   /// with both.
@@ -168,7 +190,9 @@
 class ModuleDepCollector final : public DependencyCollector {
 public:
   ModuleDepCollector(std::unique_ptr<DependencyOutputOptions> Opts,
-                     CompilerInstance &I, DependencyConsumer &C);
+                     CompilerInstance &I, DependencyConsumer &C,
+                     std::map<std::string, std::string, std::less<>>
+                         OriginalPrebuiltModuleFiles);
 
   void attachToPreprocessor(Preprocessor &PP) override;
   void attachToASTReader(ASTReader &R) override;
@@ -191,6 +215,18 @@
   std::unordered_map<const Module *, ModuleDeps> ModularDeps;
   /// Options that control the dependency output generation.
   std::unique_ptr<DependencyOutputOptions> Opts;
+  /// The mapping between prebuilt module names and module files that were
+  /// present in the original CompilerInvocation.
+  std::map<std::string, std::string, std::less<>> OriginalPrebuiltModuleFiles;
+
+  /// Checks whether the module is known as being prebuilt.
+  bool isPrebuiltModule(const Module *M);
+
+  /// Constructs a CompilerInvocation that can be used to build the given
+  /// module, excluding paths to discovered modular dependencies that are yet to
+  /// be built.
+  CompilerInvocation
+  makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps) const;
 };
 
 } // end namespace dependencies
Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
+++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
@@ -37,6 +37,8 @@
   virtual void handleFileDependency(const DependencyOutputOptions &Opts,
                                     StringRef Filename) = 0;
 
+  virtual void handlePrebuiltModuleDependency(PrebuiltModuleDep PMD) = 0;
+
   virtual void handleModuleDependency(ModuleDeps MD) = 0;
 
   virtual void handleContextHash(std::string Hash) = 0;
Index: clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
+++ clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
@@ -31,6 +31,10 @@
   /// directly depends on, not including transitive dependencies.
   std::vector<std::string> FileDeps;
 
+  /// A collection of prebuilt modules this translation unit directly depends
+  /// on, not including transitive dependencies.
+  std::vector<PrebuiltModuleDep> PrebuiltModuleDeps;
+
   /// A list of modules this translation unit directly depends on, not including
   /// transitive dependencies.
   ///
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D103526: [... Duncan P. N. Exon Smith via Phabricator via cfe-commits
    • [PATCH] D1035... Jan Svoboda via Phabricator via cfe-commits

Reply via email to