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); }