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

Add ios_bundle rule #1085

Closed
wants to merge 1 commit into from
Closed
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
184 changes: 180 additions & 4 deletions apple/internal/ios_rules.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ load(
"AppleSupportToolchainInfo",
"IosAppClipBundleInfo",
"IosApplicationBundleInfo",
"IosBundleBundleInfo",
"IosExtensionBundleInfo",
"IosFrameworkBundleInfo",
"IosImessageApplicationBundleInfo",
Expand Down Expand Up @@ -127,8 +128,8 @@ def _ios_application_impl(ctx):
bundle_id = ctx.attr.bundle_id
bundle_name, bundle_extension = bundling_support.bundle_full_name_from_rule_ctx(ctx)
executable_name = bundling_support.executable_name(ctx)
bundle_verification_targets = [struct(target = ext) for ext in ctx.attr.extensions]
embeddable_targets = ctx.attr.frameworks + ctx.attr.extensions + ctx.attr.app_clips
bundle_verification_targets = [struct(target = bundle) for bundle in ctx.attr.extensions + ctx.attr.bundles]
embeddable_targets = ctx.attr.frameworks + ctx.attr.extensions + ctx.attr.app_clips + ctx.attr.bundles
entitlements = entitlements_support.entitlements(
entitlements_attr = getattr(ctx.attr, "entitlements", None),
entitlements_file = getattr(ctx.file, "entitlements", None),
Expand Down Expand Up @@ -220,7 +221,7 @@ def _ios_application_impl(ctx):
platform_prerequisites = platform_prerequisites,
provisioning_profile = getattr(ctx.file, "provisioning_profile", None),
rule_descriptor = rule_descriptor,
targets = ctx.attr.deps + ctx.attr.extensions + ctx.attr.frameworks,
targets = ctx.attr.bundles + ctx.attr.deps + ctx.attr.extensions + ctx.attr.frameworks,
),
partials.resources_partial(
actions = actions,
Expand Down Expand Up @@ -372,7 +373,7 @@ def _ios_app_clip_impl(ctx):
bundle_id = ctx.attr.bundle_id
bundle_name, bundle_extension = bundling_support.bundle_full_name_from_rule_ctx(ctx)
executable_name = bundling_support.executable_name(ctx)
embeddable_targets = ctx.attr.frameworks
embeddable_targets = ctx.attr.bundles + ctx.attr.frameworks
features = features_support.compute_enabled_features(
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
Expand Down Expand Up @@ -567,6 +568,174 @@ def _ios_app_clip_impl(ctx):
link_result.binary_provider,
] + processor_result.providers

def _ios_bundle_impl(ctx):
"""Implementation of ios_bundle."""
extra_linkopts = []
if ctx.attr.extension_safe:
extra_linkopts.append("-fapplication-extension")

link_result = linking_support.register_linking_action(
ctx,
extra_linkopts = extra_linkopts,
stamp = ctx.attr.stamp,
)
binary_artifact = link_result.binary_provider.binary
debug_outputs_provider = link_result.debug_outputs_provider

actions = ctx.actions
apple_toolchain_info = ctx.attr._toolchain[AppleSupportToolchainInfo]
bin_root_path = ctx.bin_dir.path
bundle_id = ctx.attr.bundle_id
bundle_name, bundle_extension = bundling_support.bundle_full_name_from_rule_ctx(ctx)
executable_name = bundling_support.executable_name(ctx)
entitlements = entitlements_support.entitlements(
entitlements_attr = getattr(ctx.attr, "entitlements", None),
entitlements_file = getattr(ctx.file, "entitlements", None),
)
features = features_support.compute_enabled_features(
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
label = ctx.label
platform_prerequisites = platform_support.platform_prerequisites_from_rule_ctx(ctx)
predeclared_outputs = ctx.outputs
rule_descriptor = rule_support.rule_descriptor(ctx)

archive = outputs.archive(
actions = actions,
bundle_extension = bundle_extension,
bundle_name = bundle_name,
executable_name = executable_name,
platform_prerequisites = platform_prerequisites,
predeclared_outputs = predeclared_outputs,
)

processor_partials = [
partials.apple_bundle_info_partial(
actions = actions,
bundle_extension = bundle_extension,
bundle_id = bundle_id,
bundle_name = bundle_name,
executable_name = executable_name,
entitlements = entitlements,
label_name = label.name,
platform_prerequisites = platform_prerequisites,
predeclared_outputs = predeclared_outputs,
product_type = rule_descriptor.product_type,
),
partials.binary_partial(
actions = actions,
binary_artifact = binary_artifact,
executable_name = executable_name,
label_name = label.name,
),
partials.bitcode_symbols_partial(
actions = actions,
binary_artifact = binary_artifact,
debug_outputs_provider = debug_outputs_provider,
dependency_targets = ctx.attr.frameworks,
label_name = label.name,
platform_prerequisites = platform_prerequisites,
),
partials.clang_rt_dylibs_partial(
actions = actions,
apple_toolchain_info = apple_toolchain_info,
binary_artifact = binary_artifact,
features = features,
label_name = label.name,
platform_prerequisites = platform_prerequisites,
),
partials.debug_symbols_partial(
actions = actions,
bin_root_path = bin_root_path,
bundle_extension = bundle_extension,
bundle_name = bundle_name,
debug_dependencies = ctx.attr.frameworks,
debug_outputs_provider = debug_outputs_provider,
dsym_info_plist_template = apple_toolchain_info.dsym_info_plist_template,
executable_name = executable_name,
platform_prerequisites = platform_prerequisites,
rule_label = label,
),
partials.embedded_bundles_partial(
platform_prerequisites = platform_prerequisites,
embeddable_targets = ctx.attr.frameworks,
custom_bundles = {getattr(ctx.attr, "bundle_location", ""): [archive]},
),
partials.extension_safe_validation_partial(
is_extension_safe = ctx.attr.extension_safe,
rule_label = label,
targets_to_validate = ctx.attr.frameworks,
),
partials.resources_partial(
actions = actions,
apple_toolchain_info = apple_toolchain_info,
bundle_extension = bundle_extension,
bundle_id = bundle_id,
bundle_name = bundle_name,
environment_plist = ctx.file._environment_plist,
executable_name = executable_name,
launch_storyboard = None,
platform_prerequisites = platform_prerequisites,
plist_attrs = ["infoplists"],
rule_attrs = ctx.attr,
rule_descriptor = rule_descriptor,
rule_label = label,
targets_to_avoid = ctx.attr.frameworks,
top_level_attrs = [
"app_icons",
"strings",
"resources",
],
),
partials.swift_dylibs_partial(
actions = actions,
apple_toolchain_info = apple_toolchain_info,
binary_artifact = binary_artifact,
dependency_targets = ctx.attr.frameworks,
label_name = label.name,
platform_prerequisites = platform_prerequisites,
),
]

if platform_prerequisites.platform.is_device:
processor_partials.append(
partials.provisioning_profile_partial(
actions = actions,
profile_artifact = ctx.file.provisioning_profile,
rule_label = label,
),
)

processor_result = processor.process(
actions = actions,
apple_toolchain_info = apple_toolchain_info,
bundle_extension = bundle_extension,
bundle_name = bundle_name,
codesignopts = codesigning_support.codesignopts_from_rule_ctx(ctx),
entitlements = entitlements,
executable_name = executable_name,
ipa_post_processor = ctx.executable.ipa_post_processor,
partials = processor_partials,
platform_prerequisites = platform_prerequisites,
predeclared_outputs = predeclared_outputs,
process_and_sign_template = apple_toolchain_info.process_and_sign_template,
provisioning_profile = getattr(ctx.file, "provisioning_profile", None),
rule_descriptor = rule_descriptor,
rule_label = label,
)

return [
DefaultInfo(files = processor_result.output_files),
IosBundleBundleInfo(),
OutputGroupInfo(
**outputs.merge_output_groups(
link_result.output_groups,
processor_result.output_groups,
)
),
] + processor_result.providers

def _ios_framework_impl(ctx):
"""Experimental implementation of ios_framework."""

Expand Down Expand Up @@ -1687,6 +1856,13 @@ ios_app_clip = rule_factory.create_apple_bundling_rule(
doc = "Builds and bundles an iOS App Clip.",
)

ios_bundle = rule_factory.create_apple_bundling_rule(
implementation = _ios_bundle_impl,
platform_type = "ios",
product_type = apple_product_type.bundle,
doc = "Builds and bundles an iOS Loadable Bundle.",
)

ios_extension = rule_factory.create_apple_bundling_rule(
implementation = _ios_extension_impl,
platform_type = "ios",
Expand Down
64 changes: 64 additions & 0 deletions apple/internal/partials/embedded_bundles.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ load(
"@build_bazel_rules_apple//apple/internal:processor.bzl",
"processor",
)
load(
"@bazel_skylib//lib:new_sets.bzl",
"sets",
)
load(
"@bazel_skylib//lib:paths.bzl",
"paths",
)
load(
"@bazel_skylib//lib:partial.bzl",
"partial",
Expand All @@ -35,6 +43,10 @@ top-level bundling rule will need to package.""",
"app_clips": """
A depset with the zipped archives of bundles that need to be expanded into the
AppClips section of the packaging bundle.""",
"custom_bundles": """
A dict of depsets of zipped archives of bundles that need to be expanded into
custom locations of the packaging bundle, where the key is the path to the
custom location.""",
Comment on lines +46 to +49
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is needed to support the EarlGray use case where the bundle needs to be placed at a custom location within the packaging bundle.

"frameworks": """
A depset with the zipped archives of bundles that need to be expanded into the
Frameworks section of the packaging bundle.""",
Expand Down Expand Up @@ -120,6 +132,54 @@ def _embedded_bundles_partial_impl(
transitive = transitive_bundles.get(bundle_type, []),
)

# Process custom bundle locations
transitive_custom_bundles = dict()
for provider in embeddable_providers:
if hasattr(provider, "custom_bundles"):
custom_bundles = getattr(provider, "custom_bundles")
for bundle_location, input_bundles in custom_bundles.items():
transitive_custom_bundles.setdefault(
bundle_location,
default = [],
).append(input_bundles)

if bundle_embedded_bundles:
# If this partial is configured to embed the transitive embeddable partials, collect
# them into a list to be returned by this partial.
for bundle_location, input_bundles_array in transitive_custom_bundles.items():
transitive_depset = depset(transitive = input_bundles_array)

# With tree artifacts, we need to set the parent_dir of the file to be the basename
# of the file. Expanding these depsets shouldn't be too much work as there shouldn't
# be too many embedded targets per top-level bundle.
if is_experimental_tree_artifact_enabled(config_vars = config_vars):
for bundle in transitive_depset.to_list():
bundles_to_embed.append((
processor.location.loadable_bundle,
paths.join(bundle_location, bundle.basename),
depset([bundle])
))
else:
bundles_to_embed.append((processor.location.loadable_bundle, bundle_location, transitive_depset))

# Clear the transitive list of custom bundles since they will be packaged
# in the bundle processing this partial and do not need to be propagated.
transitive_custom_bundles = dict()

# Construct the _AppleEmbeddableInfo provider field for the bundle type being processed.
# At this step, we inject the bundles that are inputs to this partial, since that propagates
# the info for a higher level bundle to embed this bundle.
direct_custom_bundles = input_bundles_by_type.get("custom_bundles", {})
bundle_locations = sets.to_list(sets.make(direct_custom_bundles.keys() + transitive_custom_bundles.keys()))
if bundle_locations:
embeddedable_info_fields["custom_bundles"] = {
bundle_location: depset(
direct_custom_bundles.get(bundle_location, []),
transitive = transitive_custom_bundles.get(bundle_location, []),
)
for bundle_location in bundle_locations
}

# Construct the output files fields. If tree artifacts is enabled, propagate the bundles to
# package into bundle_files. Otherwise, propagate through bundle_zips so that they can be
# extracted.
Expand Down Expand Up @@ -165,6 +225,7 @@ def embedded_bundles_partial(
*,
app_clips = [],
bundle_embedded_bundles = False,
custom_bundles = {},
embeddable_targets = [],
frameworks = [],
platform_prerequisites,
Expand All @@ -185,6 +246,8 @@ def embedded_bundles_partial(
bundle_embedded_bundles: If True, this target will embed all transitive embeddable_bundles
_only_ propagated through the targets given in embeddable_targets. If False, the
embeddable bundles will be propagated downstream for a top level target to bundle them.
custom_bundles: Dictionary of list of bundles that should be propagated downstream for a
top level target to bundle inside directories named by the dictionary key.
embeddable_targets: The list of targets that propagate embeddable bundles to bundle or
propagate.
frameworks: List of framework bundles that should be propagated downstream for a top level
Expand All @@ -206,6 +269,7 @@ def embedded_bundles_partial(
_embedded_bundles_partial_impl,
app_clips = app_clips,
bundle_embedded_bundles = bundle_embedded_bundles,
custom_bundles = custom_bundles,
embeddable_targets = embeddable_targets,
frameworks = frameworks,
platform_prerequisites = platform_prerequisites,
Expand Down
2 changes: 2 additions & 0 deletions apple/internal/processor.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ _LOCATION_ENUM = struct(
bundle = "bundle",
content = "content",
framework = "framework",
loadable_bundle = "loadable_bundle",
plugin = "plugin",
resource = "resource",
watch = "watch",
Expand Down Expand Up @@ -199,6 +200,7 @@ def _archive_paths(
contents_path,
rule_descriptor.bundle_locations.contents_relative_frameworks,
),
_LOCATION_ENUM.loadable_bundle: contents_path,
_LOCATION_ENUM.plugin: paths.join(
contents_path,
rule_descriptor.bundle_locations.contents_relative_plugins,
Expand Down
Loading