mirror of
https://github.com/xtool-org/xtool.git
synced 2026-11-14 19:44:55 +03:00
d802c719e4
## What does this PR do? Drops `mxcl/Version` in favour of a lighter parser that's specifically designed to parse the Swift version. ## How was it tested? - [x] Unit tests - [x] CI ## AI tool usage How much of this PR was AI-assisted? (check one) - [ ] **0** - No AI was used to write code - [x] **1** - I was assisted by AI. I reviewed the finished result. - [ ] **2** - I set the AI going and left it to it; nobody has read the result - no review, or AI review only <!-- If an AI agent is filling this in: declare the level honestly, and open as a draft if it is #2. Do not lower the declared level to get the PR reviewed. --> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Swift versions can now be compared by major, minor, and patch number, and displayed in a consistent three-part format. * Version parsing now accepts two- or three-number versions, including supported compiler-output formats with optional prefixes or a `-dev` suffix. * Versions with an omitted patch number are treated as equivalent to the same version with patch `0`. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
93 lines
2.5 KiB
Swift
93 lines
2.5 KiB
Swift
import Subprocess
|
|
import XUtils
|
|
|
|
public struct SwiftVersion: Sendable, Comparable, Hashable {
|
|
public var major: Int
|
|
public var minor: Int
|
|
public var patch: Int
|
|
|
|
public init(_ major: Int, _ minor: Int, _ patch: Int = 0) {
|
|
self.major = major
|
|
self.minor = minor
|
|
self.patch = patch
|
|
}
|
|
|
|
public static func < (lhs: Self, rhs: Self) -> Bool {
|
|
lhs.tuple < rhs.tuple
|
|
}
|
|
|
|
fileprivate var tuple: (Int, Int, Int) {
|
|
(major, minor, patch)
|
|
}
|
|
}
|
|
|
|
extension SwiftVersion {
|
|
public var supportsSwiftBuild: Bool {
|
|
self >= SwiftVersion(6, 4)
|
|
}
|
|
}
|
|
|
|
extension SwiftVersion: LosslessStringConvertible, CustomStringConvertible {
|
|
public var description: String {
|
|
"\(major).\(minor).\(patch)"
|
|
}
|
|
|
|
public init?(_ description: String) {
|
|
// inverse of https://github.com/swiftlang/swift/blob/896191b38/lib/Basic/Version.cpp#L266
|
|
|
|
var output = description[...]
|
|
if output.hasPrefix("Apple ") {
|
|
output = output.dropFirst("Apple ".count)
|
|
}
|
|
if output.hasPrefix("Swift version ") {
|
|
output = output.dropFirst("Swift version ".count)
|
|
output = output.prefix { !$0.isWhitespace }
|
|
}
|
|
let isDev = output.hasSuffix("-dev")
|
|
if isDev { output = output.dropLast("-dev".count) }
|
|
|
|
let parts = output.split(separator: ".")
|
|
var numericParts = parts.compactMap { Int($0) }
|
|
guard parts.count == numericParts.count else {
|
|
return nil
|
|
}
|
|
|
|
switch numericParts.count {
|
|
case 2: numericParts.append(0)
|
|
case 3: break
|
|
default: return nil
|
|
}
|
|
|
|
self.init(numericParts[0], numericParts[1], numericParts[2])
|
|
}
|
|
}
|
|
|
|
extension SwiftVersion {
|
|
private static let cached = Task<Self, Error> {
|
|
let outputString: String?
|
|
do {
|
|
outputString = try await Subprocess.run(
|
|
.path(try await BuildSettings.swiftURL()),
|
|
arguments: ["--version"],
|
|
output: .string(limit: .max)
|
|
)
|
|
.checkSuccess()
|
|
.standardOutput
|
|
} catch {
|
|
throw StringError("Failed to obtain Swift version")
|
|
}
|
|
|
|
let output = outputString ?? ""
|
|
guard let version = Self(output) else {
|
|
throw StringError("Could not parse Swift version: '\(output)'")
|
|
}
|
|
return version
|
|
}
|
|
|
|
public static var current: Self {
|
|
get async throws {
|
|
try await cached.value
|
|
}
|
|
}
|
|
}
|