From dda1cda4dfacafcbaaf32db6fba0457693dc66da Mon Sep 17 00:00:00 2001 From: Tony Allevato Date: Sun, 22 Jan 2023 19:34:28 -0800 Subject: [PATCH] Allow Clang modules to be listed in the explicit module map. This lets users of `-explicit-swift-module-map-file` use a single mapping for all module dependencies, regardless of whether they're Swift or Clang modules, instead of manually splitting them among this file and command line flags. --- .../swift/Frontend/ModuleInterfaceLoader.h | 29 +++++++++---- lib/Frontend/Frontend.cpp | 26 +++++++---- lib/Frontend/ModuleInterfaceLoader.cpp | 31 +++++++++++++ .../explicit-module-map-clang-and-swift.swift | 43 +++++++++++++++++++ .../explicit-module-map-clang-explicit.swift | 39 +++++++++++++++++ .../explicit-module-map-clang-implicit.swift | 37 ++++++++++++++++ 6 files changed, 188 insertions(+), 17 deletions(-) create mode 100644 test/ScanDependencies/explicit-module-map-clang-and-swift.swift create mode 100644 test/ScanDependencies/explicit-module-map-clang-explicit.swift create mode 100644 test/ScanDependencies/explicit-module-map-clang-implicit.swift diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index a4ec781c10b03..ee16da87208ca 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -173,19 +173,24 @@ class ExplicitSwiftModuleLoader: public SerializedModuleLoaderBase { ~ExplicitSwiftModuleLoader(); }; -/// Information about explicitly specified Swift module files. +/// Information about explicitly specified Swift and Clang module files. struct ExplicitModuleInfo { - // Path of the .swiftmodule file. + // Path of the .swiftmodule file. Empty for pure Clang modules. std::string modulePath; - // Path of the .swiftmoduledoc file. + // Path of the .swiftmoduledoc file. Empty for pure Clang modules. std::string moduleDocPath; - // Path of the .swiftsourceinfo file. + // Path of the .swiftsourceinfo file. Empty for pure Clang modules. std::string moduleSourceInfoPath; // A flag that indicates whether this module is a framework bool isFramework = false; // A flag that indicates whether this module is a system module // Set the default to be false. bool isSystem = false; + // Path of the Clang module map file. Empty for pure Swift modules. + std::string clangModuleMapPath; + // Path of a compiled Clang explicit module file. Empty for pure Swift + // modules. + std::string clangModulePath; }; /// Parser of explicit module maps passed into the compiler. @@ -194,15 +199,19 @@ struct ExplicitModuleInfo { // "moduleName": "A", // "modulePath": "A.swiftmodule", // "docPath": "A.swiftdoc", -// "sourceInfoPath": "A.swiftsourceinfo" -// "isFramework": false +// "sourceInfoPath": "A.swiftsourceinfo", +// "isFramework": false, +// "clangModuleMapPath": "A/module.modulemap", +// "clangModulePath": "A.pcm", // }, // { // "moduleName": "B", // "modulePath": "B.swiftmodule", // "docPath": "B.swiftdoc", -// "sourceInfoPath": "B.swiftsourceinfo" -// "isFramework": false +// "sourceInfoPath": "B.swiftsourceinfo", +// "isFramework": false, +// "clangModuleMapPath": "B/module.modulemap", +// "clangModulePath": "B.pcm", // } // ] class ExplicitModuleMapParser { @@ -279,6 +288,10 @@ class ExplicitModuleMapParser { result.isFramework = parseBoolValue(val); } else if (key == "isSystem") { result.isSystem = parseBoolValue(val); + } else if (key == "clangModuleMapPath") { + result.clangModuleMapPath = val.str(); + } else if (key == "clangModulePath") { + result.clangModulePath = val.str(); } else { // Being forgiving for future fields. continue; diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index fb69df991a5a6..56b929e2865bd 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -554,6 +554,20 @@ bool CompilerInstance::setUpModuleLoaders() { Context->addModuleLoader(std::move(MemoryBufferLoader)); } + // If using `-explicit-swift-module-map-file`, create the explicit loader + // before creating `ClangImporter` because the entries in the map influence + // the Clang flags. The loader is added to the context below. + std::unique_ptr ESML = nullptr; + bool ExplicitModuleBuild = + Invocation.getFrontendOptions().DisableImplicitModules; + if (ExplicitModuleBuild || + !Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty()) { + ESML = ExplicitSwiftModuleLoader::create( + *Context, getDependencyTracker(), MLM, + Invocation.getSearchPathOptions().ExplicitSwiftModuleMap, + IgnoreSourceInfoFile); + } + // Wire up the Clang importer. If the user has specified an SDK, use it. // Otherwise, we just keep it around as our interface to Clang's ABI // knowledge. @@ -575,15 +589,9 @@ bool CompilerInstance::setUpModuleLoaders() { *Context, ModuleCachePath, FEOpts.PrebuiltModuleCachePath, FEOpts.BackupModuleInterfaceDir, LoaderOpts, RequireOSSAModules_t(Invocation.getSILOptions()))); - // If implicit modules are disabled, we need to install an explicit module - // loader. - bool ExplicitModuleBuild = Invocation.getFrontendOptions().DisableImplicitModules; - if (ExplicitModuleBuild || !Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty()) { - auto ESML = ExplicitSwiftModuleLoader::create( - *Context, - getDependencyTracker(), MLM, - Invocation.getSearchPathOptions().ExplicitSwiftModuleMap, - IgnoreSourceInfoFile); + + // Install an explicit module loader if it was created earlier. + if (ESML) { this->DefaultSerializedLoader = ESML.get(); Context->addModuleLoader(std::move(ESML)); } diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index cd36509dc08cf..e1ca8adfc760d 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1875,6 +1875,27 @@ struct ExplicitSwiftModuleLoader::Implementation { else if (result == std::errc::no_such_file_or_directory) Ctx.Diags.diagnose(SourceLoc(), diag::explicit_swift_module_map_missing, fileName); + + // A single module map can define multiple modules; keep track of the ones + // we've seen so that we don't generate duplicate flags. + std::set moduleMapsSeen; + std::vector &extraClangArgs = Ctx.ClangImporterOpts.ExtraArgs; + for (auto &entry : ExplicitModuleMap) { + const auto &moduleMapPath = entry.getValue().clangModuleMapPath; + if (!moduleMapPath.empty() && + moduleMapsSeen.find(moduleMapPath) == moduleMapsSeen.end()) { + moduleMapsSeen.insert(moduleMapPath); + extraClangArgs.push_back( + (Twine("-fmodule-map-file=") + moduleMapPath).str()); + } + + const auto &modulePath = entry.getValue().clangModulePath; + if (!modulePath.empty()) { + extraClangArgs.push_back( + (Twine("-fmodule-file=") + entry.getKey() + "=" + modulePath) + .str()); + } + } } }; @@ -1910,6 +1931,16 @@ bool ExplicitSwiftModuleLoader::findModule(ImportPath::Element ModuleID, } auto &moduleInfo = it->getValue(); + // If this is only a Clang module with no paired Swift module, return false + // now so that we don't emit diagnostics about it being missing. This gives + // ClangImporter an opportunity to import it. + bool hasClangModule = !moduleInfo.clangModuleMapPath.empty() || + !moduleInfo.clangModulePath.empty(); + bool hasSwiftModule = !moduleInfo.modulePath.empty(); + if (hasClangModule && !hasSwiftModule) { + return false; + } + // Set IsFramework bit according to the moduleInfo IsFramework = moduleInfo.isFramework; IsSystemModule = moduleInfo.isSystem; diff --git a/test/ScanDependencies/explicit-module-map-clang-and-swift.swift b/test/ScanDependencies/explicit-module-map-clang-and-swift.swift new file mode 100644 index 0000000000000..ac46cee7e5945 --- /dev/null +++ b/test/ScanDependencies/explicit-module-map-clang-and-swift.swift @@ -0,0 +1,43 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/clang-module-cache +// RUN: mkdir -p %t/inputs +// RUN: echo "public func anotherFuncA() {}" > %t/A.swift +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/inputs/A.swiftmodule -emit-module-doc-path %t/inputs/A.swiftdoc -emit-module-source-info -emit-module-source-info-path %t/inputs/A.swiftsourceinfo -import-underlying-module -I%S/Inputs/CHeaders -module-cache-path %t.module-cache %t/A.swift -module-name A + +// RUN: echo "[{" > %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"A\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/t/inputs/A.swiftmodule\"," >> %/t/inputs/map.json +// RUN: echo "\"docPath\": \"%/t/inputs/A.swiftdoc\"," >> %/t/inputs/map.json +// RUN: echo "\"sourceInfoPath\": \"%/t/inputs/A.swiftsourceinfo\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json +// RUN: echo "\"clangModuleMapPath\": \"%/S/Inputs/CHeaders/module.modulemap\"" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}]" >> %/t/inputs/map.json + +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/Foo.swiftmodule -module-cache-path %t.module-cache -explicit-swift-module-map-file %t/inputs/map.json %s + +import A + +func callA() { + funcA() + anotherFuncA() +} diff --git a/test/ScanDependencies/explicit-module-map-clang-explicit.swift b/test/ScanDependencies/explicit-module-map-clang-explicit.swift new file mode 100644 index 0000000000000..5ff764e34d460 --- /dev/null +++ b/test/ScanDependencies/explicit-module-map-clang-explicit.swift @@ -0,0 +1,39 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/clang-module-cache +// RUN: mkdir -p %t/inputs + +// RUN: echo "[{" > %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"A\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json +// RUN: echo "\"clangModuleMapPath\": \"%/S/Inputs/CHeaders/module.modulemap\"," >> %/t/inputs/map.json +// RUN: echo "\"clangModulePath\": \"%t/inputs/A.pcm\"" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}]" >> %/t/inputs/map.json + +// RUN: %target-swift-emit-pcm -module-name A -o %t/inputs/A.pcm %S/Inputs/CHeaders/module.modulemap +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/Foo.swiftmodule -module-cache-path %t.module-cache -explicit-swift-module-map-file %t/inputs/map.json %s + +import A + +func callA() { + funcA() +} diff --git a/test/ScanDependencies/explicit-module-map-clang-implicit.swift b/test/ScanDependencies/explicit-module-map-clang-implicit.swift new file mode 100644 index 0000000000000..a03ac9eb494b6 --- /dev/null +++ b/test/ScanDependencies/explicit-module-map-clang-implicit.swift @@ -0,0 +1,37 @@ +// RUN: %empty-directory(%t) +// RUN: mkdir -p %t/clang-module-cache +// RUN: mkdir -p %t/inputs + +// RUN: echo "[{" > %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"A\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false," >> %/t/inputs/map.json +// RUN: echo "\"clangModuleMapPath\": \"%/S/Inputs/CHeaders/module.modulemap\"" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"Swift\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/stdlib_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"SwiftOnoneSupport\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/ononesupport_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_Concurrency\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/concurrency_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}," >> %/t/inputs/map.json +// RUN: echo "{" >> %/t/inputs/map.json +// RUN: echo "\"moduleName\": \"_StringProcessing\"," >> %/t/inputs/map.json +// RUN: echo "\"modulePath\": \"%/string_processing_module\"," >> %/t/inputs/map.json +// RUN: echo "\"isFramework\": false" >> %/t/inputs/map.json +// RUN: echo "}]" >> %/t/inputs/map.json + +// RUN: %target-swift-frontend -emit-module -emit-module-path %t/Foo.swiftmodule -module-cache-path %t.module-cache -explicit-swift-module-map-file %t/inputs/map.json %s + +import A + +func callA() { + funcA() +}