diff --git a/Sources/apk/Graph/ApkPackageGraph.swift b/Sources/apk/Graph/ApkPackageGraph.swift index 99a0721..09c79dc 100644 --- a/Sources/apk/Graph/ApkPackageGraph.swift +++ b/Sources/apk/Graph/ApkPackageGraph.swift @@ -24,33 +24,27 @@ public class ApkPackageGraph { for (idx, package) in self.pkgIndex.packages.enumerated() { provides[package.name] = idx 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() { + 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, id: id, - children: package.dependencies.compactMap { dependency in - 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 )) - } + children: children )) } var reverseDependencies = [ApkIndexRequirementRef: [ApkIndexRequirementRef]]() + for (index, node) in self._nodes.enumerated() { for child in node.children { reverseDependencies[child, default: []].append( @@ -60,6 +54,7 @@ public class ApkPackageGraph { } for (ref, parents) in reverseDependencies { + let package = self._nodes[ref.packageID].package self._nodes[ref.packageID].parents = parents } } @@ -67,30 +62,31 @@ public class ApkPackageGraph { extension ApkPackageGraph { func findDependencyCycle(node: ApkPackageGraphNode) -> (ApkPackageGraphNode, ApkPackageGraphNode)? { - var resolving = Set() - var visited = Set() + var resolving = Set() + var visited = Set() return self.findDependencyCycle(node: node, &resolving, &visited) } func findDependencyCycle( node: ApkPackageGraphNode, - _ resolving: inout Set, - _ visited: inout Set + _ resolving: inout Set, + _ visited: inout Set ) -> (ApkPackageGraphNode, ApkPackageGraphNode)? { for dependency in node.children { 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) } - if !visited.contains(depNode) { - resolving.insert(depNode) + if !visited.contains(depNode.packageID) { + resolving.insert(depNode.packageID) if let cycle = findDependencyCycle(node: depNode, &resolving, &visited) { return cycle } - resolving.remove(depNode) - visited.insert(depNode) + resolving.remove(depNode.packageID) + visited.insert(depNode.packageID) } } @@ -121,12 +117,13 @@ extension ApkPackageGraph { working[$0] = .init() } - while true { + while !working.isEmpty { // Set of all nodes now with satisfied dependencies var set = working .filter { _, children in children.isEmpty } .map(\.key) - + + print("set \(set.count) working \(working.count)") // If nothing was satisfied in this loop, check for cycles // If no cycles exist and the working set is empty, resolve is complete if set.isEmpty { @@ -137,18 +134,21 @@ extension ApkPackageGraph { let cycles = working.keys.compactMap { node in self.findDependencyCycle(node: node) } - + // Error if cycle breaking is turned off if !breakCycles { throw .cyclicDependency(cycles: cycles.map { node, dependency in "\(node) -> \(dependency)" }.joined(separator: "\n")) } - - // Bread cycles by setting the new resolution set to dependencies that cycled - set = cycles.map(\.1) + + // Break cycles by setting the new resolution set to dependencies that cycled + 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 results.append(set) @@ -158,10 +158,12 @@ extension ApkPackageGraph { }.reduce(into: [ApkPackageGraphNode: Set]()) { d, node in 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 } diff --git a/Sources/apk/Index/ApkIndexRequirementRef.swift b/Sources/apk/Index/ApkIndexRequirementRef.swift index 2869644..b1d2cad 100644 --- a/Sources/apk/Index/ApkIndexRequirementRef.swift +++ b/Sources/apk/Index/ApkIndexRequirementRef.swift @@ -22,6 +22,10 @@ struct ApkIndexRequirementRef { func satisfied(by other: ApkVersionRequirement) -> Bool { true } + + func normalize() -> ApkIndexRequirementRef { + .init(self._graph!, id: self.packageID, constraint: .dep(version: .any)) + } } extension ApkIndexRequirementRef: Equatable, Hashable { diff --git a/Sources/dpk-cli/Subcommands/DpkGraphCommand.swift b/Sources/dpk-cli/Subcommands/DpkGraphCommand.swift index 5d34652..15801ee 100644 --- a/Sources/dpk-cli/Subcommands/DpkGraphCommand.swift +++ b/Sources/dpk-cli/Subcommands/DpkGraphCommand.swift @@ -32,7 +32,7 @@ struct DpkGraphCommand: AsyncParsableCommand { #else do { let sorted = try graph.parallelOrderSort() - print(sorted) + print(sorted.count) } catch { fatalError(error.localizedDescription) }