[clang] 4629554 - [clang][deps] Handle modular dependencies present in PCH

Jan Svoboda via cfe-commits cfe-commits at lists.llvm.org
Mon Jun 14 03:01:04 PDT 2021


Author: Jan Svoboda
Date: 2021-06-14T11:59:35+02:00
New Revision: 4629554f0b664c94ada7c44fe40855d7a9a39820

URL: https://github.com/llvm/llvm-project/commit/4629554f0b664c94ada7c44fe40855d7a9a39820
DIFF: https://github.com/llvm/llvm-project/commit/4629554f0b664c94ada7c44fe40855d7a9a39820.diff

LOG: [clang][deps] Handle modular dependencies present in PCH

When a translation unit uses a PCH and imports the same modules as the PCH, 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 nudge the compiler to reuse them when deciding whether to build a new module and don't report them as regular modular dependencies.

Depends on D103524 & D103802.

Reviewed By: dexonsmith

Differential Revision: https://reviews.llvm.org/D103526

Added: 
    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/tu_with_common.c

Modified: 
    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/module.modulemap
    clang/test/ClangScanDeps/Inputs/modules-pch/pch.h
    clang/test/ClangScanDeps/modules-pch.c

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
index 89a70fb723c4..f88dc472c80b 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h
@@ -31,6 +31,10 @@ struct FullDependencies {
   /// 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.
   ///

diff  --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
index 689119330c41..e51040d2c0f5 100644
--- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
+++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h
@@ -37,6 +37,8 @@ class DependencyConsumer {
   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;

diff  --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
index b8b7882e54bd..73bc41d1f955 100644
--- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
+++ b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h
@@ -29,6 +29,18 @@ namespace dependencies {
 
 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 @@ struct ModuleDeps {
   /// 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 @@ class ModuleDepCollectorPP final : public PPCallbacks {
   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 ModuleDepCollectorPP final : public PPCallbacks {
 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 @@ class ModuleDepCollector final : public DependencyCollector {
   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

diff  --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
index 196b05bafea0..409ccd393e78 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp
@@ -16,10 +16,7 @@ namespace dependencies{
 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::getAdditionalArgs(
 
 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 @@ llvm::Expected<std::string> DependencyScanningTool::getDependencyFile(
       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 @@ DependencyScanningTool::getFullDependencies(
       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 @@ DependencyScanningTool::getFullDependencies(
           FD.ClangModuleDeps.push_back(MD.ID);
       }
 
+      FD.PrebuiltModuleDeps = std::move(PrebuiltModuleDeps);
+
       FullDependenciesResult FDR;
 
       for (auto &&M : ClangModuleDeps) {
@@ -162,6 +176,7 @@ DependencyScanningTool::getFullDependencies(
 
   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;

diff  --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
index 0df515a4c99f..ebcd16e5ab6f 100644
--- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
+++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp
@@ -45,6 +45,23 @@ class DependencyConsumerForwarder : public DependencyFileGenerator {
   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 @@ class DependencyScanningAction : public tooling::ToolAction {
     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 @@ class DependencyScanningAction : public tooling::ToolAction {
       break;
     case ScanningOutputFormat::Full:
       Compiler.addDependencyCollector(std::make_shared<ModuleDepCollector>(
-          std::move(Opts), Compiler, Consumer));
+          std::move(Opts), Compiler, Consumer,
+          std::move(OrigPrebuiltModuleFiles)));
       break;
     }
 

diff  --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
index 3271075d9281..fc6ba6956b34 100644
--- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
+++ b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp
@@ -18,11 +18,10 @@ using namespace clang;
 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 @@ makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps,
 
   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 @@ void ModuleDepCollectorPP::handleImport(const Module *Imported) {
     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 @@ void ModuleDepCollectorPP::EndOfMainFile() {
 
   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 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
         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 @@ ModuleID ModuleDepCollectorPP::handleTopLevelModule(const Module *M) {
   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 @@ void ModuleDepCollectorPP::addModuleDep(
     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 @@ void ModuleDepCollectorPP::addModuleDep(
 
 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;
+}

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/cdb_pch.json b/clang/test/ClangScanDeps/Inputs/modules-pch/cdb_pch.json
new file mode 100644
index 000000000000..dc2fc550b019
--- /dev/null
+++ b/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"
+  }
+]

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/cdb_tu_with_common.json b/clang/test/ClangScanDeps/Inputs/modules-pch/cdb_tu_with_common.json
new file mode 100644
index 000000000000..bc235cd77990
--- /dev/null
+++ b/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"
+  }
+]

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_1.h b/clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_1.h
new file mode 100644
index 000000000000..63acd9bc3eab
--- /dev/null
+++ b/clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_1.h
@@ -0,0 +1 @@
+// mod_common_1.h

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_2.h b/clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_2.h
new file mode 100644
index 000000000000..cfe099a015eb
--- /dev/null
+++ b/clang/test/ClangScanDeps/Inputs/modules-pch/mod_common_2.h
@@ -0,0 +1 @@
+// mod_common_2.h

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/mod_pch.h b/clang/test/ClangScanDeps/Inputs/modules-pch/mod_pch.h
new file mode 100644
index 000000000000..37516b293030
--- /dev/null
+++ b/clang/test/ClangScanDeps/Inputs/modules-pch/mod_pch.h
@@ -0,0 +1,3 @@
+// mod_pch.h
+
+#include "mod_common_2.h"

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/mod_tu_with_common.h b/clang/test/ClangScanDeps/Inputs/modules-pch/mod_tu_with_common.h
new file mode 100644
index 000000000000..8a493f6025eb
--- /dev/null
+++ b/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"

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/module.modulemap b/clang/test/ClangScanDeps/Inputs/modules-pch/module.modulemap
index 405ba5c53a4c..fdec125d27d9 100644
--- a/clang/test/ClangScanDeps/Inputs/modules-pch/module.modulemap
+++ b/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"
+}

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/pch.h b/clang/test/ClangScanDeps/Inputs/modules-pch/pch.h
index 6222fcefca10..1aad74711ec2 100644
--- a/clang/test/ClangScanDeps/Inputs/modules-pch/pch.h
+++ b/clang/test/ClangScanDeps/Inputs/modules-pch/pch.h
@@ -1 +1,4 @@
 // pch.h
+
+#include "mod_common_1.h"
+#include "mod_pch.h"

diff  --git a/clang/test/ClangScanDeps/Inputs/modules-pch/tu_with_common.c b/clang/test/ClangScanDeps/Inputs/modules-pch/tu_with_common.c
new file mode 100644
index 000000000000..a7e5cad688ca
--- /dev/null
+++ b/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"

diff  --git a/clang/test/ClangScanDeps/modules-pch.c b/clang/test/ClangScanDeps/modules-pch.c
index 020818f74ed6..34a0043acd5c 100644
--- a/clang/test/ClangScanDeps/modules-pch.c
+++ b/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


        


More information about the cfe-commits mailing list