Skip to content

Cache key should account for dependency build state to prevent stale cache usage #265

@giginet

Description

@giginet

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

  1. FrameworkB depends on FrameworkA v1.0.0
  2. Scipio builds and caches both frameworks
  3. FrameworkA is updated to v1.1.0 with non-binary-compatible API changes
  4. Scipio rebuilds FrameworkA (its pin changed, so cache is invalidated)
  5. Scipio reuses the cached FrameworkB (its own pin hasn't changed)
  6. 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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions