From 3c90ae7b5dc19f2c57061267f5e33c55fcbc5aa6 Mon Sep 17 00:00:00 2001 From: Mauricio Garcia Date: Mon, 1 Aug 2022 13:24:59 -0700 Subject: [PATCH 1/2] Add support for importing Swift XCFrameworks through Swift interface files Previously, support for importing Swift based XCFrameworks was limited to Swift interoperability with Objective-C targets through imported Clang module map files. This change adopts recently introduced swift_common API to compile Swift module interface files imported by XCFrameworks. (cherry picked from commit 3ed9049c0fd7454c4b806ca200962d41c51718a0) --- apple/internal/apple_xcframework_import.bzl | 120 +++++++++++++----- apple/internal/framework_import_support.bzl | 72 +++++++++++ ...apple_dynamic_xcframework_import_tests.bzl | 37 ++++++ .../apple_static_xcframework_import_tests.bzl | 41 ++++++ .../targets_under_test/apple/BUILD | 1 + .../xcframeworks/generate_xcframework.bzl | 6 +- .../xcframework_processor_tool.py | 4 +- 7 files changed, 247 insertions(+), 34 deletions(-) diff --git a/apple/internal/apple_xcframework_import.bzl b/apple/internal/apple_xcframework_import.bzl index e39a9699b..e4ef14675 100644 --- a/apple/internal/apple_xcframework_import.bzl +++ b/apple/internal/apple_xcframework_import.bzl @@ -195,19 +195,19 @@ def _get_xcframework_library_from_paths(*, target_triplet, xcframework): headers = filter_by_library_identifier(files_by_category.header_imports) module_maps = filter_by_library_identifier(files_by_category.module_map_imports) - swiftmodules = [ - f - for f in files_by_category.swift_module_imports - if _matches_library(f) and - f.basename.startswith(target_triplet.architecture) - ] + swiftmodules = framework_import_support.filter_swift_module_files_for_architecture( + architecture = target_triplet.architecture, + swift_module_files = filter_by_library_identifier( + files_by_category.swift_module_imports, + ), + ) - swift_module_interfaces = [ - f - for f in files_by_category.swift_interface_imports - if _matches_library(f) and - f.basename.startswith(target_triplet.architecture) - ] + swift_module_interfaces = framework_import_support.filter_swift_module_files_for_architecture( + architecture = target_triplet.architecture, + swift_module_files = filter_by_library_identifier( + files_by_category.swift_interface_imports, + ), + ) framework_files = filter_by_library_identifier(xcframework.files) @@ -281,8 +281,9 @@ def _get_xcframework_library_with_xcframework_processor( dir_name = paths.join(library_path, "Headers"), **intermediates_common ) + modules_dir_path = paths.join(library_path, "Modules") module_map_file = intermediates.file( - file_name = paths.join(library_path, "Modules", "module.modulemap"), + file_name = paths.join(modules_dir_path, "module.modulemap"), **intermediates_common ) @@ -315,6 +316,30 @@ def _get_xcframework_library_with_xcframework_processor( module_map_file, ] + swiftinterface_file = None + if files_by_category.swift_interface_imports: + swiftinterface_path = paths.join( + modules_dir_path, + "{module_name}.swiftmodule".format( + module_name = xcframework.bundle_name, + ), + "{architecture}.swiftinterface".format( + architecture = target_triplet.architecture, + ), + ) + swiftinterface_file = intermediates.file( + file_name = swiftinterface_path, + **intermediates_common + ) + args.add_all( + framework_import_support.filter_swift_module_files_for_architecture( + architecture = target_triplet.architecture, + swift_module_files = files_by_category.swift_interface_imports, + ), + before_each = "--swiftinterface_file", + ) + outputs.append(swiftinterface_file) + xcframework_processor_tool = apple_mac_toolchain_info.resolved_xcframework_processor_tool apple_support.run( @@ -347,7 +372,7 @@ def _get_xcframework_library_with_xcframework_processor( includes = includes, clang_module_map = module_map_file, swiftmodule = [], - swift_module_interface = None, + swift_module_interface = swiftinterface_file, framework_files = [], ) @@ -473,14 +498,30 @@ def _apple_dynamic_xcframework_import_impl(ctx): ) providers.append(apple_dynamic_framework_info) - # Create _SwiftInteropInfo provider if applicable - swift_interop_info = framework_import_support.swift_interop_info_with_dependencies( - deps = deps, - module_name = xcframework.bundle_name, - module_map_imports = [xcframework_library.clang_module_map], - ) - if swift_interop_info: - providers.append(swift_interop_info) + if xcframework_library.swift_module_interface: + # Create SwiftInfo provider + swift_toolchain = swift_common.get_toolchain(ctx, "_swift_toolchain") + providers.append( + framework_import_support.swift_info_from_module_interface( + actions = actions, + ctx = ctx, + deps = deps, + disabled_features = disabled_features, + features = features, + module_name = xcframework.bundle_name, + swift_toolchain = swift_toolchain, + swiftinterface_file = xcframework_library.swift_module_interface, + ), + ) + else: + # Create SwiftInteropInfo provider for swift_clang_module_aspect + swift_interop_info = framework_import_support.swift_interop_info_with_dependencies( + deps = deps, + module_name = xcframework.bundle_name, + module_map_imports = [xcframework_library.clang_module_map], + ) + if swift_interop_info: + providers.append(swift_interop_info) return providers @@ -595,14 +636,30 @@ def _apple_static_xcframework_import_impl(ctx): ) providers.append(cc_info) - # Create _SwiftInteropInfo provider if applicable - swift_interop_info = framework_import_support.swift_interop_info_with_dependencies( - deps = deps, - module_name = xcframework.bundle_name, - module_map_imports = [xcframework_library.clang_module_map], - ) - if swift_interop_info: - providers.append(swift_interop_info) + if xcframework_library.swift_module_interface: + # Create SwiftInfo provider + swift_toolchain = swift_common.get_toolchain(ctx, "_swift_toolchain") + providers.append( + framework_import_support.swift_info_from_module_interface( + actions = actions, + ctx = ctx, + deps = deps, + disabled_features = disabled_features, + features = features, + module_name = xcframework.bundle_name, + swift_toolchain = swift_toolchain, + swiftinterface_file = xcframework_library.swift_module_interface, + ), + ) + else: + # Create SwiftInteropInfo provider for swift_clang_module_aspect + swift_interop_info = framework_import_support.swift_interop_info_with_dependencies( + deps = deps, + module_name = xcframework.bundle_name, + module_map_imports = [xcframework_library.clang_module_map], + ) + if swift_interop_info: + providers.append(swift_interop_info) # Create AppleFrameworkImportBundleInfo provider. bundle_files = [x for x in xcframework_library.framework_files if ".bundle/" in x.short_path] @@ -637,6 +694,7 @@ objc_library( implementation = _apple_dynamic_xcframework_import_impl, attrs = dicts.add( rule_factory.common_tool_attributes, + swift_common.toolchain_attrs(toolchain_attr_name = "_swift_toolchain"), { "xcframework_imports": attr.label_list( allow_empty = False, @@ -711,7 +769,7 @@ objc_library( implementation = _apple_static_xcframework_import_impl, attrs = dicts.add( rule_factory.common_tool_attributes, - swift_common.toolchain_attrs(), + swift_common.toolchain_attrs(toolchain_attr_name = "_swift_toolchain"), { "alwayslink": attr.bool( default = False, diff --git a/apple/internal/framework_import_support.bzl b/apple/internal/framework_import_support.bzl index 36a54bc6b..b217ac4cc 100644 --- a/apple/internal/framework_import_support.bzl +++ b/apple/internal/framework_import_support.bzl @@ -320,6 +320,26 @@ def _libraries_to_link_for_static_framework( return libraries_to_link +def _filter_swift_module_files_for_architecture(architecture, swift_module_files): + """Filters Swift module files for a given architecture. + + Traverses a list of Swift module files (.swiftdoc, .swiftinterface, .swiftmodule) and selects + the effective files based on target architecture and file's basename without extension. + + Args: + architecture: Effective target architecture (e.g. 'x86_64', 'arm64'). + swift_module_files: List of Swift module files to filter using architecture. + Returns: + List of Swift module files for given architecture. + """ + files = [] + for file in swift_module_files: + filename, _ = paths.split_extension(file.basename) + if filename == architecture: + files.append(file) + + return files + def _framework_import_info_with_dependencies( *, build_archs, @@ -407,6 +427,56 @@ def _objc_provider_with_dependencies( objc_provider_fields.update(**additional_objc_provider_fields) return apple_common.new_objc_provider(**objc_provider_fields) +def _swift_info_from_module_interface( + *, + actions, + ctx, + deps, + disabled_features, + features, + module_name, + swift_toolchain, + swiftinterface_file): + """Returns SwiftInfo provider for a pre-compiled Swift module compiling it's interface file. + + + Args: + actions: The actions provider from `ctx.actions`. + ctx: The Starlark context for a rule target being built. + deps: List of dependencies for a given target to retrieve transitive CcInfo providers. + disabled_features: List of features to be disabled for cc_common.compile + features: List of features to be enabled for cc_common.compile. + module_name: Swift module name. + swift_toolchain: SwiftToolchainInfo provider for current target. + swiftinterface_file: `.swiftinterface` File to compile. + Returns: + A SwiftInfo provider. + """ + swift_infos = [dep[SwiftInfo] for dep in deps if SwiftInfo in dep] + module_context = swift_common.compile_module_interface( + actions = actions, + compilation_contexts = [ + dep[CcInfo].compilation_context + for dep in deps + if CcInfo in dep + ], + feature_configuration = swift_common.configure_features( + ctx = ctx, + swift_toolchain = swift_toolchain, + requested_features = features, + unsupported_features = disabled_features, + ), + module_name = module_name, + swiftinterface_file = swiftinterface_file, + swift_infos = swift_infos, + swift_toolchain = swift_toolchain, + ) + + return swift_common.create_swift_info( + modules = [module_context], + swift_infos = swift_infos, + ) + def _swift_interop_info_with_dependencies(deps, module_name, module_map_imports): """Return a Swift interop provider for the framework if it has a module map.""" if not module_map_imports: @@ -425,7 +495,9 @@ framework_import_support = struct( cc_info_with_dependencies = _cc_info_with_dependencies, classify_file_imports = _classify_file_imports, classify_framework_imports = _classify_framework_imports, + filter_swift_module_files_for_architecture = _filter_swift_module_files_for_architecture, framework_import_info_with_dependencies = _framework_import_info_with_dependencies, objc_provider_with_dependencies = _objc_provider_with_dependencies, + swift_info_from_module_interface = _swift_info_from_module_interface, swift_interop_info_with_dependencies = _swift_interop_info_with_dependencies, ) diff --git a/test/starlark_tests/apple_dynamic_xcframework_import_tests.bzl b/test/starlark_tests/apple_dynamic_xcframework_import_tests.bzl index d4bea814f..c1b081731 100644 --- a/test/starlark_tests/apple_dynamic_xcframework_import_tests.bzl +++ b/test/starlark_tests/apple_dynamic_xcframework_import_tests.bzl @@ -81,6 +81,24 @@ def apple_dynamic_xcframework_import_test_suite(name): ], tags = [name], ) + archive_contents_test( + name = "{}_swift_contains_imported_swift_xcframework_framework_files".format(name), + build_type = "simulator", + target_under_test = "//test/starlark_tests/targets_under_test/ios:swift_app_with_imported_swift_xcframework", + contains = [ + "$BUNDLE_ROOT/Frameworks/SwiftFmwkWithGenHeader.framework/Info.plist", + "$BUNDLE_ROOT/Frameworks/SwiftFmwkWithGenHeader.framework/SwiftFmwkWithGenHeader", + ], + not_contains = [ + "$BUNDLE_ROOT/Frameworks/SwiftFmwkWithGenHeader.framework/Headers/", + "$BUNDLE_ROOT/Frameworks/SwiftFmwkWithGenHeader.framework/Modules/", + ], + binary_test_file = "$BINARY", + macho_load_commands_contain = [ + "name @rpath/SwiftFmwkWithGenHeader.framework/SwiftFmwkWithGenHeader (offset 24)", + ], + tags = [name], + ) # Verify the correct XCFramework library was bundled and sliced for the required architecture. binary_contents_test( @@ -163,6 +181,25 @@ def apple_dynamic_xcframework_import_test_suite(name): target_features = ["apple.parse_xcframework_info_plist"], tags = [name], ) + archive_contents_test( + name = "{}_swift_contains_imported_swift_xcframework_framework_files_with_xcframework_import_tool".format(name), + build_type = "simulator", + target_under_test = "//test/starlark_tests/targets_under_test/ios:swift_app_with_imported_swift_xcframework", + contains = [ + "$BUNDLE_ROOT/Frameworks/SwiftFmwkWithGenHeader.framework/Info.plist", + "$BUNDLE_ROOT/Frameworks/SwiftFmwkWithGenHeader.framework/SwiftFmwkWithGenHeader", + ], + not_contains = [ + "$BUNDLE_ROOT/Frameworks/SwiftFmwkWithGenHeader.framework/Headers/", + "$BUNDLE_ROOT/Frameworks/SwiftFmwkWithGenHeader.framework/Modules/", + ], + binary_test_file = "$BINARY", + macho_load_commands_contain = [ + "name @rpath/SwiftFmwkWithGenHeader.framework/SwiftFmwkWithGenHeader (offset 24)", + ], + target_features = ["apple.parse_xcframework_info_plist"], + tags = [name], + ) # Verify importing XCFramework with dynamic libraries (i.e. not Apple frameworks) fails. analysis_failure_message_test( diff --git a/test/starlark_tests/apple_static_xcframework_import_tests.bzl b/test/starlark_tests/apple_static_xcframework_import_tests.bzl index 878c896e6..fda7b119f 100644 --- a/test/starlark_tests/apple_static_xcframework_import_tests.bzl +++ b/test/starlark_tests/apple_static_xcframework_import_tests.bzl @@ -93,6 +93,18 @@ def apple_static_xcframework_import_test_suite(name): contains = ["$BUNDLE_ROOT/Frameworks/libswiftCore.dylib"], tags = [name], ) + archive_contents_test( + name = "{}_swift_with_imported_swift_static_fmwk_contains_symbols_and_bundles_swift_std_libraries".format(name), + build_type = "simulator", + target_under_test = "//test/starlark_tests/targets_under_test/ios:swift_app_with_imported_swift_xcframework_with_static_library", + binary_test_file = "$BINARY", + binary_test_architecture = "x86_64", + binary_contains_symbols = [ + "_OBJC_CLASS_$__TtC34generated_swift_static_xcframework11SharedClass", + ], + contains = ["$BUNDLE_ROOT/Frameworks/libswiftCore.dylib"], + tags = [name], + ) # Verify Swift standard libraries are bundled for an imported XCFramework that has a Swift # static library containing no module interface files (.swiftmodule directory) and where the @@ -110,6 +122,35 @@ def apple_static_xcframework_import_test_suite(name): tags = [name], ) + # Verify ios_application bundles Framework files when using xcframework_processor_tool. + archive_contents_test( + name = "{}_ios_application_with_imported_static_xcframework_includes_symbols_with_xcframework_import_tool".format(name), + build_type = "simulator", + target_under_test = "//test/starlark_tests/targets_under_test/ios:app_with_imported_xcframework_with_static_library", + binary_test_file = "$BINARY", + binary_test_architecture = "x86_64", + binary_contains_symbols = [ + "-[SharedClass doSomethingShared]", + "_OBJC_CLASS_$_SharedClass", + ], + not_contains = ["$BUNDLE_ROOT/Frameworks/"], + target_features = ["apple.parse_xcframework_info_plist"], + tags = [name], + ) + archive_contents_test( + name = "{}_swift_with_imported_swift_static_fmwk_contains_symbols_and_bundles_swift_std_libraries_with_xcframework_import_tool".format(name), + build_type = "simulator", + target_under_test = "//test/starlark_tests/targets_under_test/ios:swift_app_with_imported_swift_xcframework_with_static_library", + binary_test_file = "$BINARY", + binary_test_architecture = "x86_64", + binary_contains_symbols = [ + "_OBJC_CLASS_$__TtC34generated_swift_static_xcframework11SharedClass", + ], + contains = ["$BUNDLE_ROOT/Frameworks/libswiftCore.dylib"], + target_features = ["apple.parse_xcframework_info_plist"], + tags = [name], + ) + native.test_suite( name = name, tags = [name], diff --git a/test/starlark_tests/targets_under_test/apple/BUILD b/test/starlark_tests/targets_under_test/apple/BUILD index 35a8eeb8c..3b81a125e 100644 --- a/test/starlark_tests/targets_under_test/apple/BUILD +++ b/test/starlark_tests/targets_under_test/apple/BUILD @@ -1355,6 +1355,7 @@ apple_static_xcframework_import( apple_static_xcframework_import( name = "ios_imported_swift_static_xcframework", + features = ["-swift.layering_check"], tags = common.fixture_tags, visibility = ["//visibility:public"], xcframework_imports = ["//test/testdata/xcframeworks:generated_swift_static_xcframework"], diff --git a/test/testdata/xcframeworks/generate_xcframework.bzl b/test/testdata/xcframeworks/generate_xcframework.bzl index b4aae27e8..5c0b9850f 100644 --- a/test/testdata/xcframeworks/generate_xcframework.bzl +++ b/test/testdata/xcframeworks/generate_xcframework.bzl @@ -370,8 +370,12 @@ def _generate_static_xcframework_impl(ctx): module_interfaces = [ generation_support.copy_file( actions = actions, - file = interface_file, base_path = swiftmodule_path, + file = interface_file, + target_filename = "{architecture}.{extension}".format( + architecture = architectures[0], + extension = interface_file.extension, + ), ) for interface_file in swift_library if interface_file.extension.startswith("swift") diff --git a/tools/xcframework_processor_tool/xcframework_processor_tool.py b/tools/xcframework_processor_tool/xcframework_processor_tool.py index ca7c16e08..71f79ae49 100644 --- a/tools/xcframework_processor_tool/xcframework_processor_tool.py +++ b/tools/xcframework_processor_tool/xcframework_processor_tool.py @@ -92,7 +92,7 @@ def _create_args_parser() -> argparse.ArgumentParser: optional_list_args = { "bundle_file": "Imported XCFramework bundle file path.", "modulemap_file": "Imported XCFramework modulemap file path.", - "swiftmodule_file": "Imported XCFramework Swift module file path.", + "swiftinterface_file": "Imported XCFramework Swift module file path.", } for arg_name, arg_help in optional_list_args.items(): parser.add_argument( @@ -277,7 +277,7 @@ def main() -> int: _copy_xcframework_files( library_identifier=library_identifier, output_directories=[swiftmodule_dir], - xcframework_files=args.swiftmodule_files) + xcframework_files=args.swiftinterface_files) return 0 From 88c256d1812b8f915187155276e21cf67f6a8ac1 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 11 Aug 2023 12:55:01 -0700 Subject: [PATCH 2/2] compat --- apple/internal/apple_xcframework_import.bzl | 8 ++++---- test/starlark_tests/targets_under_test/ios/BUILD | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/apple/internal/apple_xcframework_import.bzl b/apple/internal/apple_xcframework_import.bzl index e4ef14675..30b20bdde 100644 --- a/apple/internal/apple_xcframework_import.bzl +++ b/apple/internal/apple_xcframework_import.bzl @@ -500,7 +500,7 @@ def _apple_dynamic_xcframework_import_impl(ctx): if xcframework_library.swift_module_interface: # Create SwiftInfo provider - swift_toolchain = swift_common.get_toolchain(ctx, "_swift_toolchain") + swift_toolchain = ctx.attr._toolchain[SwiftToolchainInfo] providers.append( framework_import_support.swift_info_from_module_interface( actions = actions, @@ -638,7 +638,7 @@ def _apple_static_xcframework_import_impl(ctx): if xcframework_library.swift_module_interface: # Create SwiftInfo provider - swift_toolchain = swift_common.get_toolchain(ctx, "_swift_toolchain") + swift_toolchain = ctx.attr._toolchain[SwiftToolchainInfo] providers.append( framework_import_support.swift_info_from_module_interface( actions = actions, @@ -694,7 +694,7 @@ objc_library( implementation = _apple_dynamic_xcframework_import_impl, attrs = dicts.add( rule_factory.common_tool_attributes, - swift_common.toolchain_attrs(toolchain_attr_name = "_swift_toolchain"), + swift_common.toolchain_attrs(), { "xcframework_imports": attr.label_list( allow_empty = False, @@ -769,7 +769,7 @@ objc_library( implementation = _apple_static_xcframework_import_impl, attrs = dicts.add( rule_factory.common_tool_attributes, - swift_common.toolchain_attrs(toolchain_attr_name = "_swift_toolchain"), + swift_common.toolchain_attrs(), { "alwayslink": attr.bool( default = False, diff --git a/test/starlark_tests/targets_under_test/ios/BUILD b/test/starlark_tests/targets_under_test/ios/BUILD index f67b3fdca..30ebf98fd 100644 --- a/test/starlark_tests/targets_under_test/ios/BUILD +++ b/test/starlark_tests/targets_under_test/ios/BUILD @@ -2290,9 +2290,23 @@ swift_library( tags = common.fixture_tags, deps = [ "//test/starlark_tests/targets_under_test/apple:ios_imported_static_xcframework_depends_on_base", + "//test/starlark_tests/targets_under_test/apple:ios_imported_swift_static_xcframework", ], ) +genrule( + name = "static_swift_xcframework_depending_swift_lib_src", + outs = ["SwiftWithStaticSwiftFramework.swift"], + cmd = """cat > $@ <