Skip to content

Commit

Permalink
Internal change
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 540589258
  • Loading branch information
allevato authored and swiple-rules-gardener committed Jun 15, 2023
1 parent 693b10c commit b114729
Show file tree
Hide file tree
Showing 19 changed files with 596 additions and 10 deletions.
57 changes: 57 additions & 0 deletions examples/xplatform/macros/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
load(
"//swift:swift_binary.bzl",
"swift_binary",
)
load(
"//swift:swift_compiler_plugin.bzl",
"swift_compiler_plugin",
)
load(
"//swift:swift_library.bzl",
"swift_library",
)
load(
"//swift:swift_test.bzl",
"swift_test",
)

licenses(["notice"])

swift_library(
name = "stringify",
srcs = ["Stringify.swift"],
module_name = "Stringify",
plugins = [":stringify_macro"],
)

swift_compiler_plugin(
name = "stringify_macro",
srcs = [
"StringifyMacro.swift",
"StringifyMacroPlugin.swift",
],
module_name = "StringifyMacroPlugin",
deps = [
"@SwiftSyntax",
"@SwiftSyntax//:SwiftCompilerPlugin",
"@SwiftSyntax//:SwiftSyntaxBuilder",
"@SwiftSyntax//:SwiftSyntaxMacros",
],
)

swift_binary(
name = "stringify_client",
srcs = ["StringifyClient.swift"],
deps = [":stringify"],
)

swift_test(
name = "stringify_macro_test",
srcs = ["StringifyMacroTests.swift"],
deps = [
":stringify_macro",
"@SwiftSyntax",
"@SwiftSyntax//:SwiftSyntaxBuilder",
"@SwiftSyntax//:SwiftSyntaxMacros",
],
)
17 changes: 17 additions & 0 deletions examples/xplatform/macros/Stringify.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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.

@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) =
#externalMacro(module: "StringifyMacroPlugin", type: "StringifyMacro")
17 changes: 17 additions & 0 deletions examples/xplatform/macros/StringifyClient.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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 Stringify

