diff --git a/Sources/ScipioKit/Producer/Cache/CacheSystem.swift b/Sources/ScipioKit/Producer/Cache/CacheSystem.swift index 27230098..a3842ec6 100644 --- a/Sources/ScipioKit/Producer/Cache/CacheSystem.swift +++ b/Sources/ScipioKit/Producer/Cache/CacheSystem.swift @@ -96,8 +96,10 @@ extension PinsStore.PinState: @retroactive Hashable { } public struct SwiftPMCacheKey: CacheKey { - public var targetName: String + /// The canonical repository URL the manifest was loaded from, for local packages only. + public var localPackageCanonicalLocation: String? public var pin: PinsStore.PinState + public var targetName: String var buildOptions: BuildOptions public var clangVersion: String public var xcodeVersion: XcodeVersion @@ -230,8 +232,18 @@ struct CacheSystem: Sendable { } func calculateCacheKey(of target: CacheTarget) async throws -> SwiftPMCacheKey { + let package = target.buildProduct.package + + let localPackageCanonicalLocation: String? = switch package.manifest.packageKind { + case .fileSystem, .localSourceControl: + package.manifest.canonicalPackageLocation.description + case .root, .remoteSourceControl, .registry: + nil + } + + let pin = try retrievePin(package: package) + let targetName = target.buildProduct.target.name - let pin = try retrievePin(package: target.buildProduct.package) let buildOptions = target.buildOptions guard let clangVersion = try await ClangChecker().fetchClangVersion() else { throw Error.compilerVersionNotDetected @@ -240,8 +252,9 @@ struct CacheSystem: Sendable { throw Error.xcodeVersionNotDetected } return SwiftPMCacheKey( - targetName: targetName, + localPackageCanonicalLocation: localPackageCanonicalLocation, pin: pin.state, + targetName: targetName, buildOptions: buildOptions, clangVersion: clangVersion, xcodeVersion: xcodeVersion, diff --git a/Tests/ScipioKitTests/CacheSystemTests.swift b/Tests/ScipioKitTests/CacheSystemTests.swift index 72ab3327..44e73b64 100644 --- a/Tests/ScipioKitTests/CacheSystemTests.swift +++ b/Tests/ScipioKitTests/CacheSystemTests.swift @@ -2,6 +2,7 @@ import Foundation @testable import ScipioKit import XCTest import Basics +import struct PackageModel.CanonicalPackageLocation private let fixturePath = URL(fileURLWithPath: #filePath) .deletingLastPathComponent() @@ -19,8 +20,9 @@ final class CacheSystemTests: XCTestCase { func testEncodeCacheKey() throws { let cacheKey = SwiftPMCacheKey( - targetName: "MyTarget", + localPackageCanonicalLocation: "/path/to/MyPackage", pin: .revision("111111111"), + targetName: "MyTarget", buildOptions: .init( buildConfiguration: .release, isDebugSymbolsEmbedded: false, @@ -64,6 +66,7 @@ final class CacheSystemTests: XCTestCase { ] }, "clangVersion" : "clang-1400.0.29.102", + "localPackageCanonicalLocation" : "\\/path\\/to\\/MyPackage", "pin" : { "revision" : "111111111" }, @@ -78,6 +81,82 @@ final class CacheSystemTests: XCTestCase { XCTAssertEqual(rawString, expected) } + func testCacheKeyForRemoteAndLocalPackageDifference() async throws { + let fileSystem = localFileSystem + + let tempDir = try fileSystem.tempDirectory.appending(#function) + try fileSystem.removeFileTree(tempDir) + try fileSystem.createDirectory(tempDir) + + defer { try? fileSystem.removeFileTree(tempDir) } + + let tempCacheKeyTestsDir = tempDir.appending(component: "CacheKeyTests").scipioAbsolutePath + try fileSystem.copy( + from: fixturePath.appending(component: "CacheKeyTests").absolutePath, + to: tempCacheKeyTestsDir + ) + + // For local package consumption + let executor = ProcessExecutor() + _ = try await executor.execute([ + "git", + "clone", + "https://github.com/giginet/scipio-testing", + tempDir.appending(component: "scipio-testing").pathString, + "-b", + "3.0.0", + "--depth", + "1", + ]) + + func scipioTestingCacheKey(fixture: String) async throws -> SwiftPMCacheKey { + let descriptionPackage = try DescriptionPackage( + packageDirectory: tempCacheKeyTestsDir.appending(component: fixture), + mode: .createPackage, + onlyUseVersionsFromResolvedFile: false + ) + let package = descriptionPackage + .graph + .packages + .first { $0.manifest.displayName == "scipio-testing" }! + let target = package.modules.first { $0.name == "ScipioTesting" }! + let cacheTarget = CacheSystem.CacheTarget( + buildProduct: BuildProduct( + package: package, + target: target + ), + buildOptions: BuildOptions( + buildConfiguration: .release, + isDebugSymbolsEmbedded: false, + frameworkType: .dynamic, + sdks: [.iOS, .iOSSimulator], + extraFlags: nil, + extraBuildParameters: nil, + enableLibraryEvolution: false, + keepPublicHeadersStructure: false, + customFrameworkModuleMapContents: nil + ) + ) + + let cacheSystem = CacheSystem( + pinsStore: try descriptionPackage.workspace.pinsStore.load(), + outputDirectory: FileManager.default.temporaryDirectory.appendingPathComponent("XCFrameworks") + ) + return try await cacheSystem.calculateCacheKey(of: cacheTarget) + } + + let scipioTestingRemote = try await scipioTestingCacheKey(fixture: "AsRemotePackage") + let scipioTestingLocal = try await scipioTestingCacheKey(fixture: "AsLocalPackage") + + XCTAssertNil(scipioTestingRemote.localPackageCanonicalLocation) + XCTAssertEqual( + scipioTestingLocal.localPackageCanonicalLocation, + CanonicalPackageLocation(tempDir.appending(component: "scipio-testing").pathString).description + ) + XCTAssertEqual(scipioTestingRemote.targetName, scipioTestingLocal.targetName) + XCTAssertEqual(scipioTestingRemote.pin, scipioTestingLocal.pin) + } + func testCacheKeyCalculationForRootPackageTarget() async throws { let fileSystem = localFileSystem let testingPackagePath = fixturePath.appendingPathComponent("TestingPackage") diff --git a/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsLocalPackage/Package.swift b/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsLocalPackage/Package.swift new file mode 100644 index 00000000..31606334 --- /dev/null +++ b/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsLocalPackage/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "AsLocalPackage", + products: [ + .library( + name: "Foo", + targets: ["Foo"] + ), + ], + dependencies: [ + .package(path: "../../scipio-testing"), + ], + targets: [ + .target( + name: "Foo", + dependencies: [ + .product(name: "ScipioTesting", package: "scipio-testing"), + ] + ), + ] +) diff --git a/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsLocalPackage/Sources/Foo/Foo.swift b/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsLocalPackage/Sources/Foo/Foo.swift new file mode 100644 index 00000000..e69de29b diff --git a/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsRemotePackage/Package.swift b/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsRemotePackage/Package.swift new file mode 100644 index 00000000..a8ce894f --- /dev/null +++ b/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsRemotePackage/Package.swift @@ -0,0 +1,25 @@ +// swift-tools-version: 6.0 +// The swift-tools-version declares the minimum version of Swift required to build this package. + +import PackageDescription + +let package = Package( + name: "AsRemotePackage", + products: [ + .library( + name: "Foo", + targets: ["Foo"] + ), + ], + dependencies: [ + .package(url: "https://github.com/giginet/scipio-testing.git", exact: "3.0.0"), + ], + targets: [ + .target( + name: "Foo", + dependencies: [ + .product(name: "ScipioTesting", package: "scipio-testing"), + ] + ), + ] +) diff --git a/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsRemotePackage/Sources/Foo/Foo.swift b/Tests/ScipioKitTests/Resources/Fixtures/CacheKeyTests/AsRemotePackage/Sources/Foo/Foo.swift new file mode 100644 index 00000000..e69de29b