diff --git a/Sources/apk/Graph/ApkPackageGraph.swift b/Sources/apk/Graph/ApkPackageGraph.swift index 1539a5d..4d7403c 100644 --- a/Sources/apk/Graph/ApkPackageGraph.swift +++ b/Sources/apk/Graph/ApkPackageGraph.swift @@ -6,31 +6,27 @@ import Foundation public class ApkPackageGraph { - public var pkgIndex: ApkIndex - private var _nodes = [ApkPackageGraphNode]() public var nodes: [ApkPackageGraphNode] { self._nodes } public var shallowIsolates: [ApkPackageGraphNode] { self._nodes.filter(\.isShallow) } public var deepIsolates: [ApkPackageGraphNode] { self._nodes.filter(\.isDeep) } - public init(index: ApkIndex) { - self.pkgIndex = index - } + public init() {} - public func buildGraphNode() { + public func buildGraphNode(index pkgIndex: ApkIndex, providers: ApkIndexProviderCache) { for (packageID, package) in pkgIndex.packages.enumerated() { let children: [ApkPackageGraphNode.ChildRef] = package.dependencies.compactMap { dependency in - guard let providerID = pkgIndex.resolveIndex(requirement: dependency.requirement) else { + guard let providerID = providers.resolve(index: pkgIndex, requirement: dependency.requirement) else { return nil } return .init(constraint: .dependency, packageID: providerID, versionSpec: dependency.requirement.versionSpec) - } + package.installIf.compactMap { installIf in - guard let prvID = pkgIndex.resolveIndex(requirement: installIf.requirement) else { + } /* + package.installIf.compactMap { installIf in + guard let prvID = providers.resolve(index: pkgIndex, requirement: installIf.requirement) else { return nil } return .init(constraint: .installIf, packageID: prvID, versionSpec: installIf.requirement.versionSpec) - } + } */ self._nodes.append(.init(self, id: packageID, children: children diff --git a/Sources/apk/Graph/ApkPackageGraphNode.swift b/Sources/apk/Graph/ApkPackageGraphNode.swift index b82f207..cb8c3ca 100644 --- a/Sources/apk/Graph/ApkPackageGraphNode.swift +++ b/Sources/apk/Graph/ApkPackageGraphNode.swift @@ -10,23 +10,10 @@ public class ApkPackageGraphNode { public var parentIDs = [ApkIndex.Index]() public var children: [ChildRef] - private weak var _graph: ApkPackageGraph? - - public var package: ApkIndexPackage { - self._graph!.pkgIndex.packages[self.packageID] - } - public var parents: [ApkIndexPackage] { - self.parentIDs.map { index in self._graph!.pkgIndex.packages[index] } - } - public var childPackages: [ApkIndexPackage] { - self.children.map { child in self._graph!.pkgIndex.packages[child.packageID] } - } - @inlinable public var isShallow: Bool { self.parentIDs.isEmpty } @inlinable public var isDeep: Bool { self.children.isEmpty } internal init(_ graph: ApkPackageGraph, id: Int, children: [ChildRef]) { - self._graph = graph self.packageID = id self.children = children } @@ -54,31 +41,8 @@ extension ApkPackageGraphNode { } } -extension ApkPackageGraphNode: CustomStringConvertible { - public var description: String { - let package = self.package - var result = " \(package.nameDescription):\n" - if !self.parentIDs.isEmpty { - result += " parents:\n" - for parent in self.parents { - result += " \(parent.nameDescription)\n" - } - } - if !self.children.isEmpty { - result += " children:\n" - for child in self.children { - let childPackage = self._graph!.pkgIndex.packages[child.packageID] - result += " " - switch child.constraint { - case .dependency: result += "dep=" - case .installIf: result += "installIf=" - } - result += childPackage.nameDescription - result += ", " - result += child.versionSpec.description - result += "\n" - } - } - return result +extension ApkIndex { + public func at(node: ApkPackageGraphNode) -> ApkIndexPackage { + self.packages[node.packageID] } } diff --git a/Sources/apk/Index/ApkIndex.swift b/Sources/apk/Index/ApkIndex.swift index bafb3ed..378f1af 100644 --- a/Sources/apk/Index/ApkIndex.swift +++ b/Sources/apk/Index/ApkIndex.swift @@ -8,12 +8,6 @@ import Foundation public struct ApkIndex: Sendable { public let packages: [ApkIndexPackage] public typealias Index = Array.Index - - lazy var providers: [(ApkIndexProvides, Index)] = { - self.packages.enumerated().flatMap { index, pkg in - [ (.specific(name: pkg.name, version: pkg.version), index) ] + pkg.provides.map { ($0, index) } - } - }() } public extension ApkIndex { @@ -28,24 +22,8 @@ public extension ApkIndex { $0.name == name } } - - mutating func resolve(requirement: ApkVersionRequirement) -> ApkIndexPackage? { - self.providers.filter { prv in prv.0.satisfies(requirement) } - .map { self.packages[$1] }.max() - } - - mutating func resolveIndex(requirement: ApkVersionRequirement) -> Index? { - self.providers.filter { prv in prv.0.satisfies(requirement) } - .max { self.packages[$0.1] < self.packages[$1.1] }?.1 - } } -extension ApkIndexPackage: Comparable { - public static func < (lhs: Self, rhs: Self) -> Bool { - // Prefer highest declared provider priority - lhs.providerPriority ?? 0 < rhs.providerPriority ?? 0 - } -} public extension ApkIndex { static func merge(_ tables: S) -> Self where S.Element == Self { diff --git a/Sources/apk/Index/ApkIndexProviderCache.swift b/Sources/apk/Index/ApkIndexProviderCache.swift new file mode 100644 index 0000000..6130fb6 --- /dev/null +++ b/Sources/apk/Index/ApkIndexProviderCache.swift @@ -0,0 +1,37 @@ +/* + * darwin-apk © 2025 Gay Pizza Specifications + * SPDX-License-Identifier: Apache-2.0 + */ + +import Foundation + +public struct ApkIndexProviderCache { + private var _providers = [(ApkIndexProvides, ApkIndex.Index)]() + //private var _installIfCache = [(ApkIndexInstallIf, Set)]() + + public init(index pkgIndex: ApkIndex) { + for (index, pkg) in pkgIndex.packages.enumerated() { + self._providers.append((.specific(name: pkg.name, version: pkg.version), index)) + for provision in pkg.provides { + self._providers.append((provision, index)) + } + //for installIf in pkg.installIf { + // self._installIfCache.append((installIf, index)) + //} + } + } +} + +extension ApkIndexProviderCache { + func resolve(index pkgIndex: ApkIndex, requirement: ApkVersionRequirement) -> ApkIndex.Index? { + self._providers.filter { prv in prv.0.satisfies(requirement) } + .max { pkgIndex.packages[$0.1] < pkgIndex.packages[$1.1] }?.1 + } +} + +extension ApkIndexPackage: Comparable { + public static func < (lhs: Self, rhs: Self) -> Bool { + // Prefer highest declared provider priority + lhs.providerPriority ?? 0 < rhs.providerPriority ?? 0 + } +} diff --git a/Sources/dpk-cli/Subcommands/DpkGraphCommand.swift b/Sources/dpk-cli/Subcommands/DpkGraphCommand.swift index 1c0d90d..fec57e5 100644 --- a/Sources/dpk-cli/Subcommands/DpkGraphCommand.swift +++ b/Sources/dpk-cli/Subcommands/DpkGraphCommand.swift @@ -11,23 +11,23 @@ struct DpkGraphCommand: AsyncParsableCommand { static let configuration = CommandConfiguration(commandName: "graph") func run() async throws(ExitCode) { - let graph: ApkPackageGraph do { let localRepositories = try await ApkRepositoriesConfig() var timerStart = DispatchTime.now() - graph = ApkPackageGraph(index: - try await ApkIndexReader.resolve(localRepositories.repositories, fetch: .lazy)) + let pkgIndex = try await ApkIndexReader.resolve(localRepositories.repositories, fetch: .lazy) print("Index build took \(timerStart.distance(to: .now()).seconds) seconds") - try graph.pkgIndex.description.write(to: URL(filePath: "packages.txt"), atomically: false, encoding: .utf8) + try pkgIndex.description.write(to: URL(filePath: "packages.txt"), atomically: false, encoding: .utf8) timerStart = DispatchTime.now() - graph.buildGraphNode() + let providerCache = ApkIndexProviderCache(index: pkgIndex) + let graph = ApkPackageGraph() + graph.buildGraphNode(index: pkgIndex, providers: providerCache) print("Graph build took \(timerStart.distance(to: .now()).seconds) seconds") - try graph.shallowIsolates.map { $0.package.nameDescription }.joined(separator: "\n") + try graph.shallowIsolates.map { pkgIndex.at(node: $0).nameDescription }.joined(separator: "\n") .write(to: URL(filePath: "shallowIsolates.txt"), atomically: false, encoding: .utf8) - try graph.deepIsolates.map { $0.package.nameDescription }.joined(separator: "\n") + try graph.deepIsolates.map { pkgIndex.at(node: $0).nameDescription }.joined(separator: "\n") .write(to: URL(filePath: "deepIsolates.txt"), atomically: false, encoding: .utf8) timerStart = DispatchTime.now() @@ -37,17 +37,19 @@ struct DpkGraphCommand: AsyncParsableCommand { if var out = TextFileWriter(URL(filePath: "sorted.txt")) { for (i, set) in sorted.enumerated() { - print("\(i):\n", to: &out) + print("\(i):", to: &out) for item in set { - print("\(item.description)", to: &out) + let pkg = pkgIndex.at(node: item) + print(" \(pkg.nameDescription)", to: &out) } + print(to: &out) } } #else let sorted = try graph.orderSort() print("Order sort took \(timerStart.distance(to: .now()).seconds) seconds") - try sorted.map(String.init).joined(separator: "\n") + try sorted.map { node in pkgIndex.at(node: node).nameDescription }.joined(separator: "\n") .write(to: URL(filePath: "sorted.txt"), atomically: false, encoding: .utf8) #endif } catch { @@ -64,8 +66,7 @@ fileprivate extension DispatchTimeInterval { case .microseconds(let value): Double(value) / 1_000_000 case .nanoseconds(let value): Double(value) / 1_000_000_000 case .never: .infinity - @unknown default: - fatalError("Unsupported") + @unknown default: fatalError("Unsupported") } } }