Skip to content

Commit 87c519d

Browse files
authored
Implement rule for linker order files apple_order_file (#2855)
1 parent df90109 commit 87c519d

11 files changed

Lines changed: 711 additions & 0 deletions

File tree

apple/BUILD

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,14 @@ bzl_library(
142142
],
143143
)
144144

145+
bzl_library(
146+
name = "linker",
147+
srcs = ["linker.bzl"],
148+
deps = [
149+
"//apple/internal:order_file",
150+
],
151+
)
152+
145153
bzl_library(
146154
name = "macos",
147155
srcs = ["macos.bzl"],

apple/internal/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,17 @@ bzl_library(
398398
],
399399
)
400400

401+
bzl_library(
402+
name = "order_file",
403+
srcs = ["order_file.bzl"],
404+
visibility = [
405+
"//apple:__subpackages__",
406+
],
407+
deps = [
408+
"@rules_cc//cc/common",
409+
],
410+
)
411+
401412
bzl_library(
402413
name = "outputs",
403414
srcs = ["outputs.bzl"],

apple/internal/order_file.bzl

Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Copyright 2023 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+
"""
16+
A rule for providing support for order files during build.
17+
"""
18+
19+
load("@rules_cc//cc/common:cc_common.bzl", "cc_common")
20+
load("@rules_cc//cc/common:cc_info.bzl", "CcInfo")
21+
22+
visibility([
23+
"//apple/...",
24+
"//test/...",
25+
])
26+
27+
def _concatenate_files(*, actions, files, name):
28+
"""Concatenates multiple files together.
29+
30+
Args:
31+
actions: The ctx.actions associated with the rule.
32+
files: The files that should be concatenated. Order will be preserved.
33+
name: The ctx.attr.name associated with the rule.
34+
35+
Returns:
36+
A declared file that contains the concatenated output.
37+
"""
38+
39+
out_file = actions.declare_file("%s_concat.order" % name)
40+
41+
actions.run_shell(
42+
inputs = files,
43+
outputs = [out_file],
44+
progress_message = "Concatenating order files into %s" % out_file.short_path,
45+
arguments = [out_file.path] + [f.path for f in files],
46+
command = "awk '1' ${@:2} > \"$1\"",
47+
mnemonic = "OrderFileConcatenation",
48+
)
49+
50+
return out_file
51+
52+
def _dedup_file(*, actions, file, name):
53+
"""Removes duplicate lines from a file.
54+
55+
Args:
56+
actions: The ctx.actions associated with the rule.
57+
file: The file that should have its duplicate lines removed. Order will be preserved.
58+
name: The ctx.attr.name associated with the rule.
59+
60+
Returns:
61+
A declared file that contains the deduplicated output.
62+
"""
63+
64+
out_file = actions.declare_file("%s_dedup.order" % name)
65+
66+
actions.run_shell(
67+
inputs = [file],
68+
outputs = [out_file],
69+
progress_message = "Deduping order files into %s" % out_file.short_path,
70+
arguments = [file.path, out_file.path],
71+
command = "awk '!x[$0]++' \"$1\" > \"$2\"",
72+
mnemonic = "OrderFileDeduplication",
73+
)
74+
75+
return out_file
76+
77+
def _link_order_file(*, ctx, order_file, stats):
78+
"""Returns a provider that will inject an order file during linking of the iOS application.
79+
80+
Args:
81+
ctx: The context associated with the order_file rule.
82+
order_file: The final order file to be used during linking.
83+
stats: A boolean indicating whether to log stats about how the linker used the order file.
84+
"""
85+
86+
linkopts = [
87+
"-Wl,-order_file,%s" % order_file.path,
88+
]
89+
if stats:
90+
linkopts.append("-Wl,-order_file_statistics")
91+
92+
linker_input = cc_common.create_linker_input(
93+
owner = ctx.label,
94+
user_link_flags = depset(linkopts),
95+
additional_inputs = depset([order_file]),
96+
)
97+
98+
return CcInfo(
99+
linking_context = cc_common.create_linking_context(
100+
linker_inputs = depset([linker_input]),
101+
),
102+
)
103+
104+
def _order_file_impl(ctx):
105+
"""Prepares a list of order files for inclusion into an iOS Application.
106+
107+
Order files optimize the order of symbols in the binary, thus improving performance of the
108+
application. This method will concatenate multiple order files together, remove duplicate lines
109+
and prepare the linker commands necessary to apply the order files to the binary.
110+
111+
Full details on the contents of order files are available at
112+
https://developer.apple.com/documentation/xcode/build-settings-reference#Order-File
113+
With additional details on how to generate an order file at
114+
https://developer.apple.com/library/archive/documentation/Performance/Conceptual/CodeFootprint/Articles/ImprovingLocality.html
115+
116+
Args:
117+
ctx: The ctx associated with the rule.
118+
119+
Returns:
120+
An array of Info objects for consumption by later stages of build.
121+
"""
122+
123+
if ctx.var["COMPILATION_MODE"] != "opt":
124+
# Apple's guidance: Generally you should not specify an order file in Debug or Development
125+
# configurations, as this will make the linked binary less readable to the debugger.
126+
# Use them only in Release or Deployment configurations.
127+
return [CcInfo()]
128+
129+
concatenated_order_file = _concatenate_files(
130+
name = ctx.attr.name,
131+
actions = ctx.actions,
132+
files = ctx.files.deps,
133+
)
134+
deduped_order_file = _dedup_file(
135+
name = ctx.attr.name,
136+
actions = ctx.actions,
137+
file = concatenated_order_file,
138+
)
139+
140+
linker_cc_info = _link_order_file(
141+
ctx = ctx,
142+
order_file = deduped_order_file,
143+
stats = ctx.attr.stats,
144+
)
145+
146+
return [
147+
linker_cc_info,
148+
]
149+
150+
# This (apple_)order_file rule will inject order files into your application dependencies.
151+
#
152+
# See additional documentation in `_order_file_impl` above.
153+
#
154+
# Use like any other dependency in your BUILD. For example:
155+
#
156+
# apple_order_file(
157+
# name = "app_order_file"
158+
# deps = [
159+
# "my_file.order",
160+
# "my_second_order_file.order",
161+
# ]
162+
# )
163+
#
164+
# ios_application(
165+
# name = "app",
166+
# deps = [":app_order_file"],
167+
# )
168+
#
169+
order_file = rule(
170+
implementation = _order_file_impl,
171+
attrs = {
172+
"deps": attr.label_list(
173+
allow_files = True,
174+
mandatory = True,
175+
doc = "The raw text order files to be used in the iOS application.",
176+
),
177+
"stats": attr.bool(
178+
default = False,
179+
doc = "Indicate whether to log stats about how the linker used the order file.",
180+
),
181+
},
182+
doc = """\
183+
Injects the provided `.order` files into Apple link lines, concatenating and deduplicating them before supplying the appropriate linker flags.
184+
The rule short-circuits in non-optimized compilations because generating order files is intended for release/deployment builds where they improve runtime locality.
185+
186+
Example:
187+
188+
```starlark
189+
apple_order_file(
190+
name = "app_order_file",
191+
deps = [
192+
"my_file.order",
193+
"my_second_order_file.order",
194+
],
195+
)
196+
197+
ios_application(
198+
name = "app",
199+
deps = [":app_order_file"],
200+
)
201+
```
202+
203+
Set `stats = True` if you want the linker to emit information about how it used the order file.
204+
""",
205+
)

apple/linker.bzl

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Copyright 2023 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+
"""Rules related to Apple linker."""
16+
17+
load(
18+
"//apple/internal:order_file.bzl",
19+
_apple_order_file = "order_file",
20+
)
21+
22+
visibility("public")
23+
24+
apple_order_file = _apple_order_file

doc/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ _RULES_DOC_SRCS = [
1212
"docc",
1313
"dtrace",
1414
"header_map",
15+
"linker",
1516
"ios.doc",
1617
"macos.doc",
1718
"resources",

doc/rules-linker.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<!-- Generated with Stardoc: http://skydoc.bazel.build -->
2+
3+
Rules related to Apple linker.
4+
5+
<a id="apple_order_file"></a>
6+
7+
## apple_order_file
8+
9+
<pre>
10+
load("@rules_apple//apple:linker.bzl", "apple_order_file")
11+
12+
apple_order_file(<a href="#apple_order_file-name">name</a>, <a href="#apple_order_file-deps">deps</a>, <a href="#apple_order_file-stats">stats</a>)
13+
</pre>
14+
15+
Injects the provided `.order` files into Apple link lines, concatenating and deduplicating them before supplying the appropriate linker flags.
16+
The rule short-circuits in non-optimized compilations because generating order files is intended for release/deployment builds where they improve runtime locality.
17+
18+
Example:
19+
20+
```starlark
21+
apple_order_file(
22+
name = "app_order_file",
23+
deps = [
24+
"my_file.order",
25+
"my_second_order_file.order",
26+
],
27+
)
28+
29+
ios_application(
30+
name = "app",
31+
deps = [":app_order_file"],
32+
)
33+
```
34+
35+
Set `stats = True` if you want the linker to emit information about how it used the order file.
36+
37+
**ATTRIBUTES**
38+
39+
40+
| Name | Description | Type | Mandatory | Default |
41+
| :------------- | :------------- | :------------- | :------------- | :------------- |
42+
| <a id="apple_order_file-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
43+
| <a id="apple_order_file-deps"></a>deps | The raw text order files to be used in the iOS application. | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
44+
| <a id="apple_order_file-stats"></a>stats | Indicate whether to log stats about how the linker used the order file. | Boolean | optional | `False` |
45+
46+

test/starlark_tests/BUILD

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ load(":macos_quick_look_plugin_tests.bzl", "macos_quick_look_plugin_test_suite")
3939
load(":macos_static_framework_tests.bzl", "macos_static_framework_test_suite")
4040
load(":macos_ui_test_tests.bzl", "macos_ui_test_test_suite")
4141
load(":macos_unit_test_tests.bzl", "macos_unit_test_test_suite")
42+
load(":order_file_tests.bzl", "order_file_test_suite")
4243
load(":tvos_application_swift_tests.bzl", "tvos_application_swift_test_suite")
4344
load(":tvos_application_tests.bzl", "tvos_application_test_suite")
4445
load(":tvos_dynamic_framework_tests.bzl", "tvos_dynamic_framework_test_suite")
@@ -144,6 +145,8 @@ macos_ui_test_test_suite(name = "macos_ui_test")
144145

145146
macos_unit_test_test_suite(name = "macos_unit_test")
146147

148+
order_file_test_suite(name = "order_file")
149+
147150
tvos_application_swift_test_suite(name = "tvos_application_swift")
148151

149152
tvos_application_test_suite(name = "tvos_application")
@@ -194,6 +197,8 @@ bzl_library(
194197
name = "starlark_tests_bzls",
195198
srcs = glob(["**/*.bzl"]),
196199
deps = [
200+
"//apple:linker",
201+
"//apple/build_settings",
197202
"//test/starlark_tests/rules:test_rules",
198203
],
199204
)

0 commit comments

Comments
 (0)