Skip to content

Commit

Permalink
Add ios_bundle rule
Browse files Browse the repository at this point in the history
  • Loading branch information
Brentley Jones authored and Brentley Jones committed Nov 9, 2020
1 parent c67301e commit 558ea6a
Show file tree
Hide file tree
Showing 7 changed files with 293 additions and 5 deletions.
164 changes: 159 additions & 5 deletions apple/internal/ios_rules.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ load(
"AppleBinaryInfo",
"IosAppClipBundleInfo",
"IosApplicationBundleInfo",
"IosBundleBundleInfo",
"IosExtensionBundleInfo",
"IosFrameworkBundleInfo",
"IosImessageApplicationBundleInfo",
Expand Down Expand Up @@ -116,8 +117,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 @@ -208,7 +209,7 @@ def _ios_application_impl(ctx):
package_symbols = True,
platform_prerequisites = platform_prerequisites,
rule_executables = rule_executables,
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 @@ -354,13 +355,12 @@ 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.frameworks + ctx.attr.bundles
features = features_support.compute_enabled_features(
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
platform_prerequisites = platform_support.platform_prerequisites_from_rule_ctx(ctx)
embeddable_targets = ctx.attr.frameworks
entitlements = entitlements_support.entitlements(
entitlements_attr = getattr(ctx.attr, "entitlements", None),
entitlements_file = getattr(ctx.file, "entitlements", None),
Expand Down Expand Up @@ -547,6 +547,153 @@ def _ios_app_clip_impl(ctx):
link_result.binary_provider,
] + processor_result.providers

def _ios_bundle_impl(ctx):
"""Implementation of ios_bundle."""
link_result = linking_support.register_linking_action(ctx)
binary_artifact = link_result.binary_provider.binary
debug_outputs_provider = link_result.debug_outputs_provider

actions = ctx.actions
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)
embeddable_targets = ctx.attr.frameworks
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)
rule_executables = ctx.executable

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.clang_rt_dylibs_partial(
actions = actions,
binary_artifact = binary_artifact,
clangrttool = ctx.executable._clangrttool,
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 = embeddable_targets,
debug_outputs_provider = debug_outputs_provider,
dsym_info_plist_template = ctx.file._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 = embeddable_targets,
custom_bundles = {getattr(ctx.attr, "bundle_location", ""): [archive]},
),
partials.resources_partial(
actions = actions,
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_executables = rule_executables,
rule_label = label,
top_level_attrs = [
"app_icons",
"strings",
"resources",
],
),
partials.swift_dylibs_partial(
actions = actions,
binary_artifact = binary_artifact,
dependency_targets = embeddable_targets,
label_name = label.name,
platform_prerequisites = platform_prerequisites,
swift_stdlib_tool = ctx.executable._swift_stdlib_tool,
),
]

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(
ctx = ctx,
actions = actions,
bundle_extension = bundle_extension,
bundle_name = bundle_name,
entitlements = entitlements,
executable_name = executable_name,
partials = processor_partials,
platform_prerequisites = platform_prerequisites,
predeclared_outputs = predeclared_outputs,
provisioning_profile = getattr(ctx.file, "provisioning_profile", None),
rule_descriptor = rule_descriptor,
rule_executables = rule_executables,
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_dylib_impl(ctx):
"""Implementation of the ios_dylib rule."""
link_result = linking_support.register_linking_action(ctx)
Expand Down Expand Up @@ -1529,6 +1676,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_dylib = rule_factory.create_apple_binary_rule(
implementation = _ios_dylib_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.""",
"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 @@ -122,6 +134,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 @@ -167,6 +227,7 @@ def embedded_bundles_partial(
*,
app_clips = [],
bundle_embedded_bundles = False,
custom_bundles = {},
embeddable_targets = [],
frameworks = [],
platform_prerequisites,
Expand All @@ -187,6 +248,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 @@ -208,6 +271,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
3 changes: 3 additions & 0 deletions apple/internal/processor.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ _LOCATION_ENUM = struct(
bundle = "bundle",
content = "content",
framework = "framework",
loadable_bundle = "loadable_bundle",
plugin = "plugin",
resource = "resource",
watch = "watch",
Expand Down Expand Up @@ -198,6 +199,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 Expand Up @@ -337,6 +339,7 @@ def _bundle_partial_outputs_files(
source_path = source.path
if (location == _LOCATION_ENUM.app_clip or
location == _LOCATION_ENUM.framework or
location == _LOCATION_ENUM.loadable_bundle or
location == _LOCATION_ENUM.plugin):
# This fixes bundling for other bundled products built by Bazel
source_path = bundletool_output_file_path(
Expand Down
16 changes: 16 additions & 0 deletions apple/internal/rule_factory.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ load(
"AppleTestRunnerInfo",
"IosAppClipBundleInfo",
"IosApplicationBundleInfo",
"IosBundleBundleInfo",
"IosExtensionBundleInfo",
"IosFrameworkBundleInfo",
"IosImessageApplicationBundleInfo",
Expand Down Expand Up @@ -636,6 +637,12 @@ fashion, such as a Cocoapod.
A list of iOS app clips to include in the final application bundle.
""",
),
"bundles": attr.label_list(
providers = [
[AppleBundleInfo, IosBundleBundleInfo],
],
doc = "A list of iOS loadable bundles to include in the final application bundle.",
),
"extensions": attr.label_list(
providers = [[AppleBundleInfo, IosExtensionBundleInfo]],
doc = """
Expand Down Expand Up @@ -690,6 +697,15 @@ Info.plist under the key `UILaunchStoryboardName`.
providers = required_providers,
),
})
elif rule_descriptor.product_type == apple_product_type.bundle:
attrs.append({
"bundle_location": attr.string(
mandatory = False,
doc = """
The directiory within the packaging bundle that this bundle should be placed.
""",
),
})

# TODO(kaipi): Once all platforms have framework rules, move this into
# _common_binary_linking_attrs().
Expand Down
Loading

0 comments on commit 558ea6a

Please sign in to comment.