Skip to content

Commit 9f06c93

Browse files
committed
Hide the feature behind a package trait (which is on by default in untagged package builds)
1 parent 514ed01 commit 9f06c93

9 files changed

+479
-34
lines changed

Diff for: Package.swift

+8
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,14 @@ extension Array where Element == PackageDescription.SwiftSetting {
221221
.define("SWT_NO_PIPES", .when(platforms: [.wasi])),
222222
]
223223

224+
// Unconditionally enable 'ExperimentalExitTestValueCapture' when building
225+
// for development.
226+
if buildingForDevelopment {
227+
result += [
228+
.define("ExperimentalExitTestValueCapture")
229+
]
230+
}
231+
224232
return result
225233
}
226234

Diff for: [email protected]

+310
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,310 @@
1+
// swift-tools-version: 6.1
2+
3+
//
4+
// This source file is part of the Swift.org open source project
5+
//
6+
// Copyright (c) 2023 Apple Inc. and the Swift project authors
7+
// Licensed under Apache License v2.0 with Runtime Library Exception
8+
//
9+
// See https://swift.org/LICENSE.txt for license information
10+
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
11+
//
12+
13+
import PackageDescription
14+
import CompilerPluginSupport
15+
16+
/// Information about the current state of the package's git repository.
17+
let git = Context.gitInformation
18+
19+
/// Whether or not this package is being built for development rather than
20+
/// distribution as a package dependency.
21+
let buildingForDevelopment = (git?.currentTag == nil)
22+
23+
let package = Package(
24+
name: "swift-testing",
25+
26+
platforms: [
27+
.macOS(.v10_15),
28+
.iOS(.v13),
29+
.watchOS(.v6),
30+
.tvOS(.v13),
31+
.macCatalyst(.v13),
32+
.visionOS(.v1),
33+
],
34+
35+
products: {
36+
var result = [Product]()
37+
38+
#if os(Windows)
39+
result.append(
40+
.library(
41+
name: "Testing",
42+
type: .dynamic, // needed so Windows exports ABI entry point symbols
43+
targets: ["Testing"]
44+
)
45+
)
46+
#else
47+
result.append(
48+
.library(
49+
name: "Testing",
50+
targets: ["Testing"]
51+
)
52+
)
53+
#endif
54+
55+
result.append(
56+
.library(
57+
name: "_TestDiscovery",
58+
type: .static,
59+
targets: ["_TestDiscovery"]
60+
)
61+
)
62+
63+
return result
64+
}(),
65+
66+
traits: [
67+
.trait(
68+
name: "ExperimentalExitTestValueCapture",
69+
description: "Enable experimental support for capturing values in exit tests"
70+
),
71+
],
72+
73+
dependencies: [
74+
.package(url: "https://github.com/swiftlang/swift-syntax.git", from: "601.0.0-latest"),
75+
],
76+
77+
targets: [
78+
.target(
79+
name: "Testing",
80+
dependencies: [
81+
"_TestDiscovery",
82+
"_TestingInternals",
83+
"TestingMacros",
84+
],
85+
exclude: ["CMakeLists.txt", "Testing.swiftcrossimport"],
86+
cxxSettings: .packageSettings,
87+
swiftSettings: .packageSettings + .enableLibraryEvolution(),
88+
linkerSettings: [
89+
.linkedLibrary("execinfo", .when(platforms: [.custom("freebsd"), .openbsd]))
90+
]
91+
),
92+
.testTarget(
93+
name: "TestingTests",
94+
dependencies: [
95+
"Testing",
96+
"_Testing_CoreGraphics",
97+
"_Testing_Foundation",
98+
],
99+
swiftSettings: .packageSettings
100+
),
101+
102+
.macro(
103+
name: "TestingMacros",
104+
dependencies: [
105+
.product(name: "SwiftDiagnostics", package: "swift-syntax"),
106+
.product(name: "SwiftSyntax", package: "swift-syntax"),
107+
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
108+
.product(name: "SwiftParser", package: "swift-syntax"),
109+
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
110+
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
111+
],
112+
exclude: ["CMakeLists.txt"],
113+
swiftSettings: .packageSettings + {
114+
var result = [PackageDescription.SwiftSetting]()
115+
116+
// The only target which needs the ability to import this macro
117+
// implementation target's module is its unit test target. Users of the
118+
// macros this target implements use them via their declarations in the
119+
// Testing module. This target's module is never distributed to users,
120+
// but as an additional guard against accidental misuse, this specifies
121+
// the unit test target as the only allowable client.
122+
if buildingForDevelopment {
123+
result.append(.unsafeFlags(["-Xfrontend", "-allowable-client", "-Xfrontend", "TestingMacrosTests"]))
124+
}
125+
126+
return result
127+
}()
128+
),
129+
130+
// "Support" targets: These targets are not meant to be used directly by
131+
// test authors.
132+
.target(
133+
name: "_TestingInternals",
134+
exclude: ["CMakeLists.txt"],
135+
cxxSettings: .packageSettings
136+
),
137+
.target(
138+
name: "_TestDiscovery",
139+
dependencies: ["_TestingInternals",],
140+
exclude: ["CMakeLists.txt"],
141+
cxxSettings: .packageSettings,
142+
swiftSettings: .packageSettings
143+
),
144+
145+
// Cross-import overlays (not supported by Swift Package Manager)
146+
.target(
147+
name: "_Testing_CoreGraphics",
148+
dependencies: [
149+
"Testing",
150+
],
151+
path: "Sources/Overlays/_Testing_CoreGraphics",
152+
swiftSettings: .packageSettings + .enableLibraryEvolution()
153+
),
154+
.target(
155+
name: "_Testing_Foundation",
156+
dependencies: [
157+
"Testing",
158+
],
159+
path: "Sources/Overlays/_Testing_Foundation",
160+
exclude: ["CMakeLists.txt"],
161+
// The Foundation module only has Library Evolution enabled on Apple
162+
// platforms, and since this target's module publicly imports Foundation,
163+
// it can only enable Library Evolution itself on those platforms.
164+
swiftSettings: .packageSettings + .enableLibraryEvolution(applePlatformsOnly: true)
165+
),
166+
167+
// Utility targets: These are utilities intended for use when developing
168+
// this package, not for distribution.
169+
.executableTarget(
170+
name: "SymbolShowcase",
171+
dependencies: [
172+
"Testing",
173+
],
174+
swiftSettings: .packageSettings
175+
),
176+
],
177+
178+
cxxLanguageStandard: .cxx20
179+
)
180+
181+
// BUG: swift-package-manager-#6367
182+
#if !os(Windows) && !os(FreeBSD) && !os(OpenBSD)
183+
package.targets.append(contentsOf: [
184+
.testTarget(
185+
name: "TestingMacrosTests",
186+
dependencies: [
187+
"Testing",
188+
"TestingMacros",
189+
],
190+
swiftSettings: .packageSettings
191+
)
192+
])
193+
#endif
194+
195+
extension Array where Element == PackageDescription.SwiftSetting {
196+
/// Settings intended to be applied to every Swift target in this package.
197+
/// Analogous to project-level build settings in an Xcode project.
198+
static var packageSettings: Self {
199+
var result = availabilityMacroSettings
200+
201+
if buildingForDevelopment {
202+
result.append(.unsafeFlags(["-require-explicit-sendable"]))
203+
}
204+
205+
result += [
206+
.enableUpcomingFeature("ExistentialAny"),
207+
208+
.enableExperimentalFeature("AccessLevelOnImport"),
209+
.enableUpcomingFeature("InternalImportsByDefault"),
210+
211+
.enableUpcomingFeature("MemberImportVisibility"),
212+
213+
// This setting is enabled in the package, but not in the toolchain build
214+
// (via CMake). Enabling it is dependent on acceptance of the @section
215+
// proposal via Swift Evolution.
216+
.enableExperimentalFeature("SymbolLinkageMarkers"),
217+
218+
// When building as a package, the macro plugin always builds as an
219+
// executable rather than a library.
220+
.define("SWT_NO_LIBRARY_MACRO_PLUGINS"),
221+
222+
.define("SWT_TARGET_OS_APPLE", .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])),
223+
224+
.define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
225+
.define("SWT_NO_PROCESS_SPAWNING", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
226+
.define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android])),
227+
.define("SWT_NO_DYNAMIC_LINKING", .when(platforms: [.wasi])),
228+
.define("SWT_NO_PIPES", .when(platforms: [.wasi])),
229+
]
230+
231+
// Unconditionally enable 'ExperimentalExitTestValueCapture' when building
232+
// for development.
233+
if buildingForDevelopment {
234+
result += [
235+
.define("ExperimentalExitTestValueCapture")
236+
]
237+
}
238+
239+
return result
240+
}
241+
242+
/// Settings which define commonly-used OS availability macros.
243+
///
244+
/// These leverage a pseudo-experimental feature in the Swift compiler for
245+
/// setting availability definitions, which was added in
246+
/// [swift#65218](https://github.com/swiftlang/swift/pull/65218).
247+
private static var availabilityMacroSettings: Self {
248+
[
249+
.enableExperimentalFeature("AvailabilityMacro=_mangledTypeNameAPI:macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0"),
250+
.enableExperimentalFeature("AvailabilityMacro=_uttypesAPI:macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0"),
251+
.enableExperimentalFeature("AvailabilityMacro=_backtraceAsyncAPI:macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0"),
252+
.enableExperimentalFeature("AvailabilityMacro=_clockAPI:macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0"),
253+
.enableExperimentalFeature("AvailabilityMacro=_regexAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0"),
254+
.enableExperimentalFeature("AvailabilityMacro=_swiftVersionAPI:macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0"),
255+
.enableExperimentalFeature("AvailabilityMacro=_typedThrowsAPI:macOS 15.0, iOS 18.0, watchOS 11.0, tvOS 18.0, visionOS 2.0"),
256+
257+
.enableExperimentalFeature("AvailabilityMacro=_distantFuture:macOS 99.0, iOS 99.0, watchOS 99.0, tvOS 99.0, visionOS 99.0"),
258+
]
259+
}
260+
261+
/// Create a Swift setting which enables Library Evolution, optionally
262+
/// constraining it to only Apple platforms.
263+
///
264+
/// - Parameters:
265+
/// - applePlatformsOnly: Whether to constrain this setting to only Apple
266+
/// platforms.
267+
static func enableLibraryEvolution(applePlatformsOnly: Bool = false) -> Self {
268+
var result = [PackageDescription.SwiftSetting]()
269+
270+
if buildingForDevelopment {
271+
var condition: BuildSettingCondition?
272+
if applePlatformsOnly {
273+
condition = .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])
274+
}
275+
result.append(.unsafeFlags(["-enable-library-evolution"], condition))
276+
}
277+
278+
return result
279+
}
280+
}
281+
282+
extension Array where Element == PackageDescription.CXXSetting {
283+
/// Settings intended to be applied to every C++ target in this package.
284+
/// Analogous to project-level build settings in an Xcode project.
285+
static var packageSettings: Self {
286+
var result = Self()
287+
288+
result += [
289+
.define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
290+
.define("SWT_NO_PROCESS_SPAWNING", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
291+
.define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android])),
292+
.define("SWT_NO_DYNAMIC_LINKING", .when(platforms: [.wasi])),
293+
.define("SWT_NO_PIPES", .when(platforms: [.wasi])),
294+
]
295+
296+
// Capture the testing library's version as a C++ string constant.
297+
if let git {
298+
let testingLibraryVersion = if let tag = git.currentTag {
299+
tag
300+
} else if git.hasUncommittedChanges {
301+
"\(git.currentCommit) (modified)"
302+
} else {
303+
git.currentCommit
304+
}
305+
result.append(.define("SWT_TESTING_LIBRARY_VERSION", to: #""\#(testingLibraryVersion)""#))
306+
}
307+
308+
return result
309+
}
310+
}

