Пример #1
0
        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 = Cache <TItem> .RentDictionary();

            var patience   = 1000;
            var incomplete = true;

            var tracker = Cache <TItem> .RentTracker();

            Func <GraphNode <TItem>, bool> skipNode = null;

            var centralTransitiveNodes           = root.InnerNodes.Where(n => n.Item.IsCentralTransitive).ToList();
            var hasCentralTransitiveDependencies = centralTransitiveNodes.Count > 0;

            if (hasCentralTransitiveDependencies)
            {
                skipNode = (node) => { return(node.Item.IsCentralTransitive); };
            }

            while (incomplete && --patience != 0)
            {
                // Create a picture of what has not been rejected yet
                root.ForEach(true, (node, state, context) => WalkTreeRejectNodesOfRejectedNodes(state, node, context), tracker, skipNode);

                if (hasCentralTransitiveDependencies)
                {
                    // Some of the central transitive nodes may be rejected now because their parents were rejected
                    // Reject them accordingly
                    root.RejectCentralTransitiveBecauseOfRejectedParents(tracker, centralTransitiveNodes);
                }

                // Inform tracker of ambiguity beneath nodes that are not resolved yet
                root.ForEach(WalkState.Walking, (node, state, context) => WalkTreeMarkAmbiguousNodes(node, state, context), tracker);

                if (hasCentralTransitiveDependencies)
                {
                    DetectAndMarkAmbiguousCentralTransitiveDependencies(tracker, centralTransitiveNodes);
                }

                root.ForEach(true, (node, state, context) => WalkTreeAcceptOrRejectNodes(context, state, node), CreateState(tracker, acceptedLibraries));

                incomplete = root.ForEachGlobalState(false, (node, state) => state || node.Disposition == Disposition.Acceptable);

                tracker.Clear();
            }

            Cache <TItem> .ReleaseTracker(tracker);

            root.ForEach((node, context) => WalkTreeDectectConflicts(node, context), CreateState(versionConflicts, acceptedLibraries));

            Cache <TItem> .ReleaseDictionary(acceptedLibraries);

            return(!incomplete);
        }
Пример #2
0
        private static void CheckCycleAndNearestWins(
            this GraphNode <RemoteResolveResult> root,
            List <DowngradeResult <RemoteResolveResult> > downgrades,
            List <GraphNode <RemoteResolveResult> > cycles)
        {
            var workingDowngrades = RentDowngradesDictionary();

            root.ForEach((node, context) => WalkTreeCheckCycleAndNearestWins(context, node), CreateState(cycles, workingDowngrades));

#if IS_DESKTOP || NETSTANDARD2_0
            // Increase List size for items to be added, if too small
            var requiredCapacity = downgrades.Count + workingDowngrades.Count;
            if (downgrades.Capacity < requiredCapacity)
            {
                downgrades.Capacity = requiredCapacity;
            }
#endif
            foreach (var p in workingDowngrades)
            {
                downgrades.Add(new DowngradeResult <RemoteResolveResult>
                {
                    DowngradedFrom = p.Key,
                    DowngradedTo   = p.Value
                });
            }

            ReleaseDowngradesDictionary(workingDowngrades);
        }
