Skip to content

Commit

Permalink
Add swift_compiler_plugin_group rule
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
brentleyjones committed Apr 5, 2024
1 parent 1faa3cb commit 850ceba
Show file tree
Hide file tree
Showing 20 changed files with 283 additions and 26 deletions.
2 changes: 2 additions & 0 deletions doc/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ _DOC_SRCS = {
"SwiftInfo",
"SwiftToolchainInfo",
"SwiftProtoCompilerInfo",
"SwiftProtoCompilerCollectionInfo",
"SwiftProtoInfo",
"SwiftUsageInfo",
"DeprecatedSwiftGRPCInfo",
Expand All @@ -23,6 +24,7 @@ _DOC_SRCS = {
"swift_binary",
"swift_c_module",
"swift_compiler_plugin",
"swift_compiler_plugin_group",
"universal_swift_compiler_plugin",
"swift_feature_allowlist",
"swift_import",
Expand Down
2 changes: 2 additions & 0 deletions doc/doc.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ load(
# api
_swift_common = "swift_common",
_swift_compiler_plugin = "swift_compiler_plugin",
_swift_compiler_plugin_group = "swift_compiler_plugin_group",
_swift_feature_allowlist = "swift_feature_allowlist",
_swift_import = "swift_import",
_swift_library = "swift_library",
Expand Down Expand Up @@ -92,6 +93,7 @@ deprecated_swift_proto_library = _deprecated_swift_proto_library
swift_binary = _swift_binary
swift_c_module = _swift_c_module
swift_compiler_plugin = _swift_compiler_plugin
swift_compiler_plugin_group = _swift_compiler_plugin_group
universal_swift_compiler_plugin = _universal_swift_compiler_plugin
swift_feature_allowlist = _swift_feature_allowlist
swift_import = _swift_import
Expand Down
1 change: 1 addition & 0 deletions doc/providers.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ On this page:
* [SwiftInfo](#SwiftInfo)
* [SwiftToolchainInfo](#SwiftToolchainInfo)
* [SwiftProtoCompilerInfo](#SwiftProtoCompilerInfo)
* [SwiftProtoCompilerCollectionInfo](#SwiftProtoCompilerCollectionInfo)
* [SwiftProtoInfo](#SwiftProtoInfo)
* [SwiftUsageInfo](#SwiftUsageInfo)
* [DeprecatedSwiftGRPCInfo](#DeprecatedSwiftGRPCInfo)
Expand Down
24 changes: 22 additions & 2 deletions doc/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ On this page:
* [swift_binary](#swift_binary)
* [swift_c_module](#swift_c_module)
* [swift_compiler_plugin](#swift_compiler_plugin)
* [swift_compiler_plugin_group](#swift_compiler_plugin_group)
* [universal_swift_compiler_plugin](#universal_swift_compiler_plugin)
* [swift_feature_allowlist](#swift_feature_allowlist)
* [swift_import](#swift_import)
Expand Down Expand Up @@ -401,6 +402,25 @@ swift_library(
| <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 | `[]` |


<a id="swift_compiler_plugin_group"></a>

## swift_compiler_plugin_group

<pre>
swift_compiler_plugin_group(<a href="#swift_compiler_plugin_group-name">name</a>, <a href="#swift_compiler_plugin_group-plugins">plugins</a>)
</pre>



**ATTRIBUTES**


| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <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 | |
| <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 | |


<a id="swift_feature_allowlist"></a>

## swift_feature_allowlist
Expand Down Expand Up @@ -678,7 +698,7 @@ swift_proto_library(
| <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 | `{}` |
| <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` |
| <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` |
| <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"]` |
| <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"]` |
| <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 | `[]` |
| <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 | `[]` |
| <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 | `""` |
Expand Down Expand Up @@ -893,6 +913,6 @@ swift_library(
| Name | Description | Type | Mandatory | Default |
| :------------- | :------------- | :------------- | :------------- | :------------- |
| <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 | |
| <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 | |
| <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 | |


51 changes: 48 additions & 3 deletions examples/xplatform/macros/BUILD
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
load("//swift:swift.bzl", "swift_binary", "swift_library", "swift_test")
load("//swift:swift_compiler_plugin.bzl", "swift_compiler_plugin", "universal_swift_compiler_plugin")
load(
"//swift:swift_compiler_plugin.bzl",
"swift_compiler_plugin",
"swift_compiler_plugin_group",
"universal_swift_compiler_plugin",
)

licenses(["notice"])

Expand All @@ -12,14 +17,19 @@ swift_library(
name = "stringify",
srcs = ["Stringify.swift"],
module_name = "Stringify",
plugins = [":stringify_macro"],
plugins = [":stringify_macro_group"],
)

swift_library(
name = "stringify_universal",
srcs = ["Stringify.swift"],
module_name = "StringifyUniversal",
plugins = [":stringify_macro_universal"],
plugins = [
# We don't create and use a `stringify_macro_universal` here, but we
# could have if we wanted to
":stringify_macro_universal",
":stringify2_macro_universal",
],
)

swift_compiler_plugin(
Expand All @@ -41,11 +51,43 @@ swift_compiler_plugin(
],
)

swift_compiler_plugin(
name = "stringify2_macro",
srcs = [
"Stringify2Macro.swift",
"Stringify2MacroPlugin.swift",
],
module_name = "Stringify2MacroPlugin",
target_compatible_with = select({
":supports_macros": [],
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = [
"@SwiftSyntax",
"@SwiftSyntax//:SwiftCompilerPlugin",
"@SwiftSyntax//:SwiftSyntaxBuilder",
"@SwiftSyntax//:SwiftSyntaxMacros",
],
)

swift_compiler_plugin_group(
name = "stringify_macro_group",
plugins = [
":stringify_macro",
":stringify2_macro",
],
)

universal_swift_compiler_plugin(
name = "stringify_macro_universal",
plugin = ":stringify_macro",
)

universal_swift_compiler_plugin(
name = "stringify2_macro_universal",
plugin = ":stringify_macro",
)

swift_binary(
name = "stringify_client",
srcs = ["StringifyClient.swift"],
Expand All @@ -66,6 +108,9 @@ swift_test(
"//conditions:default": ["@platforms//:incompatible"],
}),
deps = [
":stringify_macro_group",
# `stringify_macro` is in `stringify_macro_group`, but we list both to
# show that it handles that fine
":stringify_macro",
"@SwiftSyntax",
"@SwiftSyntax//:SwiftSyntaxBuilder",
Expand Down
4 changes: 4 additions & 0 deletions examples/xplatform/macros/Stringify.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,7 @@
@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) =
#externalMacro(module: "StringifyMacroPlugin", type: "StringifyMacro")

@freestanding(expression)
public macro stringify2<T>(_ value: T) -> (T, String) =
#externalMacro(module: "Stringify2MacroPlugin", type: "Stringify2Macro")
29 changes: 29 additions & 0 deletions examples/xplatform/macros/Stringify2Macro.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright 2023 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros

public struct Stringify2Macro: ExpressionMacro {
public static func expansion(
of node: some FreestandingMacroExpansionSyntax,
in context: some MacroExpansionContext
) -> ExprSyntax {
guard let argument = node.argumentList.first?.expression else {
fatalError("compiler bug: the macro does not have any arguments")
}
return "(\(argument), \(literal: argument.description))"
}
}
25 changes: 25 additions & 0 deletions examples/xplatform/macros/Stringify2MacroPlugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright 2023 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#if canImport(SwiftCompilerPlugin)
import SwiftCompilerPlugin
import SwiftSyntaxMacros

@main
struct Stringify2MacroPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
Stringify2Macro.self
]
}
#endif
1 change: 1 addition & 0 deletions examples/xplatform/macros/StringifyClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ import Stringify
struct Main {
static func main() {
print(#stringify(1 + 2))
print(#stringify2(2 + 1))
}
}
18 changes: 18 additions & 0 deletions examples/xplatform/macros/StringifyMacroTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import SwiftSyntax
import SwiftSyntaxBuilder
import SwiftSyntaxMacros
import StringifyMacroPlugin
import Stringify2MacroPlugin
import XCTest

class StringifyMacroTests: XCTestCase {
Expand All @@ -35,4 +36,21 @@ class StringifyMacroTests: XCTestCase {
"""#
)
}

func testStringify2() {
let sourceFile: SourceFileSyntax = #"""
_ = #stringify2(2 + 1)
"""#
let context = BasicMacroExpansionContext(
sourceFiles: [sourceFile: .init(moduleName: "TestModule", fullFilePath: "Test.swift")]
)
let transformedSourceFile =
sourceFile.expand(macros: ["stringify2": Stringify2Macro.self], in: context)
XCTAssertEqual(
String(describing: transformedSourceFile),
#"""
_ = (2 + 1, "2 + 1")
"""#
)
}
}
2 changes: 1 addition & 1 deletion swift/deprecated_proto/deprecated_swift_grpc_library.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def _register_grpcswift_generate_action(
return generated_files

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

if len(ctx.attr.deps) != 1:
fail(
Expand Down
13 changes: 11 additions & 2 deletions swift/internal/attrs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
"""Common attributes used by multiple Swift build rules."""

load("@bazel_skylib//lib:dicts.bzl", "dicts")
load(":providers.bzl", "SwiftCompilerPluginInfo", "SwiftInfo", "SwiftToolchainInfo")
load(
":providers.bzl",
"SwiftCompilerPluginInfo",
"SwiftCompilerPluginCollectionInfo",
"SwiftInfo",
"SwiftToolchainInfo",
)

def swift_common_rule_attrs(
additional_deps_aspects = [],
Expand Down Expand Up @@ -146,7 +152,10 @@ Swift 5.9+.
A list of `swift_compiler_plugin` targets that should be loaded by the compiler
when compiling this module and any modules that directly depend on it.
""",
providers = [[SwiftCompilerPluginInfo]],
providers = [
[SwiftCompilerPluginInfo],
[SwiftCompilerPluginCollectionInfo],
],
),
"srcs": attr.label_list(
allow_empty = not requires_srcs,
Expand Down
4 changes: 2 additions & 2 deletions swift/internal/feature_names.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -209,13 +209,13 @@ SWIFT_FEATURE_USE_RESPONSE_FILES = "swift.use_response_files"

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

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

Expand Down
31 changes: 31 additions & 0 deletions swift/internal/providers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@

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

SwiftCompilerPluginCollectionInfo = provider(
doc = "Information about a collection of `SwiftCompilerPluginInfo` providers.",
fields = {
"plugins": "A `depset` of `SwiftCompilerPluginInfo` providers",
},
)

SwiftCompilerPluginInfo = provider(
doc = "Information about compiler plugins, like macros.",
fields = {
Expand Down Expand Up @@ -548,3 +555,27 @@ def create_swift_info(
transitive = transitive_modules,
),
)

def get_compiler_plugin_infos(targets):
"""Returns effective `SwiftCompilerPluginInfo` providers from each target in the list.
The returned list may not be the same size as `targets` if some of the
targets do not contain a `SwiftCompilerPluginInfo` provider, or if they
contain a `SwiftCompilerPluginCollectionInfo` provider that expands to 0 or
more `SwiftCompilerPluginInfo` providers.
Args:
targets: A list of targets.
Returns:
A list of `SwiftCompilerPluginInfo` providers from the targets.
"""
plugins = []
for target in targets:
if SwiftCompilerPluginInfo in target:
plugins.append(target[SwiftCompilerPluginInfo])
if SwiftCompilerPluginCollectionInfo in target:
plugins.extend(
target[SwiftCompilerPluginCollectionInfo].plugins.to_list(),
)
return plugins
Loading

0 comments on commit 850ceba

Please sign in to comment.