Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 86 additions & 18 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,53 @@ let git = Context.gitInformation
/// distribution as a package dependency.
let buildingForDevelopment = (git?.currentTag == nil)

/// Whether or not this package is being built for Embedded Swift.
///
/// This value is `true` if `SWT_EMBEDDED` is set in the environment to `true`
/// when `swift build` is invoked. This inference is experimental and is subject
/// to change in the future.
///
/// - Bug: There is currently no way for us to tell if we are being asked to
/// build for an Embedded Swift target at the package manifest level.
/// ([swift-syntax-#8431](https://github.com/swiftlang/swift-package-manager/issues/8431))
let buildingForEmbedded: Bool = {
guard let envvar = Context.environment["SWT_EMBEDDED"] else {
return false
}
let result = Bool(envvar) ?? ((Int(envvar) ?? 0) != 0)
if result {
print("Building for Embedded Swift...")
}
return result
}()

let package = Package(
name: "swift-testing",

platforms: [
.macOS(.v10_15),
.iOS(.v13),
.watchOS(.v6),
.tvOS(.v13),
.macCatalyst(.v13),
.visionOS(.v1),
],
platforms: {
if !buildingForEmbedded {
[
.macOS(.v10_15),
.iOS(.v13),
.watchOS(.v6),
.tvOS(.v13),
.macCatalyst(.v13),
.visionOS(.v1),
]
} else {
// Open-source main-branch toolchains (currently required to build this
// package for Embedded Swift) have higher Apple platform deployment
// targets than we would otherwise require.
[
.macOS(.v14),
.iOS(.v18),
.watchOS(.v10),
.tvOS(.v18),
.macCatalyst(.v18),
.visionOS(.v1),
]
}
}(),

products: {
var result = [Product]()
Expand Down Expand Up @@ -185,6 +221,31 @@ package.targets.append(contentsOf: [
])
#endif

extension BuildSettingCondition {
/// Creates a build setting condition that evaluates to `true` for Embedded
/// Swift.
///
/// - Parameters:
/// - nonEmbeddedCondition: The value to return if the target is not
/// Embedded Swift. If `nil`, the build condition evaluates to `false`.
///
/// - Returns: A build setting condition that evaluates to `true` for Embedded
/// Swift or is equal to `nonEmbeddedCondition` for non-Embedded Swift.
static func whenEmbedded(or nonEmbeddedCondition: @autoclosure () -> Self? = nil) -> Self? {
if !buildingForEmbedded {
if let nonEmbeddedCondition = nonEmbeddedCondition() {
nonEmbeddedCondition
} else {
// The caller did not supply a fallback.
.when(platforms: [])
}
} else {
// Enable unconditionally because the target is Embedded Swift.
nil
}
}
}

extension Array where Element == PackageDescription.SwiftSetting {
/// Settings intended to be applied to every Swift target in this package.
/// Analogous to project-level build settings in an Xcode project.
Expand All @@ -197,6 +258,7 @@ extension Array where Element == PackageDescription.SwiftSetting {

result += [
.enableUpcomingFeature("ExistentialAny"),
.enableExperimentalFeature("Embedded", .whenEmbedded()),

.enableExperimentalFeature("AccessLevelOnImport"),
.enableUpcomingFeature("InternalImportsByDefault"),
Expand All @@ -214,11 +276,14 @@ extension Array where Element == PackageDescription.SwiftSetting {

.define("SWT_TARGET_OS_APPLE", .when(platforms: [.macOS, .iOS, .macCatalyst, .watchOS, .tvOS, .visionOS])),

.define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
.define("SWT_NO_PROCESS_SPAWNING", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
.define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android])),
.define("SWT_NO_DYNAMIC_LINKING", .when(platforms: [.wasi])),
.define("SWT_NO_PIPES", .when(platforms: [.wasi])),
.define("SWT_NO_EXIT_TESTS", .whenEmbedded(or: .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android]))),
.define("SWT_NO_PROCESS_SPAWNING", .whenEmbedded(or: .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android]))),
.define("SWT_NO_SNAPSHOT_TYPES", .whenEmbedded(or: .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android]))),
.define("SWT_NO_DYNAMIC_LINKING", .whenEmbedded(or: .when(platforms: [.wasi]))),
.define("SWT_NO_PIPES", .whenEmbedded(or: .when(platforms: [.wasi]))),

.define("SWT_NO_LEGACY_TEST_DISCOVERY", .whenEmbedded()),
.define("SWT_NO_LIBDISPATCH", .whenEmbedded()),
]

return result
Expand Down Expand Up @@ -271,11 +336,14 @@ extension Array where Element == PackageDescription.CXXSetting {
var result = Self()

result += [
.define("SWT_NO_EXIT_TESTS", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
.define("SWT_NO_PROCESS_SPAWNING", .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android])),
.define("SWT_NO_SNAPSHOT_TYPES", .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android])),
.define("SWT_NO_DYNAMIC_LINKING", .when(platforms: [.wasi])),
.define("SWT_NO_PIPES", .when(platforms: [.wasi])),
.define("SWT_NO_EXIT_TESTS", .whenEmbedded(or: .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android]))),
.define("SWT_NO_PROCESS_SPAWNING", .whenEmbedded(or: .when(platforms: [.iOS, .watchOS, .tvOS, .visionOS, .wasi, .android]))),
.define("SWT_NO_SNAPSHOT_TYPES", .whenEmbedded(or: .when(platforms: [.linux, .custom("freebsd"), .openbsd, .windows, .wasi, .android]))),
.define("SWT_NO_DYNAMIC_LINKING", .whenEmbedded(or: .when(platforms: [.wasi]))),
.define("SWT_NO_PIPES", .whenEmbedded(or: .when(platforms: [.wasi]))),

.define("SWT_NO_LEGACY_TEST_DISCOVERY", .whenEmbedded()),
.define("SWT_NO_LIBDISPATCH", .whenEmbedded()),
]

// Capture the testing library's version as a C++ string constant.
Expand Down
2 changes: 2 additions & 0 deletions Sources/_TestDiscovery/DiscoverableAsTestContent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,10 @@ public protocol DiscoverableAsTestContent: Sendable, ~Copyable {
#endif
}

#if !SWT_NO_LEGACY_TEST_DISCOVERY
extension DiscoverableAsTestContent where Self: ~Copyable {
public static var _testContentTypeNameHint: String {
"__🟡$"
}
}
#endif
10 changes: 5 additions & 5 deletions Sources/_TestDiscovery/SectionBounds.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,11 @@ extension SectionBounds.Kind {
/// The Mach-O segment and section name for this instance as a pair of
/// null-terminated UTF-8 C strings and pass them to a function.
///
/// The values of this property within this function are instances of
/// `StaticString` rather than `String` because the latter's inner storage is
/// sometimes Objective-C-backed and touching it here can cause a recursive
/// access to an internal libobjc lock, whereas `StaticString`'s internal
/// storage is immediately available.
/// The values of this property are instances of `StaticString` rather than
/// `String` because the latter's inner storage is sometimes backed by
/// Objective-C and touching it here can cause a recursive access to an
/// internal libobjc lock, whereas `StaticString`'s internal storage is
/// immediately available.
fileprivate var segmentAndSectionName: (segmentName: StaticString, sectionName: StaticString) {
switch self {
case .testContent:
Expand Down
4 changes: 3 additions & 1 deletion Sources/_TestDiscovery/TestContentKind.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,18 @@ extension TestContentKind: Equatable, Hashable {
}
}

#if !hasFeature(Embedded)
// MARK: - Codable

extension TestContentKind: Codable {}
#endif

// MARK: - ExpressibleByStringLiteral, ExpressibleByIntegerLiteral

extension TestContentKind: ExpressibleByStringLiteral, ExpressibleByIntegerLiteral {
@inlinable public init(stringLiteral stringValue: StaticString) {
precondition(stringValue.utf8CodeUnitCount == MemoryLayout<UInt32>.stride, #""\#(stringValue)".utf8CodeUnitCount = \#(stringValue.utf8CodeUnitCount), expected \#(MemoryLayout<UInt32>.stride)"#)
let rawValue = stringValue.withUTF8Buffer { stringValue in
precondition(stringValue.count == MemoryLayout<UInt32>.stride, #""\#(stringValue)".utf8CodeUnitCount = \#(stringValue.count), expected \#(MemoryLayout<UInt32>.stride)"#)
let bigEndian = UnsafeRawBufferPointer(stringValue).loadUnaligned(as: UInt32.self)
return UInt32(bigEndian: bigEndian)
}
Expand Down
53 changes: 39 additions & 14 deletions Sources/_TestDiscovery/TestContentRecord.swift
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,34 @@ public struct TestContentRecord<T> where T: DiscoverableAsTestContent & ~Copyabl
/// The type of the `hint` argument to ``load(withHint:)``.
public typealias Hint = T.TestContentAccessorHint

/// Invoke an accessor function to load a test content record.
///
/// - Parameters:
/// - accessor: The accessor function to call.
/// - typeAddress: A pointer to the type of test content record.
/// - hint: An optional hint value.
///
/// - Returns: An instance of the test content type `T`, or `nil` if the
/// underlying test content record did not match `hint` or otherwise did not
/// produce a value.
///
/// Do not call this function directly. Instead, call ``load(withHint:)``.
private static func _load(using accessor: _TestContentRecordAccessor, withTypeAt typeAddress: UnsafeRawPointer, withHint hint: Hint? = nil) -> T? {
withUnsafeTemporaryAllocation(of: T.self, capacity: 1) { buffer in
let initialized = if let hint {
withUnsafePointer(to: hint) { hint in
accessor(buffer.baseAddress!, typeAddress, hint, 0)
}
} else {
accessor(buffer.baseAddress!, typeAddress, nil, 0)
}
guard initialized else {
return nil
}
return buffer.baseAddress!.move()
}
}

/// Load the value represented by this record.
///
/// - Parameters:
Expand All @@ -157,21 +185,14 @@ public struct TestContentRecord<T> where T: DiscoverableAsTestContent & ~Copyabl
return nil
}

return withUnsafePointer(to: T.self) { type in
withUnsafeTemporaryAllocation(of: T.self, capacity: 1) { buffer in
let initialized = if let hint {
withUnsafePointer(to: hint) { hint in
accessor(buffer.baseAddress!, type, hint, 0)
}
} else {
accessor(buffer.baseAddress!, type, nil, 0)
}
guard initialized else {
return nil
}
return buffer.baseAddress!.move()
}
#if !hasFeature(Embedded)
return withUnsafePointer(to: T.self) { typeAddress in
Self._load(using: accessor, withTypeAt: typeAddress, withHint: hint)
}
#else
let typeAddress = UnsafeRawPointer(bitPattern: UInt(T.testContentKind.rawValue)).unsafelyUnwrapped
return Self._load(using: accessor, withTypeAt: typeAddress, withHint: hint)
#endif
}
}

Expand All @@ -188,7 +209,11 @@ extension TestContentRecord: Sendable where Context: Sendable {}

extension TestContentRecord: CustomStringConvertible {
public var description: String {
#if !hasFeature(Embedded)
let typeName = String(describing: Self.self)
#else
let typeName = "TestContentRecord"
#endif
switch _recordStorage {
case let .atAddress(recordAddress):
let recordAddress = imageAddress.map { imageAddress in
Expand Down