jansvoboda11 created this revision.
jansvoboda11 added reviewers: Bigcheese, dexonsmith.
jansvoboda11 requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

When a translation unit uses a PCH and imports the same modules, we'd prefer to 
resolve to those modules instead of inventing new modules and reporting them as 
modular dependencies. Since the PCH modules have already been built, report 
them as file dependencies instead and nudge the compiler to reuse them when 
deciding whether to build a new module.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D103526

Files:
  clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
  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_pch.json
+// RUN: echo -%t > %t/result_pch.json
+// RUN: clang-scan-deps -compilation-database %t/cdb_pch.json -format experimental-full \
+// RUN:   -generate-modules-path-args -build-dir %t/build -mode preprocess >> %t/result_pch.json
+// RUN: cat %t/result_pch.json | 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 -fno-implicit-modules -fno-implicit-module-maps
+// RUN:   -fmodules-cache-path=%t/cache -o %t/pch.h.gch @%t/pch.rsp
 
 // Scan dependencies of the TU:
 //
@@ -57,3 +171,81 @@
 // 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 %s -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_tu_with_common.json
+// RUN: echo -%t > %t/result_tu_with_common.json
+// RUN: clang-scan-deps -compilation-database %t/cdb_tu_with_common.json -format experimental-full \
+// RUN:   -generate-modules-path-args -build-dir %t/build -mode preprocess >> %t/result_tu_with_common.json
+// RUN: cat %t/result_tu_with_common.json | 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:              "-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]]/build/{{.*}}/ModCommon1-{{.*}}.pcm",
+// 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",
+// 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 %s -fmodules -gmodules -fimplicit-module-maps \
+// RUN:   -fmodules-cache-path=%t/cache -include %t/pch.h -o %t/tu.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,9 +18,9 @@
 using namespace tooling;
 using namespace dependencies;
 
-static CompilerInvocation
-makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps,
-                                         const CompilerInvocation &Invocation) {
+CompilerInvocation ModuleDepCollector::makeInvocationForModuleBuildWithoutPaths(
+    const ModuleDeps &Deps) const {
+  const CompilerInvocation &Invocation = Instance.getInvocation();
   // Make a deep copy of the invocation.
   CompilerInvocation CI(Invocation);
 
@@ -34,6 +34,19 @@
 
   CI.getLangOpts()->ImplicitModules = false;
 
+  // The HeaderSearchOptions::PrebuiltModuleFiles that were injected into the
+  // original CompilerInvocation were an over-approximation. Check which ones
+  // were actually used in the module and pass them as FrontendOpts::ModuleFiles
+  // instead.
+  for (const auto &PrebuiltModuleFile : PrebuiltModuleFiles) {
+    CI.getHeaderSearchOpts().PrebuiltModuleFiles.erase(
+        PrebuiltModuleFile.first);
+    if (Deps.FileDeps.contains(PrebuiltModuleFile.second))
+      CI.getFrontendOpts().ModuleFiles.push_back(PrebuiltModuleFile.second);
+  }
+
+  CI.getPreprocessorOpts().ImplicitPCHInclude.clear();
+
   return CI;
 }
 
@@ -148,7 +161,15 @@
     return;
 
   const Module *TopLevelModule = Imported->getTopLevelModule();
-  DirectModularDeps.insert(TopLevelModule);
+
+  auto PrebuiltModuleIt = MDC.PrebuiltModuleFiles.find(
+      TopLevelModule->getTopLevelModuleName().str());
+  // Don't add prebuilt modules to modular dependencies. The original
+  // command-line already contains these and we report them (or their parent) as
+  // file dependencies. We don't want to report them as modular dependencies,
+  // since they don't need to be built.
+  if (PrebuiltModuleIt == MDC.PrebuiltModuleFiles.end())
+    DirectModularDeps.insert(TopLevelModule);
 }
 
 void ModuleDepCollectorPP::EndOfMainFile() {
@@ -206,8 +227,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 +241,18 @@
   return MD.ID;
 }
 
+void ModuleDepCollectorPP::addDirectPrebuiltModuleDeps(const Module *M,
+                                                       ModuleDeps &MD) {
+  for (const Module *Import : M->Imports) {
+    if (Import->getTopLevelModule() != M->getTopLevelModule()) {
+      auto PrebuiltModuleIt =
+          MDC.PrebuiltModuleFiles.find(Import->getTopLevelModuleName().str());
+      if (PrebuiltModuleIt != MDC.PrebuiltModuleFiles.end())
+        MD.FileDeps.insert(PrebuiltModuleIt->second);
+    }
+  }
+}
+
 void ModuleDepCollectorPP::addAllSubmoduleDeps(
     const Module *M, ModuleDeps &MD,
     llvm::DenseSet<const Module *> &AddedModules) {
@@ -229,7 +266,9 @@
     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.PrebuiltModuleFiles.find(Import->getTopLevelModuleName().str()) ==
+            MDC.PrebuiltModuleFiles.end()) {
       ModuleID ImportID = handleTopLevelModule(Import->getTopLevelModule());
       if (AddedModules.insert(Import->getTopLevelModule()).second)
         MD.ClangModuleDeps.push_back(ImportID);
@@ -239,8 +278,10 @@
 
 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> PrebuiltModuleFiles)
+    : Instance(I), Consumer(C), Opts(std::move(Opts)),
+      PrebuiltModuleFiles(std::move(PrebuiltModuleFiles)) {}
 
 void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) {
   PP.addPPCallbacks(std::make_unique<ModuleDepCollectorPP>(Instance, *this));
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 {
@@ -101,6 +118,22 @@
     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);
+    }
+    /// Configure the compiler with prebuilt modules. This will prevent it from
+    /// inventing new modules and instead force it to reuse whatever the PCH
+    /// already contains.
+    Compiler.getHeaderSearchOpts().PrebuiltModuleFiles.insert(
+        PrebuiltModuleFiles.begin(), PrebuiltModuleFiles.end());
+
     // Create the dependency collector that will collect the produced
     // dependencies.
     //
@@ -122,7 +155,7 @@
       break;
     case ScanningOutputFormat::Full:
       Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>(
-          std::move(Opts), Compiler, Consumer));
+          std::move(Opts), Compiler, Consumer, std::move(PrebuiltModuleFiles)));
       break;
     }
 
Index: clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
===================================================================
--- clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
+++ clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -143,6 +143,10 @@
 
   void handleImport(const Module *Imported);
 
+  /// Adds direct modular dependencies that were prebuilt to the ModuleDeps
+  /// instance as file dependencies.
+  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.
@@ -158,7 +162,8 @@
 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> PrebuiltModuleFiles);
 
   void attachToPreprocessor(Preprocessor &PP) override;
   void attachToASTReader(ASTReader &R) override;
@@ -181,6 +186,12 @@
   std::unordered_map<const Module *, ModuleDeps> ModularDeps;
   /// Options that control the dependency output generation.
   std::unique_ptr<DependencyOutputOptions> Opts;
+  /// Mapping between module names to module files that have been built prior to
+  /// the dependency scan.
+  std::map<std::string, std::string> PrebuiltModuleFiles;
+
+  CompilerInvocation
+  makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps) const;
 };
 
 } // end namespace dependencies
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to