From ba264c60ef35b491f6d0e06be2bf3fe0a8fddedc Mon Sep 17 00:00:00 2001 From: Joel Verhagen Date: Mon, 9 May 2016 13:39:40 -0700 Subject: [PATCH] Use NuGet 3.x (compiled in from submodule) for compatibility and for Get Nearest --- .gitmodules | 4 + Build/Build.proj | 3 + NuGet.Client | 1 + NuGet.sln | 3 +- src/Core/Core.csproj | 134 ++++++ src/Core/GlobalSuppressions.cs | 18 + src/Core/NETPortable/NetPortableProfile.cs | 19 +- .../NETPortable/NetPortableProfileTable.cs | 72 ++- .../ReferenceAssemblyCompatibilityProvider.cs | 29 ++ .../ReferenceAssemblyFrameworkNameProvider.cs | 39 ++ ...erenceAssemblyPortableFrameworkMappings.cs | 92 ++++ src/Core/NuGet.Frameworks/comparers/empty.txt | 0 src/Core/NuGet.Frameworks/def/empty.txt | 0 src/Core/NuGet.Shared/empty.txt | 0 src/Core/Utility/VersionUtility.cs | 437 ++++-------------- test/Core.Test/NetPortableProfileTableTest.cs | 44 +- test/Core.Test/NetPortableProfileTest.cs | 308 ++++++++---- test/Core.Test/ProjectManagerTest.cs | 34 +- test/Core.Test/VersionUtilityTest.cs | 286 ++++-------- 19 files changed, 830 insertions(+), 693 deletions(-) create mode 160000 NuGet.Client create mode 100644 src/Core/NETPortable/ReferenceAssemblyCompatibilityProvider.cs create mode 100644 src/Core/NETPortable/ReferenceAssemblyFrameworkNameProvider.cs create mode 100644 src/Core/NETPortable/ReferenceAssemblyPortableFrameworkMappings.cs create mode 100644 src/Core/NuGet.Frameworks/comparers/empty.txt create mode 100644 src/Core/NuGet.Frameworks/def/empty.txt create mode 100644 src/Core/NuGet.Shared/empty.txt diff --git a/.gitmodules b/.gitmodules index c9eb40523..e07d3ec48 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,7 @@ [submodule "xdt"] path = xdt url = https://git01.codeplex.com/xdt +[submodule "NuGet.Client"] + path = NuGet.Client + url = https://github.com/NuGet/NuGet.Client.git + branch = dev diff --git a/Build/Build.proj b/Build/Build.proj index b5f867956..3a9ac1fc0 100644 --- a/Build/Build.proj +++ b/Build/Build.proj @@ -147,6 +147,9 @@ + diff --git a/NuGet.Client b/NuGet.Client new file mode 160000 index 000000000..49234c230 --- /dev/null +++ b/NuGet.Client @@ -0,0 +1 @@ +Subproject commit 49234c230df0fc01868d8b90e667af89be8cc4f6 diff --git a/NuGet.sln b/NuGet.sln index e49dc49f9..45d45d6a9 100644 --- a/NuGet.sln +++ b/NuGet.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 14 -VisualStudioVersion = 14.0.21917.0 +VisualStudioVersion = 14.0.25123.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E7B39EAD-EA32-4011-845A-C949A336389A}" EndProject @@ -125,6 +125,7 @@ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dialog11", "src\Dialog11\Dialog11.csproj", "{FD1E159B-A36F-49E4-856D-8555A20D09CF}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisualStudio11.Test", "test\VisualStudio11.Test\VisualStudio11.Test.csproj", "{25A2D439-B045-4059-9E1F-738362B72554}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TeamFoundationServer11", "src\TeamFoundationServer11\TeamFoundationServer11.csproj", "{5220A205-0C3F-4CD5-B1B4-7CD5B8298D7A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VisualStudio12", "src\VisualStudio12\VisualStudio12.csproj", "{3DD213F7-999B-4C15-9560-BF9C96CB8C52}" diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj index c04396226..5f6696c19 100644 --- a/src/Core/Core.csproj +++ b/src/Core/Core.csproj @@ -12,6 +12,8 @@ v4.0 Client AnyCPU + IS_NET40_CLIENT;NUGET_FRAMEWORKS_INTERNAL + 5 @@ -20,6 +22,9 @@ CommonResources.Designer.cs Designer + + NuGet.Frameworks\Strings.resx + ResXFileCodeGenerator AnalysisResources.Designer.cs @@ -41,6 +46,132 @@ Common\GlobalSuppressions.cs + + NuGet.Frameworks\comparers\CompatibilityMappingComparer.cs + + + NuGet.Frameworks\comparers\FrameworkPrecedenceSorter.cs + + + NuGet.Frameworks\comparers\FrameworkRangeComparer.cs + + + NuGet.Frameworks\comparers\NuGetFrameworkFullComparer.cs + + + NuGet.Frameworks\comparers\NuGetFrameworkNameComparer.cs + + + NuGet.Frameworks\comparers\NuGetFrameworkSorter.cs + + + NuGet.Frameworks\CompatibilityCacheKey.cs + + + NuGet.Frameworks\CompatibilityListProvider.cs + + + NuGet.Frameworks\CompatibilityProvider.cs + + + NuGet.Frameworks\CompatibilityTable.cs + + + NuGet.Frameworks\DefaultCompatibilityProvider.cs + + + NuGet.Frameworks\DefaultFrameworkMappings.cs + + + NuGet.Frameworks\DefaultFrameworkNameProvider.cs + + + NuGet.Frameworks\DefaultPortableFrameworkMappings.cs + + + NuGet.Frameworks\def\IFrameworkCompatibilityListProvider.cs + + + NuGet.Frameworks\def\IFrameworkCompatibilityProvider.cs + + + NuGet.Frameworks\def\IFrameworkMappings.cs + + + NuGet.Frameworks\def\IFrameworkNameProvider.cs + + + NuGet.Frameworks\def\IFrameworkSpecific.cs + + + NuGet.Frameworks\def\IFrameworkTargetable.cs + + + NuGet.Frameworks\def\IPortableFrameworkMappings.cs + + + NuGet.Frameworks\FallbackFramework.cs + + + NuGet.Frameworks\FrameworkConstants.cs + + + NuGet.Frameworks\FrameworkException.cs + + + NuGet.Frameworks\FrameworkExpander.cs + + + NuGet.Frameworks\FrameworkExtensions.cs + + + NuGet.Frameworks\FrameworkNameHelpers.cs + + + NuGet.Frameworks\FrameworkNameProvider.cs + + + NuGet.Frameworks\FrameworkRange.cs + + + NuGet.Frameworks\FrameworkReducer.cs + + + NuGet.Frameworks\FrameworkRuntimePair.cs + + + NuGet.Frameworks\FrameworkSpecificMapping.cs + + + NuGet.Frameworks\HashCodeCombiner.cs + + + NuGet.Frameworks\NuGetFramework.cs + + + NuGet.Frameworks\NuGetFrameworkFactory.cs + + + NuGet.Frameworks\NuGetFrameworkUtility.cs + + + NuGet.Frameworks\OneWayCompatibilityMappingEntry.cs + + + NuGet.Frameworks\Strings.Designer.cs + + + NuGet.Shared\HashCodeCombiner.cs + + + NuGet.Shared\SharedExtensions.cs + + + NuGet.Shared\TypeExtensions.cs + + + NuGet.Shared\TypeInfo.cs + @@ -106,9 +237,12 @@ + + + diff --git a/src/Core/GlobalSuppressions.cs b/src/Core/GlobalSuppressions.cs index 664790b40..e26fbf8bf 100644 --- a/src/Core/GlobalSuppressions.cs +++ b/src/Core/GlobalSuppressions.cs @@ -12,3 +12,21 @@ [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "NuGet.Analysis.Rules", Justification = "Don't want to pollute the NuGet namespace.")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "ps", Scope = "resource", Target = "NuGet.Resources.AnalysisResources.resources")] [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "NuGet.ManifestMetadata.#NuGet.IPackageMetadata.PackageAssemblyReferences")] + +// Suppressions for NuGet.Frameworks code. +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Scope = "type", Target = "NuGet.Frameworks.FrameworkException")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Scope = "type", Target = "NuGet.Frameworks.FrameworkException")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1307:SpecifyStringComparison", MessageId = "System.String.CompareTo(System.String)", Scope = "member", Target = "NuGet.Frameworks.FrameworkRuntimePair.#CompareTo(NuGet.Frameworks.FrameworkRuntimePair)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Scope = "member", Target = "NuGet.Frameworks.NuGetFramework.#GetShortFolderName(NuGet.Frameworks.IFrameworkNameProvider)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase", Scope = "member", Target = "NuGet.Frameworks.NuGetFramework.#TryParseCommonFramework(System.String,NuGet.Frameworks.NuGetFramework&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "NuGet.Frameworks.FrameworkNameProvider.#AddCompatibleCandidates()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "NuGet.Frameworks.FrameworkReducer.#GetNearestInternal(NuGet.Frameworks.NuGetFramework,System.Collections.Generic.IEnumerable`1)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "NuGet.Frameworks.FrameworkReducer.#IsBetterPCL(NuGet.Frameworks.NuGetFramework,NuGet.Frameworks.NuGetFramework)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Scope = "member", Target = "NuGet.Frameworks.NuGetFramework.#TryParseCommonFramework(System.String,NuGet.Frameworks.NuGetFramework&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "NuGet.Frameworks.CompatibilityListProvider.#ReduceDownwards(System.Collections.Generic.IEnumerable`1)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "NuGet.Frameworks.CompatibilityProvider.#IsSpecialFrameworkCompatible(NuGet.Frameworks.NuGetFramework,NuGet.Frameworks.NuGetFramework)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "NuGet.Frameworks.DefaultPortableFrameworkMappings.#CreateProfileFrameworks(System.Int32,NuGet.Frameworks.NuGetFramework[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Scope = "member", Target = "NuGet.Frameworks.FrameworkNameProvider.#AddFrameworkPrecedenceMappings(System.Collections.Generic.IDictionary`2,System.Collections.Generic.IEnumerable`1)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "includeOptional", Scope = "member", Target = "NuGet.Frameworks.FrameworkReducer.#ExplodePortableFrameworks(System.Collections.Generic.IEnumerable`1,System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "mappings", Scope = "member", Target = "NuGet.Frameworks.CompatibilityTable.#GetTable(System.Collections.Generic.IEnumerable`1,NuGet.Frameworks.IFrameworkNameProvider,NuGet.Frameworks.IFrameworkCompatibilityProvider)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Scope = "type", Target = "NuGet.Frameworks.FrameworkException")] diff --git a/src/Core/NETPortable/NetPortableProfile.cs b/src/Core/NETPortable/NetPortableProfile.cs index efa7313fd..d94fe6560 100644 --- a/src/Core/NETPortable/NetPortableProfile.cs +++ b/src/Core/NETPortable/NetPortableProfile.cs @@ -11,6 +11,10 @@ namespace NuGet /// public class NetPortableProfile : IEquatable { + private static readonly NetPortableProfileTable EmptyTable + = new NetPortableProfileTable(new NetPortableProfileCollection()); + + internal const string ProfilePrefix = "Profile"; private string _customProfile; /// @@ -111,17 +115,18 @@ public string CustomProfileString { get { + if (_customProfile == null) { var frameworks = SupportedFrameworks.Concat(OptionalFrameworks); - _customProfile = String.Join("+", frameworks.Select(f => VersionUtility.GetShortFrameworkName(f))); + _customProfile = String.Join("+", frameworks.Select(f => VersionUtility.GetShortFrameworkName(EmptyTable, f))); } return _customProfile; } } - public bool IsCompatibleWith(NetPortableProfile projectFrameworkProfile) + internal bool IsCompatibleWith(NetPortableProfile projectFrameworkProfile) { if (projectFrameworkProfile == null) { @@ -133,7 +138,7 @@ public bool IsCompatibleWith(NetPortableProfile projectFrameworkProfile) packageFramework => VersionUtility.IsCompatible(projectFramework, packageFramework))); } - public bool IsCompatibleWith(FrameworkName projectFramework) + internal bool IsCompatibleWith(NetPortableProfileTable table, FrameworkName projectFramework) { if (projectFramework == null) { @@ -141,14 +146,14 @@ public bool IsCompatibleWith(FrameworkName projectFramework) } return SupportedFrameworks.Any(packageFramework => VersionUtility.IsCompatible(projectFramework, packageFramework)) - || NetPortableProfileTable.HasCompatibleProfileWith(this, projectFramework); + || table.HasCompatibleProfileWith(this, projectFramework); } /// /// Attempt to parse a profile string into an instance of . /// The profile string can be either ProfileXXX or sl4+net45+wp7 /// - public static NetPortableProfile Parse(string profileValue, bool treatOptionalFrameworksAsSupportedFrameworks = false) + public static NetPortableProfile Parse(NetPortableProfileTable table, string profileValue, bool treatOptionalFrameworksAsSupportedFrameworks = false) { if (String.IsNullOrEmpty(profileValue)) { @@ -160,7 +165,7 @@ public static NetPortableProfile Parse(string profileValue, bool treatOptionalFr // was supported in other places. By fixing the way the profile table indexes the cached // profiles, we can now indeed access by either naming, so we don't need the old check // for the string starting with "Profile". - var result = NetPortableProfileTable.GetProfile(profileValue); + var result = table.GetProfile(profileValue); if (result != null) { if (treatOptionalFrameworksAsSupportedFrameworks) @@ -171,7 +176,7 @@ public static NetPortableProfile Parse(string profileValue, bool treatOptionalFr return result; } - if (profileValue.StartsWith("Profile", StringComparison.OrdinalIgnoreCase)) + if (profileValue.StartsWith(ProfilePrefix, StringComparison.OrdinalIgnoreCase)) { // This can happen if profileValue is an unrecognized profile, or // for some rare cases, the Portable Profile files are missing on disk. diff --git a/src/Core/NETPortable/NetPortableProfileTable.cs b/src/Core/NETPortable/NetPortableProfileTable.cs index 1ddaf0890..74b68f6da 100644 --- a/src/Core/NETPortable/NetPortableProfileTable.cs +++ b/src/Core/NETPortable/NetPortableProfileTable.cs @@ -9,57 +9,71 @@ namespace NuGet { - public static class NetPortableProfileTable + public class NetPortableProfileTable { + private readonly CompiledNetPortableProfileCollection _compiled; + + public NetPortableProfileTable(NetPortableProfileCollection profileCollection) + { + _compiled = new CompiledNetPortableProfileCollection(profileCollection); + } + private const string PortableReferenceAssemblyPathEnvironmentVariableName = "NuGetPortableReferenceAssemblyPath"; - private static Lazy _portableProfiles - = new Lazy(() => new CompiledNetPortableProfileCollection(BuildPortableProfileCollection())); + public NetPortableProfileCollection Profiles + { + get + { + return _compiled.Profiles; + } + } - public static NetPortableProfile GetProfile(string profileName) + public NetPortableProfile GetProfile(string profileName) { if (string.IsNullOrEmpty(profileName)) { throw new ArgumentException(CommonResources.Argument_Cannot_Be_Null_Or_Empty, "profileName"); } - var compiled = _portableProfiles.Value; - // Original behavior fully preserved, as we first try the original behavior. // NOTE: this could be a single TryGetValue if this collection was kept as a dictionary... - if (compiled.Profiles.Contains(profileName)) + if (_compiled.Profiles.Contains(profileName)) { - return compiled.Profiles[profileName]; + return _compiled.Profiles[profileName]; } // If we didn't get a profile by the simple profile name, try now with // the custom profile string (i.e. "net40-client") NetPortableProfile result = null; - compiled.PortableProfilesByCustomProfileString.TryGetValue(profileName, out result); + _compiled.PortableProfilesByCustomProfileString.TryGetValue(profileName, out result); return result; } /// - /// This method should only be used by tests. + /// The setter should only ever be used by tests. /// internal static void SetProfileCollection(NetPortableProfileCollection profileCollection) { - _portableProfiles = new Lazy(() => - new CompiledNetPortableProfileCollection(profileCollection ?? BuildPortableProfileCollection())); + if (profileCollection == null) + { + _instance = null; + } + else + { + _instance = new NetPortableProfileTable(profileCollection); + } } - internal static bool HasCompatibleProfileWith(NetPortableProfile packageFramework, FrameworkName projectOptionalFrameworkName) + internal bool HasCompatibleProfileWith(NetPortableProfile packageFramework, FrameworkName projectOptionalFrameworkName) { List versionProfileISetTupleList = null; - - var compiled = _portableProfiles.Value; - + // In the dictionary _portableProfilesSetByOptionalFrameworks, // key is the identifier of the optional framework and value is the tuple of (optional Framework Version, set of profiles in which they are optional) // We try to get a value with key as projectOptionalFrameworkName.Identifier. If one exists, we check if the project version is >= the version from the retrieved tuple // If so, then, we see if one of the profiles, in the set from the retrieved tuple, is compatible with the packageFramework profile - if (compiled.PortableProfilesSetByOptionalFrameworks.TryGetValue(projectOptionalFrameworkName.Identifier, out versionProfileISetTupleList)) + if (_compiled.PortableProfilesSetByOptionalFrameworks.TryGetValue(projectOptionalFrameworkName.Identifier, out versionProfileISetTupleList)) { foreach (var versionProfileISetTuple in versionProfileISetTupleList) { @@ -80,7 +94,7 @@ internal static bool HasCompatibleProfileWith(NetPortableProfile packageFramewor return false; } - private static NetPortableProfileCollection BuildPortableProfileCollection() + internal static NetPortableProfileCollection BuildPortableProfileCollection() { var profileCollection = new NetPortableProfileCollection(); @@ -285,7 +299,11 @@ public CompiledNetPortableProfileCollection(NetPortableProfileCollection profile private static IDictionary CreatePortableProfilesByCustomProfileString(NetPortableProfileCollection profileCollection) { - return profileCollection.ToDictionary(x => x.CustomProfileString); + // If multiple profiles end up having the same CustomProfileString (that is, the + // same set of contained TFMs), prefer the last one found. + return profileCollection + .ToLookup(x => x.CustomProfileString) + .ToDictionary(g => g.Key, g => g.Last()); } private static IDictionary> CreateOptionalFrameworksDictionary(NetPortableProfileCollection profileCollection) @@ -321,5 +339,21 @@ private static IDictionary> CreateOptionalF return portableProfilesSetByOptionalFrameworks; } } + + private static NetPortableProfileTable _instance; + + public static NetPortableProfileTable Instance + { + get + { + if (_instance == null) + { + var profileCollection = BuildPortableProfileCollection(); + _instance = new NetPortableProfileTable(profileCollection); + } + + return _instance; + } + } } } diff --git a/src/Core/NETPortable/ReferenceAssemblyCompatibilityProvider.cs b/src/Core/NETPortable/ReferenceAssemblyCompatibilityProvider.cs new file mode 100644 index 000000000..7daf9fada --- /dev/null +++ b/src/Core/NETPortable/ReferenceAssemblyCompatibilityProvider.cs @@ -0,0 +1,29 @@ +using NuGet.Frameworks; + +namespace NuGet +{ + internal class ReferenceAssemblyCompatibilityProvider : CompatibilityProvider + { + + public ReferenceAssemblyCompatibilityProvider(NetPortableProfileCollection profileCollection) + : base(new ReferenceAssemblyFrameworkNameProvider(profileCollection)) + { + } + + private static ReferenceAssemblyCompatibilityProvider _instance; + + public static ReferenceAssemblyCompatibilityProvider Instance + { + get + { + if (_instance == null) + { + _instance = new ReferenceAssemblyCompatibilityProvider( + NetPortableProfileTable.Instance.Profiles); + } + + return _instance; + } + } + } +} diff --git a/src/Core/NETPortable/ReferenceAssemblyFrameworkNameProvider.cs b/src/Core/NETPortable/ReferenceAssemblyFrameworkNameProvider.cs new file mode 100644 index 000000000..f6de7b770 --- /dev/null +++ b/src/Core/NETPortable/ReferenceAssemblyFrameworkNameProvider.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using NuGet.Frameworks; + +namespace NuGet +{ + internal class ReferenceAssemblyFrameworkNameProvider : FrameworkNameProvider + { + public ReferenceAssemblyFrameworkNameProvider(NetPortableProfileCollection profileCollection) + : base(GetMappings(), GetPortableMappings(profileCollection)) + { + } + + private static IEnumerable GetMappings() + { + yield return DefaultFrameworkMappings.Instance; + } + + private static IEnumerable GetPortableMappings(NetPortableProfileCollection profileCollection) + { + yield return new ReferenceAssemblyPortableFrameworkMappings(profileCollection); + } + + private static ReferenceAssemblyFrameworkNameProvider _instance; + + public static ReferenceAssemblyFrameworkNameProvider Instance + { + get + { + if (_instance == null) + { + _instance = new ReferenceAssemblyFrameworkNameProvider( + NetPortableProfileTable.Instance.Profiles); + } + + return _instance; + } + } + } +} diff --git a/src/Core/NETPortable/ReferenceAssemblyPortableFrameworkMappings.cs b/src/Core/NETPortable/ReferenceAssemblyPortableFrameworkMappings.cs new file mode 100644 index 000000000..db67b3795 --- /dev/null +++ b/src/Core/NETPortable/ReferenceAssemblyPortableFrameworkMappings.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.Versioning; +using NuGet.Frameworks; + +namespace NuGet +{ + internal class ReferenceAssemblyPortableFrameworkMappings : IPortableFrameworkMappings + { + public ReferenceAssemblyPortableFrameworkMappings(NetPortableProfileCollection profileCollection) + { + var table = new NetPortableProfileTable(profileCollection); + foreach (var profile in profileCollection) + { + AddPortableProfile(table, profile); + } + } + + private void AddPortableProfile(NetPortableProfileTable table, NetPortableProfile profile) + { + if (!profile.Name.StartsWith(NetPortableProfile.ProfilePrefix, StringComparison.OrdinalIgnoreCase)) + { + return; + } + + var rawNumber = profile.Name.Substring(NetPortableProfile.ProfilePrefix.Length); + int number; + if (!int.TryParse(rawNumber, out number)) + { + return; + } + + var profileFrameworks = new KeyValuePair( + number, + profile.SupportedFrameworks.Select(f => VersionUtility.GetNuGetFramework( + table, + DefaultFrameworkNameProvider.Instance, + f)).ToArray()); + _profileFrameworks.Add(profileFrameworks); + + var profileOptionalFrameworks = new KeyValuePair( + number, + profile.OptionalFrameworks.Select(f => VersionUtility.GetNuGetFramework( + table, + DefaultFrameworkNameProvider.Instance, + f)).ToArray()); + _profileOptionalFrameworks.Add(profileOptionalFrameworks); + } + + /// + /// Use the compatibility mappings from NuGet 3.x, since these are not all all expressed + /// in the old world. + /// + public IEnumerable> CompatibilityMappings + { + get { return DefaultPortableFrameworkMappings.Instance.CompatibilityMappings; } + } + + private readonly List> _profileFrameworks + = new List>(); + + public IEnumerable> ProfileFrameworks + { + get { return _profileFrameworks; } + } + + private readonly List> _profileOptionalFrameworks + = new List>(); + + public IEnumerable> ProfileOptionalFrameworks + { + get { return _profileOptionalFrameworks; } + } + + private static ReferenceAssemblyPortableFrameworkMappings _instance; + + public static ReferenceAssemblyPortableFrameworkMappings Instance + { + get + { + if (_instance == null) + { + _instance = new ReferenceAssemblyPortableFrameworkMappings( + NetPortableProfileTable.Instance.Profiles); + } + + return _instance; + } + } + } +} diff --git a/src/Core/NuGet.Frameworks/comparers/empty.txt b/src/Core/NuGet.Frameworks/comparers/empty.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/Core/NuGet.Frameworks/def/empty.txt b/src/Core/NuGet.Frameworks/def/empty.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/Core/NuGet.Shared/empty.txt b/src/Core/NuGet.Shared/empty.txt new file mode 100644 index 000000000..e69de29bb diff --git a/src/Core/Utility/VersionUtility.cs b/src/Core/Utility/VersionUtility.cs index f14d37a9f..19880581b 100644 --- a/src/Core/Utility/VersionUtility.cs +++ b/src/Core/Utility/VersionUtility.cs @@ -8,8 +8,8 @@ using System.Runtime.Versioning; using System.Text; using System.Text.RegularExpressions; +using NuGet.Frameworks; using NuGet.Resources; -using CompatibilityMapping = System.Collections.Generic.Dictionary; namespace NuGet { @@ -194,23 +194,7 @@ public static class VersionUtility { "WindowsPhone71", "wp71" }, { "CompactFramework", "cf" } }; - - private static readonly Dictionary _compatibiltyMapping = new Dictionary(StringComparer.OrdinalIgnoreCase) { - { - // Client profile is compatible with the full framework (empty string is full) - NetFrameworkIdentifier, new CompatibilityMapping(StringComparer.OrdinalIgnoreCase) { - { "", new [] { "Client" } }, - { "Client", new [] { "" } } - } - }, - { - "Silverlight", new CompatibilityMapping(StringComparer.OrdinalIgnoreCase) { - { "WindowsPhone", new[] { "WindowsPhone71" } }, - { "WindowsPhone71", new[] { "WindowsPhone" } } - } - } - }; - + // These aliases allow us to accept 'wp', 'wp70', 'wp71', 'windows', 'windows8' as valid target farmework folders. private static readonly Dictionary _frameworkNameAlias = new Dictionary(FrameworkNameEqualityComparer.Default) { @@ -225,25 +209,6 @@ public static class VersionUtility { new FrameworkName("Windows, Version=v8.1"), new FrameworkName(".NETCore, Version=v4.5.1") } }; - // See IsCompatible - // The ASP.Net framework authors desire complete compatibility between 'aspnet50' and all 'net' versions - // So we use this MaxVersion value to achieve complete compatiblilty. - private static readonly Version MaxVersion = new Version(Int32.MaxValue, Int32.MaxValue, Int32.MaxValue, Int32.MaxValue); - private static readonly Dictionary _equivalentProjectFrameworks = new Dictionary() - { - // Allow a core package to be installed in a dnxcore project - // { DnxCoreFrameworkIdentifier, new FrameworkName(CoreFrameworkIdentifier, MaxVersion) }, - - // Allow an aspnetcore package to be installed in a dnxcore project - { DnxCoreFrameworkIdentifier, new FrameworkName(AspNetCoreFrameworkIdentifier, MaxVersion) }, - - // Allow an aspnet package to be installed in a dnx project - { DnxFrameworkIdentifier, new FrameworkName(AspNetFrameworkIdentifier, MaxVersion) }, - - // Allow a net package to be installed in an aspnet (or dnx, transitively by above) project - { AspNetFrameworkIdentifier, new FrameworkName(NetFrameworkIdentifier, MaxVersion) }, - }; - public static Version DefaultTargetFrameworkVersion { get @@ -644,7 +609,13 @@ public static string GetFrameworkString(FrameworkName frameworkName) return name + "-" + frameworkName.Profile; } + public static string GetShortFrameworkName(FrameworkName frameworkName) + { + return GetShortFrameworkName(NetPortableProfileTable.Instance, frameworkName); + } + + internal static string GetShortFrameworkName(NetPortableProfileTable table, FrameworkName frameworkName) { if (frameworkName == null) { @@ -681,7 +652,10 @@ public static string GetShortFrameworkName(FrameworkName frameworkName) string profile; if (name.Equals("portable", StringComparison.OrdinalIgnoreCase)) { - NetPortableProfile portableProfile = NetPortableProfile.Parse(frameworkName.Profile); + var portableProfile = NetPortableProfile.Parse( + table, + frameworkName.Profile); + if (portableProfile != null) { profile = portableProfile.CustomProfileString; @@ -905,15 +879,39 @@ from framework in frameworks }; // Group references by target framework (if there is no target framework we assume it is the default) - var frameworkGroups = normalizedItems.GroupBy(g => g.TargetFramework, g => g.Item).ToList(); - - // Try to find the best match - // Not all projects have a framework, we need to consider those projects. - compatibleItems = (from g in frameworkGroups - where g.Key != null && IsCompatible(internalProjectFramework, g.Key) - orderby GetProfileCompatibility(internalProjectFramework, g.Key) descending - select g).FirstOrDefault(); - + var frameworkGroups = normalizedItems + .GroupBy(g => g.TargetFramework, g => g.Item) + .ToList(); + + // Try to find the best match using NuGet's Get Nearest algorithm + var nuGetFrameworkToFrameworkGroup = frameworkGroups + .Where(g => g.Key != null) + .ToDictionary(g => GetNuGetFramework( + NetPortableProfileTable.Instance, + ReferenceAssemblyFrameworkNameProvider.Instance, + g.Key), + NuGetFramework.Comparer); + + var reducer = new FrameworkReducer( + ReferenceAssemblyFrameworkNameProvider.Instance, + ReferenceAssemblyCompatibilityProvider.Instance); + + var nearest = reducer.GetNearest( + GetNuGetFramework( + NetPortableProfileTable.Instance, + ReferenceAssemblyFrameworkNameProvider.Instance, + internalProjectFramework), + nuGetFrameworkToFrameworkGroup.Keys); + + if (nearest != null) + { + compatibleItems = nuGetFrameworkToFrameworkGroup[nearest]; + } + else + { + compatibleItems = null; + } + bool hasItems = compatibleItems != null && compatibleItems.Any(); if (!hasItems) { @@ -931,27 +929,6 @@ orderby GetProfileCompatibility(internalProjectFramework, g.Key) descending return hasItems; } - - - internal static Version NormalizeVersion(Version version) - { - return new Version(version.Major, - version.Minor, - Math.Max(version.Build, 0), - Math.Max(version.Revision, 0)); - } - - public static FrameworkName NormalizeFrameworkName(FrameworkName framework) - { - FrameworkName aliasFramework; - if (_frameworkNameAlias.TryGetValue(framework, out aliasFramework)) - { - return aliasFramework; - } - - return framework; - } - /// /// Returns all possible versions for a version. i.e. 1.0 would return 1.0, 1.0.0, 1.0.0.0 /// @@ -990,79 +967,63 @@ public static bool IsCompatible(FrameworkName projectFrameworkName, IEnumerable< /// The project's framework /// The package's target framework internal static bool IsCompatible(FrameworkName projectFrameworkName, FrameworkName packageTargetFrameworkName) + { + return IsCompatible( + NetPortableProfileTable.Instance, + ReferenceAssemblyCompatibilityProvider.Instance, + ReferenceAssemblyFrameworkNameProvider.Instance, + projectFrameworkName, + packageTargetFrameworkName); + } + + internal static bool IsCompatible( + NetPortableProfileTable table, + IFrameworkCompatibilityProvider compatibilityProvider, + IFrameworkNameProvider nameProvider, + FrameworkName projectFrameworkName, + FrameworkName packageTargetFrameworkName) { if (projectFrameworkName == null) { return true; } - // Treat portable library specially - if (packageTargetFrameworkName.IsPortableFramework()) - { - return IsPortableLibraryCompatible(projectFrameworkName, packageTargetFrameworkName); - } + var projectNuGetFramework = GetNuGetFramework( + table, + nameProvider, + projectFrameworkName); - packageTargetFrameworkName = NormalizeFrameworkName(packageTargetFrameworkName); - projectFrameworkName = NormalizeFrameworkName(projectFrameworkName); + var packageTargetNuGetFramework = GetNuGetFramework( + table, + nameProvider, + packageTargetFrameworkName); - check: + var isCompatible = compatibilityProvider.IsCompatible( + projectNuGetFramework, + packageTargetNuGetFramework); - if (!projectFrameworkName.Identifier.Equals(packageTargetFrameworkName.Identifier, StringComparison.OrdinalIgnoreCase)) + // 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()) { - // Try to convert the project framework into an equivalent target framework - // If the identifiers didn't match, we need to see if this framework has an equivalent framework that DOES match. - // If it does, we use that from here on. - // Example: - // If the Project Targets ASP.Net, Version=5.0. It can accept Packages targetting .NETFramework, Version=4.5.1 - // so since the identifiers don't match, we need to "translate" the project target framework to .NETFramework - // however, we still want direct ASP.Net == ASP.Net matches, so we do this ONLY if the identifiers don't already match - if (_equivalentProjectFrameworks.TryGetValue(projectFrameworkName.Identifier, out projectFrameworkName)) - { - // Goto might be evil but it's so nice to use here - goto check; - } - else - { - return false; - } + return IsPortableLibraryCompatible(table, projectFrameworkName, packageTargetFrameworkName); } - if (NormalizeVersion(projectFrameworkName.Version) < - NormalizeVersion(packageTargetFrameworkName.Version)) - { - return false; - } - - // If the profile names are equal then they're compatible - if (String.Equals(projectFrameworkName.Profile, packageTargetFrameworkName.Profile, StringComparison.OrdinalIgnoreCase)) - { - return true; - } - - // Get the compatibility mapping for this framework identifier - CompatibilityMapping mapping; - if (_compatibiltyMapping.TryGetValue(projectFrameworkName.Identifier, out mapping)) - { - // Get all compatible profiles for the target profile - string[] compatibleProfiles; - if (mapping.TryGetValue(packageTargetFrameworkName.Profile, out compatibleProfiles)) - { - // See if this profile is in the list of compatible profiles - return compatibleProfiles.Contains(projectFrameworkName.Profile, StringComparer.OrdinalIgnoreCase); - } - } - - return false; + return isCompatible; } - private static bool IsPortableLibraryCompatible(FrameworkName projectFrameworkName, FrameworkName packageTargetFrameworkName) + private static bool IsPortableLibraryCompatible( + NetPortableProfileTable table, + FrameworkName projectFrameworkName, + FrameworkName packageTargetFrameworkName) { - if (String.IsNullOrEmpty(packageTargetFrameworkName.Profile)) + if (string.IsNullOrEmpty(packageTargetFrameworkName.Profile)) { return false; } - NetPortableProfile targetFrameworkPortableProfile = NetPortableProfile.Parse(packageTargetFrameworkName.Profile); + NetPortableProfile targetFrameworkPortableProfile = NetPortableProfile.Parse(table, packageTargetFrameworkName.Profile); if (targetFrameworkPortableProfile == null) { return false; @@ -1071,12 +1032,12 @@ private static bool IsPortableLibraryCompatible(FrameworkName projectFrameworkNa if (projectFrameworkName.IsPortableFramework()) { // this is the case with Portable Library vs. Portable Library - if (String.Equals(projectFrameworkName.Profile, packageTargetFrameworkName.Profile, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(projectFrameworkName.Profile, packageTargetFrameworkName.Profile, StringComparison.OrdinalIgnoreCase)) { return true; } - NetPortableProfile frameworkPortableProfile = NetPortableProfile.Parse(projectFrameworkName.Profile); + NetPortableProfile frameworkPortableProfile = NetPortableProfile.Parse(table, projectFrameworkName.Profile); if (frameworkPortableProfile == null) { return false; @@ -1087,222 +1048,8 @@ private static bool IsPortableLibraryCompatible(FrameworkName projectFrameworkNa else { // this is the case with Portable Library installed into a normal project - return targetFrameworkPortableProfile.IsCompatibleWith(projectFrameworkName); - } - } - - /// - /// Given 2 framework names, this method returns a number which determines how compatible - /// the names are. The higher the number the more compatible the frameworks are. - /// - private static long GetProfileCompatibility(FrameworkName projectFrameworkName, FrameworkName packageTargetFrameworkName) - { - projectFrameworkName = NormalizeFrameworkName(projectFrameworkName); - packageTargetFrameworkName = NormalizeFrameworkName(packageTargetFrameworkName); - - if (packageTargetFrameworkName.IsPortableFramework()) - { - if (projectFrameworkName.IsPortableFramework()) - { - return GetCompatibilityBetweenPortableLibraryAndPortableLibrary(projectFrameworkName, packageTargetFrameworkName); - } - else - { - // we divide by 2 to ensure Portable framework has less compatibility value than specific framework. - return GetCompatibilityBetweenPortableLibraryAndNonPortableLibrary(projectFrameworkName, packageTargetFrameworkName) / 2; - } - } - - long compatibility = 0; - - // Calculate the "distance" between the target framework version and the project framework version. - // When comparing two framework candidates, we pick the one with higher version. - compatibility += CalculateVersionDistance( - projectFrameworkName.Version, - GetEffectiveFrameworkVersion(projectFrameworkName, packageTargetFrameworkName)); - - // Things with matching profiles are more compatible than things without. - // This means that if we have net40 and net40-client assemblies and the target framework is - // net40, both sets of assemblies are compatible but we prefer net40 since it matches - // the profile exactly. - if (packageTargetFrameworkName.Profile.Equals(projectFrameworkName.Profile, StringComparison.OrdinalIgnoreCase)) - { - compatibility++; - } - - // this is to give specific profile higher compatibility than portable profile - if (packageTargetFrameworkName.Identifier.Equals(projectFrameworkName.Identifier, StringComparison.OrdinalIgnoreCase)) - { - // Let's say a package has two framework folders: 'net40' and 'portable-net45+wp8'. - // The package is installed into a net45 project. We want to pick the 'net40' folder, even though - // the 'net45' in portable folder has a matching version with the project's framework. - // - // So, in order to achieve that, here we give the folder that has matching identifer with the project's - // framework identifier a compatibility score of 10, to make sure it weighs more than the compatibility of matching version. - - compatibility += 10 * (1L << 32); + return targetFrameworkPortableProfile.IsCompatibleWith(table, projectFrameworkName); } - - return compatibility; - } - - private static long CalculateVersionDistance(Version projectVersion, Version targetFrameworkVersion) - { - // the +5 is to counter the profile compatibility increment (+1) - const long MaxValue = 1L << 32 + 5; - - // calculate the "distance" between 2 versions - var distance = (projectVersion.Major - targetFrameworkVersion.Major) * 255L * 255 * 255 + - (projectVersion.Minor - targetFrameworkVersion.Minor) * 255L * 255 + - (projectVersion.Build - targetFrameworkVersion.Build) * 255L + - (projectVersion.Revision - targetFrameworkVersion.Revision); - - Debug.Assert(MaxValue >= distance); - - // the closer the versions are, the higher the returned value is. - return MaxValue - distance; - } - - private static Version GetEffectiveFrameworkVersion(FrameworkName projectFramework, FrameworkName targetFrameworkVersion) - { - if (targetFrameworkVersion.IsPortableFramework()) - { - NetPortableProfile profile = NetPortableProfile.Parse(targetFrameworkVersion.Profile); - if (profile != null) - { - // if it's a portable library, return the version of the matching framework - var compatibleFramework = profile.SupportedFrameworks.FirstOrDefault(f => VersionUtility.IsCompatible(projectFramework, f)); - if (compatibleFramework != null) - { - return compatibleFramework.Version; - } - } - } - - return targetFrameworkVersion.Version; - } - - /// - /// Attempt to calculate how compatible a portable framework folder is to a portable project. - /// The two portable frameworks passed to this method MUST be compatible with each other. - /// - /// - /// The returned score will be negative value. - /// - internal static int GetCompatibilityBetweenPortableLibraryAndPortableLibrary(FrameworkName projectFrameworkName, FrameworkName packageTargetFrameworkName) - { - // Algorithms: Give a score from 0 to N indicating how close *in version* each package platform is the project’s platforms - // and then choose the folder with the lowest score. If the score matches, choose the one with the least platforms. - // - // For example: - // - // Project targeting: .NET 4.5 + SL5 + WP71 - // - // Package targeting: - // .NET 4.5 (0) + SL5 (0) + WP71 (0) == 0 - // .NET 4.5 (0) + SL5 (0) + WP71 (0) + Win8 (0) == 0 - // .NET 4.5 (0) + SL4 (1) + WP71 (0) + Win8 (0) == 1 - // .NET 4.0 (1) + SL4 (1) + WP71 (0) + Win8 (0) == 2 - // .NET 4.0 (1) + SL4 (1) + WP70 (1) + Win8 (0) == 3 - // - // Above, there’s two matches with the same result, choose the one with the least amount of platforms. - // - // There will be situations, however, where there is still undefined behavior, such as: - // - // .NET 4.5 (0) + SL4 (1) + WP71 (0) == 1 - // .NET 4.0 (1) + SL5 (0) + WP71 (0) == 1 - - NetPortableProfile projectFrameworkProfile = NetPortableProfile.Parse(projectFrameworkName.Profile); - Debug.Assert(projectFrameworkProfile != null); - - NetPortableProfile packageTargetFrameworkProfile = NetPortableProfile.Parse(packageTargetFrameworkName.Profile, treatOptionalFrameworksAsSupportedFrameworks: true); - Debug.Assert(packageTargetFrameworkProfile != null); - - int nonMatchingCompatibleFrameworkCount = 0; - int inCompatibleOptionalFrameworkCount = 0; - foreach (var supportedPackageTargetFramework in packageTargetFrameworkProfile.SupportedFrameworks) - { - var compatibleProjectFramework = projectFrameworkProfile.SupportedFrameworks.FirstOrDefault(f => IsCompatible(f, supportedPackageTargetFramework)); - if (compatibleProjectFramework != null && compatibleProjectFramework.Version > supportedPackageTargetFramework.Version) - { - nonMatchingCompatibleFrameworkCount++; - } - } - - foreach (var optionalProjectFramework in projectFrameworkProfile.OptionalFrameworks) - { - var compatiblePackageTargetFramework = packageTargetFrameworkProfile.SupportedFrameworks.FirstOrDefault(f => IsCompatible(f, optionalProjectFramework)); - if (compatiblePackageTargetFramework == null || compatiblePackageTargetFramework.Version > optionalProjectFramework.Version) - { - inCompatibleOptionalFrameworkCount++; - } - else if (compatiblePackageTargetFramework != null && compatiblePackageTargetFramework.Version < optionalProjectFramework.Version) - { - // we check again if the package version < project version, because, if they are equal, they are matching compatible frameworks - // neither inCompatibleOptionalFrameworkCount nor nonMatchingCompatibleFrameworkCount should be incremented - nonMatchingCompatibleFrameworkCount++; - } - } - - // The following is the maximum project framework count which is also the maximum possible incompatibilities - int maxPossibleIncompatibleFrameworkCount = 1 + projectFrameworkProfile.SupportedFrameworks.Count + projectFrameworkProfile.OptionalFrameworks.Count; - - // This is to ensure that profile with compatible optional frameworks wins over profiles without, even, when supported frameworks are highly compatible - // If there are no incompatible optional frameworks, the score below will be simply nonMatchingCompatibleFrameworkCount - // For example, Let Project target net45+sl5+monotouch+monoandroid. And, Package has 4 profiles, (THIS EXAMPLE IS LIKELY NOT A REAL_WORLD SCENARIO :)) - // A: net45+sl5, B: net40+sl5+monotouch, C: net40+sl4+monotouch+monoandroid, D: net40+sl4+monotouch+monoandroid+wp71 - // At this point, Compatibility is as follows. C = D > B > A. Scores for A = (5 * 2 + 0), B = (5 * 1 + 1), C = (5 * 0 + 2), D = (5 * 0 + 2) - // The scores are 10, 6, 2 and 2. Both C and D are the most compatible with a score of 2 - // Clearly, having more number of frameworks, supported and optional, that are compatible is preferred over most compatible supported frameworks alone - int score = maxPossibleIncompatibleFrameworkCount * inCompatibleOptionalFrameworkCount + - nonMatchingCompatibleFrameworkCount; - - // This is to ensure that if two portable frameworks have the same score, - // we pick the one that has less number of supported platforms. - // In the example described in comments above, both C and D had an equal score of 2. With the following correction, new scores are as follows - // A = (10 * 50 + 2), B = (6 * 50 + 3), C = (2 * 50 + 4), D = (2 * 50 + 5) - // A = 502, B = 303, C = 104, D = 105. And, C has the lowest score and the most compatible - score = score * 50 + packageTargetFrameworkProfile.SupportedFrameworks.Count; - - // Our algorithm returns lowest score for the most compatible framework. - // However, the caller of this method expects it to have the highest score. - // Hence, we return the negative value of score here. - return -score; - } - - internal static long GetCompatibilityBetweenPortableLibraryAndNonPortableLibrary(FrameworkName projectFrameworkName, FrameworkName packagePortableFramework) - { - NetPortableProfile packageFrameworkProfile = NetPortableProfile.Parse(packagePortableFramework.Profile, treatOptionalFrameworksAsSupportedFrameworks: true); - if (packageFrameworkProfile == null) - { - // defensive coding, this should never happen - Debug.Fail("'portableFramework' is not a valid portable framework."); - return long.MinValue; - } - - // among the supported frameworks by the Portable library, pick the one that is compatible with 'projectFrameworkName' - var compatibleFramework = packageFrameworkProfile.SupportedFrameworks.FirstOrDefault(f => IsCompatible(projectFrameworkName, f)); - - if (compatibleFramework != null) - { - var score = GetProfileCompatibility(projectFrameworkName, compatibleFramework); - - // This is to ensure that if two portable frameworks have the same score, - // we pick the one that has less number of supported platforms. - // The *2 is to make up for the /2 to which the result of this method is subject. - score -= (packageFrameworkProfile.SupportedFrameworks.Count * 2); - - return score; - } - else if (NetPortableProfileTable.HasCompatibleProfileWith(packageFrameworkProfile, projectFrameworkName)) - { - // Get the list of portable profiles that supports projectFrameworkName - // And, see if there is atleast 1 profile which is compatible with packageFrameworkProfile - // If so, return 0 - (packageFrameworkProfile.SupportedFrameworks.Count * 2) - return 0 - (packageFrameworkProfile.SupportedFrameworks.Count * 2); - } - - return long.MinValue; } private static bool TryParseVersion(string versionString, out SemanticVersion version) @@ -1327,5 +1074,17 @@ public static bool IsPortableFramework(this FrameworkName framework) // Thus we can ignore the profile part here return framework != null && PortableFrameworkIdentifier.Equals(framework.Identifier, StringComparison.OrdinalIgnoreCase); } + + internal static NuGetFramework GetNuGetFramework( + NetPortableProfileTable table, + IFrameworkNameProvider provider, + FrameworkName framework) + { + // Use the short folder name as the common format between FrameworkName and + // NuGetFramework. With portable frameworks, there are differences in + // FrameworkName and NuGetFramework.DotNetFrameworkName. + var folderName = GetShortFrameworkName(table, framework); + return NuGetFramework.ParseFolder(folderName, provider); + } } } diff --git a/test/Core.Test/NetPortableProfileTableTest.cs b/test/Core.Test/NetPortableProfileTableTest.cs index 335084759..f7ec11b1c 100644 --- a/test/Core.Test/NetPortableProfileTableTest.cs +++ b/test/Core.Test/NetPortableProfileTableTest.cs @@ -12,25 +12,33 @@ public class NetPortableProfileTableTest public async Task NetPortableProfileTable_LoadTestForThreadSafety() { // Arrange - var tasks = Enumerable - .Range(1, 20) - .Select(task => Task.Run(async () => - { - await Task.Yield(); - - for (int iteration = 0; iteration < 50; iteration++) + try + { + var tasks = Enumerable + .Range(1, 20) + .Select(task => Task.Run(async () => { - NetPortableProfileTable.SetProfileCollection(null); - - // Act - var result = NetPortableProfileTable.GetProfile("not-real"); - - // Assert - Assert.Null(result); - } - })); - - await Task.WhenAll(tasks); + await Task.Yield(); + + for (int iteration = 0; iteration < 50; iteration++) + { + NetPortableProfileTable.SetProfileCollection(null); + + // Act + var result = NetPortableProfileTable.Instance.GetProfile("not-real"); + + // Assert + Assert.Null(result); + } + })); + + await Task.WhenAll(tasks); + } + finally + { + // Reset the static state. + NetPortableProfileTable.SetProfileCollection(null); + } } [Fact] diff --git a/test/Core.Test/NetPortableProfileTest.cs b/test/Core.Test/NetPortableProfileTest.cs index a3a7925fc..a730f6ad4 100644 --- a/test/Core.Test/NetPortableProfileTest.cs +++ b/test/Core.Test/NetPortableProfileTest.cs @@ -1,15 +1,55 @@ using System; using System.Collections.Generic; -using System.Linq; +using System.Diagnostics; using System.Runtime.Versioning; -using System.Text; using Xunit; -using Xunit.Extensions; namespace NuGet.Test { public class NetPortableProfileTest { + [Fact] + public void IsCompatible_SupportsCustomProfileNumber() + { + // Arrange + var profile = new NetPortableProfile( + "Profile500", + new[] { + new FrameworkName("Silverlight, Version=4.0"), + new FrameworkName("WindowsPhone, Version=7.1"), + }); + + var tc = new TestContext(profile); + var project = new FrameworkName("Silverlight, Version=5.0"); + var compatible = new FrameworkName(".NETPortable, Version=4.0, Profile=Profile500"); + var notCompatible = new FrameworkName(".NETPortable, Version=4.0, Profile=Profile501"); + + // Act & Assert + tc.VerifyIsCompatible(project, compatible, true); + tc.VerifyIsCompatible(project, notCompatible, false); + } + + [Fact] + public void IsCompatible_SupportsCustomProfileName() + { + // Arrange + var profile = new NetPortableProfile( + "MyProfile", + new[] { + new FrameworkName("Silverlight, Version=4.0"), + new FrameworkName("WindowsPhone, Version=7.1"), + }); + + var tc = new TestContext(profile); + var project = new FrameworkName("Silverlight, Version=5.0"); + var compatible = new FrameworkName(".NETPortable, Version=4.0, Profile=MyProfile"); + var notCompatible = new FrameworkName(".NETPortable, Version=4.0, Profile=YourProfile"); + + // Act & Assert + tc.VerifyIsCompatible(project, compatible, true); + tc.VerifyIsCompatible(project, notCompatible, false); + } + [Fact] public void NamePropertyReturnsCorrectValue() { @@ -44,7 +84,7 @@ public void SupportedFrameworksReturnsCorrectValue() public void TestIsCompatibleWithFrameworkName() { // Arrange - var profile = new NetPortableProfile( + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { new FrameworkName(".NETFramework, Version=4.5"), @@ -52,6 +92,9 @@ public void TestIsCompatibleWithFrameworkName() new FrameworkName("WindowsPhone, Version=7.1"), }); + var tc = new TestContext(packageProfile); + var packageFramework = GetFrameworkName(packageProfile); + var fw1 = new FrameworkName(".NETFramework, Version=4.0"); var fw2 = new FrameworkName(".NETFramework, Version=4.5"); var fw3 = new FrameworkName("Silverlight, Version=3.0"); @@ -61,20 +104,20 @@ public void TestIsCompatibleWithFrameworkName() var fw7 = new FrameworkName(".NETCore, Version=4.5"); // Act & Assert - Assert.False(profile.IsCompatibleWith(fw1)); - Assert.True(profile.IsCompatibleWith(fw2)); - Assert.False(profile.IsCompatibleWith(fw3)); - Assert.True(profile.IsCompatibleWith(fw4)); - Assert.True(profile.IsCompatibleWith(fw5)); - Assert.False(profile.IsCompatibleWith(fw6)); - Assert.False(profile.IsCompatibleWith(fw7)); + tc.VerifyIsCompatible(fw1, packageFramework, false); + tc.VerifyIsCompatible(fw2, packageFramework, true); + tc.VerifyIsCompatible(fw3, packageFramework, false); + tc.VerifyIsCompatible(fw4, packageFramework, true); + tc.VerifyIsCompatible(fw5, packageFramework, true); + tc.VerifyIsCompatible(fw6, packageFramework, false); + tc.VerifyIsCompatible(fw7, packageFramework, false); } [Fact] public void TestIsCompatibleWithPortableProfile1() { // Arrange - var profile = new NetPortableProfile( + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { new FrameworkName(".NETFramework, Version=4.5"), @@ -82,23 +125,26 @@ public void TestIsCompatibleWithPortableProfile1() new FrameworkName("WindowsPhone, Version=7.1"), }); - var targetProfile = new NetPortableProfile( + var projectProfile = new NetPortableProfile( "MyProfile", new[] { new FrameworkName(".NETFramework, Version=5.0"), new FrameworkName("Silverlight, Version=4.0") }); + var tc = new TestContext(packageProfile, projectProfile); + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = GetFrameworkName(projectProfile); // Act & Assert - Assert.True(profile.IsCompatibleWith(targetProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, true); } [Fact] public void TestIsCompatibleWithPortableProfile2() { // Arrange - var profile = new NetPortableProfile( + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { new FrameworkName(".NETFramework, Version=4.5"), @@ -106,22 +152,27 @@ public void TestIsCompatibleWithPortableProfile2() new FrameworkName("WindowsPhone, Version=7.1"), }); - var targetProfile = new NetPortableProfile( + var projectProfile = new NetPortableProfile( "MyProfile", new[] { new FrameworkName(".NETFramework, Version=4.0"), new FrameworkName("Silverlight, Version=4.0") }); + var tc = new TestContext(packageProfile, projectProfile); + + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = GetFrameworkName(projectProfile); + // Act & Assert - Assert.False(profile.IsCompatibleWith(targetProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, false); } [Fact] public void TestIsCompatibleWithPortableProfile3() { // Arrange - var profile = new NetPortableProfile( + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { new FrameworkName(".NETFramework, Version=4.5"), @@ -129,7 +180,7 @@ public void TestIsCompatibleWithPortableProfile3() new FrameworkName("WindowsPhone, Version=7.1"), }); - var targetProfile = new NetPortableProfile( + var projectProfile = new NetPortableProfile( "MyProfile", new[] { new FrameworkName(".NETFramework, Version=4.5"), @@ -137,15 +188,20 @@ public void TestIsCompatibleWithPortableProfile3() new FrameworkName(".NETCore, Version=4.0") }); + var tc = new TestContext(packageProfile, projectProfile); + + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = GetFrameworkName(projectProfile); + // Act & Assert - Assert.False(profile.IsCompatibleWith(targetProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, false); } [Fact] public void TestIsCompatibleWithPortableProfile4() { // Arrange - var profile = new NetPortableProfile( + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { new FrameworkName(".NETFramework, Version=4.5"), @@ -153,75 +209,95 @@ public void TestIsCompatibleWithPortableProfile4() new FrameworkName("WindowsPhone, Version=7.1"), }); - var targetProfile = new NetPortableProfile( + var projectProfile = new NetPortableProfile( "MyProfile", new[] { new FrameworkName(".NETCore, Version=4.0") }); + var tc = new TestContext(packageProfile, projectProfile); + + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = GetFrameworkName(projectProfile); + // Act & Assert - Assert.False(profile.IsCompatibleWith(targetProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, false); } [Fact] public void TestIsCompatibleWithPortableProfile5() { // Arrange - var profile = new NetPortableProfile( + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { new FrameworkName(".NETFramework, Version=4.5"), }); - var targetProfile = new NetPortableProfile( + var projectProfile = new NetPortableProfile( "MyProfile", new[] { new FrameworkName(".NETFramework, Version=4.5"), new FrameworkName(".NETCore, Version=4.0") }); + var tc = new TestContext(packageProfile, projectProfile); + + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = GetFrameworkName(projectProfile); + // Act & Assert - Assert.False(profile.IsCompatibleWith(targetProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, false); } [Fact] public void TestIsCompatibleWithPortableProfile6() { // Arrange - var profile = new NetPortableProfile( + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { new FrameworkName("WindowsPhone, Version=8.0"), }); - var targetProfile = new NetPortableProfile( + var projectProfile = new NetPortableProfile( "MyProfile", new[] { new FrameworkName("WindowsPhone, Version=8.0"), }); + var tc = new TestContext(packageProfile, projectProfile); + + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = GetFrameworkName(projectProfile); + // Act & Assert - Assert.True(profile.IsCompatibleWith(targetProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, true); } [Fact] public void TestIsCompatibleWithPortableProfile7() { // Arrange - var profile = new NetPortableProfile( + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { new FrameworkName("WindowsPhone, Version=7.0"), }); - var targetProfile = new NetPortableProfile( + var projectProfile = new NetPortableProfile( "MyProfile", new[] { new FrameworkName("WindowsPhone, Version=7.1"), }); + var tc = new TestContext(packageProfile, projectProfile); + + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = GetFrameworkName(projectProfile); + // Act & Assert - Assert.True(profile.IsCompatibleWith(targetProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, true); } [Fact] @@ -246,8 +322,13 @@ public void TestIsCompatibleWithPortableProfile8WithMono() new FrameworkName("MonoAndroid, Version=1.0"), }); + var tc = new TestContext(packageProfile, projectProfile); + + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = GetFrameworkName(projectProfile); + // Act & Assert - Assert.True(packageProfile.IsCompatibleWith(projectProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, true); } [Fact] @@ -268,9 +349,6 @@ public void TestIsCompatibleWithPortableProfile9WithMonoProjectWithVersionEqualT new FrameworkName("MonoMac, Version=1.0"), }); - NetPortableProfileCollection profileCollection = new NetPortableProfileCollection(); - profileCollection.Add(profile1); - var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { @@ -280,12 +358,13 @@ public void TestIsCompatibleWithPortableProfile9WithMonoProjectWithVersionEqualT new FrameworkName("Windows, Version=8.0"), }); - var projectProfile = new FrameworkName("MonoAndroid, Version=1.0"); - - NetPortableProfileTable.SetProfileCollection(profileCollection); + var tc = new TestContext(profile1, packageProfile); + + var packageFramework = GetFrameworkName(profile1); + var projectFramework = new FrameworkName("MonoAndroid, Version=1.0"); // Act & Assert - Assert.True(packageProfile.IsCompatibleWith(projectProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, true); } [Fact] @@ -305,10 +384,7 @@ public void TestIsCompatibleWithPortableProfile10WithMonoProjectWithVersionGreat new FrameworkName("MonoAndroid, Version=1.0"), new FrameworkName("MonoMac, Version=1.0"), }); - - NetPortableProfileCollection profileCollection = new NetPortableProfileCollection(); - profileCollection.Add(profile1); - + var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { @@ -318,12 +394,13 @@ public void TestIsCompatibleWithPortableProfile10WithMonoProjectWithVersionGreat new FrameworkName("Windows, Version=8.0"), }); - var projectProfile = new FrameworkName("MonoAndroid, Version=2.0"); + var tc = new TestContext(profile1, packageProfile); - NetPortableProfileTable.SetProfileCollection(profileCollection); + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = new FrameworkName("MonoAndroid, Version=2.0"); // Act & Assert - Assert.True(packageProfile.IsCompatibleWith(projectProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, true); } [Fact] @@ -344,9 +421,6 @@ public void TestIsCompatibleWithPortableProfile11WithMonoProjectWithVersionLesse new FrameworkName("MonoMac, Version=1.0"), }); - NetPortableProfileCollection profileCollection = new NetPortableProfileCollection(); - profileCollection.Add(profile1); - var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { @@ -356,12 +430,13 @@ public void TestIsCompatibleWithPortableProfile11WithMonoProjectWithVersionLesse new FrameworkName("Windows, Version=8.0"), }); - var projectProfile = new FrameworkName("MonoAndroid, Version=1.0"); + var tc = new TestContext(profile1, packageProfile); - NetPortableProfileTable.SetProfileCollection(profileCollection); + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = new FrameworkName("MonoAndroid, Version=1.0"); // Act & Assert - Assert.False(packageProfile.IsCompatibleWith(projectProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, false); } [Fact] @@ -405,11 +480,6 @@ public void TestIsCompatibleWithPortableProfile12WithMonoProjectWithMultipleInst new FrameworkName("MonoAndroid, Version=3.0"), }); - NetPortableProfileCollection profileCollection = new NetPortableProfileCollection(); - profileCollection.Add(profile1); - profileCollection.Add(profile2); - profileCollection.Add(profile3); - var packageProfile = new NetPortableProfile( "ProfileXXX", new[] { @@ -419,19 +489,20 @@ public void TestIsCompatibleWithPortableProfile12WithMonoProjectWithMultipleInst new FrameworkName("Windows, Version=8.0"), }); - var projectProfile = new FrameworkName("MonoAndroid, Version=2.0"); + var tc = new TestContext(profile1, profile2, profile3, packageProfile); - NetPortableProfileTable.SetProfileCollection(profileCollection); + var packageFramework = GetFrameworkName(packageProfile); + var projectFramework = new FrameworkName("MonoAndroid, Version=2.0"); // Act & Assert - Assert.True(packageProfile.IsCompatibleWith(projectProfile)); + tc.VerifyIsCompatible(projectFramework, packageFramework, true); } [Fact] public void TestParseWithCustomProfileString1() { // Arrange & Act - var profile = NetPortableProfile.Parse("sl3+net+netcore45"); + var profile = NetPortableProfile.Parse(NetPortableProfileTable.Instance, "sl3+net+netcore45"); // Assert Assert.Equal(3, profile.SupportedFrameworks.Count); @@ -444,7 +515,7 @@ public void TestParseWithCustomProfileString1() public void TestParseWithCustomProfileString2() { // Arrange & Act - var profile = NetPortableProfile.Parse("wp7"); + var profile = NetPortableProfile.Parse(NetPortableProfileTable.Instance, "wp7"); // Assert Assert.Equal(1, profile.SupportedFrameworks.Count); @@ -455,7 +526,7 @@ public void TestParseWithCustomProfileString2() public void TestParseWithInvalidCustomProfileReturnsNull() { // Arrange & Act - var profile = NetPortableProfile.Parse("Profile3284"); + var profile = NetPortableProfile.Parse(NetPortableProfileTable.Instance, "Profile3284"); // Assert Assert.Null(profile); @@ -465,7 +536,9 @@ public void TestParseWithInvalidCustomProfileReturnsNull() public void TestParseWithCustomProfileString3() { // Arrange & Act - var profile = NetPortableProfile.Parse("wp71+win8+monoandroid1.6+monotouch1.0+sl4+net45"); + var profile = NetPortableProfile.Parse( + NetPortableProfileTable.Instance, + "wp71+win8+monoandroid1.6+monotouch1.0+sl4+net45"); // Assert Assert.Equal(6, profile.SupportedFrameworks.Count); @@ -480,8 +553,8 @@ public void TestParseWithCustomProfileString3() [Fact] public void TestParseWithStandardProfileString() { - // Arrange & Act - var profileCollection = new NetPortableProfileCollection(); + // Arrange + var collection = new NetPortableProfileCollection(); var profile1 = new NetPortableProfile( "Profile1", new[] { @@ -497,12 +570,13 @@ public void TestParseWithStandardProfileString() new FrameworkName("Silverlight, Version=3.0"), new FrameworkName("WindowsPhone, Version=7.1"), }); - profileCollection.Add(profile1); - profileCollection.Add(profile2); + collection.Add(profile1); + collection.Add(profile2); - NetPortableProfileTable.SetProfileCollection(profileCollection); + var table = new NetPortableProfileTable(collection); - var profile = NetPortableProfile.Parse("Profile2"); + // Act + var profile = NetPortableProfile.Parse(table, "Profile2"); // Assert Assert.Equal(3, profile.SupportedFrameworks.Count); @@ -514,8 +588,8 @@ public void TestParseWithStandardProfileString() [Fact] public void TestParseWithCustomProfileString4WithDifferentOrderingOfFrameworks() { - // Arrange & Act - var profileCollection = new NetPortableProfileCollection(); + // Arrange + var collection = new NetPortableProfileCollection(); var profile1 = new NetPortableProfile( "Profile1", new[] { @@ -535,12 +609,15 @@ public void TestParseWithCustomProfileString4WithDifferentOrderingOfFrameworks() new FrameworkName("Silverlight, Version=3.0"), new FrameworkName("WindowsPhone, Version=7.1"), }); - profileCollection.Add(profile1); - profileCollection.Add(profile2); + collection.Add(profile1); + collection.Add(profile2); - NetPortableProfileTable.SetProfileCollection(profileCollection); + var table = new NetPortableProfileTable(collection); - var profile = NetPortableProfile.Parse("net45+sl40+MonoTouch+wp71+MonoAndroid20"); + // Act + var profile = NetPortableProfile.Parse( + table, + "net45+sl40+MonoTouch+wp71+MonoAndroid20"); // Assert Assert.Equal(5, profile.SupportedFrameworks.Count); @@ -554,8 +631,8 @@ public void TestParseWithCustomProfileString4WithDifferentOrderingOfFrameworks() [Fact] public void TestParseWithCustomProfileString5WithOptionalFrameworkAndTreatedAsOptional() { - // Arrange & Act - var profileCollection = new NetPortableProfileCollection(); + // Arrange + var collection = new NetPortableProfileCollection(); var profile1 = new NetPortableProfile( "Profile1", new[] { @@ -575,13 +652,16 @@ public void TestParseWithCustomProfileString5WithOptionalFrameworkAndTreatedAsOp new FrameworkName("Silverlight, Version=3.0"), new FrameworkName("WindowsPhone, Version=7.1"), }); - profileCollection.Add(profile1); - profileCollection.Add(profile2); + collection.Add(profile1); + collection.Add(profile2); - NetPortableProfileTable.SetProfileCollection(profileCollection); + var table = new NetPortableProfileTable(collection); + // Act // Default value of second parameter treatOptionalFrameworksAsSupportedFrameworks is false - var profile = NetPortableProfile.Parse("net45+sl40+wp71+MonoTouch+MonoAndroid20"); + var profile = NetPortableProfile.Parse( + table, + "net45+sl40+wp71+MonoTouch+MonoAndroid20"); // Assert Assert.Equal(3, profile.SupportedFrameworks.Count); @@ -596,8 +676,8 @@ public void TestParseWithCustomProfileString5WithOptionalFrameworkAndTreatedAsOp [Fact] public void TestParseWithCustomProfileString6WithOptionalFrameworkAndTreatedAsSupported() { - // Arrange & Act - var profileCollection = new NetPortableProfileCollection(); + // Arrange + var collection = new NetPortableProfileCollection(); var profile1 = new NetPortableProfile( "Profile1", new[] { @@ -617,12 +697,16 @@ public void TestParseWithCustomProfileString6WithOptionalFrameworkAndTreatedAsSu new FrameworkName("Silverlight, Version=3.0"), new FrameworkName("WindowsPhone, Version=7.1"), }); - profileCollection.Add(profile1); - profileCollection.Add(profile2); + collection.Add(profile1); + collection.Add(profile2); - NetPortableProfileTable.SetProfileCollection(profileCollection); + var table = new NetPortableProfileTable(collection); - var profile = NetPortableProfile.Parse("net45+sl40+wp71+MonoTouch+MonoAndroid20", treatOptionalFrameworksAsSupportedFrameworks: true); + // Act + var profile = NetPortableProfile.Parse( + table, + "net45+sl40+wp71+MonoTouch+MonoAndroid20", + treatOptionalFrameworksAsSupportedFrameworks: true); // Assert Assert.Equal(5, profile.SupportedFrameworks.Count); @@ -632,5 +716,51 @@ public void TestParseWithCustomProfileString6WithOptionalFrameworkAndTreatedAsSu Assert.True(profile.SupportedFrameworks.Contains(new FrameworkName("WindowsPhone, Version=7.1"))); Assert.True(profile.SupportedFrameworks.Contains(new FrameworkName("MonoTouch, Version=0.0"))); } + + private static FrameworkName GetFrameworkName(NetPortableProfile profile) + { + return new FrameworkName( + ".NETPortable", + new Version(profile.FrameworkVersion.Substring(1)), // "v0.0" becomes "0.0" + profile.Name); + } + + private class TestContext + { + public TestContext(params NetPortableProfile[] portableProfiles) + { + Collection = new NetPortableProfileCollection(); + Collection.AddRange(portableProfiles); + + Table = new NetPortableProfileTable(Collection); + + CompatibilityProvider = new ReferenceAssemblyCompatibilityProvider(Collection); + + NameProvider = new ReferenceAssemblyFrameworkNameProvider(Collection); + } + + public NetPortableProfileCollection Collection { get; private set; } + public ReferenceAssemblyCompatibilityProvider CompatibilityProvider { get; private set; } + public ReferenceAssemblyFrameworkNameProvider NameProvider { get; private set; } + public NetPortableProfileTable Table { get; private set; } + + public void VerifyIsCompatible(FrameworkName project, FrameworkName package, bool expected) + { + var actual = VersionUtility.IsCompatible( + Table, + CompatibilityProvider, + NameProvider, + project, + package); + + Assert.True( + actual == expected, + string.Format( + "'{0}' should {1}be compatible with '{2}'.", + project, + expected ? string.Empty : "not ", + package)); + } + } } } \ No newline at end of file diff --git a/test/Core.Test/ProjectManagerTest.cs b/test/Core.Test/ProjectManagerTest.cs index ec0fae859..fe0cc13d9 100644 --- a/test/Core.Test/ProjectManagerTest.cs +++ b/test/Core.Test/ProjectManagerTest.cs @@ -486,21 +486,29 @@ public void AddingPackageReferenceAddsPreprocessedFileToTargetPathWithRemovedExt public void AddPackageReferenceThrowsWhenNoTargetFrameworkIsCompatibleWithPortableProject() { // Arrange - var portableCollection = new NetPortableProfileCollection(); - portableCollection.Add(new NetPortableProfile("Profile104", new [] { VersionUtility.ParseFrameworkName("net45"), VersionUtility.ParseFrameworkName("sl5")})); + try + { + var portableCollection = new NetPortableProfileCollection(); + portableCollection.Add(new NetPortableProfile("Profile104", new[] { VersionUtility.ParseFrameworkName("net45"), VersionUtility.ParseFrameworkName("sl5") })); - NetPortableProfileTable.SetProfileCollection(portableCollection); + NetPortableProfileTable.SetProfileCollection(portableCollection); - var sourceRepository = new MockPackageRepository(); - var projectSystem = new MockProjectSystem(new FrameworkName(".NETPortable, Version=1.0, Profile=Profile104")); - var projectManager = new ProjectManager(sourceRepository, new DefaultPackagePathResolver(projectSystem), projectSystem, new MockPackageRepository()); - IPackage packageA = PackageUtility.CreatePackage("A", "1.0", new[] { "silverlight4\\a.txt"}); - sourceRepository.AddPackage(packageA); + var sourceRepository = new MockPackageRepository(); + var projectSystem = new MockProjectSystem(new FrameworkName(".NETPortable, Version=1.0, Profile=Profile104")); + var projectManager = new ProjectManager(sourceRepository, new DefaultPackagePathResolver(projectSystem), projectSystem, new MockPackageRepository()); + IPackage packageA = PackageUtility.CreatePackage("A", "1.0", new[] { "silverlight4\\a.txt" }); + sourceRepository.AddPackage(packageA); - // Act - ExceptionAssert.Throws( - () => projectManager.AddPackageReference("A"), - "Could not install package 'A 1.0'. You are trying to install this package into a project that targets 'portable-net45+sl50', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author."); + // Act + ExceptionAssert.Throws( + () => projectManager.AddPackageReference("A"), + "Could not install package 'A 1.0'. You are trying to install this package into a project that targets 'portable-net45+sl50', but the package does not contain any assembly references or content files that are compatible with that framework. For more information, contact the package author."); + } + finally + { + // Reset the static state. + NetPortableProfileTable.SetProfileCollection(null); + } } [Fact] @@ -772,7 +780,7 @@ public void AddPackageReferencePicksMatchingProfileEvenIfItIsEmpty() [InlineData("portable-net40+sl3+wp71\\one.txt", "portable-windows8+sl2\\two.txt")] public void AddPackageReferencePicksThePortableLibraryWithHigherVersionOfTheMatchingFrameworks(string contentFile, string otherContentFile) { - // Arrange + // Arrange var sourceRepository = new MockPackageRepository(); var projectSystem = new MockProjectSystem(new FrameworkName("Silverlight, Version=4.0")); var projectManager = new ProjectManager(sourceRepository, new DefaultPackagePathResolver(projectSystem), projectSystem, new MockPackageRepository()); diff --git a/test/Core.Test/VersionUtilityTest.cs b/test/Core.Test/VersionUtilityTest.cs index f4ff0c421..7c682590c 100644 --- a/test/Core.Test/VersionUtilityTest.cs +++ b/test/Core.Test/VersionUtilityTest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Runtime.Versioning; using Xunit; @@ -9,6 +10,44 @@ namespace NuGet.Test { public class VersionUtilityTest { + /// + /// This is not meant to be an exhaustive list. Instead it should be a simple test to + /// verify that NuGet 3.x code is being called. + /// + [Theory] + [InlineData("net45", "netstandard1.1", true)] + [InlineData("net45", "netstandard1.2", false)] + [InlineData("net463", "netstandard1.6", true)] + [InlineData("net463", "netstandard1.7", false)] + [InlineData("netstandardapp1.6", "netstandard1.6", true)] + [InlineData("netstandardapp1.6", "netstandard1.7", false)] + [InlineData("netcoreapp1.0", "netstandard1.6", true)] + [InlineData("netcoreapp1.0", "netstandard1.7", false)] + [InlineData("xamarinmac", "netstandard1.6", true)] + [InlineData("xamarinmac", "netstandard1.7", false)] + [InlineData("portable-net45+win8", "netstandard1.1", true)] + [InlineData("portable-net45+win8", "netstandard1.2", false)] + [InlineData("portable-net451+win81", "netstandard1.2", true)] + [InlineData("portable-net451+win81", "netstandard1.3", false)] + public void SupportsFrameworksFromNuGet3(string projectFolder, string packageFolder, bool expected) + { + // Arrange + var project = VersionUtility.ParseFrameworkName(projectFolder); + var package = VersionUtility.ParseFrameworkName(packageFolder); + + // Act + var actual = VersionUtility.IsCompatible(project, package); + + // Assert + Assert.True( + expected == actual, + string.Format( + "{0} should {1}support {2}.", + projectFolder, + expected ? string.Empty : "not ", + packageFolder)); + } + [Fact] public void ParseUAPFrameworkShortName() { @@ -799,16 +838,6 @@ public void ParseVersionSpecRangeInclusiveInclusiveExtraSpaces() Assert.True(versionInfo.IsMaxInclusive); } - [Fact] - public void NormalizeVersionFillsInZerosForUnsetVersionParts() - { - // Act - Version version = VersionUtility.NormalizeVersion(new Version("1.5")); - - // Assert - Assert.Equal(new Version("1.5.0.0"), version); - } - [Fact] public void ParseVersionSpecRangeIntegerRanges() { @@ -1110,6 +1139,9 @@ public void WindowsPhoneApolloIdentifierCompatibleWithAllWPProjects(string apoll [InlineData("windows8")] [InlineData("win")] [InlineData("win8")] + [InlineData("windows45")] + [InlineData("windows1")] + [InlineData("windows10")] // Parses to Windows v1.0 public void WindowsIdentifierCompatibleWithWindowsStoreAppProjects(string identifier) { // Arrange @@ -1124,10 +1156,8 @@ public void WindowsIdentifierCompatibleWithWindowsStoreAppProjects(string identi [Theory] [InlineData("windows9")] [InlineData("win9")] - [InlineData("win10")] + [InlineData("win10.0")] [InlineData("windows81")] - [InlineData("windows45")] - [InlineData("windows1")] public void WindowsIdentifierWithUnsupportedVersionNotCompatibleWithWindowsStoreAppProjects(string identifier) { // Arrange @@ -1336,12 +1366,13 @@ public void ParsePortableFrameworkNameThrowsIfProfileContainsPortableFramework() public void TestGetShortNameForPortableFramework() { // Arrange - NetPortableProfileTable.SetProfileCollection(BuildProfileCollection()); + var profileCollection = BuildProfileCollection(); + var table = new NetPortableProfileTable(profileCollection); var framework = new FrameworkName(".NETPortable, Version=4.0, Profile=Profile1"); // Act-1 - string shortName = VersionUtility.GetShortFrameworkName(framework); + string shortName = VersionUtility.GetShortFrameworkName(table, framework); // Assert-2 Assert.Equal("portable-net45+sl40+wp71", shortName); @@ -1350,7 +1381,7 @@ public void TestGetShortNameForPortableFramework() var framework2 = new FrameworkName(".NETPortable, Version=4.0, Profile=Profile2"); // Act-2 - string shortName2 = VersionUtility.GetShortFrameworkName(framework2); + string shortName2 = VersionUtility.GetShortFrameworkName(table, framework2); // Assert-2 Assert.Equal("portable-win+sl30+wp71", shortName2); @@ -1359,7 +1390,7 @@ public void TestGetShortNameForPortableFramework() var framework3 = new FrameworkName(".NETPortable, Version=4.0, Profile=Profile4"); // Act-3 - string shortName3 = VersionUtility.GetShortFrameworkName(framework3); + string shortName3 = VersionUtility.GetShortFrameworkName(table, framework3); // Assert-4 Assert.Equal("portable-sl20+wp", shortName3); @@ -1509,12 +1540,12 @@ public void TestGetShortNameForPortableXamarinFrameworks(string frameworkIdentif }); var profile4 = new NetPortableProfile( - "Profile4", - new[] { + "Profile4", + new[] { new FrameworkName(".NETFramework, Version=4.0"), new FrameworkName("Xamarin.iOS, Version=1.0"), new FrameworkName("Xamarin.WatchOS, Version=1.0"), - }); + }); var profile5 = new NetPortableProfile( "Profile5", @@ -1529,13 +1560,12 @@ public void TestGetShortNameForPortableXamarinFrameworks(string frameworkIdentif profileCollection.Add(profile3); profileCollection.Add(profile4); profileCollection.Add(profile5); - - NetPortableProfileTable.SetProfileCollection(profileCollection); - + + var table = new NetPortableProfileTable(profileCollection); var framework = new FrameworkName(frameworkIdentifier); // Act - string shortName = VersionUtility.GetShortFrameworkName(framework); + string shortName = VersionUtility.GetShortFrameworkName(table, framework); // Assert Assert.Equal(expectedShortName, shortName); @@ -1619,14 +1649,15 @@ public void IsCompatibleReturnsFalseForPortableFrameworkAndNormalFramework2() [InlineData("dnx451", "dnxcore50", false)] [InlineData("dnxcore50", "dnx451", false)] - // COMPATIBLE: dnx project, net package (any version) - // Don't get excited by version numbers here. I'm just randomly guessing higher version numbers :) + // COMPATIBLE: dnx project, earlier or equal net package [InlineData("dnx451", "net451", true)] [InlineData("dnx451", "net40", true)] [InlineData("dnx451", "net20", true)] - [InlineData("dnx451", "net50", true)] - [InlineData("dnx451", "net60", true)] - [InlineData("dnx451", "net70", true)] + + // NOT COMPATIBLE: dnx project, later net package + [InlineData("dnx451", "net50", false)] + [InlineData("dnx451", "net60", false)] + [InlineData("dnx451", "net70", false)] // NOT COMPATIBLE: Package targeting later framework [InlineData("dnx451", "dnx51", false)] @@ -1655,27 +1686,29 @@ public void IsCompatibleReturnsFalseForPortableFrameworkAndNormalFramework2() [InlineData("aspnet50", "aspnet50", true)] [InlineData("aspnetcore50", "aspnetcore50", true)] - // COMPATIBLE: Project targeting later framework + // COMPATIBLE: Project targeting earlier or same version [InlineData("aspnet51", "aspnet50", true)] - [InlineData("aspnet51", "net451", true)] [InlineData("aspnet51", "net40", true)] [InlineData("aspnet51", "net20", true)] [InlineData("aspnetcore51", "aspnetcore50", true)] + // NOT COMPATIBLE: Project targeting great version + [InlineData("aspnet51", "net46", false)] + [InlineData("aspnet51", "net451", false)] + // NOT COMPATIBLE: aspnet into aspnetcore and vice-versa [InlineData("aspnet50", "aspnetcore50", false)] [InlineData("aspnetcore50", "aspnet50", false)] // COMPATIBLE: aspnet project, net package (any version) - // Don't get excited by version numbers here. I'm just randomly guessing higher version numbers :) - [InlineData("aspnet50", "net451", true)] [InlineData("aspnet50", "net40", true)] [InlineData("aspnet50", "net20", true)] - [InlineData("aspnet50", "net50", true)] - [InlineData("aspnet50", "net60", true)] - [InlineData("aspnet50", "net70", true)] // NOT COMPATIBLE: Package targeting later framework + [InlineData("aspnet50", "net451", false)] + [InlineData("aspnet50", "net50", false)] + [InlineData("aspnet50", "net60", false)] + [InlineData("aspnet50", "net70", false)] [InlineData("aspnet50", "aspnet51", false)] [InlineData("aspnetcore50", "aspnetcore51", false)] @@ -1699,32 +1732,32 @@ public void IsCompatibleReturnsFalseForPortableFrameworkAndNormalFramework2() public void IsCompatibleMatrixForASPNetFrameworks(string projectFramework, string packageFramework, bool compatible) { Assert.Equal( + compatible, VersionUtility.IsCompatible( VersionUtility.ParseFrameworkName(projectFramework), - VersionUtility.ParseFrameworkName(packageFramework)), - compatible); + VersionUtility.ParseFrameworkName(packageFramework))); } [Theory] [InlineData("dnx451", "aspnet50", true)] [InlineData("dnxcore50", "aspnetcore50", true)] [InlineData("aspnet50", "dnx451", false)] - [InlineData("aspnetcore50", "dnxcore50", false)] + [InlineData("aspnetcore50", "dnxcore50", true)] [InlineData("dnx", "aspnet50", true)] [InlineData("dnxcore", "aspnetcore50", true)] [InlineData("aspnet", "dnx451", false)] - [InlineData("aspnetcore", "dnxcore50", false)] + [InlineData("aspnetcore", "dnxcore50", true)] [InlineData("dnx451", "aspnet", true)] [InlineData("dnxcore50", "aspnetcore", true)] - [InlineData("aspnet50", "dnx", false)] - [InlineData("aspnetcore50", "dnxcore", false)] + [InlineData("aspnet50", "dnx", true)] + [InlineData("aspnetcore50", "dnxcore", true)] public void IsCompatibleMatrixForDNXAspTempFrameworks(string projectFramework, string packageFramework, bool compatible) { Assert.Equal( + compatible, VersionUtility.IsCompatible( VersionUtility.ParseFrameworkName(projectFramework), - VersionUtility.ParseFrameworkName(packageFramework)), - compatible); + VersionUtility.ParseFrameworkName(packageFramework))); } [Theory] @@ -1768,6 +1801,8 @@ public void IsCompatibleReturnsFalseForNormalFrameworkAndPortableFramework(strin [InlineData("portable-wp8+wpa81", "portable-wpa81+wp81")] [InlineData("portable-wp81+wpa81", "portable-wpa81+wp81")] [InlineData("portable-wpa81+wp81", "portable-wpa81+wp81")] + [InlineData("portable-netcore45+sl4+wp", "portable-netcore4+sl4")] + [InlineData("portable-netcore45+sl4+wp+net", "portable-wp7+netcore4")] public void IsCompatibleReturnsTrueForPortableFrameworkAndPortableFramework(string packageFramework, string projectFramework) { // Arrange @@ -1782,9 +1817,7 @@ public void IsCompatibleReturnsTrueForPortableFrameworkAndPortableFramework(stri } [Theory] - [InlineData("portable-netcore45+sl4+wp", "portable-netcore4+sl4")] [InlineData("portable-netcore45+sl4+wp", "portable-netcore5+wp7+net")] - [InlineData("portable-netcore45+sl4+wp+net", "portable-wp7+netcore4")] [InlineData("portable-netcore45+sl4", "portable-net4+wp7")] [InlineData("portable-net40+win8+sl4+wp71", "portable-wpa81+wp81")] public void IsCompatibleReturnsFalseForPortableFrameworkAndPortableFramework(string packageFramework, string projectFramework) @@ -1800,167 +1833,6 @@ public void IsCompatibleReturnsFalseForPortableFrameworkAndPortableFramework(str Assert.False(isCompatible); } - [Theory] - [InlineData("portable-net45+sl5+wp71", "portable-net45+sl5+wp71", -3)] - [InlineData("portable-net45+sl5+wp71", "portable-net45+sl5+wp71+win8", -4)] - [InlineData("portable-net45+sl5+wp71", "portable-net45+sl4+wp71+win8", -54)] - [InlineData("portable-net45+sl5+wp71", "portable-net4+sl4+wp71+win8", -104)] - [InlineData("portable-net45+sl5+wp71", "portable-net4+sl4+wp7+win8", -154)] - [InlineData("portable-win8+wp8", "portable-win8+wp7", -52)] - [InlineData("portable-win8+wp8", "portable-win8+wp7+silverlight4", -53)] - public void TestGetCompatibilityBetweenPortableLibraryAndPortableLibrary(string frameworkName, string targetFrameworkName, int expectedScore) - { - // Arrange - var framework = VersionUtility.ParseFrameworkName(frameworkName); - var targetFramework = VersionUtility.ParseFrameworkName(targetFrameworkName); - - // Act - int score = VersionUtility.GetCompatibilityBetweenPortableLibraryAndPortableLibrary(framework, targetFramework); - - // Assert - Assert.Equal(expectedScore, score); - } - - /// - /// The following example is used in the comments provided in the product code too including how the computation takes place - /// Refer VersionUtility.GetCompatibilityBetweenPortableLibraryAndPortableLibrary for more details - /// For example, Let Project target net45+sl5+monotouch+monoandroid. And, Package has 4 profiles - /// A: net45+sl5, B: net40+sl5+monotouch, C: net40+sl4+monotouch+monoandroid, D: net40+sl4+monotouch+monoandroid+wp71 - /// - [Theory] - [InlineData("portable-net45+sl50+MonoTouch+MonoAndroid", "portable-net45+sl5", -502)] - [InlineData("portable-net45+sl50+MonoTouch+MonoAndroid", "portable-net40+sl5+MonoTouch", -303)] - [InlineData("portable-net45+sl50+MonoTouch+MonoAndroid", "portable-net40+sl4+MonoTouch+MonoAndroid", -104)] - [InlineData("portable-net45+sl50+MonoTouch+MonoAndroid", "portable-net40+sl4+MonoTouch+MonoAndroid+wp71", -105)] - public void TestGetCompatibilityBetweenPortableLibraryAndPortableLibraryWithOptionalFx(string frameworkName, string targetFrameworkName, int expectedScore) - { - var profile1 = new NetPortableProfile( - "Profile1", - new[] { - new FrameworkName(".NETFramework, Version=4.5"), - new FrameworkName("Silverlight, Version=5.0"), - }, - new[] { - new FrameworkName("MonoTouch, Version=0.0"), - new FrameworkName("MonoAndroid, Version=0.0"), - }); - - NetPortableProfileCollection profileCollection = new NetPortableProfileCollection(); - profileCollection.Add(profile1); - - NetPortableProfileTable.SetProfileCollection(profileCollection); - - // Arrange - var framework = VersionUtility.ParseFrameworkName(frameworkName); - var targetFramework = VersionUtility.ParseFrameworkName(targetFrameworkName); - - // Act - int score = VersionUtility.GetCompatibilityBetweenPortableLibraryAndPortableLibrary(framework, targetFramework); - - // Assert - Assert.Equal(expectedScore, score); - } - - /// - /// This test is used to ensure that when the packageTargetFrameworkProfile is already available in NetPortableProfileCollection - /// Still the - /// - [Theory] - [InlineData("portable-net40+sl40+MonoTouch+MonoAndroid", "portable-net40+sl40+MonoTouch+MonoAndroid", -4)] - [InlineData("portable-net45+MonoTouch+MonoAndroid", "portable-net40+sl40+MonoTouch+MonoAndroid", -54)] - public void TestGetCompatibilityBetweenPortableLibraryAndPortableLibraryWithPreLoadedPackageProfile(string frameworkName, string targetFrameworkName, int expectedScore) - { - var profile1 = new NetPortableProfile( - "Profile1", - new[] { - new FrameworkName(".NETFramework, Version=4.0"), - new FrameworkName("Silverlight, Version=4.0"), - }, - new[] { - new FrameworkName("MonoTouch, Version=0.0"), - new FrameworkName("MonoAndroid, Version=0.0"), - }); - - NetPortableProfileCollection profileCollection = new NetPortableProfileCollection(); - profileCollection.Add(profile1); - - NetPortableProfileTable.SetProfileCollection(profileCollection); - - // Arrange - var framework = VersionUtility.ParseFrameworkName(frameworkName); - var targetFramework = VersionUtility.ParseFrameworkName(targetFrameworkName); - - // Act - int score = VersionUtility.GetCompatibilityBetweenPortableLibraryAndPortableLibrary(framework, targetFramework); - - // Assert - Assert.Equal(expectedScore, score); - } - - /// - /// (a) First case is when projectFrameworkName is not compatible with packageTargetFrameworkName and returns long.MinValue - /// (b) Second case is where there is a framework in portable packageFramework compatible with the Mono projectFramework - /// (c) The last cases are when there is no framework in portable packageFrameowrk that is compatible with the Mono projectFramework - /// (i) Check if there is an *installed* portable profile which has the aforementioned project framework as an optional framework - /// (ii) And, check if the project framework version >= found optional framework and that the supported frameworks are compatible with the ones in packageTargetFramework - /// (iii) In the source code, this is the else part in method GetCompatibilityBetweenPortableLibraryAndNonPortableLibrary() - /// - [Theory] - [InlineData("MonoAndroid10", "portable-net45+sl5", long.MinValue)] - // 180388626433 below = (1L << 32 + 5) + 1 + (10 * (1L << 32)). And, this is the score accumulated - // across methods like CalculateVersionDistance and GetProfileCompatibility - [InlineData("MonoAndroid10", "portable-net40+sl4+wp71+win8+MonoAndroid10", (180388626433 - 5 * 2))] - [InlineData("MonoAndroid10", "portable-net40+sl4+wp71+win8", -4*2)] - [InlineData("MonoAndroid10", "portable-net45+wp8+win8", -3*2)] - [InlineData("MonoAndroid10", "portable-net40+sl4+wp71+win8+MonoTouch", -5*2)] - [InlineData("MonoAndroid20", "portable-net40+sl4+wp71+win8+MonoTouch", -5 * 2)] - [InlineData("MonoAndroid", "portable-net40+sl4+wp71+win8+MonoTouch", long.MinValue)] - public void TestGetCompatibilityBetweenPortableLibraryAndNonPortableLibraryForMono(string projectFrameworkName, string packageTargetFrameworkName, long expectedScore) - { - // Arrange - var profile1 = new NetPortableProfile( - "Profile1", - new[] { - new FrameworkName(".NETFramework, Version=4.5"), - new FrameworkName("Silverlight, Version=4.0"), - new FrameworkName("WindowsPhone, Version=7.1"), - new FrameworkName("Windows, Version=8.0"), - }, - new[] { - new FrameworkName("MonoTouch, Version=1.0"), - new FrameworkName("MonoAndroid, Version=1.0"), - new FrameworkName("MonoMac, Version=1.0"), - }); - - var profile2 = new NetPortableProfile( - "Profile2", - new[] { - new FrameworkName(".NETFramework, Version=4.5"), - new FrameworkName("WindowsPhone, Version=8.0"), - new FrameworkName("Windows, Version=8.0"), - }, - new[] { - new FrameworkName("MonoTouch, Version=1.0"), - new FrameworkName("MonoAndroid, Version=1.0"), - }); - - NetPortableProfileCollection profileCollection = new NetPortableProfileCollection(); - profileCollection.Add(profile1); - profileCollection.Add(profile2); - - NetPortableProfileTable.SetProfileCollection(profileCollection); - - // Arrange - var framework = VersionUtility.ParseFrameworkName(projectFrameworkName); - var targetFramework = VersionUtility.ParseFrameworkName(packageTargetFrameworkName); - - // Act - long score = VersionUtility.GetCompatibilityBetweenPortableLibraryAndNonPortableLibrary(framework, targetFramework); - - // Assert - Assert.Equal(expectedScore, score); - } - private NetPortableProfileCollection BuildProfileCollection() { var profileCollection = new NetPortableProfileCollection();