diff --git a/apple/internal/BUILD b/apple/internal/BUILD index 3a8d08bff..c6fd7e768 100644 --- a/apple/internal/BUILD +++ b/apple/internal/BUILD @@ -460,6 +460,7 @@ bzl_library( ":experimental", ":intermediates", ":outputs", + ":providers", "//apple/internal/utils:bundle_paths", "//apple/internal/utils:defines", "@bazel_skylib//lib:partial", diff --git a/apple/internal/processor.bzl b/apple/internal/processor.bzl index e2540b1f3..193ab56d1 100644 --- a/apple/internal/processor.bzl +++ b/apple/internal/processor.bzl @@ -91,6 +91,10 @@ load( "//apple/internal:outputs.bzl", "outputs", ) +load( + "//apple/internal:providers.bzl", + "new_applebundlearchivesupportinfo", +) load( "//apple/internal/utils:bundle_paths.bzl", "bundle_paths", @@ -316,7 +320,7 @@ def _bundle_partial_outputs_files( for partial_output in partial_outputs: for location, parent_dir, files in getattr(partial_output, "bundle_files", []): if tree_artifact_is_enabled and location == _LOCATION_ENUM.archive: - # Skip bundling archive related files, as we're only building the bundle directory. + # These files get relayed via AppleBundleArchiveSupportInfo instead. continue if trim_locales: @@ -353,7 +357,7 @@ def _bundle_partial_outputs_files( for location, parent_dir, zip_files in getattr(partial_output, "bundle_zips", []): if tree_artifact_is_enabled and location == _LOCATION_ENUM.archive: - # Skip bundling archive related files, as we're only building the bundle directory. + # These zips get relayed via AppleBundleArchiveSupportInfo instead. continue parent_dir_is_valid = _is_parent_dir_valid( @@ -505,6 +509,9 @@ def _bundle_post_process_and_sign( provisioning_profile: File for the provisioning profile. rule_descriptor: A rule descriptor for platform and product types from the rule context. rule_label: The label of the target being analyzed. + + Returns: + A List of providers if any were created during bundling. Can be an empty List. """ tree_artifact_is_enabled = is_experimental_tree_artifact_enabled( platform_prerequisites = platform_prerequisites, @@ -515,6 +522,7 @@ def _bundle_post_process_and_sign( rule_descriptor = rule_descriptor, tree_artifact_is_enabled = tree_artifact_is_enabled, ) + bundling_providers = [] signed_frameworks_depsets = [] for partial_output in partial_outputs: if hasattr(partial_output, "signed_frameworks"): @@ -522,6 +530,25 @@ def _bundle_post_process_and_sign( transitive_signed_frameworks = depset(transitive = signed_frameworks_depsets) if tree_artifact_is_enabled: + bundle_files_for_xcarchive = [] + bundle_zips_for_xcarchive = [] + + for partial_output in partial_outputs: + for location, parent_dir, files in getattr(partial_output, "bundle_files", []): + if location == _LOCATION_ENUM.archive: + bundle_files_for_xcarchive.append((parent_dir, files)) + + for location, parent_dir, zip_files in getattr(partial_output, "bundle_zips", []): + if location == _LOCATION_ENUM.archive: + bundle_zips_for_xcarchive.append((parent_dir, zip_files)) + + bundling_providers.append( + new_applebundlearchivesupportinfo( + bundle_files = bundle_files_for_xcarchive, + bundle_zips = bundle_zips_for_xcarchive, + ), + ) + extra_input_files = [] if entitlements: @@ -690,6 +717,8 @@ def _bundle_post_process_and_sign( signed_frameworks = transitive_signed_frameworks, ) + return bundling_providers + def _process( *, actions, @@ -746,6 +775,7 @@ def _process( """ partial_outputs = [partial.call(p) for p in partials] + providers = [] if bundle_post_process_and_sign: output_archive = outputs.archive( @@ -757,7 +787,7 @@ def _process( predeclared_outputs = predeclared_outputs, rule_descriptor = rule_descriptor, ) - _bundle_post_process_and_sign( + bundling_providers = _bundle_post_process_and_sign( actions = actions, apple_mac_toolchain_info = apple_mac_toolchain_info, apple_xplat_toolchain_info = apple_xplat_toolchain_info, @@ -779,11 +809,11 @@ def _process( rule_descriptor = rule_descriptor, rule_label = rule_label, ) + providers.extend(bundling_providers) transitive_output_files = [depset([output_archive])] else: transitive_output_files = [] - providers = [] output_group_dicts = [] for partial_output in partial_outputs: if hasattr(partial_output, "providers"): diff --git a/apple/internal/providers.bzl b/apple/internal/providers.bzl index 431dd9ba0..63cafc381 100644 --- a/apple/internal/providers.bzl +++ b/apple/internal/providers.bzl @@ -150,6 +150,23 @@ set to true for the extension but false for the application. init = _make_banned_init(provider_name = "AppleBundleInfo"), ) +AppleBundleArchiveSupportInfo, new_applebundlearchivesupportinfo = provider( + doc = "Provides supporting files to be embedded within an xcarchive bundle.", + fields = { + "bundle_files": """ +Required. A List of tuples of the format (parent_dir, files) where `parent_dir` is a String +indicating the parent directory structure from the root of the archive to the desired output +directory and `files` is a `depset` of `File`s referencing the files to be placed there. +""", + "bundle_zips": """ +Required. A List of tuples of the format (parent_dir, files) where `parent_dir` is a String +indicating the parent directory structure from the root of the archive to the desired output +directory and `files` is a `depset` of `File`s referencing the ZIP files to be extracted there. +""", + }, + init = _make_banned_init(provider_name = "AppleBundleArchiveSupportInfo"), +) + AppleBundleVersionInfo, new_applebundleversioninfo = provider( doc = "Provides versioning information for an Apple bundle.", fields = { diff --git a/test/starlark_tests/ios_extension_tests.bzl b/test/starlark_tests/ios_extension_tests.bzl index 551a966d1..ae14618c3 100644 --- a/test/starlark_tests/ios_extension_tests.bzl +++ b/test/starlark_tests/ios_extension_tests.bzl @@ -18,6 +18,10 @@ load( "//test/starlark_tests/rules:analysis_output_group_info_files_test.bzl", "analysis_output_group_info_files_test", ) +load( + "//test/starlark_tests/rules:apple_bundle_archive_support_info_device_test.bzl", + "apple_bundle_archive_support_info_device_test", +) load( "//test/starlark_tests/rules:apple_dsym_bundle_info_test.bzl", "apple_dsym_bundle_info_test", @@ -205,6 +209,12 @@ def ios_extension_test_suite(name): ], tags = [name], ) + apple_bundle_archive_support_info_device_test( + name = "{}_bundle_archive_support_contains_stub_executable_device_test".format(name), + expected_archive_bundle_files = ["SwiftSupport/iphoneos/swiftlibs"], + target_under_test = "//test/starlark_tests/targets_under_test/ios:app_with_swift_ext", + tags = [name], + ) archive_contents_test( name = "{}_simulator_swift_dylibs_present".format(name), build_type = "simulator", diff --git a/test/starlark_tests/ios_imessage_application_tests.bzl b/test/starlark_tests/ios_imessage_application_tests.bzl index 3e6a32740..a5dfd32a6 100644 --- a/test/starlark_tests/ios_imessage_application_tests.bzl +++ b/test/starlark_tests/ios_imessage_application_tests.bzl @@ -14,6 +14,10 @@ """ios_imessage_application Starlark tests.""" +load( + "//test/starlark_tests/rules:apple_bundle_archive_support_info_device_test.bzl", + "apple_bundle_archive_support_info_device_test", +) load( "//test/starlark_tests/rules:apple_verification_test.bzl", "apple_verification_test", @@ -89,6 +93,13 @@ def ios_imessage_application_test_suite(name): tags = [name], ) + apple_bundle_archive_support_info_device_test( + name = "{}_bundle_archive_support_contains_stub_executable_device_test".format(name), + expected_archive_bundle_files = ["MessagesApplicationSupport/MessagesApplicationSupportStub"], + target_under_test = "//test/starlark_tests/targets_under_test/ios:imessage_app", + tags = [name], + ) + native.test_suite( name = name, tags = [name], diff --git a/test/starlark_tests/rules/apple_bundle_archive_support_info_device_test.bzl b/test/starlark_tests/rules/apple_bundle_archive_support_info_device_test.bzl new file mode 100644 index 000000000..c9e9b84d6 --- /dev/null +++ b/test/starlark_tests/rules/apple_bundle_archive_support_info_device_test.bzl @@ -0,0 +1,111 @@ +# Copyright 2024 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. + +"""Starlark test rule for files found in AppleBundleArchiveSupportInfo fields.""" + +load( + "@bazel_skylib//lib:new_sets.bzl", + "sets", +) +load( + "@bazel_skylib//lib:paths.bzl", + "paths", +) +load( + "@bazel_skylib//lib:unittest.bzl", + "asserts", +) +load( + "//apple/build_settings:build_settings.bzl", + "build_settings_labels", +) +load( + "//apple/internal:providers.bzl", + "AppleBundleArchiveSupportInfo", +) # buildifier: disable=bzl-visibility +load( + "//test/starlark_tests/rules:analysis_provider_test.bzl", + "make_provider_test_rule", +) + +visibility("//test/starlark_tests/...") + +def _assert_outputs_in_set(*, actual_outputs, env, expected_outputs): + """Assert the expected set of outputs is within actual_outputs.""" + + actual_set = sets.make(actual_outputs) + expected_set = sets.make(expected_outputs) + + asserts.set_equals( + env, + expected_set, + sets.intersection(actual_set, expected_set), + "{expected_list} not contained in {actual_list}".format( + actual_list = sets.to_list(actual_set), + expected_list = sets.to_list(expected_set), + ), + ) + +def _assert_contains_expected_bundle_files_and_zips( + ctx, + env, + apple_bundle_archive_support_info): + """Assert AppleBundleArchiveSupportInfo contains expected bundle files and zips.""" + + _assert_outputs_in_set( + env = env, + expected_outputs = ctx.attr.expected_archive_bundle_files, + actual_outputs = [ + paths.join(parent_dir, file.basename) + for parent_dir, files in apple_bundle_archive_support_info.bundle_files + for file in files.to_list() + ], + ) + + _assert_outputs_in_set( + env = env, + expected_outputs = ctx.attr.expected_archive_bundle_zips, + actual_outputs = [ + paths.join(parent_dir, file.basename) + for parent_dir, files in apple_bundle_archive_support_info.bundle_zips + for file in files.to_list() + ], + ) + +apple_bundle_archive_support_info_device_test = make_provider_test_rule( + provider = AppleBundleArchiveSupportInfo, + assertion_fn = _assert_contains_expected_bundle_files_and_zips, + attrs = { + "expected_archive_bundle_files": attr.string_list( + mandatory = False, + doc = """ +List of archive-relative bundle file paths expected as outputs of AppleBundleArchiveSupportInfo. +""", + ), + "expected_archive_bundle_zips": attr.string_list( + mandatory = False, + doc = """ +List of archive-relative bundle zip paths expected as outputs of AppleBundleArchiveSupportInfo. +""", + ), + }, + config_settings = { + build_settings_labels.use_tree_artifacts_outputs: True, + "//command_line_option:macos_cpus": "arm64,x86_64", + "//command_line_option:ios_multi_cpus": "arm64", + "//command_line_option:tvos_cpus": "arm64", + "//command_line_option:visionos_cpus": "arm64", + "//command_line_option:watchos_cpus": "arm64_32", + }, +) diff --git a/test/starlark_tests/watchos_application_tests.bzl b/test/starlark_tests/watchos_application_tests.bzl index 6c3f24f7d..6da2620b4 100644 --- a/test/starlark_tests/watchos_application_tests.bzl +++ b/test/starlark_tests/watchos_application_tests.bzl @@ -22,6 +22,10 @@ load( "//test/starlark_tests/rules:analysis_target_actions_test.bzl", "analysis_target_actions_test", ) +load( + "//test/starlark_tests/rules:apple_bundle_archive_support_info_device_test.bzl", + "apple_bundle_archive_support_info_device_test", +) load( "//test/starlark_tests/rules:apple_verification_test.bzl", "apple_verification_test", @@ -132,6 +136,15 @@ def watchos_application_test_suite(name): tags = [name], ) + # Tests that the WatchKit stub executable is referenced via the provider if we're building the + # iOS companion app as a tree artifact. + apple_bundle_archive_support_info_device_test( + name = "{}_bundle_archive_support_contains_stub_executable_device_test".format(name), + expected_archive_bundle_files = ["WatchKitSupport2/WK"], + target_under_test = "//test/starlark_tests/targets_under_test/watchos:app_companion", + tags = [name], + ) + # Test that the output multi-arch stub binary is identified as watchOS device via the Mach-O # load command LC_VERSION_MIN_WATCHOS for the arm64_32 binary slice, and that 64-bit archs are # eliminated.