Skip to content

Commit 6beacf8

Browse files
allevatobrentleyjones
authored andcommitted
Add a Boolean library_evolution attribute to swift_library
Historically, library evolution has been supported by a global flag that Apple framework rules transition on in order to build all dependencies with library evolution enabled. In retrospect, this is wrong; we shouldn't be automatically applying library evolution to the whole subgraph in this way. Instead, the owner of the framework should explicitly specify library evolution on the client-facing modules that make up their SDK. The goal here is to ensure that framework owners carefully audit their public `deps` vs. implementation-only `private_deps` so that the `.swiftinterface` doesn't attempt to import anything that it shouldn't, especially with Swift 5.7 validating emitted interfaces after compilation by default. This change only adds the new attribute; the flag is still honored while we have clients and rules_apple depending on it. PiperOrigin-RevId: 486750877 (cherry picked from commit b9175e6) Cherry-pick notes: With this change the private swiftinterface is enabled by `ctx.attr.library_evolution` as well. It can be disabled with `features = ["-swift.emit_private_swiftinterface”]`. Signed-off-by: Brentley Jones <[email protected]>
1 parent 884373e commit 6beacf8

File tree

12 files changed

+178
-95
lines changed

12 files changed

+178
-95
lines changed

doc/rules.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,8 +379,8 @@ its transitive dependencies be propagated.
379379

380380
<pre>
381381
swift_library(<a href="#swift_library-name">name</a>, <a href="#swift_library-deps">deps</a>, <a href="#swift_library-srcs">srcs</a>, <a href="#swift_library-data">data</a>, <a href="#swift_library-always_include_developer_search_paths">always_include_developer_search_paths</a>, <a href="#swift_library-alwayslink">alwayslink</a>, <a href="#swift_library-copts">copts</a>,
382-
<a href="#swift_library-defines">defines</a>, <a href="#swift_library-generated_header_name">generated_header_name</a>, <a href="#swift_library-generates_header">generates_header</a>, <a href="#swift_library-linkopts">linkopts</a>, <a href="#swift_library-linkstatic">linkstatic</a>, <a href="#swift_library-module_name">module_name</a>,
383-
<a href="#swift_library-package_name">package_name</a>, <a href="#swift_library-plugins">plugins</a>, <a href="#swift_library-private_deps">private_deps</a>, <a href="#swift_library-swiftc_inputs">swiftc_inputs</a>)
382+
<a href="#swift_library-defines">defines</a>, <a href="#swift_library-generated_header_name">generated_header_name</a>, <a href="#swift_library-generates_header">generates_header</a>, <a href="#swift_library-library_evolution">library_evolution</a>, <a href="#swift_library-linkopts">linkopts</a>,
383+
<a href="#swift_library-linkstatic">linkstatic</a>, <a href="#swift_library-module_name">module_name</a>, <a href="#swift_library-package_name">package_name</a>, <a href="#swift_library-plugins">plugins</a>, <a href="#swift_library-private_deps">private_deps</a>, <a href="#swift_library-swiftc_inputs">swiftc_inputs</a>)
384384
</pre>
385385

