From 85d78e386a59a06ff8d2bbf27e563c625f945d34 Mon Sep 17 00:00:00 2001 From: Marc Prud'hommeaux Date: Sun, 13 Jul 2025 14:27:37 -0400 Subject: [PATCH 1/2] Allow omitting the target triple for `swift sdk configure` (#8856) You can now run `swift sdk configure --sdk-root-path /some/sdk/root sdk-id` without needing to specify the target triple The Android SDK (https://github.com/swiftlang/swift/issues/80788#issuecomment-2840356225) needs to have its `sdkRootPath` configured to point to an external NDK sysroot. But it contains many different target triples, like `armv7-unknown-linux-androideabi33` and `x86_64-unknown-linux-android29`, all of which need to be configured to point to the same sysroot. Following on to https://github.com/swiftlang/swift-package-manager/pull/8687 and https://github.com/swiftlang/swift-evolution/pull/2888, this PR enables running a single command to configure the entire SDK, like so: ``` swift run swift-sdk configure --sdk-root-path ~/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot swift-6.1-RELEASE-android-24-0.1 ``` This will result in configurations being created for each of the target triples for the SDK ID.
``` % swift run swift-sdk configure --sdk-root-path ~/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot swift-6.1-RELEASE-android-24-0.1 info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android25` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi33` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android26` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi35` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android31` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi29` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android32` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android34` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android27` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi25` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi26` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi24` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android33` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android35` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android29` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android32` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android25` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android24` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi30` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android26` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android33` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi32` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android35` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android34` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android31` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android28` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android27` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android24` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android30` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi28` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `x86_64-unknown-linux-android28` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi27` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android30` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi34` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `armv7-unknown-linux-androideabi31` were successfully updated: sdkRootPath. info: These properties of Swift SDK `swift-6.1-RELEASE-android-24-0.1` for target triple `aarch64-unknown-linux-android29` were successfully updated: sdkRootPath. % ls ~/Library/org.swift.swiftpm/swift-sdks/configuration/ swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android24.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android25.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android26.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android27.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android28.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android29.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android30.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android31.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android32.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android33.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android34.json swift-6.1-RELEASE-android-24-0.1_aarch64-unknown-linux-android35.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi24.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi25.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi26.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi27.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi28.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi29.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi30.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi31.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi32.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi33.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi34.json swift-6.1-RELEASE-android-24-0.1_armv7-unknown-linux-androideabi35.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android24.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android25.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android26.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android27.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android28.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android29.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android30.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android31.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android32.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android33.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android34.json swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android35.json % cat ~/Library/org.swift.swiftpm/swift-sdks/configuration/swift-6.1-RELEASE-android-24-0.1_x86_64-unknown-linux-android31.json { "sdkRootPath" : "/Users/marc/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot", "swiftResourcesPath" : "/Users/marc/Library/org.swift.swiftpm/swift-sdks/swift-6.1-RELEASE-android-24-0.1.artifactbundle/swift-6.1-release-android-24-sdk/android-27c-sysroot/usr/lib/swift", "swiftStaticResourcesPath" : "/Users/marc/Library/org.swift.swiftpm/swift-sdks/swift-6.1-RELEASE-android-24-0.1.artifactbundle/swift-6.1-release-android-24-sdk/android-27c-sysroot/usr/lib/swift_static-x86_64", "toolsetPaths" : [ "/Users/marc/Library/org.swift.swiftpm/swift-sdks/swift-6.1-RELEASE-android-24-0.1.artifactbundle/swift-6.1-release-android-24-sdk/swift-toolset.json" ] } ```
In addition, this fixes https://github.com/swiftlang/swift-package-manager/issues/8584, where `swift sdk configure` with a specified target triple didn't work at all, and instead would configure some random target triple for the specified SDK. Make the `targetTriple` flag for `ConfigureSwiftSDK` optional and, when nil, apply the command to all triples included in the specified SDK. Fixes https://github.com/swiftlang/swift-package-manager/issues/8584 and enables configuring multiple target triples with a single `swift sdk configure` command. --------- Co-authored-by: Max Desiatov (cherry picked from commit 69f851d48eacdfc19c2cb94eb61e4c0858713474) --- .../Documentation.docc/SDK/SDKConfigure.md | 16 +- Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 222 ++++++++++-------- .../SwiftSDKs/SwiftSDKBundleStore.swift | 2 +- .../SwiftSDKConfigurationStore.swift | 112 +++++++++ .../SwiftSDKCommand/ConfigureSwiftSDK.swift | 120 ++-------- .../SwiftSDKBundleTests.swift | 116 +++++++++ 6 files changed, 379 insertions(+), 209 deletions(-) diff --git a/Sources/PackageManagerDocs/Documentation.docc/SDK/SDKConfigure.md b/Sources/PackageManagerDocs/Documentation.docc/SDK/SDKConfigure.md index 5198bcdf4ee..82554186134 100644 --- a/Sources/PackageManagerDocs/Documentation.docc/SDK/SDKConfigure.md +++ b/Sources/PackageManagerDocs/Documentation.docc/SDK/SDKConfigure.md @@ -8,7 +8,21 @@ Manages configuration options for installed Swift SDKs. ``` -sdk configure [--package-path=] [--cache-path=] [--config-path=] [--security-path=] [--scratch-path=] [--swift-sdks-path=] [--toolset=...] [--pkg-config-path=...] [--sdk-root-path=] [--swift-resources-path=] [--swift-static-resources-path=] [--include-search-path=...] [--library-search-path=...] [--toolset-path=...] [--reset] [--show-configuration] [--version] [--help] +sdk configure [--package-path=] + [--cache-path=] [--config-path=] + [--security-path=] + [--scratch-path=] + [--swift-sdks-path=] + [--toolset=...] + [--pkg-config-path=...] + [--sdk-root-path=] + [--swift-resources-path=] + [--swift-static-resources-path=] + [--include-search-path=...] + [--library-search-path=...] + [--toolset-path=...] [--reset] + [--show-configuration] [target-triple...] [--version] + [--help] ``` - term **--package-path=\**: diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index cdcdfd781f4..d04216cc651 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -53,7 +53,7 @@ public enum SwiftSDKError: Swift.Error { case unserializableMetadata /// No configuration values are available for this Swift SDK and target triple. - case swiftSDKNotFound(artifactID: String, hostTriple: Triple, targetTriple: Triple) + case swiftSDKNotFound(artifactID: String, hostTriple: Triple, targetTriple: Triple?) /// A Swift SDK bundle with this name is already installed, can't install a new bundle with the same name. case swiftSDKBundleAlreadyInstalled(bundleName: String) @@ -108,10 +108,16 @@ extension SwiftSDKError: CustomStringConvertible { properties required for initialization """ case .swiftSDKNotFound(let artifactID, let hostTriple, let targetTriple): - return """ - Swift SDK with ID `\(artifactID)`, host triple \(hostTriple), and target triple \(targetTriple) is not \ - currently installed. - """ + if let targetTriple { + return """ + Swift SDK with ID `\(artifactID)`, host triple \(hostTriple), and target triple \(targetTriple) is not \ + currently installed. + """ + } else { + return """ + Swift SDK with ID `\(artifactID)` is not currently installed. + """ + } case .swiftSDKBundleAlreadyInstalled(let bundleName): return """ Swift SDK bundle with name `\(bundleName)` is already installed. Can't install a new bundle \ @@ -259,14 +265,17 @@ public struct SwiftSDK: Equatable { /// deserialization. public private(set) var toolset: Toolset - public struct PathsConfiguration: Equatable { + /// The paths associated with a Swift SDK. The Path type can be a `String` + /// to encapsulate the arguments for the `SwiftSDKConfigurationStore.configure` + /// function, or can be a fully-realized `AbsolutePath` when deserialized from a configuration. + public struct PathsConfiguration: Equatable { public init( - sdkRootPath: Basics.AbsolutePath?, - swiftResourcesPath: Basics.AbsolutePath? = nil, - swiftStaticResourcesPath: Basics.AbsolutePath? = nil, - includeSearchPaths: [Basics.AbsolutePath]? = nil, - librarySearchPaths: [Basics.AbsolutePath]? = nil, - toolsetPaths: [Basics.AbsolutePath]? = nil + sdkRootPath: Path? = nil, + swiftResourcesPath: Path? = nil, + swiftStaticResourcesPath: Path? = nil, + includeSearchPaths: [Path]? = nil, + librarySearchPaths: [Path]? = nil, + toolsetPaths: [Path]? = nil ) { self.sdkRootPath = sdkRootPath self.swiftResourcesPath = swiftResourcesPath @@ -277,22 +286,22 @@ public struct SwiftSDK: Equatable { } /// Root directory path of the SDK used to compile for the target triple. - public var sdkRootPath: Basics.AbsolutePath? + public var sdkRootPath: Path? /// Path containing Swift resources for dynamic linking. - public var swiftResourcesPath: Basics.AbsolutePath? + public var swiftResourcesPath: Path? /// Path containing Swift resources for static linking. - public var swiftStaticResourcesPath: Basics.AbsolutePath? + public var swiftStaticResourcesPath: Path? /// Array of paths containing headers. - public var includeSearchPaths: [Basics.AbsolutePath]? + public var includeSearchPaths: [Path]? /// Array of paths containing libraries. - public var librarySearchPaths: [Basics.AbsolutePath]? + public var librarySearchPaths: [Path]? /// Array of paths containing toolset files. - public var toolsetPaths: [Basics.AbsolutePath]? + public var toolsetPaths: [Path]? /// Initialize paths configuration from values deserialized using v3 schema. /// - Parameters: @@ -301,92 +310,53 @@ public struct SwiftSDK: Equatable { fileprivate init( _ properties: SerializedDestinationV3.TripleProperties, swiftSDKDirectory: Basics.AbsolutePath? = nil - ) throws { - if let swiftSDKDirectory { - self.init( - sdkRootPath: try AbsolutePath(validating: properties.sdkRootPath, relativeTo: swiftSDKDirectory), - swiftResourcesPath: try properties.swiftResourcesPath.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - }, - swiftStaticResourcesPath: try properties.swiftStaticResourcesPath.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - }, - includeSearchPaths: try properties.includeSearchPaths?.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - }, - librarySearchPaths: try properties.librarySearchPaths?.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - }, - toolsetPaths: try properties.toolsetPaths?.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - } - ) - } else { - self.init( - sdkRootPath: try AbsolutePath(validating: properties.sdkRootPath), - swiftResourcesPath: try properties.swiftResourcesPath.map { - try AbsolutePath(validating: $0) - }, - swiftStaticResourcesPath: try properties.swiftStaticResourcesPath.map { - try AbsolutePath(validating: $0) - }, - includeSearchPaths: try properties.includeSearchPaths?.map { - try AbsolutePath(validating: $0) - }, - librarySearchPaths: try properties.librarySearchPaths?.map { - try AbsolutePath(validating: $0) - }, - toolsetPaths: try properties.toolsetPaths?.map { - try AbsolutePath(validating: $0) - } - ) - } + ) throws where Path == Basics.AbsolutePath { + self.init( + sdkRootPath: try AbsolutePath(validating: properties.sdkRootPath, relativeTo: swiftSDKDirectory), + swiftResourcesPath: try properties.swiftResourcesPath.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, + swiftStaticResourcesPath: try properties.swiftStaticResourcesPath.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, + includeSearchPaths: try properties.includeSearchPaths?.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, + librarySearchPaths: try properties.librarySearchPaths?.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, + toolsetPaths: try properties.toolsetPaths?.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + } + ) } /// Initialize paths configuration from values deserialized using v4 schema. /// - Parameters: /// - properties: properties of a Swift SDK for the given triple. /// - swiftSDKDirectory: directory used for converting relative paths in `properties` to absolute paths. - fileprivate init(_ properties: SwiftSDKMetadataV4.TripleProperties, swiftSDKDirectory: Basics.AbsolutePath? = nil) throws { - if let swiftSDKDirectory { - self.init( - sdkRootPath: try AbsolutePath(validating: properties.sdkRootPath, relativeTo: swiftSDKDirectory), - swiftResourcesPath: try properties.swiftResourcesPath.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - }, - swiftStaticResourcesPath: try properties.swiftStaticResourcesPath.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - }, - includeSearchPaths: try properties.includeSearchPaths?.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - }, - librarySearchPaths: try properties.librarySearchPaths?.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - }, - toolsetPaths: try properties.toolsetPaths?.map { - try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) - } - ) - } else { - self.init( - sdkRootPath: try AbsolutePath(validating: properties.sdkRootPath), - swiftResourcesPath: try properties.swiftResourcesPath.map { - try AbsolutePath(validating: $0) - }, - swiftStaticResourcesPath: try properties.swiftStaticResourcesPath.map { - try AbsolutePath(validating: $0) - }, - includeSearchPaths: try properties.includeSearchPaths?.map { - try AbsolutePath(validating: $0) - }, - librarySearchPaths: try properties.librarySearchPaths?.map { - try AbsolutePath(validating: $0) - }, - toolsetPaths: try properties.toolsetPaths?.map { - try AbsolutePath(validating: $0) - } - ) - } + fileprivate init( + _ properties: SwiftSDKMetadataV4.TripleProperties, + swiftSDKDirectory: Basics.AbsolutePath? = nil + ) throws where Path == Basics.AbsolutePath { + self.init( + sdkRootPath: try AbsolutePath(validating: properties.sdkRootPath, relativeTo: swiftSDKDirectory), + swiftResourcesPath: try properties.swiftResourcesPath.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, + swiftStaticResourcesPath: try properties.swiftStaticResourcesPath.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, + includeSearchPaths: try properties.includeSearchPaths?.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, + librarySearchPaths: try properties.librarySearchPaths?.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, + toolsetPaths: try properties.toolsetPaths?.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + } + ) } public mutating func merge(with newConfiguration: Self) { @@ -414,10 +384,48 @@ public struct SwiftSDK: Equatable { self.toolsetPaths = toolsetPaths } } + + mutating func merge( + with newConfiguration: PathsConfiguration, + relativeTo basePath: Path? + ) throws -> [String] where Path == Basics.AbsolutePath { + var updatedProperties: [String] = [] + if let sdkRootPath = newConfiguration.sdkRootPath { + self.sdkRootPath = try AbsolutePath(validating: sdkRootPath, relativeTo: basePath) + updatedProperties.append("sdkRootPath") + } + + if let swiftResourcesPath = newConfiguration.swiftResourcesPath { + self.swiftResourcesPath = try AbsolutePath(validating: swiftResourcesPath, relativeTo: basePath) + updatedProperties.append("swiftResourcesPath") + } + + if let swiftStaticResourcesPath = newConfiguration.swiftStaticResourcesPath { + self.swiftResourcesPath = try AbsolutePath(validating: swiftStaticResourcesPath, relativeTo: basePath) + updatedProperties.append("swiftStaticResourcesPath") + } + + if let includeSearchPaths = newConfiguration.includeSearchPaths, !includeSearchPaths.isEmpty { + self.includeSearchPaths = try includeSearchPaths.map { try AbsolutePath(validating: $0, relativeTo: basePath) } + updatedProperties.append("includeSearchPath") + } + + if let librarySearchPaths = newConfiguration.librarySearchPaths, !librarySearchPaths.isEmpty { + self.librarySearchPaths = try librarySearchPaths.map { try AbsolutePath(validating: $0, relativeTo: basePath) } + updatedProperties.append("librarySearchPath") + } + + if let toolsetPaths = newConfiguration.toolsetPaths, !toolsetPaths.isEmpty { + self.toolsetPaths = try toolsetPaths.map { try AbsolutePath(validating: $0, relativeTo: basePath) } + updatedProperties.append("toolsetPath") + } + + return updatedProperties + } } /// Configuration of file system paths used by this Swift SDK when building. - public var pathsConfiguration: PathsConfiguration + public var pathsConfiguration: PathsConfiguration /// Creates a Swift SDK with the specified properties. @available(*, deprecated, message: "use `init(targetTriple:sdkRootDir:toolset:)` instead") @@ -464,7 +472,7 @@ public struct SwiftSDK: Equatable { hostTriple: Triple? = nil, targetTriple: Triple? = nil, toolset: Toolset, - pathsConfiguration: PathsConfiguration, + pathsConfiguration: PathsConfiguration, supportsTesting: Bool ) { let xctestSupport: XCTestSupport @@ -489,7 +497,7 @@ public struct SwiftSDK: Equatable { hostTriple: Triple? = nil, targetTriple: Triple? = nil, toolset: Toolset, - pathsConfiguration: PathsConfiguration, + pathsConfiguration: PathsConfiguration, xctestSupport: XCTestSupport = .supported ) { self.hostTriple = hostTriple @@ -1201,7 +1209,7 @@ extension Optional where Wrapped == [Basics.AbsolutePath] { } } -extension SwiftSDK.PathsConfiguration: CustomStringConvertible { +extension SwiftSDK.PathsConfiguration: CustomStringConvertible where Path == Basics.AbsolutePath { public var description: String { """ sdkRootPath: \(sdkRootPath.configurationString) @@ -1213,3 +1221,13 @@ extension SwiftSDK.PathsConfiguration: CustomStringConvertible { """ } } + +extension Basics.AbsolutePath { + fileprivate init(validating string: String, relativeTo basePath: Basics.AbsolutePath?) throws { + if let basePath { + try self.init(validating: string, relativeTo: basePath) + } else { + try self.init(validating: string) + } + } +} diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift index 39bc3d22a43..5b506feba78 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift @@ -70,7 +70,7 @@ public final class SwiftSDKBundleStore { let fileSystem: any FileSystem /// Observability scope used for logging. - private let observabilityScope: ObservabilityScope + let observabilityScope: ObservabilityScope /// Closure invoked for output produced by this store during its operation. private let outputHandler: (Output) -> Void diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKConfigurationStore.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKConfigurationStore.swift index 105fae09b6c..6f446551233 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKConfigurationStore.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKConfigurationStore.swift @@ -85,6 +85,22 @@ public final class SwiftSDKConfigurationStore { try encoder.encode(path: configurationPath, fileSystem: fileSystem, properties) } + private func swiftSDKs(for id: String) throws -> [SwiftSDK] { + for bundle in try self.swiftSDKBundleStore.allValidBundles { + for (artifactID, variants) in bundle.artifacts { + guard artifactID == id else { + continue + } + + for variant in variants { + return variant.swiftSDKs + } + } + } + + return [] + } + public func readConfiguration( sdkID: String, targetTriple: Triple @@ -141,4 +157,100 @@ public final class SwiftSDKConfigurationStore { try fileSystem.removeFileTree(configurationPath) return true } + + /// Configures the specified Swift SDK and identified target triple with the configuration parameter. + /// - Parameters: + /// - sdkID: ID of the Swift SDK to operate on. + /// - tripleString: run-time triple for which the properties should be configured, or nil to configure all triples for the Swift SDK + /// - showConfiguration: if true, simply print the current configuration for the target triple(s) + /// - resetConfiguration: if true, reset the configuration for the target triple(s) + /// - config: the configuration parameters to set for for the target triple(s) + /// - Returns: `true` if custom configuration was successful, `false` if no configuration was performed. + package func configure( + sdkID: String, + targetTriple: String?, + showConfiguration: Bool, + resetConfiguration: Bool, + config: SwiftSDK.PathsConfiguration + ) throws -> Bool { + let targetTriples: [Triple] + if let targetTriple = targetTriple { + targetTriples = try [Triple(targetTriple)] + } else { + // when `targetTriple` is unspecified, configure every triple for the SDK + let validBundles = try self.swiftSDKs(for: sdkID) + targetTriples = validBundles.compactMap(\.targetTriple) + if targetTriples.isEmpty { + throw SwiftSDKError.swiftSDKNotFound( + artifactID: sdkID, + hostTriple: hostTriple, + targetTriple: nil + ) + } + } + + for targetTriple in targetTriples { + guard let swiftSDK = try self.readConfiguration( + sdkID: sdkID, + targetTriple: targetTriple + ) else { + throw SwiftSDKError.swiftSDKNotFound( + artifactID: sdkID, + hostTriple: hostTriple, + targetTriple: targetTriple + ) + } + + if showConfiguration { + print(swiftSDK.pathsConfiguration) + continue + } + + if resetConfiguration { + if try !self.resetConfiguration(sdkID: sdkID, targetTriple: targetTriple) { + swiftSDKBundleStore.observabilityScope.emit( + warning: "No configuration for Swift SDK `\(sdkID)`" + ) + } else { + swiftSDKBundleStore.observabilityScope.emit( + info: """ + All configuration properties of Swift SDK `\(sdkID)` for target triple \ + `\(targetTriple)` were successfully reset. + """ + ) + } + } else { + var configuration = swiftSDK.pathsConfiguration + let updatedProperties = try configuration.merge(with: config, relativeTo: fileSystem.currentWorkingDirectory) + + guard !updatedProperties.isEmpty else { + swiftSDKBundleStore.observabilityScope.emit( + error: """ + No properties of Swift SDK `\(sdkID)` for target triple `\(targetTriple)` were updated \ + since none were specified. Pass `--help` flag to see the list of all available properties. + """ + ) + return false + } + + var swiftSDK = swiftSDK + swiftSDK.pathsConfiguration = configuration + swiftSDK.targetTriple = targetTriple + try self.updateConfiguration(sdkID: sdkID, swiftSDK: swiftSDK) + + swiftSDKBundleStore.observabilityScope.emit( + info: """ + These properties of Swift SDK `\(sdkID)` for target triple \ + `\(targetTriple)` were successfully updated: \(updatedProperties.joined(separator: ", ")). + """ + ) + } + + if swiftSDKBundleStore.observabilityScope.errorsReported { + return false + } + } + + return true + } } diff --git a/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift b/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift index 63503962043..1c6f9512e93 100644 --- a/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift +++ b/Sources/SwiftSDKCommand/ConfigureSwiftSDK.swift @@ -90,7 +90,7 @@ struct ConfigureSwiftSDK: AsyncParsableCommand { var sdkID: String @Argument(help: "The target triple of the Swift SDK to configure.") - var targetTriple: String + var targetTriple: String? /// The file system used by default by this command. private var fileSystem: FileSystem { localFileSystem } @@ -132,101 +132,21 @@ struct ConfigureSwiftSDK: AsyncParsableCommand { hostTimeTriple: triple, swiftSDKBundleStore: bundleStore ) - let targetTriple = try Triple(self.targetTriple) - - guard let swiftSDK = try configurationStore.readConfiguration( + let config = SwiftSDK.PathsConfiguration( + sdkRootPath: self.sdkRootPath, + swiftResourcesPath: self.swiftResourcesPath, + swiftStaticResourcesPath: self.swiftStaticResourcesPath, + includeSearchPaths: self.includeSearchPath, + librarySearchPaths: self.librarySearchPath, + toolsetPaths: self.toolsetPath + ) + if try !configurationStore.configure( sdkID: sdkID, - targetTriple: targetTriple - ) else { - throw SwiftSDKError.swiftSDKNotFound( - artifactID: sdkID, - hostTriple: triple, - targetTriple: targetTriple - ) - } - - if self.shouldShowConfiguration { - print(swiftSDK.pathsConfiguration) - return - } - - var configuration = swiftSDK.pathsConfiguration - if self.shouldReset { - if try !configurationStore.resetConfiguration(sdkID: sdkID, targetTriple: targetTriple) { - observabilityScope.emit( - warning: "No configuration for Swift SDK `\(sdkID)`" - ) - } else { - observabilityScope.emit( - info: """ - All configuration properties of Swift SDK `\(sdkID)` for target triple \ - `\(targetTriple)` were successfully reset. - """ - ) - } - } else { - var updatedProperties = [String]() - - let currentWorkingDirectory: AbsolutePath? = fileSystem.currentWorkingDirectory - - if let sdkRootPath { - configuration.sdkRootPath = try AbsolutePath(validating: sdkRootPath, relativeTo: currentWorkingDirectory) - updatedProperties.append(CodingKeys.sdkRootPath.stringValue) - } - - if let swiftResourcesPath { - configuration.swiftResourcesPath = - try AbsolutePath(validating: swiftResourcesPath, relativeTo: currentWorkingDirectory) - updatedProperties.append(CodingKeys.swiftResourcesPath.stringValue) - } - - if let swiftStaticResourcesPath { - configuration.swiftResourcesPath = - try AbsolutePath(validating: swiftStaticResourcesPath, relativeTo: currentWorkingDirectory) - updatedProperties.append(CodingKeys.swiftStaticResourcesPath.stringValue) - } - - if !includeSearchPath.isEmpty { - configuration.includeSearchPaths = - try includeSearchPath.map { try AbsolutePath(validating: $0, relativeTo: currentWorkingDirectory) } - updatedProperties.append(CodingKeys.includeSearchPath.stringValue) - } - - if !librarySearchPath.isEmpty { - configuration.librarySearchPaths = - try librarySearchPath.map { try AbsolutePath(validating: $0, relativeTo: currentWorkingDirectory) } - updatedProperties.append(CodingKeys.librarySearchPath.stringValue) - } - - if !toolsetPath.isEmpty { - configuration.toolsetPaths = - try toolsetPath.map { try AbsolutePath(validating: $0, relativeTo: currentWorkingDirectory) } - updatedProperties.append(CodingKeys.toolsetPath.stringValue) - } - - guard !updatedProperties.isEmpty else { - observabilityScope.emit( - error: """ - No properties of Swift SDK `\(sdkID)` for target triple `\(targetTriple)` were updated \ - since none were specified. Pass `--help` flag to see the list of all available properties. - """ - ) - return - } - - var swiftSDK = swiftSDK - swiftSDK.pathsConfiguration = configuration - try configurationStore.updateConfiguration(sdkID: sdkID, swiftSDK: swiftSDK) - - observabilityScope.emit( - info: """ - These properties of Swift SDK `\(sdkID)` for target triple \ - `\(targetTriple)` were successfully updated: \(updatedProperties.joined(separator: ", ")). - """ - ) - } - - if observabilityScope.errorsReported { + targetTriple: targetTriple, + showConfiguration: shouldShowConfiguration, + resetConfiguration: shouldReset, + config: config + ) { throw ExitCode.failure } } catch { @@ -241,13 +161,3 @@ struct ConfigureSwiftSDK: AsyncParsableCommand { } } } - -extension AbsolutePath { - fileprivate init(validating string: String, relativeTo basePath: AbsolutePath?) throws { - if let basePath { - try self.init(validating: string, relativeTo: basePath) - } else { - try self.init(validating: string) - } - } -} diff --git a/Tests/PackageModelTests/SwiftSDKBundleTests.swift b/Tests/PackageModelTests/SwiftSDKBundleTests.swift index a65ed0df18a..8262336c8c3 100644 --- a/Tests/PackageModelTests/SwiftSDKBundleTests.swift +++ b/Tests/PackageModelTests/SwiftSDKBundleTests.swift @@ -551,4 +551,120 @@ final class SwiftSDKBundleTests: XCTestCase { ), ]) } + + func testConfigureSDKRootPath() async throws { + func createConfigurationStore() async throws -> (SwiftSDKConfigurationStore, FileSystem) { + let (fileSystem, bundles, swiftSDKsDirectory) = try generateTestFileSystem( + bundleArtifacts: [ + .init(id: testArtifactID, supportedTriples: [arm64Triple, i686Triple]), + ] + ) + let system = ObservabilitySystem.makeForTesting() + + var output = [SwiftSDKBundleStore.Output]() + let store = SwiftSDKBundleStore( + swiftSDKsDirectory: swiftSDKsDirectory, + hostToolchainBinDir: "/tmp", + fileSystem: fileSystem, + observabilityScope: system.topScope, + outputHandler: { + output.append($0) + } + ) + + let archiver = MockArchiver() + for bundle in bundles { + try await store.install(bundlePathOrURL: bundle.path, archiver) + } + + let hostTriple = try Triple("arm64-apple-macosx14.0") + let sdk = try store.selectBundle( + matching: testArtifactID, + hostTriple: hostTriple + ) + + XCTAssertEqual(sdk.targetTriple, targetTriple) + XCTAssertEqual(output, [ + .installationSuccessful( + bundlePathOrURL: bundles[0].path, + bundleName: AbsolutePath(bundles[0].path).components.last! + ) + ]) + + let config = try SwiftSDKConfigurationStore( + hostTimeTriple: hostTriple, + swiftSDKBundleStore: store + ) + + return (config, fileSystem) + } + + do { + let (config, _) = try await createConfigurationStore() + let args = SwiftSDK.PathsConfiguration() + let configSuccess = try config.configure( + sdkID: testArtifactID, + targetTriple: nil, + showConfiguration: false, + resetConfiguration: false, + config: args + ) + XCTAssertEqual(configSuccess, false, "Expected failure for SwiftSDKConfigurationStore.configure with no updated properties") + } + + let targetTripleConfigPath = AbsolutePath("/sdks/configuration/\(testArtifactID)_\(targetTriple.tripleString).json") + + #if os(Windows) + let sdkRootPath = "C:\\some\\sdk\\root\\path" + #else + let sdkRootPath = "/some/sdk/root/path" + #endif + + do { + let (config, fileSystem) = try await createConfigurationStore() + var args = SwiftSDK.PathsConfiguration() + args.sdkRootPath = sdkRootPath + let configSuccess = try config.configure( + sdkID: testArtifactID, + targetTriple: targetTriple.tripleString, + showConfiguration: false, + resetConfiguration: false, + config: args + ) + XCTAssertTrue(configSuccess) + XCTAssertTrue(fileSystem.isFile(targetTripleConfigPath)) + + let updatedConfig = try config.readConfiguration( + sdkID: testArtifactID, + targetTriple: targetTriple + ) + XCTAssertEqual(args.sdkRootPath, updatedConfig?.pathsConfiguration.sdkRootPath?.pathString) + } + + do { + let (config, fileSystem) = try await createConfigurationStore() + var args = SwiftSDK.PathsConfiguration() + args.sdkRootPath = sdkRootPath + // an empty targetTriple will configure all triples + let configSuccess = try config.configure( + sdkID: testArtifactID, + targetTriple: nil, + showConfiguration: false, + resetConfiguration: false, + config: args + ) + XCTAssertTrue(configSuccess) + XCTAssertTrue(fileSystem.isFile(targetTripleConfigPath)) + + let resetSuccess = try config.configure( + sdkID: testArtifactID, + targetTriple: nil, + showConfiguration: false, + resetConfiguration: true, + config: args + ) + XCTAssertTrue(resetSuccess, "Reset configuration should succeed") + XCTAssertFalse(fileSystem.isFile(targetTripleConfigPath), "Reset configuration should clear configuration folder") + } + } } From 8d65c3d647ca76955e1daa70e02f1bcb2eb758c4 Mon Sep 17 00:00:00 2001 From: Marc Prud'hommeaux Date: Wed, 16 Jul 2025 10:49:04 -0400 Subject: [PATCH 2/2] Make sdkRootPath property of swift-sdk.json targetTriples object optional (#8687) Makes the "sdkRootPath" property of `swift-sdk.json` optional. ### Motivation: The Android SDK bundle (https://github.com/swiftlang/swift/issues/80788) does not include the Android NDK's sysroot in the bundle itself, but instead relies on it being installed locally. The install location will vary, and the user will be able to configure it with a command (modulo https://github.com/swiftlang/swift-package-manager/issues/8584) like: ``` swift sdk configure --sdk-root-path ~/Library/Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/ swift-6.2-RELEASE-android-0.1 aarch64-unknown-linux-android28 ``` However, since the `SwiftSDKMetadataV4.sdkRootPath` property is declared as non-optional, *some* value must be included in the `swift-sdk.json` file. @MaxDesiatov at https://github.com/swiftlang/swift/issues/80788#issuecomment-2841270696 mentions: > As for making it optional, I don't quite remember the exact issue that caused it to become non-optional. After all, making it optional is technically not a breaking change, so potentially could be considered if necessary. ### Modifications: Change `SwiftSDKMetadataV4.sdkRootPath` from `String` to `String?` ### Result: The swift-sdk.json can now contain destinations that do not specify a `sdkRootPath` property. (cherry picked from commit 55f0a830ad6043d0cf3886fad6b6fe34b3304492) --- Sources/PackageModel/SwiftSDKs/SwiftSDK.swift | 6 ++- Tests/PackageModelTests/SwiftSDKTests.swift | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift index d04216cc651..e0e75b96075 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDK.swift @@ -340,7 +340,9 @@ public struct SwiftSDK: Equatable { swiftSDKDirectory: Basics.AbsolutePath? = nil ) throws where Path == Basics.AbsolutePath { self.init( - sdkRootPath: try AbsolutePath(validating: properties.sdkRootPath, relativeTo: swiftSDKDirectory), + sdkRootPath: try properties.sdkRootPath.map { + try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) + }, swiftResourcesPath: try properties.swiftResourcesPath.map { try AbsolutePath(validating: $0, relativeTo: swiftSDKDirectory) }, @@ -1175,7 +1177,7 @@ struct SerializedDestinationV3: Decodable { struct SwiftSDKMetadataV4: Decodable { struct TripleProperties: Codable { /// Path relative to `swift-sdk.json` containing SDK root. - var sdkRootPath: String + var sdkRootPath: String? /// Path relative to `swift-sdk.json` containing Swift resources for dynamic linking. var swiftResourcesPath: String? diff --git a/Tests/PackageModelTests/SwiftSDKTests.swift b/Tests/PackageModelTests/SwiftSDKTests.swift index 7f7a5dd9650..d3c6b2f0643 100644 --- a/Tests/PackageModelTests/SwiftSDKTests.swift +++ b/Tests/PackageModelTests/SwiftSDKTests.swift @@ -25,6 +25,7 @@ private let hostTriple = try! Triple("arm64-apple-darwin22.1.0") private let olderHostTriple = try! Triple("arm64-apple-darwin20.1.0") private let linuxGNUTargetTriple = try! Triple("x86_64-unknown-linux-gnu") private let linuxMuslTargetTriple = try! Triple("x86_64-unknown-linux-musl") +private let androidTargetTriple = try! Triple("aarch64-unknown-linux-android28") private let wasiTargetTriple = try! Triple("wasm32-unknown-wasi") private let extraFlags = BuildFlags( cCompilerFlags: ["-fintegrated-as"], @@ -186,6 +187,20 @@ private let toolsetRootSwiftSDKv4 = ( """# as SerializedJSON ) +private let androidWithoutSDKRootPathSwiftSDKv4 = ( + path: bundleRootPath.appending(component: "androidWithoutSDKRootPathSwiftSDKv4.json"), + json: #""" + { + "targetTriples": { + "\#(androidTargetTriple.tripleString)": { + "toolsetPaths": ["/tools/otherToolsNoRoot.json"] + } + }, + "schemaVersion": "4.0" + } + """# as SerializedJSON +) + private let missingToolsetSwiftSDKv4 = ( path: bundleRootPath.appending(component: "missingToolsetSwiftSDKv4.json"), json: #""" @@ -351,6 +366,23 @@ private let parsedToolsetRootDestination = SwiftSDK( ) ) +private let parsedToolsetNoSDKRootPathDestination = SwiftSDK( + targetTriple: androidTargetTriple, + toolset: .init( + knownTools: [ + .librarian: .init(path: try! AbsolutePath(validating: "\(usrBinTools[.librarian]!)")), + .linker: .init(path: try! AbsolutePath(validating: "\(usrBinTools[.linker]!)")), + .debugger: .init(path: try! AbsolutePath(validating: "\(usrBinTools[.debugger]!)")), + ], + rootPaths: [] + ), + pathsConfiguration: .init( + sdkRootPath: nil, + toolsetPaths: ["/tools/otherToolsNoRoot.json"] + .map { try! AbsolutePath(validating: $0) } + ) +) + private let testFiles: [(path: AbsolutePath, json: SerializedJSON)] = [ destinationV1, destinationV2, @@ -365,6 +397,7 @@ private let testFiles: [(path: AbsolutePath, json: SerializedJSON)] = [ invalidVersionSwiftSDKv4, invalidToolsetSwiftSDKv4, wasiWithoutToolsetsSwiftSDKv4, + androidWithoutSDKRootPathSwiftSDKv4, otherToolsNoRoot, someToolsWithRoot, invalidToolset, @@ -488,6 +521,15 @@ final class SwiftSDKTests: XCTestCase { XCTAssertEqual(toolsetRootSwiftSDKv4Decoded, [parsedToolsetRootDestination]) + let androidWithoutSDKRootPathSwiftSDKv4Decoded = try SwiftSDK.decode( + fromFile: androidWithoutSDKRootPathSwiftSDKv4.path, + hostToolchainBinDir: toolchainBinAbsolutePath, + fileSystem: fs, + observabilityScope: observability + ) + + XCTAssertEqual(androidWithoutSDKRootPathSwiftSDKv4Decoded, [parsedToolsetNoSDKRootPathDestination]) + XCTAssertThrowsError(try SwiftSDK.decode( fromFile: missingToolsetSwiftSDKv4.path, hostToolchainBinDir: toolchainBinAbsolutePath,