Skip to content

Commit f8a0428

Browse files
authored
feat: add always_include_developer_search_paths attribute to swift_library (#1162)
1 parent c5c8efe commit f8a0428

21 files changed

+440
-63
lines changed

doc/api.md

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ A C++ `FeatureConfiguration` value (see
3333
## swift_common.compilation_attrs
3434

3535
<pre>
36-
swift_common.compilation_attrs(<a href="#swift_common.compilation_attrs-additional_deps_aspects">additional_deps_aspects</a>, <a href="#swift_common.compilation_attrs-additional_deps_providers">additional_deps_providers</a>, <a href="#swift_common.compilation_attrs-requires_srcs">requires_srcs</a>)
36+
swift_common.compilation_attrs(<a href="#swift_common.compilation_attrs-additional_deps_aspects">additional_deps_aspects</a>, <a href="#swift_common.compilation_attrs-additional_deps_providers">additional_deps_providers</a>,
37+
<a href="#swift_common.compilation_attrs-include_dev_srch_paths_attrib">include_dev_srch_paths_attrib</a>, <a href="#swift_common.compilation_attrs-requires_srcs">requires_srcs</a>)
3738
</pre>
3839

3940
Returns an attribute dictionary for rules that compile Swift code.
@@ -70,6 +71,7 @@ attributes from the earlier items in the list.
7071
| :------------- | :------------- | :------------- |
7172
| <a id="swift_common.compilation_attrs-additional_deps_aspects"></a>additional_deps_aspects | A list of additional aspects that should be applied to `deps`. Defaults to the empty list. These must be passed 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. | `[]` |
7273
| <a id="swift_common.compilation_attrs-additional_deps_providers"></a>additional_deps_providers | A list of lists representing additional providers that should be allowed by the `deps` attribute of the rule. | `[]` |
74+
| <a id="swift_common.compilation_attrs-include_dev_srch_paths_attrib"></a>include_dev_srch_paths_attrib | A `bool` that indicates whether to include the `always_include_developer_search_paths` attribute. | `False` |
7375
| <a id="swift_common.compilation_attrs-requires_srcs"></a>requires_srcs | Indicates whether the `srcs` attribute should be marked as mandatory and non-empty. Defaults to `True`. | `True` |
7476

7577
**RETURNS**
@@ -85,8 +87,9 @@ A new attribute dictionary that can be added to the attributes of a
8587

8688
<pre>
8789
swift_common.compile(<a href="#swift_common.compile-actions">actions</a>, <a href="#swift_common.compile-additional_inputs">additional_inputs</a>, <a href="#swift_common.compile-copts">copts</a>, <a href="#swift_common.compile-defines">defines</a>, <a href="#swift_common.compile-deps">deps</a>, <a href="#swift_common.compile-extra_swift_infos">extra_swift_infos</a>,
88-
<a href="#swift_common.compile-feature_configuration">feature_configuration</a>, <a href="#swift_common.compile-generated_header_name">generated_header_name</a>, <a href="#swift_common.compile-is_test">is_test</a>, <a href="#swift_common.compile-module_name">module_name</a>, <a href="#swift_common.compile-package_name">package_name</a>,
89-
<a href="#swift_common.compile-plugins">plugins</a>, <a href="#swift_common.compile-private_deps">private_deps</a>, <a href="#swift_common.compile-srcs">srcs</a>, <a href="#swift_common.compile-swift_toolchain">swift_toolchain</a>, <a href="#swift_common.compile-target_name">target_name</a>, <a href="#swift_common.compile-workspace_name">workspace_name</a>)
90+
<a href="#swift_common.compile-feature_configuration">feature_configuration</a>, <a href="#swift_common.compile-generated_header_name">generated_header_name</a>, <a href="#swift_common.compile-is_test">is_test</a>, <a href="#swift_common.compile-include_dev_srch_paths">include_dev_srch_paths</a>,
91+
<a href="#swift_common.compile-module_name">module_name</a>, <a href="#swift_common.compile-package_name">package_name</a>, <a href="#swift_common.compile-plugins">plugins</a>, <a href="#swift_common.compile-private_deps">private_deps</a>, <a href="#swift_common.compile-srcs">srcs</a>, <a href="#swift_common.compile-swift_toolchain">swift_toolchain</a>,
92+
<a href="#swift_common.compile-target_name">target_name</a>, <a href="#swift_common.compile-workspace_name">workspace_name</a>)
9093
</pre>
9194

9295
Compiles a Swift module.
@@ -104,7 +107,8 @@ Compiles a Swift module.
104107
| <a id="swift_common.compile-extra_swift_infos"></a>extra_swift_infos | Extra `SwiftInfo` providers that aren't contained by the `deps` of the target being compiled but are required for compilation. | `[]` |
105108
| <a id="swift_common.compile-feature_configuration"></a>feature_configuration | A feature configuration obtained from `swift_common.configure_features`. | none |
106109
| <a id="swift_common.compile-generated_header_name"></a>generated_header_name | The name of the Objective-C generated header that should be generated for this module. If omitted, no header will be generated. | `None` |
107-
| <a id="swift_common.compile-is_test"></a>is_test | Represents if the `testonly` value of the context. | none |
110+
| <a id="swift_common.compile-is_test"></a>is_test | Deprecated. This argument will be removed in the next major release. Use the `include_dev_srch_paths` attribute instead. Represents if the `testonly` value of the context. | `None` |
111+
| <a id="swift_common.compile-include_dev_srch_paths"></a>include_dev_srch_paths | A `bool` that indicates whether the developer framework search paths will be added to the compilation command. | `None` |
108112
| <a id="swift_common.compile-module_name"></a>module_name | The name of the Swift module being compiled. This must be present and valid; use `swift_common.derive_module_name` to generate a default from the target's label if needed. | none |
109113
| <a id="swift_common.compile-package_name"></a>package_name | The semantic package of the name of the Swift module being compiled. | none |
110114
| <a id="swift_common.compile-plugins"></a>plugins | A list of `SwiftCompilerPluginInfo` providers that represent plugins that should be loaded by the compiler. | `[]` |
@@ -291,7 +295,8 @@ A `struct` containing four fields:
291295
<pre>
292296
swift_common.create_linking_context_from_compilation_outputs(<a href="#swift_common.create_linking_context_from_compilation_outputs-actions">actions</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-additional_inputs">additional_inputs</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-alwayslink">alwayslink</a>,
293297
<a href="#swift_common.create_linking_context_from_compilation_outputs-compilation_outputs">compilation_outputs</a>,
294-
<a href="#swift_common.create_linking_context_from_compilation_outputs-feature_configuration">feature_configuration</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-is_test">is_test</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-label">label</a>,
298+
<a href="#swift_common.create_linking_context_from_compilation_outputs-feature_configuration">feature_configuration</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-is_test">is_test</a>,
299+
<a href="#swift_common.create_linking_context_from_compilation_outputs-include_dev_srch_paths">include_dev_srch_paths</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-label">label</a>,
295300
<a href="#swift_common.create_linking_context_from_compilation_outputs-linking_contexts">linking_contexts</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-module_context">module_context</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-name">name</a>,
296301
<a href="#swift_common.create_linking_context_from_compilation_outputs-swift_toolchain">swift_toolchain</a>, <a href="#swift_common.create_linking_context_from_compilation_outputs-user_link_flags">user_link_flags</a>)
297302
</pre>
@@ -316,7 +321,8 @@ command line parameters file, those actions will be created here.
316321
| <a id="swift_common.create_linking_context_from_compilation_outputs-alwayslink"></a>alwayslink | If True, any binary that depends on the providers returned by this function will link in all of the library's object files, even if some contain no symbols referenced by the binary. | `False` |
317322
| <a id="swift_common.create_linking_context_from_compilation_outputs-compilation_outputs"></a>compilation_outputs | A `CcCompilationOutputs` value containing the object files to link. Typically, this is the second tuple element in the value returned by `swift_common.compile`. | none |
318323
| <a id="swift_common.create_linking_context_from_compilation_outputs-feature_configuration"></a>feature_configuration | A feature configuration obtained from `swift_common.configure_features`. | none |
319-
| <a id="swift_common.create_linking_context_from_compilation_outputs-is_test"></a>is_test | Represents if the `testonly` value of the context. | none |
324+
| <a id="swift_common.create_linking_context_from_compilation_outputs-is_test"></a>is_test | Deprecated. This argument will be removed in the next major release. Use the `include_dev_srch_paths` attribute instead. Represents if the `testonly` value of the context. | `None` |
325+
| <a id="swift_common.create_linking_context_from_compilation_outputs-include_dev_srch_paths"></a>include_dev_srch_paths | A `bool` that indicates whether the developer framework search paths will be added to the compilation command. | `None` |
320326
| <a id="swift_common.create_linking_context_from_compilation_outputs-label"></a>label | The `Label` of the target being built. This is used as the owner of the linker inputs created for post-compile actions (if any), and the label's name component also determines the name of the artifact unless it is overridden by the `name` argument. | none |
321327
| <a id="swift_common.create_linking_context_from_compilation_outputs-linking_contexts"></a>linking_contexts | A `list` of `CcLinkingContext`s containing libraries from dependencies. | `[]` |
322328
| <a id="swift_common.create_linking_context_from_compilation_outputs-module_context"></a>module_context | The module context returned by `swift_common.compile` containing information about the Swift module that was compiled. Typically, this is the first tuple element in the value returned by `swift_common.compile`. | none |

doc/rules.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -355,9 +355,9 @@ Allows for the use of Swift textual module interfaces and/or precompiled Swift m
355355
## swift_library
356356

357357
<pre>
358-
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-alwayslink">alwayslink</a>, <a href="#swift_library-copts">copts</a>, <a href="#swift_library-defines">defines</a>, <a href="#swift_library-generated_header_name">generated_header_name</a>,
359-
<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>, <a href="#swift_library-package_name">package_name</a>, <a href="#swift_library-plugins">plugins</a>,
360-
<a href="#swift_library-private_deps">private_deps</a>, <a href="#swift_library-swiftc_inputs">swiftc_inputs</a>)
358+
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>,
359+
<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>,
360+
<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>)
361361
</pre>
362362

