Problem
Currently, SwiftPMCacheKey only considers the target's own package pin state, build options, compiler version, Xcode version, and Scipio version when computing cache keys. It does not account for the build state of the target's dependencies.
This means that when a dependency (e.g., FrameworkA) is updated with a non-binary-compatible change, a downstream target (e.g., FrameworkB that depends on FrameworkA) may still have a valid cache key — even though FrameworkB was built against the old version of FrameworkA. This results in Scipio serving a stale cached binary for FrameworkB, which can cause link-time or runtime failures due to binary incompatibility.
Current Cache Key Structure
From Sources/ScipioKit/Producer/Cache/CacheSystem.swift (L39-L48):
public struct SwiftPMCacheKey: CacheKey {
public var localPackageCanonicalLocation: String?
public var pin: Pin.State
public var targetName: String
var buildOptions: BuildOptions
public var clangVersion: String
public var xcodeVersion: XcodeVersion
public var scipioVersion: String?
}
None of these fields capture the state of the target's transitive dependencies.
Expected Behavior
When a target's dependency cache is invalidated (e.g., due to a version bump or rebuild), the cache for the dependent target should also be invalidated automatically. The cache key for a target should incorporate information about how its dependencies were built, such as:
- The cache keys (or hashes thereof) of all direct dependencies
- Whether each dependency was built as dynamic or static framework
Reproduction Scenario
FrameworkB depends on FrameworkA v1.0.0
- Scipio builds and caches both frameworks
FrameworkA is updated to v1.1.0 with non-binary-compatible API changes
- Scipio rebuilds
FrameworkA (its pin changed, so cache is invalidated)
- Scipio reuses the cached
FrameworkB (its own pin hasn't changed)
FrameworkB is now linked against the old FrameworkA binary, causing failures
Proposed Solution
Include dependency build information in the cache key computation. One approach:
- Compute cache keys in dependency order (leaf dependencies first)
- Include each direct dependency's cache key hash as part of the dependent target's cache key
- This creates a cascade where any change in a dependency automatically invalidates all dependents
Problem
Currently,
SwiftPMCacheKeyonly considers the target's own package pin state, build options, compiler version, Xcode version, and Scipio version when computing cache keys. It does not account for the build state of the target's dependencies.This means that when a dependency (e.g.,
FrameworkA) is updated with a non-binary-compatible change, a downstream target (e.g.,FrameworkBthat depends onFrameworkA) may still have a valid cache key — even thoughFrameworkBwas built against the old version ofFrameworkA. This results in Scipio serving a stale cached binary forFrameworkB, which can cause link-time or runtime failures due to binary incompatibility.Current Cache Key Structure
From
Sources/ScipioKit/Producer/Cache/CacheSystem.swift(L39-L48):None of these fields capture the state of the target's transitive dependencies.
Expected Behavior
When a target's dependency cache is invalidated (e.g., due to a version bump or rebuild), the cache for the dependent target should also be invalidated automatically. The cache key for a target should incorporate information about how its dependencies were built, such as:
Reproduction Scenario
FrameworkBdepends onFrameworkAv1.0.0FrameworkAis updated to v1.1.0 with non-binary-compatible API changesFrameworkA(its pin changed, so cache is invalidated)FrameworkB(its own pin hasn't changed)FrameworkBis now linked against the oldFrameworkAbinary, causing failuresProposed Solution
Include dependency build information in the cache key computation. One approach: