diff --git a/Sources/ScipioKit/DescriptionPackage.swift b/Sources/ScipioKit/DescriptionPackage.swift index e508b643..8b4a3281 100644 --- a/Sources/ScipioKit/DescriptionPackage.swift +++ b/Sources/ScipioKit/DescriptionPackage.swift @@ -150,6 +150,8 @@ struct DescriptionPackage { extension DescriptionPackage { func resolveBuildProducts() throws -> [BuildProduct] { + let resolver = BuildProductsResolver(descriptionPackage: self) + return try resolver.resolveBuildProducts() } } @@ -186,7 +188,7 @@ struct BuildProduct: Hashable, Sendable { private final class BuildProductsResolver { - private var visitedTargets: Set = [] + private var buildProductsCache: [BuildProduct: Set] = [:] let descriptionPackage: DescriptionPackage init(descriptionPackage: DescriptionPackage) { @@ -225,7 +227,7 @@ private final class BuildProductsResolver { } } catch { switch error { - case GraphError.unexpectedCycle: throw Error.cycleDetected + case GraphError.unexpectedCycle: throw DescriptionPackage.Error.cycleDetected default: throw error } } @@ -234,7 +236,7 @@ private final class BuildProductsResolver { } private func targetsToBuild() throws -> [ScipioResolvedModule] { - switch mode { + switch descriptionPackage.mode { case .createPackage: // In create mode, all products should be built // In future update, users will be enable to specify products want to build @@ -258,8 +260,8 @@ private final class BuildProductsResolver { } private func fetchRootPackage() throws -> ResolvedPackage { - guard let rootPackage = graph.rootPackages.first else { - throw Error.packageNotDefined + guard let rootPackage = descriptionPackage.graph.rootPackages.first else { + throw DescriptionPackage.Error.packageNotDefined } return rootPackage } @@ -273,7 +275,7 @@ private final class BuildProductsResolver { .flatMap(buildProducts(from:))) #endif - switch mode { + switch descriptionPackage.mode { case .createPackage: // In create mode, rootTarget should be built let rootTargetProducts = try buildProducts(from: rootTarget) @@ -285,16 +287,25 @@ private final class BuildProductsResolver { } private func buildProducts(from target: ScipioResolvedModule) throws -> Set { - guard let package = graph.package(for: target) else { + guard let package = descriptionPackage.graph.package(for: target) else { return [] } let rootTargetProduct = BuildProduct(package: package, target: target) + + if let buildProducts = buildProductsCache[rootTargetProduct] { + return buildProducts + } + #if compiler(>=6.0) let dependencyProducts = try target.recursiveDependencies().compactMap(\.module).flatMap(buildProducts(from:)) #else let dependencyProducts = try target.recursiveDependencies().compactMap(\.target).flatMap(buildProducts(from:)) #endif - return Set([rootTargetProduct] + dependencyProducts) + + let buildProducts = Set([rootTargetProduct] + dependencyProducts) + buildProductsCache.updateValue(buildProducts, forKey: rootTargetProduct) + + return buildProducts } }