Skip to content

Dynamically determine the swift compiler version. #8707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
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
2 changes: 1 addition & 1 deletion Sources/CoreCommands/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ public struct CachingOptions: ParsableArguments {
@Flag(name: .customLong("experimental-prebuilts"),
inversion: .prefixedEnableDisable,
help: "Whether to use prebuilt swift-syntax libraries for macros.")
public var usePrebuilts: Bool = false
public var usePrebuilts: Bool = true

/// Hidden option to override the prebuilts download location for testing
@Option(
Expand Down
81 changes: 79 additions & 2 deletions Sources/PackageModel/UserToolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import Basics
import Foundation
import TSCUtility
import enum TSCBasic.JSON

import class Basics.AsyncProcess

Expand Down Expand Up @@ -74,6 +75,9 @@ public final class UserToolchain: Toolchain {

public let targetTriple: Basics.Triple

// A version string that can be used to identify the swift compiler version
public let swiftCompilerVersion: String?

/// The list of CPU architectures to build for.
public let architectures: [String]?

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

private static func getTargetInfo(swiftCompiler: AbsolutePath) throws -> JSON {
// Call the compiler to get the target info JSON.
let compilerOutput: String
do {
let result = try AsyncProcess.popen(args: swiftCompiler.pathString, "-print-target-info")
compilerOutput = try result.utf8Output().spm_chomp()
} catch {
throw InternalError("Failed to get target info (\(error.interpolationDescription))")
}
// Parse the compiler's JSON output.
do {
return try JSON(string: compilerOutput)
} catch {
throw InternalError(
"Failed to parse target info (\(error.interpolationDescription)).\nRaw compiler output: \(compilerOutput)"
)
}
}

private static func getHostTriple(targetInfo: JSON) throws -> Basics.Triple {
// Get the triple string from the target info.
let tripleString: String
do {
tripleString = try targetInfo.get("target").get("triple")
} catch {
throw InternalError(
"Target info does not contain a triple string (\(error.interpolationDescription)).\nTarget info: \(targetInfo)"
)
}

// Parse the triple string.
do {
return try Triple(tripleString)
} catch {
throw InternalError(
"Failed to parse triple string (\(error.interpolationDescription)).\nTriple string: \(tripleString)"
)
}
}

private static func computeSwiftCompilerVersion(targetInfo: JSON) -> String? {
// Get the compiler version from the target info
let compilerVersion: String
do {
compilerVersion = try targetInfo.get("compilerVersion")
} catch {
return nil
}

// Extract the swift version using regex from the description if available
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm pretty sure what you have captures the necessary variations, but here is what SwiftBuild is doing for the record: https://github.com/swiftlang/swift-build/blob/8e71bde5ac572174bc7e65f505abf3559bf05455/Sources/SWBCore/SpecImplementations/Tools/SwiftCompiler.swift#L3607

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm about to put a patch up for swift-build. It's way too specific right now 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All the mocks failed. Another commit incoming.

do {
let regex = try Regex(#"\((swift(lang)?-[^ )]*)"#)
if let match = try regex.firstMatch(in: compilerVersion), match.count > 1, let substring = match[1].substring {
return String(substring)
}

let regex2 = try Regex(#"\(.*Swift (.*)[ )]"#)
if let match2 = try regex2.firstMatch(in: compilerVersion), match2.count > 1, let substring = match2[1].substring {
return "swift-\(substring)"
} else {
return nil
}
} catch {
return nil
}
}

// MARK: - public API

public static func determineLibrarian(
Expand Down Expand Up @@ -612,8 +683,14 @@ public final class UserToolchain: Toolchain {
default: InstalledSwiftPMConfiguration.default)
}

// Use the triple from Swift SDK or compute the host triple using swiftc.
var triple = try swiftSDK.targetTriple ?? Triple.getHostTriple(usingSwiftCompiler: swiftCompilers.compile)
// targetInfo from the compiler
let targetInfo = try Self.getTargetInfo(swiftCompiler: swiftCompilers.compile)

// Get compiler version information from target info
self.swiftCompilerVersion = Self.computeSwiftCompilerVersion(targetInfo: targetInfo)

// Use the triple from Swift SDK or compute the host triple from the target info
var triple = try swiftSDK.targetTriple ?? Self.getHostTriple(targetInfo: targetInfo)

// Change the triple to the specified arch if there's exactly one of them.
// The Triple property is only looked at by the native build system currently.
Expand Down
14 changes: 11 additions & 3 deletions Sources/Workspace/Workspace+Prebuilts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -127,19 +127,22 @@ extension Workspace {

/// For simplified init in tests
public struct CustomPrebuiltsManager {
let swiftVersion: String
let httpClient: HTTPClient?
let archiver: Archiver?
let useCache: Bool?
let hostPlatform: PrebuiltsManifest.Platform?
let rootCertPath: AbsolutePath?

public init(
swiftVersion: String,
httpClient: HTTPClient? = .none,
archiver: Archiver? = .none,
useCache: Bool? = .none,
hostPlatform: PrebuiltsManifest.Platform? = nil,
rootCertPath: AbsolutePath? = nil
) {
self.swiftVersion = swiftVersion
self.httpClient = httpClient
self.archiver = archiver
self.useCache = useCache
Expand All @@ -153,6 +156,7 @@ extension Workspace {
public typealias Delegate = PrebuiltsManagerDelegate

private let fileSystem: FileSystem
private let swiftVersion: String
private let authorizationProvider: AuthorizationProvider?
private let httpClient: HTTPClient
private let archiver: Archiver
Expand All @@ -167,6 +171,7 @@ extension Workspace {
init(
fileSystem: FileSystem,
hostPlatform: PrebuiltsManifest.Platform,
swiftCompilerVersion: String,
authorizationProvider: AuthorizationProvider?,
scratchPath: AbsolutePath,
cachePath: AbsolutePath?,
Expand All @@ -178,6 +183,7 @@ extension Workspace {
) {
self.fileSystem = fileSystem
self.hostPlatform = hostPlatform
self.swiftVersion = swiftCompilerVersion
self.authorizationProvider = authorizationProvider
self.httpClient = customHTTPClient ?? HTTPClient()

Expand Down Expand Up @@ -222,9 +228,6 @@ extension Workspace {

private let prebuiltPackages: [PrebuiltPackage]

// Version of the compiler we're building against
private let swiftVersion = "\(SwiftVersion.current.major).\(SwiftVersion.current.minor)"

fileprivate func findPrebuilts(packages: [PackageReference]) -> [PrebuiltPackage] {
var prebuilts: [PrebuiltPackage] = []
for packageRef in packages {
Expand Down Expand Up @@ -310,6 +313,11 @@ extension Workspace {
}
}

// Skip prebuilts if this file exists.
if let cachePath, fileSystem.exists(cachePath.appending("noprebuilts")) {
return nil
}

if fileSystem.exists(destination), let manifest = try? await loadManifest() {
return manifest
} else if let cacheDest, fileSystem.exists(cacheDest) {
Expand Down
6 changes: 5 additions & 1 deletion Sources/Workspace/Workspace.swift
Original file line number Diff line number Diff line change
Expand Up @@ -568,7 +568,10 @@ public class Workspace {
// register the binary artifacts downloader with the cancellation handler
cancellator?.register(name: "binary artifacts downloads", handler: binaryArtifactsManager)

if configuration.usePrebuilts, let hostPlatform = customPrebuiltsManager?.hostPlatform ?? PrebuiltsManifest.Platform.hostPlatform {
if configuration.usePrebuilts,
let hostPlatform = customPrebuiltsManager?.hostPlatform ?? PrebuiltsManifest.Platform.hostPlatform,
let swiftCompilerVersion = hostToolchain.swiftCompilerVersion
{
let rootCertPath: AbsolutePath?
if let path = configuration.prebuiltsRootCertPath {
rootCertPath = try AbsolutePath(validating: path)
Expand All @@ -579,6 +582,7 @@ public class Workspace {
let prebuiltsManager = PrebuiltsManager(
fileSystem: fileSystem,
hostPlatform: hostPlatform,
swiftCompilerVersion: customPrebuiltsManager?.swiftVersion ?? swiftCompilerVersion,
authorizationProvider: authorizationProvider,
scratchPath: location.prebuiltsDirectory,
cachePath: customPrebuiltsManager?.useCache == false || !configuration.sharedDependenciesCacheEnabled ? .none : location.sharedPrebuiltsCacheDirectory,
Expand Down
27 changes: 26 additions & 1 deletion Sources/swift-build-prebuilts/BuildPrebuilts.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import ArgumentParser
import Basics
import Foundation
import PackageModel
import struct TSCBasic.ByteString
import struct TSCBasic.SHA256
import Workspace
Expand Down Expand Up @@ -168,7 +169,6 @@ var prebuiltRepos: IdentifiableSet<PrebuiltRepos> = [
),
]

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

@main
Expand Down Expand Up @@ -216,6 +216,21 @@ struct BuildPrebuilts: AsyncParsableCommand {
}
}

func computeSwiftVersion() throws -> String? {
let fileSystem = localFileSystem

let environment = Environment.current
let hostToolchain = try UserToolchain(
swiftSDK: SwiftSDK.hostSwiftSDK(
environment: environment,
fileSystem: fileSystem
),
environment: environment
)

return hostToolchain.swiftCompilerVersion
}

mutating func run() async throws {
if build {
try await build()
Expand All @@ -231,6 +246,11 @@ struct BuildPrebuilts: AsyncParsableCommand {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted

guard let swiftVersion = try computeSwiftVersion() else {
print("Unable to determine swift compiler version")
return
}

print("Stage directory: \(stageDir)")

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

guard let swiftVersion = try computeSwiftVersion() else {
print("Unable to determine swift compiler version")
return
}

for repo in prebuiltRepos.values {
let prebuiltDir = stageDir.appending(repo.url.lastPathComponent)
for version in repo.versions {
Expand Down
11 changes: 11 additions & 0 deletions Tests/WorkspaceTests/PrebuiltsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
hostPlatform: .macos_aarch64,
Expand Down Expand Up @@ -268,6 +269,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
hostPlatform: .macos_aarch64,
Expand Down Expand Up @@ -372,6 +374,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
hostPlatform: .macos_aarch64,
Expand Down Expand Up @@ -434,6 +437,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
hostPlatform: .macos_aarch64,
Expand Down Expand Up @@ -486,6 +490,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
rootCertPath: rootCertPath
Expand Down Expand Up @@ -551,6 +556,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
hostPlatform: .ubuntu_noble_x86_64,
Expand Down Expand Up @@ -600,6 +606,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
rootCertPath: rootCertPath
Expand Down Expand Up @@ -666,6 +673,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
hostPlatform: .macos_aarch64,
Expand Down Expand Up @@ -727,6 +735,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
hostPlatform: .macos_aarch64,
Expand Down Expand Up @@ -790,6 +799,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver,
hostPlatform: .macos_aarch64,
Expand Down Expand Up @@ -846,6 +856,7 @@ final class PrebuiltsTests: XCTestCase {
swiftSyntax
],
prebuiltsManager: .init(
swiftVersion: swiftVersion,
httpClient: httpClient,
archiver: archiver
)
Expand Down