Skip to content

Commit 6bbf726

Browse files
committed
Generalize the Swift version regex
The "swiftlang" version was only being used in a single location for a compatibility check that is no longer required. The clang version was unusued (which is unsurprising since it's often not available from the Swift `--version` output). Really we only need the version itself and a unique tag to pass as additional signature data to Swift tasks.
1 parent 8e71bde commit 6bbf726

File tree

4 files changed

+46
-122
lines changed

4 files changed

+46
-122
lines changed

Sources/SWBCore/SpecImplementations/Tools/DocumentationCompiler.swift

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,6 @@ final public class DocumentationCompilerSpec: GenericCompilerSpec, SpecIdentifie
8484

8585
switch DocumentationType(from: cbc) {
8686
case .executable:
87-
guard swiftCompilerInfo.supportsSymbolGraphMinimumAccessLevelFlag else {
88-
// The swift compiler doesn't support specifying a minimum access level,
89-
// so just return an empty array.
90-
return additionalFlags
91-
}
92-
9387
// When building executable types (like applications and command-line tools), include
9488
// internal symbols in the generated symbol graph.
9589
return additionalFlags.appending(contentsOf: ["-symbol-graph-minimum-access-level", "internal"])
@@ -476,26 +470,6 @@ extension DocumentationCompilerSpec {
476470
}
477471
}
478472

479-
private extension DiscoveredSwiftCompilerToolSpecInfo {
480-
/// A Boolean value that is true if the Swift compiler supports specifying a minimum
481-
/// access level for symbol graph generation.
482-
///
483-
/// The `-symbol-graph-minimum-access-level` flag was added in `swiftlang-5.6.0.316.14`.
484-
var supportsSymbolGraphMinimumAccessLevelFlag: Bool {
485-
// We're explicitly checking the swiftlangVersion here instead of a value in the
486-
// the toolchain's `features.json` because a `features.json` flag wasn't originally
487-
// added when support for `-symbol-graph-minimum-access-level` was added.
488-
//
489-
// Instead of regressing the current documentation build experience while waiting for a
490-
// submission that includes the `features.json` flag, we're checking the raw version here.
491-
//
492-
// For future coordinated changes like this between the Swift-DocC infrastructure
493-
// in Swift Build and the Swift compiler, we'll rely on the `features.json` instead of
494-
// raw version numbers.
495-
return swiftlangVersion >= Version(5, 6, 0, 316, 14)
496-
}
497-
}
498-
499473
// MARK: - Diagnostics
500474

501475
/// An output parser which forwards all output unchanged, then generates diagnostics from a serialized diagnostics file passed in the payload once it is closed.

Sources/SWBCore/SpecImplementations/Tools/SwiftCompiler.swift

Lines changed: 12 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,16 +1027,14 @@ public struct DiscoveredSwiftCompilerToolSpecInfo: DiscoveredCommandLineToolSpec
10271027
public let toolPath: Path
10281028
/// The version of the Swift language in the tool.
10291029
public let swiftVersion: Version
1030-
/// The version of swiftlang in the tool.
1031-
public let swiftlangVersion: Version
1030+
/// The name that this Swift was tagged with.
1031+
public let swiftTag: String
10321032
/// The version of the stable ABI for the Swift language in the tool.
10331033
public let swiftABIVersion: String?
1034-
/// The version of clang in the tool.
1035-
public let clangVersion: Version?
10361034
/// `compilerClientsConfig` blocklists for Swift
10371035
public let blocklists: SwiftBlocklists
10381036

1039-
public var toolVersion: Version? { return self.swiftlangVersion }
1037+
public var toolVersion: Version? { return self.swiftVersion }
10401038

10411039
public var hostLibraryDirectory: Path {
10421040
toolPath.dirname.dirname.join("lib/swift/host")
@@ -1066,12 +1064,11 @@ public struct DiscoveredSwiftCompilerToolSpecInfo: DiscoveredCommandLineToolSpec
10661064
return toolFeatures.has(flag)
10671065
}
10681066

1069-
public init(toolPath: Path, swiftVersion: Version, swiftlangVersion: Version, swiftABIVersion: String?, clangVersion: Version?, blocklists: SwiftBlocklists, toolFeatures: ToolFeatures<DiscoveredSwiftCompilerToolSpecInfo.FeatureFlag>) {
1067+
public init(toolPath: Path, swiftVersion: Version, swiftTag: String, swiftABIVersion: String?, blocklists: SwiftBlocklists, toolFeatures: ToolFeatures<DiscoveredSwiftCompilerToolSpecInfo.FeatureFlag>) {
10701068
self.toolPath = toolPath
10711069
self.swiftVersion = swiftVersion
1072-
self.swiftlangVersion = swiftlangVersion
1070+
self.swiftTag = swiftTag
10731071
self.swiftABIVersion = swiftABIVersion
1074-
self.clangVersion = clangVersion
10751072
self.blocklists = blocklists
10761073
self.toolFeatures = toolFeatures
10771074
}
@@ -2213,7 +2210,7 @@ public final class SwiftCompilerSpec : CompilerSpec, SpecIdentifierType, SwiftDi
22132210
if !toolchains.isEmpty {
22142211
environment.append(("TOOLCHAINS", toolchains))
22152212
}
2216-
let additionalSignatureData = "SWIFTC: \(toolSpecInfo.swiftlangVersion.description)"
2213+
let additionalSignatureData = "SWIFTC: \(toolSpecInfo.swiftTag)"
22172214
let environmentBindings = EnvironmentBindings(environment)
22182215

22192216
let indexingInputReplacements = Dictionary(uniqueKeysWithValues: cbc.inputs.compactMap { ftb -> (Path, Path)? in
@@ -3600,35 +3597,18 @@ public func discoveredSwiftCompilerInfo(_ producer: any CommandProducer, _ deleg
36003597

36013598
// Values we will parse. If we end up not parsing any values, then we return an empty info struct.
36023599
var swiftVersion: Version? = nil
3603-
var swiftlangVersion: Version? = nil
3604-
var clangVersion: Version? = nil
3600+
var swiftTag: String? = nil
36053601
var swiftABIVersion: String? = nil
36063602

3607-
// Note that Swift toolchains downloaded from swift.org have a swiftc with a different version format than those built by Apple; the 'releaseVersionRegex' reflects that format. c.f. <rdar://problem/34956869>
3608-
let versionRegex = #/Apple Swift version (?<swiftVersion>[\d.]+) \(swiftlang-(?<swiftlangVersion>[\d.]+) clang-(?<clangVersion>[\d.]+)\)/#
3609-
let releaseVersionRegex = #/(?:Apple )?Swift version (?<swiftVersion>[\d.]+) \(swift-(?<swiftlangVersion>[\d.]+)-RELEASE\)/#
3610-
let developmentVersionRegex = #/Swift version (?<swiftVersion>[\d.]+)-dev \(LLVM (?:\b[0-9a-f]+), Swift (?:\b[0-9a-f]+)\)/#
3603+
let versionRegex = #/Swift version (?<swiftVersion>[\d.]+).*\((?<swiftTag>.*)\)/#
36113604
let abiVersionRegex = #/ABI version: (?<abiVersion>[\d.]+)/#
36123605

36133606
// Iterate over each line and add any discovered info to the info object.
36143607
for line in outputString.components(separatedBy: "\n") {
3615-
if swiftlangVersion == nil {
3608+
if swiftVersion == nil {
36163609
if let groups = try versionRegex.firstMatch(in: line) {
36173610
swiftVersion = try? Version(String(groups.output.swiftVersion))
3618-
swiftlangVersion = try? Version(String(groups.output.swiftlangVersion))
3619-
clangVersion = try? Version(String(groups.output.clangVersion))
3620-
}
3621-
else if let groups = try releaseVersionRegex.firstMatch(in: line) {
3622-
swiftVersion = try? Version(String(groups.output.swiftVersion))
3623-
swiftlangVersion = try? Version(String(groups.output.swiftlangVersion))
3624-
// This form has no clang version.
3625-
} else if let groups = try developmentVersionRegex.firstMatch(in: line) {
3626-
swiftVersion = try? Version(String(groups.output.swiftVersion))
3627-
guard let swiftVersion else {
3628-
throw StubError.error("Could not parse Swift version from: \(outputString)")
3629-
}
3630-
clangVersion = try? Version(swiftVersion.description + ".999.999")
3631-
swiftlangVersion = try? Version(swiftVersion.description + ".999.999")
3611+
swiftTag = String(groups.output.swiftTag)
36323612
}
36333613
}
36343614
if swiftABIVersion == nil {
@@ -3638,7 +3618,7 @@ public func discoveredSwiftCompilerInfo(_ producer: any CommandProducer, _ deleg
36383618
}
36393619
}
36403620

3641-
guard let swiftVersion, let swiftlangVersion else {
3621+
guard let swiftVersion, let swiftTag else {
36423622
throw StubError.error("Could not parse Swift versions from: \(outputString)")
36433623
}
36443624

@@ -3674,7 +3654,7 @@ public func discoveredSwiftCompilerInfo(_ producer: any CommandProducer, _ deleg
36743654
blocklists.installAPILazyTypecheck = getBlocklist(type: SwiftBlocklists.InstallAPILazyTypecheckInfo.self, toolchainFilename: "swift-lazy-installapi.json", delegate: delegate)
36753655
blocklists.caching = getBlocklist(type: SwiftBlocklists.CachingBlockList.self, toolchainFilename: "swift-caching.json", delegate: delegate)
36763656
blocklists.languageFeatureEnablement = getBlocklist(type: SwiftBlocklists.LanguageFeatureEnablementInfo.self, toolchainFilename: "swift-language-feature-enablement.json", delegate: delegate)
3677-
return DiscoveredSwiftCompilerToolSpecInfo(toolPath: toolPath, swiftVersion: swiftVersion, swiftlangVersion: swiftlangVersion, swiftABIVersion: swiftABIVersion, clangVersion: clangVersion, blocklists: blocklists, toolFeatures: getFeatures(at: toolPath))
3657+
return DiscoveredSwiftCompilerToolSpecInfo(toolPath: toolPath, swiftVersion: swiftVersion, swiftTag: swiftTag, swiftABIVersion: swiftABIVersion, blocklists: blocklists, toolFeatures: getFeatures(at: toolPath))
36783658
})
36793659
}
36803660

Tests/SWBCoreTests/CommandLineToolSpecDiscoveredInfoTests.swift

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,35 +112,38 @@ import SWBMacro
112112
try await withSpec(SwiftCompilerSpec.self, .deferred) { (info: DiscoveredSwiftCompilerToolSpecInfo) in
113113
#expect(info.toolPath.basename == core.hostOperatingSystem.imageFormat.executableName(basename: "swiftc"))
114114
#expect(info.swiftVersion > Version(0, 0, 0))
115-
#expect(info.swiftlangVersion > Version(0, 0, 0))
115+
#expect(!info.swiftTag.isEmpty)
116116
#expect(info.swiftABIVersion == nil)
117-
#if canImport(Darwin)
118-
#expect(info.clangVersion != nil)
119-
#endif
120-
if let clangVersion = info.clangVersion {
121-
#expect(clangVersion > Version(0, 0, 0))
122-
}
123117
}
124118

125119
try await withSpec(SwiftCompilerSpec.self, .result(status: .exit(0), stdout: Data("Swift version 5.9-dev (LLVM fd31e7eab45779f, Swift 86e6bda88e47178)\n".utf8), stderr: Data())) { (info: DiscoveredSwiftCompilerToolSpecInfo) in
126120
#expect(info.toolPath.basename == core.hostOperatingSystem.imageFormat.executableName(basename: "swiftc"))
127121
#expect(info.swiftVersion == Version(5, 9))
128-
#expect(info.swiftlangVersion == Version(5, 9, 999, 999))
129-
#expect(info.clangVersion == Version(5, 9, 999, 999))
122+
#expect(info.swiftTag == "LLVM fd31e7eab45779f, Swift 86e6bda88e47178")
123+
}
124+
125+
try await withSpec(SwiftCompilerSpec.self, .result(status: .exit(0), stdout: Data("Swift version 5.9 (Swift 86e6bda88e47178 LLVM fd31e7eab45779f)\n".utf8), stderr: Data())) { (info: DiscoveredSwiftCompilerToolSpecInfo) in
126+
#expect(info.toolPath.basename == core.hostOperatingSystem.imageFormat.executableName(basename: "swiftc"))
127+
#expect(info.swiftVersion == Version(5, 9))
128+
#expect(info.swiftTag == "Swift 86e6bda88e47178 LLVM fd31e7eab45779f")
129+
}
130+
131+
try await withSpec(SwiftCompilerSpec.self, .result(status: .exit(0), stdout: Data("Swift version 6.2 (swift-6.2-DEVELOPMENT-SNAPSHOT-2025-05-15-a)\n".utf8), stderr: Data())) { (info: DiscoveredSwiftCompilerToolSpecInfo) in
132+
#expect(info.toolPath.basename == core.hostOperatingSystem.imageFormat.executableName(basename: "swiftc"))
133+
#expect(info.swiftVersion == Version(6, 2))
134+
#expect(info.swiftTag == "swift-6.2-DEVELOPMENT-SNAPSHOT-2025-05-15-a")
130135
}
131136

132137
try await withSpec(SwiftCompilerSpec.self, .result(status: .exit(0), stdout: Data("Apple Swift version 5.9 (swiftlang-5.9.0.106.53 clang-1500.0.13.6)\n".utf8), stderr: Data("swift-driver version: 1.80 ".utf8))) { (info: DiscoveredSwiftCompilerToolSpecInfo) in
133138
#expect(info.toolPath.basename == core.hostOperatingSystem.imageFormat.executableName(basename: "swiftc"))
134139
#expect(info.swiftVersion == Version(5, 9))
135-
#expect(info.swiftlangVersion == Version(5, 9, 0, 106, 53))
136-
#expect(info.clangVersion == Version(1500, 0, 13, 6))
140+
#expect(info.swiftTag == "swiftlang-5.9.0.106.53 clang-1500.0.13.6")
137141
}
138142

139143
try await withSpec(SwiftCompilerSpec.self, .result(status: .exit(0), stdout: Data("Swift version 5.10.1 (swift-5.10.1-RELEASE)\nTarget: aarch64-unknown-linux-gnu\n".utf8), stderr: Data())) { (info: DiscoveredSwiftCompilerToolSpecInfo) in
140144
#expect(info.toolPath.basename == core.hostOperatingSystem.imageFormat.executableName(basename: "swiftc"))
141145
#expect(info.swiftVersion == Version(5, 10, 1))
142-
#expect(info.swiftlangVersion == Version(5, 10, 1))
143-
#expect(info.clangVersion == nil)
146+
#expect(info.swiftTag == "swift-5.10.1-RELEASE")
144147
}
145148
}
146149

Tests/SWBCoreTests/DocumentationCompilerSpecTests.swift

Lines changed: 18 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -19,80 +19,47 @@ import SWBMacro
1919

2020
@Suite fileprivate struct DocumentationCompilerSpecTests: CoreBasedTests {
2121
// Tests that `DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs` only returns
22-
// flags that are compatible with the given Swift compiler version.
22+
// flags that are compatible with the given context.
2323
@Test(.requireSDKs(.macOS))
2424
func additionalSymbolGraphGenerationArgs() async throws {
25-
// Support for the `-symbol-graph-minimum-access-level` flag was first introduced in
26-
// swiftlang-5.6.0.316.14 (rdar://79099869).
27-
let swiftVersionsThatSupportMinimumAccessLevel = [
28-
"5.6.0.316.14",
29-
"5.6.0.318.15",
30-
"6.0.0.123.10",
31-
]
32-
33-
// Any version prior to swiftlang-5.6.0.316.14 does not have support for
34-
// the `-symbol-graph-minimum-access-level` flag.
35-
let swiftVersionsThatDoNotSupportMinimumAccessLevel = [
36-
"5.5.0.123.10",
37-
"5.6.0.0.0",
38-
"5.6.0.113.6",
39-
"5.6.0.315.13",
40-
"5.6.0.316.13",
41-
]
42-
43-
// Confirm that when requesting additional symbol graph generation args for
44-
// swift versions that _do_ support minimum access level, we
45-
// get that flag.
46-
for version in swiftVersionsThatSupportMinimumAccessLevel {
47-
let symbolGraphGenerationArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs(
48-
try mockApplicationBuildContext(),
49-
swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftLangVersion: version)
50-
)
51-
52-
#expect(symbolGraphGenerationArgs == ["-symbol-graph-minimum-access-level", "internal"])
53-
}
54-
55-
// Confirm that when requesting additional symbol graph generation args for
56-
// swift versions that do _not_ support minimum access level, we
57-
// do not get that flag.
58-
for version in swiftVersionsThatDoNotSupportMinimumAccessLevel {
59-
let symbolGraphGenerationArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs(
60-
try mockApplicationBuildContext(),
61-
swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftLangVersion: version)
62-
)
25+
let applicationArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs(
26+
try mockApplicationBuildContext(application: true),
27+
swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0")
28+
)
29+
#expect(applicationArgs == ["-symbol-graph-minimum-access-level", "internal"])
6330

64-
#expect(symbolGraphGenerationArgs.isEmpty,
65-
"""
66-
'swiftlang-\(version)' does not support the minimum-access-level flag that \
67-
was introduced in 'swiftlang-5.6.0.316.14'.
68-
""")
69-
}
31+
let frameworkArgs = await DocumentationCompilerSpec.additionalSymbolGraphGenerationArgs(
32+
try mockApplicationBuildContext(application: false),
33+
swiftCompilerInfo: try mockSwiftCompilerSpec(swiftVersion: "5.6", swiftTag: "swiftlang-5.6.0.0")
34+
)
35+
#expect(frameworkArgs == [])
7036
}
7137

72-
private func mockApplicationBuildContext() async throws -> CommandBuildContext {
38+
private func mockApplicationBuildContext(application: Bool) async throws -> CommandBuildContext {
7339
let core = try await getCore()
7440

7541
let producer = try MockCommandProducer(
7642
core: core,
77-
productTypeIdentifier: "com.apple.product-type.application",
43+
productTypeIdentifier: application ? "com.apple.product-type.application" : "com.apple.product-type.framework",
7844
platform: "macosx"
7945
)
8046

8147
var mockTable = MacroValueAssignmentTable(namespace: core.specRegistry.internalMacroNamespace)
82-
mockTable.push(BuiltinMacros.MACH_O_TYPE, literal: "mh_execute")
48+
if application {
49+
mockTable.push(BuiltinMacros.MACH_O_TYPE, literal: "mh_execute")
50+
}
8351

8452
let mockScope = MacroEvaluationScope(table: mockTable)
8553

8654
return CommandBuildContext(producer: producer, scope: mockScope, inputs: [])
8755
}
8856

89-
private func mockSwiftCompilerSpec(swiftVersion: String, swiftLangVersion: String) throws -> DiscoveredSwiftCompilerToolSpecInfo {
57+
private func mockSwiftCompilerSpec(swiftVersion: String, swiftTag: String) throws -> DiscoveredSwiftCompilerToolSpecInfo {
9058
return DiscoveredSwiftCompilerToolSpecInfo(
9159
toolPath: .root,
9260
swiftVersion: try Version(swiftVersion),
93-
swiftlangVersion: try Version(swiftLangVersion),
61+
swiftTag: swiftTag,
9462
swiftABIVersion: nil,
95-
clangVersion: nil,
9663
blocklists: SwiftBlocklists(),
9764
toolFeatures: ToolFeatures([])
9865
)

0 commit comments

Comments
 (0)