/// <summary> /// Returns the nearest matching framework that is compatible. /// </summary> /// <param name="framework">Project target framework</param> /// <param name="possibleFrameworks">Possible frameworks to narrow down</param> /// <returns>Nearest compatible framework. If no frameworks are compatible null is returned.</returns> public NuGetFramework GetNearest(NuGetFramework framework, IEnumerable <NuGetFramework> possibleFrameworks) { NuGetFramework nearest = null; // Unsupported frameworks always lose, throw them out unless it's all we were given if (possibleFrameworks.Any(e => e != NuGetFramework.UnsupportedFramework)) { possibleFrameworks = possibleFrameworks.Where(e => e != NuGetFramework.UnsupportedFramework); } // Try exact matches first nearest = possibleFrameworks.Where(f => _fullComparer.Equals(framework, f)).FirstOrDefault(); if (nearest == null) { // Elimate non-compatible frameworks var compatible = possibleFrameworks.Where(f => _compat.IsCompatible(framework, f)); // Remove lower versions of compatible frameworks var reduced = ReduceUpwards(compatible); // Reduce to the same framework name if possible if (reduced.Count() > 1 && reduced.Any(f => _fwNameComparer.Equals(f, framework))) { reduced = reduced.Where(f => _fwNameComparer.Equals(f, framework)); } // PCL reduce if (reduced.Count() > 1) { // if we have a pcl and non-pcl mix, throw out the pcls if (reduced.Any(f => f.IsPCL) && reduced.Any(f => !f.IsPCL)) { reduced = reduced.Where(f => !f.IsPCL); } else if (reduced.All(f => f.IsPCL)) { // decide between PCLs if (framework.IsPCL) { reduced = GetNearestPCLtoPCL(framework, reduced); } else { reduced = GetNearestNonPCLtoPCL(framework, reduced); } if (reduced.Count() > 1) { // For scenarios where we are unable to decide between PCLs, choose the PCL with the // least frameworks. Less frameworks means less compatibility which means it is nearer to the target. reduced = new NuGetFramework[] { GetBestPCL(reduced) }; } } } // Profile reduce if (reduced.Count() > 1 && !reduced.Any(f => f.IsPCL)) { // Prefer the same framework and profile if (framework.HasProfile) { var sameProfile = reduced.Where(f => _fwNameComparer.Equals(framework, f) && StringComparer.OrdinalIgnoreCase.Equals(framework.Profile, f.Profile)); if (sameProfile.Any()) { reduced = sameProfile; } } // Prefer frameworks without profiles if (reduced.Count() > 1 && reduced.Any(f => f.HasProfile) && reduced.Any(f => !f.HasProfile)) { reduced = reduced.Where(f => !f.HasProfile); } } // if we have reduced down to a single framework, use that if (reduced.Count() == 1) { nearest = reduced.Single(); } // this should be a very rare occurrence // at this point we are unable to decide between the remaining frameworks in any useful way // just take the first one by rev alphabetical order if we can't narrow it down at all if (nearest == null && reduced.Any()) { // Sort by precedence rules, then by name in the case of a tie nearest = reduced.OrderBy(f => f, new FrameworkPrecedenceSorter(_mappings)) .ThenByDescending(f => f, new NuGetFrameworkSorter()) .ThenBy(f => f.GetHashCode()) .First(); } } return(nearest); }
private NuGetFramework GetNearestInternal(NuGetFramework framework, IEnumerable <NuGetFramework> possibleFrameworks) { NuGetFramework nearest = null; // Unsupported frameworks always lose, throw them out unless it's all we were given if (possibleFrameworks.Any(e => e != NuGetFramework.UnsupportedFramework)) { possibleFrameworks = possibleFrameworks.Where(e => e != NuGetFramework.UnsupportedFramework); } // Try exact matches first nearest = possibleFrameworks.Where(f => _fullComparer.Equals(framework, f)).FirstOrDefault(); if (nearest == null) { // Elimate non-compatible frameworks var compatible = possibleFrameworks.Where(f => _compat.IsCompatible(framework, f)); // Remove lower versions of compatible frameworks var reduced = ReduceUpwards(compatible); bool isNet6Era = framework.IsNet5Era && framework.Version.Major >= 6; // Reduce to the same framework name if possible, with an exception for Xamarin, MonoAndroid and Tizen when net6.0+ if (reduced.Count() > 1 && reduced.Any(f => _fwNameComparer.Equals(f, framework))) { reduced = reduced.Where(f => { if (isNet6Era && framework.HasPlatform && ( f.Framework.StartsWith("xamarin.", StringComparison.OrdinalIgnoreCase) || f.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.MonoAndroid, StringComparison.OrdinalIgnoreCase) || f.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.Tizen, StringComparison.OrdinalIgnoreCase) )) { return(true); } else { return(_fwNameComparer.Equals(f, framework)); } }); } // PCL reduce if (reduced.Count() > 1) { // if we have a pcl and non-pcl mix, throw out the pcls if (reduced.Any(f => f.IsPCL) && reduced.Any(f => !f.IsPCL)) { reduced = reduced.Where(f => !f.IsPCL); } else if (reduced.All(f => f.IsPCL)) { // decide between PCLs if (framework.IsPCL) { reduced = GetNearestPCLtoPCL(framework, reduced); } else { reduced = GetNearestNonPCLtoPCL(framework, reduced); } if (reduced.Count() > 1) { // For scenarios where we are unable to decide between PCLs, choose the PCL with the // least frameworks. Less frameworks means less compatibility which means it is nearer to the target. reduced = new NuGetFramework[] { GetBestPCL(reduced) }; } } } // Packages based framework reduce, only if the project is not packages based if (reduced.Count() > 1 && !framework.IsPackageBased && reduced.Any(f => f.IsPackageBased) && reduced.Any(f => !f.IsPackageBased)) { // If we have a packages based and non-packages based mix, throw out the packages based frameworks. // This situation is unlikely but it could happen with framework mappings that do not provide // a relationship between the frameworks and the compatible packages based frameworks. // Ex: net46, dotnet -> net46 reduced = reduced.Where(f => !f.IsPackageBased); } // Profile reduce if (reduced.Count() > 1 && !reduced.Any(f => f.IsPCL)) { // Prefer the same framework and profile if (framework.HasProfile) { var sameProfile = reduced.Where(f => _fwNameComparer.Equals(framework, f) && StringComparer.OrdinalIgnoreCase.Equals(framework.Profile, f.Profile)); if (sameProfile.Any()) { reduced = sameProfile; } } // Prefer frameworks without profiles if (reduced.Count() > 1 && reduced.Any(f => f.HasProfile) && reduced.Any(f => !f.HasProfile)) { reduced = reduced.Where(f => !f.HasProfile); } } // Platforms reduce if (reduced.Count() > 1 && framework.HasPlatform) { if (!isNet6Era || reduced.Any(f => _fwNameComparer.Equals(framework, f) && f.Version.Major >= 6)) { // Prefer the highest framework version, likely to be the non-platform specific option. reduced = reduced.Where(f => _fwNameComparer.Equals(framework, f)).GroupBy(f => f.Version).OrderByDescending(f => f.Key).First(); } else if (isNet6Era && reduced.Any(f => { return(f.Framework.StartsWith("xamarin.", StringComparison.OrdinalIgnoreCase) || f.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.MonoAndroid, StringComparison.OrdinalIgnoreCase) || f.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.Tizen, StringComparison.OrdinalIgnoreCase)); })) { // We have a special case for *some* Xamarin-related frameworks here. For specific precedence rules, please see: // https://github.com/dotnet/designs/blob/main/accepted/2021/net6.0-tfms/net6.0-tfms.md#compatibility-rules reduced = reduced.GroupBy(f => f.Framework).OrderByDescending(f => f.Key).First(f => { NuGetFramework first = f.First(); return(first.Framework.StartsWith("xamarin.", StringComparison.OrdinalIgnoreCase) || first.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.MonoAndroid, StringComparison.OrdinalIgnoreCase) || first.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.Tizen, StringComparison.OrdinalIgnoreCase)); }); } } // if we have reduced down to a single framework, use that if (reduced.Count() == 1) { nearest = reduced.Single(); } // this should be a very rare occurrence // at this point we are unable to decide between the remaining frameworks in any useful way // just take the first one by rev alphabetical order if we can't narrow it down at all if (nearest == null && reduced.Any()) { // Sort by precedence rules, then by name in the case of a tie nearest = reduced .OrderBy(f => f, new FrameworkPrecedenceSorter(_mappings, false)) .ThenByDescending(f => f, new NuGetFrameworkSorter()) .ThenBy(f => f.GetHashCode()) .First(); } } return(nearest); }
/// <summary> /// Returns the nearest matching framework that is compatible. /// </summary> /// <param name="framework">Project target framework</param> /// <param name="possibleFrameworks">Possible frameworks to narrow down</param> /// <returns>Nearest compatible framework. If no frameworks are compatible null is returned.</returns> public NuGetFramework GetNearest(NuGetFramework framework, IEnumerable <NuGetFramework> possibleFrameworks) { NuGetFramework nearest = null; // Unsupported frameworks always lose, throw them out unless it's all we were given if (possibleFrameworks.Any(e => e != NuGetFramework.UnsupportedFramework)) { possibleFrameworks = possibleFrameworks.Where(e => e != NuGetFramework.UnsupportedFramework); } // Try exact matches first nearest = possibleFrameworks.Where(f => _fullComparer.Equals(framework, f)).FirstOrDefault(); if (nearest == null) { // Elimate non-compatible frameworks IEnumerable <NuGetFramework> compatible = possibleFrameworks.Where(f => _compat.IsCompatible(framework, f)); // Remove lower versions of compatible frameworks IEnumerable <NuGetFramework> reduced = ReduceUpwards(compatible); // Reduce to the same framework name if possible if (reduced.Count() > 1 && reduced.Any(f => _fwNameComparer.Equals(f, framework))) { reduced = reduced.Where(f => _fwNameComparer.Equals(f, framework)); } // PCL reduce if (reduced.Count() > 1) { // if we have a pcl and non-pcl mix, throw out the pcls if (reduced.Any(f => f.IsPCL) && reduced.Any(f => !f.IsPCL)) { reduced = reduced.Where(f => !f.IsPCL); } else if (reduced.All(f => f.IsPCL)) { // decide between PCLs if (framework.IsPCL) { reduced = GetNearestPCLtoPCL(framework, reduced); } else { reduced = GetNearestNonPCLtoPCL(framework, reduced); } if (reduced.Count() > 1) { // For scenarios where we are unable to decide between PCLs, choose the PCL with the // least frameworks. Less frameworks means less compatibility which means it is nearer to the target. reduced = OrderPCL(reduced).Take(1); } } } // Profile reduce if (reduced.Count() > 1 && !reduced.Any(f => f.IsPCL)) { // Prefer frameworks without profiles // TODO: should we try to match against the profile of the input framework? if (reduced.Any(f => f.HasProfile) && reduced.Any(f => !f.HasProfile)) { reduced = reduced.Where(f => !f.HasProfile); } } Debug.Assert(reduced.Count() < 2, "Unable to find the nearest framework: " + String.Join(", ", reduced)); // if we have reduced down to a single framework, use that if (reduced.Count() == 1) { nearest = reduced.Single(); } // this should be a very rare occurrence // at this point we are unable to decide between the remaining frameworks in any useful way // just take the first one by rev alphabetical order if we can't narrow it down at all if (nearest != null && reduced.Any()) { nearest = reduced.OrderByDescending(f => f.Framework, StringComparer.OrdinalIgnoreCase).ThenBy(f => f.GetHashCode()).First(); } } return(nearest); }