From 5f7d38d51351d2ff0cbe3ffb9b74c00b1294b8a6 Mon Sep 17 00:00:00 2001 From: Keith Smiley Date: Fri, 6 Dec 2024 09:40:25 -0800 Subject: [PATCH] Move apple specific linking logic from bazel (#2609) Cherry picks: - 2f3f608b8a8e59e9f06d030012649c1959c8cfc6 - 46535afcec837dcdaf216a3e94dff72e1f14c7a7 Fixes https://github.com/bazelbuild/rules_apple/issues/2604 --- .bazelci/presubmit.yml | 10 +- apple/apple_binary.bzl | 9 +- apple/internal/BUILD | 8 +- apple/internal/apple_framework_import.bzl | 6 +- apple/internal/apple_xcframework_import.bzl | 9 +- apple/internal/framework_import_support.bzl | 15 - apple/internal/ios_rules.bzl | 7 +- apple/internal/linking_support.bzl | 347 ++++++++++++++++-- apple/internal/macos_rules.bzl | 10 +- apple/internal/multi_arch_binary_support.bzl | 104 ++++++ apple/internal/partials/BUILD | 1 + .../partials/app_intents_metadata_bundle.bzl | 2 +- .../internal/partials/framework_provider.bzl | 6 +- apple/internal/providers.bzl | 45 +++ apple/internal/providers/BUILD | 10 + .../apple_dynamic_framework_info.bzl | 49 +++ .../testing/apple_test_bundle_support.bzl | 3 +- apple/internal/tvos_rules.bzl | 5 +- apple/internal/visionos_rules.bzl | 5 +- apple/providers.bzl | 4 + doc/providers.md | 45 +++ 21 files changed, 636 insertions(+), 64 deletions(-) create mode 100644 apple/internal/multi_arch_binary_support.bzl create mode 100644 apple/internal/providers/apple_dynamic_framework_info.bzl diff --git a/.bazelci/presubmit.yml b/.bazelci/presubmit.yml index b110abcc43..e6c90fd1bc 100644 --- a/.bazelci/presubmit.yml +++ b/.bazelci/presubmit.yml @@ -23,12 +23,14 @@ tasks: bazel: latest <<: *common + macos_last_rc: + name: "Last RC Bazel" + bazel: last_rc + <<: *common + macos_last_green: name: "Last Green Bazel" - # FIXME: Use last_green once we cherry-pick in changes needed by - # https://github.com/bazelbuild/bazel/commit/b59004dd17366b09b0758b833f98294fd0e89345 - # bazel: last_green - bazel: 21a12c72d84e9108142421d9a8526edf8dceb78c + bazel: last_green <<: *common doc_tests: diff --git a/apple/apple_binary.bzl b/apple/apple_binary.bzl index 910fe3ebc2..1cbf0be7b9 100644 --- a/apple/apple_binary.bzl +++ b/apple/apple_binary.bzl @@ -18,6 +18,11 @@ load( "//apple/internal:linking_support.bzl", "linking_support", ) +load( + "//apple/internal:providers.bzl", + "AppleExecutableBinaryInfo", + "new_appleexecutablebinaryinfo", +) load( "//apple/internal:rule_attrs.bzl", "rule_attrs", @@ -116,7 +121,7 @@ Resolved Xcode is version {xcode_version}. # so that bundles can use it as their loader. if binary_type == "executable": providers.append( - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), @@ -170,7 +175,7 @@ The target representing the executable that will be loading this bundle. Undefined symbols from the bundle are checked against this executable during linking as if it were one of the dynamic libraries the bundle was linked with. """, - providers = [apple_common.AppleExecutableBinary], + providers = [AppleExecutableBinaryInfo], ), "data": attr.label_list(allow_files = True), "sdk_dylibs": attr.string_list( diff --git a/apple/internal/BUILD b/apple/internal/BUILD index f676fa27fc..d1367d1697 100644 --- a/apple/internal/BUILD +++ b/apple/internal/BUILD @@ -330,7 +330,10 @@ bzl_library( bzl_library( name = "linking_support", - srcs = ["linking_support.bzl"], + srcs = [ + "linking_support.bzl", + "multi_arch_binary_support.bzl", + ], visibility = [ "//apple:__subpackages__", ], @@ -338,7 +341,9 @@ bzl_library( ":apple_toolchains", ":cc_toolchain_info_support", ":entitlements_support", + ":providers", ":rule_support", + "//apple/internal/providers:apple_dynamic_framework_info", "@bazel_skylib//lib:collections", "@build_bazel_apple_support//lib:lipo", ], @@ -446,6 +451,7 @@ bzl_library( "//apple/internal/partials:swift_dynamic_framework", "//apple/internal/partials:swift_framework", "//apple/internal/partials:watchos_stub", + "//apple/internal/providers:apple_dynamic_framework_info", ], ) diff --git a/apple/internal/apple_framework_import.bzl b/apple/internal/apple_framework_import.bzl index c893f6e2b0..90544fbce0 100644 --- a/apple/internal/apple_framework_import.bzl +++ b/apple/internal/apple_framework_import.bzl @@ -69,6 +69,10 @@ load( "//apple/internal/aspects:swift_usage_aspect.bzl", "SwiftUsageInfo", ) +load( + "//apple/internal/providers:apple_dynamic_framework_info.bzl", + "new_appledynamicframeworkinfo", +) load( "//apple/internal/providers:framework_import_bundle_info.bzl", "AppleFrameworkImportBundleInfo", @@ -281,7 +285,7 @@ def _apple_dynamic_framework_import_impl(ctx): # Create AppleDynamicFramework provider. framework_groups = _grouped_framework_files(framework_imports) framework_dirs_set = depset(framework_groups.keys()) - providers.append(framework_import_support.new_dynamic_framework_provider( + providers.append(new_appledynamicframeworkinfo( cc_info = cc_info, framework_dirs = framework_dirs_set, framework_files = depset(framework_imports), diff --git a/apple/internal/apple_xcframework_import.bzl b/apple/internal/apple_xcframework_import.bzl index 150761c315..c663101b78 100644 --- a/apple/internal/apple_xcframework_import.bzl +++ b/apple/internal/apple_xcframework_import.bzl @@ -43,6 +43,11 @@ load( "//apple/internal/aspects:swift_usage_aspect.bzl", "SwiftUsageInfo", ) +load( + "//apple/internal/providers:apple_dynamic_framework_info.bzl", + "AppleDynamicFrameworkInfo", + "new_appledynamicframeworkinfo", +) load( "//apple/internal/providers:framework_import_bundle_info.bzl", "AppleFrameworkImportBundleInfo", @@ -513,7 +518,7 @@ def _apple_dynamic_xcframework_import_impl(ctx): providers.append(cc_info) # Create AppleDynamicFrameworkInfo provider - apple_dynamic_framework_info = framework_import_support.new_dynamic_framework_provider( + apple_dynamic_framework_info = new_appledynamicframeworkinfo( cc_info = cc_info, ) providers.append(apple_dynamic_framework_info) @@ -751,7 +756,7 @@ Unnecssary and ignored, will be removed in the future. provides = [ AppleFrameworkImportInfo, CcInfo, - apple_common.AppleDynamicFramework, + AppleDynamicFrameworkInfo, ], toolchains = use_cpp_toolchain(), ) diff --git a/apple/internal/framework_import_support.bzl b/apple/internal/framework_import_support.bzl index 5570ebebe5..593bfedd7c 100644 --- a/apple/internal/framework_import_support.bzl +++ b/apple/internal/framework_import_support.bzl @@ -411,20 +411,6 @@ def _has_versioned_framework_files(framework_files): return True return False -def _new_dynamic_framework_provider(**kwargs): - """A wrapper API for the Bazel API of the same name to better support multiple Bazel versions - - Args: - **kwargs: Arguments to pass if supported. - """ - - # TODO: Remove once we drop bazel 7.x - _OBJC_PROVIDER_LINKING = hasattr(apple_common.new_objc_provider(), "linkopt") - if not _OBJC_PROVIDER_LINKING: - kwargs.pop("objc", None) - - return apple_common.new_dynamic_framework_provider(**kwargs) - def _swift_info_from_module_interface( *, actions, @@ -497,7 +483,6 @@ framework_import_support = struct( framework_import_info_with_dependencies = _framework_import_info_with_dependencies, get_swift_module_files_with_target_triplet = _get_swift_module_files_with_target_triplet, has_versioned_framework_files = _has_versioned_framework_files, - new_dynamic_framework_provider = _new_dynamic_framework_provider, swift_info_from_module_interface = _swift_info_from_module_interface, swift_interop_info_with_dependencies = _swift_interop_info_with_dependencies, ) diff --git a/apple/internal/ios_rules.bzl b/apple/internal/ios_rules.bzl index cc7553391d..ed75fae90a 100644 --- a/apple/internal/ios_rules.bzl +++ b/apple/internal/ios_rules.bzl @@ -84,6 +84,7 @@ load( ) load( "//apple/internal:providers.bzl", + "new_appleexecutablebinaryinfo", "new_appleframeworkbundleinfo", "new_iosappclipbundleinfo", "new_iosapplicationbundleinfo", @@ -523,7 +524,7 @@ def _ios_application_impl(ctx): processor_result.output_groups, ) ), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), @@ -837,7 +838,7 @@ def _ios_app_clip_impl(ctx): processor_result.output_groups, ) ), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), @@ -1398,7 +1399,7 @@ def _ios_extension_impl(ctx): DefaultInfo( files = processor_result.output_files, ), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), diff --git a/apple/internal/linking_support.bzl b/apple/internal/linking_support.bzl index 7e15ec15ec..f50d89e59b 100644 --- a/apple/internal/linking_support.bzl +++ b/apple/internal/linking_support.bzl @@ -23,6 +23,319 @@ load( "//apple/internal:entitlements_support.bzl", "entitlements_support", ) +load( + "//apple/internal:multi_arch_binary_support.bzl", + "get_split_target_triplet", + "subtract_linking_contexts", +) +load( + "//apple/internal:providers.bzl", + "AppleExecutableBinaryInfo", + "new_appledebugoutputsinfo", +) +load( + "//apple/internal/providers:apple_dynamic_framework_info.bzl", + "AppleDynamicFrameworkInfo", +) + +ObjcInfo = apple_common.Objc + +def _link_multi_arch_static_library(ctx): + """Links a (potentially multi-architecture) static library targeting Apple platforms. + + Rule context is a required parameter due to usage of the cc_common.configure_features API. + + Args: + ctx: The Starlark rule context. + + Returns: + A Starlark struct containing the following attributes: + - output_groups: OutputGroupInfo provider from transitive CcInfo validation_artifacts. + - outputs: List of structs containing the following attributes: + - library: Artifact representing a linked static library. + - architecture: Linked static library architecture (e.g. 'arm64', 'x86_64'). + - platform: Linked static library target Apple platform (e.g. 'ios', 'macos'). + - environment: Linked static library environment (e.g. 'device', 'simulator'). + """ + + # TODO: Delete when we drop bazel 7.x + legacy_linking_function = getattr(apple_common, "link_multi_arch_static_library", None) + if legacy_linking_function: + return legacy_linking_function(ctx = ctx) + + split_target_triplets = get_split_target_triplet(ctx) + + split_deps = ctx.split_attr.deps + split_avoid_deps = ctx.split_attr.avoid_deps + child_configs_and_toolchains = ctx.split_attr._child_configuration_dummy + + outputs = [] + + for split_transition_key, child_toolchain in child_configs_and_toolchains.items(): + cc_toolchain = child_toolchain[cc_common.CcToolchainInfo] + common_variables = apple_common.compilation_support.build_common_variables( + ctx = ctx, + toolchain = cc_toolchain, + use_pch = True, + deps = split_deps[split_transition_key], + ) + + avoid_objc_providers = [] + avoid_cc_providers = [] + avoid_cc_linking_contexts = [] + + if len(split_avoid_deps.keys()): + for dep in split_avoid_deps[split_transition_key]: + if ObjcInfo in dep: + avoid_objc_providers.append(dep[ObjcInfo]) + if CcInfo in dep: + avoid_cc_providers.append(dep[CcInfo]) + avoid_cc_linking_contexts.append(dep[CcInfo].linking_context) + + name = ctx.label.name + "-" + cc_toolchain.target_gnu_system_name + "-fl" + + cc_linking_context = subtract_linking_contexts( + owner = ctx.label, + linking_contexts = common_variables.objc_linking_context.cc_linking_contexts, + avoid_dep_linking_contexts = avoid_cc_linking_contexts, + ) + linking_outputs = apple_common.compilation_support.register_fully_link_action( + name = name, + common_variables = common_variables, + cc_linking_context = cc_linking_context, + ) + + output = { + "library": linking_outputs.library_to_link.static_library, + } + + if split_target_triplets != None: + target_triplet = split_target_triplets.get(split_transition_key) + output["platform"] = target_triplet.platform + output["architecture"] = target_triplet.architecture + output["environment"] = target_triplet.environment + + outputs.append(struct(**output)) + + header_tokens = [] + for _, deps in split_deps.items(): + for dep in deps: + if CcInfo in dep: + header_tokens.append(dep[CcInfo].compilation_context.validation_artifacts) + + output_groups = {"_validation": depset(transitive = header_tokens)} + + return struct( + outputs = outputs, + output_groups = OutputGroupInfo(**output_groups), + ) + +def _link_multi_arch_binary( + *, + ctx, + avoid_deps = [], + extra_linkopts = [], + extra_link_inputs = [], + extra_requested_features = [], + extra_disabled_features = [], + stamp = -1, + variables_extension = {}): + """Links a (potentially multi-architecture) binary targeting Apple platforms. + + This method comprises a bulk of the logic of the Starlark `apple_binary` + rule in the rules_apple domain and exists to aid in the migration of its + linking logic to Starlark in rules_apple. + + This API is **highly experimental** and subject to change at any time. Do + not depend on the stability of this function at this time. + + Args: + ctx: The Starlark rule context. + avoid_deps: A list of `Target`s representing dependencies of the binary but + whose libraries should not be linked into the binary. This is the case for + dependencies that will be found at runtime in another image, such as the + bundle loader or any dynamic libraries/frameworks that will be loaded by + this binary. + extra_linkopts: A list of strings: Extra linkopts to add to the linking action. + extra_link_inputs: A list of strings: Extra files to pass to the linker action. + extra_requested_features: A list of strings: Extra requested features to be passed + to the linker action. + extra_disabled_features: A list of strings: Extra disabled features to be passed + to the linker action. + stamp: Whether to include build information in the linked binary. If 1, build + information is always included. If 0, build information is always excluded. + If -1 (the default), then the behavior is determined by the --[no]stamp + flag. This should be set to 0 when generating the executable output for + test rules. + variables_extension: A dictionary of user-defined variables to be added to the + toolchain configuration when create link command line. + + Returns: + A `struct` which contains the following fields: + * `cc_info`: The CcInfo provider containing information about the targets that were + linked. + * `outputs`: A `list` of `struct`s containing the single-architecture binaries and + debug outputs, with identifying information about the target platform, architecture, + and environment that each was built for. + * `output_groups`: A `dict` with the single key `_validation` and as valuea depset + containing the validation artifacts from the compilation contexts of the CcInfo + providers of the targets that were linked. + * `debug_outputs_provider`: An AppleDebugOutputs provider + """ + + # TODO: Delete when we drop bazel 7.x + legacy_linking_function = getattr(apple_common, "link_multi_arch_binary", None) + if legacy_linking_function: + return legacy_linking_function( + ctx = ctx, + avoid_deps = avoid_deps, + extra_linkopts = extra_linkopts, + extra_link_inputs = extra_link_inputs, + extra_requested_features = extra_requested_features, + extra_disabled_features = extra_disabled_features, + variables_extension = variables_extension, + stamp = stamp, + ) + + split_target_triplets = get_split_target_triplet(ctx) + split_build_configs = apple_common.get_split_build_configs(ctx) + split_deps = ctx.split_attr.deps + child_configs_and_toolchains = ctx.split_attr._child_configuration_dummy + + if split_deps and split_deps.keys() != child_configs_and_toolchains.keys(): + fail(("Split transition keys are different between 'deps' [%s] and " + + "'_child_configuration_dummy' [%s]") % ( + split_deps.keys(), + child_configs_and_toolchains.keys(), + )) + + avoid_cc_infos = [ + dep[AppleDynamicFrameworkInfo].cc_info + for dep in avoid_deps + if AppleDynamicFrameworkInfo in dep + ] + avoid_cc_infos.extend([ + dep[AppleExecutableBinaryInfo].cc_info + for dep in avoid_deps + if AppleExecutableBinaryInfo in dep + ]) + avoid_cc_infos.extend([dep[CcInfo] for dep in avoid_deps if CcInfo in dep]) + avoid_cc_linking_contexts = [dep.linking_context for dep in avoid_cc_infos] + + outputs = [] + cc_infos = [] + legacy_debug_outputs = {} + + cc_infos.extend(avoid_cc_infos) + + # $(location...) is only used in one test, and tokenize only affects linkopts in one target + additional_linker_inputs = getattr(ctx.attr, "additional_linker_inputs", []) + attr_linkopts = [ + ctx.expand_location(opt, targets = additional_linker_inputs) + for opt in getattr(ctx.attr, "linkopts", []) + ] + attr_linkopts = [token for opt in attr_linkopts for token in ctx.tokenize(opt)] + + for split_transition_key, child_toolchain in child_configs_and_toolchains.items(): + cc_toolchain = child_toolchain[cc_common.CcToolchainInfo] + deps = split_deps.get(split_transition_key, []) + target_triplet = split_target_triplets.get(split_transition_key) + + common_variables = apple_common.compilation_support.build_common_variables( + ctx = ctx, + toolchain = cc_toolchain, + deps = deps, + extra_disabled_features = extra_disabled_features, + extra_enabled_features = extra_requested_features, + attr_linkopts = attr_linkopts, + ) + + cc_infos.append(CcInfo( + compilation_context = cc_common.merge_compilation_contexts( + compilation_contexts = + common_variables.objc_compilation_context.cc_compilation_contexts, + ), + linking_context = cc_common.merge_linking_contexts( + linking_contexts = common_variables.objc_linking_context.cc_linking_contexts, + ), + )) + + cc_linking_context = subtract_linking_contexts( + owner = ctx.label, + linking_contexts = common_variables.objc_linking_context.cc_linking_contexts + + avoid_cc_linking_contexts, + avoid_dep_linking_contexts = avoid_cc_linking_contexts, + ) + + child_config = split_build_configs.get(split_transition_key) + + additional_outputs = [] + extensions = {} + + dsym_binary = None + if ctx.fragments.cpp.apple_generate_dsym: + if ctx.fragments.cpp.objc_should_strip_binary: + suffix = "_bin_unstripped.dwarf" + else: + suffix = "_bin.dwarf" + dsym_binary = ctx.actions.declare_shareable_artifact( + ctx.label.package + "/" + ctx.label.name + suffix, + child_config.bin_dir, + ) + extensions["dsym_path"] = dsym_binary.path # dsym symbol file + additional_outputs.append(dsym_binary) + legacy_debug_outputs.setdefault(target_triplet.architecture, {})["dsym_binary"] = dsym_binary + + linkmap = None + if ctx.fragments.cpp.objc_generate_linkmap: + linkmap = ctx.actions.declare_shareable_artifact( + ctx.label.package + "/" + ctx.label.name + ".linkmap", + child_config.bin_dir, + ) + extensions["linkmap_exec_path"] = linkmap.path # linkmap file + additional_outputs.append(linkmap) + legacy_debug_outputs.setdefault(target_triplet.architecture, {})["linkmap"] = linkmap + + name = ctx.label.name + "_bin" + executable = apple_common.compilation_support.register_configuration_specific_link_actions( + name = name, + common_variables = common_variables, + cc_linking_context = cc_linking_context, + build_config = child_config, + extra_link_args = extra_linkopts, + stamp = stamp, + user_variable_extensions = variables_extension | extensions, + additional_outputs = additional_outputs, + deps = deps, + extra_link_inputs = extra_link_inputs, + attr_linkopts = attr_linkopts, + ) + + output = { + "binary": executable, + "platform": target_triplet.platform, + "architecture": target_triplet.architecture, + "environment": target_triplet.environment, + "dsym_binary": dsym_binary, + "linkmap": linkmap, + } + + outputs.append(struct(**output)) + + header_tokens = [] + for _, deps in split_deps.items(): + for dep in deps: + if CcInfo in dep: + header_tokens.append(dep[CcInfo].compilation_context.validation_artifacts) + + output_groups = {"_validation": depset(transitive = header_tokens)} + + return struct( + cc_info = cc_common.merge_cc_infos(direct_cc_infos = cc_infos), + output_groups = output_groups, + outputs = outputs, + debug_outputs_provider = new_appledebugoutputsinfo(outputs_map = legacy_debug_outputs), + ) def _debug_outputs_by_architecture(link_outputs): """Returns debug outputs indexed by architecture from `register_binary_linking_action` output. @@ -51,18 +364,6 @@ def _debug_outputs_by_architecture(link_outputs): linkmaps = linkmaps, ) -def _new_executable_binary_provider(*, binary, cc_info): - """Wrap the apple_common API of the same name to better support multiple Bazel versions. - - Args: - binary: The `File` representing the binary. - cc_info: The `CcInfo` provider for the binary. - """ - return apple_common.new_executable_binary_provider( - binary = binary, - cc_info = cc_info, - ) - def _sectcreate_objc_provider(label, segname, sectname, file): """Returns an objc provider that propagates a section in a linked binary. @@ -123,11 +424,11 @@ def _register_binary_linking_action( avoid_deps: A list of `Target`s representing dependencies of the binary but whose symbols should not be linked into it. bundle_loader: For Mach-O bundles, the `Target` whose binary will load this bundle. - This target must propagate the `apple_common.AppleExecutableBinary` provider. + This target must propagate the `AppleExecutableBinaryInfo` provider. This simplifies the process of passing the bundle loader to all the arguments that need it: the binary will automatically be added to the linker inputs, its path will be added to linkopts via `-bundle_loader`, and the `apple_common.Objc` - provider of its dependencies (obtained from the `AppleExecutableBinary` provider) + provider of its dependencies (obtained from the `AppleExecutableBinaryInfo` provider) will be passed as an additional `avoid_dep` to ensure that those dependencies are subtracted when linking the bundle's binary. entitlements: An optional `File` that provides the processed entitlements for the @@ -152,8 +453,7 @@ def _register_binary_linking_action( for test rules. Returns: - A `struct` which contains the following fields, which are a superset of the fields - returned by `apple_common.link_multi_arch_binary`: + A `struct` which contains the following fields: * `binary`: The final binary `File` that was linked. If only one architecture was requested, then it is a symlink to that single architecture binary. Otherwise, it @@ -167,6 +467,7 @@ def _register_binary_linking_action( and environment that each was built for. * `output_groups`: A `dict` containing output groups that should be returned in the `OutputGroupInfo` provider of the calling rule. + * `debug_outputs_provider`: An AppleDebugOutputs provider """ linkopts = [] link_inputs = [] @@ -219,12 +520,12 @@ def _register_binary_linking_action( all_avoid_deps = list(avoid_deps) if bundle_loader: - bundle_loader_file = bundle_loader[apple_common.AppleExecutableBinary].binary + bundle_loader_file = bundle_loader[AppleExecutableBinaryInfo].binary all_avoid_deps.append(bundle_loader) linkopts.extend(["-bundle_loader", bundle_loader_file.path]) link_inputs.append(bundle_loader_file) - linking_outputs = apple_common.link_multi_arch_binary( + linking_outputs = _link_multi_arch_binary( ctx = ctx, avoid_deps = all_avoid_deps, extra_linkopts = linkopts, @@ -262,8 +563,7 @@ def _register_static_library_linking_action(ctx): ctx: The rule context. Returns: - A `struct` which contains the following fields, which are a superset of the fields - returned by `apple_common.link_multi_arch_static_library`: + A `struct` which contains the following fields: * `library`: The final library `File` that was linked. If only one architecture was requested, then it is a symlink to that single architecture binary. Otherwise, it @@ -276,7 +576,7 @@ def _register_static_library_linking_action(ctx): * `output_groups`: A `dict` containing output groups that should be returned in the `OutputGroupInfo` provider of the calling rule. """ - linking_outputs = apple_common.link_multi_arch_static_library(ctx = ctx) + linking_outputs = _link_multi_arch_static_library(ctx = ctx) fat_library = ctx.actions.declare_file("{}_lipo.a".format(ctx.label.name)) @@ -319,7 +619,8 @@ def _lipo_or_symlink_inputs(*, actions, inputs, output, apple_fragment, xcode_co # Symlink if there was only a single architecture created; it's faster. actions.symlink(target_file = inputs[0], output = output) -def _link_multi_arch_binary( +# TODO: Delete when we take https://github.com/bazelbuild/rules_apple/commit/29eb94cbc9b1a898582e1e238cc2551ddbeaa58b +def _legacy_link_multi_arch_binary( *, actions, additional_inputs = [], @@ -450,9 +751,9 @@ def _link_multi_arch_binary( linking_support = struct( debug_outputs_by_architecture = _debug_outputs_by_architecture, + legacy_link_multi_arch_binary = _legacy_link_multi_arch_binary, link_multi_arch_binary = _link_multi_arch_binary, lipo_or_symlink_inputs = _lipo_or_symlink_inputs, - new_executable_binary_provider = _new_executable_binary_provider, register_binary_linking_action = _register_binary_linking_action, register_static_library_linking_action = _register_static_library_linking_action, sectcreate_objc_provider = _sectcreate_objc_provider, diff --git a/apple/internal/macos_rules.bzl b/apple/internal/macos_rules.bzl index ba1858e042..6f36246a6d 100644 --- a/apple/internal/macos_rules.bzl +++ b/apple/internal/macos_rules.bzl @@ -86,7 +86,9 @@ load( ) load( "//apple/internal:providers.bzl", + "AppleExecutableBinaryInfo", "new_applebinaryinfo", + "new_appleexecutablebinaryinfo", "new_appleframeworkbundleinfo", "new_macosapplicationbundleinfo", "new_macosbundlebundleinfo", @@ -453,7 +455,7 @@ def _macos_application_impl(ctx): processor_result.output_groups, ) ), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), @@ -967,7 +969,7 @@ def _macos_extension_impl(ctx): DefaultInfo( files = processor_result.output_files, ), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), @@ -2092,7 +2094,7 @@ def _macos_command_line_application_impl(ctx): processor_result.output_groups, ) ), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), @@ -2377,7 +2379,7 @@ The target representing the executable that will be loading this bundle. Undefin 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], + providers = [AppleExecutableBinaryInfo], ), }, ], diff --git a/apple/internal/multi_arch_binary_support.bzl b/apple/internal/multi_arch_binary_support.bzl new file mode 100644 index 0000000000..6bd62971a3 --- /dev/null +++ b/apple/internal/multi_arch_binary_support.bzl @@ -0,0 +1,104 @@ +# Copyright 2024 The Bazel Authors. All rights reserved. +# +# Licensed under the Apache License(**kwargs): 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(**kwargs): software +# distributed under the License is distributed on an "AS IS" BASIS(**kwargs): +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND(**kwargs): either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Support utility for creating multi-arch Apple binaries.""" + +visibility([ + "//apple/...", +]) + +TargetTripletInfo = provider( + "Contains the target triplet (architecture, platform, environment) for a given configuration.", + fields = { + "architecture": "string, the CPU as returned by AppleConfiguration.getSingleArchitecture()", + "platform": "apple_platform.PLATFORM_TYPE string as returned by apple_platform.get_target_platform()", + "environment": "string ('device', 'simulator' or 'macabi) as returned by apple_platform.get_target_environment", + }, +) + +def _build_avoid_library_set(avoid_dep_linking_contexts): + avoid_library_set = dict() + for linking_context in avoid_dep_linking_contexts: + for linker_input in linking_context.linker_inputs.to_list(): + for library_to_link in linker_input.libraries: + library_artifact = apple_common.compilation_support.get_static_library_for_linking(library_to_link) + if library_artifact: + avoid_library_set[library_artifact.short_path] = True + return avoid_library_set + +def subtract_linking_contexts(owner, linking_contexts, avoid_dep_linking_contexts): + """Subtracts the libraries in avoid_dep_linking_contexts from linking_contexts. + + Args: + owner: The label of the target currently being analyzed. + linking_contexts: An iterable of CcLinkingContext objects. + avoid_dep_linking_contexts: An iterable of CcLinkingContext objects. + + Returns: + A CcLinkingContext object. + """ + libraries = [] + user_link_flags = [] + additional_inputs = [] + linkstamps = [] + avoid_library_set = _build_avoid_library_set(avoid_dep_linking_contexts) + for linking_context in linking_contexts: + for linker_input in linking_context.linker_inputs.to_list(): + for library_to_link in linker_input.libraries: + library_artifact = apple_common.compilation_support.get_library_for_linking(library_to_link) + if library_artifact.short_path not in avoid_library_set: + libraries.append(library_to_link) + user_link_flags.extend(linker_input.user_link_flags) + additional_inputs.extend(linker_input.additional_inputs) + linkstamps.extend(linker_input.linkstamps) + linker_input = cc_common.create_linker_input( + owner = owner, + libraries = depset(libraries, order = "topological"), + user_link_flags = user_link_flags, + additional_inputs = depset(additional_inputs), + linkstamps = depset(linkstamps), + ) + return cc_common.create_linking_context( + linker_inputs = depset([linker_input]), + owner = owner, + ) + +def _get_target_triplet(config): + """Returns the target triplet (architecture, platform, environment) for a given configuration.""" + cpu_platform = apple_common.apple_platform.for_target_cpu(apple_common.get_cpu(config)) + apple_config = apple_common.get_apple_config(config) + + return TargetTripletInfo( + architecture = apple_config.single_arch_cpu, + platform = apple_common.apple_platform.get_target_platform(cpu_platform), + environment = apple_common.apple_platform.get_target_environment(cpu_platform), + ) + +def get_split_target_triplet(ctx): + """Transforms a rule context's ctads to a Starlark Dict mapping transitions to target triplets. + + Args: + ctx: The Starlark rule context. + + Returns: + A Starlark Dict keyed by split transition keys with + their target triplet (architecture, platform, environment) as value. + """ + result = dict() + ctads = apple_common.get_split_prerequisites(ctx) + for split_transition_key, config in ctads.items(): + if split_transition_key == None: + fail("unexpected empty key in split transition") + result[split_transition_key] = _get_target_triplet(config) + return result diff --git a/apple/internal/partials/BUILD b/apple/internal/partials/BUILD index 52a3b65664..bf4b26623c 100644 --- a/apple/internal/partials/BUILD +++ b/apple/internal/partials/BUILD @@ -235,6 +235,7 @@ bzl_library( ], deps = [ "//apple/internal:framework_import_support", + "//apple/internal/providers:apple_dynamic_framework_info", "@bazel_skylib//lib:partial", "@bazel_skylib//lib:paths", ], diff --git a/apple/internal/partials/app_intents_metadata_bundle.bzl b/apple/internal/partials/app_intents_metadata_bundle.bzl index 351ae0c70e..c665a68fd8 100644 --- a/apple/internal/partials/app_intents_metadata_bundle.bzl +++ b/apple/internal/partials/app_intents_metadata_bundle.bzl @@ -49,7 +49,7 @@ def _app_intents_metadata_bundle_partial_impl( # # TODO(b/295227222): Avoid this linker step for Xcode 15.0+ when rules_swift supports the new # swiftconstvalues-based manner of handling App Intents metadata. - link_result = linking_support.link_multi_arch_binary( + link_result = linking_support.legacy_link_multi_arch_binary( actions = actions, cc_toolchains = cc_toolchains, ctx = ctx, diff --git a/apple/internal/partials/framework_provider.bzl b/apple/internal/partials/framework_provider.bzl index 32f6145de3..776396a046 100644 --- a/apple/internal/partials/framework_provider.bzl +++ b/apple/internal/partials/framework_provider.bzl @@ -23,8 +23,8 @@ load( "paths", ) load( - "//apple/internal:framework_import_support.bzl", - "framework_import_support", + "//apple/internal/providers:apple_dynamic_framework_info.bzl", + "new_appledynamicframeworkinfo", ) def _framework_provider_partial_impl( @@ -80,7 +80,7 @@ def _framework_provider_partial_impl( ], ) - framework_provider = framework_import_support.new_dynamic_framework_provider( + framework_provider = new_appledynamicframeworkinfo( binary = binary_artifact, cc_info = wrapper_cc_info, framework_dirs = depset([absolute_framework_dir]), diff --git a/apple/internal/providers.bzl b/apple/internal/providers.bzl index abf65b083d..4b667acbc6 100644 --- a/apple/internal/providers.bzl +++ b/apple/internal/providers.bzl @@ -199,6 +199,21 @@ target if one was generated. init = _make_banned_init(provider_name = "AppleCodesigningDossierInfo"), ) +AppleDebugOutputsInfo, new_appledebugoutputsinfo = provider( + """ +Holds debug outputs of an Apple binary rule. + +This provider is DEPRECATED. Preferably use `AppleDsymBundleInfo` instead. + +The only field is `output_map`, which is a dictionary of: + `{ arch: { "dsym_binary": File, "linkmap": File }` + +Where `arch` is any Apple architecture such as "arm64" or "armv7". +""", + fields = ["outputs_map"], + init = _make_banned_init(provider_name = "AppleDebugOutputsInfo"), +) + AppleDsymBundleInfo, new_appledsymbundleinfo = provider( doc = "Provides information for an Apple dSYM bundle.", fields = { @@ -214,6 +229,36 @@ dependencies of the given target if any were generated. init = _make_banned_init(provider_name = "AppleDsymBundleInfo"), ) +_AppleExecutableBinaryInfo = provider( + doc = """ +Contains the executable binary output that was built using +`link_multi_arch_binary` with the `executable` binary type. +""", + fields = { + # TODO: Remove when we drop 7.x + "objc": """\ +apple_common.Objc provider used for legacy linking behavior. +""", + "binary": """\ +The executable binary artifact output by `link_multi_arch_binary`. +""", + "cc_info": """\ +A `CcInfo` which contains information about the transitive dependencies linked +into the binary. +""", + }, +) + +AppleExecutableBinaryInfo = getattr(apple_common, "AppleExecutableBinary", _AppleExecutableBinaryInfo) + +# TODO: Use common init pattern when we drop 7.x +def new_appleexecutablebinaryinfo(**kwargs): + legacy_initializer = getattr(apple_common, "new_executable_binary_provider", None) + if legacy_initializer: + return legacy_initializer(**kwargs) + + return AppleExecutableBinaryInfo(**kwargs) + AppleExtraOutputsInfo, new_appleextraoutputsinfo = provider( doc = """ Provides information about extra outputs that should be produced from the build. diff --git a/apple/internal/providers/BUILD b/apple/internal/providers/BUILD index 04fcab9de9..2549a3a3c7 100644 --- a/apple/internal/providers/BUILD +++ b/apple/internal/providers/BUILD @@ -31,6 +31,16 @@ bzl_library( ], ) +bzl_library( + name = "apple_dynamic_framework_info", + srcs = ["apple_dynamic_framework_info.bzl"], + visibility = [ + # Visibility set to subpackages since this provider is used + # by a number of rules in `internal`. + "//apple/internal:__subpackages__", + ], +) + bzl_library( name = "embeddable_info", srcs = ["embeddable_info.bzl"], diff --git a/apple/internal/providers/apple_dynamic_framework_info.bzl b/apple/internal/providers/apple_dynamic_framework_info.bzl new file mode 100644 index 0000000000..fa94aaff2a --- /dev/null +++ b/apple/internal/providers/apple_dynamic_framework_info.bzl @@ -0,0 +1,49 @@ +# 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. + +"""AppleDynamicFrameworkInfo provider implementation.""" + +visibility([ + "//apple/internal/...", +]) + +_AppleDynamicFrameworkInfo = provider( + doc = "Contains information about an Apple dynamic framework.", + fields = { + "framework_dirs": """\ +The framework path names used as link inputs in order to link against the +dynamic framework. +""", + "framework_files": """\ +The full set of artifacts that should be included as inputs to link against the +dynamic framework. +""", + "binary": "The dylib binary artifact of the dynamic framework.", + "cc_info": """\ +A `CcInfo` which contains information about the transitive dependencies linked +into the binary. +""", + }, +) + +# TODO: Remove when we drop 7.x +AppleDynamicFrameworkInfo = getattr(apple_common, "AppleDynamicFramework", _AppleDynamicFrameworkInfo) + +# TODO: Remove when we drop 7.x +def new_appledynamicframeworkinfo(**kwargs): + legacy_initializer = getattr(apple_common, "new_dynamic_framework_provider", None) + if legacy_initializer: + return legacy_initializer(**kwargs) + + return AppleDynamicFrameworkInfo(**kwargs) diff --git a/apple/internal/testing/apple_test_bundle_support.bzl b/apple/internal/testing/apple_test_bundle_support.bzl index 34aa473c5b..8fdde7943d 100644 --- a/apple/internal/testing/apple_test_bundle_support.bzl +++ b/apple/internal/testing/apple_test_bundle_support.bzl @@ -74,6 +74,7 @@ load( ) load( "//apple/internal:providers.bzl", + "AppleExecutableBinaryInfo", "new_appleextraoutputsinfo", "new_appletestinfo", ) @@ -343,7 +344,7 @@ def _apple_test_bundle_impl(*, ctx, product_type): # is never passed as the bundle loader, because the host application is loaded out-of-process.) if ( rule_descriptor.product_type == apple_product_type.unit_test_bundle and - test_host and apple_common.AppleExecutableBinary in test_host and + test_host and AppleExecutableBinaryInfo in test_host and ctx.attr.test_host_is_bundle_loader ): bundle_loader = test_host diff --git a/apple/internal/tvos_rules.bzl b/apple/internal/tvos_rules.bzl index 41ef5fe47b..52e4cb2285 100644 --- a/apple/internal/tvos_rules.bzl +++ b/apple/internal/tvos_rules.bzl @@ -82,6 +82,7 @@ load( ) load( "//apple/internal:providers.bzl", + "new_appleexecutablebinaryinfo", "new_appleframeworkbundleinfo", "new_tvosapplicationbundleinfo", "new_tvosextensionbundleinfo", @@ -458,7 +459,7 @@ def _tvos_application_impl(ctx): ) ), new_tvosapplicationbundleinfo(), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), @@ -1297,7 +1298,7 @@ def _tvos_extension_impl(ctx): processor_result.output_groups, ) ), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), diff --git a/apple/internal/visionos_rules.bzl b/apple/internal/visionos_rules.bzl index 20d2c7f6e2..33de047b4a 100644 --- a/apple/internal/visionos_rules.bzl +++ b/apple/internal/visionos_rules.bzl @@ -79,6 +79,7 @@ load( "ApplePlatformInfo", "VisionosExtensionBundleInfo", "VisionosFrameworkBundleInfo", + "new_appleexecutablebinaryinfo", "new_appleframeworkbundleinfo", "new_visionosapplicationbundleinfo", "new_visionosextensionbundleinfo", @@ -462,7 +463,7 @@ Resolved Xcode is version {xcode_version}. ) ), new_visionosapplicationbundleinfo(), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), @@ -1298,7 +1299,7 @@ def _visionos_extension_impl(ctx): processor_result.output_groups, ) ), - linking_support.new_executable_binary_provider( + new_appleexecutablebinaryinfo( binary = binary_artifact, cc_info = link_result.cc_info, ), diff --git a/apple/providers.bzl b/apple/providers.bzl index 84aee66309..3f715ece43 100644 --- a/apple/providers.bzl +++ b/apple/providers.bzl @@ -36,7 +36,9 @@ load( _AppleBundleInfo = "AppleBundleInfo", _AppleBundleVersionInfo = "AppleBundleVersionInfo", _AppleCodesigningDossierInfo = "AppleCodesigningDossierInfo", + _AppleDebugOutputsInfo = "AppleDebugOutputsInfo", _AppleDsymBundleInfo = "AppleDsymBundleInfo", + _AppleExecutableBinaryInfo = "AppleExecutableBinaryInfo", _AppleExtraOutputsInfo = "AppleExtraOutputsInfo", _AppleFrameworkBundleInfo = "AppleFrameworkBundleInfo", _AppleFrameworkImportInfo = "AppleFrameworkImportInfo", @@ -85,7 +87,9 @@ AppleBundleInfo = _AppleBundleInfo AppleBinaryInfo = _AppleBinaryInfo AppleBundleVersionInfo = _AppleBundleVersionInfo AppleCodesigningDossierInfo = _AppleCodesigningDossierInfo +AppleDebugOutputsInfo = _AppleDebugOutputsInfo AppleDsymBundleInfo = _AppleDsymBundleInfo +AppleExecutableBinaryInfo = _AppleExecutableBinaryInfo AppleExtraOutputsInfo = _AppleExtraOutputsInfo AppleFrameworkBundleInfo = _AppleFrameworkBundleInfo AppleFrameworkImportInfo = _AppleFrameworkImportInfo diff --git a/doc/providers.md b/doc/providers.md index b398d6d791..3b17be59ec 100644 --- a/doc/providers.md +++ b/doc/providers.md @@ -149,6 +149,31 @@ Provides information around the use of a code signing dossier. | dossier | A `File` reference to the code signing dossier zip that acts as a direct dependency of the given target if one was generated. | + + +## AppleDebugOutputsInfo + +
+AppleDebugOutputsInfo(outputs_map)
+
+ +Holds debug outputs of an Apple binary rule. + +This provider is DEPRECATED. Preferably use `AppleDsymBundleInfo` instead. + +The only field is `output_map`, which is a dictionary of: + `{ arch: { "dsym_binary": File, "linkmap": File }` + +Where `arch` is any Apple architecture such as "arm64" or "armv7". + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| outputs_map | - | + + ## AppleDeviceTestRunnerInfo @@ -187,6 +212,26 @@ Provides information for an Apple dSYM bundle. | transitive_dsyms | `depset` containing `File` references to each of the dSYM bundles that act as transitive dependencies of the given target if any were generated. | + + +## AppleExecutableBinaryInfo + +
+AppleExecutableBinaryInfo(binary, cc_info)
+
+ +Contains the executable binary output that was built using +`link_multi_arch_binary` with the `executable` binary type. + +**FIELDS** + + +| Name | Description | +| :------------- | :------------- | +| binary | The executable binary artifact output by `link_multi_arch_binary`. | +| cc_info | A `CcInfo` which contains information about the transitive dependencies linked into the binary. | + + ## AppleExtraOutputsInfo