Skip to content

Commit 0309ba6

Browse files
Add apple_precompiled_resource_bundle (#2504)
Fixes #319 --------- Co-authored-by: Brentley Jones <github@brentleyjones.com>
1 parent ab29ef8 commit 0309ba6

File tree

12 files changed

+765
-2
lines changed

12 files changed

+765
-2
lines changed

apple/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ bzl_library(
175175
"//apple/internal/resource_rules:apple_core_ml_library",
176176
"//apple/internal/resource_rules:apple_intent_library",
177177
"//apple/internal/resource_rules:apple_metal_library",
178+
"//apple/internal/resource_rules:apple_precompiled_resource_bundle",
178179
"//apple/internal/resource_rules:apple_resource_bundle",
179180
"//apple/internal/resource_rules:apple_resource_group",
180181
],

apple/internal/aspects/resource_aspect.bzl

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,17 @@ def _apple_resource_aspect_impl(target, ctx):
201201
process_args["bundle_id"] = ctx.rule.attr.bundle_id or None
202202
bundle_name = "{}.bundle".format(ctx.rule.attr.bundle_name or ctx.label.name)
203203

204+
elif ctx.rule.kind == "apple_precompiled_resource_bundle":
205+
# If the target already propagates a AppleResourceInfo, do nothing.
206+
if AppleResourceInfo in target:
207+
return []
208+
default_action = apple_resource_hint_action.resources
209+
collect_infoplists_args["res_attrs"] = ["infoplists"]
210+
collect_args["res_attrs"] = ["resources"]
211+
collect_structured_args["res_attrs"] = ["structured_resources"]
212+
process_args["bundle_id"] = ctx.rule.attr.bundle_id or None
213+
bundle_name = "{}.bundle".format(ctx.rule.attr.bundle_name or ctx.label.name)
214+
204215
if hint_action:
205216
default_action = hint_action
206217

apple/internal/resource_rules/BUILD

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,29 @@ bzl_library(
100100
],
101101
)
102102

