Skip to content
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
2 changes: 1 addition & 1 deletion Sources/PackageModel/SwiftSDKs/SwiftSDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ public struct SwiftSDK: Equatable {
swiftSDK = targetSwiftSDK
} else if let swiftSDKSelector {
do {
swiftSDK = try store.selectBundle(matching: swiftSDKSelector, hostTriple: hostTriple)
(_, swiftSDK) = try store.selectBundle(matching: swiftSDKSelector, hostTriple: hostTriple, targetTriple: customCompileTriple)
} catch {
// If a user-installed bundle for the selector doesn't exist, check if the
// selector is recognized as a default SDK.
Expand Down
65 changes: 15 additions & 50 deletions Sources/PackageModel/SwiftSDKs/SwiftSDKBundle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ extension [SwiftSDKBundle] {
}
}

return variant.swiftSDKs.first { $0.targetTriple == targetTriple }
return variant.swiftSDKs.first { $0.targetTriple?.tripleString == targetTriple.tripleString }
}
}
}
Expand All @@ -79,72 +79,37 @@ extension [SwiftSDKBundle] {
/// - Parameters:
/// - selector: either an artifact ID or target triple to filter with.
/// - hostTriple: triple of the host building with these Swift SDKs.
/// - observabilityScope: observability scope to log warnings about multiple matches.
/// - Returns: ``SwiftSDK`` value matching `query` either by artifact ID or target triple, `nil` if none found.
/// - Returns: a tuple containing all `selector` matches to the artifact ID or target triple,
/// including the corresponding artifact ID for each target triple matched
func selectSwiftSDK(
matching selector: String,
hostTriple: Triple,
observabilityScope: ObservabilityScope
) -> SwiftSDK? {
var matchedByID: (path: AbsolutePath, variant: SwiftSDKBundle.Variant, swiftSDK: SwiftSDK)?
var matchedByTriple: (path: AbsolutePath, variant: SwiftSDKBundle.Variant, swiftSDK: SwiftSDK)?
hostTriple: Triple
) -> (idMatches: [SwiftSDK], tripleMatches: [String: SwiftSDK]) {
var idHits: [SwiftSDK] = []
var tripleHits: [String: SwiftSDK] = [:]

for bundle in self {
for (artifactID, variants) in bundle.artifacts {
for variant in variants {
guard variant.isSupporting(hostTriple: hostTriple) else { continue }

for swiftSDK in variant.swiftSDKs {
// All artifact IDs are checked by installIfValid() to be
// unique, but the selected ID must only have one target triple,
// in this method where no target triple is specified with the ID.
if artifactID == selector {
if let matchedByID {
observabilityScope.emit(
warning:
"""
multiple Swift SDKs match ID `\(artifactID)` and host triple \(
hostTriple.tripleString
), selected one at \(
matchedByID.path.appending(matchedByID.variant.metadata.path)
)
"""
)
} else {
matchedByID = (bundle.path, variant, swiftSDK)
}
idHits.append(swiftSDK)
}

// Multiple SDKs can vend the same triple, so list them all and
// return the corresponding artifact ID also.
if swiftSDK.targetTriple?.tripleString == selector {
if let matchedByTriple {
observabilityScope.emit(
warning:
"""
multiple Swift SDKs match target triple `\(selector)` and host triple \(
hostTriple.tripleString
), selected one at \(
matchedByTriple.path.appending(matchedByTriple.variant.metadata.path)
)
"""
)
} else {
matchedByTriple = (bundle.path, variant, swiftSDK)
}
tripleHits[artifactID] = swiftSDK
}
}
}
}
}

if let matchedByID, let matchedByTriple, matchedByID != matchedByTriple {
observabilityScope.emit(
warning:
"""
multiple Swift SDKs match the query `\(selector)` and host triple \(
hostTriple.tripleString
), selected one at \(matchedByID.path.appending(matchedByID.variant.metadata.path))
"""
)
}

return matchedByID?.swiftSDK ?? matchedByTriple?.swiftSDK
return (idMatches: idHits, tripleMatches: tripleHits)
}

public var sortedArtifactIDs: [String] {
Expand Down
79 changes: 67 additions & 12 deletions Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -46,16 +46,44 @@ public final class SwiftSDKBundleStore {
}

enum Error: Swift.Error, CustomStringConvertible {
case matchingBothToSwiftSDK(selector: String, hostTriple: Triple)
case multipleSDKMatchesForID(selector: String, hostTriple: Triple)
case multipleSDKMatchesForTriple(selector: String, hostTriple: Triple, matches: [String])
case noMatchingSwiftSDK(selector: String, hostTriple: Triple)
case noMatchingSwiftSDKWithTriple(selector: String, hostTriple: Triple, targetTriple: Triple)

var description: String {
switch self {
case let .matchingBothToSwiftSDK(selector, hostTriple):
return """
The query for `\(selector)` and host triple `\(hostTriple.tripleString)` \
matched both an SDK and a target triple. Use the `swift sdk list` command \
to see available Swift SDKs and remove one of them.
"""
case let .multipleSDKMatchesForID(selector, hostTriple):
return """
The query for `\(selector)` and host triple `\(hostTriple.tripleString)` \
has multiple target triples. Use the `--triple` flag to specify a triple.
"""
case let .multipleSDKMatchesForTriple(selector, hostTriple, matches):
return """
The query for `\(selector)` and host triple `\(hostTriple.tripleString)` \
matched multiple SDKs: \(matches.joined(separator: ", ")). Use the \
`swift sdk list` command to see available Swift SDKs and try a different \
query like `--swift-sdk \(matches[0]) --triple \(selector)` or remove an SDK.
"""
case let .noMatchingSwiftSDK(selector, hostTriple):
return """
No Swift SDK found matching query `\(selector)` and host triple \
`\(hostTriple.tripleString)`. Use `swift sdk list` command to see \
`\(hostTriple.tripleString)`. Use the `swift sdk list` command to see \
available Swift SDKs.
"""
case let .noMatchingSwiftSDKWithTriple(selector, hostTriple, targetTriple):
return """
No Swift SDK found matching query `\(selector)`, target triple \
`\(targetTriple.tripleString)`, and host triple `\(hostTriple.tripleString)`. \
Use the `swift sdk list` command to see available Swift SDKs.
"""
}
}
}
Expand Down Expand Up @@ -121,13 +149,16 @@ public final class SwiftSDKBundleStore {
/// Select a Swift SDK matching a given query and host triple from all Swift SDKs available in
/// ``SwiftSDKBundleStore//swiftSDKsDirectory``.
/// - Parameters:
/// - query: either an artifact ID or target triple to filter with.
/// - selector: either an artifact ID or target triple to filter with.
/// - hostTriple: triple of the host building with these Swift SDKs.
/// - Returns: ``SwiftSDK`` value matching `query` either by artifact ID or target triple, `nil` if none found.
/// - targetTriple: optional separate target triple to look for
/// - Returns: tuple with the Artifact ID and ``SwiftSDK`` value that matched `selector` either by
/// artifact ID or target triple, throws if multiple or none found.
public func selectBundle(
matching selector: String,
hostTriple: Triple
) throws -> SwiftSDK {
hostTriple: Triple,
targetTriple: Triple? = nil
) throws -> (String, SwiftSDK) {
let validBundles = try self.allValidBundles

guard !validBundles.isEmpty else {
Expand All @@ -136,17 +167,41 @@ public final class SwiftSDKBundleStore {
)
}

guard var selectedSwiftSDKs = validBundles.selectSwiftSDK(
matching: selector,
hostTriple: hostTriple,
observabilityScope: self.observabilityScope
) else {
if let triple = targetTriple {
guard var selectedSwiftSDK = validBundles.selectSwiftSDK(
id: selector,
hostTriple: hostTriple,
targetTriple: triple
) else {
throw Error.noMatchingSwiftSDKWithTriple(selector: selector, hostTriple: hostTriple, targetTriple: triple)
}
selectedSwiftSDK.applyPathCLIOptions()
return (selector, selectedSwiftSDK)
}

var selectedSwiftSDK: SwiftSDK
var id: String
let SDKs = validBundles.selectSwiftSDK(matching: selector, hostTriple: hostTriple)
if SDKs.tripleMatches.count > 1 {
throw Error.multipleSDKMatchesForTriple(selector: selector, hostTriple: hostTriple, matches: Array(SDKs.tripleMatches.keys).sorted())
} else if SDKs.idMatches.count > 1 {
throw Error.multipleSDKMatchesForID(selector: selector, hostTriple: hostTriple)
} else if SDKs.idMatches.count == 1 {
guard SDKs.tripleMatches.count == 0 else {
throw Error.matchingBothToSwiftSDK(selector: selector, hostTriple: hostTriple)
}
id = selector
selectedSwiftSDK = SDKs.idMatches[0]
} else if let match = SDKs.tripleMatches.first {
id = match.key
selectedSwiftSDK = match.value
} else {
throw Error.noMatchingSwiftSDK(selector: selector, hostTriple: hostTriple)
}

selectedSwiftSDKs.applyPathCLIOptions()
selectedSwiftSDK.applyPathCLIOptions()

return selectedSwiftSDKs
return (id, selectedSwiftSDK)
}

/// Installs a Swift SDK bundle from a given path or URL to ``SwiftSDKBundleStore//swiftSDKsDirectory``.
Expand Down
2 changes: 1 addition & 1 deletion Sources/PackageModel/UserToolchain.swift
Original file line number Diff line number Diff line change
Expand Up @@ -857,7 +857,7 @@ public final class UserToolchain: Toolchain {
xcbuildFlags: swiftSDK.toolset.knownTools[.xcbuild]?.extraCLIOptions ?? [])

self.includeSearchPaths = swiftSDK.pathsConfiguration.includeSearchPaths ?? []
self.librarySearchPaths = swiftSDK.pathsConfiguration.includeSearchPaths ?? []
self.librarySearchPaths = swiftSDK.pathsConfiguration.librarySearchPaths ?? []

self.librarianPath = try swiftSDK.toolset.knownTools[.librarian]?.path ?? UserToolchain.determineLibrarian(
triple: triple,
Expand Down
2 changes: 1 addition & 1 deletion Tests/BuildTests/BuildPlanTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -5598,7 +5598,7 @@ class BuildPlanTestCase: BuildSystemProviderTestCase {

// Link Product
let exeLinkArguments = try result.buildProduct(for: "exe").linkArguments()
let exeLinkArgumentsPattern: [StringPattern] = ["-L", "\(sdkIncludeSearchPath)"]
let exeLinkArgumentsPattern: [StringPattern] = ["-L", "\(sdkLibrarySearchPath)"]
XCTAssertMatch(exeLinkArguments, exeLinkArgumentsPattern)
}

Expand Down
15 changes: 15 additions & 0 deletions Tests/CommandsTests/SwiftSDKCommandTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,21 @@ struct SwiftSDKCommandTests {
}
}

await expectThrowsCommandExecutionError(
try await command.execute(
[
"configure", "--show-configuration",
"--swift-sdks-path", fixturePath.pathString,
"test-artifact",
"aarch64-unknown-linux-gnu11.0",
]
)
) { error in
let stderr = error.stderr
#expect(stderr.contains("Error: Swift SDK with ID `test-artifact`, host triple "))
#expect(stderr.contains(", and target triple aarch64-unknown-linux-gnu11.0 is not currently installed."))
}

(stdout, stderr) = try await command.execute(
["remove", "--swift-sdks-path", fixturePath.pathString, "test-artifact"])

Expand Down
108 changes: 106 additions & 2 deletions Tests/PackageModelTests/SwiftSDKBundleTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -375,12 +375,13 @@ final class SwiftSDKBundleTests: XCTestCase {
try await store.install(bundlePathOrURL: bundle.path, archiver)
}

let sdk = try store.selectBundle(
let (id, sdk) = try store.selectBundle(
matching: "\(testArtifactID)1",
hostTriple: Triple("arm64-apple-macosx14.0")
)

XCTAssertEqual(sdk.targetTriple, targetTriple)
XCTAssertEqual(id, "\(testArtifactID)1")
XCTAssertEqual(output, [
.installationSuccessful(
bundlePathOrURL: bundles[0].path,
Expand All @@ -391,6 +392,108 @@ final class SwiftSDKBundleTests: XCTestCase {
bundleName: AbsolutePath(bundles[1].path).components.last!
),
])

let (name, tripleSDK) = try store.selectBundle(
matching: "\(testArtifactID)1",
hostTriple: Triple("arm64-apple-macosx14.0"),
targetTriple: targetTriple
)

XCTAssertEqual(tripleSDK.targetTriple, targetTriple)
XCTAssertEqual(name, "\(testArtifactID)1")

let (match, matchSDK) = try store.selectBundle(
matching: "\(targetTriple.tripleString)",
hostTriple: Triple("i686-apple-macosx14.0")
)

XCTAssertEqual(matchSDK.targetTriple, targetTriple)
XCTAssertEqual(match, "\(testArtifactID)2")
}

func testBundleSelectionByTripleAndErrors() async throws {
let (fileSystem, bundles, swiftSDKsDirectory) = try generateTestFileSystem(
bundleArtifacts: [
.init(id: "\(testArtifactID)1", supportedTriples: [arm64Triple]),
.init(id: "\(testArtifactID)2", supportedTriples: [arm64Triple]),
.init(id: "\(targetTriple.tripleString)", supportedTriples: [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)
}

XCTAssertThrowsError(try store.selectBundle(
matching: "\(testArtifactID)3",
hostTriple: Triple("arm64-apple-macosx14.0"),
targetTriple: targetTriple
)) { error in
XCTAssertEqual(
"\(error)",
"""
No Swift SDK found matching query `\(testArtifactID)3`, target triple \
`\(targetTriple.tripleString)`, and host triple `arm64-apple-macosx14.0`. \
Use the `swift sdk list` command to see available Swift SDKs.
"""
)
}

XCTAssertThrowsError(try store.selectBundle(
matching: targetTriple.tripleString,
hostTriple: Triple("arm64-apple-macosx14.0")
)) { error in
XCTAssertEqual(
"\(error)",
"""
The query for `\(targetTriple.tripleString)` and host triple `arm64-apple-macosx14.0` \
matched multiple SDKs: \(testArtifactID)1, \(testArtifactID)2. Use the \
`swift sdk list` command to see available Swift SDKs and try a different \
query like `--swift-sdk \(testArtifactID)1 --triple \(targetTriple.tripleString)` or remove an SDK.
"""
)
}

XCTAssertThrowsError(try store.selectBundle(
matching: "\(targetTriple.tripleString)",
hostTriple: Triple("i686-apple-macosx14.0")
)) { error in
XCTAssertEqual(
"\(error)",
"""
The query for `\(targetTriple.tripleString)` and host triple `i686-apple-macosx14.0` \
matched both an SDK and a target triple. Use the `swift sdk list` command \
to see available Swift SDKs and remove one of them.
"""
)
}

XCTAssertThrowsError(try store.selectBundle(
matching: "armv7-unknown-linux",
hostTriple: Triple("arm64-apple-macosx14.0")
)) { error in
XCTAssertEqual(
"\(error)",
"""
No Swift SDK found matching query `armv7-unknown-linux` and host triple \
`arm64-apple-macosx14.0`. Use the `swift sdk list` command to see \
available Swift SDKs.
"""
)
}
}

func testTargetSDKDerivation() async throws {
Expand Down Expand Up @@ -578,12 +681,13 @@ final class SwiftSDKBundleTests: XCTestCase {
}

let hostTriple = try Triple("arm64-apple-macosx14.0")
let sdk = try store.selectBundle(
let (id, sdk) = try store.selectBundle(
matching: testArtifactID,
hostTriple: hostTriple
)

XCTAssertEqual(sdk.targetTriple, targetTriple)
XCTAssertEqual(id, testArtifactID)
XCTAssertEqual(output, [
.installationSuccessful(
bundlePathOrURL: bundles[0].path,
Expand Down
Loading
Loading