diff --git a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift index 7810717e683..06ef11f865c 100644 --- a/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift +++ b/Sources/PackageModel/SwiftSDKs/SwiftSDKBundleStore.swift @@ -370,9 +370,13 @@ public final class SwiftSDKBundleStore { var variants = [SwiftSDKBundle.Variant]() for variantMetadata in artifactMetadata.variants { - let variantConfigurationPath = bundlePath + var variantConfigurationPath = bundlePath .appending(variantMetadata.path) - .appending("swift-sdk.json") + + if variantConfigurationPath.extension != ".json" && + self.fileSystem.isDirectory(variantConfigurationPath) { + variantConfigurationPath = variantConfigurationPath.appending("swift-sdk.json") + } guard self.fileSystem.exists(variantConfigurationPath) else { self.observabilityScope.emit( diff --git a/Tests/PackageModelTests/SwiftSDKBundleTests.swift b/Tests/PackageModelTests/SwiftSDKBundleTests.swift index 0a65b116675..5a69093e5cf 100644 --- a/Tests/PackageModelTests/SwiftSDKBundleTests.swift +++ b/Tests/PackageModelTests/SwiftSDKBundleTests.swift @@ -2,7 +2,7 @@ // // This source file is part of the Swift open source project // -// Copyright (c) 2023-2024 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 @@ -27,20 +27,26 @@ private let targetTriple = try! Triple("aarch64-unknown-linux") private let jsonEncoder = JSONEncoder() private func generateBundleFiles(bundle: MockBundle) throws -> [(String, ByteString)] { - try [ + return try [ ( "\(bundle.path)/info.json", ByteString(json: """ { "artifacts" : { \(bundle.artifacts.map { - """ + let path = if let metadataPath = $0.metadataPath { + metadataPath.pathString + } else { + "\($0.id)/\(targetTriple.triple)" + } + + return """ "\($0.id)" : { "type" : "swiftSDK", "version" : "0.0.1", "variants" : [ { - "path" : "\($0.id)/\(targetTriple.triple)", + "path" : "\(path)", "supportedTriples" : \($0.supportedTriples.map(\.tripleString)) } ] @@ -55,14 +61,25 @@ private func generateBundleFiles(bundle: MockBundle) throws -> [(String, ByteStr ), ] + bundle.artifacts.map { - ( - "\(bundle.path)/\($0.id)/\(targetTriple.tripleString)/swift-sdk.json", + let path = if let metadataPath = $0.metadataPath { + "\(bundle.path)/\(metadataPath.pathString)" + } else { + "\(bundle.path)/\($0.id)/\(targetTriple.triple)/swift-sdk.json" + } + + return ( + path, ByteString(json: try generateSwiftSDKMetadata(jsonEncoder, createToolset: $0.toolsetRootPath != nil)) ) } + bundle.artifacts.compactMap { artifact in - artifact.toolsetRootPath.map { path in + let toolsetPath = if artifact.metadataPath != nil { + "\(bundle.path)/toolset.json" + } else { + "\(bundle.path)/\(artifact.id)/\(targetTriple.triple)/toolset.json" + } + return artifact.toolsetRootPath.map { path in ( - "\(bundle.path)/\(artifact.id)/\(targetTriple.tripleString)/toolset.json", + "\(toolsetPath)", ByteString(json: """ { "schemaVersion": "1.0", @@ -101,16 +118,18 @@ private struct MockBundle { private struct MockArtifact { let id: String let supportedTriples: [Triple] + var metadataPath: RelativePath? var toolsetRootPath: AbsolutePath? } -private func generateTestFileSystem(bundleArtifacts: [MockArtifact]) throws -> (some FileSystem, [MockBundle], AbsolutePath) { +private func generateTestFileSystem( + bundleArtifacts: [MockArtifact] +) throws -> (some FileSystem, [MockBundle], AbsolutePath) { let bundles = bundleArtifacts.enumerated().map { (i, artifacts) in let bundleName = "test\(i).\(artifactBundleExtension)" return MockBundle(name: "test\(i).\(artifactBundleExtension)", path: "/\(bundleName)", artifacts: [artifacts]) } - let fileSystem = try InMemoryFileSystem( files: Dictionary( uniqueKeysWithValues: bundles.flatMap { @@ -475,4 +494,54 @@ final class SwiftSDKBundleTests: XCTestCase { ) } } + + func testMetadataJSONPaths() async throws { + let toolsetRootPath = AbsolutePath("/path/to/toolpath") + let (fileSystem, bundles, swiftSDKsDirectory) = try generateTestFileSystem( + bundleArtifacts: [ + .init( + id: "\(testArtifactID)1", + supportedTriples: [arm64Triple], + metadataPath: "metadata1.json" + ), + .init( + id: "\(testArtifactID)2", + supportedTriples: [i686Triple], + metadataPath: "metadata2.json", + toolsetRootPath: toolsetRootPath + ), + ] + ) + let system = ObservabilitySystem.makeForTesting() + let archiver = MockArchiver() + + var output = [SwiftSDKBundleStore.Output]() + let store = SwiftSDKBundleStore( + swiftSDKsDirectory: swiftSDKsDirectory, + fileSystem: fileSystem, + observabilityScope: system.topScope, + outputHandler: { output.append($0) } + ) + + for bundle in bundles { + try await store.install(bundlePathOrURL: bundle.path, archiver) + } + + let validBundles = try store.allValidBundles + + XCTAssertEqual(validBundles.count, bundles.count) + + XCTAssertEqual(validBundles.sortedArtifactIDs, ["\(testArtifactID)1", "\(testArtifactID)2"]) + XCTAssertEqual(output.count, 2) + XCTAssertEqual(output, [ + .installationSuccessful( + bundlePathOrURL: bundles[0].path, + bundleName: AbsolutePath(bundles[0].path).components.last! + ), + .installationSuccessful( + bundlePathOrURL: bundles[1].path, + bundleName: AbsolutePath(bundles[1].path).components.last! + ), + ]) + } }