예제 #1
0
        private static Dictionary<NuGetFramework, HashSet<NuGetFramework>> GetTable(IEnumerable<NuGetFramework> frameworks, IFrameworkNameProvider mappings, IFrameworkCompatibilityProvider compat)
        {
            // get the distinct set of frameworks, ignoring all special frameworks like Any, and Unsupported
            var input = new HashSet<NuGetFramework>(frameworks.Where(f => f.IsSpecificFramework), NuGetFramework.Comparer);
            var table = new Dictionary<NuGetFramework, HashSet<NuGetFramework>>(NuGetFramework.Comparer);

            foreach (var framework in input)
            {
                var compatFrameworks = new HashSet<NuGetFramework>(NuGetFramework.Comparer);
                table.Add(framework, compatFrameworks);

                foreach (var testFramework in input)
                {
                    if (compat.IsCompatible(framework, testFramework))
                    {
                        compatFrameworks.Add(testFramework);
                    }
                }
            }

            return table;
        }
예제 #2
0
        internal static bool IsCompatible(
            NetPortableProfileTable table,
            IFrameworkCompatibilityProvider compatibilityProvider,
            IFrameworkNameProvider nameProvider,
            FrameworkName projectFrameworkName,
            FrameworkName packageTargetFrameworkName)
        {
            if (projectFrameworkName == null)
            {
                return(true);
            }

            var projectNuGetFramework = GetNuGetFramework(
                table,
                nameProvider,
                projectFrameworkName);

            var packageTargetNuGetFramework = GetNuGetFramework(
                table,
                nameProvider,
                packageTargetFrameworkName);

            var isCompatible = compatibilityProvider.IsCompatible(
                projectNuGetFramework,
                packageTargetNuGetFramework);

            // Fallback to legacy portable compatibility logic if both:
            //   a) the modern compatibility code returns false
            //   b) the package framework is portable
            if (!isCompatible && packageTargetFrameworkName.IsPortableFramework())
            {
                return(IsPortableLibraryCompatible(table, projectFrameworkName, packageTargetFrameworkName));
            }

            return(isCompatible);
        }
예제 #3
0
        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);
        }
예제 #4
0
        /// <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);
        }
        /// <summary>
        /// Возвращает список совместимых элементов.
        /// </summary>
        /// <param name="reader">Интерфейс для чтения метаданных пакета.</param>
        /// <param name="items">Список элементов для выборки.</param>
        /// <param name="targetFramework">Версия совместимого фреймворка.</param>
        /// <param name="compatibilityProvider">Провайдер для проверки совместимости фреймворков.</param>
        private static FrameworkSpecificGroup GetCompatibleItems(PackageReaderBase reader,
                                                                 IList<FrameworkSpecificGroup> items,
                                                                 NuGetFramework targetFramework,
                                                                 IFrameworkCompatibilityProvider compatibilityProvider)
        {
            // Из пакета выбираются файлы с TargetFramework, который
            // является наиболее новым и совместимым с указанным

            var compatibleItems = items
                .OrderByDescending(i => i.TargetFramework, NuGetFrameworkComparer)
                .FirstOrDefault(i => NuGetFrameworkComparer.Compare(i.TargetFramework, targetFramework) <= 0
                                     && compatibilityProvider.IsCompatible(targetFramework, i.TargetFramework));

            if (compatibleItems == null)
            {
                var portableFramework = reader.GetSupportedFrameworks().FirstOrDefault(i => string.Equals(i.Framework, ".NETPortable", StringComparison.OrdinalIgnoreCase));

                if (portableFramework != null && compatibilityProvider.IsCompatible(targetFramework, portableFramework))
                {
                    compatibleItems = items.FirstOrDefault(i => NuGetFrameworkComparer.Compare(i.TargetFramework, portableFramework) == 0);
                }
            }

            return compatibleItems;
        }
예제 #6
0
 public bool IsCompatible(Framework project, Framework package)
 {
     return(_compatibilityProvider.IsCompatible(
                project.NuGetFramework,
                package.NuGetFramework));
 }
예제 #7
0
        /// <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);
        }