private static bool WalkTreeRejectNodesOfRejectedNodes <TItem>(bool state, GraphNode <TItem> node, Tracker <TItem> context) { if (!state || node.Disposition == Disposition.Rejected) { // Mark all nodes as rejected if they aren't already marked node.Disposition = Disposition.Rejected; return(false); } context.Track(node.Item); return(true); }
private static void RejectCentralTransitiveBecauseOfRejectedParents <TItem>(this GraphNode <TItem> root, Tracker <TItem> tracker, List <GraphNode <TItem> > centralTransitiveNodes) { HashSet <GraphNode <TItem> > internalContext = new HashSet <GraphNode <TItem> >(); // reject nodes of rejected nodes for the central transitive nodes and track nodes that were not yet rejected // as more nodes can be rejected do not track the nodes until all the rejects are completed int ctdCount = centralTransitiveNodes.Count; for (int i = 0; i < ctdCount; i++) { centralTransitiveNodes[i].ForEach(root.Disposition != Disposition.Rejected, (node, state, context) => WalkTreeRejectNodesOfRejectedNodes(state, node, context), internalContext); } // If a node has its parents rejected, reject the node and its children // Need to do this in a loop because more nodes can be rejected as their parents become rejected bool pendingRejections = true; while (pendingRejections) { pendingRejections = false; for (int i = 0; i < ctdCount; i++) { if (centralTransitiveNodes[i].Disposition == Disposition.Acceptable && centralTransitiveNodes[i].AreAllParentsRejected()) { centralTransitiveNodes[i].ForEach(n => n.Disposition = Disposition.Rejected); pendingRejections = true; } } } // now add all the not rejected nodes to the tracker foreach (var node in internalContext) { if (node.Disposition != Disposition.Rejected) { tracker.Track(node.Item); } } }
private static bool TryResolveConflicts <TItem>(this GraphNode <TItem> root, List <VersionConflictResult <TItem> > versionConflicts) { // now we walk the tree as often as it takes to determine // which paths are accepted or rejected, based on conflicts occuring // between cousin packages var acceptedLibraries = new Dictionary <string, GraphNode <TItem> >(StringComparer.OrdinalIgnoreCase); var patience = 1000; var incomplete = true; while (incomplete && --patience != 0) { // Create a picture of what has not been rejected yet var tracker = new Tracker <TItem>(); root.ForEach(true, (node, state) => { if (!state || node.Disposition == Disposition.Rejected) { // Mark all nodes as rejected if they aren't already marked node.Disposition = Disposition.Rejected; return(false); } tracker.Track(node.Item); return(true); }); // Inform tracker of ambiguity beneath nodes that are not resolved yet // between: // a1->b1->d1->x1 // a1->c1->d2->z1 // first attempt // d1/d2 are considered disputed // x1 and z1 are considered ambiguous // d1 is rejected // second attempt // d1 is rejected, d2 is accepted // x1 is no longer seen, and z1 is not ambiguous // z1 is accepted root.ForEach(WalkState.Walking, (node, state) => { if (node.Disposition == Disposition.Rejected) { return(WalkState.Rejected); } if (state == WalkState.Walking && tracker.IsDisputed(node.Item)) { return(WalkState.Ambiguous); } if (state == WalkState.Ambiguous) { tracker.MarkAmbiguous(node.Item); } return(state); }); // Now mark unambiguous nodes as accepted or rejected root.ForEach(true, (node, state) => { if (!state || node.Disposition == Disposition.Rejected) { return(false); } if (tracker.IsAmbiguous(node.Item)) { return(false); } if (node.Disposition == Disposition.Acceptable) { if (tracker.IsBestVersion(node.Item)) { node.Disposition = Disposition.Accepted; acceptedLibraries[node.Key.Name] = node; } else { node.Disposition = Disposition.Rejected; } } return(node.Disposition == Disposition.Accepted); }); incomplete = false; root.ForEach(node => incomplete |= node.Disposition == Disposition.Acceptable); } root.ForEach(node => { if (node.Disposition != Disposition.Accepted) { return; } // For all accepted nodes, find dependencies that aren't satisfied by the version // of the package that we have selected foreach (var childNode in node.InnerNodes) { GraphNode <TItem> acceptedNode; if (acceptedLibraries.TryGetValue(childNode.Key.Name, out acceptedNode) && childNode != acceptedNode && childNode.Key.VersionRange != null && acceptedNode.Item.Key.Version != null) { var acceptedType = LibraryDependencyTargetUtils.Parse(acceptedNode.Item.Key.Type); var childType = childNode.Key.TypeConstraint; // Check the type constraints, if there is any overlap check for conflict if ((childType & acceptedType) != LibraryDependencyTarget.None) { var versionRange = childNode.Key.VersionRange; var checkVersion = acceptedNode.Item.Key.Version; if (!versionRange.Satisfies(checkVersion)) { versionConflicts.Add(new VersionConflictResult <TItem> { Selected = acceptedNode, Conflicting = childNode }); } } } } }); return(!incomplete); }
public static bool TryResolveConflicts <TItem>(this GraphNode <TItem> root) { // now we walk the tree as often as it takes to determine // which paths are accepted or rejected, based on conflicts occuring // between cousin packages var patience = 1000; var incomplete = true; while (incomplete && --patience != 0) { // Create a picture of what has not been rejected yet var tracker = new Tracker <TItem>(); root.ForEach(true, (node, state) => { if (!state || node.Disposition == Disposition.Rejected) { // Mark all nodes as rejected if they aren't already marked node.Disposition = Disposition.Rejected; return(false); } tracker.Track(node.Item); return(true); }); // Inform tracker of ambiguity beneath nodes that are not resolved yet // between: // a1->b1->d1->x1 // a1->c1->d2->z1 // first attempt // d1/d2 are considered disputed // x1 and z1 are considered ambiguous // d1 is rejected // second attempt // d1 is rejected, d2 is accepted // x1 is no longer seen, and z1 is not ambiguous // z1 is accepted root.ForEach(WalkState.Walking, (node, state) => { if (node.Disposition == Disposition.Rejected) { return(WalkState.Rejected); } if (state == WalkState.Walking && tracker.IsDisputed(node.Item)) { return(WalkState.Ambiguous); } if (state == WalkState.Ambiguous) { tracker.MarkAmbiguous(node.Item); } return(state); }); // Now mark unambiguous nodes as accepted or rejected root.ForEach(true, (node, state) => { if (!state || node.Disposition == Disposition.Rejected) { return(false); } if (tracker.IsAmbiguous(node.Item)) { return(false); } if (node.Disposition == Disposition.Acceptable) { node.Disposition = tracker.IsBestVersion(node.Item) ? Disposition.Accepted : Disposition.Rejected; } return(node.Disposition == Disposition.Accepted); }); incomplete = false; root.ForEach(node => incomplete |= node.Disposition == Disposition.Acceptable); } return(!incomplete); }