Skip to content

Simplify Swift SDK installation/selection with aliases #8703

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
6 changes: 6 additions & 0 deletions Sources/CoreCommands/Options.swift
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,12 @@ public struct BuildOptions: ParsableArguments {
)
public var swiftSDKSelector: String?

@Option(
name: .customLong("experimental-swift-sdk-alias"),
help: "Alias for a compatible Swift SDK to build with."
)
public var swiftSDKAlias: String?

/// Which compile-time sanitizers should be enabled.
@Option(
name: .customLong("sanitize"),
Expand Down
9 changes: 8 additions & 1 deletion Sources/CoreCommands/SwiftCommandState.swift
Original file line number Diff line number Diff line change
Expand Up @@ -945,6 +945,13 @@ public final class SwiftCommandState {
outputHandler: { print($0.description) }
)

let swiftSDKSelector = if let swiftSDKAliasString = self.options.build.swiftSDKAlias {
try SwiftToolchainVersion(toolchain: hostToolchain, fileSystem: self.fileSystem)
.idForSwiftSDK(aliasString: swiftSDKAliasString)
} else {
self.options.build.swiftSDKSelector ?? self.options.build.deprecatedSwiftSDKSelector
}

swiftSDK = try SwiftSDK.deriveTargetSwiftSDK(
hostSwiftSDK: hostSwiftSDK,
hostTriple: hostToolchain.targetTriple,
Expand All @@ -953,7 +960,7 @@ public final class SwiftCommandState {
customCompileTriple: self.options.build.customCompileTriple,
customCompileToolchain: self.options.build.customCompileToolchain,
customCompileSDK: self.options.build.customCompileSDK,
swiftSDKSelector: self.options.build.swiftSDKSelector ?? self.options.build.deprecatedSwiftSDKSelector,
swiftSDKSelector: swiftSDKSelector,
architectures: self.options.build.architectures,
store: store,
observabilityScope: self.observabilityScope,
Expand Down
2 changes: 2 additions & 0 deletions Sources/PackageModel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,11 @@ add_library(PackageModel
SupportedLanguageExtension.swift
SwiftLanguageVersion.swift
SwiftSDKs/SwiftSDK.swift
SwiftSDKs/SwiftSDKAlias.swift
SwiftSDKs/SwiftSDKConfigurationStore.swift
SwiftSDKs/SwiftSDKBundle.swift
SwiftSDKs/SwiftSDKBundleStore.swift
SwiftToolchainVersion.swift
Toolchain.swift
ToolchainConfiguration.swift
Toolchain+SupportedFeatures.swift
Expand Down
92 changes: 92 additions & 0 deletions Sources/PackageModel/SwiftSDKs/SwiftSDKAlias.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

package struct SwiftSDKAlias {
init?(_ string: String) {
guard let kind = Kind(rawValue: string) else { return nil }
self.kind = kind
}

enum Kind: String, CaseIterable {
case staticLinux = "static-linux"
case wasi = "wasi"
case wasiEmbedded = "embedded-wasi"

var urlFileComponent: String {
switch self {
case .staticLinux, .wasi:
return self.rawValue
case .wasiEmbedded:
return Self.wasi.rawValue
}
}

var idComponent: String {
self.rawValue
}

var urlDirComponent: String {
switch self {
case .staticLinux:
"static-sdk"
case .wasi, .wasiEmbedded:
"wasi"
}
}
}

struct Version: CustomStringConvertible {
let rawValue = "0.0.1"

var description: String { self.rawValue }
}

let kind: Kind
let defaultVersion = Version()

var urlFileComponent: String {
"\(self.kind.urlFileComponent)-\(self.defaultVersion.rawValue)"
}
}

extension SwiftToolchainVersion {
package func urlForSwiftSDK(aliasString: String) throws -> String {
guard let swiftSDKAlias = SwiftSDKAlias(aliasString) else {
throw Error.unknownSwiftSDKAlias(aliasString)
}

return """
https://download.swift.org/\(
self.branch
)/\(
swiftSDKAlias.kind.urlDirComponent
)/\(
self.tag
)/\(
self.tag
)_\(swiftSDKAlias.urlFileComponent).artifactbundle.tar.gz
"""
}

package func idForSwiftSDK(aliasString: String) throws -> String {
guard let swiftSDKAlias = SwiftSDKAlias(aliasString) else {
throw Error.unknownSwiftSDKAlias(aliasString)
}

switch swiftSDKAlias.kind {
case .staticLinux:
return "\(self.tag)_\(swiftSDKAlias.kind.idComponent)-\(swiftSDKAlias.defaultVersion)"
case .wasi, .wasiEmbedded:
return "\(self.tag.replacing("swift-", with: ""))-wasm32-\(swiftSDKAlias.kind.idComponent)"
}
}
}
152 changes: 152 additions & 0 deletions Sources/PackageModel/SwiftToolchainVersion.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

import Basics
import class Foundation.JSONDecoder
import struct Foundation.URL

package struct SwiftToolchainVersion: Equatable, Decodable {
package enum Error: Swift.Error, Equatable, CustomStringConvertible {
case versionMetadataNotFound(AbsolutePath)
case unknownSwiftSDKAlias(String)

package var description: String {
switch self {
case .versionMetadataNotFound(let absolutePath):
"""
Toolchain version metadata file not found at path `\(absolutePath.pathString)`. \
Install a newer version of the Swift toolchain that includes this file.
"""
case .unknownSwiftSDKAlias(let string):
"""
Unknown alias for a Swift SDK: `\(string)`. Supported aliases: \(
SwiftSDKAlias.Kind.allCases.map { "`\($0.rawValue)`" }.joined(separator: ", ")
).
"""
}
}
}

package init(
tag: String,
branch: String,
architecture: SwiftToolchainVersion.Architecture,
platform: SwiftToolchainVersion.Platform
) {
self.tag = tag
self.branch = branch
self.architecture = architecture
self.platform = platform
}

/// Since triples don't encode the platform, we use platform identifiers
/// that match swift.org toolchain distribution names.
package enum Platform: String, Decodable {
case macOS
case ubuntu2004
case ubuntu2204
case ubuntu2404
case debian12
case amazonLinux2
case fedora39
case fedora41
case ubi9

var urlDirComponent: String {
switch self {
case .macOS:
"xcode"
case .ubuntu2004:
"ubuntu2004"
case .ubuntu2204:
"ubuntu2204"
case .ubuntu2404:
"ubuntu2404"
case .debian12:
"debian12"
case .amazonLinux2:
"amazonlinux2"
case .fedora39:
"fedora39"
case .fedora41:
"fedora41"
case .ubi9:
"ubi9"
}
}

var urlFileComponent: String {
switch self {
case .macOS:
"osx"
case .ubuntu2004:
"ubuntu20.04"
case .ubuntu2204:
"ubuntu22.04"
case .ubuntu2404:
"ubuntu24.04"
case .debian12:
"debian12"
case .amazonLinux2:
"amazonlinux2"
case .fedora39:
"fedora39"
case .fedora41:
"fedora41"
case .ubi9:
"ubi9"
}
}
}

package enum Architecture: String, Decodable {
case aarch64
case x86_64

var urlFileComponent: String {
switch self {
case .aarch64:
"-aarch64"
case .x86_64:
""
}
}
}

/// A Git tag from which this toolchain was built.
package let tag: String

/// Branch from which this toolchain was built.
package let branch: String

/// CPU architecture on which this toolchain runs.
package let architecture: Architecture

/// Platform identifier on which this toolchain runs.
package let platform: Platform

package init(toolchain: some Toolchain, fileSystem: any FileSystem) throws {
let versionMetadataPath = try toolchain.swiftCompilerPath.parentDirectory.parentDirectory.appending(
RelativePath(validating: "lib/swift/version.json")
)
guard fileSystem.exists(versionMetadataPath) else {
throw Error.versionMetadataNotFound(versionMetadataPath)
}

self = try JSONDecoder().decode(
path: versionMetadataPath,
fileSystem: fileSystem,
as: Self.self
)
}
}

8 changes: 4 additions & 4 deletions Sources/SwiftSDKCommand/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ add_library(SwiftSDKCommand
Configuration/ResetConfiguration.swift
Configuration/SetConfiguration.swift
Configuration/ShowConfiguration.swift
ConfigureSwiftSDK.swift
SwiftSDKConfigure.swift
SwiftSDKSubcommand.swift
InstallSwiftSDK.swift
ListSwiftSDKs.swift
RemoveSwiftSDK.swift
SwiftSDKInstall.swift
SwiftSDKList.swift
SwiftSDKRemove.swift
SwiftSDKCommand.swift)
target_link_libraries(SwiftSDKCommand PUBLIC
ArgumentParser
Expand Down
10 changes: 5 additions & 5 deletions Sources/SwiftSDKCommand/SwiftSDKCommand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2022-2023 Apple Inc. and the Swift project authors
// Copyright (c) 2022-2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
Expand All @@ -20,11 +20,11 @@ package struct SwiftSDKCommand: AsyncParsableCommand {
abstract: "Perform operations on Swift SDKs.",
version: SwiftVersion.current.completeDisplayString,
subcommands: [
ConfigureSwiftSDK.self,
SwiftSDKConfigure.self,
DeprecatedSwiftSDKConfigurationCommand.self,
InstallSwiftSDK.self,
ListSwiftSDKs.self,
RemoveSwiftSDK.self,
SwiftSDKInstall.self,
SwiftSDKList.self,
SwiftSDKRemove.self,
],
helpNames: [.short, .long, .customLong("help", withSingleDash: true)]
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
//
// This source file is part of the Swift open source project
//
// Copyright (c) 2023 Apple Inc. and the Swift project authors
// Copyright (c) 2023-2025 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
Expand All @@ -18,7 +18,7 @@ import PackageModel

import var TSCBasic.stdoutStream

struct ConfigureSwiftSDK: AsyncParsableCommand {
struct SwiftSDKConfigure: AsyncParsableCommand {
static let configuration = CommandConfiguration(
commandName: "configure",
abstract: """
Expand Down
Loading