103+
bzl_library(
104+
name = "apple_precompiled_resource_bundle",
105+
srcs = ["apple_precompiled_resource_bundle.bzl"],
106+
visibility = [
107+
"//apple:__subpackages__",
108+
],
109+
deps = [
110+
"//apple/internal:apple_product_type",
111+
"//apple/internal:apple_toolchains",
112+
"//apple/internal:codesigning_support",
113+
"//apple/internal:features_support",
114+
"//apple/internal:partials",
115+
"//apple/internal:platform_support",
116+
"//apple/internal:processor",
117+
"//apple/internal:providers",
118+
"//apple/internal:resources",
119+
"//apple/internal:rule_attrs",
120+
"//apple/internal:rule_support",
121+
"@bazel_skylib//lib:dicts",
122+
"@bazel_skylib//lib:partial",
123+
],
124+
)
125+
103126
bzl_library(
104127
name = "apple_resource_group",
105128
srcs = ["apple_resource_group.bzl"],
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleName</key>
6+
<string>$(BUNDLE_NAME)</string>
7+
<key>CFBundleVersion</key>
8+
<string>1</string>
9+
<key>CFBundleShortVersionString</key>
10+
<string>1.0</string>
11+
<key>CFBundleIdentifier</key>
12+
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
13+
<key>CFBundlePackageType</key>
14+
<string>BNDL</string>
15+
</dict>
16+
</plist>
Lines changed: 273 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,273 @@
1+
# Copyright 2024 The Bazel Authors. All rights reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Implementation of apple_precompiled_resource_bundle rule."""
16+
17+
load(
18+
"@bazel_skylib//lib:dicts.bzl",
19+
"dicts",
20+
)
21+
load(
22+
"@bazel_skylib//lib:partial.bzl",
23+
"partial",
24+
)
25+
load(
26+
"@build_bazel_rules_apple//apple/internal:apple_toolchains.bzl",
27+
"AppleMacToolsToolchainInfo",
28+
"AppleXPlatToolsToolchainInfo",
29+
)
30+
load(
31+
"@build_bazel_rules_apple//apple/internal:features_support.bzl",
32+
"features_support",
33+
)
34+
load(
35+
"@build_bazel_rules_apple//apple/internal:platform_support.bzl",
36+
"platform_support",
37+
)
38+
load(
39+
"@build_bazel_rules_apple//apple/internal:resources.bzl",
40+
"resources",
41+
)
42+
load(
43+
"@build_bazel_rules_apple//apple/internal:rule_attrs.bzl",
44+
"rule_attrs",
45+
)
46+
load(
47+
"@build_bazel_rules_apple//apple/internal:rule_support.bzl",
48+
"rule_support",
49+
)
50+
load(
51+
"//apple/internal:apple_product_type.bzl",
52+
"apple_product_type",
53+
)
54+
55+
def _apple_precompiled_resource_bundle_impl(ctx):
56+
# Owner to attach to the resources as they're being bucketed.
57+
owner = str(ctx.label)
58+
bucketize_args = {}
59+
60+
rule_descriptor = rule_support.rule_descriptor(
61+
platform_type = str(ctx.fragments.apple.single_arch_platform.platform_type),
62+
product_type = apple_product_type.application,
63+
)
64+
65+
features = features_support.compute_enabled_features(
66+
requested_features = ctx.features,
67+
unsupported_features = ctx.disabled_features,
68+
)
69+
70+
apple_xplat_toolchain_info = ctx.attr._xplat_toolchain[AppleXPlatToolsToolchainInfo]
71+
72+
platform_prerequisites = platform_support.platform_prerequisites(
73+
apple_fragment = ctx.fragments.apple,
74+
build_settings = apple_xplat_toolchain_info.build_settings,
75+
config_vars = ctx.var,
76+
cpp_fragment = ctx.fragments.cpp,
77+
device_families = rule_descriptor.allowed_device_families,
78+
explicit_minimum_deployment_os = None,
79+
explicit_minimum_os = None,
80+
features = features,
81+
objc_fragment = ctx.fragments.objc,
82+
platform_type_string = str(ctx.fragments.apple.single_arch_platform.platform_type),
83+
uses_swift = False,
84+
xcode_version_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig],
85+
)
86+
87+
bundle_name = "{}.bundle".format(ctx.attr.bundle_name or ctx.label.name)
88+
bundle_id = ctx.attr.bundle_id or "com.bazel.apple_precompiled_resource_bundle_".format(ctx.attr.bundle_name or ctx.label.name)
89+
90+
apple_resource_infos = []
91+
process_args = {
92+
"actions": ctx.actions,
93+
"apple_mac_toolchain_info": ctx.attr._mac_toolchain[AppleMacToolsToolchainInfo],
94+
"bundle_id": bundle_id,
95+
"product_type": rule_descriptor.product_type,
96+
"rule_label": ctx.label,
97+
}
98+
99+
resource_files = resources.collect(
100+
attr = ctx.attr,
101+
res_attrs = ["resources"],
102+
)
103+
104+
if resource_files:
105+
bucketized_owners, unowned_resources, buckets = resources.bucketize_data(
106+
resources = resource_files,
107+
owner = owner,
108+
parent_dir_param = bundle_name,
109+
**bucketize_args
110+
)
111+
apple_resource_infos.append(
112+
resources.process_bucketized_data(
113+
bucketized_owners = bucketized_owners,
114+
buckets = buckets,
115+
platform_prerequisites = platform_prerequisites,
116+
processing_owner = owner,
117+
unowned_resources = unowned_resources,
118+
**process_args
119+
),
120+
)
121+
122+
structured_files = resources.collect(
123+
attr = ctx.attr,
124+
res_attrs = ["structured_resources"],
125+
)
126+
if structured_files:
127+
if bundle_name:
128+
structured_parent_dir_param = partial.make(
129+
resources.structured_resources_parent_dir,
130+
parent_dir = bundle_name,
131+
)
132+
else:
133+
structured_parent_dir_param = partial.make(
134+
resources.structured_resources_parent_dir,
135+
)
136+
137+
# Avoid processing PNG files that are referenced through the structured_resources
138+
# attribute. This is mostly for legacy reasons and should get cleaned up in the future.
139+
bucketized_owners, unowned_resources, buckets = resources.bucketize_data(
140+
allowed_buckets = ["strings", "plists"],
141+
owner = owner,
142+
parent_dir_param = structured_parent_dir_param,
143+
resources = structured_files,
144+
**bucketize_args
145+
)
146+
apple_resource_infos.append(
147+
resources.process_bucketized_data(
148+
bucketized_owners = bucketized_owners,
149+
buckets = buckets,
150+
platform_prerequisites = platform_prerequisites,
151+
processing_owner = owner,
152+
unowned_resources = unowned_resources,
153+
**process_args
154+
),
155+
)
156+
157+
infoplists = resources.collect(
158+
attr = ctx.attr,
159+
res_attrs = ["infoplists", "_fallback_infoplist"],
160+
)
161+
162+
if infoplists and apple_resource_infos:
163+
bucketized_owners, unowned_resources, buckets = resources.bucketize_typed_data(
164+
bucket_type = "infoplists",
165+
owner = owner,
166+
parent_dir_param = bundle_name,
167+
resources = infoplists,
168+
**bucketize_args
169+
)
170+
apple_resource_infos.append(
171+
resources.process_bucketized_data(
172+
bucketized_owners = bucketized_owners,
173+
buckets = buckets,
174+
platform_prerequisites = platform_prerequisites,
175+
processing_owner = owner,
176+
unowned_resources = unowned_resources,
177+
**process_args
178+
),
179+
)
180+
181+
providers = []
182+
if apple_resource_infos:
183+
# If any providers were collected, merge them.
184+
providers.append(
185+
resources.merge_providers(
186+
default_owner = owner,
187+
providers = apple_resource_infos,
188+
),
189+
)
190+
191+
return providers
192+
193+
apple_precompiled_resource_bundle = rule(
194+
implementation = _apple_precompiled_resource_bundle_impl,
195+
fragments = ["apple", "cpp", "objc"],
196+
attrs = dicts.add(
197+
{
198+
"bundle_id": attr.string(
199+
doc = """
200+
The bundle ID for this target. It will replace `$(PRODUCT_BUNDLE_IDENTIFIER)` found in the files
201+
from defined in the `infoplists` paramter.
202+
""",
203+
),
204+
"bundle_name": attr.string(
205+
doc = """
206+
The desired name of the bundle (without the `.bundle` extension). If this attribute is not set,
207+
then the `name` of the target will be used instead.
208+
""",
209+
),
210+
"infoplists": attr.label_list(
211+
allow_empty = True,
212+
allow_files = True,
213+
doc = """
214+
A list of `.plist` files that will be merged to form the `Info.plist` that represents the extension.
215+
At least one file must be specified.
216+
Please see [Info.plist Handling](/doc/common_info.md#infoplist-handling") for what is supported.
217+
218+
Duplicate keys between infoplist files
219+
will cause an error if and only if the values conflict.
220+
Bazel will perform variable substitution on the Info.plist file for the following values (if they
221+
are strings in the top-level dict of the plist):
222+
223+
${BUNDLE_NAME}: This target's name and bundle suffix (.bundle or .app) in the form name.suffix.
224+
${PRODUCT_NAME}: This target's name.
225+
${TARGET_NAME}: This target's name.
226+
The key in ${} may be suffixed with :rfc1034identifier (for example
227+
${PRODUCT_NAME::rfc1034identifier}) in which case Bazel will replicate Xcode's behavior and replace
228+
non-RFC1034-compliant characters with -.
229+
""",
230+
),
231+
"resources": attr.label_list(
232+
allow_empty = True,
233+
allow_files = True,
234+
doc = """
235+
Files to include in the resource bundle. Files that are processable resources, like .xib,
236+
.storyboard, .strings, .png, and others, will be processed by the Apple bundling rules that have
237+
those files as dependencies. Other file types that are not processed will be copied verbatim. These
238+
files are placed in the root of the resource bundle (e.g. `Payload/foo.app/bar.bundle/...`) in most
239+
cases. However, if they appear to be localized (i.e. are contained in a directory called *.lproj),
240+
they will be placed in a directory of the same name in the app bundle.
241+
242+
You can also add other `apple_precompiled_resource_bundle` and `apple_bundle_import` targets into `resources`,
243+
and the resource bundle structures will be propagated into the final bundle.
244+
""",
245+
),
246+
"structured_resources": attr.label_list(
247+
allow_empty = True,
248+
allow_files = True,
249+
doc = """
250+
Files to include in the final resource bundle. They are not processed or compiled in any way
251+
besides the processing done by the rules that actually generate them. These files are placed in the
252+
bundle root in the same structure passed to this argument, so `["res/foo.png"]` will end up in
253+
`res/foo.png` inside the bundle.
254+
""",
255+
),
256+
"_environment_plist": attr.label(
257+
allow_single_file = True,
258+
default = "@build_bazel_rules_apple//apple/internal:environment_plist_ios",
259+
),
260+
"_fallback_infoplist": attr.label(
261+
allow_single_file = True,
262+
default = "@build_bazel_rules_apple//apple/internal/resource_rules:Info.plist",
263+
),
264+
},
265+
rule_attrs.common_tool_attrs(),
266+
),
267+
doc = """
268+
This rule encapsulates a target which is provided to dependers as a bundle. An
269+
`apple_precompiled_resource_bundle`'s resources are put in a resource bundle in the top
270+
level Apple bundle dependent. `apple_precompiled_resource_bundle` targets need to be added to
271+
library targets through the `data` attribute.
272+
""",
273+
)

