| File: ResolverComparer.cs | Web Access |
| Project: src\nuget-client\src\NuGet.Core\NuGet.Resolver\NuGet.Resolver.csproj (NuGet.Resolver) |
// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. #nullable disable using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using NuGet.Packaging.Core; using NuGet.Versioning; namespace NuGet.Resolver { public class ResolverComparer : IComparer<ResolverPackage> { private readonly DependencyBehavior _dependencyBehavior; private readonly HashSet<PackageIdentity> _preferredVersions; private readonly HashSet<string> _targetIds; private readonly IVersionComparer _versionComparer; private readonly PackageIdentityComparer _identityComparer; private readonly Dictionary<string, NuGetVersion> _installedVersions; public ResolverComparer(DependencyBehavior dependencyBehavior, HashSet<PackageIdentity> preferredVersions, HashSet<string> targetIds) { _dependencyBehavior = dependencyBehavior; _preferredVersions = preferredVersions; _targetIds = targetIds; _versionComparer = VersionComparer.Default; _identityComparer = PackageIdentity.Comparer; _installedVersions = new Dictionary<string, NuGetVersion>(); if (_preferredVersions != null) { foreach (var package in _preferredVersions) { if (package.Version != null) { _installedVersions.Add(package.Id, package.Version); } } } } public int Compare(ResolverPackage x, ResolverPackage y) { if (Object.ReferenceEquals(x, y)) { return 0; } Debug.Assert(string.Equals(x.Id, y.Id, StringComparison.OrdinalIgnoreCase)); // The absent package comes first in the sort order var isXAbsent = x.Absent; var isYAbsent = y.Absent; if (isXAbsent && !isYAbsent) { return -1; } if (!isXAbsent && isYAbsent) { return 1; } if (isXAbsent && isYAbsent) { return 0; } if (_preferredVersions != null) { //Already installed packages come next in the sort order. var xInstalled = _preferredVersions.Contains(x, _identityComparer); var yInstalled = _preferredVersions.Contains(y, _identityComparer); if (xInstalled && !yInstalled) { return -1; } if (!xInstalled && yInstalled) { return 1; } } //Prefer listed packages over unlisted if (x.Listed && !y.Listed) { return -1; } if (!x.Listed && y.Listed) { return 1; } var xv = x.Version; var yv = y.Version; var packageBehavior = _dependencyBehavior; // for new packages use the highest version if (_targetIds.Contains(x.Id, StringComparer.OrdinalIgnoreCase)) { packageBehavior = DependencyBehavior.Highest; } // stay as close to the installed version as possible // Choose upgrades over downgrades // For downgrades choose the highest version // // Example: // 1.0.0 // 1.1.0 // 2.0.0 - installed // 2.1.0 // 3.0.0 // Order: 2.0.0, 2.1.0, 3.0.0, 1.1.0, 1.0.0 if (packageBehavior != DependencyBehavior.Highest && packageBehavior != DependencyBehavior.Ignore) { NuGetVersion installedVersion = null; if (_installedVersions.TryGetValue(x.Id, out installedVersion)) { var xvDowngrade = _versionComparer.Compare(xv, installedVersion) < 0; var yvDowngrade = _versionComparer.Compare(yv, installedVersion) < 0; // the upgrade is preferred over the downgrade if (xvDowngrade && !yvDowngrade) { return 1; } else if (!xvDowngrade && yvDowngrade) { return -1; } else if (xvDowngrade && yvDowngrade) { // when both are downgrades prefer the highest return -1 * _versionComparer.Compare(xv, yv); } } } // Normal switch (packageBehavior) { case DependencyBehavior.Lowest: { return _versionComparer.Compare(xv, yv); } case DependencyBehavior.Ignore: case DependencyBehavior.Highest: return -1 * _versionComparer.Compare(xv, yv); case DependencyBehavior.HighestMinor: { if (_versionComparer.Equals(xv, yv)) { return 0; } // Take the lowest Major, then the Highest Minor and Patch return new[] { x, y }.OrderBy(p => p.Version.Major) .ThenByDescending(p => p.Version.Minor) .ThenByDescending(p => p.Version.Patch).FirstOrDefault() == x ? -1 : 1; } case DependencyBehavior.HighestPatch: { if (_versionComparer.Equals(xv, yv)) { return 0; } // Take the lowest Major and Minor, then the Highest Patch return new[] { x, y }.OrderBy(p => p.Version.Major) .ThenBy(p => p.Version.Minor) .ThenByDescending(p => p.Version.Patch).FirstOrDefault() == x ? -1 : 1; } default: return _versionComparer.Compare(xv, yv); } } } }