386386
Compiles and links Swift code into a static library and Swift module.
@@ -400,6 +400,7 @@ Compiles and links Swift code into a static library and Swift module.
400400
| <a id="swift_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 | `[]` |
401401
| <a id="swift_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 | `""` |
402402
| <a id="swift_library-generates_header"></a>generates_header | If True, an Objective-C header will be generated for this target, in the same build package where the target is defined. By default, the name of the header is `${target_name}-Swift.h`; this can be changed using the `generated_header_name` attribute.<br><br>Targets should only set this attribute to True if they export Objective-C APIs. A header generated for a target that does not export Objective-C APIs will be effectively empty (except for a large amount of prologue and epilogue code) and this is generally wasteful because the extra file needs to be propagated in the build graph and, when explicit modules are enabled, extra actions must be executed to compile the Objective-C module for the generated header. | Boolean | optional | `False` |
403+
| <a id="swift_library-library_evolution"></a>library_evolution | Indicates whether the Swift code should be compiled with library evolution mode enabled.<br><br>This attribute should be used to compile a module that will be distributed as part of a client-facing (non-implementation-only) module in a library or framework that will be distributed for use outside of the Bazel build graph. Setting this to true will compile the module with the `-library-evolution` flag and emit a `.swiftinterface` file as one of the compilation outputs. | Boolean | optional | `False` |
403404
| <a id="swift_library-linkopts"></a>linkopts | Additional linker options that should be passed to the linker for the binary that depends on this target. These strings are subject to `$(location ...)` and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. | List of strings | optional | `[]` |
404405
| <a id="swift_library-linkstatic"></a>linkstatic | If True, the Swift module will be built for static linking. This will make all interfaces internal to the module that is being linked against and will inform the consuming module that the objects will be locally available (which may potentially avoid a PLT relocation). Set to `False` to build a `.so` or `.dll`. | Boolean | optional | `True` |
405406
| <a id="swift_library-module_name"></a>module_name | The name of the Swift module being built.<br><br>If left unspecified, the module name will be computed based on the target's build label, by stripping the leading `//` and replacing `/`, `:`, and other non-identifier characters with underscores. | String | optional | `""` |
@@ -528,8 +529,8 @@ swift_proto_compiler(<a href="#swift_proto_compiler-name">name</a>, <a href="#sw
528529
<pre>
529530
swift_proto_library(<a href="#swift_proto_library-name">name</a>, <a href="#swift_proto_library-deps">deps</a>, <a href="#swift_proto_library-srcs">srcs</a>, <a href="#swift_proto_library-data">data</a>, <a href="#swift_proto_library-additional_compiler_deps">additional_compiler_deps</a>, <a href="#swift_proto_library-additional_compiler_info">additional_compiler_info</a>,
530531
<a href="#swift_proto_library-always_include_developer_search_paths">always_include_developer_search_paths</a>, <a href="#swift_proto_library-alwayslink">alwayslink</a>, <a href="#swift_proto_library-compilers">compilers</a>, <a href="#swift_proto_library-copts">copts</a>, <a href="#swift_proto_library-defines">defines</a>,
531-
<a href="#swift_proto_library-generated_header_name">generated_header_name</a>, <a href="#swift_proto_library-generates_header">generates_header</a>, <a href="#swift_proto_library-linkopts">linkopts</a>, <a href="#swift_proto_library-linkstatic">linkstatic</a>, <a href="#swift_proto_library-module_name">module_name</a>,
532-
<a href="#swift_proto_library-package_name">package_name</a>, <a href="#swift_proto_library-plugins">plugins</a>, <a href="#swift_proto_library-protos">protos</a>, <a href="#swift_proto_library-swiftc_inputs">swiftc_inputs</a>)
532+
<a href="#swift_proto_library-generated_header_name">generated_header_name</a>, <a href="#swift_proto_library-generates_header">generates_header</a>, <a href="#swift_proto_library-library_evolution">library_evolution</a>, <a href="#swift_proto_library-linkopts">linkopts</a>, <a href="#swift_proto_library-linkstatic">linkstatic</a>,
533+
<a href="#swift_proto_library-module_name">module_name</a>, <a href="#swift_proto_library-package_name">package_name</a>, <a href="#swift_proto_library-plugins">plugins</a>, <a href="#swift_proto_library-protos">protos</a>, <a href="#swift_proto_library-swiftc_inputs">swiftc_inputs</a>)
533534
</pre>
534535

535536
Generates a Swift static library from one or more targets producing `ProtoInfo`.
@@ -587,6 +588,7 @@ swift_proto_library(
587588
| <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 | `[]` |
588589
| <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 | `""` |
589590
| <a id="swift_proto_library-generates_header"></a>generates_header | If True, an Objective-C header will be generated for this target, in the same build package where the target is defined. By default, the name of the header is `${target_name}-Swift.h`; this can be changed using the `generated_header_name` attribute.<br><br>Targets should only set this attribute to True if they export Objective-C APIs. A header generated for a target that does not export Objective-C APIs will be effectively empty (except for a large amount of prologue and epilogue code) and this is generally wasteful because the extra file needs to be propagated in the build graph and, when explicit modules are enabled, extra actions must be executed to compile the Objective-C module for the generated header. | Boolean | optional | `False` |
591+
| <a id="swift_proto_library-library_evolution"></a>library_evolution | Indicates whether the Swift code should be compiled with library evolution mode enabled.<br><br>This attribute should be used to compile a module that will be distributed as part of a client-facing (non-implementation-only) module in a library or framework that will be distributed for use outside of the Bazel build graph. Setting this to true will compile the module with the `-library-evolution` flag and emit a `.swiftinterface` file as one of the compilation outputs. | Boolean | optional | `False` |
590592
| <a id="swift_proto_library-linkopts"></a>linkopts | Additional linker options that should be passed to the linker for the binary that depends on this target. These strings are subject to `$(location ...)` and ["Make" variable](https://docs.bazel.build/versions/master/be/make-variables.html) expansion. | List of strings | optional | `[]` |
591593
| <a id="swift_proto_library-linkstatic"></a>linkstatic | If True, the Swift module will be built for static linking. This will make all interfaces internal to the module that is being linked against and will inform the consuming module that the objects will be locally available (which may potentially avoid a PLT relocation). Set to `False` to build a `.so` or `.dll`. | Boolean | optional | `True` |
592594
| <a id="swift_proto_library-module_name"></a>module_name | The name of the Swift module being built.<br><br>If left unspecified, the module name will be computed based on the target's build label, by stripping the leading `//` and replacing `/`, `:`, and other non-identifier characters with underscores. | String | optional | `""` |

swift/internal/attrs.bzl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,20 @@ def swift_library_rule_attrs(
290290
),
291291
swift_config_attrs(),
292292
{
293+
"library_evolution": attr.bool(
294+
default = False,
295+
doc = """\
296+
Indicates whether the Swift code should be compiled with library evolution mode
297+
enabled.
298+
299+
This attribute should be used to compile a module that will be distributed as
300+
part of a client-facing (non-implementation-only) module in a library or
301+
framework that will be distributed for use outside of the Bazel build graph.
302+
Setting this to true will compile the module with the `-library-evolution` flag
303+
and emit a `.swiftinterface` file as one of the compilation outputs.
304+
""",
305+
mandatory = False,
306+
),
293307
"alwayslink": attr.bool(
294308
default = False,
295309
doc = """\

swift/swift_library.bzl

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,20 @@ def _swift_library_impl(ctx):
120120
copts.extend(module_copts)
121121

122122
extra_features = []
123-
if ctx.attr._config_emit_swiftinterface[BuildSettingInfo].value:
123+
124+
# TODO(b/239957001): Remove the global flag.
125+
if (
126+
ctx.attr.library_evolution or
127+
ctx.attr._config_emit_swiftinterface[BuildSettingInfo].value
128+
):
124129
extra_features.append(SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION)
125130
extra_features.append(SWIFT_FEATURE_EMIT_SWIFTINTERFACE)
126131

127-
if ctx.attr._config_emit_private_swiftinterface[BuildSettingInfo].value:
132+
# TODO(b/239957001): Remove the global flag.
133+
if (
134+
ctx.attr.library_evolution or
135+
ctx.attr._config_emit_private_swiftinterface[BuildSettingInfo].value
136+
):
128137
extra_features.append(SWIFT_FEATURE_ENABLE_LIBRARY_EVOLUTION)
129138
extra_features.append(SWIFT_FEATURE_EMIT_PRIVATE_SWIFTINTERFACE)
130139

Lines changed: 5 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
load("//swift:swift_binary.bzl", "swift_binary")
22
load("//swift:swift_import.bzl", "swift_import")
3-
load("//swift:swift_library.bzl", "swift_library")
43
load("//test/fixtures:common.bzl", "FIXTURE_TAGS")
5-
load(
6-
"//test/rules:swift_library_artifact_collector.bzl",
7-
"swift_library_artifact_collector",
8-
)
94

105
package(
116
default_testonly = True,
@@ -23,32 +18,11 @@ swift_binary(
2318

2419
swift_import(
2520
name = "toy_module",
26-
archives = [":toy_outputs/libToyModule.a"],
21+
archives = [
22+
"//test/fixtures/module_interface/library:toy_outputs/libToyModule.a",
23+
],
2724
module_name = "ToyModule",
28-
swiftdoc = ":toy_outputs/ToyModule.swiftdoc",
29-
swiftinterface = ":toy_outputs/ToyModule.swiftinterface",
30-
tags = FIXTURE_TAGS,
31-
)
32-
33-
# Checking in pre-built artifacts like a `.swiftinterface` and static libraries
34-
# would require different artifacts for every platform the test might run on.
35-
# Instead, build it on-demand but forward the outputs using the "artifact
36-
# collector" rule below to make them act as if they were pre-built outputs when
37-
# referenced by the `swift_import` rule.
38-
39-
swift_library(
40-
name = "toy_module_library",
41-
srcs = ["ToyModule.swift"],
42-
module_name = "ToyModule",
43-
tags = FIXTURE_TAGS,
44-
)
45-
46-
swift_library_artifact_collector(
47-
name = "toy_module_artifact_collector",
48-
static_library = "toy_outputs/libToyModule.a",
49-
swiftdoc = "toy_outputs/ToyModule.swiftdoc",
50-
swiftinterface = "toy_outputs/ToyModule.swiftinterface",
25+
swiftdoc = "//test/fixtures/module_interface/library:toy_outputs/ToyModule.swiftdoc",
26+
swiftinterface = "//test/fixtures/module_interface/library:toy_outputs/ToyModule.swiftinterface",
5127
tags = FIXTURE_TAGS,
52-
target = ":toy_module_library",
53-
target_compatible_with = ["@platforms//os:macos"],
5428
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
load("//swift:swift_library.bzl", "swift_library")
2+
load(
3+
"//test/fixtures:common.bzl",
4+
"FIXTURE_TAGS",
5+
)
6+
load(
7+
"//test/rules:swift_library_artifact_collector.bzl",
8+
"swift_library_artifact_collector",
9+
)
10+
11+
package(
12+
default_testonly = True,
13+
default_visibility = ["//test:__subpackages__"],
14+
)
15+
16+
licenses(["notice"])
17+
18+
# Checking in pre-built artifacts like a `.swiftinterface` and static libraries
19+
# would require different artifacts for every platform the test might run on.
20+
# Instead, build it on-demand but forward the outputs using the "artifact
21+
# collector" rule below to make them act as if they were pre-built outputs when
22+
# referenced by the `swift_import` rule.
23+
#
24+
# These must be in a separate package than the `swift_import` target because
25+
# that rule propagates its pre-built inputs in `DefaultInfo`.
26+
27+
swift_library(
28+
name = "toy_module_library",
29+
srcs = ["ToyModule.swift"],
30+
library_evolution = True,
31+
module_name = "ToyModule",
32+
tags = FIXTURE_TAGS,
33+
)
34+
35+
swift_library_artifact_collector(
36+
name = "toy_module_artifact_collector",
37+
static_library = "toy_outputs/libToyModule.a",
38+
swiftdoc = "toy_outputs/ToyModule.swiftdoc",
39+
swiftinterface = "toy_outputs/ToyModule.swiftinterface",
40+
tags = FIXTURE_TAGS,
41+
target = ":toy_module_library",
42+
target_compatible_with = ["@platforms//os:macos"],
43+
)
44+
45+
swift_library(
46+
name = "toy_module_library_without_library_evolution",
47+
srcs = ["ToyModule.swift"],
48+
library_evolution = False,
49+
module_name = "ToyModuleNoEvolution",
50+
tags = FIXTURE_TAGS,
51+
)
File renamed without changes.

test/fixtures/private_swiftinterface/BUILD

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,7 @@ check them in directly.
2020

2121
load("//swift:swift_binary.bzl", "swift_binary")
2222
load("//swift:swift_import.bzl", "swift_import")
23-
load("//swift:swift_library.bzl", "swift_library")
2423
load("//test/fixtures:common.bzl", "FIXTURE_TAGS")
25-
load(
26-
"//test/rules:swift_library_artifact_collector.bzl",
27-
"swift_library_artifact_collector",
28-
)
2924

3025
package(
3126
default_testonly = True,
@@ -43,30 +38,12 @@ swift_binary(
4338

4439
swift_import(
4540
name = "private_swiftinterface_import",
46-
archives = [":private_swiftinterface_outputs/libPrivateSwiftInterface.a"],
41+
archives = ["//test/fixtures/private_swiftinterface/library:private_swiftinterface_outputs/libPrivateSwiftInterface.a"],
4742
module_name = "PrivateSwiftInterface",
48-
swiftdoc = ":private_swiftinterface_outputs/PrivateSwiftInterface.swiftdoc",
43+
swiftdoc = "//test/fixtures/private_swiftinterface/library:private_swiftinterface_outputs/PrivateSwiftInterface.swiftdoc",
4944
# Using the private interface allows using both the normal and private interfaces of a module.
5045
# Only one swiftinterface is allowed per module, so we can't use both at the same time.
5146
# This tests that using the private interface allows a dependent module to use an `@_spi` symbol.
52-
swiftinterface = ":private_swiftinterface_outputs/PrivateSwiftInterface.private.swiftinterface",
53-
tags = FIXTURE_TAGS,
54-
)
55-
56-
swift_library(
57-
name = "private_swiftinterface",
58-
srcs = ["Lib.swift"],
59-
module_name = "PrivateSwiftInterface",
60-
tags = FIXTURE_TAGS,
61-
)
62-
63-
swift_library_artifact_collector(
64-
name = "private_swiftinterface_artifact_collector",
65-
private_swiftinterface = "private_swiftinterface_outputs/PrivateSwiftInterface.private.swiftinterface",
66-
static_library = "private_swiftinterface_outputs/libPrivateSwiftInterface.a",
67-
swiftdoc = "private_swiftinterface_outputs/PrivateSwiftInterface.swiftdoc",
68-
swiftinterface = "private_swiftinterface_outputs/PrivateSwiftInterface.swiftinterface",
47+
swiftinterface = "//test/fixtures/private_swiftinterface/library:private_swiftinterface_outputs/PrivateSwiftInterface.private.swiftinterface",
6948
tags = FIXTURE_TAGS,
70-
target = ":private_swiftinterface",
71-
target_compatible_with = ["@platforms//os:macos"],
7249
)

0 commit comments

Comments
 (0)