Пример #3
0
        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 = Cache <TItem> .RentDictionary();

            var patience   = 1000;
            var incomplete = true;

            var tracker = Cache <TItem> .RentTracker();

            while (incomplete && --patience != 0)
            {
                // Create a picture of what has not been rejected yet
                root.ForEach(true, (node, state, context) => WalkTreeRejectNodesOfRejectedNodes(state, node, context), tracker);

                // Inform tracker of ambiguity beneath nodes that are not resolved yet
                root.ForEach(WalkState.Walking, (node, state, context) => WalkTreeMarkAmbiguousNodes(node, state, context), tracker);

                // Now mark unambiguous nodes as accepted or rejected
                root.ForEach(true, (node, state, context) => WalkTreeAcceptOrRejectNodes(context, state, node), CreateState(tracker, acceptedLibraries));

                incomplete = root.ForEachGlobalState(false, (node, state) => state || node.Disposition == Disposition.Acceptable);

                tracker.Clear();
            }

            Cache <TItem> .ReleaseTracker(tracker);

            root.ForEach((node, context) => WalkTreeDectectConflicts(node, context), CreateState(versionConflicts, acceptedLibraries));

            Cache <TItem> .ReleaseDictionary(acceptedLibraries);

            return(!incomplete);
        }
        public GraphNode <Library> Walk(string name, NuGetVersion version, NuGetFramework framework)
        {
            var key = new LibraryRange
            {
                Name         = name,
                VersionRange = new VersionRange(version)
            };

            var root = new GraphNode <Library>(key);

            var resolvedItems = new Dictionary <LibraryRange, GraphItem <Library> >();

            // Recurse through dependencies optimistically, asking resolvers for dependencies
            // based on best match of each encountered dependency
            root.ForEach(node =>
            {
                node.Item = Resolve(resolvedItems, node.Key, framework);
                if (node.Item == null)
                {
                    node.Disposition = Disposition.Rejected;
                    return;
                }

                foreach (var dependency in node.Item.Data.Dependencies)
                {
                    // determine if a child dependency is eclipsed by
                    // a reference on the line leading to this point. this
                    // prevents cyclical dependencies, and also implements the
                    // "nearest wins" rule.

                    var eclipsed = false;
                    for (var scanNode = node;
                         scanNode != null && !eclipsed;
                         scanNode = scanNode.OuterNode)
                    {
                        eclipsed |= scanNode.Key.IsEclipsedBy(dependency.LibraryRange);

                        if (eclipsed)
                        {
                            throw new InvalidOperationException(string.Format("Circular dependency detected {0}.", GetChain(node, dependency)));
                        }

                        foreach (var sideNode in scanNode.InnerNodes)
                        {
                            eclipsed |= sideNode.Key.IsEclipsedBy(dependency.LibraryRange);

                            if (eclipsed)
                            {
                                break;
                            }
                        }
                    }

                    if (!eclipsed)
                    {
                        var innerNode = new GraphNode <Library>(dependency.LibraryRange)
                        {
                            OuterNode = node
                        };

                        node.InnerNodes.Add(innerNode);
                    }
                }
            });

            return(root);
        }
Пример #5
0
        private static void CheckCycleAndNearestWins <TItem>(this GraphNode <TItem> root,
                                                             List <DowngradeResult <TItem> > downgrades,
                                                             List <GraphNode <TItem> > cycles)
        {
            // Cycle

            // A -> B -> A (cycle)

            // Downgrade

            // A -> B -> C -> D 2.0 (downgrage)
            //        -> D 1.0

            // Potential downgrade that turns out to not downgrade
            // This should never happen in practice since B would have never been valid to begin with.

            // A -> B -> C -> D 2.0
            //        -> D 1.0
            //   -> D 2.0

            var workingDowngrades = new Dictionary <GraphNode <TItem>, GraphNode <TItem> >();

            root.ForEach(node =>
            {
                if (node.Disposition == Disposition.Cycle)
                {
                    cycles.Add(node);

                    // Remove this node from the tree so the nothing else evaluates this.
                    // This is ok since we have a parent pointer and we can still print the path
                    node.OuterNode.InnerNodes.Remove(node);

                    return;
                }

                if (node.Disposition != Disposition.PotentiallyDowngraded)
                {
                    return;
                }

                // REVIEW: This could probably be done in a single pass where we keep track
                // of what is nearer as we walk down the graph (BFS)
                for (var n = node.OuterNode; n != null; n = n.OuterNode)
                {
                    foreach (var sideNode in n.InnerNodes)
                    {
                        if (sideNode != node && StringComparer.OrdinalIgnoreCase.Equals(sideNode.Key.Name, node.Key.Name))
                        {
                            // Nodes that have no version range should be ignored as potential downgrades e.g. framework reference
                            if (sideNode.Key.VersionRange != null &&
                                node.Key.VersionRange != null &&
                                !RemoteDependencyWalker.IsGreaterThanOrEqualTo(sideNode.Key.VersionRange, node.Key.VersionRange))
                            {
                                workingDowngrades[node] = sideNode;
                            }
                            else
                            {
                                workingDowngrades.Remove(node);
                            }
                        }
                    }
                }

                // Remove this node from the tree so the nothing else evaluates this.
                // This is ok since we have a parent pointer and we can still print the path
                node.OuterNode.InnerNodes.Remove(node);
            });

            downgrades.AddRange(workingDowngrades.Select(p => new DowngradeResult <TItem>
            {
                DowngradedFrom = p.Key,
                DowngradedTo   = p.Value
            }));
        }
Пример #6
0
        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);
        }
