fix package graph

This commit is contained in:
Alex Zenla
2025-02-16 17:48:04 -08:00
committed by a dinosaur
parent d8a17ffd24
commit a8e16a5183
3 changed files with 42 additions and 36 deletions

View File

@ -24,33 +24,27 @@ public class ApkPackageGraph {
for (idx, package) in self.pkgIndex.packages.enumerated() { for (idx, package) in self.pkgIndex.packages.enumerated() {
provides[package.name] = idx provides[package.name] = idx
for provision in package.provides { for provision in package.provides {
provides[provision.name] = idx if !provides.keys.contains(provision.name) {
provides[provision.name] = idx
}
} }
} }
for (id, package) in pkgIndex.packages.enumerated() { for (id, package) in pkgIndex.packages.enumerated() {
let children: [ApkIndexRequirementRef] = package.dependencies.filter { dependency in dependency.requirement.versionSpec != .conflict }.compactMap { dependency in
guard let id = provides[dependency.requirement.name] else {
return nil
}
return .init(self, id: id, constraint: .dep(version: dependency.requirement.versionSpec))
}
self._nodes.append(.init(self, self._nodes.append(.init(self,
id: id, id: id,
children: package.dependencies.compactMap { dependency in children: children
guard let id = provides[dependency.requirement.name] else {
return nil
}
return .init(self, id: id, constraint: .dep(version: dependency.requirement.versionSpec))
} + package.provides.compactMap { provision in
guard let id = provides[provision.name] else {
return nil
}
return .init(self, id: id, constraint: .provision)
} + package.installIf.compactMap { installIf in
guard let id = provides[installIf.requirement.name] else {
return nil
}
return .init(self, id: id, constraint: .installIf(version: installIf.requirement.versionSpec ))
}
)) ))
} }
var reverseDependencies = [ApkIndexRequirementRef: [ApkIndexRequirementRef]]() var reverseDependencies = [ApkIndexRequirementRef: [ApkIndexRequirementRef]]()
for (index, node) in self._nodes.enumerated() { for (index, node) in self._nodes.enumerated() {
for child in node.children { for child in node.children {
reverseDependencies[child, default: []].append( reverseDependencies[child, default: []].append(
@ -60,6 +54,7 @@ public class ApkPackageGraph {
} }
for (ref, parents) in reverseDependencies { for (ref, parents) in reverseDependencies {
let package = self._nodes[ref.packageID].package
self._nodes[ref.packageID].parents = parents self._nodes[ref.packageID].parents = parents
} }
} }
@ -67,30 +62,31 @@ public class ApkPackageGraph {
extension ApkPackageGraph { extension ApkPackageGraph {
func findDependencyCycle(node: ApkPackageGraphNode) -> (ApkPackageGraphNode, ApkPackageGraphNode)? { func findDependencyCycle(node: ApkPackageGraphNode) -> (ApkPackageGraphNode, ApkPackageGraphNode)? {
var resolving = Set<ApkPackageGraphNode>() var resolving = Set<Int>()
var visited = Set<ApkPackageGraphNode>() var visited = Set<Int>()
return self.findDependencyCycle(node: node, &resolving, &visited) return self.findDependencyCycle(node: node, &resolving, &visited)
} }
func findDependencyCycle( func findDependencyCycle(
node: ApkPackageGraphNode, node: ApkPackageGraphNode,
_ resolving: inout Set<ApkPackageGraphNode>, _ resolving: inout Set<Int>,
_ visited: inout Set<ApkPackageGraphNode> _ visited: inout Set<Int>
) -> (ApkPackageGraphNode, ApkPackageGraphNode)? { ) -> (ApkPackageGraphNode, ApkPackageGraphNode)? {
for dependency in node.children { for dependency in node.children {
let depNode = self._nodes[dependency.packageID] let depNode = self._nodes[dependency.packageID]
if resolving.contains(depNode) { if resolving.contains(depNode.packageID) {
print("VIA \(resolving.map({ self._nodes[$0].package.name } )) CYCLE \(node.package.name) \(depNode.package.name)")
return (node, depNode) return (node, depNode)
} }
if !visited.contains(depNode) { if !visited.contains(depNode.packageID) {
resolving.insert(depNode) resolving.insert(depNode.packageID)
if let cycle = findDependencyCycle(node: depNode, &resolving, &visited) { if let cycle = findDependencyCycle(node: depNode, &resolving, &visited) {
return cycle return cycle
} }
resolving.remove(depNode) resolving.remove(depNode.packageID)
visited.insert(depNode) visited.insert(depNode.packageID)
} }
} }
@ -121,12 +117,13 @@ extension ApkPackageGraph {
working[$0] = .init() working[$0] = .init()
} }
while true { while !working.isEmpty {
// Set of all nodes now with satisfied dependencies // Set of all nodes now with satisfied dependencies
var set = working var set = working
.filter { _, children in children.isEmpty } .filter { _, children in children.isEmpty }
.map(\.key) .map(\.key)
print("set \(set.count) working \(working.count)")
// If nothing was satisfied in this loop, check for cycles // If nothing was satisfied in this loop, check for cycles
// If no cycles exist and the working set is empty, resolve is complete // If no cycles exist and the working set is empty, resolve is complete
if set.isEmpty { if set.isEmpty {
@ -137,18 +134,21 @@ extension ApkPackageGraph {
let cycles = working.keys.compactMap { node in let cycles = working.keys.compactMap { node in
self.findDependencyCycle(node: node) self.findDependencyCycle(node: node)
} }
// Error if cycle breaking is turned off // Error if cycle breaking is turned off
if !breakCycles { if !breakCycles {
throw .cyclicDependency(cycles: cycles.map { node, dependency in throw .cyclicDependency(cycles: cycles.map { node, dependency in
"\(node) -> \(dependency)" "\(node) -> \(dependency)"
}.joined(separator: "\n")) }.joined(separator: "\n"))
} }
// Bread cycles by setting the new resolution set to dependencies that cycled // Break cycles by setting the new resolution set to dependencies that cycled
set = cycles.map(\.1) set = cycles.flatMap { [$0.0, $0.1] }
set = Array(Set(set))
} }
print(set.map({ $0.package.name + " " + String($0.packageID) }))
// Add installation set to list of installation sets // Add installation set to list of installation sets
results.append(set) results.append(set)
@ -158,10 +158,12 @@ extension ApkPackageGraph {
}.reduce(into: [ApkPackageGraphNode: Set<ApkPackageGraphNode>]()) { d, node in }.reduce(into: [ApkPackageGraphNode: Set<ApkPackageGraphNode>]()) { d, node in
d[node.key] = node.value.subtracting(set) d[node.key] = node.value.subtracting(set)
} }
for (what, deps) in working {
print("\(what.packageID) \(what.package.name): \(deps.map { $0.package.name + " " + String($0.packageID) })")
}
} }
print(working)
return results return results
} }

View File

@ -22,6 +22,10 @@ struct ApkIndexRequirementRef {
func satisfied(by other: ApkVersionRequirement) -> Bool { func satisfied(by other: ApkVersionRequirement) -> Bool {
true true
} }
func normalize() -> ApkIndexRequirementRef {
.init(self._graph!, id: self.packageID, constraint: .dep(version: .any))
}
} }
extension ApkIndexRequirementRef: Equatable, Hashable { extension ApkIndexRequirementRef: Equatable, Hashable {

View File

@ -32,7 +32,7 @@ struct DpkGraphCommand: AsyncParsableCommand {
#else #else
do { do {
let sorted = try graph.parallelOrderSort() let sorted = try graph.parallelOrderSort()
print(sorted) print(sorted.count)
} catch { } catch {
fatalError(error.localizedDescription) fatalError(error.localizedDescription)
} }