Skip to content

Commit ae7fe60

Browse files
authored
Merge pull request #1170 from stmontgomery/main-6.2-merge
Merge 'main' branch to 'release/6.2'
2 parents 610b2bc + a808fd6 commit ae7fe60

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+1211
-256
lines changed

Documentation/Porting.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,9 @@ platform-specific attention.
6767
> conflicting requirements (for example, attempting to enable support for pipes
6868
> without also enabling support for file I/O.) You should be able to resolve
6969
> these issues by updating `Package.swift` and/or `CompilerSettings.cmake`.
70+
>
71+
> Don't forget to add your platform to the `BuildSettingCondition/whenApple(_:)`
72+
> function in `Package.swift`.
7073
7174
Most platform dependencies can be resolved through the use of platform-specific
7275
API. For example, Swift Testing uses the C11 standard [`timespec`](https://en.cppreference.com/w/c/chrono/timespec)

Package.swift

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ let buildingForDevelopment = (git?.currentTag == nil)
2727
/// to change in the future.
2828
///
2929
/// - Bug: There is currently no way for us to tell if we are being asked to
30-
/// build for an Embedded Swift target at the package manifest level.
30+
/// build for an Embedded Swift target at the package manifest level.
3131
/// ([swift-syntax-#8431](https://github.com/swiftlang/swift-package-manager/issues/8431))
3232
let buildingForEmbedded: Bool = {
3333
guard let envvar = Context.environment["SWT_EMBEDDED"] else {
@@ -208,7 +208,7 @@ let package = Package(
208208
// The Foundation module only has Library Evolution enabled on Apple
209209
// platforms, and since this target's module publicly imports Foundation,
210210
// it can only enable Library Evolution itself on those platforms.
211-
swiftSettings: .packageSettings + .enableLibraryEvolution(applePlatformsOnly: true)
211+
swiftSettings: .packageSettings + .enableLibraryEvolution(.whenApple())
212212
),
213213

214214
// Utility targets: These are utilities intended for use when developing
@@ -244,11 +244,11 @@ extension BuildSettingCondition {
244244
/// Swift.
245245
///
246246
/// - Parameters:
247-
/// - nonEmbeddedCondition: The value to return if the target is not
248-
/// Embedded Swift. If `nil`, the build condition evaluates to `false`.
247+
/// - nonEmbeddedCondition: The value to return if the target is not
248+
/// Embedded Swift. If `nil`, the build condition evaluates to `false`.
249249
///
250250
/// - Returns: A build setting condition that evaluates to `true` for Embedded
251-
/// Swift or is equal to `nonEmbeddedCondition` for non-Embedded Swift.
251+
/// Swift or is equal to `nonEmbeddedCondition` for non-Embedded Swift.
252252
static func whenEmbedded(or nonEmbeddedCondition: @autoclosure () -> Self? = nil) -> Self? {
253253
if !buildingForEmbedded {
254254
if let nonEmbeddedCondition = nonEmbeddedCondition() {
@@ -263,6 +263,21 @@ extension BuildSettingCondition {
263263
nil
264264
}
265265
}
266+
267+
/// A build setting condition representing all Apple or non-Apple platforms.
268+
///
269+
/// - Parameters:
270+
/// - isApple: Whether or not the result represents Apple platforms.
271+
///
272+
/// - Returns: A build setting condition that evaluates to `isApple` for Apple
273+
/// platforms.
274+
static func whenApple(_ isApple: Bool = true) -> Self {
275+
if isApple {
276+
.when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])
277+
} else {
278+
.when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android])
279+
}
280+
}
266281
}
267282

268283
extension Array where Element == PackageDescription.SwiftSetting {
@@ -312,13 +327,14 @@ extension Array where Element == PackageDescription.SwiftSetting {
312327
// executable rather than a library.
313328
.define("SWT_NO_LIBRARY_MACRO_PLUGINS"),
314329

315-
.define("SWT_TARGET_OS_APPLE", .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])),
330+
.define("SWT_TARGET_OS_APPLE", .whenApple()),
316331

317332
.define("SWT_NO_EXIT_TESTS", .whenEmbedded(or: .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android]))),
318333
.define("SWT_NO_PROCESS_SPAWNING", .whenEmbedded(or: .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android]))),
319-
.define("SWT_NO_SNAPSHOT_TYPES", .whenEmbedded(or: .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android]))),
334+
.define("SWT_NO_SNAPSHOT_TYPES", .whenEmbedded(or: .whenApple(false))),
320335
.define("SWT_NO_DYNAMIC_LINKING", .whenEmbedded(or: .when(platforms: [.wasi]))),
321336
.define("SWT_NO_PIPES", .whenEmbedded(or: .when(platforms: [.wasi]))),
337+
.define("SWT_NO_FOUNDATION_FILE_COORDINATION", .whenEmbedded(or: .whenApple(false))),
322338

