Skip to content

Commit 92831ae

Browse files
committed
Dynamically determine the swift compiler version.
Prebuilts for macros and the automated downloading of SwiftSDKs need to know the version of the compiler so we can fetch compatible binaries. The swiftc compiler has a --print-target-info options which dumps out a JSON structure that contains the compilerVersion. We already use info in this structure to determine the hostTriple for the UserToolchain. This adds the swiftCompilerVersion to UserToolchain that uses a couple of regex's to pull out the Swift compiler version. This is then used by the prebuilts feature instead of our current hardcodeing of the swift toolchain version. This also turns the prebuilts feature on by default which was supposed to be done in the last update.
1 parent 297b487 commit 92831ae

File tree

6 files changed

+133
-8
lines changed

6 files changed

+133
-8
lines changed

Sources/CoreCommands/Options.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ public struct CachingOptions: ParsableArguments {
198198
@Flag(name: .customLong("experimental-prebuilts"),
199199
inversion: .prefixedEnableDisable,
200200
help: "Whether to use prebuilt swift-syntax libraries for macros.")
201-
public var usePrebuilts: Bool = false
201+
public var usePrebuilts: Bool = true
202202

203203
/// Hidden option to override the prebuilts download location for testing
204204
@Option(

Sources/PackageModel/UserToolchain.swift

Lines changed: 79 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import Basics
1414
import Foundation
1515
import TSCUtility
16+
import enum TSCBasic.JSON
1617

1718
import class Basics.AsyncProcess
1819

@@ -74,6 +75,9 @@ public final class UserToolchain: Toolchain {
7475

7576
public let targetTriple: Basics.Triple
7677

78+
// A version string that can be used to identify the swift compiler version
79+
public let swiftCompilerVersion: String?
80+
7781
/// The list of CPU architectures to build for.
7882
public let architectures: [String]?
7983

@@ -160,6 +164,73 @@ public final class UserToolchain: Toolchain {
160164
return try getTool(name, binDirectories: envSearchPaths, fileSystem: fileSystem)
161165
}
162166

167+
private static func getTargetInfo(swiftCompiler: AbsolutePath) throws -> JSON {
168+
// Call the compiler to get the target info JSON.
169+
let compilerOutput: String
170+
do {
171+
let result = try AsyncProcess.popen(args: swiftCompiler.pathString, "-print-target-info")
172+
compilerOutput = try result.utf8Output().spm_chomp()
173+
} catch {
174+
throw InternalError("Failed to get target info (\(error.interpolationDescription))")
175+
}
176+
// Parse the compiler's JSON output.
177+
do {
178+
return try JSON(string: compilerOutput)
179+
} catch {
180+
throw InternalError(
181+
"Failed to parse target info (\(error.interpolationDescription)).\nRaw compiler output: \(compilerOutput)"
182+
)
183+
}
184+
}
185+
186+
private static func getHostTriple(targetInfo: JSON) throws -> Basics.Triple {
187+
// Get the triple string from the target info.
188+
let tripleString: String
189+
do {
190+
tripleString = try targetInfo.get("target").get("triple")
191+
} catch {
192+
throw InternalError(
193+
"Target info does not contain a triple string (\(error.interpolationDescription)).\nTarget info: \(targetInfo)"
194+
)
195+
}
196+
197+
// Parse the triple string.
198+
do {
199+
return try Triple(tripleString)
200+
} catch {
201+
throw InternalError(
202+
"Failed to parse triple string (\(error.interpolationDescription)).\nTriple string: \(tripleString)"
203+
)
204+
}
205+
}
206+
207+
private static func computeSwiftCompilerVersion(targetInfo: JSON) -> String? {
208+
// Get the compiler version from the target info
209+
let compilerVersion: String
210+
do {
211+
compilerVersion = try targetInfo.get("compilerVersion")
212+
} catch {
213+
return nil
214+
}
215+
216+
// Extract the swift version using regex from the description if available
217+
do {
218+
let regex = try Regex(#"\((swift(lang)?-[^ )]*)"#)
219+
if let match = try regex.firstMatch(in: compilerVersion), match.count > 1, let substring = match[1].substring {
220+
return String(substring)
221+
}
222+
223+
let regex2 = try Regex(#"\(.*Swift (.*)[ )]"#)
224+
if let match2 = try regex2.firstMatch(in: compilerVersion), match2.count > 1, let substring = match2[1].substring {
225+
return "swift-\(substring)"
226+
} else {
227+
return nil
228+
}
229+
} catch {
230+
return nil
231+
}
232+
}
233+
163234
// MARK: - public API
164235

165236
public static func determineLibrarian(
@@ -612,8 +683,14 @@ public final class UserToolchain: Toolchain {
612683
default: InstalledSwiftPMConfiguration.default)
613684
}
614685

615-
// Use the triple from Swift SDK or compute the host triple using swiftc.
616-
var triple = try swiftSDK.targetTriple ?? Triple.getHostTriple(usingSwiftCompiler: swiftCompilers.compile)
686+
// targetInfo from the compiler
687+
let targetInfo = try Self.getTargetInfo(swiftCompiler: swiftCompilers.compile)
688+
689+
// Get compiler version information from target info
690+
self.swiftCompilerVersion = Self.computeSwiftCompilerVersion(targetInfo: targetInfo)
691+
692+
// Use the triple from Swift SDK or compute the host triple from the target info
693+
var triple = try swiftSDK.targetTriple ?? Self.getHostTriple(targetInfo: targetInfo)
617694

618695
// Change the triple to the specified arch if there's exactly one of them.
619696
// The Triple property is only looked at by the native build system currently.

Sources/Workspace/Workspace+Prebuilts.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,19 +127,22 @@ extension Workspace {
127127

128128
/// For simplified init in tests
129129
public struct CustomPrebuiltsManager {
130+
let swiftVersion: String
130131
let httpClient: HTTPClient?
131132
let archiver: Archiver?
132133
let useCache: Bool?
133134
let hostPlatform: PrebuiltsManifest.Platform?
134135
let rootCertPath: AbsolutePath?
135136

136137
public init(
138+
swiftVersion: String,
137139
httpClient: HTTPClient? = .none,
138140
archiver: Archiver? = .none,
139141
useCache: Bool? = .none,
140142
hostPlatform: PrebuiltsManifest.Platform? = nil,
141143
rootCertPath: AbsolutePath? = nil
142144
) {
145+
self.swiftVersion = swiftVersion
143146
self.httpClient = httpClient
144147
self.archiver = archiver
145148
self.useCache = useCache
@@ -153,6 +156,7 @@ extension Workspace {
153156
public typealias Delegate = PrebuiltsManagerDelegate
154157

155158
private let fileSystem: FileSystem
159+
private let swiftVersion: String
156160
private let authorizationProvider: AuthorizationProvider?
157161
private let httpClient: HTTPClient
158162
private let archiver: Archiver
@@ -167,6 +171,7 @@ extension Workspace {
167171
init(
168172
fileSystem: FileSystem,
169173
hostPlatform: PrebuiltsManifest.Platform,
174+
swiftCompilerVersion: String,
170175
authorizationProvider: AuthorizationProvider?,
171176
scratchPath: AbsolutePath,
172177
cachePath: AbsolutePath?,
@@ -178,6 +183,7 @@ extension Workspace {
178183
) {
179184
self.fileSystem = fileSystem
180185
self.hostPlatform = hostPlatform
186+
self.swiftVersion = swiftCompilerVersion
181187
self.authorizationProvider = authorizationProvider
182188
self.httpClient = customHTTPClient ?? HTTPClient()
183189

@@ -222,9 +228,6 @@ extension Workspace {
222228

223229
private let prebuiltPackages: [PrebuiltPackage]
224230

225-
// Version of the compiler we're building against
226-
private let swiftVersion = "\(SwiftVersion.current.major).\(SwiftVersion.current.minor)"
227-
228231
fileprivate func findPrebuilts(packages: [PackageReference]) -> [PrebuiltPackage] {
229232
var prebuilts: [PrebuiltPackage] = []
230233
for packageRef in packages {
@@ -310,6 +313,11 @@ extension Workspace {
310313
}
311314
}
312315

316+
// Skip prebuilts if this file exists.
317+
if let cachePath, fileSystem.exists(cachePath.appending("noprebuilts")) {
318+
return nil
319+
}
320+
313321
if fileSystem.exists(destination), let manifest = try? await loadManifest() {
314322
return manifest
315323
} else if let cacheDest, fileSystem.exists(cacheDest) {

Sources/Workspace/Workspace.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,10 @@ public class Workspace {
568568
// register the binary artifacts downloader with the cancellation handler
569569
cancellator?.register(name: "binary artifacts downloads", handler: binaryArtifactsManager)
570570

571-
if configuration.usePrebuilts, let hostPlatform = customPrebuiltsManager?.hostPlatform ?? PrebuiltsManifest.Platform.hostPlatform {
571+
if configuration.usePrebuilts,
572+
let hostPlatform = customPrebuiltsManager?.hostPlatform ?? PrebuiltsManifest.Platform.hostPlatform,
573+
let swiftCompilerVersion = hostToolchain.swiftCompilerVersion
574+
{
572575
let rootCertPath: AbsolutePath?
573576
if let path = configuration.prebuiltsRootCertPath {
574577
rootCertPath = try AbsolutePath(validating: path)
@@ -579,6 +582,7 @@ public class Workspace {
579582
let prebuiltsManager = PrebuiltsManager(
580583
fileSystem: fileSystem,
581584
hostPlatform: hostPlatform,
585+
swiftCompilerVersion: customPrebuiltsManager?.swiftVersion ?? swiftCompilerVersion,
582586
authorizationProvider: authorizationProvider,
583587
scratchPath: location.prebuiltsDirectory,
584588
cachePath: customPrebuiltsManager?.useCache == false || !configuration.sharedDependenciesCacheEnabled ? .none : location.sharedPrebuiltsCacheDirectory,

Sources/swift-build-prebuilts/BuildPrebuilts.swift

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import ArgumentParser
2020
import Basics
2121
import Foundation
22+
import PackageModel
2223
import struct TSCBasic.ByteString
2324
import struct TSCBasic.SHA256
2425
import Workspace
@@ -168,7 +169,6 @@ var prebuiltRepos: IdentifiableSet<PrebuiltRepos> = [
168169
),
169170
]
170171

171-
let swiftVersion = "\(SwiftVersion.current.major).\(SwiftVersion.current.minor)"
172172
let dockerImageRoot = "swiftlang/swift:nightly-6.1-"
173173

174174
@main
@@ -216,6 +216,21 @@ struct BuildPrebuilts: AsyncParsableCommand {
216216
}
217217
}
218218

219+
func computeSwiftVersion() throws -> String? {
220+
let fileSystem = localFileSystem
221+
222+
let environment = Environment.current
223+
let hostToolchain = try UserToolchain(
224+
swiftSDK: SwiftSDK.hostSwiftSDK(
225+
environment: environment,
226+
fileSystem: fileSystem
227+
),
228+
environment: environment
229+
)
230+
231+
return hostToolchain.swiftCompilerVersion
232+
}
233+
219234
mutating func run() async throws {
220235
if build {
221236
try await build()
@@ -231,6 +246,11 @@ struct BuildPrebuilts: AsyncParsableCommand {
231246
let encoder = JSONEncoder()
232247
encoder.outputFormatting = .prettyPrinted
233248

249+
guard let swiftVersion = try computeSwiftVersion() else {
250+
print("Unable to determine swift compiler version")
251+
return
252+
}
253+
234254
print("Stage directory: \(stageDir)")
235255

236256
let srcDir = stageDir.appending("src")
@@ -379,6 +399,11 @@ struct BuildPrebuilts: AsyncParsableCommand {
379399
encoder.outputFormatting = .prettyPrinted
380400
let decoder = JSONDecoder()
381401

402+
guard let swiftVersion = try computeSwiftVersion() else {
403+
print("Unable to determine swift compiler version")
404+
return
405+
}
406+
382407
for repo in prebuiltRepos.values {
383408
let prebuiltDir = stageDir.appending(repo.url.lastPathComponent)
384409
for version in repo.versions {

Tests/WorkspaceTests/PrebuiltsTests.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ final class PrebuiltsTests: XCTestCase {
205205
swiftSyntax
206206
],
207207
prebuiltsManager: .init(
208+
swiftVersion: swiftVersion,
208209
httpClient: httpClient,
209210
archiver: archiver,
210211
hostPlatform: .macos_aarch64,
@@ -268,6 +269,7 @@ final class PrebuiltsTests: XCTestCase {
268269
swiftSyntax
269270
],
270271
prebuiltsManager: .init(
272+
swiftVersion: swiftVersion,
271273
httpClient: httpClient,
272274
archiver: archiver,
273275
hostPlatform: .macos_aarch64,
@@ -372,6 +374,7 @@ final class PrebuiltsTests: XCTestCase {
372374
swiftSyntax
373375
],
374376
prebuiltsManager: .init(
377+
swiftVersion: swiftVersion,
375378
httpClient: httpClient,
376379
archiver: archiver,
377380
hostPlatform: .macos_aarch64,
@@ -434,6 +437,7 @@ final class PrebuiltsTests: XCTestCase {
434437
swiftSyntax
435438
],
436439
prebuiltsManager: .init(
440+
swiftVersion: swiftVersion,
437441
httpClient: httpClient,
438442
archiver: archiver,
439443
hostPlatform: .macos_aarch64,
@@ -486,6 +490,7 @@ final class PrebuiltsTests: XCTestCase {
486490
swiftSyntax
487491
],
488492
prebuiltsManager: .init(
493+
swiftVersion: swiftVersion,
489494
httpClient: httpClient,
490495
archiver: archiver,
491496
rootCertPath: rootCertPath
@@ -551,6 +556,7 @@ final class PrebuiltsTests: XCTestCase {
551556
swiftSyntax
552557
],
553558
prebuiltsManager: .init(
559+
swiftVersion: swiftVersion,
554560
httpClient: httpClient,
555561
archiver: archiver,
556562
hostPlatform: .ubuntu_noble_x86_64,
@@ -600,6 +606,7 @@ final class PrebuiltsTests: XCTestCase {
600606
swiftSyntax
601607
],
602608
prebuiltsManager: .init(
609+
swiftVersion: swiftVersion,
603610
httpClient: httpClient,
604611
archiver: archiver,
605612
rootCertPath: rootCertPath
@@ -666,6 +673,7 @@ final class PrebuiltsTests: XCTestCase {
666673
swiftSyntax
667674
],
668675
prebuiltsManager: .init(
676+
swiftVersion: swiftVersion,
669677
httpClient: httpClient,
670678
archiver: archiver,
671679
hostPlatform: .macos_aarch64,
@@ -727,6 +735,7 @@ final class PrebuiltsTests: XCTestCase {
727735
swiftSyntax
728736
],
729737
prebuiltsManager: .init(
738+
swiftVersion: swiftVersion,
730739
httpClient: httpClient,
731740
archiver: archiver,
732741
hostPlatform: .macos_aarch64,
@@ -790,6 +799,7 @@ final class PrebuiltsTests: XCTestCase {
790799
swiftSyntax
791800
],
792801
prebuiltsManager: .init(
802+
swiftVersion: swiftVersion,
793803
httpClient: httpClient,
794804
archiver: archiver,
795805
hostPlatform: .macos_aarch64,
@@ -846,6 +856,7 @@ final class PrebuiltsTests: XCTestCase {
846856
swiftSyntax
847857
],
848858
prebuiltsManager: .init(
859+
swiftVersion: swiftVersion,
849860
httpClient: httpClient,
850861
archiver: archiver
851862
)

0 commit comments

Comments
 (0)