From 3947fa9e16470785c758b93f51ec627d67bce0aa Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 28 Mar 2024 11:06:55 -0700 Subject: [PATCH 1/2] [Dependency Scanning] Add libSwiftScan entrypoint to query binary module dependencies' header inputs' module dependencies --- include/swift-c/DependencyScan/DependencyScan.h | 6 +++++- tools/libSwiftScan/libSwiftScan.cpp | 6 ++++++ tools/libSwiftScan/libSwiftScan.exports | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/include/swift-c/DependencyScan/DependencyScan.h b/include/swift-c/DependencyScan/DependencyScan.h index 849a33dc891fb..c8e48b1ace62f 100644 --- a/include/swift-c/DependencyScan/DependencyScan.h +++ b/include/swift-c/DependencyScan/DependencyScan.h @@ -25,7 +25,7 @@ /// SWIFTSCAN_VERSION_MINOR should increase when there are API additions. /// SWIFTSCAN_VERSION_MAJOR is intended for "major" source/ABI breaking changes. #define SWIFTSCAN_VERSION_MAJOR 0 -#define SWIFTSCAN_VERSION_MINOR 7 +#define SWIFTSCAN_VERSION_MINOR 8 SWIFTSCAN_BEGIN_DECLS @@ -206,6 +206,10 @@ SWIFTSCAN_PUBLIC swiftscan_string_ref_t swiftscan_swift_binary_detail_get_header_dependency( swiftscan_module_details_t details); +SWIFTSCAN_PUBLIC swiftscan_string_set_t * +swiftscan_swift_binary_detail_get_header_dependency_module_dependencies( + swiftscan_module_details_t details); + SWIFTSCAN_PUBLIC bool swiftscan_swift_binary_detail_get_is_framework( swiftscan_module_details_t details); diff --git a/tools/libSwiftScan/libSwiftScan.cpp b/tools/libSwiftScan/libSwiftScan.cpp index ec13d7fd67f60..1838d8bc5fb07 100644 --- a/tools/libSwiftScan/libSwiftScan.cpp +++ b/tools/libSwiftScan/libSwiftScan.cpp @@ -365,6 +365,12 @@ swiftscan_swift_binary_detail_get_header_dependency( return details->swift_binary_details.header_dependency; } +swiftscan_string_set_t * +swiftscan_swift_binary_detail_get_header_dependency_module_dependencies( + swiftscan_module_details_t details) { + return details->swift_binary_details.header_dependencies_module_dependnecies; +} + bool swiftscan_swift_binary_detail_get_is_framework( swiftscan_module_details_t details) { return details->swift_binary_details.is_framework; diff --git a/tools/libSwiftScan/libSwiftScan.exports b/tools/libSwiftScan/libSwiftScan.exports index 0f5045ab0bcbb..af002619c7de5 100644 --- a/tools/libSwiftScan/libSwiftScan.exports +++ b/tools/libSwiftScan/libSwiftScan.exports @@ -25,6 +25,7 @@ swiftscan_swift_binary_detail_get_module_doc_path swiftscan_swift_binary_detail_get_module_source_info_path swiftscan_swift_binary_detail_get_swift_overlay_dependencies swiftscan_swift_binary_detail_get_header_dependency +swiftscan_swift_binary_detail_get_header_dependency_module_dependencies swiftscan_swift_binary_detail_get_is_framework swiftscan_swift_binary_detail_get_module_cache_key swiftscan_swift_placeholder_detail_get_compiled_module_path From f3816e033517717109c09e4abc4a2fa14312207f Mon Sep 17 00:00:00 2001 From: Artem Chikin Date: Thu, 28 Mar 2024 13:05:50 -0700 Subject: [PATCH 2/2] [Explicit Module Builds] Only specify '-fmodule-map-file' for bridging header Clang module dependencies Relying on the corresponding field in the '-explicit-swift-module-map-file' provided by the driver. Only bridging headers require a module map because that's what aids header include resolution. With lazy module loading today, '.modulemap' parsing which happens when instantiating Clang is responsible for associating headers with modules. Then upon encountering a header include inside the bridging header the compiler knows which module corresponds to said header and is then able to load explicitly-provided PCM for that module. For all other module dependencies, they are only ever queried by-name from Swift, so '.modulemap' parsing is not necessary. --- include/swift/AST/SearchPathOptions.h | 2 +- .../swift/Frontend/ModuleInterfaceLoader.h | 14 ++++- lib/Frontend/CompilerInvocation.cpp | 2 +- lib/Frontend/Frontend.cpp | 6 +-- lib/Frontend/ModuleInterfaceLoader.cpp | 5 +- .../bridging_header_modulemap_only.swift | 51 +++++++++++++++++++ tools/swift-ide-test/swift-ide-test.cpp | 2 +- 7 files changed, 73 insertions(+), 9 deletions(-) create mode 100644 test/ScanDependencies/bridging_header_modulemap_only.swift diff --git a/include/swift/AST/SearchPathOptions.h b/include/swift/AST/SearchPathOptions.h index bdd6706fb0761..14ed706c67a95 100644 --- a/include/swift/AST/SearchPathOptions.h +++ b/include/swift/AST/SearchPathOptions.h @@ -476,7 +476,7 @@ class SearchPathOptions { std::vector CandidateCompiledModules; /// A map of explicit Swift module information. - std::string ExplicitSwiftModuleMap; + std::string ExplicitSwiftModuleMapPath; /// Module inputs specified with -swift-module-input, /// diff --git a/include/swift/Frontend/ModuleInterfaceLoader.h b/include/swift/Frontend/ModuleInterfaceLoader.h index d4df4f0f68ac4..8b2c6449f7004 100644 --- a/include/swift/Frontend/ModuleInterfaceLoader.h +++ b/include/swift/Frontend/ModuleInterfaceLoader.h @@ -269,9 +269,11 @@ struct ExplicitClangModuleInputInfo { ExplicitClangModuleInputInfo( std::string moduleMapPath, std::string modulePath, bool isFramework = false, bool isSystem = false, + bool isBridgingHeaderDependency = true, std::optional moduleCacheKey = std::nullopt) : moduleMapPath(moduleMapPath), modulePath(modulePath), isFramework(isFramework), isSystem(isSystem), + isBridgingHeaderDependency(isBridgingHeaderDependency), moduleCacheKey(moduleCacheKey) {} // Path of the Clang module map file. std::string moduleMapPath; @@ -281,6 +283,8 @@ struct ExplicitClangModuleInputInfo { bool isFramework = false; // A flag that indicates whether this module is a system module bool isSystem = false; + // A flag that indicates whether this is a module dependency of a textual header input + bool isBridgingHeaderDependency = true; // The cache key for clang module. std::optional moduleCacheKey; }; @@ -367,7 +371,12 @@ class ExplicitModuleMapParser { swiftModuleSourceInfoPath, swiftModuleCacheKey, clangModuleCacheKey; std::optional> headerDependencyPaths; std::string clangModuleMapPath = "", clangModulePath = ""; - bool isFramework = false, isSystem = false; + bool isFramework = false, isSystem = false, + // The default value is 'true' in case the build system does not yet + // support emitting this field, in which case we must be conservative and + // ensure all dependencies get '-fmodule-map-file', instead of strictly + // module dependencies of textual header inputs. + isBridgingHeaderDependency = true; for (auto &entry : *mapNode) { auto key = getScalaNodeText(entry.getKey()); if (key == "prebuiltHeaderDependencyPaths") { @@ -394,6 +403,8 @@ class ExplicitModuleMapParser { swiftModuleCacheKey = val.str(); } else if (key == "clangModuleCacheKey") { clangModuleCacheKey = val.str(); + } else if (key == "isBridgingHeaderDependency") { + isBridgingHeaderDependency = parseBoolValue(val); } else { // Being forgiving for future fields. continue; @@ -423,6 +434,7 @@ class ExplicitModuleMapParser { clangModulePath, isFramework, isSystem, + isBridgingHeaderDependency, clangModuleCacheKey); clangModuleMap.try_emplace(moduleName, std::move(entry)); } diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp index 7a439b73b45bb..124b57c0d9b17 100644 --- a/lib/Frontend/CompilerInvocation.cpp +++ b/lib/Frontend/CompilerInvocation.cpp @@ -1981,7 +1981,7 @@ static bool ParseSearchPathArgs(SearchPathOptions &Opts, Args.hasArg(OPT_disable_modules_validate_system_headers); if (const Arg *A = Args.getLastArg(OPT_explicit_swift_module_map)) - Opts.ExplicitSwiftModuleMap = A->getValue(); + Opts.ExplicitSwiftModuleMapPath = A->getValue(); for (auto A : Args.getAllArgValues(options::OPT_swift_module_file)) { if (validateSwiftModuleFileArgumentAndAdd(A, Diags, Opts.ExplicitSwiftModuleInputs)) diff --git a/lib/Frontend/Frontend.cpp b/lib/Frontend/Frontend.cpp index 7b8b3c2d39d3d..5915f975d7d13 100644 --- a/lib/Frontend/Frontend.cpp +++ b/lib/Frontend/Frontend.cpp @@ -747,18 +747,18 @@ bool CompilerInstance::setUpModuleLoaders() { bool ExplicitModuleBuild = Invocation.getFrontendOptions().DisableImplicitModules; if (ExplicitModuleBuild || - !Invocation.getSearchPathOptions().ExplicitSwiftModuleMap.empty() || + !Invocation.getSearchPathOptions().ExplicitSwiftModuleMapPath.empty() || !Invocation.getSearchPathOptions().ExplicitSwiftModuleInputs.empty()) { if (Invocation.getCASOptions().EnableCaching) ESML = ExplicitCASModuleLoader::create( *Context, getObjectStore(), getActionCache(), getDependencyTracker(), - MLM, Invocation.getSearchPathOptions().ExplicitSwiftModuleMap, + MLM, Invocation.getSearchPathOptions().ExplicitSwiftModuleMapPath, Invocation.getSearchPathOptions().ExplicitSwiftModuleInputs, IgnoreSourceInfoFile); else ESML = ExplicitSwiftModuleLoader::create( *Context, getDependencyTracker(), MLM, - Invocation.getSearchPathOptions().ExplicitSwiftModuleMap, + Invocation.getSearchPathOptions().ExplicitSwiftModuleMapPath, Invocation.getSearchPathOptions().ExplicitSwiftModuleInputs, IgnoreSourceInfoFile); } diff --git a/lib/Frontend/ModuleInterfaceLoader.cpp b/lib/Frontend/ModuleInterfaceLoader.cpp index e4d1bb65a30c3..882e7077388dd 100644 --- a/lib/Frontend/ModuleInterfaceLoader.cpp +++ b/lib/Frontend/ModuleInterfaceLoader.cpp @@ -1847,8 +1847,8 @@ InterfaceSubContextDelegateImpl::InterfaceSubContextDelegateImpl( } // Pass down -explicit-swift-module-map-file - StringRef explicitSwiftModuleMap = searchPathOpts.ExplicitSwiftModuleMap; - genericSubInvocation.getSearchPathOptions().ExplicitSwiftModuleMap = + StringRef explicitSwiftModuleMap = searchPathOpts.ExplicitSwiftModuleMapPath; + genericSubInvocation.getSearchPathOptions().ExplicitSwiftModuleMapPath = explicitSwiftModuleMap.str(); // Pass down VFSOverlay flags (do not need to inherit the options because @@ -2188,6 +2188,7 @@ struct ExplicitSwiftModuleLoader::Implementation { for (auto &entry : ExplicitClangModuleMap) { const auto &moduleMapPath = entry.getValue().moduleMapPath; if (!moduleMapPath.empty() && + entry.getValue().isBridgingHeaderDependency && moduleMapsSeen.find(moduleMapPath) == moduleMapsSeen.end()) { moduleMapsSeen.insert(moduleMapPath); extraClangArgs.push_back( diff --git a/test/ScanDependencies/bridging_header_modulemap_only.swift b/test/ScanDependencies/bridging_header_modulemap_only.swift new file mode 100644 index 0000000000000..fcf6640ad8dc3 --- /dev/null +++ b/test/ScanDependencies/bridging_header_modulemap_only.swift @@ -0,0 +1,51 @@ +// REQUIRES: objc_interop +// RUN: %empty-directory(%t) +// RUN: %empty-directory(%t/TestInputs) +// RUN: split-file %s %t + +// - Fixup the input module file map +// RUN: sed -e "s|INPUTSDIR|%/t/TestInputs|g" %t/map.json.template > %t/map.json.template1 +// RUN: sed -e "s|STDLIBMOD|%/stdlib_module|g" %t/map.json.template1 > %t/map.json.template2 +// RUN: sed -e "s|ONONEMOD|%/ononesupport_module|g" %t/map.json.template2 > %t/map.json.template3 +// RUN: sed -e "s|CHEADERSDIR|%/S/Inputs/CHeaders|g" %t/map.json.template3 > %t/map.json.template4 +// RUN: sed -e "s|SWIFTLIBDIR|%swift-lib-dir|g" %t/map.json.template4 > %t/map.json + +// - Pre-compile explicit module dependency inputs +// RUN: %target-swift-emit-pcm -module-name A -o %t/TestInputs/A.pcm %S/Inputs/CHeaders/module.modulemap +// RUN: %target-swift-emit-pcm -module-name SwiftShims %swift-lib-dir/swift/shims/module.modulemap -o %t/TestInputs/SwiftShims.pcm + +// RUN: %target-swift-frontend -c -disable-implicit-swift-modules -disable-implicit-concurrency-module-import -disable-implicit-string-processing-module-import -explicit-swift-module-map-file %t/map.json -primary-file %t/bridging_header_modulemap_only.swift -o %t/bridging_header_modulemap_only.o -dump-clang-diagnostics 2>&1 | %FileCheck %s --check-prefix=CHECK-CLANG-COMMAND + +//--- map.json.template +[ + { + "moduleName": "Swift", + "modulePath": "STDLIBMOD", + "isFramework": false + }, + { + "moduleName": "SwiftOnoneSupport", + "modulePath": "ONONEMOD", + "isFramework": false + }, + { + "moduleName": "SwiftShims", + "isFramework": false, + "isBridgingHeaderDependency": false, + "clangModuleMapPath": "SWIFTLIBDIR/swift/shims/module.modulemap", + "clangModulePath": "INPUTSDIR/SwiftShims.pcm" + }, + { + "moduleName": "A", + "isFramework": false, + "isBridgingHeaderDependency": true, + "clangModulePath": "INPUTSDIR/A.pcm", + "clangModuleMapPath": "CHEADERSDIR/module.modulemap" + } +] + +//--- bridging_header_modulemap_only.swift +import A + +// CHECK-CLANG-COMMAND: -fmodule-map-file={{.*}}{{/|\\}}CHeaders{{/|\\}}module.modulemap +// CHECK-CLANG-COMMAND-NOT: -fmodule-map-file={{.*}}{{/|\\}}swift{{/|\\}}shims{{/|\\}}module.modulemap diff --git a/tools/swift-ide-test/swift-ide-test.cpp b/tools/swift-ide-test/swift-ide-test.cpp index 397943ba2974c..7029374d13032 100644 --- a/tools/swift-ide-test/swift-ide-test.cpp +++ b/tools/swift-ide-test/swift-ide-test.cpp @@ -4519,7 +4519,7 @@ int main(int argc, char *argv[]) { InitInvok.getLangOptions().addCustomConditionalCompilationFlag(ConfigName); if (!options::ExplicitSwiftModuleMap.empty()) { - InitInvok.getSearchPathOptions().ExplicitSwiftModuleMap = + InitInvok.getSearchPathOptions().ExplicitSwiftModuleMapPath = options::ExplicitSwiftModuleMap; InitInvok.getFrontendOptions().DisableImplicitModules = true; }