apple/resources.bzl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ load(
3838
"@build_bazel_rules_apple//apple/internal/resource_rules:apple_metal_library.bzl",
3939
_apple_metal_library = "apple_metal_library",
4040
)
41+
load(
42+
"@build_bazel_rules_apple//apple/internal/resource_rules:apple_precompiled_resource_bundle.bzl",
43+
_apple_precompiled_resource_bundle = "apple_precompiled_resource_bundle",
44+
)
4145
load(
4246
"@build_bazel_rules_apple//apple/internal/resource_rules:apple_resource_bundle.bzl",
4347
_apple_resource_bundle = "apple_resource_bundle",
@@ -51,6 +55,7 @@ load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
5155
apple_bundle_import = _apple_bundle_import
5256
apple_intent_library = _apple_intent_library
5357
apple_metal_library = _apple_metal_library
58+
apple_precompiled_resource_bundle = _apple_precompiled_resource_bundle
5459
apple_resource_bundle = _apple_resource_bundle
5560
apple_resource_group = _apple_resource_group
5661
apple_core_data_model = _apple_core_data_model

doc/rules-resources.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,33 @@ Compiles Metal shader language sources into a Metal library.
100100
| <a id="apple_metal_library-includes"></a>includes | A list of header search paths. | List of strings | optional | `[]` |
101101

102102

103+
<a id="apple_precompiled_resource_bundle"></a>
104+
105+
## apple_precompiled_resource_bundle
106+
107+
<pre>
108+
apple_precompiled_resource_bundle(<a href="#apple_precompiled_resource_bundle-name">name</a>, <a href="#apple_precompiled_resource_bundle-resources">resources</a>, <a href="#apple_precompiled_resource_bundle-bundle_id">bundle_id</a>, <a href="#apple_precompiled_resource_bundle-bundle_name">bundle_name</a>, <a href="#apple_precompiled_resource_bundle-infoplists">infoplists</a>,
109+
<a href="#apple_precompiled_resource_bundle-structured_resources">structured_resources</a>)
110+
</pre>
111+
112+
This rule encapsulates a target which is provided to dependers as a bundle. An
113+
`apple_precompiled_resource_bundle`'s resources are put in a resource bundle in the top
114+
level Apple bundle dependent. `apple_precompiled_resource_bundle` targets need to be added to
115+
library targets through the `data` attribute.
116+
117+
**ATTRIBUTES**
118+
119+
120+
| Name | Description | Type | Mandatory | Default |
121+
| :------------- | :------------- | :------------- | :------------- | :------------- |
122+
| <a id="apple_precompiled_resource_bundle-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
123+
| <a id="apple_precompiled_resource_bundle-resources"></a>resources | Files to include in the resource bundle. Files that are processable resources, like .xib, .storyboard, .strings, .png, and others, will be processed by the Apple bundling rules that have those files as dependencies. Other file types that are not processed will be copied verbatim. These files are placed in the root of the resource bundle (e.g. `Payload/foo.app/bar.bundle/...`) in most cases. However, if they appear to be localized (i.e. are contained in a directory called *.lproj), they will be placed in a directory of the same name in the app bundle.<br><br>You can also add other `apple_precompiled_resource_bundle` and `apple_bundle_import` targets into `resources`, and the resource bundle structures will be propagated into the final bundle. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
124+
| <a id="apple_precompiled_resource_bundle-bundle_id"></a>bundle_id | The bundle ID for this target. It will replace `$(PRODUCT_BUNDLE_IDENTIFIER)` found in the files from defined in the `infoplists` paramter. | String | optional | `""` |
125+
| <a id="apple_precompiled_resource_bundle-bundle_name"></a>bundle_name | The desired name of the bundle (without the `.bundle` extension). If this attribute is not set, then the `name` of the target will be used instead. | String | optional | `""` |
126+
| <a id="apple_precompiled_resource_bundle-infoplists"></a>infoplists | A list of `.plist` files that will be merged to form the `Info.plist` that represents the extension. At least one file must be specified. Please see [Info.plist Handling](/doc/common_info.md#infoplist-handling") for what is supported.<br><br>Duplicate keys between infoplist files will cause an error if and only if the values conflict. Bazel will perform variable substitution on the Info.plist file for the following values (if they are strings in the top-level dict of the plist):<br><br>${BUNDLE_NAME}: This target's name and bundle suffix (.bundle or .app) in the form name.suffix. ${PRODUCT_NAME}: This target's name. ${TARGET_NAME}: This target's name. The key in ${} may be suffixed with :rfc1034identifier (for example ${PRODUCT_NAME::rfc1034identifier}) in which case Bazel will replicate Xcode's behavior and replace non-RFC1034-compliant characters with -. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
127+
| <a id="apple_precompiled_resource_bundle-structured_resources"></a>structured_resources | Files to include in the final resource bundle. They are not processed or compiled in any way besides the processing done by the rules that actually generate them. These files are placed in the bundle root in the same structure passed to this argument, so `["res/foo.png"]` will end up in `res/foo.png` inside the bundle. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
128+
129+
103130
<a id="apple_resource_bundle"></a>
104131

105132
## apple_resource_bundle

0 commit comments

Comments
 (0)