diff --git a/apple/internal/ios_rules.bzl b/apple/internal/ios_rules.bzl index 64e7b4f73b..03f3c4c4e2 100644 --- a/apple/internal/ios_rules.bzl +++ b/apple/internal/ios_rules.bzl @@ -81,6 +81,7 @@ load( "new_appleframeworkbundleinfo", "new_iosappclipbundleinfo", "new_iosapplicationbundleinfo", + "new_iosbundlebundleinfo", "new_iosextensionbundleinfo", "new_iosframeworkbundleinfo", "new_iosimessageapplicationbundleinfo", @@ -133,6 +134,7 @@ load( "AppleBundleInfo", "ApplePlatformInfo", "IosAppClipBundleInfo", + "IosBundleBundleInfo", "IosExtensionBundleInfo", "IosFrameworkBundleInfo", "IosImessageExtensionBundleInfo", @@ -169,12 +171,13 @@ def _ios_application_impl(ctx): suffix_default = ctx.attr._bundle_id_suffix_default, shared_capabilities = ctx.attr.shared_capabilities, ) - bundle_verification_targets = [struct(target = ext) for ext in ctx.attr.extensions] + bundle_verification_targets = [struct(target = ext) for ext in ctx.attr.extensions + ctx.attr.bundles] cc_toolchain_forwarder = ctx.split_attr._cc_toolchain_forwarder embeddable_targets = ( ctx.attr.frameworks + ctx.attr.extensions + ctx.attr.app_clips + + ctx.attr.bundles + ctx.attr.deps ) executable_name = ctx.attr.executable_name @@ -347,7 +350,7 @@ def _ios_application_impl(ctx): platform_prerequisites = platform_prerequisites, provisioning_profile = provisioning_profile, rule_descriptor = rule_descriptor, - targets = ctx.attr.deps + ctx.attr.extensions + ctx.attr.frameworks, + targets = ctx.attr.bundles + ctx.attr.deps + ctx.attr.extensions + ctx.attr.frameworks, ), partials.resources_partial( actions = actions, @@ -529,7 +532,7 @@ def _ios_app_clip_impl(ctx): suffix_default = ctx.attr._bundle_id_suffix_default, shared_capabilities = ctx.attr.shared_capabilities, ) - embeddable_targets = ctx.attr.frameworks + embeddable_targets = ctx.attr.bundles + ctx.attr.frameworks executable_name = ctx.attr.executable_name features = features_support.compute_enabled_features( requested_features = ctx.features, @@ -797,6 +800,248 @@ def _ios_app_clip_impl(ctx): link_result.debug_outputs_provider, ] + processor_result.providers +def _ios_bundle_impl(ctx): + """Implementation of ios_bundle.""" + product_type = apple_product_type.bundle + rule_descriptor = rule_support.rule_descriptor( + platform_type = ctx.attr.platform_type, + product_type = apple_product_type.bundle, + ) + + extra_linkopts = ["-bundle"] + if ctx.attr.extension_safe: + extra_linkopts.append("-fapplication-extension") + + actions = ctx.actions + apple_mac_toolchain_info = ctx.attr._mac_toolchain[AppleMacToolsToolchainInfo] + apple_xplat_toolchain_info = ctx.attr._xplat_toolchain[AppleXPlatToolsToolchainInfo] + bin_root_path = ctx.bin_dir.path + bundle_name, bundle_extension = bundling_support.bundle_full_name( + custom_bundle_extension = ctx.attr.bundle_extension, + custom_bundle_name = ctx.attr.bundle_name, + label_name = ctx.label.name, + rule_descriptor = rule_descriptor, + ) + bundle_id = bundling_support.bundle_full_id( + bundle_id = ctx.attr.bundle_id, + bundle_id_suffix = ctx.attr.bundle_id_suffix, + bundle_name = bundle_name, + suffix_default = ctx.attr._bundle_id_suffix_default, + shared_capabilities = ctx.attr.shared_capabilities, + ) + embeddable_targets = ctx.attr.frameworks + executable_name = ctx.attr.executable_name + features = features_support.compute_enabled_features( + requested_features = ctx.features, + unsupported_features = ctx.disabled_features, + ) + label = ctx.label + platform_prerequisites = platform_support.platform_prerequisites( + apple_fragment = ctx.fragments.apple, + build_settings = apple_xplat_toolchain_info.build_settings, + config_vars = ctx.var, + cpp_fragment = ctx.fragments.cpp, + device_families = rule_descriptor.allowed_device_families, + explicit_minimum_deployment_os = ctx.attr.minimum_deployment_os_version, + explicit_minimum_os = ctx.attr.minimum_os_version, + features = features, + objc_fragment = ctx.fragments.objc, + platform_type_string = ctx.attr.platform_type, + uses_swift = swift_support.uses_swift(ctx.attr.deps), + xcode_version_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig], + ) + predeclared_outputs = ctx.outputs + provisioning_profile = ctx.file.provisioning_profile + resource_deps = ctx.attr.deps + ctx.attr.resources + top_level_infoplists = resources.collect( + attr = ctx.attr, + res_attrs = ["infoplists"], + ) + top_level_resources = resources.collect( + attr = ctx.attr, + res_attrs = [ + "app_icons", + "strings", + "resources", + ], + ) + + entitlements = entitlements_support.process_entitlements( + actions = actions, + apple_mac_toolchain_info = apple_mac_toolchain_info, + bundle_id = bundle_id, + entitlements_file = ctx.file.entitlements, + platform_prerequisites = platform_prerequisites, + product_type = rule_descriptor.product_type, + provisioning_profile = provisioning_profile, + rule_label = label, + validation_mode = ctx.attr.entitlements_validation, + ) + + link_result = linking_support.register_binary_linking_action( + ctx, + avoid_deps = ctx.attr.frameworks, + bundle_loader = ctx.attr.bundle_loader, + entitlements = entitlements.linking, + exported_symbols_lists = ctx.files.exported_symbols_lists, + extra_linkopts = extra_linkopts, + platform_prerequisites = platform_prerequisites, + stamp = ctx.attr.stamp, + ) + binary_artifact = link_result.binary + debug_outputs = linking_support.debug_outputs_by_architecture(link_result.outputs) + + archive = outputs.archive( + actions = actions, + bundle_extension = bundle_extension, + bundle_name = bundle_name, + label_name = label.name, + platform_prerequisites = platform_prerequisites, + predeclared_outputs = predeclared_outputs, + rule_descriptor = rule_descriptor, + ) + + processor_partials = [ + partials.apple_bundle_info_partial( + actions = actions, + bundle_extension = bundle_extension, + bundle_id = bundle_id, + bundle_name = bundle_name, + executable_name = executable_name, + entitlements = entitlements.bundle, + label_name = label.name, + platform_prerequisites = platform_prerequisites, + predeclared_outputs = predeclared_outputs, + product_type = rule_descriptor.product_type, + rule_descriptor = rule_descriptor, + ), + partials.binary_partial( + actions = actions, + binary_artifact = binary_artifact, + bundle_name = bundle_name, + executable_name = executable_name, + label_name = label.name, + ), + partials.clang_rt_dylibs_partial( + actions = actions, + apple_mac_toolchain_info = apple_mac_toolchain_info, + binary_artifact = binary_artifact, + features = features, + label_name = label.name, + platform_prerequisites = platform_prerequisites, + dylibs = clang_rt_dylibs.get_from_toolchain(ctx), + ), + partials.codesigning_dossier_partial( + actions = actions, + apple_mac_toolchain_info = apple_mac_toolchain_info, + apple_xplat_toolchain_info = apple_xplat_toolchain_info, + bundle_extension = bundle_extension, + bundle_location = processor.location.plugin, + bundle_name = bundle_name, + embed_target_dossiers = False, + label_name = label.name, + platform_prerequisites = platform_prerequisites, + predeclared_outputs = predeclared_outputs, + provisioning_profile = provisioning_profile, + rule_descriptor = rule_descriptor, + ), + partials.debug_symbols_partial( + actions = actions, + bundle_extension = bundle_extension, + bundle_name = bundle_name, + debug_dependencies = ctx.attr.frameworks, + dsym_binaries = debug_outputs.dsym_binaries, + dsym_info_plist_template = apple_mac_toolchain_info.dsym_info_plist_template, + executable_name = executable_name, + label_name = label.name, + linkmaps = debug_outputs.linkmaps, + platform_prerequisites = platform_prerequisites, + resolved_plisttool = apple_mac_toolchain_info.resolved_plisttool, + rule_label = label, + version = ctx.attr.version, + ), + partials.embedded_bundles_partial( + platform_prerequisites = platform_prerequisites, + embeddable_targets = embeddable_targets, + custom_bundles = {getattr(ctx.attr, "bundle_location", ""): [archive]}, + ), + partials.extension_safe_validation_partial( + is_extension_safe = ctx.attr.extension_safe, + rule_label = label, + targets_to_validate = ctx.attr.frameworks, + ), + partials.resources_partial( + actions = actions, + apple_mac_toolchain_info = apple_mac_toolchain_info, + bundle_extension = bundle_extension, + bundle_id = bundle_id, + bundle_name = bundle_name, + environment_plist = ctx.file._environment_plist, + executable_name = executable_name, + launch_storyboard = None, + platform_prerequisites = platform_prerequisites, + resource_deps = resource_deps, + rule_descriptor = rule_descriptor, + rule_label = label, + targets_to_avoid = ctx.attr.frameworks, + top_level_infoplists = top_level_infoplists, + top_level_resources = top_level_resources, + version = ctx.attr.version, + ), + partials.swift_dylibs_partial( + actions = actions, + apple_mac_toolchain_info = apple_mac_toolchain_info, + binary_artifact = binary_artifact, + dependency_targets = ctx.attr.frameworks, + label_name = label.name, + platform_prerequisites = platform_prerequisites, + ), + ] + + if provisioning_profile: + processor_partials.append( + partials.provisioning_profile_partial( + actions = actions, + profile_artifact = provisioning_profile, + rule_label = label, + ), + ) + + processor_result = processor.process( + actions = actions, + apple_mac_toolchain_info = apple_mac_toolchain_info, + apple_xplat_toolchain_info = apple_xplat_toolchain_info, + bundle_extension = bundle_extension, + bundle_name = bundle_name, + codesign_inputs = ctx.files.codesign_inputs, + codesignopts = codesigning_support.codesignopts_from_rule_ctx(ctx), + entitlements = entitlements.codesigning, + features = features, + ipa_post_processor = ctx.executable.ipa_post_processor, + partials = processor_partials, + platform_prerequisites = platform_prerequisites, + predeclared_outputs = predeclared_outputs, + process_and_sign_template = apple_mac_toolchain_info.process_and_sign_template, + provisioning_profile = provisioning_profile, + rule_descriptor = rule_descriptor, + rule_label = label, + ) + + return [ + DefaultInfo( + files = processor_result.output_files + ), + new_iosbundlebundleinfo(), + OutputGroupInfo( + **outputs.merge_output_groups( + link_result.output_groups, + processor_result.output_groups, + ) + ), + # TODO(b/228856372): Remove when downstream users are migrated off this provider. + link_result.debug_outputs_provider, + ] + processor_result.providers + def _ios_framework_impl(ctx): """Experimental implementation of ios_framework.""" rule_descriptor = rule_support.rule_descriptor( @@ -2493,6 +2738,14 @@ named after the alternate icon identifier. providers = [[AppleBundleInfo, IosAppClipBundleInfo]], doc = """ A list of iOS app clips to include in the final application bundle. +""", + ), + "bundles": attr.label_list( + providers = [ + [AppleBundleInfo, IosBundleBundleInfo], + ], + doc = """ +A list of iOS loadable bundles to include in the final application bundle. """, ), "extensions": attr.label_list( @@ -2602,6 +2855,12 @@ A list of framework targets (see that this target depends on. """, ), + "bundles": attr.label_list( + providers = [ + [AppleBundleInfo, IosBundleBundleInfo], + ], + doc = "A list of iOS loadable bundles to include in the final application bundle.", + ), "launch_storyboard": attr.label( allow_single_file = [".storyboard", ".xib"], doc = """ @@ -2615,6 +2874,79 @@ Info.plist under the key `UILaunchStoryboardName`. ], ) +ios_bundle = rule_factory.create_apple_rule( + doc = "Builds and bundles an iOS Loadable Bundle.", + implementation = _ios_bundle_impl, + predeclared_outputs = {"archive": "%{name}.zip"}, + attrs = [ + rule_attrs.app_icon_attrs(), + rule_attrs.binary_linking_attrs( + deps_cfg = transition_support.apple_platform_split_transition, + extra_deps_aspects = [ + apple_resource_aspect, + framework_provider_aspect, + ], + is_test_supporting_rule = False, + requires_legacy_cc_toolchain = True, + ), + rule_attrs.common_bundle_attrs( + deps_cfg = transition_support.apple_platform_split_transition, + ), + rule_attrs.common_tool_attrs(), + rule_attrs.custom_transition_allowlist_attr(), + rule_attrs.device_family_attrs( + allowed_families = rule_attrs.defaults.allowed_families.ios, + is_mandatory = True, + ), + rule_attrs.infoplist_attrs(), + rule_attrs.ipa_post_processor_attrs(), + rule_attrs.platform_attrs( + platform_type = "ios", + add_environment_plist = True, + ), + rule_attrs.signing_attrs( + default_bundle_id_suffix = bundle_id_suffix_default.bundle_name, + ), + { + "bundle_extension": attr.string( + doc = """ +The extension, without a leading dot, that will be used to name the bundle. If this attribute is not +set, then the extension will be `.bundle`. +""", + ), + "bundle_loader": attr.label( + doc = """ +The target representing the executable that will be loading this bundle. Undefined symbols from the +bundle are checked against this execuable during linking as if it were one of the dynamic libraries +the bundle was linked with. +""", + providers = [apple_common.AppleExecutableBinary], + ), + "bundle_location": attr.string( + mandatory = False, + doc = """ +The directory within the packaging bundle that this bundle should be placed. +""", + ), + "extension_safe": attr.bool( + default = False, + doc = """ +If true, compiles and links this framework with `-application-extension`, restricting the binary to +use only extension-safe APIs. +""", + ), + "frameworks": attr.label_list( + providers = [[AppleBundleInfo, IosFrameworkBundleInfo]], + doc = """ +A list of framework targets (see +[`ios_framework`](https://github.com/bazelbuild/rules_apple/blob/master/doc/rules-ios.md#ios_framework)) +that this target depends on. +""", + ), + }, + ], +) + ios_extension = rule_factory.create_apple_rule( doc = """Builds and bundles an iOS Application Extension. diff --git a/apple/internal/partials/embedded_bundles.bzl b/apple/internal/partials/embedded_bundles.bzl index e85ce776a7..d3359f9081 100644 --- a/apple/internal/partials/embedded_bundles.bzl +++ b/apple/internal/partials/embedded_bundles.bzl @@ -22,6 +22,14 @@ load( "@build_bazel_rules_apple//apple/internal:processor.bzl", "processor", ) +load( + "@bazel_skylib//lib:new_sets.bzl", + "sets", +) +load( + "@bazel_skylib//lib:paths.bzl", + "paths", +) load( "@bazel_skylib//lib:partial.bzl", "partial", @@ -57,6 +65,7 @@ def _embedded_bundles_partial_impl( "xpc_services": processor.location.xpc_service, } + config_vars = platform_prerequisites.config_vars transitive_bundles = dict() bundles_to_embed = [] embeddedable_info_fields = {} @@ -102,6 +111,54 @@ def _embedded_bundles_partial_impl( input_bundles_by_type.get(bundle_type, []), transitive = transitive_bundles.get(bundle_type, []), ) + + # Process custom bundle locations + transitive_custom_bundles = dict() + for provider in embeddable_providers: + if hasattr(provider, "custom_bundles"): + custom_bundles = getattr(provider, "custom_bundles") + for bundle_location, input_bundles in custom_bundles.items(): + transitive_custom_bundles.setdefault( + bundle_location, + default = [], + ).append(input_bundles) + + if bundle_embedded_bundles: + # If this partial is configured to embed the transitive embeddable partials, collect + # them into a list to be returned by this partial. + for bundle_location, input_bundles_array in transitive_custom_bundles.items(): + transitive_depset = depset(transitive = input_bundles_array) + + # With tree artifacts, we need to set the parent_dir of the file to be the basename + # of the file. Expanding these depsets shouldn't be too much work as there shouldn't + # be too many embedded targets per top-level bundle. + if is_experimental_tree_artifact_enabled(config_vars = config_vars): + for bundle in transitive_depset.to_list(): + bundles_to_embed.append(( + processor.location.loadable_bundle, + paths.join(bundle_location, bundle.basename), + depset([bundle]) + )) + else: + bundles_to_embed.append((processor.location.loadable_bundle, bundle_location, transitive_depset)) + + # Clear the transitive list of custom bundles since they will be packaged + # in the bundle processing this partial and do not need to be propagated. + transitive_custom_bundles = dict() + + # Construct the _AppleEmbeddableInfo provider field for the bundle type being processed. + # At this step, we inject the bundles that are inputs to this partial, since that propagates + # the info for a higher level bundle to embed this bundle. + direct_custom_bundles = input_bundles_by_type.get("custom_bundles", {}) + bundle_locations = sets.to_list(sets.make(direct_custom_bundles.keys() + transitive_custom_bundles.keys())) + if bundle_locations: + embeddedable_info_fields["custom_bundles"] = { + bundle_location: depset( + direct_custom_bundles.get(bundle_location, []), + transitive = transitive_custom_bundles.get(bundle_location, []), + ) + for bundle_location in bundle_locations + } # Construct the output files fields. If tree artifacts is enabled, propagate the bundles to # package into bundle_files. Otherwise, propagate through bundle_zips so that they can be @@ -148,6 +205,7 @@ def embedded_bundles_partial( *, app_clips = [], bundle_embedded_bundles = False, + custom_bundles = {}, embeddable_targets = [], extensions = [], frameworks = [], @@ -169,6 +227,8 @@ def embedded_bundles_partial( bundle_embedded_bundles: If True, this target will embed all transitive embeddable_bundles _only_ propagated through the targets given in embeddable_targets. If False, the embeddable bundles will be propagated downstream for a top level target to bundle them. + custom_bundles: Dictionary of list of bundles that should be propagated downstream for a + top level target to bundle inside directories named by the dictionary key. embeddable_targets: The list of targets that propagate embeddable bundles to bundle or propagate. extensions: List of ExtensionKit extension bundles that should be propagated downstream for @@ -192,6 +252,7 @@ def embedded_bundles_partial( _embedded_bundles_partial_impl, app_clips = app_clips, bundle_embedded_bundles = bundle_embedded_bundles, + custom_bundles = custom_bundles, embeddable_targets = embeddable_targets, extensions = extensions, frameworks = frameworks, diff --git a/apple/internal/processor.bzl b/apple/internal/processor.bzl index 57804d3328..8b011e4b58 100644 --- a/apple/internal/processor.bzl +++ b/apple/internal/processor.bzl @@ -110,6 +110,7 @@ _LOCATION_ENUM = struct( content = "content", extension = "extension", framework = "framework", + loadable_bundle = "loadable_bundle", plugin = "plugin", resource = "resource", watch = "watch", @@ -205,6 +206,7 @@ def _archive_paths( contents_path, rule_descriptor.bundle_locations.contents_relative_frameworks, ), + _LOCATION_ENUM.loadable_bundle: contents_path, _LOCATION_ENUM.plugin: paths.join( contents_path, rule_descriptor.bundle_locations.contents_relative_plugins, diff --git a/apple/internal/providers.bzl b/apple/internal/providers.bzl index abf65b083d..8bf66a356d 100644 --- a/apple/internal/providers.bzl +++ b/apple/internal/providers.bzl @@ -535,6 +535,20 @@ is an iOS app clip should use this provider to describe that requirement. init = _make_banned_init(provider_name = "IosAppClipBundleInfo"), ) +IosBundleBundleInfo, new_iosbundlebundleinfo = provider( + doc = """ +Denotes that a target is an iOS loadable bundle. + +This provider does not contain any fields of its own at this time but is used as +a "marker" to indicate that a target is specifically an iOS loadable bundle +(and not some other Apple bundle). Rule authors who wish to require that a +dependency is an iOS loadable bundle should use this provider to describe that +requirement. +""", + fields = {}, + init = _make_banned_init(provider_name = "IosBundleBundleInfo"), +) + IosExtensionBundleInfo, new_iosextensionbundleinfo = provider( doc = """ Denotes that a target is an iOS application extension. diff --git a/apple/internal/providers/embeddable_info.bzl b/apple/internal/providers/embeddable_info.bzl index fb64c03165..e5e751e617 100644 --- a/apple/internal/providers/embeddable_info.bzl +++ b/apple/internal/providers/embeddable_info.bzl @@ -18,6 +18,10 @@ _APPLE_EMBEDDABLE_INFO_FIELDS = { "app_clips": """ A depset with the zipped archives of bundles that need to be expanded into the AppClips section of the packaging bundle.""", + "custom_bundles": """ +A dict of depsets of zipped archives of bundles that need to be expanded into +custom locations of the packaging bundle, where the key is the path to the +custom location.""", "extensions": """ A depset with the zipped archives of bundles that need to be expanded into the Extensions section of the packaging bundle.""", diff --git a/apple/internal/rule_support.bzl b/apple/internal/rule_support.bzl index 698ee256c3..d4b03d49f6 100644 --- a/apple/internal/rule_support.bzl +++ b/apple/internal/rule_support.bzl @@ -176,6 +176,19 @@ _RULE_TYPE_DESCRIPTORS = { "@executable_path/Frameworks", ], ), + # ios_bundle + apple_product_type.bundle: _describe_rule_type( + allowed_device_families = ["iphone", "ipad"], + bundle_extension = ".bundle", + bundle_package_type = bundle_package_type.bundle, + product_type = apple_product_type.bundle, + rpaths = [ + # Bundle binaries are loaded from the executable location and application binaries + # live in Application.app/Application + # Frameworks are packaged in Application.app/Frameworks + "@executable_path/Frameworks", + ], + ), # ios_extension (NSExtension) apple_product_type.app_extension: _describe_rule_type( allowed_device_families = ["iphone", "ipad"], diff --git a/apple/internal/testing/ios_rules.bzl b/apple/internal/testing/ios_rules.bzl index e6ac2a0f85..84c8c1f17f 100644 --- a/apple/internal/testing/ios_rules.bzl +++ b/apple/internal/testing/ios_rules.bzl @@ -58,6 +58,7 @@ load( "@build_bazel_rules_apple//apple:providers.bzl", "AppleBundleInfo", "IosApplicationBundleInfo", + "IosBundleBundleInfo", "IosExtensionBundleInfo", "IosFrameworkBundleInfo", "IosImessageApplicationBundleInfo", diff --git a/apple/ios.bzl b/apple/ios.bzl index 37e967ded2..49c28ad189 100644 --- a/apple/ios.bzl +++ b/apple/ios.bzl @@ -33,6 +33,7 @@ load( "@build_bazel_rules_apple//apple/internal:ios_rules.bzl", _ios_app_clip = "ios_app_clip", _ios_application = "ios_application", + _ios_bundle = "ios_bundle", _ios_dynamic_framework = "ios_dynamic_framework", _ios_extension = "ios_extension", _ios_framework = "ios_framework", @@ -45,6 +46,7 @@ load( # TODO(b/118104491): Remove these re-exports and move the rule definitions into this file. ios_application = _ios_application ios_app_clip = _ios_app_clip +ios_bundle = _ios_bundle ios_dynamic_framework = _ios_dynamic_framework ios_extension = _ios_extension ios_framework = _ios_framework diff --git a/apple/providers.bzl b/apple/providers.bzl index 6c787dea55..d6d43a7208 100644 --- a/apple/providers.bzl +++ b/apple/providers.bzl @@ -50,6 +50,7 @@ load( _AppleXcframeworkBundleInfo = "AppleXcframeworkBundleInfo", _IosAppClipBundleInfo = "IosAppClipBundleInfo", _IosApplicationBundleInfo = "IosApplicationBundleInfo", + _IosBundleBundleInfo = "IosBundleBundleInfo", _IosExtensionBundleInfo = "IosExtensionBundleInfo", _IosFrameworkBundleInfo = "IosFrameworkBundleInfo", _IosImessageApplicationBundleInfo = "IosImessageApplicationBundleInfo", @@ -98,6 +99,7 @@ AppleTestInfo = _AppleTestInfo AppleTestRunnerInfo = _AppleTestRunnerInfo AppleXcframeworkBundleInfo = _AppleXcframeworkBundleInfo IosAppClipBundleInfo = _IosAppClipBundleInfo +IosBundleBundleInfo = _IosBundleBundleInfo IosApplicationBundleInfo = _IosApplicationBundleInfo IosExtensionBundleInfo = _IosExtensionBundleInfo IosFrameworkBundleInfo = _IosFrameworkBundleInfo