Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow to opt-out from library evolution #3

Merged
merged 2 commits into from
Feb 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apple/build_settings/build_settings.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ Declare a code signing identity, to be used in all code signing flows related to
"use_tree_artifacts_outputs": struct(
doc = """
Enables Bazel's tree artifacts for Apple bundle rules (instead of archives).
""",
default = False,
),
"no_library_evolution": struct(
doc = """
Disables library evolution and prevents the emission of .swiftinterface files.
""",
default = False,
),
Expand Down
27 changes: 19 additions & 8 deletions apple/internal/partials/swift_framework.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -70,14 +70,25 @@ issue with a reproducible error case.

found_module_name = swift_module.name

bundle_interface = swift_info_support.declare_swiftinterface(
actions = actions,
arch = arch,
label_name = label_name,
output_discriminator = output_discriminator,
swiftinterface = swift_module.swift.swiftinterface,
)
bundle_files.append((processor.location.bundle, modules_parent, depset([bundle_interface])))
# A swiftinterface will not be present when library evolution is disabled, if so, fallback to swiftmodule.
if swift_module.swift.swiftinterface:
bundle_interface = swift_info_support.declare_swiftinterface(
actions = actions,
arch = arch,
label_name = label_name,
output_discriminator = output_discriminator,
swiftinterface = swift_module.swift.swiftinterface,
)
bundle_files.append((processor.location.bundle, modules_parent, depset([bundle_interface])))
else:
bundle_swiftmodule = swift_info_support.declare_swiftmodule(
actions = actions,
arch = arch,
label_name = label_name,
output_discriminator = output_discriminator,
swiftmodule = swift_module.swift.swiftmodule,
)
bundle_files.append((processor.location.bundle, modules_parent, depset([bundle_swiftmodule])))

