Skip to content

Commit f13e258

Browse files
committed
Add swift_compiler_plugin_group rule
This is similar to `swift_library_group`, in that it allows you to group multiple `swift_compiler_plugin` targets under a single target. This is needed to support SPM’s `plugin` products, which support multiple plugin targets under a single product. Signed-off-by: Brentley Jones <github@brentleyjones.com>
1 parent ea2d4bd commit f13e258

20 files changed

+283
-26
lines changed

doc/BUILD

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ _DOC_SRCS = {
1515
"SwiftInfo",
1616
"SwiftToolchainInfo",
1717
"SwiftProtoCompilerInfo",
18+
"SwiftProtoCompilerCollectionInfo",
1819
"SwiftProtoInfo",
1920
"SwiftUsageInfo",
2021
"DeprecatedSwiftGRPCInfo",
@@ -23,6 +24,7 @@ _DOC_SRCS = {
2324
"swift_binary",
2425
"swift_c_module",
2526
"swift_compiler_plugin",
27+
"swift_compiler_plugin_group",
2628
"universal_swift_compiler_plugin",
2729
"swift_feature_allowlist",
2830
"swift_import",

doc/doc.bzl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ load(
5858
# api
5959
_swift_common = "swift_common",
6060
_swift_compiler_plugin = "swift_compiler_plugin",
61+
_swift_compiler_plugin_group = "swift_compiler_plugin_group",
6162
_swift_feature_allowlist = "swift_feature_allowlist",
6263
_swift_import = "swift_import",
6364
_swift_library = "swift_library",
@@ -92,6 +93,7 @@ deprecated_swift_proto_library = _deprecated_swift_proto_library
9293
swift_binary = _swift_binary
9394
swift_c_module = _swift_c_module
9495
swift_compiler_plugin = _swift_compiler_plugin
96+
swift_compiler_plugin_group = _swift_compiler_plugin_group
9597
universal_swift_compiler_plugin = _universal_swift_compiler_plugin
9698
swift_feature_allowlist = _swift_feature_allowlist
9799
swift_import = _swift_import

doc/providers.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ On this page:
1010
* [SwiftInfo](#SwiftInfo)
1111
* [SwiftToolchainInfo](#SwiftToolchainInfo)
1212
* [SwiftProtoCompilerInfo](#SwiftProtoCompilerInfo)
13+
* [SwiftProtoCompilerCollectionInfo](#SwiftProtoCompilerCollectionInfo)
1314
* [SwiftProtoInfo](#SwiftProtoInfo)
1415
* [SwiftUsageInfo](#SwiftUsageInfo)
1516
* [DeprecatedSwiftGRPCInfo](#DeprecatedSwiftGRPCInfo)

doc/rules.md

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ On this page:
2222
* [swift_binary](#swift_binary)
2323
* [swift_c_module](#swift_c_module)
2424
* [swift_compiler_plugin](#swift_compiler_plugin)
25+
* [swift_compiler_plugin_group](#swift_compiler_plugin_group)
2526
* [universal_swift_compiler_plugin](#universal_swift_compiler_plugin)
2627
* [swift_feature_allowlist](#swift_feature_allowlist)
2728
* [swift_import](#swift_import)
@@ -401,6 +402,25 @@ swift_library(
401402
| <a id="swift_compiler_plugin-swiftc_inputs"></a>swiftc_inputs | Additional files that are referenced using `$(location ...)` in attributes that support location expansion. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
402403

403404

405+
<a id="swift_compiler_plugin_group"></a>
406+
407+
## swift_compiler_plugin_group
408+
409+
<pre>
410+
swift_compiler_plugin_group(<a href="#swift_compiler_plugin_group-name">name</a>, <a href="#swift_compiler_plugin_group-plugins">plugins</a>)
411+
</pre>
412+
413+
414+
415+
**ATTRIBUTES**
416+
417+
418+
| Name | Description | Type | Mandatory | Default |
419+
| :------------- | :------------- | :------------- | :------------- | :------------- |
420+
| <a id="swift_compiler_plugin_group-name"></a>name | A unique name for this target. | <a href="https://bazel.build/concepts/labels#target-names">Name</a> | required | |
421+
| <a id="swift_compiler_plugin_group-plugins"></a>plugins | A list of `swift_compiler_plugin` or `swift_compiler_plugin_group` targets that should be loaded by the compiler when compiling this module and any modules that directly depend on it. | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
422+
423+
404424
<a id="swift_feature_allowlist"></a>
405425

406426
## swift_feature_allowlist
@@ -678,7 +698,7 @@ swift_proto_library(
678698
| <a id="swift_proto_library-additional_compiler_info"></a>additional_compiler_info | Dictionary of additional information passed to the compiler targets. See the documentation of the respective compiler rules for more information on which fields are accepted and how they are used. | <a href="https://bazel.build/rules/lib/dict">Dictionary: String -> String</a> | optional | `{}` |
679699
| <a id="swift_proto_library-always_include_developer_search_paths"></a>always_include_developer_search_paths | If `True`, the developer framework search paths will be added to the compilation command. This enables a Swift module to access `XCTest` without having to mark the target as `testonly = True`. | Boolean | optional | `False` |
680700
| <a id="swift_proto_library-alwayslink"></a>alwayslink | If true, any binary that depends (directly or indirectly) on this Swift module will link in all the object files for the files listed in `srcs`, even if some contain no symbols referenced by the binary. This is useful if your code isn't explicitly called by code in the binary; for example, if you rely on runtime checks for protocol conformances added in extensions in the library but do not directly reference any other symbols in the object file that adds that conformance. | Boolean | optional | `False` |
681-
| <a id="swift_proto_library-compilers"></a>compilers | One or more `swift_proto_compiler` targets (or targets producing `SwiftProtoCompilerInfo`), from which the Swift protos will be generated. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `["@build_bazel_rules_swift//proto/compilers:swift_proto"]` |
701+
| <a id="swift_proto_library-compilers"></a>compilers | One or more `swift_proto_compiler` targets (or targets producing `SwiftProtoCompilerInfo`), from which the Swift protos will be generated. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `["@rules_swift//proto/compilers:swift_proto"]` |
682702
| <a id="swift_proto_library-copts"></a>copts | Additional compiler options that should be passed to `swiftc`. These strings are subject to `$(location ...)` and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. | List of strings | optional | `[]` |
683703
| <a id="swift_proto_library-defines"></a>defines | A list of defines to add to the compilation command line.<br><br>Note that unlike C-family languages, Swift defines do not have values; they are simply identifiers that are either defined or undefined. So strings in this list should be simple identifiers, **not** `name=value` pairs.<br><br>Each string is prepended with `-D` and added to the command line. Unlike `copts`, these flags are added for the target and every target that depends on it, so use this attribute with caution. It is preferred that you add defines directly to `copts`, only using this feature in the rare case that a library needs to propagate a symbol up to those that depend on it. | List of strings | optional | `[]` |
684704
| <a id="swift_proto_library-generated_header_name"></a>generated_header_name | The name of the generated Objective-C interface header. This name must end with a `.h` extension and cannot contain any path separators.<br><br>If this attribute is not specified, then the default behavior is to name the header `${target_name}-Swift.h`.<br><br>This attribute is ignored if the toolchain does not support generating headers. | String | optional | `""` |
@@ -893,6 +913,6 @@ swift_library(
893913
| Name | Description | Type | Mandatory | Default |
894914
| :------------- | :------------- | :------------- | :------------- | :------------- |
895915
| <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 | |
896-
| <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 | |
916+
| <a id="universal_swift_compiler_plugin-plugin"></a>plugin | A `swift_compiler_plugin` target to generate a 'fat' binary for. | <a href="https://bazel.build/concepts/labels">Label</a> | required | |
897917

898918

examples/xplatform/macros/BUILD

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
load("//swift:swift.bzl", "swift_binary", "swift_library", "swift_test")
2-
load("//swift:swift_compiler_plugin.bzl", "swift_compiler_plugin", "universal_swift_compiler_plugin")
2+
load(
3+
"//swift:swift_compiler_plugin.bzl",
4+
"swift_compiler_plugin",
5+
"swift_compiler_plugin_group",
6+
"universal_swift_compiler_plugin",
7+
)
38

49
licenses(["notice"])
510

@@ -12,14 +17,19 @@ swift_library(
1217
name = "stringify",
1318
srcs = ["Stringify.swift"],
1419
module_name = "Stringify",
15-
plugins = [":stringify_macro"],
20+
plugins = [":stringify_macro_group"],
1621
)
1722

1823
swift_library(
1924
name = "stringify_universal",
2025
srcs = ["Stringify.swift"],
2126
module_name = "StringifyUniversal",
22-
plugins = [":stringify_macro_universal"],
27+
plugins = [
28+
# We don't create and use a `stringify_macro_universal` here, but we
29+
# could have if we wanted to
30+
":stringify_macro_universal",
31+
":stringify2_macro_universal",
32+
],
2333
)
2434

2535
swift_compiler_plugin(
@@ -41,11 +51,43 @@ swift_compiler_plugin(
4151
],
4252
)
4353

54+
swift_compiler_plugin(
55+
name = "stringify2_macro",
56+
srcs = [
57+
"Stringify2Macro.swift",
58+
"Stringify2MacroPlugin.swift",
59+
],
60+
module_name = "Stringify2MacroPlugin",
61+
target_compatible_with = select({
62+
":supports_macros": [],
63+
"//conditions:default": ["@platforms//:incompatible"],
64+
}),
65+
deps = [
66+
"@SwiftSyntax",
67+
"@SwiftSyntax//:SwiftCompilerPlugin",
68+
"@SwiftSyntax//:SwiftSyntaxBuilder",
69+
"@SwiftSyntax//:SwiftSyntaxMacros",
70+
],
71+
)
72+
73+
swift_compiler_plugin_group(
74+
name = "stringify_macro_group",
75+
plugins = [
76+
":stringify_macro",
77+
":stringify2_macro",
78+
],
79+
)
80+
4481
universal_swift_compiler_plugin(
4582
name = "stringify_macro_universal",
4683
plugin = ":stringify_macro",
4784
)
4885

86+
universal_swift_compiler_plugin(
87+
name = "stringify2_macro_universal",
88+
plugin = ":stringify_macro",
89+
)
90+
4991
swift_binary(
5092
name = "stringify_client",
5193
srcs = ["StringifyClient.swift"],
@@ -66,6 +108,9 @@ swift_test(
66108
"//conditions:default": ["@platforms//:incompatible"],
67109
}),
68110
deps = [
111+
":stringify_macro_group",
112+
# `stringify_macro` is in `stringify_macro_group`, but we list both to
113+
# show that it handles that fine
69114
":stringify_macro",
70115
"@SwiftSyntax",
71116
"@SwiftSyntax//:SwiftSyntaxBuilder",

examples/xplatform/macros/Stringify.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,7 @@
1515
@freestanding(expression)
1616
public macro stringify<T>(_ value: T) -> (T, String) =
1717
#externalMacro(module: "StringifyMacroPlugin", type: "StringifyMacro")
18+
19+
@freestanding(expression)
20+
public macro stringify2<T>(_ value: T) -> (T, String) =
21+
#externalMacro(module: "Stringify2MacroPlugin", type: "Stringify2Macro")
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
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 SwiftSyntax
16+
import SwiftSyntaxBuilder
17+
import SwiftSyntaxMacros
18+
19+
public struct Stringify2Macro: ExpressionMacro {
20+
public static func expansion(
21+
of node: some FreestandingMacroExpansionSyntax,
22+
in context: some MacroExpansionContext
23+
) -> ExprSyntax {
24+
guard let argument = node.argumentList.first?.expression else {
25+
fatalError("compiler bug: the macro does not have any arguments")
26+
}
27+
return "(\(argument), \(literal: argument.description))"
28+
}
29+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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+
#if canImport(SwiftCompilerPlugin)
16+
import SwiftCompilerPlugin
17+
import SwiftSyntaxMacros
18+
19+
@main
20+
struct Stringify2MacroPlugin: CompilerPlugin {
21+
let providingMacros: [Macro.Type] = [
22+
Stringify2Macro.self
23+
]
24+
}
25+
#endif

examples/xplatform/macros/StringifyClient.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,6 @@ import Stringify
1818
struct Main {
1919
static func main() {
2020
print(#stringify(1 + 2))
21+
print(#stringify2(2 + 1))
2122
}
2223
}

examples/xplatform/macros/StringifyMacroTests.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import SwiftSyntax
1616
import SwiftSyntaxBuilder
1717
import SwiftSyntaxMacros
1818
import StringifyMacroPlugin
19+
import Stringify2MacroPlugin
1920
import XCTest
2021

2122
class StringifyMacroTests: XCTestCase {
@@ -35,4 +36,21 @@ class StringifyMacroTests: XCTestCase {
3536
"""#
3637
)
3738
}
39+
40+
func testStringify2() {
41+
let sourceFile: SourceFileSyntax = #"""
42+
_ = #stringify2(2 + 1)
43+
"""#
44+
let context = BasicMacroExpansionContext(
45+
sourceFiles: [sourceFile: .init(moduleName: "TestModule", fullFilePath: "Test.swift")]
46+
)
47+
let transformedSourceFile =
48+
sourceFile.expand(macros: ["stringify2": Stringify2Macro.self], in: context)
49+
XCTAssertEqual(
50+
String(describing: transformedSourceFile),
51+
#"""
52+
_ = (2 + 1, "2 + 1")
53+
"""#
54+
)
55+
}
3856
}

swift/deprecated_proto/deprecated_swift_grpc_library.bzl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ def _register_grpcswift_generate_action(
222222
return generated_files
223223

224224
def _swift_grpc_library_impl(ctx):
225-
print("WARNING: This rule is deprecated. See [the proto migration doc](proto_migration.md) for more information.") # buildifier: disable=print
225+
print("WARNING: This rule is deprecated. See proto_migration.md for more information.") # buildifier: disable=print
226226

227227
if len(ctx.attr.deps) != 1:
228228
fail(

swift/internal/attrs.bzl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@
1515
"""Common attributes used by multiple Swift build rules."""
1616

1717
load("@bazel_skylib//lib:dicts.bzl", "dicts")
18-
load(":providers.bzl", "SwiftCompilerPluginInfo", "SwiftInfo", "SwiftToolchainInfo")
18+
load(
19+
":providers.bzl",
20+
"SwiftCompilerPluginInfo",
21+
"SwiftCompilerPluginCollectionInfo",
22+
"SwiftInfo",
23+
"SwiftToolchainInfo",
24+
)
1925

2026
def swift_common_rule_attrs(
2127
additional_deps_aspects = [],
@@ -146,7 +152,10 @@ Swift 5.9+.
146152
A list of `swift_compiler_plugin` targets that should be loaded by the compiler
147153
when compiling this module and any modules that directly depend on it.
148154
""",
149-
providers = [[SwiftCompilerPluginInfo]],
155+
providers = [
156+
[SwiftCompilerPluginInfo],
157+
[SwiftCompilerPluginCollectionInfo],
158+
],
150159
),
151160
"srcs": attr.label_list(
152161
allow_empty = not requires_srcs,

swift/internal/feature_names.bzl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,13 +209,13 @@ SWIFT_FEATURE_USE_RESPONSE_FILES = "swift.use_response_files"
209209

210210
# If enabled, Swift linking actions will use `swift-autolink-extract` to extract
211211
# the linker arguments. This is required for ELF targets. This is used
212-
# internally to determine the behaviour of the actions across different
212+
# internally to determine the behavior of the actions across different
213213
# toolchain platforms, this is should not be set by users of the toolchain.
214214
SWIFT_FEATURE_USE_AUTOLINK_EXTRACT = "swift.use_autolink_extract"
215215

216216
# If enabled, Swift will wrap the `.swiftmodule` into an object file and link it
217217
# into the module. This is used internally to support the different platforms
218-
# which have differing behaviour for debug information handling. This should
218+
# which have differing behavior for debug information handling. This should
219219
# not be used by users of the toolchain.
220220
SWIFT_FEATURE_USE_MODULE_WRAP = "swift.use_module_wrap"
221221

swift/internal/providers.bzl

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

1515
"""Defines Starlark providers that propagated by the Swift BUILD rules."""
1616

17+
SwiftCompilerPluginCollectionInfo = provider(
18+
doc = "Information about a collection of `SwiftCompilerPluginInfo` providers.",
19+
fields = {
20+
"plugins": "A `depset` of `SwiftCompilerPluginInfo` providers",
21+
},
22+
)
23+
1724
SwiftCompilerPluginInfo = provider(
1825
doc = "Information about compiler plugins, like macros.",
1926
fields = {
@@ -548,3 +555,27 @@ def create_swift_info(
548555
transitive = transitive_modules,
549556
),
550557
)
558+
559+
def get_compiler_plugin_infos(targets):
560+
"""Returns effective `SwiftCompilerPluginInfo` providers from each target in the list.
561+
562+
The returned list may not be the same size as `targets` if some of the
563+
targets do not contain a `SwiftCompilerPluginInfo` provider, or if they
564+
contain a `SwiftCompilerPluginCollectionInfo` provider that expands to 0 or
565+
more `SwiftCompilerPluginInfo` providers.
566+
567+
Args:
568+
targets: A list of targets.
569+
570+
Returns:
571+
A list of `SwiftCompilerPluginInfo` providers from the targets.
572+
"""
573+
plugins = []
574+
for target in targets:
575+
if SwiftCompilerPluginInfo in target:
576+
plugins.append(target[SwiftCompilerPluginInfo])
577+
if SwiftCompilerPluginCollectionInfo in target:
578+
plugins.extend(
579+
target[SwiftCompilerPluginCollectionInfo].plugins.to_list(),
580+
)
581+
return plugins

0 commit comments

Comments
 (0)