Diff for: Sources/Testing/ExitTests/ExitTest.CapturedValue.swift

+11
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#if !SWT_NO_EXIT_TESTS
1212
@_spi(Experimental) @_spi(ForToolsIntegrationOnly)
1313
extension ExitTest {
14+
#if ExperimentalExitTestValueCapture
1415
/// A type representing a value captured by an exit test's body.
1516
///
1617
/// An instance of this type may represent the actual value that was captured
@@ -107,10 +108,15 @@ extension ExitTest {
107108
}
108109
}
109110
}
111+
#else
112+
/// A placeholder type for ``CapturedValue`` when value capturing is disabled.
113+
typealias CapturedValue = Never
114+
#endif
110115
}
111116

112117
// MARK: - Collection conveniences
113118

119+
#if ExperimentalExitTestValueCapture
114120
extension Array where Element == ExitTest.CapturedValue {
115121
init<each T>(_ wrappedValues: repeat each T) where repeat each T: Codable & Sendable {
116122
self.init()
@@ -122,6 +128,7 @@ extension Array where Element == ExitTest.CapturedValue {
122128
repeat self.append(ExitTest.CapturedValue(typeOnly: (each typesOfWrappedValues).self))
123129
}
124130
}
131+
#endif
125132

126133
extension Collection where Element == ExitTest.CapturedValue {
127134
/// Cast the elements in this collection to a tuple of their wrapped values.
@@ -139,6 +146,7 @@ extension Collection where Element == ExitTest.CapturedValue {
139146
as type: U.Type,
140147
from capturedValues: inout SubSequence
141148
) throws -> U {
149+
#if ExperimentalExitTestValueCapture
142150
// Get the next captured value in the collection. If we run out of values
143151
// before running out of parameter pack elements, then something in the
144152
// exit test handler or entry point is likely broken.
@@ -159,6 +167,9 @@ extension Collection where Element == ExitTest.CapturedValue {
159167
}
160168

161169
return wrappedValue
170+
#else
171+
fatalError("Unimplemented")
172+
#endif
162173
}
163174

164175
var capturedValues = self[...]

0 commit comments

Comments
 (0)