mirror of
				https://github.com/GayPizzaSpecifications/darwin-apk.git
				synced 2025-11-04 07:59:38 +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