Skip to content

Commit 67e11d5

Browse files
authored
Various VersionNumber changes of merit. (#1246)
This PR does things to `ABI.VersionNumber`: - Renames it to `VersionNumber` as we do have some use cases that aren't related to JSON schema versioning. I initially didn't want to make this type a general version number type, but it's just too useful not to do so. Alas. - Changes the type of the `swiftStandardLibraryVersion` global variable to `VersionNumber?`. - Changes the type of the `glibcVersion` global variable to `VersionNumber`. - Adds `swiftCompilerVersion` representing the version of the Swift compiler used to compile Swift Testing. We need this value when computing the JSON schema version (see next bullet.) - Clamps the range of supported JSON schema versions to the Swift compiler version _unless_ we've explicitly defined a schema version higher than it: | Compiler | Highest Defined Schema | Requested | Result | |-|-|-|-| | 1.0 | 1.0 | 1.0 | 1.0 | | 2.0 | 1.0 | 1.0 | 1.0 | | 1.0 | 2.0 | 1.0 | 1.0 | | 1.0 | 1.0 | 2.0 | `nil` | | 2.0 | 2.0 | 1.0 | 1.0 | | 2.0 | 1.0 | 2.0 | 1.0 | | 1.0 | 2.0 | 2.0 | 2.0 | | 2.0 | 2.0 | 2.0 | 2.0 | The reasoning here is that, when we're built with a given compiler version, we presumably know about all JSON schema versions up to and including the one aligned with that compiler, so if you ask for the schema version aligned with the compiler, it's equivalent to whatever we support that's less than or equal to the compiler version. But if you ask for something greater than the compiler version, and we haven't defined it, we don't know anything about it and can't provide it. This reasoning breaks down somewhat if you build an old version of the Swift Testing package with a new compiler, but in general we don't support that sort of configuration for very long (and we can't predict the future anyway.) ### Checklist: - [x] Code and documentation should follow the style of the [Style Guide](https://github.com/apple/swift-testing/blob/main/Documentation/StyleGuide.md). - [x] If public symbols are renamed or modified, DocC references should be updated.
1 parent 54f919e commit 67e11d5

File tree

12 files changed

+153
-67
lines changed

12 files changed

+153
-67
lines changed

