diff --git a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h +++ b/clang/include/clang/Tooling/DependencyScanning/DependencyScanningTool.h @@ -31,6 +31,10 @@ /// directly depends on, not including transitive dependencies. std::vector FileDeps; + /// A collection of prebuilt modules this translation unit directly depends + /// on, not including transitive dependencies. + std::vector 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 --- a/clang/include/clang/Tooling/DependencyScanning/DependencyScanningWorker.h +++ b/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; 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 @@ -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 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 DirectModularDeps; + /// Working set of direct modular dependencies that have already been built. + llvm::DenseSet 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 Opts, - CompilerInstance &I, DependencyConsumer &C); + CompilerInstance &I, DependencyConsumer &C, + std::map> + OriginalPrebuiltModuleFiles); void attachToPreprocessor(Preprocessor &PP) override; void attachToASTReader(ASTReader &R) override; @@ -191,6 +215,18 @@ std::unordered_map ModularDeps; /// Options that control the dependency output generation. std::unique_ptr Opts; + /// The mapping between prebuilt module names and module files that were + /// present in the original CompilerInvocation. + std::map> 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 --- a/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp +++ b/clang/lib/Tooling/DependencyScanning/DependencyScanningTool.cpp @@ -16,10 +16,7 @@ std::vector FullDependencies::getAdditionalArgs( std::function LookupPCMPath, std::function LookupModuleDeps) const { - std::vector Ret{ - "-fno-implicit-modules", - "-fno-implicit-module-maps", - }; + std::vector Ret = getAdditionalArgsWithoutModulePaths(); std::vector PCMPaths; std::vector ModMapPaths; @@ -35,10 +32,17 @@ std::vector FullDependencies::getAdditionalArgsWithoutModulePaths() const { - return { + std::vector 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 Dependencies; + std::vector PrebuiltModuleDeps; std::unordered_map ClangModuleDeps; std::string ContextHash; std::vector OutputPaths; 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 { @@ -103,6 +120,25 @@ 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); + } + /// Make a backup of the original prebuilt module file arguments. + std::map> 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( - 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 --- a/clang/lib/Tooling/DependencyScanning/ModuleDepCollector.cpp +++ b/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 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 &AddedModules) { @@ -229,7 +258,8 @@ 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.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 Opts, CompilerInstance &I, - DependencyConsumer &C) - : Instance(I), Consumer(C), Opts(std::move(Opts)) {} + DependencyConsumer &C, + std::map> OriginalPrebuiltModuleFiles) + : Instance(I), Consumer(C), Opts(std::move(Opts)), + OriginalPrebuiltModuleFiles(std::move(OriginalPrebuiltModuleFiles)) {} void ModuleDepCollector::attachToPreprocessor(Preprocessor &PP) { PP.addPPCallbacks(std::make_unique(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 --- /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.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