diff --git a/apple/BUILD b/apple/BUILD index 55bbd64a3c..f051e24ee2 100644 --- a/apple/BUILD +++ b/apple/BUILD @@ -72,6 +72,14 @@ bzl_library( ], ) +bzl_library( + name = "apple_archive", + srcs = ["apple_archive.bzl"], + deps = [ + "//apple/internal:apple_archive", + ], +) + bzl_library( name = "aspects", srcs = ["aspects.bzl"], diff --git a/apple/apple_archive.bzl b/apple/apple_archive.bzl new file mode 100644 index 0000000000..5e1dd3faca --- /dev/null +++ b/apple/apple_archive.bzl @@ -0,0 +1,24 @@ +# Copyright 2025 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +Rules for creating iOS Package Archive (.ipa). +""" + +load( + "//apple/internal:apple_archive.bzl", + _apple_archive = "apple_archive", +) + +apple_archive = _apple_archive diff --git a/apple/internal/BUILD b/apple/internal/BUILD index e77d5b93d4..3a89fe2e16 100644 --- a/apple/internal/BUILD +++ b/apple/internal/BUILD @@ -75,6 +75,24 @@ bzl_library( ], ) +bzl_library( + name = "apple_archive", + srcs = ["apple_archive.bzl"], + visibility = [ + "//apple:__subpackages__", + ], + deps = [ + ":apple_toolchains", + ":providers", + "//apple:providers", + "//apple/internal/providers:apple_messages_stub_info", + "//apple/internal/providers:apple_swift_dylibs_info", + "//apple/internal/providers:apple_symbols_file_info", + "//apple/internal/providers:apple_watchos_stub_info", + "//apple/internal/utils:defines", + ], +) + bzl_library( name = "apple_universal_binary", srcs = ["apple_universal_binary.bzl"], diff --git a/apple/internal/apple_archive.bzl b/apple/internal/apple_archive.bzl new file mode 100644 index 0000000000..e537cc68c5 --- /dev/null +++ b/apple/internal/apple_archive.bzl @@ -0,0 +1,351 @@ +""" +Rule for packaging a bundle into an Apple archive. +""" + +load( + "//apple:providers.bzl", + "AppleBundleInfo", + "AppleCodesigningDossierInfo", +) +load( + "//apple/internal:apple_toolchains.bzl", + "AppleXPlatToolsToolchainInfo", +) +load( + "//apple/internal:providers.bzl", + "new_applebundleinfo", +) +load( + "//apple/internal/providers:apple_messages_stub_info.bzl", + "AppleMessagesStubInfo", +) +load( + "//apple/internal/providers:apple_swift_dylibs_info.bzl", + "AppleSwiftDylibsInfo", +) +load( + "//apple/internal/providers:apple_symbols_file_info.bzl", + "AppleSymbolsFileInfo", +) +load( + "//apple/internal/providers:apple_watchos_stub_info.bzl", + "AppleWatchosStubInfo", +) +load( + "//apple/internal/utils:defines.bzl", + "defines", +) + +def _should_compress_archive(ctx): + """Determines if the archive should be compressed based on defines and compilation mode.""" + return defines.bool_value( + config_vars = ctx.var, + define_name = "apple.compress_ipa", + default = (ctx.var.get("COMPILATION_MODE") == "opt"), + ) + +def _collect_symbols_files(ctx, bundle_merge_files): + """Collects symbols files and adds them to bundle_merge_files. + + Args: + ctx: The rule context. + bundle_merge_files: List to append symbols merge structs to. + + Returns: + List of symbols input files. + """ + symbols_inputs = [] + if ctx.attr.include_symbols and AppleSymbolsFileInfo in ctx.attr.bundle: + symbols_info = ctx.attr.bundle[AppleSymbolsFileInfo] + symbols_inputs = symbols_info.symbols_output_dirs.to_list() + for symbols_dir in symbols_inputs: + bundle_merge_files.append( + struct( + src = symbols_dir.path, + dest = "Symbols", + ), + ) + return symbols_inputs + +def _collect_swift_support_files(ctx, bundle_merge_files): + """Collects Swift support files and adds them to bundle_merge_files. + + Args: + ctx: The rule context. + bundle_merge_files: List to append Swift support merge structs to. + + Returns: + List of Swift support input files. + """ + swift_support_inputs = [] + if AppleSwiftDylibsInfo in ctx.attr.bundle: + swift_dylibs_info = ctx.attr.bundle[AppleSwiftDylibsInfo] + for platform_name, swift_support_dir in swift_dylibs_info.swift_support_files: + swift_support_inputs.append(swift_support_dir) + bundle_merge_files.append( + struct( + src = swift_support_dir.path, + dest = "SwiftSupport/%s" % platform_name, + ), + ) + return swift_support_inputs + +def _collect_watchos_stub_files(ctx, bundle_merge_files): + """Collects WatchOS stub files and adds them to bundle_merge_files. + + Args: + ctx: The rule context. + bundle_merge_files: List to append WatchOS stub merge structs to. + + Returns: + List of WatchOS stub input files. + """ + watchos_stub_inputs = [] + if AppleWatchosStubInfo in ctx.attr.bundle: + watchos_stub_info = ctx.attr.bundle[AppleWatchosStubInfo] + watchos_stub_inputs.append(watchos_stub_info.binary) + bundle_merge_files.append( + struct( + src = watchos_stub_info.binary.path, + dest = "WatchKitSupport2/WK", + ), + ) + return watchos_stub_inputs + +def _collect_messages_stub_files(ctx, bundle_merge_files): + """Collects iMessage stub files and adds them to bundle_merge_files. + + Args: + ctx: The rule context. + bundle_merge_files: List to append messages stub merge structs to. + + Returns: + List of messages stub input files. + """ + messages_stub_inputs = [] + if AppleMessagesStubInfo in ctx.attr.bundle: + messages_stub_info = ctx.attr.bundle[AppleMessagesStubInfo] + if messages_stub_info.messages_application_support: + messages_stub_inputs.append(messages_stub_info.messages_application_support) + bundle_merge_files.append( + struct( + src = messages_stub_info.messages_application_support.path, + dest = "MessagesApplicationSupport/MessagesApplicationSupportStub", + ), + ) + if messages_stub_info.messages_extension_support: + messages_stub_inputs.append(messages_stub_info.messages_extension_support) + bundle_merge_files.append( + struct( + src = messages_stub_info.messages_extension_support.path, + dest = "MessagesApplicationExtensionSupport/MessagesApplicationExtensionSupportStub", + ), + ) + return messages_stub_inputs + +def _create_archive_file(ctx, bundle_info, bundletool, should_compress, all_inputs): + """Creates the archive file using bundletool. + + Args: + ctx: The rule context. + bundle_info: The AppleBundleInfo provider. + bundletool: The bundletool executable. + should_compress: Whether to compress the archive. + all_inputs: Tuple of (bundle_merge_files, symbols_inputs, swift_support_inputs, + watchos_stub_inputs, messages_stub_inputs). + + Returns: + The declared archive file. + """ + bundle_merge_files, symbols_inputs, swift_support_inputs, watchos_stub_inputs, messages_stub_inputs = all_inputs + + archive = ctx.actions.declare_file("%s.ipa" % bundle_info.bundle_name) + + control = struct( + bundle_merge_files = bundle_merge_files, + output = archive.path, + compress = should_compress, + ) + + control_file = ctx.actions.declare_file("%s_control.json" % ctx.label.name) + ctx.actions.write( + output = control_file, + content = json.encode(control), + ) + + ctx.actions.run( + executable = bundletool, + arguments = [control_file.path], + inputs = [ + control_file, + bundle_info.archive, + ] + symbols_inputs + swift_support_inputs + watchos_stub_inputs + messages_stub_inputs, + outputs = [archive], + mnemonic = "CreateIPA", + ) + + return archive + +def _create_combined_dossier_zip(ctx, bundle_info, bundletool, archive, dossier_zip): + """Creates a combined zip file containing both the IPA and dossier. + + Args: + ctx: The rule context. + bundle_info: The AppleBundleInfo provider. + bundletool: The bundletool executable. + archive: The archive file. + dossier_zip: The dossier zip file. + + Returns: + The combined zip file. + """ + combined_zip = ctx.actions.declare_file("%s_dossier_with_bundle.zip" % bundle_info.bundle_name) + + control = struct( + bundle_merge_zips = [ + struct(src = archive.path, dest = "bundle"), + struct(src = dossier_zip.path, dest = "dossier"), + ], + output = combined_zip.path, + ) + + control_file = ctx.actions.declare_file("%s_combined_control.json" % ctx.label.name) + ctx.actions.write( + output = control_file, + content = json.encode(control), + ) + + ctx.actions.run( + executable = bundletool, + arguments = [control_file.path], + inputs = [control_file, archive, dossier_zip], + outputs = [combined_zip], + mnemonic = "CreateCombinedDossierZip", + ) + + return combined_zip + +def _create_apple_bundle_info(bundle_info, archive): + """Creates an AppleBundleInfo provider for the archive. + + Args: + bundle_info: The original AppleBundleInfo from the bundle. + archive: The archive file. + + Returns: + An AppleBundleInfo provider. + """ + return new_applebundleinfo( + archive = archive, + archive_root = bundle_info.archive_root, + binary = bundle_info.binary, + bundle_extension = bundle_info.bundle_extension, + bundle_id = bundle_info.bundle_id, + bundle_name = bundle_info.bundle_name, + entitlements = bundle_info.entitlements, + executable_name = bundle_info.executable_name, + extension_safe = bundle_info.extension_safe, + infoplist = bundle_info.infoplist, + minimum_deployment_os_version = bundle_info.minimum_deployment_os_version, + minimum_os_version = bundle_info.minimum_os_version, + platform_type = bundle_info.platform_type, + product_type = bundle_info.product_type, + uses_swift = bundle_info.uses_swift, + ) + +def _apple_archive_impl(ctx): + """ + Implementation for apple_archive. + + This rule uses the providers from the bundle target to re-package it into a .ipa. + The .ipa is a directory that contains the Payload/*.app bundle. + """ + bundle_info = ctx.attr.bundle[AppleBundleInfo] + xplat_tools = ctx.attr._xplat_tools[AppleXPlatToolsToolchainInfo] + bundletool = xplat_tools.bundletool + + should_compress = _should_compress_archive(ctx) + + bundle_merge_files = [ + struct( + src = bundle_info.archive.path, + dest = "Payload/%s%s" % (bundle_info.bundle_name, bundle_info.bundle_extension), + ), + ] + + symbols_inputs = _collect_symbols_files(ctx, bundle_merge_files) + swift_support_inputs = _collect_swift_support_files(ctx, bundle_merge_files) + watchos_stub_inputs = _collect_watchos_stub_files(ctx, bundle_merge_files) + messages_stub_inputs = _collect_messages_stub_files(ctx, bundle_merge_files) + + all_inputs = (bundle_merge_files, symbols_inputs, swift_support_inputs, watchos_stub_inputs, messages_stub_inputs) + archive = _create_archive_file(ctx, bundle_info, bundletool, should_compress, all_inputs) + + output_groups = {} + if AppleCodesigningDossierInfo in ctx.attr.bundle: + dossier_info = ctx.attr.bundle[AppleCodesigningDossierInfo] + dossier_zip = dossier_info.dossier + combined_zip = _create_combined_dossier_zip(ctx, bundle_info, bundletool, archive, dossier_zip) + output_groups["combined_dossier_zip"] = depset([combined_zip]) + + apple_archive_bundle_info = _create_apple_bundle_info(bundle_info, archive) + + return [ + DefaultInfo(files = depset([archive])), + apple_archive_bundle_info, + OutputGroupInfo(**output_groups), + ] + +apple_archive = rule( + implementation = _apple_archive_impl, + attrs = { + "bundle": attr.label( + providers = [ + AppleBundleInfo, + ], + doc = """\ +The label to a target to re-package into a .ipa. For example, an +`ios_application` target. + """, + ), + "include_symbols": attr.bool( + default = False, + doc = """ + If true, collects `$UUID.symbols` + files from all `{binary: .dSYM, ...}` pairs for the application and its + dependencies, then packages them under the `Symbols/` directory in the + final .ipa. + """, + ), + "_xplat_tools": attr.label( + default = Label("//apple/internal:xplat_tools_toolchain"), + providers = [AppleXPlatToolsToolchainInfo], + doc = """\ +A reference too xcplat_toolchain. + """, + ), + }, + doc = """\ +Re-packages an Apple bundle into a .ipa. + +This rule uses the providers from the bundle target to construct the required +metadata for the .ipa. + +Example: + +````starlark +load("//apple:apple_archive.bzl", "apple_archive") + +ios_application( + name = "App", + bundle_id = "com.example.my.app", + ... +) + +apple_archive( + name = "App.ipa", + bundle = ":App", +) +```` + """, +) diff --git a/apple/internal/experimental.bzl b/apple/internal/experimental.bzl index a234947a22..a327ea0d93 100644 --- a/apple/internal/experimental.bzl +++ b/apple/internal/experimental.bzl @@ -14,6 +14,10 @@ """Temporary file to centralize configuration of the experimental bundling logic.""" +load( + "//apple/internal:apple_product_type.bzl", + "apple_product_type", +) load( "//apple/internal/utils:defines.bzl", "defines", @@ -23,13 +27,15 @@ load( def is_experimental_tree_artifact_enabled( *, config_vars = None, - platform_prerequisites = None): + platform_prerequisites = None, + rule_descriptor = None): """Returns whether tree artifact outputs experiment is enabled. Args: config_vars: A reference to configuration variables, typically from `ctx.var`. platform_prerequisites: Struct containing information on the platform being targeted, if one exists for the rule. + rule_descriptor: A rule descriptor for platform and product types from the rule context. Returns: True if tree artifact outputs are enabled (via --define or build setting), False otherwise. """ @@ -39,6 +45,17 @@ def is_experimental_tree_artifact_enabled( if platform_prerequisites and platform_prerequisites.build_settings.use_tree_artifacts_outputs: return True + # Enable tree artifacts by default for iOS/tvOS/visionOS applications + # These will produce .app bundles that can be wrapped into .ipa files + if rule_descriptor and platform_prerequisites: + if (platform_prerequisites.platform.platform_type, rule_descriptor.product_type) in [ + (str(apple_common.platform_type.ios), apple_product_type.application), + (str(apple_common.platform_type.ios), apple_product_type.messages_application), + (str(apple_common.platform_type.tvos), apple_product_type.application), + (str(apple_common.platform_type.visionos), apple_product_type.application), + ]: + return True + return defines.bool_value( config_vars = platform_prerequisites.config_vars if platform_prerequisites else config_vars, define_name = "apple.experimental.tree_artifact_outputs", diff --git a/apple/internal/ios_rules.bzl b/apple/internal/ios_rules.bzl index e8fcec5967..8c2fdfc5e2 100644 --- a/apple/internal/ios_rules.bzl +++ b/apple/internal/ios_rules.bzl @@ -408,7 +408,7 @@ def _ios_application_impl(ctx): dependency_targets = embeddable_targets + ctx.attr.deps, dsym_binaries = debug_outputs.dsym_binaries, label_name = label.name, - include_symbols_in_bundle = ctx.attr.include_symbols_in_bundle, + include_symbols_in_bundle = False, platform_prerequisites = platform_prerequisites, ), ] @@ -2629,15 +2629,6 @@ A list of framework targets (see [`ios_framework`](https://github.com/bazelbuild/rules_apple/blob/main/doc/rules-ios.md#ios_framework)) that this target depends on. """, - ), - "include_symbols_in_bundle": attr.bool( - default = False, - doc = """ - If true and --output_groups=+dsyms is specified, generates `$UUID.symbols` - files from all `{binary: .dSYM, ...}` pairs for the application and its - dependencies, then packages them under the `Symbols/` directory in the - final application bundle. - """, ), "launch_storyboard": attr.label( allow_single_file = [".storyboard", ".xib"], diff --git a/apple/internal/outputs.bzl b/apple/internal/outputs.bzl index 07ffeb4e12..7fcd1906f9 100644 --- a/apple/internal/outputs.bzl +++ b/apple/internal/outputs.bzl @@ -46,6 +46,7 @@ def _archive( tree_artifact_enabled = is_experimental_tree_artifact_enabled( platform_prerequisites = platform_prerequisites, + rule_descriptor = rule_descriptor, ) if tree_artifact_enabled: if bundle_name != label_name: @@ -126,6 +127,7 @@ def _has_different_embedding_archive(*, platform_prerequisites, rule_descriptor) """Returns True if this target exposes a different archive when embedded in another target.""" tree_artifact_enabled = is_experimental_tree_artifact_enabled( platform_prerequisites = platform_prerequisites, + rule_descriptor = rule_descriptor, ) if tree_artifact_enabled: return False diff --git a/apple/internal/partials/BUILD b/apple/internal/partials/BUILD index d03d124ddc..f2120a1f56 100644 --- a/apple/internal/partials/BUILD +++ b/apple/internal/partials/BUILD @@ -56,6 +56,7 @@ bzl_library( deps = [ "//apple/internal:intermediates", "//apple/internal:processor", + "//apple/internal/providers:apple_symbols_file_info", "@bazel_skylib//lib:partial", "@bazel_skylib//lib:shell", "@build_bazel_apple_support//lib:apple_support", @@ -264,6 +265,7 @@ bzl_library( deps = [ "//apple/internal:intermediates", "//apple/internal:processor", + "//apple/internal/providers:apple_messages_stub_info", "@bazel_skylib//lib:partial", ], ) @@ -325,6 +327,7 @@ bzl_library( deps = [ "//apple/internal:intermediates", "//apple/internal:processor", + "//apple/internal/providers:apple_swift_dylibs_info", "//apple/internal/utils:defines", "@bazel_skylib//lib:partial", "@bazel_skylib//lib:paths", @@ -369,6 +372,7 @@ bzl_library( deps = [ "//apple/internal:intermediates", "//apple/internal:processor", + "//apple/internal/providers:apple_watchos_stub_info", "@bazel_skylib//lib:partial", ], ) diff --git a/apple/internal/partials/apple_symbols_file.bzl b/apple/internal/partials/apple_symbols_file.bzl index 1a5cfb3553..a67fba968c 100644 --- a/apple/internal/partials/apple_symbols_file.bzl +++ b/apple/internal/partials/apple_symbols_file.bzl @@ -35,12 +35,9 @@ load( "//apple/internal:processor.bzl", "processor", ) - -_AppleSymbolsFileInfo = provider( - doc = "Private provider to propagate the transitive .symbols `File`s.", - fields = { - "symbols_output_dirs": "Depset of `File`s containing directories of $UUID.symbols files for transitive dependencies.", - }, +load( + "//apple/internal/providers:apple_symbols_file_info.bzl", + "AppleSymbolsFileInfo", ) def _apple_symbols_file_partial_impl( @@ -89,9 +86,9 @@ def _apple_symbols_file_partial_impl( transitive_output_files = depset( direct = outputs, transitive = [ - x[_AppleSymbolsFileInfo].symbols_output_dirs + x[AppleSymbolsFileInfo].symbols_output_dirs for x in dependency_targets - if _AppleSymbolsFileInfo in x + if AppleSymbolsFileInfo in x ], ) @@ -102,7 +99,7 @@ def _apple_symbols_file_partial_impl( return struct( bundle_files = bundle_files, - providers = [_AppleSymbolsFileInfo(symbols_output_dirs = transitive_output_files)], + providers = [AppleSymbolsFileInfo(symbols_output_dirs = transitive_output_files)], ) def apple_symbols_file_partial( diff --git a/apple/internal/partials/codesigning_dossier.bzl b/apple/internal/partials/codesigning_dossier.bzl index c552a0cc9a..9a1510b03c 100644 --- a/apple/internal/partials/codesigning_dossier.bzl +++ b/apple/internal/partials/codesigning_dossier.bzl @@ -161,7 +161,8 @@ def _create_combined_zip_artifact( label_name, output_combined_zip, output_discriminator, - platform_prerequisites): + platform_prerequisites, + rule_descriptor): """Generates a zip file with the IPA contents in one subdirectory and the dossier in another. Args: @@ -205,6 +206,7 @@ def _create_combined_zip_artifact( tree_artifact_is_enabled = is_experimental_tree_artifact_enabled( platform_prerequisites = platform_prerequisites, + rule_descriptor = rule_descriptor, ) if tree_artifact_is_enabled: @@ -332,6 +334,7 @@ def _codesigning_dossier_partial_impl( output_combined_zip = output_combined_zip, output_discriminator = output_discriminator, platform_prerequisites = platform_prerequisites, + rule_descriptor = rule_descriptor, ) return struct( diff --git a/apple/internal/partials/messages_stub.bzl b/apple/internal/partials/messages_stub.bzl index d9f3da16d2..d2d27b650b 100644 --- a/apple/internal/partials/messages_stub.bzl +++ b/apple/internal/partials/messages_stub.bzl @@ -26,10 +26,15 @@ load( "//apple/internal:processor.bzl", "processor", ) +load( + "//apple/internal/providers:apple_messages_stub_info.bzl", + "AppleMessagesStubInfo", +) -_AppleMessagesStubInfo = provider( +# Private provider for internal propagation from extensions to parent app. +_AppleMessagesPrivateStubInfo = provider( doc = """ -Private provider to propagate the messages stub that needs to be package in the iOS archive. +Private provider to propagate the messages stub that needs to be packaged in the iOS archive. """, fields = { "binary": """ @@ -54,20 +59,23 @@ def _messages_stub_partial_impl( if package_messages_support: extension_binaries = [ - x[_AppleMessagesStubInfo].binary + x[_AppleMessagesPrivateStubInfo].binary for x in extensions - if _AppleMessagesStubInfo in x + if _AppleMessagesPrivateStubInfo in x ] + extension_support_file = None if extension_binaries: + extension_support_file = extension_binaries[0] bundle_files.append( ( processor.location.archive, "MessagesApplicationExtensionSupport", - depset([extension_binaries[0]]), + depset([extension_support_file]), ), ) + app_support_file = None if binary_artifact: intermediate_file = intermediates.file( actions = actions, @@ -79,6 +87,7 @@ def _messages_stub_partial_impl( target_file = binary_artifact, output = intermediate_file, ) + app_support_file = intermediate_file bundle_files.append( ( @@ -88,6 +97,13 @@ def _messages_stub_partial_impl( ), ) + # We propagate the public provider for ipa rule access + if app_support_file or extension_support_file: + providers.append(AppleMessagesStubInfo( + messages_application_support = app_support_file, + messages_extension_support = extension_support_file, + )) + elif binary_artifact: intermediate_file = intermediates.file( actions = actions, @@ -99,7 +115,7 @@ def _messages_stub_partial_impl( target_file = binary_artifact, output = intermediate_file, ) - providers.append(_AppleMessagesStubInfo(binary = intermediate_file)) + providers.append(_AppleMessagesPrivateStubInfo(binary = intermediate_file)) return struct( bundle_files = bundle_files, diff --git a/apple/internal/partials/swift_dylibs.bzl b/apple/internal/partials/swift_dylibs.bzl index 127060aecf..03f342cc31 100644 --- a/apple/internal/partials/swift_dylibs.bzl +++ b/apple/internal/partials/swift_dylibs.bzl @@ -34,29 +34,15 @@ load( "//apple/internal:processor.bzl", "processor", ) +load( + "//apple/internal/providers:apple_swift_dylibs_info.bzl", + "AppleSwiftDylibsInfo", +) load( "//apple/internal/utils:defines.bzl", "defines", ) -_AppleSwiftDylibsInfo = provider( - doc = """ -Private provider to propagate the transitive binary `File`s that depend on -Swift. -""", - fields = { - "binary": """ -Depset of binary `File`s containing the transitive dependency binaries that use -Swift. -""", - "swift_support_files": """ -List of 2-element tuples that represent which files should be bundled as part of the SwiftSupport -archive directory. The first element of the tuple is the platform name, and the second element is a -File object that represents a directory containing the Swift dylibs to package for that platform. -""", - }, -) - # Minimum OS versions for which we no longer need to potentially bundle any # Swift dylibs with the application. The first cutoff point was when the # platforms bundled the standard libraries, the second was when they started @@ -125,11 +111,11 @@ def _swift_dylibs_partial_impl( transitive_binary_sets = [] transitive_swift_support_files = [] for dependency in dependency_targets: - if _AppleSwiftDylibsInfo not in dependency: - # Skip targets without the _AppleSwiftDylibsInfo provider, as they don't use Swift + if AppleSwiftDylibsInfo not in dependency: + # Skip targets without the AppleSwiftDylibsInfo provider, as they don't use Swift # (i.e. sticker extensions that have stubs). continue - provider = dependency[_AppleSwiftDylibsInfo] + provider = dependency[AppleSwiftDylibsInfo] transitive_binary_sets.append(provider.binary) transitive_swift_support_files.extend(provider.swift_support_files) @@ -222,7 +208,7 @@ def _swift_dylibs_partial_impl( return struct( bundle_files = bundle_files, - providers = [_AppleSwiftDylibsInfo( + providers = [AppleSwiftDylibsInfo( binary = propagated_binaries, swift_support_files = transitive_swift_support_files, )], diff --git a/apple/internal/partials/watchos_stub.bzl b/apple/internal/partials/watchos_stub.bzl index 22a36cf111..ee6cb982ec 100644 --- a/apple/internal/partials/watchos_stub.bzl +++ b/apple/internal/partials/watchos_stub.bzl @@ -26,17 +26,9 @@ load( "//apple/internal:processor.bzl", "processor", ) - -_AppleWatchosStubInfo = provider( - doc = """ -Private provider to propagate the watchOS stub that needs to be package in the iOS archive. -""", - fields = { - "binary": """ -File artifact that contains a reference to the stub binary that needs to be packaged in the iOS -archive. -""", - }, +load( + "//apple/internal/providers:apple_watchos_stub_info.bzl", + "AppleWatchosStubInfo", ) def _watchos_stub_partial_impl( @@ -65,13 +57,14 @@ def _watchos_stub_partial_impl( bundle_files.append( (processor.location.bundle, "_WatchKitStub", depset([intermediate_file])), ) - providers.append(_AppleWatchosStubInfo(binary = intermediate_file)) + providers.append(AppleWatchosStubInfo(binary = intermediate_file)) if watch_application: - binary_artifact = watch_application[_AppleWatchosStubInfo].binary + binary_artifact = watch_application[AppleWatchosStubInfo].binary bundle_files.append( (processor.location.archive, "WatchKitSupport2", depset([binary_artifact])), ) + providers.append(AppleWatchosStubInfo(binary = binary_artifact)) return struct( bundle_files = bundle_files, diff --git a/apple/internal/processor.bzl b/apple/internal/processor.bzl index cd785eacb2..6b5fe9df8a 100644 --- a/apple/internal/processor.bzl +++ b/apple/internal/processor.bzl @@ -297,6 +297,7 @@ def _bundle_partial_outputs_files( tree_artifact_is_enabled = is_experimental_tree_artifact_enabled( platform_prerequisites = platform_prerequisites, + rule_descriptor = rule_descriptor, ) location_to_paths = _archive_paths( @@ -508,6 +509,7 @@ def _bundle_post_process_and_sign( """ tree_artifact_is_enabled = is_experimental_tree_artifact_enabled( platform_prerequisites = platform_prerequisites, + rule_descriptor = rule_descriptor, ) archive_paths = _archive_paths( bundle_extension = bundle_extension, diff --git a/apple/internal/providers/BUILD b/apple/internal/providers/BUILD index 3decfb86d9..89b1170411 100644 --- a/apple/internal/providers/BUILD +++ b/apple/internal/providers/BUILD @@ -31,6 +31,38 @@ bzl_library( ], ) +bzl_library( + name = "apple_symbols_file_info", + srcs = ["apple_symbols_file_info.bzl"], + visibility = [ + "//apple/internal:__subpackages__", + ], +) + +bzl_library( + name = "apple_swift_dylibs_info", + srcs = ["apple_swift_dylibs_info.bzl"], + visibility = [ + "//apple/internal:__subpackages__", + ], +) + +bzl_library( + name = "apple_watchos_stub_info", + srcs = ["apple_watchos_stub_info.bzl"], + visibility = [ + "//apple/internal:__subpackages__", + ], +) + +bzl_library( + name = "apple_messages_stub_info", + srcs = ["apple_messages_stub_info.bzl"], + visibility = [ + "//apple/internal:__subpackages__", + ], +) + bzl_library( name = "embeddable_info", srcs = ["embeddable_info.bzl"], diff --git a/apple/internal/providers/apple_messages_stub_info.bzl b/apple/internal/providers/apple_messages_stub_info.bzl new file mode 100644 index 0000000000..ec0d0a3aa1 --- /dev/null +++ b/apple/internal/providers/apple_messages_stub_info.bzl @@ -0,0 +1,31 @@ +# Copyright 2025 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""AppleMessagesStubInfo implementation.""" + +visibility("//apple/internal/...") + +AppleMessagesStubInfo = provider( + doc = """ +Provider for iMessage application stub binaries that need to be packaged in the archive. +""", + fields = { + "messages_application_support": """ +File for the MessagesApplicationSupport stub binary (for iMessage apps). May be None. +""", + "messages_extension_support": """ +File for the MessagesApplicationExtensionSupport stub binary (from extensions). May be None. +""", + }, +) diff --git a/apple/internal/providers/apple_swift_dylibs_info.bzl b/apple/internal/providers/apple_swift_dylibs_info.bzl new file mode 100644 index 0000000000..4d1bed2d04 --- /dev/null +++ b/apple/internal/providers/apple_swift_dylibs_info.bzl @@ -0,0 +1,35 @@ +# Copyright 2025 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""AppleSwiftDylibsInfo implementation..""" + +visibility("//apple/internal/...") + +AppleSwiftDylibsInfo = provider( + doc = """ +Internal provider to propagate the transitive binary `File`s that depend on +Swift. +""", + fields = { + "binary": """ +Depset of binary `File`s containing the transitive dependency binaries that use +Swift. +""", + "swift_support_files": """ +List of 2-element tuples that represent which files should be bundled as part of the SwiftSupport +archive directory. The first element of the tuple is the platform name, and the second element is a +File object that represents a directory containing the Swift dylibs to package for that platform. +""", + }, +) diff --git a/apple/internal/providers/apple_symbols_file_info.bzl b/apple/internal/providers/apple_symbols_file_info.bzl new file mode 100644 index 0000000000..177e3d6e9f --- /dev/null +++ b/apple/internal/providers/apple_symbols_file_info.bzl @@ -0,0 +1,28 @@ +# Copyright 2025 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""AppleSymbolsFileInfo implementation..""" + +visibility("//apple/internal/...") + +AppleSymbolsFileInfo = provider( + doc = """ +Provider containing .symbols files for crash symbolication. +""", + fields = { + "symbols_output_dirs": """ +Depset of Files containing directories of $UUID.symbols files. +""", + }, +) diff --git a/apple/internal/providers/apple_watchos_stub_info.bzl b/apple/internal/providers/apple_watchos_stub_info.bzl new file mode 100644 index 0000000000..f48e557dac --- /dev/null +++ b/apple/internal/providers/apple_watchos_stub_info.bzl @@ -0,0 +1,29 @@ +# Copyright 2025 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""AppleWatchosStubInfo implementation..""" + +visibility("//apple/internal/...") + +AppleWatchosStubInfo = provider( + doc = """ +Internal provider to propagate the watchOS stub that needs to be package in the iOS archive. +""", + fields = { + "binary": """ +File artifact that contains a reference to the stub binary that needs to be packaged in the iOS +archive. +""", + }, +) diff --git a/doc/BUILD.bazel b/doc/BUILD.bazel index 02c1978bd6..0750dd3c76 100644 --- a/doc/BUILD.bazel +++ b/doc/BUILD.bazel @@ -9,6 +9,7 @@ _PLAIN_DOC_SRCS = [ _RULES_DOC_SRCS = [ "apple", + "apple_archive", "docc", "dtrace", "header_map", diff --git a/doc/rules-apple_archive.md b/doc/rules-apple_archive.md new file mode 100755 index 0000000000..7a0d1c2df6 --- /dev/null +++ b/doc/rules-apple_archive.md @@ -0,0 +1,46 @@ + + +Rules for creating iOS Package Archive (.ipa). + + + +## apple_archive + +
+load("@rules_apple//apple:apple_archive.bzl", "apple_archive")
+
+apple_archive(name, bundle, include_symbols)
+
+
+Re-packages an Apple bundle into a .ipa.
+
+This rule uses the providers from the bundle target to construct the required
+metadata for the .ipa.
+
+Example:
+
+````starlark
+load("//apple:apple_archive.bzl", "apple_archive")
+
+ios_application(
+ name = "App",
+ bundle_id = "com.example.my.app",
+ ...
+)
+
+apple_archive(
+ name = "App.ipa",
+ bundle = ":App",
+)
+````
+
+**ATTRIBUTES**
+
+
+| Name | Description | Type | Mandatory | Default |
+| :------------- | :------------- | :------------- | :------------- | :------------- |
+| name | A unique name for this target. | Name | required | |
+| bundle | The label to a target to re-package into a .ipa. For example, an `ios_application` target. | Label | optional | `None` |
+| include_symbols | If true, collects `$UUID.symbols` files from all `{binary: .dSYM, ...}` pairs for the application and its dependencies, then packages them under the `Symbols/` directory in the final .ipa. | Boolean | optional | `False` |
+
+
diff --git a/doc/rules-ios.md b/doc/rules-ios.md
index 9c2fa3890a..5debf211d7 100644
--- a/doc/rules-ios.md
+++ b/doc/rules-ios.md
@@ -66,11 +66,11 @@ load("@rules_apple//apple:ios.doc.bzl", "ios_application")
ios_application(name, deps, resources, additional_linker_inputs, alternate_icons, app_clips,
app_icons, app_intents, bundle_id, bundle_id_suffix, bundle_name, codesign_inputs,
codesignopts, entitlements, entitlements_validation, executable_name,
- exported_symbols_lists, extensions, families, frameworks, include_symbols_in_bundle,
- infoplists, ipa_post_processor, launch_images, launch_storyboard, linkopts,
- locales_to_include, minimum_deployment_os_version, minimum_os_version, platform_type,
- primary_app_icon, provisioning_profile, sdk_frameworks, settings_bundle,
- shared_capabilities, stamp, strings, version, watch_application)
+ exported_symbols_lists, extensions, families, frameworks, infoplists,
+ ipa_post_processor, launch_images, launch_storyboard, linkopts, locales_to_include,
+ minimum_deployment_os_version, minimum_os_version, platform_type, primary_app_icon,
+ provisioning_profile, sdk_frameworks, settings_bundle, shared_capabilities, stamp,
+ strings, version, watch_application)
Builds and bundles an iOS Application.
@@ -100,7 +100,6 @@ Builds and bundles an iOS Application.
| extensions | A list of iOS application extensions to include in the final application bundle. | List of labels | optional | `[]` |
| families | A list of device families supported by this rule. At least one must be specified. | List of strings | required | |
| frameworks | A list of framework targets (see [`ios_framework`](https://github.com/bazelbuild/rules_apple/blob/main/doc/rules-ios.md#ios_framework)) that this target depends on. | List of labels | optional | `[]` |
-| include_symbols_in_bundle | If true and --output_groups=+dsyms is specified, generates `$UUID.symbols` files from all `{binary: .dSYM, ...}` pairs for the application and its dependencies, then packages them under the `Symbols/` directory in the final application bundle. | Boolean | optional | `False` |
| infoplists | A list of .plist files that will be merged to form the Info.plist for this target. At least one file must be specified. Please see [Info.plist Handling](https://github.com/bazelbuild/rules_apple/blob/main/doc/common_info.md#infoplist-handling) for what is supported. | List of labels | required | |
| ipa_post_processor | A tool that edits this target's archive after it is assembled but before it is signed. The tool is invoked with a single command-line argument that denotes the path to a directory containing the unzipped contents of the archive; this target's bundle will be the directory's only contents.