src/NuGet.Core/NuGet.Frameworks/FrameworkNameProvider.cs (917 lines of code) (raw):

// 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. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Linq; using System.Text; using NuGet.Shared; namespace NuGet.Frameworks { public class FrameworkNameProvider : IFrameworkNameProvider { private static readonly HashSet<NuGetFramework> EmptyFrameworkSet = new(); /// <summary> /// Legacy frameworks that are allowed to have a single digit for the version number. /// </summary> private static readonly HashSet<string> SingleDigitVersionFrameworks = new(StringComparer.OrdinalIgnoreCase) { FrameworkConstants.FrameworkIdentifiers.Windows, FrameworkConstants.FrameworkIdentifiers.WindowsPhone, FrameworkConstants.FrameworkIdentifiers.Silverlight }; /// <summary> /// Frameworks that must always include a decimal point (period) between numerical parts. /// </summary> private static readonly HashSet<string> DecimalPointFrameworks = new(StringComparer.OrdinalIgnoreCase) { FrameworkConstants.FrameworkIdentifiers.NetCoreApp, FrameworkConstants.FrameworkIdentifiers.NetStandard, FrameworkConstants.FrameworkIdentifiers.NanoFramework }; /// <summary> /// Contains identifier -> identifier /// Ex: .NET Framework -> .NET Framework /// Ex: NET Framework -> .NET Framework /// This includes self mappings. /// </summary> private readonly Dictionary<string, string> _identifierSynonyms; private readonly Dictionary<string, string> _identifierToShortName; private readonly Dictionary<string, string> _profilesToShortName; private readonly Dictionary<string, string> _identifierShortToLong; private readonly Dictionary<string, string> _profileShortToLong; // profile -> supported frameworks, optional frameworks private readonly Dictionary<int, HashSet<NuGetFramework>> _portableFrameworks; private readonly Dictionary<int, HashSet<NuGetFramework>> _portableOptionalFrameworks; // PCL compatibility mappings private readonly Dictionary<int, HashSet<FrameworkRange>> _portableCompatibilityMappings; // equivalent frameworks private readonly Dictionary<NuGetFramework, HashSet<NuGetFramework>> _equivalentFrameworks; // equivalent profiles private readonly Dictionary<string, Dictionary<string, HashSet<string>>> _equivalentProfiles; // non-PCL compatibility mappings private readonly Dictionary<string, HashSet<OneWayCompatibilityMappingEntry>> _compatibilityMappings; // subsets, net -> netcore private readonly Dictionary<string, HashSet<string>> _subSetFrameworks; // framework ordering (for non-package based frameworks) private readonly Dictionary<string, int> _nonPackageBasedFrameworkPrecedence; // framework ordering (for package based frameworks) private readonly Dictionary<string, int> _packageBasedFrameworkPrecedence; // framework ordering (when choosing between equivalent frameworks) private readonly Dictionary<string, int> _equivalentFrameworkPrecedence; // Rewrite mappings private readonly Dictionary<NuGetFramework, NuGetFramework> _shortNameRewrites; private readonly Dictionary<NuGetFramework, NuGetFramework> _fullNameRewrites; // NetStandard information private readonly List<NuGetFramework> _netStandardVersions; private readonly List<NuGetFramework> _compatibleCandidates; public FrameworkNameProvider(IEnumerable<IFrameworkMappings>? mappings, IEnumerable<IPortableFrameworkMappings>? portableMappings) { _identifierSynonyms = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); _identifierToShortName = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); _profilesToShortName = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); _identifierShortToLong = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); _profileShortToLong = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase); _portableFrameworks = new Dictionary<int, HashSet<NuGetFramework>>(); _portableOptionalFrameworks = new Dictionary<int, HashSet<NuGetFramework>>(); _equivalentFrameworks = new Dictionary<NuGetFramework, HashSet<NuGetFramework>>(); _equivalentProfiles = new Dictionary<string, Dictionary<string, HashSet<string>>>(StringComparer.OrdinalIgnoreCase); _subSetFrameworks = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); _nonPackageBasedFrameworkPrecedence = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); _packageBasedFrameworkPrecedence = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); _equivalentFrameworkPrecedence = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase); _compatibilityMappings = new Dictionary<string, HashSet<OneWayCompatibilityMappingEntry>>(StringComparer.OrdinalIgnoreCase); _portableCompatibilityMappings = new Dictionary<int, HashSet<FrameworkRange>>(); _shortNameRewrites = new Dictionary<NuGetFramework, NuGetFramework>(); _fullNameRewrites = new Dictionary<NuGetFramework, NuGetFramework>(); _netStandardVersions = new List<NuGetFramework>(); _compatibleCandidates = new List<NuGetFramework>(); InitMappings(mappings); InitPortableMappings(portableMappings); InitNetStandard(); } /// <summary> /// Converts a key using the mappings, or if the key is already converted, finds the normalized form. /// </summary> private static bool TryConvertOrNormalize(string key, IDictionary<string, string> mappings, IDictionary<string, string> reverse, [NotNullWhen(true)] out string? value) { if (mappings.TryGetValue(key, out value)) { return true; } else if (reverse.ContainsKey(key)) { foreach (var item in reverse.NoAllocEnumerate()) { if (StringComparer.OrdinalIgnoreCase.Equals(item.Key, key)) { value = item.Key; return true; } } } value = null; return false; } public bool TryGetIdentifier(string framework, [NotNullWhen(true)] out string? identifier) { return TryConvertOrNormalize(framework, _identifierSynonyms, _identifierToShortName, out identifier); } public bool TryGetProfile(string frameworkIdentifier, string profileShortName, [NotNullWhen(true)] out string? profile) { return TryConvertOrNormalize(profileShortName, _profileShortToLong, _profilesToShortName, out profile); } public bool TryGetShortIdentifier(string identifier, [NotNullWhen(true)] out string? identifierShortName) { return TryConvertOrNormalize(identifier, _identifierToShortName, _identifierShortToLong, out identifierShortName); } public bool TryGetShortProfile(string frameworkIdentifier, string profile, [NotNullWhen(true)] out string? profileShortName) { return TryConvertOrNormalize(profile, _profilesToShortName, _profileShortToLong, out profileShortName); } public bool TryGetVersion(string versionString, [NotNullWhen(true)] out Version? version) { if (string.IsNullOrEmpty(versionString)) { version = null; return false; } else { if (versionString.IndexOf('.') > -1) { // parse the version as a normal dot delimited version return Version.TryParse(versionString, out version); } else { // make sure we have at least 2 digits if (versionString.Length < 2) { versionString += "0"; } // take only the first 4 digits and add dots // 451 -> 4.5.1 // 81233 -> 8123 return Version.TryParse(string.Join(".", versionString.ToCharArray().Take(4)), out version); } } } public bool TryGetPlatformVersion(string versionString, [NotNullWhen(true)] out Version? version) { if (string.IsNullOrEmpty(versionString)) { version = null; return false; } else { if (versionString.IndexOf('.') < 0) { versionString += ".0"; } return Version.TryParse(versionString, out version); } } public string GetVersionString(string framework, Version version) { if (version is null || IsZero(version)) { return string.Empty; } int major = version.Major > 0 ? version.Major : 0; int minor = version.Minor > 0 ? version.Minor : 0; int build = version.Build > 0 ? version.Build : 0; int revision = version.Revision > 0 ? version.Revision : 0; // Remove all trailing zeros beyond the minor version. int partCount = (minor == 0, build == 0, revision == 0) switch { (true, true, true) => 1, (false, true, true) => 2, (_, false, true) => 3, (_, _, false) => 4 }; // Only some legacy frameworks are allowed to have one part in their version. if (partCount == 1 && !SingleDigitVersionFrameworks.Contains(framework)) { partCount = 2; } StringBuilder sb = StringBuilderPool.Shared.Rent(256); // Some frameworks require a decimal point between parts. // If any part is greater than 9 (requiring multiple digits), we add decimal points. if (DecimalPointFrameworks.Contains(framework) || HasGreaterThanNinePart()) { // An additional zero is needed for decimals. if (partCount == 1) partCount = 2; sb.AppendInt(major); if (partCount > 1) sb.Append('.').AppendInt(minor); if (partCount > 2) sb.Append('.').AppendInt(build); if (partCount > 3) sb.Append('.').AppendInt(revision); } else { sb.AppendInt(major); if (partCount > 1) sb.AppendInt(minor); if (partCount > 2) sb.AppendInt(build); if (partCount > 3) sb.AppendInt(revision); } return StringBuilderPool.Shared.ToStringAndReturn(sb); bool HasGreaterThanNinePart() { return major > 9 || minor > 9 || build > 9 || revision > 9; } static bool IsZero(Version version) { // Build and Revision can be -1 when only major & minor are specified. // Out of caution, check all values for zero or less. return version.Major <= 0 && version.Minor <= 0 && version.Build <= 0 && version.Revision <= 0; } } public bool TryGetPortableProfile(IEnumerable<NuGetFramework> supportedFrameworks, out int profileNumber) { if (supportedFrameworks == null) { throw new ArgumentNullException(nameof(supportedFrameworks)); } profileNumber = -1; // Remove duplicate frameworks, ex: win+win8 -> win var profileFrameworks = RemoveDuplicateFramework(supportedFrameworks); var reduced = new HashSet<NuGetFramework>(); foreach (var pair in _portableFrameworks) { // to match the required set must be less than or the same count as the input // if we knew which frameworks were optional in the input we could rule out the lesser ones also if (pair.Value.Count <= profileFrameworks.Count) { foreach (var curFw in profileFrameworks) { var isOptional = false; foreach (var optional in GetOptionalFrameworks(pair.Key)) { // TODO: profile check? Is the version check correct here? if (NuGetFramework.FrameworkNameComparer.Equals(optional, curFw) && StringComparer.OrdinalIgnoreCase.Equals(optional.Profile, curFw.Profile) && curFw.Version >= optional.Version) { isOptional = true; } } if (!isOptional) { reduced.Add(curFw); } } // check all frameworks while taking into account equivalent variations foreach (var permutation in GetEquivalentPermutations(pair.Value)) { if (SetEquals(reduced, permutation)) { // found a match profileNumber = pair.Key; return true; } } } reduced.Clear(); } return false; } private HashSet<NuGetFramework> RemoveDuplicateFramework(IEnumerable<NuGetFramework> supportedFrameworks) { var result = new HashSet<NuGetFramework>(); var existingFrameworks = new HashSet<NuGetFramework>(); foreach (var framework in supportedFrameworks) { if (!existingFrameworks.Contains(framework)) { result.Add(framework); // Add in the existing framework (included here) and all equivalent frameworks var equivalentFrameworks = GetAllEquivalentFrameworks(framework); UnionWith(existingFrameworks, equivalentFrameworks); } } return result; } /// <summary> /// Get all equivalent frameworks including the given framework /// </summary> private HashSet<NuGetFramework> GetAllEquivalentFrameworks(NuGetFramework framework) { // Loop through the frameworks, all frameworks that are not in results yet // will be added to toProcess to get the equivalent frameworks var toProcess = new Stack<NuGetFramework>(); var results = new HashSet<NuGetFramework>(); toProcess.Push(framework); results.Add(framework); while (toProcess.Count > 0) { var current = toProcess.Pop(); if (_equivalentFrameworks.TryGetValue(current, out HashSet<NuGetFramework>? currentEquivalent)) { foreach (var equalFramework in currentEquivalent) { if (results.Add(equalFramework)) { toProcess.Push(equalFramework); } } } } return results; } // find all combinations that are equivalent // ex: net4+win8 <-> net4+netcore45 private IEnumerable<HashSet<NuGetFramework>> GetEquivalentPermutations(HashSet<NuGetFramework> frameworks) { if (frameworks.Count > 0) { NuGetFramework? current = null; var remaining = frameworks.Count == 1 ? null : new HashSet<NuGetFramework>(); var isFirst = true; foreach (var fw in frameworks) { if (isFirst) { current = fw; isFirst = false; continue; } remaining!.Add(fw); } HashSet<NuGetFramework> equalFrameworks; // find all equivalent frameworks for the current one if (_equivalentFrameworks.TryGetValue(current!, out HashSet<NuGetFramework>? curFrameworks)) { equalFrameworks = new HashSet<NuGetFramework>(curFrameworks); } else { equalFrameworks = new HashSet<NuGetFramework>(); } // include ourselves equalFrameworks.Add(current!); foreach (var fw in equalFrameworks) { if (remaining != null && remaining.Count > 0) { foreach (var result in GetEquivalentPermutations(remaining)) { // work backwards adding the frameworks into the sets result.Add(fw); yield return result; } } else { var singleFramework = new HashSet<NuGetFramework>(); singleFramework.Add(fw); yield return singleFramework; } } } yield break; } private HashSet<NuGetFramework> GetOptionalFrameworks(int profile) { if (_portableOptionalFrameworks.TryGetValue(profile, out HashSet<NuGetFramework>? frameworks)) { return frameworks; } return EmptyFrameworkSet; } public bool TryGetPortableFrameworks(int profile, [NotNullWhen(true)] out IEnumerable<NuGetFramework>? frameworks) { return TryGetPortableFrameworks(profile, true, out frameworks); } public bool TryGetPortableFrameworks(int profile, bool includeOptional, [NotNullWhen(true)] out IEnumerable<NuGetFramework>? frameworks) { var result = new HashSet<NuGetFramework>(); if (_portableFrameworks.TryGetValue(profile, out HashSet<NuGetFramework>? tmpFrameworks)) { foreach (var fw in tmpFrameworks) { result.Add(fw); } } if (includeOptional) { if (_portableOptionalFrameworks.TryGetValue(profile, out HashSet<NuGetFramework>? optional)) { foreach (var fw in optional) { result.Add(fw); } } } frameworks = result; return result.Count > 0; } public bool TryGetPortableFrameworks(string shortPortableProfiles, [NotNullWhen(true)] out IEnumerable<NuGetFramework>? frameworks) { if (shortPortableProfiles == null) { throw new ArgumentNullException(nameof(shortPortableProfiles)); } var shortNames = shortPortableProfiles.Split(new char[] { '+' }, StringSplitOptions.RemoveEmptyEntries); var result = new List<NuGetFramework>(); foreach (var name in shortNames) { var framework = NuGetFramework.Parse(name, this); if (framework.HasProfile) { // Frameworks within the portable profile are not allowed // to have profiles themselves #1869 throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, Strings.InvalidPortableFrameworksDueToHyphen, shortPortableProfiles)); } result.Add(framework); } frameworks = result; return result.Count > 0; } public bool TryGetPortableCompatibilityMappings(int profile, [NotNullWhen(true)] out IEnumerable<FrameworkRange>? supportedFrameworkRanges) { if (_portableCompatibilityMappings.TryGetValue(profile, out HashSet<FrameworkRange>? entries)) { supportedFrameworkRanges = entries; return supportedFrameworkRanges.Any(); } supportedFrameworkRanges = null; return false; } public bool TryGetPortableProfileNumber(string profile, out int profileNumber) { // attempt to parse the profile for a number if (profile.StartsWith("Profile", StringComparison.OrdinalIgnoreCase)) { var trimmed = profile.Substring(7, profile.Length - 7); return int.TryParse(trimmed, out profileNumber); } profileNumber = -1; return false; } public bool TryGetPortableFrameworks(string profile, bool includeOptional, [NotNullWhen(true)] out IEnumerable<NuGetFramework>? frameworks) { // attempt to parse the profile for a number int profileNum; if (TryGetPortableProfileNumber(profile, out profileNum)) { if (TryGetPortableFrameworks(profileNum, includeOptional, out frameworks)) { return true; } frameworks = Enumerable.Empty<NuGetFramework>(); return false; } // treat the profile as a list of frameworks return TryGetPortableFrameworks(profile, out frameworks); } public bool TryGetEquivalentFrameworks(NuGetFramework framework, [NotNullWhen(true)] out IEnumerable<NuGetFramework>? frameworks) { var result = new HashSet<NuGetFramework>(); // add in all framework aliases if (_equivalentFrameworks.TryGetValue(framework, out HashSet<NuGetFramework>? eqFrameworks)) { foreach (var eqFw in eqFrameworks) { result.Add(eqFw); } } var baseFrameworks = new List<NuGetFramework>(result); baseFrameworks.Add(framework); // add in all profile aliases foreach (var fw in baseFrameworks) { if (_equivalentProfiles.TryGetValue(fw.Framework, out Dictionary<string, HashSet<string>>? eqProfiles)) { if (eqProfiles.TryGetValue(fw.Profile, out HashSet<string>? matchingProfiles)) { foreach (var eqProfile in matchingProfiles) { result.Add(new NuGetFramework(fw.Framework, fw.Version, eqProfile)); } } } } // do not include the original framework result.Remove(framework); frameworks = result; return result.Count > 0; } public bool TryGetEquivalentFrameworks(FrameworkRange range, [NotNullWhen(true)] out IEnumerable<NuGetFramework>? frameworks) { if (range == null) { throw new ArgumentNullException(nameof(range)); } var relevant = new HashSet<NuGetFramework>(); foreach (var framework in _equivalentFrameworks.Keys.Where(f => range.Satisfies(f))) { relevant.Add(framework); } var results = new HashSet<NuGetFramework>(); foreach (var framework in relevant) { if (TryGetEquivalentFrameworks(framework, out IEnumerable<NuGetFramework>? values)) { foreach (var val in values) { results.Add(val); } } } frameworks = results; return results.Count > 0; } private void InitMappings(IEnumerable<IFrameworkMappings>? mappings) { if (mappings != null) { foreach (var mapping in mappings) { // eq profiles AddEquivalentProfiles(mapping.EquivalentProfiles); // equivalent frameworks AddEquivalentFrameworks(mapping.EquivalentFrameworks); // add synonyms AddFrameworkSynonyms(mapping.IdentifierSynonyms); // populate short <-> long AddIdentifierShortNames(mapping.IdentifierShortNames); // official profile short names AddProfileShortNames(mapping.ProfileShortNames); // add compatibility mappings AddCompatibilityMappings(mapping.CompatibilityMappings); // add subset frameworks AddSubSetFrameworks(mapping.SubSetFrameworks); // add framework ordering rules AddFrameworkPrecedenceMappings(_nonPackageBasedFrameworkPrecedence, mapping.NonPackageBasedFrameworkPrecedence); AddFrameworkPrecedenceMappings(_packageBasedFrameworkPrecedence, mapping.PackageBasedFrameworkPrecedence); AddFrameworkPrecedenceMappings(_equivalentFrameworkPrecedence, mapping.EquivalentFrameworkPrecedence); // add rewrite rules AddShortNameRewriteMappings(mapping.ShortNameReplacements); AddFullNameRewriteMappings(mapping.FullNameReplacements); } } } private void InitPortableMappings(IEnumerable<IPortableFrameworkMappings>? portableMappings) { if (portableMappings != null) { foreach (var portableMapping in portableMappings) { // populate portable framework names AddPortableProfileMappings(portableMapping.ProfileFrameworks); // populate portable optional frameworks AddPortableOptionalFrameworks(portableMapping.ProfileOptionalFrameworks); // populate portable compatibility mappings AddPortableCompatibilityMappings(portableMapping.CompatibilityMappings); } } } private void InitNetStandard() { // populate the list of frameworks that could be compatible with NetStandard AddCompatibleCandidates(); // populate the list of NetStandard versions AddNetStandardVersions(); } private void AddShortNameRewriteMappings(IEnumerable<KeyValuePair<NuGetFramework, NuGetFramework>> mappings) { if (mappings != null) { foreach (var mapping in mappings) { if (!_shortNameRewrites.ContainsKey(mapping.Key)) { _shortNameRewrites.Add(mapping.Key, mapping.Value); } } } } private void AddFullNameRewriteMappings(IEnumerable<KeyValuePair<NuGetFramework, NuGetFramework>> mappings) { if (mappings != null) { foreach (var mapping in mappings) { if (!_fullNameRewrites.ContainsKey(mapping.Key)) { _fullNameRewrites.Add(mapping.Key, mapping.Value); } } } } private void AddCompatibilityMappings(IEnumerable<OneWayCompatibilityMappingEntry> mappings) { if (mappings != null) { foreach (var mapping in mappings) { if (!_compatibilityMappings.TryGetValue(mapping.TargetFrameworkRange.Min.Framework, out HashSet<OneWayCompatibilityMappingEntry>? entries)) { entries = new HashSet<OneWayCompatibilityMappingEntry>(OneWayCompatibilityMappingEntry.Comparer); _compatibilityMappings.Add(mapping.TargetFrameworkRange.Min.Framework, entries); } entries.Add(mapping); } } } private void AddSubSetFrameworks(IEnumerable<KeyValuePair<string, string>> mappings) { if (mappings != null) { foreach (var mapping in mappings) { if (!_subSetFrameworks.TryGetValue(mapping.Value, out HashSet<string>? subSets)) { subSets = new HashSet<string>(StringComparer.OrdinalIgnoreCase); _subSetFrameworks.Add(mapping.Value, subSets); } subSets.Add(mapping.Key); } } } /// <summary> /// 2 way per framework profile equivalence /// </summary> /// <param name="mappings"></param> private void AddEquivalentProfiles(IEnumerable<FrameworkSpecificMapping> mappings) { if (mappings != null) { foreach (var profileMapping in mappings) { var frameworkIdentifier = profileMapping.FrameworkIdentifier; var profile1 = profileMapping.Mapping.Key; var profile2 = profileMapping.Mapping.Value; if (!_equivalentProfiles.TryGetValue(frameworkIdentifier, out Dictionary<string, HashSet<string>>? profileMappings)) { profileMappings = new Dictionary<string, HashSet<string>>(StringComparer.OrdinalIgnoreCase); _equivalentProfiles.Add(frameworkIdentifier, profileMappings); } if (!profileMappings.TryGetValue(profile1, out HashSet<string>? innerMappings1)) { innerMappings1 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); profileMappings.Add(profile1, innerMappings1); } if (!profileMappings.TryGetValue(profile2, out HashSet<string>? innerMappings2)) { innerMappings2 = new HashSet<string>(StringComparer.OrdinalIgnoreCase); profileMappings.Add(profile2, innerMappings2); } innerMappings1.Add(profile2); innerMappings2.Add(profile1); } } } /// <summary> /// 2 way framework equivalence /// </summary> /// <param name="mappings"></param> private void AddEquivalentFrameworks(IEnumerable<KeyValuePair<NuGetFramework, NuGetFramework>> mappings) { if (mappings != null) { foreach (var pair in mappings) { var remaining = new Stack<NuGetFramework>(); remaining.Push(pair.Key); remaining.Push(pair.Value); var seen = new HashSet<NuGetFramework>(); while (remaining.Any()) { var next = remaining.Pop(); if (!seen.Add(next)) { continue; } if (!_equivalentFrameworks.TryGetValue(next, out HashSet<NuGetFramework>? eqFrameworks)) { // initialize set eqFrameworks = new HashSet<NuGetFramework>(); _equivalentFrameworks.Add(next, eqFrameworks); } else { // explore all equivalent foreach (var framework in eqFrameworks) { remaining.Push(framework); } } } // add this equivalency rule, enforcing transitivity foreach (var framework in seen) { foreach (var other in seen) { if (!NuGetFramework.Comparer.Equals(framework, other)) { _equivalentFrameworks[framework].Add(other); } } } } } } private void AddFrameworkSynonyms(IEnumerable<KeyValuePair<string, string>> mappings) { if (mappings != null) { foreach (var pair in mappings) { if (!_identifierSynonyms.ContainsKey(pair.Key)) { _identifierSynonyms.Add(pair.Key, pair.Value); } } } } private void AddIdentifierShortNames(IEnumerable<KeyValuePair<string, string>> mappings) { if (mappings != null) { foreach (var pair in mappings) { var shortName = pair.Value; var longName = pair.Key; if (!_identifierSynonyms.ContainsKey(pair.Value)) { _identifierSynonyms.Add(pair.Value, pair.Key); } _identifierShortToLong.Add(shortName, longName); _identifierToShortName.Add(longName, shortName); } } } private void AddProfileShortNames(IEnumerable<FrameworkSpecificMapping> mappings) { if (mappings != null) { foreach (var profileMapping in mappings) { _profilesToShortName.Add(profileMapping.Mapping.Value, profileMapping.Mapping.Key); _profileShortToLong.Add(profileMapping.Mapping.Key, profileMapping.Mapping.Value); } } } // Add supported frameworks for each portable profile number private void AddPortableProfileMappings(IEnumerable<KeyValuePair<int, NuGetFramework[]>> mappings) { if (mappings != null) { foreach (var pair in mappings) { if (!_portableFrameworks.TryGetValue(pair.Key, out HashSet<NuGetFramework>? frameworks)) { frameworks = new HashSet<NuGetFramework>(); _portableFrameworks.Add(pair.Key, frameworks); } foreach (var fw in pair.Value) { frameworks.Add(fw); } } } } // Add optional frameworks for each portable profile number private void AddPortableOptionalFrameworks(IEnumerable<KeyValuePair<int, NuGetFramework[]>> mappings) { if (mappings != null) { foreach (var pair in mappings) { if (!_portableOptionalFrameworks.TryGetValue(pair.Key, out HashSet<NuGetFramework>? frameworks)) { frameworks = new HashSet<NuGetFramework>(); _portableOptionalFrameworks.Add(pair.Key, frameworks); } foreach (var fw in pair.Value) { frameworks.Add(fw); } } } } private void AddPortableCompatibilityMappings(IEnumerable<KeyValuePair<int, FrameworkRange>> mappings) { if (mappings != null) { foreach (var mapping in mappings) { if (!_portableCompatibilityMappings.TryGetValue(mapping.Key, out HashSet<FrameworkRange>? entries)) { entries = new HashSet<FrameworkRange>(FrameworkRangeComparer.Instance); _portableCompatibilityMappings.Add(mapping.Key, entries); } entries.Add(mapping.Value); } } } // Ordered lists of framework identifiers public void AddFrameworkPrecedenceMappings(IDictionary<string, int> destination, IEnumerable<string> mappings) { if (mappings != null) { foreach (var framework in mappings) { if (!destination.ContainsKey(framework)) { destination.Add(framework, destination.Count); } } } } public bool TryGetCompatibilityMappings(NuGetFramework framework, [NotNullWhen(true)] out IEnumerable<FrameworkRange>? supportedFrameworkRanges) { if (_compatibilityMappings.TryGetValue(framework.Framework, out HashSet<OneWayCompatibilityMappingEntry>? entries)) { supportedFrameworkRanges = entries.Where(m => m.TargetFrameworkRange.Satisfies(framework)).Select(m => m.SupportedFrameworkRange); return supportedFrameworkRanges.Any(); } supportedFrameworkRanges = null; return false; } public bool TryGetSubSetFrameworks(string frameworkIdentifier, [NotNullWhen(true)] out IEnumerable<string>? subSetFrameworks) { if (_subSetFrameworks.TryGetValue(frameworkIdentifier, out HashSet<string>? values)) { subSetFrameworks = values; return true; } subSetFrameworks = null; return false; } public int CompareFrameworks(NuGetFramework? x, NuGetFramework? y) { if (x is null && y is null) return 0; if (x is null) return -1; if (y is null) return 1; // For the purposes of this compare do not treat netcore50 as packages based var xPackagesBased = x.IsPackageBased && !NuGetFrameworkUtility.IsNetCore50AndUp(x); var yPackagesBased = y.IsPackageBased && !NuGetFrameworkUtility.IsNetCore50AndUp(y); if (xPackagesBased != yPackagesBased) { // non-package based always come before package based return xPackagesBased.CompareTo(yPackagesBased); } var precedence = xPackagesBased ? _packageBasedFrameworkPrecedence : _nonPackageBasedFrameworkPrecedence; return CompareUsingPrecedence(x, y, precedence); } public int CompareEquivalentFrameworks(NuGetFramework? x, NuGetFramework? y) { return CompareUsingPrecedence(x, y, _equivalentFrameworkPrecedence); } private static int CompareUsingPrecedence(NuGetFramework? x, NuGetFramework? y, Dictionary<string, int> precedence) { if (x is null && y is null) return 0; if (x is null) return -1; if (y is null) return 1; if (StringComparer.OrdinalIgnoreCase.Equals(x.Framework, y.Framework)) { return 0; } int xIndex; if (!precedence.TryGetValue(x.Framework, out xIndex)) { xIndex = int.MaxValue; } int yIndex; if (!precedence.TryGetValue(y.Framework, out yIndex)) { yIndex = int.MaxValue; } return xIndex.CompareTo(yIndex); } public NuGetFramework GetShortNameReplacement(NuGetFramework framework) { // Replace the framework name if a rewrite exists if (!_shortNameRewrites.TryGetValue(framework, out NuGetFramework? result)) { result = framework; } return result; } public NuGetFramework GetFullNameReplacement(NuGetFramework framework) { // Replace the framework name if a rewrite exists if (!_fullNameRewrites.TryGetValue(framework, out NuGetFramework? result)) { result = framework; } return result; } public IEnumerable<NuGetFramework> GetNetStandardVersions() { return _netStandardVersions.AsReadOnly(); } public IEnumerable<NuGetFramework> GetCompatibleCandidates() { return _compatibleCandidates.AsReadOnly(); } private void AddNetStandardVersions() { foreach (var framework in _compatibleCandidates) { if (StringComparer.OrdinalIgnoreCase.Equals(framework.Framework, FrameworkConstants.FrameworkIdentifiers.NetStandard)) { _netStandardVersions.Add(framework); } } _netStandardVersions.Sort(NuGetFrameworkSorter.Instance); } private void AddCompatibleCandidates() { var set = new HashSet<NuGetFramework>(); // equivalent foreach (var framework in _equivalentFrameworks.Values.SelectMany(x => x)) { set.Add(framework); } // compatible foreach (var mapping in _compatibilityMappings.SelectMany(p => p.Value)) { set.Add(mapping.TargetFrameworkRange.Min); set.Add(mapping.TargetFrameworkRange.Max); set.Add(mapping.SupportedFrameworkRange.Min); set.Add(mapping.SupportedFrameworkRange.Max); } // portable compatible foreach (var pair in _portableCompatibilityMappings) { var portable = new NuGetFramework( FrameworkConstants.FrameworkIdentifiers.Portable, FrameworkConstants.EmptyVersion, string.Format(NumberFormatInfo.InvariantInfo, "Profile{0}", pair.Key)); set.Add(portable); foreach (var range in pair.Value) { set.Add(range.Min); set.Add(range.Max); } } // subset and superset var superSetFrameworks = _subSetFrameworks .SelectMany(p => p.Value.Select(subset => new { Superset = p.Key, Subset = subset })) .GroupBy(p => p.Subset, p => p.Superset, StringComparer.OrdinalIgnoreCase) .ToDictionary(g => g.Key, g => new HashSet<string>(g, StringComparer.OrdinalIgnoreCase)); foreach (var framework in set.ToArray()) { if (framework.HasProfile) { continue; } if (_subSetFrameworks.TryGetValue(framework.Framework, out HashSet<string>? subset)) { foreach (var subFramework in subset) { set.Add(new NuGetFramework(subFramework, framework.Version, framework.Profile)); } } if (superSetFrameworks.TryGetValue(framework.Framework, out HashSet<string>? superset)) { foreach (var superFramework in superset) { set.Add(new NuGetFramework(superFramework, framework.Version, framework.Profile)); } } } _compatibleCandidates.AddRange(set); _compatibleCandidates.Sort(NuGetFrameworkSorter.Instance); } // Strong typed non-IEnumerator based HashSet functions private static bool SetEquals(HashSet<NuGetFramework> left, HashSet<NuGetFramework> right) { if (left.Count != right.Count) { return false; } foreach (var fw in left) { if (!right.Contains(fw)) { return false; } } return true; } private static void UnionWith(HashSet<NuGetFramework> toAccumulate, HashSet<NuGetFramework> toAdd) { foreach (var fw in toAdd) { toAccumulate.Add(fw); } } } }