bundle_doc = swift_info_support.declare_swiftdoc(
actions = actions,
Expand Down
35 changes: 34 additions & 1 deletion apple/internal/swift_info_support.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ swift_library dependencies.\
""",
)

if not all([module.name, module.swift.swiftdoc, module.swift.swiftinterface]):
if not all([module.name, module.swift.swiftdoc]) or not (module.swift.swiftmodule or module.swift.swiftinterface):
fail(
"""\
error: Could not find all required artifacts and information to build a Swift framework. \
Expand Down Expand Up @@ -246,6 +246,38 @@ def _declare_swiftinterface(
)
return bundle_interface

def _declare_swiftmodule(
*,
actions,
arch,
label_name,
output_discriminator,
swiftmodule):
"""Declares the swiftmodule for this Swift framework.

Args:
actions: The actions provider from `ctx.actions`.
arch: The cpu architecture that the generated swiftdoc belongs to.
label_name: Name of the target being built.
output_discriminator: A string to differentiate between different target intermediate files
or `None`.
swiftmodule: A File referencing the swiftmodule file from a SwiftInfo provider.

Returns:
A File referencing the intermediate swiftmodule.
"""
bundle_module = intermediates.file(
actions = actions,
target_name = label_name,
output_discriminator = output_discriminator,
file_name = "{}.swiftmodule".format(arch),
)
actions.symlink(
target_file = swiftmodule,
output = bundle_module,
)
return bundle_module

swift_info_support = struct(
verify_found_module_name = _verify_found_module_name,
modules_from_avoid_deps = _modules_from_avoid_deps,
Expand All @@ -254,4 +286,5 @@ swift_info_support = struct(
declare_generated_header = _declare_generated_header,
declare_swiftdoc = _declare_swiftdoc,
declare_swiftinterface = _declare_swiftinterface,
declare_swiftmodule = _declare_swiftmodule,
)
36 changes: 28 additions & 8 deletions apple/internal/transition_support.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ def _command_line_options(
default_platforms = [settings[_CPU_TO_DEFAULT_PLATFORM_FLAG[cpu]]] if _is_bazel_7 else []
return {
build_settings_labels.use_tree_artifacts_outputs: force_bundle_outputs if force_bundle_outputs else settings[build_settings_labels.use_tree_artifacts_outputs],
build_settings_labels.no_library_evolution: settings[build_settings_labels.no_library_evolution],
"//command_line_option:apple configuration distinguisher": "applebin_" + platform_type,
"//command_line_option:apple_platform_type": platform_type,
"//command_line_option:apple_platforms": apple_platforms,
Expand Down Expand Up @@ -361,7 +362,10 @@ def _command_line_options_for_xcframework_platform(
environment = target_environment,
platform_type = platform_type,
): _command_line_options(
emit_swiftinterface = True,
emit_swiftinterface = _should_emit_swiftinterface(
settings,
is_xcframework = True,
),
environment_arch = resolved_environment_arch,
minimum_os_version = minimum_os_version,
platform_type = platform_type,
Expand All @@ -372,11 +376,28 @@ def _command_line_options_for_xcframework_platform(

return output_dictionary

def _should_emit_swiftinterface(settings, is_xcframework = False):
"""Determines if a .swiftinterface file should be generated for Swift dependencies.

By providing the option to disable the emission of these files, it allows consumers to opt out
of library evolution features, offering flexibility in how Swift dependencies are integrated and managed.
"""

# Do not emit swiftinterface file when library evolution is disabled for a given build
if settings[build_settings_labels.no_library_evolution]:
return False

# For iOS and tvOS static frameworks, it's historically been required for the underlying swift_library targets
# to generate a Swift interface file, though this is primarily for legacy support. `swiftmodule` files may
# be used as an alternative. The introduction of a private attribute `_emitswiftinterface` within these
# rules allows for selective configuration of Swift build settings across the build graph.
return is_xcframework or hasattr(attr, "_emitswiftinterface")

def _apple_rule_base_transition_impl(settings, attr):
"""Rule transition for Apple rules using Bazel CPUs and a valid Apple split transition."""
platform_type = attr.platform_type
return _command_line_options(
emit_swiftinterface = hasattr(attr, "_emitswiftinterface"),
emit_swiftinterface = _should_emit_swiftinterface(settings),
environment_arch = _environment_archs(platform_type, settings)[0],
minimum_os_version = attr.minimum_os_version,
platform_type = platform_type,
Expand All @@ -389,6 +410,7 @@ def _apple_rule_base_transition_impl(settings, attr):
# - https://github.com/bazelbuild/bazel/blob/master/src/main/java/com/google/devtools/build/lib/rules/cpp/CppOptions.java
_apple_rule_common_transition_inputs = [
build_settings_labels.use_tree_artifacts_outputs,
build_settings_labels.no_library_evolution,
"//command_line_option:apple_crosstool_top",
] + _CPU_TO_DEFAULT_PLATFORM_FLAG.values()
_apple_rule_base_transition_inputs = _apple_rule_common_transition_inputs + [
Expand All @@ -407,6 +429,7 @@ _apple_platform_transition_inputs = _apple_platforms_rule_base_transition_inputs
]
_apple_rule_base_transition_outputs = [
build_settings_labels.use_tree_artifacts_outputs,
build_settings_labels.no_library_evolution,
"//command_line_option:apple configuration distinguisher",
"//command_line_option:apple_platform_type",
"//command_line_option:apple_platforms",
Expand Down Expand Up @@ -447,7 +470,7 @@ def _apple_platforms_rule_base_transition_impl(settings, attr):
environment_arch = _environment_archs(platform_type, settings)[0]
return _command_line_options(
apple_platforms = settings["//command_line_option:apple_platforms"],
emit_swiftinterface = hasattr(attr, "_emitswiftinterface"),
emit_swiftinterface = _should_emit_swiftinterface(settings),
environment_arch = environment_arch,
minimum_os_version = minimum_os_version,
platform_type = platform_type,
Expand All @@ -470,7 +493,7 @@ def _apple_platforms_rule_bundle_output_base_transition_impl(settings, attr):
environment_arch = _environment_archs(platform_type, settings)[0]
return _command_line_options(
apple_platforms = settings["//command_line_option:apple_platforms"],
emit_swiftinterface = hasattr(attr, "_emitswiftinterface"),
emit_swiftinterface = _should_emit_swiftinterface(settings),
environment_arch = environment_arch,
force_bundle_outputs = True,
minimum_os_version = minimum_os_version,
Expand Down Expand Up @@ -547,10 +570,7 @@ def _apple_platform_split_transition_impl(settings, attr):
output_dictionary = {}
invalid_requested_archs = []

# iOS and tvOS static frameworks require underlying swift_library targets generate a Swift
# interface file. These rules define a private attribute called `_emitswiftinterface` that
# let's this transition flip rules_swift config down the build graph.
emit_swiftinterface = hasattr(attr, "_emitswiftinterface")
emit_swiftinterface = _should_emit_swiftinterface(settings)

if settings["//command_line_option:incompatible_enable_apple_toolchain_resolution"]:
platforms = (
Expand Down