323339
.define("SWT_NO_LEGACY_TEST_DISCOVERY", .whenEmbedded()),
324340
.define("SWT_NO_LIBDISPATCH", .whenEmbedded()),
@@ -354,20 +370,16 @@ extension Array where Element == PackageDescription.SwiftSetting {
354370
]
355371
}
356372

357-
/// Create a Swift setting which enables Library Evolution, optionally
358-
/// constraining it to only Apple platforms.
373+
/// Create a Swift setting which enables Library Evolution.
359374
///
360375
/// - Parameters:
361-
/// - applePlatformsOnly: Whether to constrain this setting to only Apple
362-
/// platforms.
363-
static func enableLibraryEvolution(applePlatformsOnly: Bool = false) -> Self {
376+
/// - condition: A build setting condition to apply to this setting.
377+
///
378+
/// - Returns: A Swift setting that enables Library Evolution.
379+
static func enableLibraryEvolution(_ condition: BuildSettingCondition? = nil) -> Self {
364380
var result = [PackageDescription.SwiftSetting]()
365381

366382
if buildingForDevelopment {
367-
var condition: BuildSettingCondition?
368-
if applePlatformsOnly {
369-
condition = .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])
370-
}
371383
result.append(.unsafeFlags(["-enable-library-evolution"], condition))
372384
}
373385

@@ -410,9 +422,10 @@ extension Array where Element == PackageDescription.CXXSetting {
410422
result += [
411423
.define("SWT_NO_EXIT_TESTS", .whenEmbedded(or: .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android]))),
412424
.define("SWT_NO_PROCESS_SPAWNING", .whenEmbedded(or: .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android]))),
413-
.define("SWT_NO_SNAPSHOT_TYPES", .whenEmbedded(or: .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android]))),
425+
.define("SWT_NO_SNAPSHOT_TYPES", .whenEmbedded(or: .whenApple(false))),
414426
.define("SWT_NO_DYNAMIC_LINKING", .whenEmbedded(or: .when(platforms: [.wasi]))),
415427
.define("SWT_NO_PIPES", .whenEmbedded(or: .when(platforms: [.wasi]))),
428+
.define("SWT_NO_FOUNDATION_FILE_COORDINATION", .whenEmbedded(or: .whenApple(false))),
416429

417430
.define("SWT_NO_LEGACY_TEST_DISCOVERY", .whenEmbedded()),
418431
.define("SWT_NO_LIBDISPATCH", .whenEmbedded()),

Sources/Overlays/_Testing_Foundation/Attachments/Attachable+Encodable+NSSecureCoding.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public import Foundation
2020

