Skip to content

Commit 03e9abf

Browse files
authored
Add support for universal compiler plugins (#1107)
This is useful if you populate caches using fat binaries so that they can be shared between aarch64 and x86_64 macs
1 parent 3f3711c commit 03e9abf

File tree

7 files changed

+221
-1
lines changed

7 files changed

+221
-1
lines changed

doc/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ _DOC_SRCS = {
1919
"swift_binary",
2020
"swift_c_module",
2121
"swift_compiler_plugin",
22+
"universal_swift_compiler_plugin",
2223
"swift_feature_allowlist",
2324
"swift_grpc_library",
2425
"swift_import",

doc/rules.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ On this page:
1919
* [swift_binary](#swift_binary)
2020
* [swift_c_module](#swift_c_module)
2121
* [swift_compiler_plugin](#swift_compiler_plugin)
22+
* [universal_swift_compiler_plugin](#universal_swift_compiler_plugin)
2223
* [swift_feature_allowlist](#swift_feature_allowlist)
2324
* [swift_grpc_library](#swift_grpc_library)
2425
* [swift_import](#swift_import)
@@ -623,3 +624,55 @@ bazel test //:Tests --test_filter=TestModuleName.TestClassName/testMethodName
623624
| <a id="swift_test-swiftc_inputs"></a>swiftc_inputs | Additional files that are referenced using <code>$(location ...)</code> in attributes that support location expansion. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | <code>[]</code> |
624625

625626

627+
<a id="universal_swift_compiler_plugin"></a>
628+
629+
## universal_swift_compiler_plugin
630+
631+
<pre>
632+
universal_swift_compiler_plugin(<a href="#universal_swift_compiler_plugin-name">name</a>, <a href="#universal_swift_compiler_plugin-plugin">plugin</a>)
633+
</pre>
634+
635+
Wraps an existing `swift_compiler_plugin` target to produce a universal binary.
636+
637+
This is useful to allow sharing of caches between Intel and Apple Silicon Macs
638+
at the cost of building everything twice.
639+
640+
Example:
641+
642+
```bzl
643+
# The actual macro code, using SwiftSyntax, as usual.
644+
swift_compiler_plugin(
645+
name = "Macros",
646+
srcs = glob(["Macros/*.swift"]),
647+
deps = [
648+
"@SwiftSyntax",
649+
"@SwiftSyntax//:SwiftCompilerPlugin",
650+
"@SwiftSyntax//:SwiftSyntaxMacros",
651+
],
652+
)
653+
654+
# Wrap your compiler plugin in this universal shim.
655+
universal_swift_compiler_plugin(
656+
name = "Macros.universal",
657+
plugin = ":Macros",
658+
)
659+
660+
# The library that defines the macro hook for use in your project, this
661+
# references the universal_swift_compiler_plugin.
662+
swift_library(
663+
name = "MacroLibrary",
664+
srcs = glob(["MacroLibrary/*.swift"]),
665+
plugins = [":Macros.universal"],
666+
)
667+
```
668+
669+
670+
**ATTRIBUTES**
671+
672+
673+
| Name | Description | Type | Mandatory | Default |
674+
| :------------- | :------------- | :------------- | :------------- | :------------- |
675+
| <a id="universal_swift_compiler_plugin-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
676+
| <a id="universal_swift_compiler_plugin-plugin"></a>plugin | Target to generate a 'fat' binary from. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
677+
678+

examples/xplatform/macros/BUILD

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
load("//swift:swift.bzl", "swift_binary", "swift_library", "swift_test")
2-
load("//swift:swift_compiler_plugin.bzl", "swift_compiler_plugin")
2+
load("//swift:swift_compiler_plugin.bzl", "swift_compiler_plugin", "universal_swift_compiler_plugin")
33

44
licenses(["notice"])
55

@@ -15,6 +15,13 @@ swift_library(
1515
plugins = [":stringify_macro"],
1616
)
1717

18+
swift_library(
19+
name = "stringify_universal",
20+
srcs = ["Stringify.swift"],
21+
module_name = "StringifyUniversal",
22+
plugins = [":stringify_macro_universal"],
23+
)
24+
1825
swift_compiler_plugin(
1926
name = "stringify_macro",
2027
srcs = [
@@ -34,12 +41,23 @@ swift_compiler_plugin(
3441
],
3542
)
3643

44+
universal_swift_compiler_plugin(
45+
name = "stringify_macro_universal",
46+
plugin = ":stringify_macro",
47+
)
48+
3749
swift_binary(
3850
name = "stringify_client",
3951
srcs = ["StringifyClient.swift"],
4052
deps = [":stringify"],
4153
)
4254

55+
swift_binary(
56+
name = "stringify_universal_client",
57+
srcs = ["StringifyUniversalClient.swift"],
58+
deps = [":stringify_universal"],
59+
)
60+
4361
swift_test(
4462
name = "stringify_macro_test",
4563
srcs = ["StringifyMacroTests.swift"],
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
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+
import StringifyUniversal
16+
17+
@main
18+
struct Main {
19+
static func main() {
20+
print(#stringify(1 + 2))
21+
}
22+
}

swift/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ bzl_library(
4242
"//swift/internal:swift_common",
4343
"//swift/internal:utils",
4444
"@bazel_skylib//lib:dicts",
45+
"@build_bazel_apple_support//lib:apple_support",
46+
"@build_bazel_apple_support//lib:lipo",
47+
"@build_bazel_apple_support//lib:transitions",
4548
],
4649
)
4750

swift/swift.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ load(
5454
load(
5555
"@build_bazel_rules_swift//swift:swift_compiler_plugin.bzl",
5656
_swift_compiler_plugin = "swift_compiler_plugin",
57+
_universal_swift_compiler_plugin = "universal_swift_compiler_plugin",
5758
)
5859
load(
5960
"@build_bazel_rules_swift//swift/internal:swift_feature_allowlist.bzl",
@@ -101,6 +102,7 @@ swift_common = _swift_common
101102
swift_binary = _swift_binary
102103
swift_c_module = _swift_c_module
103104
swift_compiler_plugin = _swift_compiler_plugin
105+
universal_swift_compiler_plugin = _universal_swift_compiler_plugin
104106
swift_feature_allowlist = _swift_feature_allowlist
105107
swift_grpc_library = _swift_grpc_library
106108
swift_import = _swift_import

swift/swift_compiler_plugin.bzl

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
"""Implementation of the `swift_compiler_plugin` rule."""
1616

17+
load("@build_bazel_apple_support//lib:apple_support.bzl", "apple_support")
18+
load("@build_bazel_apple_support//lib:lipo.bzl", "lipo")
19+
load("@build_bazel_apple_support//lib:transitions.bzl", "macos_universal_transition")
1720
load(
1821
"@build_bazel_rules_swift//swift/internal:compiling.bzl",
1922
"output_groups_from_other_compilation_outputs",
@@ -254,3 +257,121 @@ swift_library(
254257
fragments = ["cpp"],
255258
implementation = _swift_compiler_plugin_impl,
256259
)
260+
261+
def _universal_swift_compiler_plugin_impl(ctx):
262+
inputs = [
263+
plugin.files.to_list()[0]
264+
for plugin in ctx.split_attr.plugin.values()
265+
]
266+
267+
if not inputs:
268+
fail("Target (%s) `plugin` label ('%s') does not provide any " +
269+
"file for universal binary" % (ctx.attr.name, ctx.attr.plugin))
270+
271+
output = ctx.actions.declare_file(ctx.label.name)
272+
if len(inputs) > 1:
273+
lipo.create(
274+
actions = ctx.actions,
275+
apple_fragment = ctx.fragments.apple,
276+
inputs = inputs,
277+
output = output,
278+
xcode_config = ctx.attr._xcode_config[apple_common.XcodeVersionConfig],
279+
)
280+
else:
281+
ctx.actions.symlink(target_file = inputs[0], output = output)
282+
283+
cc_infos = []
284+
direct_swift_modules = []
285+
module_name = None
286+
swift_infos = []
287+
for plugin in ctx.split_attr.plugin.values():
288+
cc_infos.append(plugin[SwiftCompilerPluginInfo].cc_info)
289+
direct_swift_modules.extend(plugin[SwiftCompilerPluginInfo].swift_info.direct_modules)
290+
module_name = plugin[SwiftCompilerPluginInfo].module_names.to_list()[0]
291+
swift_infos.append(plugin[SwiftCompilerPluginInfo].swift_info)
292+
293+
first_output_group_info = ctx.split_attr.plugin.values()[0][OutputGroupInfo]
294+
combined_output_group_info = {}
295+
for key in first_output_group_info:
296+
all_values = []
297+
for plugin in ctx.split_attr.plugin.values():
298+
all_values.append(plugin[OutputGroupInfo][key])
299+
combined_output_group_info[key] = depset(transitive = all_values)
300+
301+
transitive_runfiles = [
302+
plugin[DefaultInfo].default_runfiles
303+
for plugin in ctx.split_attr.plugin.values()
304+
]
305+
306+
return [
307+
DefaultInfo(
308+
executable = output,
309+
files = depset([output]),
310+
runfiles = ctx.runfiles().merge_all(transitive_runfiles),
311+
),
312+
OutputGroupInfo(**combined_output_group_info),
313+
SwiftCompilerPluginInfo(
314+
cc_info = cc_common.merge_cc_infos(cc_infos = cc_infos),
315+
executable = output,
316+
module_names = depset([module_name]),
317+
swift_info = swift_common.create_swift_info(
318+
modules = direct_swift_modules,
319+
swift_infos = swift_infos,
320+
),
321+
),
322+
]
323+
324+
universal_swift_compiler_plugin = rule(
325+
attrs = dicts.add(
326+
apple_support.action_required_attrs(),
327+
{
328+
"plugin": attr.label(
329+
cfg = macos_universal_transition,
330+
doc = "Target to generate a 'fat' binary from.",
331+
mandatory = True,
332+
providers = [SwiftCompilerPluginInfo],
333+
),
334+
"_allowlist_function_transition": attr.label(
335+
default = "@bazel_tools//tools/allowlists/function_transition_allowlist",
336+
),
337+
},
338+
),
339+
executable = True,
340+
fragments = ["cpp", "apple"],
341+
implementation = _universal_swift_compiler_plugin_impl,
342+
doc = """\
343+
Wraps an existing `swift_compiler_plugin` target to produce a universal binary.
344+
345+
This is useful to allow sharing of caches between Intel and Apple Silicon Macs
346+
at the cost of building everything twice.
347+
348+
Example:
349+
350+
```bzl
351+
# The actual macro code, using SwiftSyntax, as usual.
352+
swift_compiler_plugin(
353+
name = "Macros",
354+
srcs = glob(["Macros/*.swift"]),
355+
deps = [
356+
"@SwiftSyntax",
357+
"@SwiftSyntax//:SwiftCompilerPlugin",
358+
"@SwiftSyntax//:SwiftSyntaxMacros",
359+
],
360+
)
361+
362+
# Wrap your compiler plugin in this universal shim.
363+
universal_swift_compiler_plugin(
364+
name = "Macros.universal",
365+
plugin = ":Macros",
366+
)
367+
368+
# The library that defines the macro hook for use in your project, this
369+
# references the universal_swift_compiler_plugin.
370+
swift_library(
371+
name = "MacroLibrary",
372+
srcs = glob(["MacroLibrary/*.swift"]),
373+
plugins = [":Macros.universal"],
374+
)
375+
```
376+
""",
377+
)

0 commit comments

Comments
 (0)