Пример #7
0
        private static void CheckCycleAndNearestWins(
            this GraphNode <RemoteResolveResult> root,
            List <DowngradeResult <RemoteResolveResult> > downgrades,
            List <GraphNode <RemoteResolveResult> > cycles)
        {
            // Cycle:
            //
            // A -> B -> A (cycle)
            //
            // Downgrade:
            //
            // A -> B -> C -> D 2.0 (downgrade)
            //        -> D 1.0
            //
            // Potential downgrades that turns out to not be downgrades:
            //
            // 1. This should never happen in practice since B would have never been valid to begin with.
            //
            //    A -> B -> C -> D 2.0
            //           -> D 1.0
            //      -> D 2.0
            //
            // 2. This occurs if none of the sources have version C 1.0 so C 1.0 is bumped up to C 2.0.
            //
            //   A -> B -> C 2.0
            //     -> C 1.0

            var workingDowngrades = new Dictionary <GraphNode <RemoteResolveResult>, GraphNode <RemoteResolveResult> >();

            root.ForEach(node =>
            {
                if (node.Disposition == Disposition.Cycle)
                {
                    cycles.Add(node);

                    // Remove this node from the tree so the nothing else evaluates this.
                    // This is ok since we have a parent pointer and we can still print the path
                    node.OuterNode.InnerNodes.Remove(node);

                    return;
                }

                if (node.Disposition != Disposition.PotentiallyDowngraded)
                {
                    return;
                }

                // REVIEW: This could probably be done in a single pass where we keep track
                // of what is nearer as we walk down the graph (BFS)
                for (var n = node.OuterNode; n != null; n = n.OuterNode)
                {
                    foreach (var sideNode in n.InnerNodes)
                    {
                        if (sideNode != node && StringComparer.OrdinalIgnoreCase.Equals(sideNode.Key.Name, node.Key.Name))
                        {
                            // Nodes that have no version range should be ignored as potential downgrades e.g. framework reference
                            if (sideNode.Key.VersionRange != null &&
                                node.Key.VersionRange != null &&
                                !RemoteDependencyWalker.IsGreaterThanOrEqualTo(sideNode.Key.VersionRange, node.Key.VersionRange))
                            {
                                // Is the resolved version actually within node's version range? This happen if there
                                // was a different request for a lower version of the library than this version range
                                // allows but no matching library was found, so the library is bumped up into this
                                // version range.
                                var resolvedVersion = sideNode?.Item?.Data?.Match?.Library?.Version;
                                if (resolvedVersion != null && node.Key.VersionRange.Satisfies(resolvedVersion))
                                {
                                    continue;
                                }

                                workingDowngrades[node] = sideNode;
                            }
                            else
                            {
                                workingDowngrades.Remove(node);
                            }
                        }
                    }
                }

                // Remove this node from the tree so the nothing else evaluates this.
                // This is ok since we have a parent pointer and we can still print the path
                node.OuterNode.InnerNodes.Remove(node);
            });

            downgrades.AddRange(workingDowngrades.Select(p => new DowngradeResult <RemoteResolveResult>
            {
                DowngradedFrom = p.Key,
                DowngradedTo   = p.Value
            }));
        }
Пример #8
0
        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);
        }
Пример #9
0
        public GraphNode <ResolveResult> Walk(string name, NuGetVersion version, NuGetFramework framework)
        {
            var key = new LibraryRange
            {
                Name         = name,
                VersionRange = new NuGetVersionRange(version)
            };

            var root = new GraphNode <ResolveResult>(key);

            var resolvedItems = new Dictionary <LibraryRange, GraphItem <ResolveResult> >();

            // Recurse through dependencies optimistically, asking resolvers for dependencies
            // based on best match of each encountered dependency
            root.ForEach(node =>
            {
                node.Item = Resolve(resolvedItems, node.Key, framework);
                if (node.Item == null)
                {
                    node.Disposition = Disposition.Rejected;
                    return;
                }

                foreach (var dependency in node.Item.Data.LibraryDescription.Dependencies)
                {
                    // determine if a child dependency is eclipsed by
                    // a reference on the line leading to this point. this
                    // prevents cyclical dependencies, and also implements the
                    // "nearest wins" rule.

                    var eclipsed = false;
                    for (var scanNode = node;
                         scanNode != null && !eclipsed;
                         scanNode = scanNode.OuterNode)
                    {
                        eclipsed |= string.Equals(
                            scanNode.Key.Name,
                            dependency.Name,
                            StringComparison.OrdinalIgnoreCase);

                        if (eclipsed)
                        {
                            break;
                        }

                        foreach (var sideNode in scanNode.InnerNodes)
                        {
                            eclipsed |= string.Equals(
                                sideNode.Key.Name,
                                dependency.Name,
                                StringComparison.OrdinalIgnoreCase);

                            if (eclipsed)
                            {
                                break;
                            }
                        }
                    }

                    if (!eclipsed)
                    {
                        var innerNode = new GraphNode <ResolveResult>(dependency.LibraryRange)
                        {
                            OuterNode = node
                        };

                        node.InnerNodes.Add(innerNode);
                    }
                }
            });

            return(root);
        }