2121
/// @Metadata {
2222
/// @Available(Swift, introduced: 6.2)
23+
/// @Available(Xcode, introduced: 26.0)
2324
/// }
2425
extension Attachable where Self: Encodable & NSSecureCoding {
2526
@_documentation(visibility: private)

Sources/Overlays/_Testing_Foundation/Attachments/Attachable+Encodable.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ func withUnsafeBytes<E, R>(encoding attachableValue: borrowing E, for attachment
5656

5757
/// @Metadata {
5858
/// @Available(Swift, introduced: 6.2)
59+
/// @Available(Xcode, introduced: 26.0)
5960
/// }
6061
extension Attachable where Self: Encodable {
6162
/// Encode this value into a buffer using either [`PropertyListEncoder`](https://developer.apple.com/documentation/foundation/propertylistencoder)
@@ -92,6 +93,7 @@ extension Attachable where Self: Encodable {
9293
///
9394
/// @Metadata {
9495
/// @Available(Swift, introduced: 6.2)
96+
/// @Available(Xcode, introduced: 26.0)
9597
/// }
9698
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
9799
try _Testing_Foundation.withUnsafeBytes(encoding: self, for: attachment, body)

Sources/Overlays/_Testing_Foundation/Attachments/Attachable+NSSecureCoding.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public import Foundation
1818

1919
/// @Metadata {
2020
/// @Available(Swift, introduced: 6.2)
21+
/// @Available(Xcode, introduced: 26.0)
2122
/// }
2223
extension Attachable where Self: NSSecureCoding {
2324
/// Encode this object using [`NSKeyedArchiver`](https://developer.apple.com/documentation/foundation/nskeyedarchiver)
@@ -52,6 +53,7 @@ extension Attachable where Self: NSSecureCoding {
5253
///
5354
/// @Metadata {
5455
/// @Available(Swift, introduced: 6.2)
56+
/// @Available(Xcode, introduced: 26.0)
5557
/// }
5658
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
5759
let format = try EncodingFormat(for: attachment)

Sources/Overlays/_Testing_Foundation/Attachments/Attachment+URL.swift

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ extension Attachment where AttachableValue == _AttachableURLWrapper {
5353
///
5454
/// @Metadata {
5555
/// @Available(Swift, introduced: 6.2)
56+
/// @Available(Xcode, introduced: 26.0)
5657
/// }
5758
public init(
5859
contentsOf url: URL,
@@ -70,7 +71,7 @@ extension Attachment where AttachableValue == _AttachableURLWrapper {
7071
let url = url.resolvingSymlinksInPath()
7172
let isDirectory = try url.resourceValues(forKeys: [.isDirectoryKey]).isDirectory!
7273

73-
#if SWT_TARGET_OS_APPLE
74+
#if SWT_TARGET_OS_APPLE && !SWT_NO_FOUNDATION_FILE_COORDINATION
7475
let data: Data = try await withCheckedThrowingContinuation { continuation in
7576
let fileCoordinator = NSFileCoordinator()
7677
let fileAccessIntent = NSFileAccessIntent.readingIntent(with: url, options: [.forUploading])
@@ -165,25 +166,31 @@ private func _compressContentsOfDirectory(at directoryURL: URL) async throws ->
165166
// knows how to write PKZIP archives, while Windows inherited FreeBSD's tar
166167
// tool in Windows 10 Build 17063 (per https://techcommunity.microsoft.com/blog/containers/tar-and-curl-come-to-windows/382409).
167168
//
168-
// On Linux (which does not have FreeBSD's version of tar(1)), we can use
169-
// zip(1) instead.
169+
// On Linux and OpenBSD (which do not have FreeBSD's version of tar(1)), we
170+
// can use zip(1) instead. This tool compresses paths relative to the current
171+
// working directory, and posix_spawn_file_actions_addchdir_np() is not always
172+
// available for us to call (not present on OpenBSD, requires glibc ≥ 2.28 on
173+
// Linux), so we'll spawn a shell that calls cd before calling zip(1).
170174
//
171175
// OpenBSD's tar(1) does not support writing PKZIP archives, and /usr/bin/zip
172176
// tool is an optional install, so we check if it's present before trying to
173177
// execute it.
178+
#if os(Linux) || os(OpenBSD)
179+
let archiverPath = "/bin/sh"
174180
#if os(Linux)
175-
let archiverPath = "/usr/bin/zip"
176-
#elseif SWT_TARGET_OS_APPLE || os(FreeBSD)
177-
let archiverPath = "/usr/bin/tar"
178-
#elseif os(OpenBSD)
179-
let archiverPath = "/usr/local/bin/zip"
181+
let trueArchiverPath = "/usr/bin/zip"
182+
#else
183+
let trueArchiverPath = "/usr/local/bin/zip"
180184
var isDirectory = false
181-
if !FileManager.default.fileExists(atPath: archiverPath, isDirectory: &isDirectory) || isDirectory {
185+
if !FileManager.default.fileExists(atPath: trueArchiverPath, isDirectory: &isDirectory) || isDirectory {
182186
throw CocoaError(.fileNoSuchFile, userInfo: [
183187
NSLocalizedDescriptionKey: "The 'zip' package is not installed.",
184-
NSFilePathErrorKey: archiverPath
188+
NSFilePathErrorKey: trueArchiverPath
185189
])
186190
}
191+
#endif
192+
#elseif SWT_TARGET_OS_APPLE || os(FreeBSD)
193+
let archiverPath = "/usr/bin/tar"
187194
#elseif os(Windows)
188195
guard let archiverPath = _archiverPath else {
189196
throw CocoaError(.fileWriteUnknown, userInfo: [
@@ -196,20 +203,15 @@ private func _compressContentsOfDirectory(at directoryURL: URL) async throws ->
196203
throw CocoaError(.featureUnsupported, userInfo: [NSLocalizedDescriptionKey: "This platform does not support attaching directories to tests."])
197204
#endif
198205

199-
try await withCheckedThrowingContinuation { continuation in
200-
let process = Process()
201-
202-
process.executableURL = URL(fileURLWithPath: archiverPath, isDirectory: false)
203-
204-
let sourcePath = directoryURL.fileSystemPath
205-
let destinationPath = temporaryURL.fileSystemPath
206+
let sourcePath = directoryURL.fileSystemPath
207+
let destinationPath = temporaryURL.fileSystemPath
208+
let arguments = {
206209
#if os(Linux) || os(OpenBSD)
207210
// The zip command constructs relative paths from the current working
208211
// directory rather than from command-line arguments.
209-
process.arguments = [destinationPath, "--recurse-paths", "."]
210-
process.currentDirectoryURL = directoryURL
212+
["-c", #"cd "$0" && "$1" "$2" --recurse-paths ."#, sourcePath, trueArchiverPath, destinationPath]
211213
#elseif SWT_TARGET_OS_APPLE || os(FreeBSD)
212-
process.arguments = ["--create", "--auto-compress", "--directory", sourcePath, "--file", destinationPath, "."]
214+
["--create", "--auto-compress", "--directory", sourcePath, "--file", destinationPath, "."]
213215
#elseif os(Windows)
214216
// The Windows version of bsdtar can handle relative paths for other archive
215217
// formats, but produces empty archives when inferring the zip format with
@@ -218,30 +220,15 @@ private func _compressContentsOfDirectory(at directoryURL: URL) async throws ->
218220
// An alternative may be to use PowerShell's Compress-Archive command,
219221
// however that comes with a security risk as we'd be responsible for two
220222
// levels of command-line argument escaping.
221-
process.arguments = ["--create", "--auto-compress", "--file", destinationPath, sourcePath]
223+
["--create", "--auto-compress", "--file", destinationPath, sourcePath]
222224
#endif
225+
}()
223226

224-
process.standardOutput = nil
225-
process.standardError = nil
226-
227-
process.terminationHandler = { process in
228-
let terminationReason = process.terminationReason
229-
let terminationStatus = process.terminationStatus
230-
if terminationReason == .exit && terminationStatus == EXIT_SUCCESS {
231-
continuation.resume()
232-
} else {
233-
let error = CocoaError(.fileWriteUnknown, userInfo: [
234-
NSLocalizedDescriptionKey: "The directory at '\(sourcePath)' could not be compressed (\(terminationStatus)).",
235-
])
236-
continuation.resume(throwing: error)
237-
}
238-
}
239-
240-
do {
241-
try process.run()
242-
} catch {
243-
continuation.resume(throwing: error)
244-
}
227+
let exitStatus = try await spawnExecutableAtPathAndWait(archiverPath, arguments: arguments)
228+
guard case .exitCode(EXIT_SUCCESS) = exitStatus else {
229+
throw CocoaError(.fileWriteUnknown, userInfo: [
230+
NSLocalizedDescriptionKey: "The directory at '\(sourcePath)' could not be compressed (\(exitStatus)).",
231+
])
245232
}
246233

247234
return try Data(contentsOf: temporaryURL, options: [.mappedIfSafe])

Sources/Overlays/_Testing_Foundation/Attachments/Data+Attachable.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,12 @@ public import Foundation
1414

1515
/// @Metadata {
1616
/// @Available(Swift, introduced: 6.2)
17+
/// @Available(Xcode, introduced: 26.0)
1718
/// }
1819
extension Data: Attachable {
1920
/// @Metadata {
2021
/// @Available(Swift, introduced: 6.2)
22+
/// @Available(Xcode, introduced: 26.0)
2123
/// }
2224
public func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R {
2325
try withUnsafeBytes(body)

Sources/Testing/Attachments/Attachable.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
///
3030
/// @Metadata {
3131
/// @Available(Swift, introduced: 6.2)
32+
/// @Available(Xcode, introduced: 26.0)
3233
/// }
3334
public protocol Attachable: ~Copyable {
3435
/// An estimate of the number of bytes of memory needed to store this value as
@@ -48,6 +49,7 @@ public protocol Attachable: ~Copyable {
4849
///
4950
/// @Metadata {
5051
/// @Available(Swift, introduced: 6.2)
52+
/// @Available(Xcode, introduced: 26.0)
5153
/// }
5254
var estimatedAttachmentByteCount: Int? { get }
5355

@@ -74,6 +76,7 @@ public protocol Attachable: ~Copyable {
7476
///
7577
/// @Metadata {
7678
/// @Available(Swift, introduced: 6.2)
79+
/// @Available(Xcode, introduced: 26.0)
7780
/// }
7881
borrowing func withUnsafeBytes<R>(for attachment: borrowing Attachment<Self>, _ body: (UnsafeRawBufferPointer) throws -> R) throws -> R
7982

@@ -94,6 +97,7 @@ public protocol Attachable: ~Copyable {
9497
///
9598
/// @Metadata {
9699
/// @Available(Swift, introduced: 6.2)
100+
/// @Available(Xcode, introduced: 26.0)
97101
/// }
98102
borrowing func preferredName(for attachment: borrowing Attachment<Self>, basedOn suggestedName: String) -> String
99103
}

Sources/Testing/Attachments/AttachableWrapper.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,22 @@
2424
///
2525
/// @Metadata {
2626
/// @Available(Swift, introduced: 6.2)
27+
/// @Available(Xcode, introduced: 26.0)
2728
/// }
2829
public protocol AttachableWrapper<Wrapped>: Attachable, ~Copyable {
2930
/// The type of the underlying value represented by this type.
3031
///
3132
/// @Metadata {
3233
/// @Available(Swift, introduced: 6.2)
34+
/// @Available(Xcode, introduced: 26.0)
3335
/// }
3436
associatedtype Wrapped
3537

3638
/// The underlying value represented by this instance.
3739
///
3840
/// @Metadata {
3941
/// @Available(Swift, introduced: 6.2)
42+
/// @Available(Xcode, introduced: 26.0)
4043
/// }
4144
var wrappedValue: Wrapped { get }
4245
}

0 commit comments

Comments
 (0)