print(#stringify(1 + 2))
29 changes: 29 additions & 0 deletions examples/xplatform/macros/StringifyMacro.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 StringifyMacro: 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/StringifyMacroPlugin.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 StringifyMacroPlugin: CompilerPlugin {
let providingMacros: [Macro.Type] = [
StringifyMacro.self
]
}
#endif
38 changes: 38 additions & 0 deletions examples/xplatform/macros/StringifyMacroTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// 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
import StringifyMacroPlugin
import XCTest

class StringifyMacroTests: XCTestCase {
func testStringify() {
let sourceFile: SourceFileSyntax = #"""
_ = #stringify(1 + 2)
"""#
let context = BasicMacroExpansionContext(
sourceFiles: [sourceFile: .init(moduleName: "TestModule", fullFilePath: "Test.swift")]
)
let transformedSourceFile =
sourceFile.expand(macros: ["stringify": StringifyMacro.self], in: context)
XCTAssertEqual(
String(describing: transformedSourceFile),
#"""
_ = (1 + 2, "1 + 2")
"""#
)
}
}
19 changes: 18 additions & 1 deletion swift/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ bzl_library(
":providers",
"//swift/internal:compiling",
"//swift/internal:linking",
"//swift/internal:providers",
"//swift/internal:toolchain_utils",
"//swift/internal:utils",
],
Expand Down Expand Up @@ -70,6 +71,21 @@ bzl_library(
],
)

bzl_library(
name = "swift_compiler_plugin",
srcs = ["swift_compiler_plugin.bzl"],
deps = [
":module_name",
":providers",
"//swift/internal:compiling",
"//swift/internal:linking",
"//swift/internal:providers",
"//swift/internal:toolchain_utils",
"//swift/internal:utils",
"@bazel_skylib//lib:dicts",
],
)

bzl_library(
name = "swift_cross_import_overlay",
srcs = ["swift_cross_import_overlay.bzl"],
Expand Down Expand Up @@ -158,6 +174,7 @@ bzl_library(
"//swift/internal:feature_names",
"//swift/internal:features",
"//swift/internal:linking",
"//swift/internal:providers",
"//swift/internal:toolchain_utils",
"//swift/internal:utils",
"@bazel_skylib//lib:dicts",
Expand Down Expand Up @@ -216,8 +233,8 @@ bzl_library(
":module_name",
":providers",
"//swift/internal:compiling",
"//swift/internal:features",
"//swift/internal:linking",
"//swift/internal:providers",
"//swift/internal:swift_symbol_graph_aspect",
"//swift/internal:symbol_graph_extracting",
"//swift/internal:toolchain_utils",
Expand Down
4 changes: 4 additions & 0 deletions swift/internal/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ bzl_library(
name = "attrs",
srcs = ["attrs.bzl"],
deps = [
":providers",
"//swift:providers",
"@bazel_skylib//lib:dicts",
],
Expand Down Expand Up @@ -122,6 +123,9 @@ bzl_library(
bzl_library(
name = "providers",
srcs = ["providers.bzl"],
visibility = [
"//swift:__subpackages__",
],
)

bzl_library(
Expand Down
26 changes: 23 additions & 3 deletions swift/internal/attrs.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,15 @@

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

visibility([
"@build_bazel_rules_swift//swift/...",
])

def swift_common_rule_attrs(additional_deps_aspects = []):
def swift_common_rule_attrs(
additional_deps_aspects = [],
additional_deps_providers = []):
return {
"data": attr.label_list(
allow_files = True,
Expand All @@ -34,6 +37,7 @@ binary or library, or other programs needed by it.
""",
),
"deps": swift_deps_attr(
additional_deps_providers = additional_deps_providers,
aspects = additional_deps_aspects,
doc = """\
A list of targets that are dependencies of the target being built, which will be
Expand All @@ -52,6 +56,7 @@ indirect (transitive) dependents.

def swift_compilation_attrs(
additional_deps_aspects = [],
additional_deps_providers = [],
requires_srcs = True):
"""Returns an attribute dictionary for rules that compile Swift code.
Expand Down Expand Up @@ -85,6 +90,9 @@ def swift_compilation_attrs(
by the individual rules to avoid potential circular dependencies
between the API and the aspects; the API loaded the aspects
directly, then those aspects would not be able to load the API.
additional_deps_providers: A list of lists representing additional
providers that should be allowed by the `deps` attribute of the
rule.
requires_srcs: Indicates whether the `srcs` attribute should be marked
as mandatory and non-empty. Defaults to `True`.
Expand All @@ -96,6 +104,7 @@ def swift_compilation_attrs(
return dicts.add(
swift_common_rule_attrs(
additional_deps_aspects = additional_deps_aspects,
additional_deps_providers = additional_deps_providers,
),
swift_toolchain_attrs(),
{
Expand Down Expand Up @@ -138,6 +147,14 @@ build label, by stripping the leading `//` and replacing `/`, `:`, and other
non-identifier characters with underscores.
""",
),
"plugins": attr.label_list(
cfg = "exec",
doc = """\
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]],
),
"swiftc_inputs": attr.label_list(
allow_files = True,
doc = """\
Expand All @@ -164,13 +181,16 @@ def swift_config_attrs():
),
}

def swift_deps_attr(doc, **kwargs):
def swift_deps_attr(*, additional_deps_providers = [], doc, **kwargs):
"""Returns an attribute suitable for representing Swift rule dependencies.
The returned attribute will be configured to accept targets that propagate
`CcInfo` or `SwiftInfo` providers.
Args:
additional_deps_providers: A list of lists representing additional
providers that should be allowed by the `deps` attribute of the
rule.
doc: A string containing a summary description of the purpose of the
attribute. This string will be followed by additional text that
lists the permitted kinds of targets that may go in this attribute.
Expand All @@ -189,7 +209,7 @@ Allowed kinds of dependencies are:
* `cc_library` and `objc_library` (or anything propagating `CcInfo`)
""",
providers = [[CcInfo], [SwiftInfo]],
providers = [[CcInfo], [SwiftInfo]] + additional_deps_providers,
**kwargs
)

Expand Down
17 changes: 17 additions & 0 deletions swift/internal/compiling.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ def compile(
feature_configuration,
generated_header_name = None,
module_name,
plugins = [],
private_swift_infos = [],
srcs,
swift_infos,
Expand Down Expand Up @@ -249,6 +250,8 @@ def compile(
module_name: The name of the Swift module being compiled. This must be
present and valid; use `derive_swift_module_name` to generate a
default from the target's label if needed.
plugins: A list of `SwiftCompilerPluginInfo` providers that represent
plugins that should be loaded by the compiler.
private_swift_infos: A list of `SwiftInfo` providers from private
(implementation-only) dependencies of the target being compiled. The
modules defined by these providers are used as dependencies of the
Expand Down Expand Up @@ -428,6 +431,18 @@ def compile(
else:
deps_modules_file = None

# As of the time of this writing (Xcode 15.0), macros are the only kind of
# plugins that are available. Since macros do source-level transformations,
# we only need to load plugins directly used by the module being compiled.
# Plugins that are only used by transitive dependencies do *not* need to be
# passed; the compiler does not attempt to load them when deserializing
# modules.
used_plugins = list(plugins)
for swift_info in swift_infos:
for module_context in swift_info.direct_modules:
if module_context.swift and module_context.swift.plugins:
used_plugins.extend(module_context.swift.plugins)

prerequisites = struct(
additional_inputs = additional_inputs,
always_include_headers = is_feature_enabled(
Expand All @@ -443,6 +458,7 @@ def compile(
is_swift = True,
module_name = module_name,
original_module_name = original_module_name,
plugins = used_plugins,
source_files = srcs,
target_label = feature_configuration._label,
transitive_modules = transitive_modules,
Expand Down Expand Up @@ -521,6 +537,7 @@ def compile(
swift = create_swift_module_inputs(
defines = defines,
original_module_name = original_module_name,
plugins = plugins,
swiftdoc = compile_outputs.swiftdoc_file,
swiftinterface = compile_outputs.swiftinterface_file,
swiftmodule = compile_outputs.swiftmodule_file,
Expand Down
Loading

2 comments on commit b114729

@keith
Copy link
Member

@keith keith commented on b114729 Jun 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brentleyjones
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also #1395

Please # to comment.