Skip to content

Commit

Permalink
Merge pull request #158 from ikesyo/validate-existing-frameworks-outs…
Browse files Browse the repository at this point in the history
…ide-restorer

Factor out the validation logic of existing frameworks in the output directory from Restorer
  • Loading branch information
ikesyo authored Nov 12, 2024
2 parents 43b504b + 03bf402 commit faa660b
Showing 1 changed file with 82 additions and 39 deletions.
121 changes: 82 additions & 39 deletions Sources/ScipioKit/Producer/FrameworkProducer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -104,23 +104,36 @@ struct FrameworkProducer {
buildOptions: buildOptionsForProduct
)
})

let pinsStore = try descriptionPackage.workspace.pinsStore.load()
let cacheSystem = CacheSystem(
pinsStore: pinsStore,
outputDirectory: outputDir,
storage: cacheStorage
)

let cacheSystem = CacheSystem(pinsStore: pinsStore,
outputDirectory: outputDir,
storage: cacheStorage)
let cacheEnabledTargets: Set<CacheSystem.CacheTarget>
let targetsToBuild: OrderedSet<CacheSystem.CacheTarget>
if cacheMode.isConsumingCacheEnabled {
cacheEnabledTargets = await restoreAllAvailableCaches(
availableTargets: Set(allTargets),
let targets = Set(allTargets)

// Validate the existing frameworks in `outputDir` before restoration
let valid = await validateExistingFrameworks(
availableTargets: targets,
cacheSystem: cacheSystem
)

let restored = await restoreAllAvailableCaches(
availableTargets: targets.subtracting(valid),
cacheSystem: cacheSystem
)

targetsToBuild = allTargets
.subtracting(valid)
.subtracting(restored)
} else {
cacheEnabledTargets = []
targetsToBuild = allTargets
}

let targetsToBuild = allTargets.subtracting(cacheEnabledTargets)

for target in targetsToBuild {
try await buildXCFrameworks(
target,
Expand All @@ -141,6 +154,46 @@ struct FrameworkProducer {
}
}

private func validateExistingFrameworks(
availableTargets: Set<CacheSystem.CacheTarget>,
cacheSystem: CacheSystem
) async -> Set<CacheSystem.CacheTarget> {
let chunked = availableTargets.chunks(ofCount: CacheSystem.defaultParalellNumber)

var validFrameworks: Set<CacheSystem.CacheTarget> = []
for chunk in chunked {
await withTaskGroup(of: CacheSystem.CacheTarget?.self) { group in
for target in chunk {
group.addTask { [outputDir, fileSystem] in
do {
let product = target.buildProduct
let outputPath = outputDir.appendingPathComponent(product.frameworkName)
let exists = fileSystem.exists(outputPath.absolutePath)
guard exists else { return nil }

let expectedCacheKey = try await cacheSystem.calculateCacheKey(of: target)
let isValidCache = await cacheSystem.existsValidCache(cacheKey: expectedCacheKey)
guard isValidCache else { return nil }

let expectedCacheKeyHash = try expectedCacheKey.calculateChecksum()
logger.info(
// swiftlint:disable:next line_length
"✅ Valid \(product.target.name).xcframework (\(expectedCacheKeyHash)) exists. Skip restoring or building.", metadata: .color(.green)
)
return target
} catch {
return nil
}
}
}
for await case let target? in group {
validFrameworks.insert(target)
}
}
}
return validFrameworks
}

private func restoreAllAvailableCaches(
availableTargets: Set<CacheSystem.CacheTarget>,
cacheSystem: CacheSystem
Expand All @@ -149,7 +202,7 @@ struct FrameworkProducer {

var restored: Set<CacheSystem.CacheTarget> = []
for chunk in chunked {
let restorer = Restorer(outputDir: outputDir, cacheMode: cacheMode, fileSystem: fileSystem)
let restorer = Restorer(outputDir: outputDir, fileSystem: fileSystem)
await withTaskGroup(of: CacheSystem.CacheTarget?.self) { group in
for target in chunk {
group.addTask {
Expand All @@ -175,7 +228,6 @@ struct FrameworkProducer {
/// Sendable interface to provide restore caches
private struct Restorer: Sendable {
let outputDir: URL
let cacheMode: Runner.Options.CacheMode
let fileSystem: any FileSystem

// Return true if pre-built artifact is available (already existing or restored from cache)
Expand All @@ -185,40 +237,31 @@ struct FrameworkProducer {
) async throws -> Bool {
let product = target.buildProduct
let frameworkName = product.frameworkName
let outputPath = outputDir.appendingPathComponent(product.frameworkName)
let outputPath = outputDir.appendingPathComponent(frameworkName)
let exists = fileSystem.exists(outputPath.absolutePath)

guard cacheMode.isConsumingCacheEnabled else {
return false
}
let expectedCacheKey = try await cacheSystem.calculateCacheKey(of: target)
let isValidCache = await cacheSystem.existsValidCache(cacheKey: expectedCacheKey)
let expectedCacheKeyHash = try expectedCacheKey.calculateChecksum()
if isValidCache && exists {
logger.info(
"✅ Valid \(product.target.name).xcframework (\(expectedCacheKeyHash)) is exists. Skip building.", metadata: .color(.green)
)

if exists {
logger.warning("⚠️ Existing \(frameworkName) is outdated.", metadata: .color(.yellow))
logger.info("🗑️ Delete \(frameworkName)", metadata: .color(.red))
try fileSystem.removeFileTree(outputPath.absolutePath)
}

let restoreResult = await cacheSystem.restoreCacheIfPossible(target: target)
switch restoreResult {
case .succeeded:
logger.info("✅ Restore \(frameworkName) (\(expectedCacheKeyHash)) from cache storage.", metadata: .color(.green))
return true
} else {
if exists {
logger.warning("⚠️ Existing \(frameworkName) is outdated.", metadata: .color(.yellow))
logger.info("🗑️ Delete \(frameworkName)", metadata: .color(.red))
try fileSystem.removeFileTree(outputPath.absolutePath)
}
let restoreResult = await cacheSystem.restoreCacheIfPossible(target: target)
switch restoreResult {
case .succeeded:
logger.info("✅ Restore \(frameworkName) (\(expectedCacheKeyHash)) from cache storage.", metadata: .color(.green))
return true
case .failed(let error):
logger.warning("⚠️ Restoring \(frameworkName) (\(expectedCacheKeyHash)) is failed", metadata: .color(.yellow))
if let description = error?.errorDescription {
logger.warning("\(description)", metadata: .color(.yellow))
}
return false
case .noCache:
return false
case .failed(let error):
logger.warning("⚠️ Restoring \(frameworkName) (\(expectedCacheKeyHash)) is failed", metadata: .color(.yellow))
if let description = error?.errorDescription {
logger.warning("\(description)", metadata: .color(.yellow))
}
return false
case .noCache:
return false
}
}
}
Expand Down

0 comments on commit faa660b

Please sign in to comment.