Sources/Testing/ABI/ABI.Record.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ extension ABI.Record: Codable {
6666
init(from decoder: any Decoder) throws {
6767
let container = try decoder.container(keyedBy: CodingKeys.self)
6868

69-
let versionNumber = try container.decode(ABI.VersionNumber.self, forKey: .version)
69+
let versionNumber = try container.decode(VersionNumber.self, forKey: .version)
7070
if versionNumber != V.versionNumber {
7171
throw DecodingError.dataCorrupted(
7272
DecodingError.Context(

Sources/Testing/ABI/ABI.swift

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ extension ABI {
4545
/// The current supported ABI version (ignoring any experimental versions.)
4646
typealias CurrentVersion = v0
4747

48+
/// The highest supported ABI version (including any experimental versions.)
49+
typealias HighestVersion = v6_3
50+
4851
#if !hasFeature(Embedded)
4952
/// Get the type representing a given ABI version.
5053
///
@@ -55,7 +58,24 @@ extension ABI {
5558
/// - Returns: A type conforming to ``ABI/Version`` that represents the given
5659
/// ABI version, or `nil` if no such type exists.
5760
static func version(forVersionNumber versionNumber: VersionNumber = ABI.CurrentVersion.versionNumber) -> (any Version.Type)? {
58-
switch versionNumber {
61+
if versionNumber > ABI.HighestVersion.versionNumber {
62+
// If the caller requested an ABI version higher than the current Swift
63+
// compiler version and it's not an ABI version we've explicitly defined,
64+
// then we assume we don't know what they're talking about and return nil.
65+
//
66+
// Note that it is possible for the Swift compiler version to be lower
67+
// than the highest defined ABI version (e.g. if you use a 6.2 toolchain
68+
// to build this package's release/6.3 branch with a 6.3 ABI defined.)
69+
//
70+
// Note also that building an old version of Swift Testing with a newer
71+
// compiler may produce incorrect results here. We don't generally support
72+
// that configuration though.
73+
if versionNumber > swiftCompilerVersion {
74+
return nil
75+
}
76+
}
77+
78+
return switch versionNumber {
5979
case ABI.v6_3.versionNumber...:
6080
ABI.v6_3.self
6181
case ABI.v0.versionNumber...:

Sources/Testing/ABI/Encoded/ABI.EncodedTest.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ extension ABI {
7878
/// - Warning: Tags are not yet part of the JSON schema.
7979
///
8080
/// @Metadata {
81-
/// @Available("Swift Testing ABI", introduced: 1)
81+
/// @Available("Swift Testing ABI", introduced: 6.3)
8282
/// }
8383
var _tags: [String]?
8484

Sources/Testing/ABI/EntryPoints/EntryPoint.swift

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public struct __CommandLineArguments_v0: Sendable {
262262
/// This property is internal because its type is internal. External users of
263263
/// this structure can use the ``eventStreamSchemaVersion`` property to get or
264264
/// set the value of this property.
265-
var eventStreamVersionNumber: ABI.VersionNumber?
265+
var eventStreamVersionNumber: VersionNumber?
266266

267267
/// The value of the `--event-stream-version` or `--experimental-event-stream-version`
268268
/// argument, representing the version of the event stream schema to use when
@@ -282,7 +282,7 @@ public struct __CommandLineArguments_v0: Sendable {
282282
}
283283
set {
284284
eventStreamVersionNumber = newValue.flatMap { newValue in
285-
guard let newValue = ABI.VersionNumber(newValue) else {
285+
guard let newValue = VersionNumber(newValue) else {
286286
preconditionFailure("Invalid event stream version number '\(newValue)'. Specify a version number of the form 'major.minor.patch'.")
287287
}
288288
return newValue
@@ -404,7 +404,7 @@ func parseCommandLineArguments(from args: [String]) throws -> __CommandLineArgum
404404

405405
// If the caller specified a version that could not be parsed, treat it as
406406
// an invalid argument.
407-
guard let eventStreamVersion = ABI.VersionNumber(versionString) else {
407+
guard let eventStreamVersion = VersionNumber(versionString) else {
408408
let argument = allowExperimental ? "--experimental-event-stream-version" : "--event-stream-version"
409409
throw _EntryPointError.invalidArgument(argument, value: versionString)
410410
}
@@ -652,7 +652,7 @@ public func configurationForEntryPoint(from args: __CommandLineArguments_v0) thr
652652
///
653653
/// - Throws: If `version` is not a supported ABI version.
654654
func eventHandlerForStreamingEvents(
655-
withVersionNumber versionNumber: ABI.VersionNumber?,
655+
withVersionNumber versionNumber: VersionNumber?,
656656
encodeAsJSONLines: Bool,
657657
forwardingTo targetEventHandler: @escaping @Sendable (UnsafeRawBufferPointer) -> Void
658658
) throws -> Event.Handler {
@@ -822,7 +822,7 @@ private enum _EntryPointError: Error {
822822
///
823823
/// - Parameters:
824824
/// - versionNumber: The experimental ABI version number.
825-
case experimentalABIVersion(_ versionNumber: ABI.VersionNumber)
825+
case experimentalABIVersion(_ versionNumber: VersionNumber)
826826
}
827827

828828
extension _EntryPointError: CustomStringConvertible {
@@ -847,7 +847,7 @@ extension __CommandLineArguments_v0 {
847847
eventStreamVersionNumber.map(\.majorComponent).map(Int.init)
848848
}
849849
set {
850-
eventStreamVersionNumber = newValue.map { ABI.VersionNumber(majorComponent: Int8(clamping: $0), minorComponent: 0) }
850+
eventStreamVersionNumber = newValue.map { VersionNumber(majorComponent: .init(clamping: $0), minorComponent: 0) }
851851
}
852852
}
853853
}

Sources/Testing/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ add_library(Testing
1313
ABI/ABI.Record.swift
1414
ABI/ABI.Record+Streaming.swift
1515
ABI/ABI.swift
16-
ABI/ABI.VersionNumber.swift
1716
ABI/Encoded/ABI.EncodedAttachment.swift
1817
ABI/Encoded/ABI.EncodedBacktrace.swift
1918
ABI/Encoded/ABI.EncodedError.swift
@@ -84,6 +83,7 @@ add_library(Testing
8483
Support/JSON.swift
8584
Support/Locked.swift
8685
Support/Locked+Platform.swift
86+
Support/VersionNumber.swift
8787
Support/Versions.swift
8888
Discovery+Macro.swift
8989
Test.ID.Selection.swift

Sources/Testing/Events/Recorder/Event.HumanReadableOutputRecorder.swift

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,13 @@ extension Event.HumanReadableOutputRecorder {
342342
case .runStarted:
343343
var comments = [Comment]()
344344
if verbosity > 0 {
345-
comments.append("Swift Version: \(swiftStandardLibraryVersion)")
345+
if let swiftStandardLibraryVersion {
346+
comments.append("Swift Standard Library Version: \(swiftStandardLibraryVersion)")
347+
}
348+
comments.append("Swift Compiler Version: \(swiftCompilerVersion)")
349+
#if canImport(Glibc) && !os(FreeBSD) && !os(OpenBSD)
350+
comments.append("GNU C Library Version: \(glibcVersion)")
351+
#endif
346352
}
347353
comments.append("Testing Library Version: \(testingLibraryVersion)")
348354
if let targetTriple {

Sources/Testing/ExitTests/SpawnProcess.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func spawnExecutable(
138138
// and https://www.austingroupbugs.net/view.php?id=411).
139139
_ = posix_spawn_file_actions_adddup2(fileActions, fd, fd)
140140
#if canImport(Glibc) && !os(FreeBSD) && !os(OpenBSD)
141-
if _slowPath(glibcVersion.major < 2 || (glibcVersion.major == 2 && glibcVersion.minor < 29)) {
141+
if _slowPath(glibcVersion < VersionNumber(2, 29)) {
142142
// This system is using an older version of glibc that does not
143143
// implement FD_CLOEXEC clearing in posix_spawn_file_actions_adddup2(),
144144
// so we must clear it here in the parent process.

Sources/Testing/ABI/ABI.VersionNumber.swift renamed to Sources/Testing/Support/VersionNumber.swift

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,34 +8,42 @@
88
// See https://swift.org/CONTRIBUTORS.txt for Swift project authors
99
//
1010

11-
extension ABI {
12-
/// A type describing an ABI version number.
11+
private import _TestingInternals
12+
13+
/// A type describing an ABI version number.
14+
///
15+
/// This type implements a subset of the [semantic versioning](https://semver.org)
16+
/// specification (specifically parsing, displaying, and comparing
17+
/// `<version core>` values we expect that the testing library will need for the
18+
/// foreseeable future.)
19+
struct VersionNumber: Sendable {
20+
/// The integer type used to store a component.
1321
///
14-
/// This type implements a subset of the [semantic versioning](https://semver.org)
15-
/// specification (specifically parsing, displaying, and comparing
16-
/// `<version core>` values we expect that Swift will need for the foreseeable
17-
/// future.)
18-
struct VersionNumber: Sendable {
19-
/// The major version.
20-
var majorComponent: Int8
22+
/// The testing library does not generally need to deal with version numbers
23+
/// whose components exceed the width of this type. If we need to deal with
24+
/// larger version number components in the future, we can increase the width
25+
/// of this type accordingly.
26+
typealias Component = Int8
2127

22-
/// The minor version.
23-
var minorComponent: Int8
28+
/// The major version.
29+
var majorComponent: Component
2430

25-
/// The patch, revision, or bug fix version.
26-
var patchComponent: Int8 = 0
27-
}
31+
/// The minor version.
32+
var minorComponent: Component
33+
34+
/// The patch, revision, or bug fix version.
35+
var patchComponent: Component = 0
2836
}
2937

30-
extension ABI.VersionNumber {
31-
init(_ majorComponent: _const Int8, _ minorComponent: _const Int8, _ patchComponent: _const Int8 = 0) {
38+
extension VersionNumber {
39+
init(_ majorComponent: _const Component, _ minorComponent: _const Component, _ patchComponent: _const Component = 0) {
3240
self.init(majorComponent: majorComponent, minorComponent: minorComponent, patchComponent: patchComponent)
3341
}
3442
}
3543

3644
// MARK: - CustomStringConvertible
3745

38-
extension ABI.VersionNumber: CustomStringConvertible {
46+
extension VersionNumber: CustomStringConvertible {
3947
/// Initialize an instance of this type by parsing the given string.
4048
///
4149
/// - Parameters:
@@ -55,8 +63,8 @@ extension ABI.VersionNumber: CustomStringConvertible {
5563
// Split the string on "." (assuming it is of the form "1", "1.2", or
5664
// "1.2.3") and parse the individual components as integers.
5765
let components = string.split(separator: ".", omittingEmptySubsequences: false)
58-
func componentValue(_ index: Int) -> Int8? {
59-
components.count > index ? Int8(components[index]) : 0
66+
func componentValue(_ index: Int) -> Component? {
67+
components.count > index ? Component(components[index]) : 0
6068
}
6169

6270
guard let majorComponent = componentValue(0),
@@ -81,7 +89,7 @@ extension ABI.VersionNumber: CustomStringConvertible {
8189

8290
// MARK: - Equatable, Comparable
8391

84-
extension ABI.VersionNumber: Equatable, Comparable {
92+
extension VersionNumber: Equatable, Comparable {
8593
static func <(lhs: Self, rhs: Self) -> Bool {
8694
if lhs.majorComponent != rhs.majorComponent {
8795
return lhs.majorComponent < rhs.majorComponent
@@ -96,10 +104,10 @@ extension ABI.VersionNumber: Equatable, Comparable {
96104

97105
// MARK: - Codable
98106

99-
extension ABI.VersionNumber: Codable {
107+
extension VersionNumber: Codable {
100108
init(from decoder: any Decoder) throws {
101109
let container = try decoder.singleValueContainer()
102-
if let number = try? container.decode(Int8.self) {
110+
if let number = try? container.decode(Component.self) {
103111
// Allow for version numbers encoded as integers for compatibility with
104112
// Swift 6.2 and earlier.
105113
self.init(majorComponent: number, minorComponent: 0)

Sources/Testing/Support/Versions.swift

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
//
1010

1111
private import _TestingInternals
12+
private import SwiftShims
1213

1314
/// A human-readable string describing the current operating system's version.
1415
///
@@ -141,23 +142,50 @@ var targetTriple: String? {
141142

142143
/// A human-readable string describing the Swift Standard Library's version.
143144
///
144-
/// This value's format is platform-specific and is not meant to be
145-
/// machine-readable. It is added to the output of a test run when using
146-
/// an event writer.
145+
/// This value is unavailable on some earlier Apple runtime targets. On those
146+
/// targets, this property has a value of `5.0.0`.
147147
///
148148
/// This value is not part of the public interface of the testing library.
149-
let swiftStandardLibraryVersion: String = {
150-
if #available(_swiftVersionAPI, *) {
151-
return String(describing: _SwiftStdlibVersion.current)
149+
let swiftStandardLibraryVersion: VersionNumber? = {
150+
guard #available(_swiftVersionAPI, *) else {
151+
return VersionNumber(5, 0)
152152
}
153-
return "unknown"
153+
let packedValue = _SwiftStdlibVersion.current._value
154+
return VersionNumber(
155+
majorComponent: .init((packedValue & 0xFFFF0000) >> 16),
156+
minorComponent: .init((packedValue & 0x0000FF00) >> 8),
157+
patchComponent: .init((packedValue & 0x000000FF) >> 0)
158+
)
154159
}()
155160

161+
/// The version of the Swift compiler used to build the testing library.
162+
///
163+
/// This value is determined at compile time by the Swift compiler. For more
164+
/// information, see [Version.h](https://github.com/swiftlang/swift/blob/main/include/swift/Basic/Version.h)
165+
/// and [ClangImporter.cpp](https://github.com/swiftlang/swift/blob/main/lib/ClangImporter/ClangImporter.cpp)
166+
/// in the Swift repository.
167+
///
168+
/// This value is not part of the public interface of the testing library.
169+
var swiftCompilerVersion: VersionNumber {
170+
let packedValue = swt_getSwiftCompilerVersion()
171+
if packedValue == 0, let swiftStandardLibraryVersion {
172+
// The compiler did not supply its version. This is currently expected on
173+
// non-Darwin targets in particular. Substitute the stdlib version (which
174+
// should generally be aligned on non-Darwin targets.)
175+
return swiftStandardLibraryVersion
176+
}
177+
return VersionNumber(
178+
majorComponent: .init((packedValue % 1_000_000_000_000_000) / 1_000_000_000_000),
179+
minorComponent: .init((packedValue % 1_000_000_000_000) / 1_000_000_000),
180+
patchComponent: .init((packedValue % 1_000_000_000) / 1_000_000)
181+
)
182+
}
183+
156184
#if canImport(Glibc) && !os(FreeBSD) && !os(OpenBSD)
157185
/// The (runtime, not compile-time) version of glibc in use on this system.
158186
///
159187
/// This value is not part of the public interface of the testing library.
160-
let glibcVersion: (major: Int, minor: Int) = {
188+
let glibcVersion: VersionNumber = {
161189
// Default to the statically available version number if the function call
162190
// fails for some reason.
163191
var major = Int(clamping: __GLIBC__)
@@ -173,7 +201,7 @@ let glibcVersion: (major: Int, minor: Int) = {
173201
}
174202
}
175203

176-
return (major, minor)
204+
return VersionNumber(majorComponent: .init(clamping: major), minorComponent: .init(clamping: minor))
177205
}()
178206
#endif
179207

Sources/_TestingInternals/include/Versions.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,20 @@
1616

1717
SWT_ASSUME_NONNULL_BEGIN
1818

19+
/// Get the version of the compiler used to build the testing library.
20+
///
21+
/// - Returns: An integer containing the packed major, minor, and patch
22+
/// components of the compiler version. For more information, see
23+
/// [ClangImporter.cpp](https://github.com/swiftlang/swift/blob/36246a2c8e9501cd29a75f34c9631a8f4e2e1e9b/lib/ClangImporter/ClangImporter.cpp#L647)
24+
/// in the Swift repository.
25+
static inline uint64_t swt_getSwiftCompilerVersion(void) {
26+
#if defined(__SWIFT_COMPILER_VERSION)
27+
return __SWIFT_COMPILER_VERSION;
28+
#else
29+
return 0;
30+
#endif
31+
}
32+
1933
/// Get the human-readable version of the testing library.
2034
///
2135
/// - Returns: A human-readable string describing the version of the testing

0 commit comments

Comments
 (0)