363363
Compiles and links Swift code into a static library and Swift module.
@@ -371,6 +371,7 @@ Compiles and links Swift code into a static library and Swift module.
371371
| <a id="swift_library-deps"></a>deps | A list of targets that are dependencies of the target being built, which will be linked into that target.<br><br>If the Swift toolchain supports implementation-only imports (`private_deps` on `swift_library`), then targets in `deps` are treated as regular (non-implementation-only) imports that are propagated both to their direct and indirect (transitive) dependents.<br><br>Allowed kinds of dependencies are:<br><br>* `swift_c_module`, `swift_import` and `swift_library` (or anything propagating `SwiftInfo`)<br><br>* `cc_library` (or anything propagating `CcInfo`)<br><br>Additionally, on platforms that support Objective-C interop, `objc_library` targets (or anything propagating the `apple_common.Objc` provider) are allowed as dependencies. On platforms that do not support Objective-C interop (such as Linux), those dependencies will be **ignored.** | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
372372
| <a id="swift_library-srcs"></a>srcs | A list of `.swift` source files that will be compiled into the library. | <a href="https://bazel.build/concepts/labels">List of labels</a> | required | |
373373
| <a id="swift_library-data"></a>data | The list of files needed by this target at runtime.<br><br>Files and targets named in the `data` attribute will appear in the `*.runfiles` area of this target, if it has one. This may include data files needed by a binary or library, or other programs needed by it. | <a href="https://bazel.build/concepts/labels">List of labels</a> | optional | `[]` |
374+
| <a id="swift_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` |
374375
| <a id="swift_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` |
375376
| <a id="swift_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 | `[]` |
376377
| <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 | `[]` |
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
load("//swift:swift.bzl", "swift_library", "swift_library_group", "swift_test")
2+
3+
swift_library(
4+
name = "TestHelpers",
5+
srcs = ["TestHelper.swift"],
6+
always_include_developer_search_paths = True,
7+
module_name = "TestHelpers",
8+
tags = ["manual"],
9+
)
10+
11+
swift_library(
12+
name = "StringHelpers",
13+
srcs = ["String+RandomExtensions.swift"],
14+
module_name = "StringHelpers",
15+
tags = ["manual"],
16+
visibility = ["//:__subpackages__"],
17+
)
18+
19+
swift_library_group(
20+
name = "Helpers",
21+
tags = ["manual"],
22+
deps = [
23+
":StringHelpers",
24+
":TestHelpers",
25+
],
26+
)
27+
28+
swift_test(
29+
name = "DemoHelpersTest",
30+
srcs = [
31+
"DemoTestHelperTest.swift",
32+
"String+RandomExtensionsTests.swift",
33+
"main.swift",
34+
],
35+
deps = [":Helpers"],
36+
)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
@testable import TestHelpers
2+
import XCTest
3+
4+
class DemoTestHelperTest: XCTestCase {
5+
func test_assertThat_isEqualTo() {
6+
// To demonstrate a failure, change the expected value to "goodbye".
7+
assertThat("hello").isEqualTo("hello")
8+
}
9+
10+
static var allTests = [
11+
("test_assertThat_isEqualTo", test_assertThat_isEqualTo),
12+
]
13+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Demonstrate `swift_library_group` and `always_include_developer_search_paths`
2+
3+
This example demonstrates the use of the `swift_library_group` rule and the
4+
`always_include_developer_search_paths` attribute for `swift_library`.
5+
6+
## Scenario
7+
8+
The scenario is a developer provides a suite of utility modules that they would like to provide as a
9+
single Bazel dependency. One module, `TestHelpers`, provides a custom assertion syntax for `XCTest`
10+
tests. The other module, `StringHelpers`, provides functions for generating string values. They
11+
should be made available to clients using a single target, `Helpers`.
12+
13+
## Combine Swift modules using `swift_library`
14+
15+
To provide multiple Swift modules as a single target (i.e., this is anlagous to Swift Package
16+
Manager products), one can combine the Swift module Bazel targets using the `swift_library_group`
17+
rule. The Swift modules provided as `deps` to the `swift_library_group` are forwarded to any target
18+
that depends on it.
19+
20+
## Include developer search paths using `always_include_developer_search_paths`
21+
22+
The `TestHelpers` module can only provide the custom assertions if `XCTest` is available. The
23+
`XCTest` module is special in that it is only available if the developer search paths are visible
24+
during compilation. Historically, this would require that the `swift_library` be marked
25+
`testonly = True`. However, because the author wants to provide their utilities as a single
26+
dependency, marking `TestHelpers` as `testonly` would mandate that the `Helpers` target be marked as
27+
`testonly`. This would prevent non-test targets from using the `StringHelpers` module via the
28+
`Helpers` target.
29+
30+
The `always_include_developer_search_paths` attribute on the `swift_library` rule was introduced to
31+
allow the author of the library to dictate whether the developer search paths are added during
32+
compilation. By default, the attribute is `False`. An author must explicitly set
33+
`always_include_developer_search_paths = True` or `testonly = True` for the developer search paths
34+
to be availble during compilation.
35+
36+
### When should I use `testonly` vs `always_include_developer_search_paths`?
37+
38+
In short, prefer marking your target as `testonly`, if you import `XCTest`. The
39+
`always_include_developer_search_paths` attribute was added to support Swift packages
40+
that provide test and non-test dependencies in a single Swift product or Swift target. Marking the
41+
corresponding Bazel targets `testonly` makes them unusable in non-test scenarios.
42+
43+
### Should I combine test and non-test Swift modules?
44+
45+
Typically, no. Prefer keeping test-specific code separate from non-test code. As The Offspring told
46+
us ["You gotta keep 'em separated"](https://youtu.be/1jOk8dk-qaU?si=G73P7X7hf6HDluVG).

0 commit comments

Comments
 (0)