diff --git a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h b/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h --- a/clang/include/clang/Tooling/DependencyScanning/ModuleDepCollector.h +++ b/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 Opts, - CompilerInstance &I, DependencyConsumer &C); + CompilerInstance &I, DependencyConsumer &C, + std::map PrebuiltModuleFiles); void attachToPreprocessor(Preprocessor &PP) override; void attachToASTReader(ASTReader &R) override; @@ -181,6 +186,12 @@ std::unordered_map ModularDeps; /// Options that control the dependency output generation. std::unique_ptr Opts; + /// Mapping between module names to module files that have been built prior to + /// the dependency scan. + std::map PrebuiltModuleFiles; + + CompilerInvocation + makeInvocationForModuleBuildWithoutPaths(const ModuleDeps &Deps) const; }; } // end namespace dependencies diff --git a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp b/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningWorker.cpp +++ b/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 &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 &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 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( - std::move(Opts), Compiler, Consumer)); + std::move(Opts), Compiler, Consumer, std::move(PrebuiltModuleFiles))); break; } diff --git a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp b/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/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 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 &AddedModules) { @@ -229,7 +266,9 @@ const Module *M, ModuleDeps &MD, llvm::DenseSet &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 Opts, CompilerInstance &I, - DependencyConsumer &C) - : Instance(I), Consumer(C), Opts(std::move(Opts)) {} + DependencyConsumer &C, + std::map PrebuiltModuleFiles) + : Instance(I), Consumer(C), Opts(std::move(Opts)), + PrebuiltModuleFiles(std::move(PrebuiltModuleFiles)) {} void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) { PP.addPPCallbacks(std::make_unique(Instance, *this)); 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 --- /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 --- /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 --- /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 --- /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 --- /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 --- /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 --- 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 --- 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 --- /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 --- 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_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