mirror of
https://github.com/GayPizzaSpecifications/darwin-apk.git
synced 2025-08-06 14:41:35 +00:00
Factor out resolution responsibilities
This commit is contained in:
@ -6,31 +6,27 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
public class ApkPackageGraph {
|
public class ApkPackageGraph {
|
||||||
public var pkgIndex: ApkIndex
|
|
||||||
|
|
||||||
private var _nodes = [ApkPackageGraphNode]()
|
private var _nodes = [ApkPackageGraphNode]()
|
||||||
|
|
||||||
public var nodes: [ApkPackageGraphNode] { self._nodes }
|
public var nodes: [ApkPackageGraphNode] { self._nodes }
|
||||||
public var shallowIsolates: [ApkPackageGraphNode] { self._nodes.filter(\.isShallow) }
|
public var shallowIsolates: [ApkPackageGraphNode] { self._nodes.filter(\.isShallow) }
|
||||||
public var deepIsolates: [ApkPackageGraphNode] { self._nodes.filter(\.isDeep) }
|
public var deepIsolates: [ApkPackageGraphNode] { self._nodes.filter(\.isDeep) }
|
||||||
|
|
||||||
public init(index: ApkIndex) {
|
public init() {}
|
||||||
self.pkgIndex = index
|
|
||||||
}
|
|
||||||
|
|
||||||
public func buildGraphNode() {
|
public func buildGraphNode(index pkgIndex: ApkIndex, providers: ApkIndexProviderCache) {
|
||||||
for (packageID, package) in pkgIndex.packages.enumerated() {
|
for (packageID, package) in pkgIndex.packages.enumerated() {
|
||||||
let children: [ApkPackageGraphNode.ChildRef] = package.dependencies.compactMap { dependency in
|
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 nil
|
||||||
}
|
}
|
||||||
return .init(constraint: .dependency, packageID: providerID, versionSpec: dependency.requirement.versionSpec)
|
return .init(constraint: .dependency, packageID: providerID, versionSpec: dependency.requirement.versionSpec)
|
||||||
} + package.installIf.compactMap { installIf in
|
} /* + package.installIf.compactMap { installIf in
|
||||||
guard let prvID = pkgIndex.resolveIndex(requirement: installIf.requirement) else {
|
guard let prvID = providers.resolve(index: pkgIndex, requirement: installIf.requirement) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return .init(constraint: .installIf, packageID: prvID, versionSpec: installIf.requirement.versionSpec)
|
return .init(constraint: .installIf, packageID: prvID, versionSpec: installIf.requirement.versionSpec)
|
||||||
}
|
} */
|
||||||
self._nodes.append(.init(self,
|
self._nodes.append(.init(self,
|
||||||
id: packageID,
|
id: packageID,
|
||||||
children: children
|
children: children
|
||||||
|
@ -10,23 +10,10 @@ public class ApkPackageGraphNode {
|
|||||||
public var parentIDs = [ApkIndex.Index]()
|
public var parentIDs = [ApkIndex.Index]()
|
||||||
public var children: [ChildRef]
|
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 isShallow: Bool { self.parentIDs.isEmpty }
|
||||||
@inlinable public var isDeep: Bool { self.children.isEmpty }
|
@inlinable public var isDeep: Bool { self.children.isEmpty }
|
||||||
|
|
||||||
internal init(_ graph: ApkPackageGraph, id: Int, children: [ChildRef]) {
|
internal init(_ graph: ApkPackageGraph, id: Int, children: [ChildRef]) {
|
||||||
self._graph = graph
|
|
||||||
self.packageID = id
|
self.packageID = id
|
||||||
self.children = children
|
self.children = children
|
||||||
}
|
}
|
||||||
@ -54,31 +41,8 @@ extension ApkPackageGraphNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ApkPackageGraphNode: CustomStringConvertible {
|
extension ApkIndex {
|
||||||
public var description: String {
|
public func at(node: ApkPackageGraphNode) -> ApkIndexPackage {
|
||||||
let package = self.package
|
self.packages[node.packageID]
|
||||||
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,12 +8,6 @@ import Foundation
|
|||||||
public struct ApkIndex: Sendable {
|
public struct ApkIndex: Sendable {
|
||||||
public let packages: [ApkIndexPackage]
|
public let packages: [ApkIndexPackage]
|
||||||
public typealias Index = Array<ApkIndexPackage>.Index
|
public typealias Index = Array<ApkIndexPackage>.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 {
|
public extension ApkIndex {
|
||||||
@ -28,24 +22,8 @@ public extension ApkIndex {
|
|||||||
$0.name == name
|
$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 {
|
public extension ApkIndex {
|
||||||
static func merge<S: Sequence>(_ tables: S) -> Self where S.Element == Self {
|
static func merge<S: Sequence>(_ tables: S) -> Self where S.Element == Self {
|
||||||
|
37
Sources/apk/Index/ApkIndexProviderCache.swift
Normal file
37
Sources/apk/Index/ApkIndexProviderCache.swift
Normal file
@ -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<ApkIndex.Index>)]()
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
@ -11,23 +11,23 @@ struct DpkGraphCommand: AsyncParsableCommand {
|
|||||||
static let configuration = CommandConfiguration(commandName: "graph")
|
static let configuration = CommandConfiguration(commandName: "graph")
|
||||||
|
|
||||||
func run() async throws(ExitCode) {
|
func run() async throws(ExitCode) {
|
||||||
let graph: ApkPackageGraph
|
|
||||||
do {
|
do {
|
||||||
let localRepositories = try await ApkRepositoriesConfig()
|
let localRepositories = try await ApkRepositoriesConfig()
|
||||||
|
|
||||||
var timerStart = DispatchTime.now()
|
var timerStart = DispatchTime.now()
|
||||||
graph = ApkPackageGraph(index:
|
let pkgIndex = try await ApkIndexReader.resolve(localRepositories.repositories, fetch: .lazy)
|
||||||
try await ApkIndexReader.resolve(localRepositories.repositories, fetch: .lazy))
|
|
||||||
print("Index build took \(timerStart.distance(to: .now()).seconds) seconds")
|
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()
|
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")
|
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)
|
.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)
|
.write(to: URL(filePath: "deepIsolates.txt"), atomically: false, encoding: .utf8)
|
||||||
|
|
||||||
timerStart = DispatchTime.now()
|
timerStart = DispatchTime.now()
|
||||||
@ -37,17 +37,19 @@ struct DpkGraphCommand: AsyncParsableCommand {
|
|||||||
|
|
||||||
if var out = TextFileWriter(URL(filePath: "sorted.txt")) {
|
if var out = TextFileWriter(URL(filePath: "sorted.txt")) {
|
||||||
for (i, set) in sorted.enumerated() {
|
for (i, set) in sorted.enumerated() {
|
||||||
print("\(i):\n", to: &out)
|
print("\(i):", to: &out)
|
||||||
for item in set {
|
for item in set {
|
||||||
print("\(item.description)", to: &out)
|
let pkg = pkgIndex.at(node: item)
|
||||||
|
print(" \(pkg.nameDescription)", to: &out)
|
||||||
}
|
}
|
||||||
|
print(to: &out)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
let sorted = try graph.orderSort()
|
let sorted = try graph.orderSort()
|
||||||
print("Order sort took \(timerStart.distance(to: .now()).seconds) seconds")
|
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)
|
.write(to: URL(filePath: "sorted.txt"), atomically: false, encoding: .utf8)
|
||||||
#endif
|
#endif
|
||||||
} catch {
|
} catch {
|
||||||
@ -64,8 +66,7 @@ fileprivate extension DispatchTimeInterval {
|
|||||||
case .microseconds(let value): Double(value) / 1_000_000
|
case .microseconds(let value): Double(value) / 1_000_000
|
||||||
case .nanoseconds(let value): Double(value) / 1_000_000_000
|
case .nanoseconds(let value): Double(value) / 1_000_000_000
|
||||||
case .never: .infinity
|
case .never: .infinity
|
||||||
@unknown default:
|
@unknown default: fatalError("Unsupported")
|
||||||
fatalError("Unsupported")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user