From b092f4c38809f08b007643f35f3a284e7864cf0d Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Tue, 25 May 2021 11:40:00 -0700 Subject: [PATCH 01/13] Add initial c++/cli support --- .../VSNominationUtilities.cs | 4 +- .../MSBuildStaticGraphRestore.cs | 3 +- .../NuGet.Build.Tasks/NuGet.targets | 1 + .../PublicAPI/net472/PublicAPI.Unshipped.txt | 1 + .../netcoreapp5.0/PublicAPI.Unshipped.txt | 1 + .../netstandard2.0/PublicAPI.Unshipped.txt | 1 + .../RestoreCommand/Utility/LockFileUtils.cs | 21 ++- .../Utility/MSBuildRestoreUtility.cs | 5 +- .../Utility/MSBuildProjectFrameworkUtility.cs | 35 ++++- .../ResolverUtility.cs | 1 + .../DualCompatibilityFramework.cs | 100 ++++++++++++ .../NuGet.Frameworks/FrameworkConstants.cs | 3 + .../PublicAPI/net40/PublicAPI.Unshipped.txt | 9 ++ .../PublicAPI/net472/PublicAPI.Unshipped.txt | 9 ++ .../netstandard2.0/PublicAPI.Unshipped.txt | 9 ++ .../Projects/ProjectBuildProperties.cs | 2 + .../PublicAPI.Unshipped.txt | 1 + .../JsonPackageSpecReader.cs | 24 ++- .../PackageSpecReferenceDependencyProvider.cs | 9 +- .../NuGet.ProjectModel/PackageSpecWriter.cs | 1 + .../VsSolutionRestoreServiceTests.cs | 2 + .../MsbuildRestoreTaskTests.cs | 144 ++++++++++++++++++ .../MSBuildProjectFrameworkUtilityTests.cs | 32 ++++ .../DualCompatibilityFrameworkTests.cs | 51 +++++++ .../SimpleTestPackageUtility.cs | 1 + 25 files changed, 457 insertions(+), 13 deletions(-) create mode 100644 src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs create mode 100644 test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs diff --git a/src/NuGet.Clients/NuGet.SolutionRestoreManager/VSNominationUtilities.cs b/src/NuGet.Clients/NuGet.SolutionRestoreManager/VSNominationUtilities.cs index 0830e209538..687dae43409 100644 --- a/src/NuGet.Clients/NuGet.SolutionRestoreManager/VSNominationUtilities.cs +++ b/src/NuGet.Clients/NuGet.SolutionRestoreManager/VSNominationUtilities.cs @@ -160,12 +160,14 @@ internal static NuGetFramework GetTargetFramework(IVsProjectProperties propertie var targetFrameworkMoniker = GetPropertyValueOrNull(properties, ProjectBuildProperties.TargetFrameworkMoniker); var targetPlatformMoniker = GetPropertyValueOrNull(properties, ProjectBuildProperties.TargetPlatformMoniker); var targetPlatformMinVersion = GetPropertyValueOrNull(properties, ProjectBuildProperties.TargetPlatformMinVersion); + var clrSupport = GetPropertyValueOrNull(properties, ProjectBuildProperties.CLRSupport); return MSBuildProjectFrameworkUtility.GetProjectFramework( projectFullPath, targetFrameworkMoniker, targetPlatformMoniker, - targetPlatformMinVersion); + targetPlatformMinVersion, + clrSupport); } internal static ProjectRestoreMetadataFrameworkInfo ToProjectRestoreMetadataFrameworkInfo( diff --git a/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs b/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs index 49faf863b8d..f27b05d5e6d 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs +++ b/src/NuGet.Core/NuGet.Build.Tasks.Console/MSBuildStaticGraphRestore.cs @@ -570,7 +570,8 @@ internal static List GetTargetFrameworkInfos(IReadOn projectFilePath: projectInnerNode.Value.FullPath, targetFrameworkMoniker: msBuildProjectInstance.GetProperty("TargetFrameworkMoniker"), targetPlatformMoniker: msBuildProjectInstance.GetProperty("TargetPlatformMoniker"), - targetPlatformMinVersion: msBuildProjectInstance.GetProperty("TargetPlatformMinVersion")); + targetPlatformMinVersion: msBuildProjectInstance.GetProperty("TargetPlatformMinVersion"), + clrSupport: msBuildProjectInstance.GetProperty("CLRSupport")); var targetFrameworkInformation = new TargetFrameworkInformation() { diff --git a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets index 57af6fc6cd3..ef85e22ab11 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets +++ b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets @@ -943,6 +943,7 @@ Copyright (c) .NET Foundation. All rights reserved. $(TargetPlatformIdentifier) $(TargetPlatformVersion) $(TargetPlatformMinVersion) + $(CLRSupport) $(RuntimeIdentifierGraphPath) diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt index c59e0655723..e1afc1cc156 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -1,2 +1,3 @@ NuGet.Commands.SignArgs.PackagePaths.get -> System.Collections.Generic.IReadOnlyList NuGet.Commands.SignArgs.PackagePaths.set -> void +static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetPlatformMoniker, string targetPlatformMinVersion, string clrSupport) -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/netcoreapp5.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/netcoreapp5.0/PublicAPI.Unshipped.txt index c59e0655723..e1afc1cc156 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/netcoreapp5.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/netcoreapp5.0/PublicAPI.Unshipped.txt @@ -1,2 +1,3 @@ NuGet.Commands.SignArgs.PackagePaths.get -> System.Collections.Generic.IReadOnlyList NuGet.Commands.SignArgs.PackagePaths.set -> void +static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetPlatformMoniker, string targetPlatformMinVersion, string clrSupport) -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.Commands/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Commands/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index c59e0655723..e1afc1cc156 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -1,2 +1,3 @@ NuGet.Commands.SignArgs.PackagePaths.get -> System.Collections.Generic.IReadOnlyList NuGet.Commands.SignArgs.PackagePaths.set -> void +static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetPlatformMoniker, string targetPlatformMinVersion, string clrSupport) -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs index c9f29f8a48c..119b4e40b47 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs @@ -147,6 +147,7 @@ internal static List> CreateOrderedCriteriaSets(RestoreT var orderedCriteriaSets = new List>(1); var assetTargetFallback = framework as AssetTargetFallbackFramework; + var multipleCompatibilityFramework = framework as DualCompatibilityFramework; if (assetTargetFallback != null) { @@ -156,6 +157,14 @@ internal static List> CreateOrderedCriteriaSets(RestoreT // Add all fallbacks in order. orderedCriteriaSets.AddRange(assetTargetFallback.Fallback.Select(e => CreateCriteria(targetGraph, e))); } + else if (multipleCompatibilityFramework != null) + { + // Add the root project framework first. + orderedCriteriaSets.Add(CreateCriteria(targetGraph, multipleCompatibilityFramework.RootFramework)); + + // Add all fallback. + orderedCriteriaSets.Add(CreateCriteria(targetGraph, multipleCompatibilityFramework.SecondaryFramework)); + } else { // Add the current framework. @@ -458,9 +467,17 @@ private static void AddDependencies(IEnumerable dependencies, { if (dependencies == null) { - // AssetFallbackFramework does not apply to dependencies. + // DualCompatibilityFramework & AssetFallbackFramework does not apply to dependencies. // Convert it to a fallback framework if needed. - var currentFramework = (framework as AssetTargetFallbackFramework)?.AsFallbackFramework() ?? framework; + NuGetFramework currentFramework = framework; + if (framework is AssetTargetFallbackFramework atf) + { + currentFramework = atf.AsFallbackFramework(); + } + else if (framework is DualCompatibilityFramework mcf) + { + currentFramework = mcf.AsFallbackFramework(); + } var dependencySet = nuspec .GetDependencyGroups() diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs index 77e41a3ec5c..e103dd54064 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs @@ -425,7 +425,7 @@ private static IEnumerable GetTargetFrameworkInforma var targetFrameworkMoniker = item.GetProperty("TargetFrameworkMoniker"); var targetPlatforMoniker = item.GetProperty("TargetPlatformMoniker"); var targetPlatformMinVersion = item.GetProperty("TargetPlatformMinVersion"); - + var clrSupport = item.GetProperty("CLRSupport"); var targetAlias = string.IsNullOrEmpty(frameworkString) ? string.Empty : frameworkString; if (uniqueIds.Contains(targetAlias)) { @@ -437,7 +437,8 @@ private static IEnumerable GetTargetFrameworkInforma projectFilePath: filePath, targetFrameworkMoniker: targetFrameworkMoniker, targetPlatformMoniker: targetPlatforMoniker, - targetPlatformMinVersion: targetPlatformMinVersion); + targetPlatformMinVersion: targetPlatformMinVersion, + clrSupport: clrSupport); var targetFrameworkInfo = new TargetFrameworkInformation() { diff --git a/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs b/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs index 8878aea1747..955105d9de1 100644 --- a/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs +++ b/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs @@ -58,6 +58,26 @@ public static NuGetFramework GetProjectFramework( targetPlatformIdentifier: null, targetPlatformVersion: null, targetPlatformMinVersion, + clrSupport: null, + isXnaWindowsPhoneProject: false, + isManagementPackProject: false); + } + + public static NuGetFramework GetProjectFramework( + string projectFilePath, + string targetFrameworkMoniker, + string targetPlatformMoniker, + string targetPlatformMinVersion, + string clrSupport) + { + return GetProjectFramework( + projectFilePath, + targetFrameworkMoniker, + targetPlatformMoniker, + targetPlatformIdentifier: null, + targetPlatformVersion: null, + targetPlatformMinVersion, + clrSupport, isXnaWindowsPhoneProject: false, isManagementPackProject: false); } @@ -89,6 +109,7 @@ public static IEnumerable GetProjectFrameworkStrings( targetPlatformIdentifier, targetPlatformVersion, targetPlatformMinVersion, + clrSupport: null, isXnaWindowsPhoneProject, isManagementPackProject); } @@ -102,6 +123,7 @@ internal static IEnumerable GetProjectFrameworks( string targetPlatformIdentifier, string targetPlatformVersion, string targetPlatformMinVersion, + string clrSupport, bool isXnaWindowsPhoneProject, bool isManagementPackProject) { @@ -132,6 +154,7 @@ internal static IEnumerable GetProjectFrameworks( targetPlatformIdentifier, targetPlatformVersion, targetPlatformMinVersion, + clrSupport, isXnaWindowsPhoneProject, isManagementPackProject).DotNetFrameworkName }; } @@ -143,15 +166,18 @@ internal static NuGetFramework GetProjectFramework( string targetPlatformIdentifier, string targetPlatformVersion, string targetPlatformMinVersion, + string clrSupport, bool isXnaWindowsPhoneProject, bool isManagementPackProject) { + bool isCppCli = clrSupport?.Equals("NetCore", StringComparison.OrdinalIgnoreCase) == true; // C++ check - if (projectFilePath?.EndsWith(".vcxproj", StringComparison.OrdinalIgnoreCase) == true) + if (projectFilePath?.EndsWith(".vcxproj", StringComparison.OrdinalIgnoreCase) == true + && !isCppCli) { // The C++ project does not have a TargetFrameworkMoniker property set. // We hard-code the return value to Native. - return NuGetFramework.Parse("Native, Version=0.0"); + return FrameworkConstants.CommonFrameworks.Native; } // The MP project does not have a TargetFrameworkMoniker property set. @@ -227,6 +253,11 @@ internal static NuGetFramework GetProjectFramework( } NuGetFramework framework = NuGetFramework.ParseComponents(currentFrameworkString, platformMoniker); + if (isCppCli) + { + return new DualCompatibilityFramework(framework, FrameworkConstants.CommonFrameworks.Native); + } + return framework; } diff --git a/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs b/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs index 9c3ce9814b5..6596a2297bf 100644 --- a/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs +++ b/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs @@ -173,6 +173,7 @@ public static async Task FindLibraryMatchAsync( } var key = new LockFileCacheKey(targetFramework, runtimeIdentifier); + // TODO NK - Not doing this for MCF *completely* breaks potential lock file use for this, maybe reconsidered? // This is only applicable when packages has to be resolved from packages.lock.json file if (lockFileLibraries.TryGetValue(key, out var libraries)) diff --git a/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs b/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs new file mode 100644 index 00000000000..52a90a532e8 --- /dev/null +++ b/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs @@ -0,0 +1,100 @@ +// 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 NuGet.Shared; + +namespace NuGet.Frameworks +{ + /// + /// Represents a framework that behaves as 2 potentially independent frameworks. + /// Ex. C++/CLI can support both .NET 5.0 and native. + /// This type is immutable. + /// + public class DualCompatibilityFramework : NuGetFramework + { + /// + /// The root framework. Any compatibility checks should be performed against this framework first. + /// + public NuGetFramework RootFramework { get; } + + /// + /// The secondary framework. If the root framework compatibility checks fail, then the compat checks should be performed against this framework next. + /// + public NuGetFramework SecondaryFramework { get; } + + private int? _hashCode; + + /// + /// Multiple compatbility + /// + /// Root framework. Never . + /// Secondary framework. Never . + /// if either or are . + public DualCompatibilityFramework(NuGetFramework framework, NuGetFramework secondaryFramework) + : base(ValidateFrameworkArgument(framework)) + { + if (secondaryFramework == null) + { + throw new ArgumentNullException(nameof(secondaryFramework)); + } + + SecondaryFramework = secondaryFramework; + RootFramework = framework; + } + + /// + /// Create a FallbackFramework from the current DualCompatibilityFramework. + /// + public FallbackFramework AsFallbackFramework() + { + return new FallbackFramework(RootFramework, new NuGetFramework[] { SecondaryFramework }); + } + + private static NuGetFramework ValidateFrameworkArgument(NuGetFramework framework) + { + if (framework == null) + { + throw new ArgumentNullException(nameof(framework)); + } + return framework; + } + + public override bool Equals(object obj) + { + return Equals(obj as DualCompatibilityFramework); + } + + public bool Equals(DualCompatibilityFramework other) + { + if (other == null) + { + return false; + } + + if (ReferenceEquals(this, other)) + { + return true; + } + + return Comparer.Equals(this, other) + && Comparer.Equals(SecondaryFramework, other.SecondaryFramework); + } + + public override int GetHashCode() + { + if (_hashCode == null) + { + var combiner = new HashCodeCombiner(); + // Ensure that this is different from AssetTargetFallback & FallbackFramework; + combiner.AddStringIgnoreCase("multipleCompat"); + combiner.AddObject(Comparer.GetHashCode(this)); + combiner.AddObject(Comparer.GetHashCode(SecondaryFramework)); + _hashCode = combiner.CombinedHash; + } + + return _hashCode.Value; + } + + } +} diff --git a/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs b/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs index 14a8f374ccc..f8800caea19 100644 --- a/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs +++ b/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs @@ -185,6 +185,9 @@ public static readonly NuGetFramework NetCoreApp31 // .NET 5.0 and later has NetCoreApp identifier public static readonly NuGetFramework Net50 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version5); + + public static readonly NuGetFramework Native = new NuGetFramework(FrameworkIdentifiers.Native, new Version(0, 0, 0, 0)); + } } } diff --git a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net40/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net40/PublicAPI.Unshipped.txt index e69de29bb2d..974ff837cc5 100644 --- a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net40/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net40/PublicAPI.Unshipped.txt @@ -0,0 +1,9 @@ +NuGet.Frameworks.DualCompatibilityFramework +NuGet.Frameworks.DualCompatibilityFramework.AsFallbackFramework() -> NuGet.Frameworks.FallbackFramework +NuGet.Frameworks.DualCompatibilityFramework.Equals(NuGet.Frameworks.DualCompatibilityFramework other) -> bool +NuGet.Frameworks.DualCompatibilityFramework.DualCompatibilityFramework(NuGet.Frameworks.NuGetFramework framework, NuGet.Frameworks.NuGetFramework secondaryFramework) -> void +NuGet.Frameworks.DualCompatibilityFramework.RootFramework.get -> NuGet.Frameworks.NuGetFramework +NuGet.Frameworks.DualCompatibilityFramework.SecondaryFramework.get -> NuGet.Frameworks.NuGetFramework +override NuGet.Frameworks.DualCompatibilityFramework.Equals(object obj) -> bool +override NuGet.Frameworks.DualCompatibilityFramework.GetHashCode() -> int +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Native -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net472/PublicAPI.Unshipped.txt index e69de29bb2d..974ff837cc5 100644 --- a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -0,0 +1,9 @@ +NuGet.Frameworks.DualCompatibilityFramework +NuGet.Frameworks.DualCompatibilityFramework.AsFallbackFramework() -> NuGet.Frameworks.FallbackFramework +NuGet.Frameworks.DualCompatibilityFramework.Equals(NuGet.Frameworks.DualCompatibilityFramework other) -> bool +NuGet.Frameworks.DualCompatibilityFramework.DualCompatibilityFramework(NuGet.Frameworks.NuGetFramework framework, NuGet.Frameworks.NuGetFramework secondaryFramework) -> void +NuGet.Frameworks.DualCompatibilityFramework.RootFramework.get -> NuGet.Frameworks.NuGetFramework +NuGet.Frameworks.DualCompatibilityFramework.SecondaryFramework.get -> NuGet.Frameworks.NuGetFramework +override NuGet.Frameworks.DualCompatibilityFramework.Equals(object obj) -> bool +override NuGet.Frameworks.DualCompatibilityFramework.GetHashCode() -> int +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Native -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index e69de29bb2d..974ff837cc5 100644 --- a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -0,0 +1,9 @@ +NuGet.Frameworks.DualCompatibilityFramework +NuGet.Frameworks.DualCompatibilityFramework.AsFallbackFramework() -> NuGet.Frameworks.FallbackFramework +NuGet.Frameworks.DualCompatibilityFramework.Equals(NuGet.Frameworks.DualCompatibilityFramework other) -> bool +NuGet.Frameworks.DualCompatibilityFramework.DualCompatibilityFramework(NuGet.Frameworks.NuGetFramework framework, NuGet.Frameworks.NuGetFramework secondaryFramework) -> void +NuGet.Frameworks.DualCompatibilityFramework.RootFramework.get -> NuGet.Frameworks.NuGetFramework +NuGet.Frameworks.DualCompatibilityFramework.SecondaryFramework.get -> NuGet.Frameworks.NuGetFramework +override NuGet.Frameworks.DualCompatibilityFramework.Equals(object obj) -> bool +override NuGet.Frameworks.DualCompatibilityFramework.GetHashCode() -> int +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Native -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.PackageManagement/Projects/ProjectBuildProperties.cs b/src/NuGet.Core/NuGet.PackageManagement/Projects/ProjectBuildProperties.cs index e67e8dc33f4..73e3873ff17 100644 --- a/src/NuGet.Core/NuGet.PackageManagement/Projects/ProjectBuildProperties.cs +++ b/src/NuGet.Core/NuGet.PackageManagement/Projects/ProjectBuildProperties.cs @@ -26,6 +26,7 @@ public static class ProjectBuildProperties public const string TargetPlatformIdentifier = nameof(TargetPlatformIdentifier); public const string TargetPlatformMoniker = nameof(TargetPlatformMoniker); public const string TargetPlatformMinVersion = nameof(TargetPlatformMinVersion); + public const string CLRSupport = nameof(CLRSupport); public const string TargetPlatformVersion = nameof(TargetPlatformVersion); public const string Version = nameof(Version); public const string RestorePackagesPath = nameof(RestorePackagesPath); @@ -50,5 +51,6 @@ public static class ProjectBuildProperties public const string Clear = nameof(Clear); public const string RuntimeIdentifierGraphPath = nameof(RuntimeIdentifierGraphPath); public const string ManagePackageVersionsCentrally = nameof(ManagePackageVersionsCentrally); + } } diff --git a/src/NuGet.Core/NuGet.PackageManagement/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.PackageManagement/PublicAPI.Unshipped.txt index e69de29bb2d..d85b08e34be 100644 --- a/src/NuGet.Core/NuGet.PackageManagement/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.PackageManagement/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +const NuGet.ProjectManagement.ProjectBuildProperties.CLRSupport = "CLRSupport" -> string diff --git a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs index a54c336364f..6b417ce4b91 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs @@ -1626,7 +1626,7 @@ private static void ReadTargetFrameworks(PackageSpec packageSpec, JsonTextReader NuGetFramework frameworkName = NuGetFramework.Parse((string)jsonReader.Value); var targetFrameworkInformation = new TargetFrameworkInformation(); - + NuGetFramework secondaryFramework = default; jsonReader.ReadObject(propertyName => { switch (propertyName) @@ -1635,6 +1635,14 @@ private static void ReadTargetFrameworks(PackageSpec packageSpec, JsonTextReader targetFrameworkInformation.AssetTargetFallback = ReadNextTokenAsBoolOrFalse(jsonReader, packageSpec.FilePath); break; + case "secondaryFramework": + var secondaryFrameworkString = jsonReader.ReadAsString(); + if (!string.IsNullOrEmpty(secondaryFrameworkString)) + { + secondaryFramework = NuGetFramework.Parse(secondaryFrameworkString); + } + break; + case "centralPackageVersions": ReadCentralPackageVersions( jsonReader, @@ -1698,11 +1706,11 @@ private static void ReadTargetFrameworks(PackageSpec packageSpec, JsonTextReader if (targetFrameworkInformation.AssetTargetFallback) { - updatedFramework = new AssetTargetFallbackFramework(frameworkName, imports); + updatedFramework = new AssetTargetFallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); } else { - updatedFramework = new FallbackFramework(frameworkName, imports); + updatedFramework = new FallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); } } @@ -1711,6 +1719,16 @@ private static void ReadTargetFrameworks(PackageSpec packageSpec, JsonTextReader packageSpec.TargetFrameworks.Add(targetFrameworkInformation); } + private static NuGetFramework GetDualCompatibilityFrameworkIfNeeded(NuGetFramework frameworkName, NuGetFramework secondaryFramework) + { + if (secondaryFramework != default) + { + return new DualCompatibilityFramework(frameworkName, secondaryFramework); + } + + return frameworkName; + } + private static bool ValidateDependencyTarget(LibraryDependencyTarget targetValue) { var isValid = false; diff --git a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs index f0d56e0540b..b4c657e038a 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs @@ -186,12 +186,17 @@ private static void AddLibraryProperties(Library library, PackageSpec packageSpe var targetFrameworkInfo = packageSpec.GetTargetFramework(targetFramework); // FrameworkReducer.GetNearest does not consider ATF since it is used for more than just compat - if (targetFrameworkInfo.FrameworkName == null && targetFramework is AssetTargetFallbackFramework) + if (targetFrameworkInfo.FrameworkName == null && targetFramework is AssetTargetFallbackFramework atfFramework) { - var atfFramework = targetFramework as AssetTargetFallbackFramework; targetFrameworkInfo = packageSpec.GetTargetFramework(atfFramework.AsFallbackFramework()); } + if (targetFrameworkInfo.FrameworkName == null && targetFramework is DualCompatibilityFramework mcfFramework) + { + targetFrameworkInfo = packageSpec.GetTargetFramework(mcfFramework.AsFallbackFramework()); + } + + library[KnownLibraryProperties.TargetFrameworkInformation] = targetFrameworkInfo; // Add framework assemblies diff --git a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs index 4210a0fe1f0..15fe9da5a5e 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs @@ -544,6 +544,7 @@ private static void SetFrameworks(IObjectWriter writer, IList string.IsNullOrEmpty(e.RuntimeIdentifier)); + targetsSection.Libraries.Should().Contain(e => e.Name.Equals("x"), because: string.Join(",", targetsSection.Libraries)); + // TODO NK - Check the correct assets are selected. + } + } + + [PlatformFact(Platform.Windows)] + public async Task MsbuildRestore_WithCPPCliVcxproj_WithNativeDependency_Succeeds() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set-up packages + var packageX = new SimpleTestPackageContext("x", "1.0.0"); + packageX.AddFile("build/native/x.targets"); + packageX.AddFile("lib/native/x.dll"); + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + packageX); + // Set up project + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + var framework = NuGetFramework.Parse("net5.0-windows7.0"); + var projectA = SimpleTestProjectContext.CreateNETCore("projectName", pathContext.SolutionRoot, framework); + projectA.Properties.Add("CLRSupport", "NetCore"); + //update path to vcxproj + projectA.ProjectPath = Path.Combine(Path.GetDirectoryName(projectA.ProjectPath), projectA.ProjectName + ".vcxproj"); + projectA.AddPackageToAllFrameworks(packageX); + solution.Projects.Add(projectA); + solution.Create(pathContext.SolutionRoot); + // Act + var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}", ignoreExitCode: true); + + // Assert + result.Success.Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.AssetsFileOutputPath).Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.TargetsOutput).Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.PropsOutput).Should().BeTrue(because: result.AllOutput); + + var targetsSection = projectA.AssetsFile.Targets.First(e => string.IsNullOrEmpty(e.RuntimeIdentifier)); + targetsSection.Libraries.Should().Contain(e => e.Name.Equals("x"), because: string.Join(",", targetsSection.Libraries)); + // TODO NK - Check the correct assets are selected. + } + } + + [PlatformFact(Platform.Windows)] + public async Task MsbuildRestore_WithCPPCliVcxproj_WithNativeAndManagedTransitiveDependency_Succeeds() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set-up packages + // Managed 1.0.0 -> Managed.Child 1.0.0 + // Native 1.0.0 -> Native.Child 1.0. + var packageNativeChild = new SimpleTestPackageContext("native.child", "1.0.0"); + packageNativeChild.AddFile("build/native/native.child.targets"); + packageNativeChild.AddFile("lib/native/native.child.dll"); + + var packageNative = new SimpleTestPackageContext("native", "1.0.0"); + packageNative.AddFile("build/native/native.targets"); + packageNative.AddFile("lib/native/native.dll"); + + packageNative.Dependencies.Add(packageNativeChild); + + var packageManagedChild = new SimpleTestPackageContext("managed.child", "1.0.0"); + packageManagedChild.AddFile("build/net5.0/managed.child.targets"); + packageManagedChild.AddFile("lib/net5.0/managed.child.dll"); + + var packageManaged = new SimpleTestPackageContext("managed", "1.0.0"); + packageManaged.AddFile("build/net5.0/managed.targets"); + packageManaged.AddFile("lib/net5.0/managed.dll"); + + packageManaged.Dependencies.Add(packageManagedChild); + + // TODO NK - Ensure the dependencies are using the dependency groups, cause this should be failing right now. + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + packageNative, + packageNativeChild, + packageManaged, + packageManagedChild); + + // Set up project + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + var framework = NuGetFramework.Parse("net5.0-windows7.0"); + var projectA = SimpleTestProjectContext.CreateNETCore("projectName", pathContext.SolutionRoot, framework); + projectA.Properties.Add("CLRSupport", "NetCore"); + //update path to vcxproj + projectA.ProjectPath = Path.Combine(Path.GetDirectoryName(projectA.ProjectPath), projectA.ProjectName + ".vcxproj"); + projectA.AddPackageToAllFrameworks(packageNative); + projectA.AddPackageToAllFrameworks(packageManaged); + solution.Projects.Add(projectA); + solution.Create(pathContext.SolutionRoot); + // Act + var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}", ignoreExitCode: true); + + // Assert + result.Success.Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.AssetsFileOutputPath).Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.TargetsOutput).Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.PropsOutput).Should().BeTrue(because: result.AllOutput); + + var targetsSection = projectA.AssetsFile.Targets.First(e => string.IsNullOrEmpty(e.RuntimeIdentifier)); + targetsSection.Libraries.Should().Contain(e => e.Name.Equals("native"), because: string.Join(",", targetsSection.Libraries)); + targetsSection.Libraries.Should().Contain(e => e.Name.Equals("native.child"), because: string.Join(",", targetsSection.Libraries)); + targetsSection.Libraries.Should().Contain(e => e.Name.Equals("managed"), because: string.Join(",", targetsSection.Libraries)); + targetsSection.Libraries.Should().Contain(e => e.Name.Equals("managed.child"), because: string.Join(",", targetsSection.Libraries)); + // TODO NK - Check the correct assets are selected. + } + } + + // TODO NK - Add tests to ensure ATF + Dual Compatibility are combinable! } } diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs index 64e46a36e83..a1450c14904 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs @@ -268,6 +268,38 @@ public void GetProjectFramework_WithInvalidInput_Throws( targetPlatformMinVersion)); } + [Theory] + [InlineData(@"C:\project.vcxproj", ".NETFramework,Version=v4.5", "Windows,Version=7.0", "", "", "native", null)] + [InlineData(@"C:\project.vcxproj", ".NETFramework,Version=v4.5", "", "", "false", "native", null)] + [InlineData(@"C:\project.vcxproj", ".NETFramework,Version=v4.5", "", "", "NetFx", "native", null)] + [InlineData(@"C:\project.vcxproj", ".NETCoreApp,Version=v5.0", "", "", "NetCore", "net5.0", "native")] + [InlineData(@"C:\project.csproj", ".NETCoreApp,Version=v5.0", "", "", "NetCore", "net5.0", null)] + [InlineData(@"C:\project.csproj", ".NETFramework,Version=v4.5", "", "", "NetFramework", "net45", null)] + public void GetProjectFramework_WithCLRSupport_VariousInputs( + string projectFilePath, + string targetFrameworkMoniker, + string targetPlatformMoniker, + string targetPlatformMinVersion, + string clrSupport, + string expectedPrimaryShortName, + string expectedSecondaryShortName) + { + var nugetFramework = MSBuildProjectFrameworkUtility.GetProjectFramework( + projectFilePath: projectFilePath, + targetFrameworkMoniker, + targetPlatformMoniker, + targetPlatformMinVersion, + clrSupport); + + Assert.Equal(expectedPrimaryShortName, nugetFramework.GetShortFolderName()); + if (expectedSecondaryShortName != null) + { + Assert.IsAssignableFrom(typeof(DualCompatibilityFramework), nugetFramework); + var extendedFramework = nugetFramework as DualCompatibilityFramework; + Assert.Equal(expectedPrimaryShortName, extendedFramework.RootFramework.GetShortFolderName()); + Assert.Equal(expectedSecondaryShortName, extendedFramework.SecondaryFramework.GetShortFolderName()); + } + } /// /// Test helper diff --git a/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs b/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs new file mode 100644 index 00000000000..4f9e98d62d2 --- /dev/null +++ b/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs @@ -0,0 +1,51 @@ +// 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 FluentAssertions; +using Xunit; + +namespace NuGet.Frameworks.Test +{ + // TODO NK - Add simple compatibility tests in restore. + // TODO NK - Add dependencies tests + public class DualCompatibilityFrameworkTests + { + [Fact] + public void Constructor_WithNullFramework_Throws() + { + Assert.Throws(() => new DualCompatibilityFramework(framework: null, secondaryFramework: NuGetFramework.AnyFramework)); + } + + [Fact] + public void Constructor_WithNullSecondary_Throws() + { + Assert.Throws(() => new DualCompatibilityFramework(framework: NuGetFramework.AnyFramework, secondaryFramework: null)); + } + + [Theory] + [InlineData("net5.0", "net5.0", true)] + [InlineData("net45", "net45", true)] + [InlineData("net5.0-windows10.0.6508.1", "net5.0-windows10.0.6508.1", true)] + [InlineData("net5.0-windows10.0.6508.1", "net5.0-windows10.0.6508.2", false)] + public void NuGetFrameworkEquals_WithDualCompatibilityFramework_Succeeds(string shortFrameworkName, string rootFrameworkName, bool equals) + { + var nugetFramework = NuGetFramework.Parse(shortFrameworkName); + var extendedFramework = new DualCompatibilityFramework(NuGetFramework.Parse(rootFrameworkName), secondaryFramework: NuGetFramework.Parse(rootFrameworkName)); + var comparer = new NuGetFrameworkFullComparer(); + comparer.Equals(nugetFramework, extendedFramework).Should().Be(equals); + nugetFramework.Equals(extendedFramework).Should().Be(equals); + } + + [Theory] + [InlineData("net5.0")] + [InlineData("net45")] + [InlineData("net5.0-windows10.0.6508.1")] + public void DualCompatibilityFrameworkEquals_WithNonDualCompatibilityFramework_Succeeds(string shortFrameworkName) + { + var nugetFramework = NuGetFramework.Parse(shortFrameworkName); + var assetTargetFallback = new DualCompatibilityFramework(nugetFramework, secondaryFramework: NuGetFramework.AnyFramework); + Assert.False(assetTargetFallback.Equals((object)nugetFramework)); + } + } +} diff --git a/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageUtility.cs b/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageUtility.cs index 307bfcfdf98..ad673bafd74 100644 --- a/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageUtility.cs +++ b/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageUtility.cs @@ -196,6 +196,7 @@ public static async Task CreatePackageAsync(Stream stream, SimpleTestPackageCont dependenciesNode.Add(groupNode); metadata.Add(dependenciesNode); + // TODO NK - There should be a way to foreach (var dependency in dependencies) { var node = new XElement(XName.Get("dependency")); From cccd323a67ba6dc3ed337ca3189288154aa19cb7 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Tue, 25 May 2021 17:50:33 -0700 Subject: [PATCH 02/13] Add more tests --- .../ResolverUtility.cs | 2 +- .../ProjectRestoreInfoBuilder.cs | 5 +- .../VsSolutionRestoreServiceTests.cs | 48 +++++++++- .../MsbuildRestoreTaskTests.cs | 76 ++++++++++++++-- .../DualCompatibilityFrameworkTests.cs | 2 - .../JsonPackageSpecReaderTests.cs | 12 +++ .../SimpleTestPackageContext.cs | 1 + .../SimpleTestPackageUtility.cs | 90 +++++++++++++------ 8 files changed, 195 insertions(+), 41 deletions(-) diff --git a/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs b/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs index 6596a2297bf..4d4033eb51c 100644 --- a/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs +++ b/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs @@ -173,7 +173,7 @@ public static async Task FindLibraryMatchAsync( } var key = new LockFileCacheKey(targetFramework, runtimeIdentifier); - // TODO NK - Not doing this for MCF *completely* breaks potential lock file use for this, maybe reconsidered? + // TODO NK - Not doing this for DualCompatibiltiyFramework *completely* breaks potential lock file use for this, maybe reconsidered? // This is only applicable when packages has to be resolved from packages.lock.json file if (lockFileLibraries.TryGetValue(key, out var libraries)) diff --git a/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/ProjectRestoreInfoBuilder.cs b/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/ProjectRestoreInfoBuilder.cs index 2b4b27c4bf5..1e1be4e21e5 100644 --- a/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/ProjectRestoreInfoBuilder.cs +++ b/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/ProjectRestoreInfoBuilder.cs @@ -195,7 +195,7 @@ private static VsTargetFrameworkInfo2 ToTargetFrameworkInfo2( originalTargetFramework: tfm.TargetAlias); } - public static IEnumerable GetTargetFrameworkProperties(NuGetFramework framework, string originalString = null) + public static IEnumerable GetTargetFrameworkProperties(NuGetFramework framework, string originalString = null, string clrSupport = null) { return new IVsProjectProperty[] { @@ -206,7 +206,8 @@ public static IEnumerable GetTargetFrameworkProperties(NuGet new VsProjectProperty(ProjectBuildProperties.TargetFrameworkProfile, framework.Profile), new VsProjectProperty(ProjectBuildProperties.TargetPlatformIdentifier, framework.Platform), new VsProjectProperty(ProjectBuildProperties.TargetPlatformVersion, framework.PlatformVersion.ToString()), - new VsProjectProperty(ProjectBuildProperties.TargetFramework, originalString ?? framework.GetShortFolderName()) + new VsProjectProperty(ProjectBuildProperties.TargetFramework, originalString ?? framework.GetShortFolderName()), + new VsProjectProperty(ProjectBuildProperties.CLRSupport, clrSupport ?? string.Empty) }; } diff --git a/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VsSolutionRestoreServiceTests.cs b/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VsSolutionRestoreServiceTests.cs index fadf1830735..9b2dedc87f1 100644 --- a/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VsSolutionRestoreServiceTests.cs +++ b/test/NuGet.Clients.Tests/NuGet.SolutionRestoreManager.Test/VsSolutionRestoreServiceTests.cs @@ -2144,7 +2144,53 @@ public void CreateMinimalDependencyGraphSpec_ComparedToTestUtility_AreEqual() Assert.Equal(prodJson, testJson); } - // TODO NK - Add test for CLRSupport reading in VS. + [Theory] + [InlineData("NetCore", true)] + [InlineData("NetFx", false)] + [InlineData("false", false)] + public void ToPackageSpec_TargetFrameworkWithCLRSupport_InterpretsFrameworkCorrect(string clrSupport, bool isDualCompatibilityFramework) + { + // Arrange + ProjectNames projectName = new ProjectNames(@"f:\project\project.vcxproj", "project", "project.csproj", "project", Guid.NewGuid().ToString()); + var emptyReferenceItems = Array.Empty(); + var packageReferenceProperties = new VsReferenceProperties(); + var managedFramework = CommonFrameworks.Net50; + var nativeFramework = CommonFrameworks.Native; + var targetFrameworks = new VsTargetFrameworkInfo2[] + { + new VsTargetFrameworkInfo2( + targetFrameworkMoniker: "tfm1", + packageReferences: emptyReferenceItems, + projectReferences: emptyReferenceItems, + packageDownloads: emptyReferenceItems, + frameworkReferences: emptyReferenceItems, + projectProperties: ProjectRestoreInfoBuilder.GetTargetFrameworkProperties(managedFramework, "tfm1", clrSupport), + addTargetFrameworkProperties: false), + }; + var originalTargetFrameworksString = string.Join(";", targetFrameworks.Select(tf => tf.TargetFrameworkMoniker)); + + // Act + var result = VsSolutionRestoreService.ToPackageSpec(projectName, targetFrameworks, originalTargetFrameworksString, string.Empty); + + // Assert + Assert.Equal(1, result.TargetFrameworks.Count); + TargetFrameworkInformation targetFrameworkInfo = Assert.Single(result.TargetFrameworks, tf => tf.TargetAlias == "tfm1"); + + if (isDualCompatibilityFramework) + { + var comparer = new NuGetFrameworkFullComparer(); + comparer.Equals(targetFrameworkInfo.FrameworkName, managedFramework).Should().BeTrue(); + targetFrameworkInfo.FrameworkName.Should().BeOfType(); + var dualCompatibilityFramework = targetFrameworkInfo.FrameworkName as DualCompatibilityFramework; + dualCompatibilityFramework.RootFramework.Should().Be(managedFramework); + dualCompatibilityFramework.SecondaryFramework.Should().Be(CommonFrameworks.Native); + } + else + { + targetFrameworkInfo.FrameworkName.Should().NotBeOfType(); + targetFrameworkInfo.FrameworkName.Should().Be(nativeFramework); + } + } private delegate void TryGetProjectNamesCallback(string projectPath, out ProjectNames projectNames); private delegate bool TryGetProjectNamesReturns(string projectPath, out ProjectNames projectNames); diff --git a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs index 6b61727af8d..e18548e94cd 100644 --- a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs +++ b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs @@ -737,7 +737,8 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var targetsSection = projectA.AssetsFile.Targets.First(e => string.IsNullOrEmpty(e.RuntimeIdentifier)); targetsSection.Libraries.Should().Contain(e => e.Name.Equals("x"), because: string.Join(",", targetsSection.Libraries)); - // TODO NK - Check the correct assets are selected. + var lockFileTargetLibrary = targetsSection.Libraries.First(e => e.Name.Equals("x")); + lockFileTargetLibrary.CompileTimeAssemblies.Should().Contain("lib/net5.0/a.dll"); } } @@ -775,7 +776,9 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( var targetsSection = projectA.AssetsFile.Targets.First(e => string.IsNullOrEmpty(e.RuntimeIdentifier)); targetsSection.Libraries.Should().Contain(e => e.Name.Equals("x"), because: string.Join(",", targetsSection.Libraries)); - // TODO NK - Check the correct assets are selected. + var lockFileTargetLibrary = targetsSection.Libraries.First(e => e.Name.Equals("x")); + lockFileTargetLibrary.CompileTimeAssemblies.Should().Contain("lib/native/x.dll"); + lockFileTargetLibrary.Build.Should().Contain("build/native/x.targets"); } } @@ -796,7 +799,8 @@ public async Task MsbuildRestore_WithCPPCliVcxproj_WithNativeAndManagedTransitiv packageNative.AddFile("build/native/native.targets"); packageNative.AddFile("lib/native/native.dll"); - packageNative.Dependencies.Add(packageNativeChild); + + packageNative.PerFrameworkDependencies.Add(FrameworkConstants.CommonFrameworks.Native, new List { packageNativeChild }); var packageManagedChild = new SimpleTestPackageContext("managed.child", "1.0.0"); packageManagedChild.AddFile("build/net5.0/managed.child.targets"); @@ -806,9 +810,7 @@ public async Task MsbuildRestore_WithCPPCliVcxproj_WithNativeAndManagedTransitiv packageManaged.AddFile("build/net5.0/managed.targets"); packageManaged.AddFile("lib/net5.0/managed.dll"); - packageManaged.Dependencies.Add(packageManagedChild); - - // TODO NK - Ensure the dependencies are using the dependency groups, cause this should be failing right now. + packageManaged.PerFrameworkDependencies.Add(FrameworkConstants.CommonFrameworks.Net50, new List { packageManagedChild }); await SimpleTestPackageUtility.CreateFolderFeedV3Async( pathContext.PackageSource, @@ -842,10 +844,68 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( targetsSection.Libraries.Should().Contain(e => e.Name.Equals("native.child"), because: string.Join(",", targetsSection.Libraries)); targetsSection.Libraries.Should().Contain(e => e.Name.Equals("managed"), because: string.Join(",", targetsSection.Libraries)); targetsSection.Libraries.Should().Contain(e => e.Name.Equals("managed.child"), because: string.Join(",", targetsSection.Libraries)); - // TODO NK - Check the correct assets are selected. + + var nativeChild = targetsSection.Libraries.First(e => e.Name.Equals("native.child")); + nativeChild.CompileTimeAssemblies.Should().Contain("lib/native/native.child.dll"); + nativeChild.Build.Should().Contain("build/native/native.child.targets"); } } - // TODO NK - Add tests to ensure ATF + Dual Compatibility are combinable! + [PlatformFact(Platform.Windows)] + public async Task MsbuildRestore_WithCPPCliVcxproj_WithAssetTargetFallback_Succeeds() + { + // Arrange + using (var pathContext = new SimpleTestPathContext()) + { + // Set-up packages + var packageNative = new SimpleTestPackageContext("native", "1.0.0"); + packageNative.AddFile("build/native/native.targets"); + packageNative.AddFile("lib/native/native.dll"); + + var packageManaged = new SimpleTestPackageContext("managed", "1.0.0"); + packageManaged.AddFile("build/net472/managed.targets"); + packageManaged.AddFile("lib/net472.0/managed.dll"); + + packageManaged.PerFrameworkDependencies.Add(FrameworkConstants.CommonFrameworks.Net50, new List { packageManagedChild }); + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + packageNative, + packageManaged); + + // Set up project + var solution = new SimpleTestSolutionContext(pathContext.SolutionRoot); + var framework = NuGetFramework.Parse("net5.0-windows7.0"); + var projectA = SimpleTestProjectContext.CreateNETCore("projectName", pathContext.SolutionRoot, framework); + projectA.Properties.Add("CLRSupport", "NetCore"); + projectA.Properties.Add("AssetTargetFallback", "net472"); + //update path to vcxproj + projectA.ProjectPath = Path.Combine(Path.GetDirectoryName(projectA.ProjectPath), projectA.ProjectName + ".vcxproj"); + projectA.AddPackageToAllFrameworks(packageNative); + projectA.AddPackageToAllFrameworks(packageManaged); + solution.Projects.Add(projectA); + solution.Create(pathContext.SolutionRoot); + // Act + var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}", ignoreExitCode: true); + + // Assert + result.Success.Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.AssetsFileOutputPath).Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.TargetsOutput).Should().BeTrue(because: result.AllOutput); + File.Exists(projectA.PropsOutput).Should().BeTrue(because: result.AllOutput); + + var targetsSection = projectA.AssetsFile.Targets.First(e => string.IsNullOrEmpty(e.RuntimeIdentifier)); + targetsSection.Libraries.Should().Contain(e => e.Name.Equals("native"), because: string.Join(",", targetsSection.Libraries)); + targetsSection.Libraries.Should().Contain(e => e.Name.Equals("managed"), because: string.Join(",", targetsSection.Libraries)); + + var native = targetsSection.Libraries.First(e => e.Name.Equals("native")); + native.CompileTimeAssemblies.Should().Contain("lib/native/native.dll"); + native.Build.Should().Contain("build/native/native.targets"); + + var managed = targetsSection.Libraries.First(e => e.Name.Equals("managed")); + managed.CompileTimeAssemblies.Should().Contain("lib/net472/managed.dll"); + managed.Build.Should().Contain("build/net472/managed.targets"); + } + } } } diff --git a/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs b/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs index 4f9e98d62d2..c91cfcaf98a 100644 --- a/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs @@ -7,8 +7,6 @@ namespace NuGet.Frameworks.Test { - // TODO NK - Add simple compatibility tests in restore. - // TODO NK - Add dependencies tests public class DualCompatibilityFrameworkTests { [Fact] diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs index d7f2522b9db..70f568ff4f0 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs @@ -3573,6 +3573,18 @@ public void PackageSpecReader_Read() Assert.True(secondGroup.TransitiveDependencies.First().VersionCentrallyManaged); } + [Theory] + [InlineData(false)] + public void GetPackageSpec_WithSecondaryFrameworks_ReturnsTargetFrameworkInformationWithDualCompatibilityFramework(bool expectedValue) + { + // TODO NK - dual compatbility framework reader tests. + // TODO NK - Dual compatibility framework writer tests. + var json = $"{{\"frameworks\":{{\"a\":{{\"assetTargetFallback\":{expectedValue.ToString().ToLowerInvariant()}}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + Assert.Equal(true, framework.AssetTargetFallback); + } private static PackageSpec GetPackageSpec(string json) { diff --git a/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageContext.cs b/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageContext.cs index c395e734875..c918e510276 100644 --- a/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageContext.cs +++ b/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageContext.cs @@ -43,6 +43,7 @@ public SimpleTestPackageContext() public string Version { get; set; } = "1.0.0"; public string MinClientVersion { get; set; } public List Dependencies { get; set; } = new List(); + public Dictionary> PerFrameworkDependencies { get; set; } = new(); public Dictionary FrameworkReferences { get; set; } = new Dictionary(); public string Include { get; set; } = string.Empty; public string Exclude { get; set; } = string.Empty; diff --git a/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageUtility.cs b/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageUtility.cs index ad673bafd74..16c97044b71 100644 --- a/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageUtility.cs +++ b/test/TestUtilities/Test.Utility/SimpleTestSetup/SimpleTestPackageUtility.cs @@ -19,6 +19,7 @@ using NuGet.Packaging.Core; using NuGet.Packaging.PackageExtraction; using NuGet.Packaging.Signing; +using NuGet.Shared; using NuGet.Versioning; namespace NuGet.Test.Utility @@ -176,42 +177,39 @@ public static async Task CreatePackageAsync(Stream stream, SimpleTestPackageCont .Add(new XAttribute(XName.Get("minClientVersion"), packageContext.MinClientVersion)); } - var dependencies = packageContext.Dependencies.Select(e => - new PackageDependency( - e.Id, - VersionRange.Parse(e.Version), - string.IsNullOrEmpty(e.Include) - ? new List() - : e.Include.Split(',').ToList(), - string.IsNullOrEmpty(e.Exclude) - ? new List() - : e.Exclude.Split(',').ToList())); - - if (dependencies.Any()) + List<(string, List)> dependenciesPerFramework = GetPackageDependencies(packageContext); + + if (dependenciesPerFramework.Any()) { var metadata = xml.Element(XName.Get("package")).Element(XName.Get("metadata")); - var dependenciesNode = new XElement(XName.Get("dependencies")); - var groupNode = new XElement(XName.Get("group")); - dependenciesNode.Add(groupNode); - metadata.Add(dependenciesNode); - // TODO NK - There should be a way to - foreach (var dependency in dependencies) + foreach (var deps in dependenciesPerFramework) { - var node = new XElement(XName.Get("dependency")); - groupNode.Add(node); - node.Add(new XAttribute(XName.Get("id"), dependency.Id)); - node.Add(new XAttribute(XName.Get("version"), dependency.VersionRange.ToNormalizedString())); - - if (dependency.Include.Count > 0) + var groupNode = new XElement(XName.Get("group")); + if (!string.IsNullOrEmpty(deps.Item1)) { - node.Add(new XAttribute(XName.Get("include"), string.Join(",", dependency.Include))); + groupNode.SetAttributeValue("targetFramework", deps.Item1); } + dependenciesNode.Add(groupNode); + metadata.Add(dependenciesNode); - if (dependency.Exclude.Count > 0) + foreach (var dependency in deps.Item2) { - node.Add(new XAttribute(XName.Get("exclude"), string.Join(",", dependency.Exclude))); + var node = new XElement(XName.Get("dependency")); + groupNode.Add(node); + node.Add(new XAttribute(XName.Get("id"), dependency.Id)); + node.Add(new XAttribute(XName.Get("version"), dependency.VersionRange.ToNormalizedString())); + + if (dependency.Include.Count > 0) + { + node.Add(new XAttribute(XName.Get("include"), string.Join(",", dependency.Include))); + } + + if (dependency.Exclude.Count > 0) + { + node.Add(new XAttribute(XName.Get("exclude"), string.Join(",", dependency.Exclude))); + } } } } @@ -291,6 +289,44 @@ public static async Task CreatePackageAsync(Stream stream, SimpleTestPackageCont stream.Position = 0; } + private static List<(string, List)> GetPackageDependencies(SimpleTestPackageContext package) + { + if (package.PerFrameworkDependencies.Count > 0 && package.Dependencies.Count > 0) + { + throw new ArgumentException("A package context can't have dependencies with and without a group. Please use only one."); + } + var packageDependencies = new List<(string, List)>(); + + if (package.PerFrameworkDependencies.Count > 0) + { + foreach (var dependencies in package.PerFrameworkDependencies) + { + packageDependencies.Add((dependencies.Key.GetFrameworkString(), GetPackageDependencyList(dependencies.Value))); + } + } + + if (package.Dependencies.Count > 0) + { + packageDependencies.Add((string.Empty, GetPackageDependencyList(package.Dependencies))); + } + + return packageDependencies; + } + + private static List GetPackageDependencyList(List packages) + { + return packages.Select(e => + new PackageDependency( + e.Id, + VersionRange.Parse(e.Version), + string.IsNullOrEmpty(e.Include) + ? new List() + : e.Include.Split(',').ToList(), + string.IsNullOrEmpty(e.Exclude) + ? new List() + : e.Exclude.Split(',').ToList())).ToList(); + } + #if IS_SIGNING_SUPPORTED private static SignPackageRequest GetPrimarySignRequest(SimpleTestPackageContext packageContext) { From ca734cc14cbed100001b157b193c176799812456 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Wed, 26 May 2021 16:19:30 -0700 Subject: [PATCH 03/13] Clean-up and add more tests --- .../NuGet.Frameworks/FrameworkConstants.cs | 3 ++ .../NuGet.Frameworks/NuGetFrameworkFactory.cs | 13 +++++ .../PublicAPI/net40/PublicAPI.Unshipped.txt | 3 ++ .../PublicAPI/net472/PublicAPI.Unshipped.txt | 3 ++ .../netstandard2.0/PublicAPI.Unshipped.txt | 3 ++ .../JsonPackageSpecReader.cs | 4 ++ .../NuGet.ProjectModel/PackageSpecWriter.cs | 19 +++++++- .../MsbuildRestoreTaskTests.cs | 2 - .../JsonPackageSpecReaderTests.cs | 47 ++++++++++++++++--- .../PackageSpecWriterTests.cs | 27 +++++++++++ 10 files changed, 114 insertions(+), 10 deletions(-) diff --git a/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs b/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs index f8800caea19..53ccd6aeb03 100644 --- a/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs +++ b/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs @@ -88,6 +88,9 @@ public static class CommonFrameworks public static readonly NuGetFramework Net461 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 6, 1, 0)); public static readonly NuGetFramework Net462 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 6, 2, 0)); public static readonly NuGetFramework Net463 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 6, 3, 0)); + public static readonly NuGetFramework Net47 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 7, 0, 0)); + public static readonly NuGetFramework Net471 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 7, 1, 0)); + public static readonly NuGetFramework Net472 = new NuGetFramework(FrameworkIdentifiers.Net, new Version(4, 7, 2, 0)); public static readonly NuGetFramework NetCore45 = new NuGetFramework(FrameworkIdentifiers.NetCore, new Version(4, 5, 0, 0)); public static readonly NuGetFramework NetCore451 = new NuGetFramework(FrameworkIdentifiers.NetCore, new Version(4, 5, 1, 0)); diff --git a/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs b/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs index 5991f61b7b8..c264dcfa1b1 100644 --- a/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs +++ b/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs @@ -609,6 +609,15 @@ private static bool TryParseCommonFramework(string frameworkString, out NuGetFra case "net462": framework = FrameworkConstants.CommonFrameworks.Net462; break; + case "net47": + framework = FrameworkConstants.CommonFrameworks.Net47; + break; + case "net471": + framework = FrameworkConstants.CommonFrameworks.Net471; + break; + case "net472": + framework = FrameworkConstants.CommonFrameworks.Net472; + break; case "win8": framework = FrameworkConstants.CommonFrameworks.Win8; break; @@ -662,6 +671,10 @@ private static bool TryParseCommonFramework(string frameworkString, out NuGetFra case "netcoreapp21": framework = FrameworkConstants.CommonFrameworks.NetCoreApp21; break; + case "netcoreapp3.0": + case "netcoreapp30": + framework = FrameworkConstants.CommonFrameworks.NetCoreApp30; + break; case "netcoreapp3.1": case "netcoreapp31": framework = FrameworkConstants.CommonFrameworks.NetCoreApp31; diff --git a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net40/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net40/PublicAPI.Unshipped.txt index 974ff837cc5..63d638a0b8f 100644 --- a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net40/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net40/PublicAPI.Unshipped.txt @@ -7,3 +7,6 @@ NuGet.Frameworks.DualCompatibilityFramework.SecondaryFramework.get -> NuGet.Fram override NuGet.Frameworks.DualCompatibilityFramework.Equals(object obj) -> bool override NuGet.Frameworks.DualCompatibilityFramework.GetHashCode() -> int static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Native -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net47 -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net471 -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net472 -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net472/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net472/PublicAPI.Unshipped.txt index 974ff837cc5..63d638a0b8f 100644 --- a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -7,3 +7,6 @@ NuGet.Frameworks.DualCompatibilityFramework.SecondaryFramework.get -> NuGet.Fram override NuGet.Frameworks.DualCompatibilityFramework.Equals(object obj) -> bool override NuGet.Frameworks.DualCompatibilityFramework.GetHashCode() -> int static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Native -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net47 -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net471 -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net472 -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt index 974ff837cc5..63d638a0b8f 100644 --- a/src/NuGet.Core/NuGet.Frameworks/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Frameworks/PublicAPI/netstandard2.0/PublicAPI.Unshipped.txt @@ -7,3 +7,6 @@ NuGet.Frameworks.DualCompatibilityFramework.SecondaryFramework.get -> NuGet.Fram override NuGet.Frameworks.DualCompatibilityFramework.Equals(object obj) -> bool override NuGet.Frameworks.DualCompatibilityFramework.GetHashCode() -> int static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Native -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net47 -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net471 -> NuGet.Frameworks.NuGetFramework +static readonly NuGet.Frameworks.FrameworkConstants.CommonFrameworks.Net472 -> NuGet.Frameworks.NuGetFramework diff --git a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs index 6b417ce4b91..cbf03f61cac 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/JsonPackageSpecReader.cs @@ -1713,6 +1713,10 @@ private static void ReadTargetFrameworks(PackageSpec packageSpec, JsonTextReader updatedFramework = new FallbackFramework(GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework), imports); } } + else + { + updatedFramework = GetDualCompatibilityFrameworkIfNeeded(frameworkName, secondaryFramework); + } targetFrameworkInformation.FrameworkName = updatedFramework; diff --git a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs index 15fe9da5a5e..b1fe56583dc 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecWriter.cs @@ -544,7 +544,8 @@ private static void SetFrameworks(IObjectWriter writer, IList frameworkReferences) { if (frameworkReferences?.Any() == true) diff --git a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs index e18548e94cd..0c63efa76e5 100644 --- a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs +++ b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs @@ -866,8 +866,6 @@ public async Task MsbuildRestore_WithCPPCliVcxproj_WithAssetTargetFallback_Succe packageManaged.AddFile("build/net472/managed.targets"); packageManaged.AddFile("lib/net472.0/managed.dll"); - packageManaged.PerFrameworkDependencies.Add(FrameworkConstants.CommonFrameworks.Net50, new List { packageManagedChild }); - await SimpleTestPackageUtility.CreateFolderFeedV3Async( pathContext.PackageSource, packageNative, diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs index 70f568ff4f0..8232fde8efc 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/JsonPackageSpecReaderTests.cs @@ -6,6 +6,7 @@ using System.IO; using System.Linq; using System.Text; +using FluentAssertions; using Newtonsoft.Json; using NuGet.Common; using NuGet.Configuration; @@ -1587,6 +1588,21 @@ public void GetPackageSpec_WhenFrameworksAssetTargetFallbackValueIsValid_Returns Assert.Equal(expectedValue, framework.AssetTargetFallback); } + [Fact] + public void GetPackageSpec_WithAssetTargetFallbackAndImportsValues_ReturnsValidAssetTargetFallbackFramework() + { + var json = $"{{\"frameworks\":{{\"net5.0\":{{\"assetTargetFallback\": true, \"imports\": [\"net472\", \"net471\"]}}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + + framework.AssetTargetFallback.Should().BeTrue(); + var assetTargetFallback = framework.FrameworkName as AssetTargetFallbackFramework; + assetTargetFallback.RootFramework.Should().Be(FrameworkConstants.CommonFrameworks.Net50); + assetTargetFallback.Fallback.Should().HaveCount(2); + assetTargetFallback.Fallback.First().Should().Be(FrameworkConstants.CommonFrameworks.Net472); + assetTargetFallback.Fallback.Last().Should().Be(FrameworkConstants.CommonFrameworks.Net471); + } + [Fact] public void GetPackageSpec_WhenFrameworksCentralPackageVersionsPropertyIsAbsent_ReturnsEmptyCentralPackageVersions() { @@ -3573,17 +3589,34 @@ public void PackageSpecReader_Read() Assert.True(secondGroup.TransitiveDependencies.First().VersionCentrallyManaged); } - [Theory] - [InlineData(false)] - public void GetPackageSpec_WithSecondaryFrameworks_ReturnsTargetFrameworkInformationWithDualCompatibilityFramework(bool expectedValue) + [Fact] + public void GetPackageSpec_WithSecondaryFrameworks_ReturnsTargetFrameworkInformationWithDualCompatibilityFramework() { - // TODO NK - dual compatbility framework reader tests. - // TODO NK - Dual compatibility framework writer tests. - var json = $"{{\"frameworks\":{{\"a\":{{\"assetTargetFallback\":{expectedValue.ToString().ToLowerInvariant()}}}}}}}"; + var json = $"{{\"frameworks\":{{\"net5.0\":{{\"secondaryFramework\": \"native\"}}}}}}"; TargetFrameworkInformation framework = GetFramework(json); + framework.FrameworkName.Should().BeOfType(); + var dualCompatibilityFramework = framework.FrameworkName as DualCompatibilityFramework; + dualCompatibilityFramework.RootFramework.Should().Be(FrameworkConstants.CommonFrameworks.Net50); + dualCompatibilityFramework.SecondaryFramework.Should().Be(FrameworkConstants.CommonFrameworks.Native); + } - Assert.Equal(true, framework.AssetTargetFallback); + [Fact] + public void GetPackageSpec_WithAssetTargetFallbackAndWithSecondaryFrameworks_ReturnsTargetFrameworkInformationWithDualCompatibilityFramework() + { + var json = $"{{\"frameworks\":{{\"net5.0\":{{\"assetTargetFallback\": true, \"imports\": [\"net472\", \"net471\"], \"secondaryFramework\": \"native\" }}}}}}"; + + TargetFrameworkInformation framework = GetFramework(json); + framework.FrameworkName.Should().BeOfType(); + framework.AssetTargetFallback.Should().BeTrue(); + var assetTargetFallbackFramework = framework.FrameworkName as AssetTargetFallbackFramework; + assetTargetFallbackFramework.RootFramework.Should().BeOfType(); + var dualCompatibilityFramework = assetTargetFallbackFramework.RootFramework as DualCompatibilityFramework; + dualCompatibilityFramework.RootFramework.Should().Be(FrameworkConstants.CommonFrameworks.Net50); + dualCompatibilityFramework.SecondaryFramework.Should().Be(FrameworkConstants.CommonFrameworks.Native); + assetTargetFallbackFramework.Fallback.Should().HaveCount(2); + assetTargetFallbackFramework.Fallback.First().Should().Be(FrameworkConstants.CommonFrameworks.Net472); + assetTargetFallbackFramework.Fallback.Last().Should().Be(FrameworkConstants.CommonFrameworks.Net471); } private static PackageSpec GetPackageSpec(string json) diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/PackageSpecWriterTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/PackageSpecWriterTests.cs index a176caf6ea2..462f612eca3 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/PackageSpecWriterTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/PackageSpecWriterTests.cs @@ -662,6 +662,33 @@ public void RoundTripTargetFrameworkAliases() VerifyJsonPackageSpecRoundTrip(json); } + [Fact] + public void RoundTripDualCompatibilityFramework() + { + // Arrange + var json = @"{ + ""frameworks"": { + ""net5.0"": { + ""dependencies"": { + ""a"": { + ""version"": ""[1.0.0, )"", + ""autoReferenced"": true + } + }, + ""imports"": [ + ""net472"", + ""net471"" + ], + ""assetTargetFallback"" : true, + ""secondaryFramework"" : ""native"" + } + } + }"; + + // Act & Assert + VerifyJsonPackageSpecRoundTrip(json); + } + private static string GetJsonString(PackageSpec packageSpec) { JObject jObject = packageSpec.ToJObject(); From cafa4e3655844f85b8a1e9e7a80982d2df271d93 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Wed, 26 May 2021 16:26:40 -0700 Subject: [PATCH 04/13] More and more clean-up --- .../MsbuildIntegrationTestFixture.cs | 3 ++- .../MsbuildRestoreTaskTests.cs | 15 +++++++++------ .../PackageSpecWriterTests.cs | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildIntegrationTestFixture.cs b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildIntegrationTestFixture.cs index 99246ba8b30..f3a469cdab9 100644 --- a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildIntegrationTestFixture.cs +++ b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildIntegrationTestFixture.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.IO; +using FluentAssertions; using NuGet.Common; using NuGet.Test.Utility; using Xunit; @@ -112,7 +113,7 @@ internal CommandRunnerResult RunMsBuild(string workingDirectory, string args, bo if (!ignoreExitCode) { - Assert.True(result.ExitCode == 0, $"msbuild.exe {args} command failed with following log information :\n {result.AllOutput}"); + result.ExitCode.Should().Be(0, because: $"msbuild.exe {args} command failed with following log information :\n {result.AllOutput}"); } return result; diff --git a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs index 0c63efa76e5..d7be8482276 100644 --- a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs +++ b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs @@ -704,8 +704,10 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( } } - [PlatformFact(Platform.Windows)] - public async Task MsbuildRestore_WithCPPCliVcxproj_RestoresSuccessfullyWithPackageReference() + [PlatformTheory(Platform.Windows)] + [InlineData(true)] + [InlineData(false)] + public async Task MsbuildRestore_WithCPPCliVcxproj_RestoresSuccessfullyWithPackageReference(bool isStaticGraphRestore) { // Arrange using (var pathContext = new SimpleTestPathContext()) @@ -727,7 +729,8 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( solution.Projects.Add(projectA); solution.Create(pathContext.SolutionRoot); // Act - var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}", ignoreExitCode: true); + var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, + $"/t:restore {pathContext.SolutionRoot}" + (isStaticGraphRestore ? " /p:RestoreUseStaticGraphEvaluation=true" : string.Empty)); // Assert result.Success.Should().BeTrue(because: result.AllOutput); @@ -766,7 +769,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( solution.Projects.Add(projectA); solution.Create(pathContext.SolutionRoot); // Act - var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}", ignoreExitCode: true); + var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}"); // Assert result.Success.Should().BeTrue(because: result.AllOutput); @@ -831,7 +834,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( solution.Projects.Add(projectA); solution.Create(pathContext.SolutionRoot); // Act - var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}", ignoreExitCode: true); + var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}"); // Assert result.Success.Should().BeTrue(because: result.AllOutput); @@ -884,7 +887,7 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( solution.Projects.Add(projectA); solution.Create(pathContext.SolutionRoot); // Act - var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}", ignoreExitCode: true); + var result = _msbuildFixture.RunMsBuild(pathContext.WorkingDirectory, $"/t:restore {pathContext.SolutionRoot}"); // Assert result.Success.Should().BeTrue(because: result.AllOutput); diff --git a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/PackageSpecWriterTests.cs b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/PackageSpecWriterTests.cs index 462f612eca3..d85cc9a911a 100644 --- a/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/PackageSpecWriterTests.cs +++ b/test/NuGet.Core.Tests/NuGet.ProjectModel.Test/PackageSpecWriterTests.cs @@ -663,7 +663,7 @@ public void RoundTripTargetFrameworkAliases() } [Fact] - public void RoundTripDualCompatibilityFramework() + public void Write_WithAssetTargetFallbackAndDualCompatibilityFramework_RoundTrips() { // Arrange var json = @"{ From 4858ff504d8b162c76f73c59ac6b11e921b1d7e8 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Wed, 26 May 2021 16:41:23 -0700 Subject: [PATCH 05/13] more clean-up. --- .../RestoreCommand/Utility/LockFileUtils.cs | 11 +---------- src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs | 1 - .../PackageSpecReferenceDependencyProvider.cs | 2 +- 3 files changed, 2 insertions(+), 12 deletions(-) diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs index 119b4e40b47..4379da63fdd 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs @@ -147,8 +147,7 @@ internal static List> CreateOrderedCriteriaSets(RestoreT var orderedCriteriaSets = new List>(1); var assetTargetFallback = framework as AssetTargetFallbackFramework; - var multipleCompatibilityFramework = framework as DualCompatibilityFramework; - + // TODO NK - Is dual compatibility framework accounted for. if (assetTargetFallback != null) { // Add the root project framework first. @@ -157,14 +156,6 @@ internal static List> CreateOrderedCriteriaSets(RestoreT // Add all fallbacks in order. orderedCriteriaSets.AddRange(assetTargetFallback.Fallback.Select(e => CreateCriteria(targetGraph, e))); } - else if (multipleCompatibilityFramework != null) - { - // Add the root project framework first. - orderedCriteriaSets.Add(CreateCriteria(targetGraph, multipleCompatibilityFramework.RootFramework)); - - // Add all fallback. - orderedCriteriaSets.Add(CreateCriteria(targetGraph, multipleCompatibilityFramework.SecondaryFramework)); - } else { // Add the current framework. diff --git a/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs b/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs index 53ccd6aeb03..1883fff27f2 100644 --- a/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs +++ b/src/NuGet.Core/NuGet.Frameworks/FrameworkConstants.cs @@ -190,7 +190,6 @@ public static readonly NuGetFramework NetCoreApp31 public static readonly NuGetFramework Net50 = new NuGetFramework(FrameworkIdentifiers.NetCoreApp, Version5); public static readonly NuGetFramework Native = new NuGetFramework(FrameworkIdentifiers.Native, new Version(0, 0, 0, 0)); - } } } diff --git a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs index b4c657e038a..863d4b2f5a8 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs @@ -195,7 +195,7 @@ private static void AddLibraryProperties(Library library, PackageSpec packageSpe { targetFrameworkInfo = packageSpec.GetTargetFramework(mcfFramework.AsFallbackFramework()); } - + // TODO NK - Fix it? library[KnownLibraryProperties.TargetFrameworkInformation] = targetFrameworkInfo; From c0f7f15037750962ec0cbecc9170cd8443ed727e Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Wed, 26 May 2021 17:02:21 -0700 Subject: [PATCH 06/13] w --- src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs b/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs index 4d4033eb51c..9c3ce9814b5 100644 --- a/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs +++ b/src/NuGet.Core/NuGet.DependencyResolver.Core/ResolverUtility.cs @@ -173,7 +173,6 @@ public static async Task FindLibraryMatchAsync( } var key = new LockFileCacheKey(targetFramework, runtimeIdentifier); - // TODO NK - Not doing this for DualCompatibiltiyFramework *completely* breaks potential lock file use for this, maybe reconsidered? // This is only applicable when packages has to be resolved from packages.lock.json file if (lockFileLibraries.TryGetValue(key, out var libraries)) From 63aba617b74c4cb8d64f9b183f1211bb791e38c6 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Wed, 26 May 2021 18:51:34 -0700 Subject: [PATCH 07/13] work --- .../NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs index 4379da63fdd..e899a6b17f8 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs @@ -148,6 +148,7 @@ internal static List> CreateOrderedCriteriaSets(RestoreT var assetTargetFallback = framework as AssetTargetFallbackFramework; // TODO NK - Is dual compatibility framework accounted for. + // Account for dual compatibility framework here and not in the resolver. if (assetTargetFallback != null) { // Add the root project framework first. From 9d1dd87806c0ff05d3678516f2f497fabc2cd209 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Thu, 27 May 2021 16:17:36 -0700 Subject: [PATCH 08/13] Fix the world --- .../SourceRepositoryDependencyProvider.cs | 5 + .../RestoreCommand/Utility/LockFileUtils.cs | 15 +- .../PackageSpecReferenceDependencyProvider.cs | 1 - .../RestoreCommandTests.cs | 168 ++++++++++++++++++ 4 files changed, 185 insertions(+), 4 deletions(-) diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs index de2f50be47c..28ecc6377b2 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs @@ -465,6 +465,11 @@ private IEnumerable GetDependencies( targetFramework, item => item.TargetFramework); + if(dependencyGroup == null && targetFramework is DualCompatibilityFramework dualCompatibilityFramework) + { + dependencyGroup = NuGetFrameworkUtility.GetNearest(packageInfo.DependencyGroups, dualCompatibilityFramework.SecondaryFramework, item => item.TargetFramework); + } + if (dependencyGroup != null) { return dependencyGroup.Packages.Select(PackagingUtility.GetLibraryDependencyFromNuspec).ToArray(); diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs index e899a6b17f8..232958afcff 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs @@ -142,17 +142,21 @@ internal static List> CreateOrderedCriteriaSets(RestoreT // Create an ordered list of selection criteria. Each will be applied, if the result is empty // fallback frameworks from "imports" will be tried. // These are only used for framework/RID combinations where content model handles everything. - // AssetTargetFallback frameworks will provide multiple criteria since all assets need to be + // AssetTargetFallback and DualCompatbiility frameworks will provide multiple criteria since all assets need to be // evaluated before selecting the TFM to use. var orderedCriteriaSets = new List>(1); var assetTargetFallback = framework as AssetTargetFallbackFramework; - // TODO NK - Is dual compatibility framework accounted for. - // Account for dual compatibility framework here and not in the resolver. + if (assetTargetFallback != null) { // Add the root project framework first. orderedCriteriaSets.Add(CreateCriteria(targetGraph, assetTargetFallback.RootFramework)); + // Add the secondary framework if dual compatibility framework. + if (framework is DualCompatibilityFramework dualCompatibilityFramework) + { + orderedCriteriaSets.Add(CreateCriteria(targetGraph, dualCompatibilityFramework.SecondaryFramework)); + } // Add all fallbacks in order. orderedCriteriaSets.AddRange(assetTargetFallback.Fallback.Select(e => CreateCriteria(targetGraph, e))); @@ -161,6 +165,11 @@ internal static List> CreateOrderedCriteriaSets(RestoreT { // Add the current framework. orderedCriteriaSets.Add(CreateCriteria(targetGraph, framework)); + + if (framework is DualCompatibilityFramework dualCompatibilityFramework) + { + orderedCriteriaSets.Add(CreateCriteria(targetGraph, dualCompatibilityFramework.SecondaryFramework)); + } } return orderedCriteriaSets; diff --git a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs index 863d4b2f5a8..52fcca1d041 100644 --- a/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs +++ b/src/NuGet.Core/NuGet.ProjectModel/PackageSpecReferenceDependencyProvider.cs @@ -195,7 +195,6 @@ private static void AddLibraryProperties(Library library, PackageSpec packageSpe { targetFrameworkInfo = packageSpec.GetTargetFramework(mcfFramework.AsFallbackFramework()); } - // TODO NK - Fix it? library[KnownLibraryProperties.TargetFrameworkInformation] = targetFrameworkInfo; diff --git a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs index 2b4c185ae83..c2488e67cb5 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs @@ -3055,6 +3055,174 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( } } + [Fact] + public async Task RestoreCommand_WithCPPCliProject_WithNativePackageWithTransitiveDependency_Succeeds() + { + using (var pathContext = new SimpleTestPathContext()) + using (var context = new SourceCacheContext()) + { + var configJson = JObject.Parse(@" + { + ""frameworks"": { + ""net5.0-windows7.0"": { + ""targetAlias"" : ""net5.0-windows"", + ""secondaryFramework"" : ""native"", + ""dependencies"": { + ""Native"": { + ""version"" : ""1.0.0"", + } + } + } + } + }"); + + // Arrange + var packageA = new SimpleTestPackageContext("a", "1.0.0"); + packageA.Files.Clear(); + packageA.AddFile("lib/net5.0/a.dll"); + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + PackageSaveMode.Defaultv3, + packageA); + + var sources = new List + { + new PackageSource(pathContext.PackageSource) + }; + var logger = new TestLogger(); + + var projectDirectory = Path.Combine(pathContext.SolutionRoot, "TestProject"); + var cachingSourceProvider = new CachingSourceProvider(new PackageSourceProvider(NullSettings.Instance)); + + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); + var dgSpec = new DependencyGraphSpec(); + dgSpec.AddProject(spec); + dgSpec.AddRestore(spec.Name); + + var request = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + { + ProjectStyle = ProjectStyle.PackageReference, + DependencyGraphSpec = dgSpec, + AllowNoOp = true, + }; + var command = new RestoreCommand(request); + + // Preconditions + var result = await command.ExecuteAsync(); + await result.CommitAsync(logger, CancellationToken.None); + result.Success.Should().BeTrue(because: logger.ShowMessages()); + + // Modify the assets file. No-op restore should not read the assets file, if it does, it will throw. + File.WriteAllText(Path.Combine(spec.RestoreMetadata.OutputPath, "project.assets.json"), " "); + var newSpec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); + var newDgSpec = new DependencyGraphSpec(); + newDgSpec.AddProject(newSpec); + newDgSpec.AddRestore(newSpec.Name); + + var newRequest = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + { + ProjectStyle = ProjectStyle.PackageReference, + DependencyGraphSpec = dgSpec, + AllowNoOp = true, + }; + var newCommand = new RestoreCommand(newRequest); + + // Act + result = await newCommand.ExecuteAsync(); + // Assert + + await result.CommitAsync(logger, CancellationToken.None); + result.Success.Should().BeTrue(because: logger.ShowMessages()); + } + } + + [Fact] + public async Task RestoreCommand_WithCPPCliProjectWithAssetTargetFallback_WithNativePackageWithTransitiveDependency_Succeeds() + { + using (var pathContext = new SimpleTestPathContext()) + using (var context = new SourceCacheContext()) + { + var configJson = JObject.Parse(@" + { + ""frameworks"": { + ""net5.0-windows7.0"": { + ""targetAlias"" : ""net5.0-windows"", + ""secondaryFramework"" : ""native"", + ""assetTargetFallback"" : ""true"", + ""imports"" : [ + ""net472"" + ], + ""dependencies"": { + ""Native"": { + ""version"" : ""1.0.0"", + } + } + } + } + }"); + + // Arrange + var packageA = new SimpleTestPackageContext("a", "1.0.0"); + packageA.Files.Clear(); + packageA.AddFile("lib/net5.0/a.dll"); + + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + PackageSaveMode.Defaultv3, + packageA); + + var sources = new List + { + new PackageSource(pathContext.PackageSource) + }; + var logger = new TestLogger(); + + var projectDirectory = Path.Combine(pathContext.SolutionRoot, "TestProject"); + var cachingSourceProvider = new CachingSourceProvider(new PackageSourceProvider(NullSettings.Instance)); + + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); + var dgSpec = new DependencyGraphSpec(); + dgSpec.AddProject(spec); + dgSpec.AddRestore(spec.Name); + + var request = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + { + ProjectStyle = ProjectStyle.PackageReference, + DependencyGraphSpec = dgSpec, + AllowNoOp = true, + }; + var command = new RestoreCommand(request); + + // Preconditions + var result = await command.ExecuteAsync(); + await result.CommitAsync(logger, CancellationToken.None); + result.Success.Should().BeTrue(because: logger.ShowMessages()); + + // Modify the assets file. No-op restore should not read the assets file, if it does, it will throw. + File.WriteAllText(Path.Combine(spec.RestoreMetadata.OutputPath, "project.assets.json"), " "); + var newSpec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); + var newDgSpec = new DependencyGraphSpec(); + newDgSpec.AddProject(newSpec); + newDgSpec.AddRestore(newSpec.Name); + + var newRequest = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + { + ProjectStyle = ProjectStyle.PackageReference, + DependencyGraphSpec = dgSpec, + AllowNoOp = true, + }; + var newCommand = new RestoreCommand(newRequest); + + // Act + result = await newCommand.ExecuteAsync(); + // Assert + + await result.CommitAsync(logger, CancellationToken.None); + result.Success.Should().BeTrue(because: logger.ShowMessages()); + } + } + private static byte[] GetTestUtilityResource(string name) { return ResourceTestUtility.GetResourceBytes( From dcfc4b1066f39982f0ab26e91c9109dc705d63f6 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Thu, 27 May 2021 18:26:25 -0700 Subject: [PATCH 09/13] Clean-up --- .../SourceRepositoryDependencyProvider.cs | 17 +- .../RestoreCommand/Utility/LockFileUtils.cs | 2 +- .../LibraryRangeCacheKey.cs | 2 + .../MsbuildRestoreTaskTests.cs | 2 +- .../RestoreCommandTests.cs | 174 +++++++----------- 5 files changed, 85 insertions(+), 112 deletions(-) diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs index 28ecc6377b2..43d454866fe 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs @@ -465,7 +465,7 @@ private IEnumerable GetDependencies( targetFramework, item => item.TargetFramework); - if(dependencyGroup == null && targetFramework is DualCompatibilityFramework dualCompatibilityFramework) + if(dependencyGroup == null && DeconstructFallbackFrameworks(targetFramework) is DualCompatibilityFramework dualCompatibilityFramework) { dependencyGroup = NuGetFrameworkUtility.GetNearest(packageInfo.DependencyGroups, dualCompatibilityFramework.SecondaryFramework, item => item.TargetFramework); } @@ -478,6 +478,21 @@ private IEnumerable GetDependencies( return Enumerable.Empty(); } + private static NuGetFramework DeconstructFallbackFrameworks(NuGetFramework nuGetFramework) + { + if (nuGetFramework is AssetTargetFallbackFramework assetTargetFallbackFramework) + { + return assetTargetFallbackFramework.RootFramework; + } + + if (nuGetFramework is FallbackFramework fallbackFramework) + { + return fallbackFramework; + } + + return nuGetFramework; + } + private async Task EnsureResource() { if (_findPackagesByIdResource == null) diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs index 232958afcff..6f34e729ab3 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/LockFileUtils.cs @@ -153,7 +153,7 @@ internal static List> CreateOrderedCriteriaSets(RestoreT // Add the root project framework first. orderedCriteriaSets.Add(CreateCriteria(targetGraph, assetTargetFallback.RootFramework)); // Add the secondary framework if dual compatibility framework. - if (framework is DualCompatibilityFramework dualCompatibilityFramework) + if (assetTargetFallback.RootFramework is DualCompatibilityFramework dualCompatibilityFramework) { orderedCriteriaSets.Add(CreateCriteria(targetGraph, dualCompatibilityFramework.SecondaryFramework)); } diff --git a/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs b/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs index 0329ac814d5..eb5bd6b0f23 100644 --- a/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs +++ b/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs @@ -14,6 +14,8 @@ namespace NuGet.DependencyResolver /// public class LibraryRangeCacheKey : IEquatable { +// TODO NK - Make sure that the *caching* is done with the dual compatibility framework in mind. + public LibraryRangeCacheKey(LibraryRange range, NuGetFramework framework) { Framework = framework; diff --git a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs index d7be8482276..53947a8b77c 100644 --- a/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs +++ b/test/NuGet.Core.FuncTests/Msbuild.Integration.Test/MsbuildRestoreTaskTests.cs @@ -867,7 +867,7 @@ public async Task MsbuildRestore_WithCPPCliVcxproj_WithAssetTargetFallback_Succe var packageManaged = new SimpleTestPackageContext("managed", "1.0.0"); packageManaged.AddFile("build/net472/managed.targets"); - packageManaged.AddFile("lib/net472.0/managed.dll"); + packageManaged.AddFile("lib/net472/managed.dll"); await SimpleTestPackageUtility.CreateFolderFeedV3Async( pathContext.PackageSource, diff --git a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs index c2488e67cb5..c228d802b01 100644 --- a/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs +++ b/test/NuGet.Core.FuncTests/NuGet.Commands.FuncTest/RestoreCommandTests.cs @@ -3058,10 +3058,8 @@ await SimpleTestPackageUtility.CreateFolderFeedV3Async( [Fact] public async Task RestoreCommand_WithCPPCliProject_WithNativePackageWithTransitiveDependency_Succeeds() { - using (var pathContext = new SimpleTestPathContext()) - using (var context = new SourceCacheContext()) - { - var configJson = JObject.Parse(@" + using var pathContext = new SimpleTestPathContext(); + var configJson = JObject.Parse(@" { ""frameworks"": { ""net5.0-windows7.0"": { @@ -3076,83 +3074,61 @@ public async Task RestoreCommand_WithCPPCliProject_WithNativePackageWithTransiti } }"); - // Arrange - var packageA = new SimpleTestPackageContext("a", "1.0.0"); - packageA.Files.Clear(); - packageA.AddFile("lib/net5.0/a.dll"); - - await SimpleTestPackageUtility.CreateFolderFeedV3Async( - pathContext.PackageSource, - PackageSaveMode.Defaultv3, - packageA); + // Arrange + var nativePackage = new SimpleTestPackageContext("native", "1.0.0"); + nativePackage.AddFile("lib/native/native.dll"); - var sources = new List - { - new PackageSource(pathContext.PackageSource) - }; - var logger = new TestLogger(); + var nativePackageChild = new SimpleTestPackageContext("native.child", "1.0.0"); + nativePackageChild.AddFile("lib/native/native.child.dll"); - var projectDirectory = Path.Combine(pathContext.SolutionRoot, "TestProject"); - var cachingSourceProvider = new CachingSourceProvider(new PackageSourceProvider(NullSettings.Instance)); + nativePackage.PerFrameworkDependencies.Add(CommonFrameworks.Native, new List { nativePackageChild }); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); - var dgSpec = new DependencyGraphSpec(); - dgSpec.AddProject(spec); - dgSpec.AddRestore(spec.Name); + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + PackageSaveMode.Defaultv3, + nativePackage, + nativePackageChild); - var request = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + var sources = new List { - ProjectStyle = ProjectStyle.PackageReference, - DependencyGraphSpec = dgSpec, - AllowNoOp = true, + new PackageSource(pathContext.PackageSource) }; - var command = new RestoreCommand(request); + var logger = new TestLogger(); - // Preconditions - var result = await command.ExecuteAsync(); - await result.CommitAsync(logger, CancellationToken.None); - result.Success.Should().BeTrue(because: logger.ShowMessages()); + var projectDirectory = Path.Combine(pathContext.SolutionRoot, "TestProject"); + var cachingSourceProvider = new CachingSourceProvider(new PackageSourceProvider(NullSettings.Instance)); - // Modify the assets file. No-op restore should not read the assets file, if it does, it will throw. - File.WriteAllText(Path.Combine(spec.RestoreMetadata.OutputPath, "project.assets.json"), " "); - var newSpec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); - var newDgSpec = new DependencyGraphSpec(); - newDgSpec.AddProject(newSpec); - newDgSpec.AddRestore(newSpec.Name); - - var newRequest = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) - { - ProjectStyle = ProjectStyle.PackageReference, - DependencyGraphSpec = dgSpec, - AllowNoOp = true, - }; - var newCommand = new RestoreCommand(newRequest); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.vcxproj")).WithTestRestoreMetadata(); - // Act - result = await newCommand.ExecuteAsync(); - // Assert + var request = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + { + ProjectStyle = ProjectStyle.PackageReference, + }; + var command = new RestoreCommand(request); - await result.CommitAsync(logger, CancellationToken.None); - result.Success.Should().BeTrue(because: logger.ShowMessages()); - } + // Preconditions + var result = await command.ExecuteAsync(); + await result.CommitAsync(logger, CancellationToken.None); + result.Success.Should().BeTrue(because: logger.ShowMessages()); + result.LockFile.Libraries.Should().HaveCount(2); + result.LockFile.Libraries.Should().Contain(e => e.Name.Equals("native")); + result.LockFile.Libraries.Should().Contain(e => e.Name.Equals("native.child")); } [Fact] public async Task RestoreCommand_WithCPPCliProjectWithAssetTargetFallback_WithNativePackageWithTransitiveDependency_Succeeds() { - using (var pathContext = new SimpleTestPathContext()) - using (var context = new SourceCacheContext()) - { - var configJson = JObject.Parse(@" + using var pathContext = new SimpleTestPathContext(); + var configJson = JObject.Parse(@" { ""frameworks"": { ""net5.0-windows7.0"": { ""targetAlias"" : ""net5.0-windows"", - ""secondaryFramework"" : ""native"", - ""assetTargetFallback"" : ""true"", + ""assetTargetFallback"" : true, ""imports"" : [ ""net472"" ], + ""secondaryFramework"" : ""native"", ""dependencies"": { ""Native"": { ""version"" : ""1.0.0"", @@ -3162,65 +3138,45 @@ public async Task RestoreCommand_WithCPPCliProjectWithAssetTargetFallback_WithNa } }"); - // Arrange - var packageA = new SimpleTestPackageContext("a", "1.0.0"); - packageA.Files.Clear(); - packageA.AddFile("lib/net5.0/a.dll"); - - await SimpleTestPackageUtility.CreateFolderFeedV3Async( - pathContext.PackageSource, - PackageSaveMode.Defaultv3, - packageA); + // Arrange + var nativePackage = new SimpleTestPackageContext("native", "1.0.0"); + nativePackage.AddFile("lib/native/native.dll"); - var sources = new List - { - new PackageSource(pathContext.PackageSource) - }; - var logger = new TestLogger(); + var nativePackageChild = new SimpleTestPackageContext("native.child", "1.0.0"); + nativePackageChild.AddFile("lib/native/native.child.dll"); - var projectDirectory = Path.Combine(pathContext.SolutionRoot, "TestProject"); - var cachingSourceProvider = new CachingSourceProvider(new PackageSourceProvider(NullSettings.Instance)); + nativePackage.PerFrameworkDependencies.Add(CommonFrameworks.Native, new List { nativePackageChild }); - var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); - var dgSpec = new DependencyGraphSpec(); - dgSpec.AddProject(spec); - dgSpec.AddRestore(spec.Name); + await SimpleTestPackageUtility.CreateFolderFeedV3Async( + pathContext.PackageSource, + PackageSaveMode.Defaultv3, + nativePackage, + nativePackageChild); - var request = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + var sources = new List { - ProjectStyle = ProjectStyle.PackageReference, - DependencyGraphSpec = dgSpec, - AllowNoOp = true, + new PackageSource(pathContext.PackageSource) }; - var command = new RestoreCommand(request); + var logger = new TestLogger(); - // Preconditions - var result = await command.ExecuteAsync(); - await result.CommitAsync(logger, CancellationToken.None); - result.Success.Should().BeTrue(because: logger.ShowMessages()); + var projectDirectory = Path.Combine(pathContext.SolutionRoot, "TestProject"); + var cachingSourceProvider = new CachingSourceProvider(new PackageSourceProvider(NullSettings.Instance)); - // Modify the assets file. No-op restore should not read the assets file, if it does, it will throw. - File.WriteAllText(Path.Combine(spec.RestoreMetadata.OutputPath, "project.assets.json"), " "); - var newSpec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.csproj")).WithTestRestoreMetadata(); - var newDgSpec = new DependencyGraphSpec(); - newDgSpec.AddProject(newSpec); - newDgSpec.AddRestore(newSpec.Name); + var spec = JsonPackageSpecReader.GetPackageSpec(configJson.ToString(), "TestProject", Path.Combine(projectDirectory, "project.vcxproj")).WithTestRestoreMetadata(); - var newRequest = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) - { - ProjectStyle = ProjectStyle.PackageReference, - DependencyGraphSpec = dgSpec, - AllowNoOp = true, - }; - var newCommand = new RestoreCommand(newRequest); - - // Act - result = await newCommand.ExecuteAsync(); - // Assert - - await result.CommitAsync(logger, CancellationToken.None); - result.Success.Should().BeTrue(because: logger.ShowMessages()); - } + var request = new TestRestoreRequest(spec, sources, pathContext.UserPackagesFolder, logger) + { + ProjectStyle = ProjectStyle.PackageReference, + }; + var command = new RestoreCommand(request); + + // Preconditions + var result = await command.ExecuteAsync(); + await result.CommitAsync(logger, CancellationToken.None); + result.Success.Should().BeTrue(because: logger.ShowMessages()); + result.LockFile.Libraries.Should().HaveCount(2); + result.LockFile.Libraries.Should().Contain(e => e.Name.Equals("native")); + result.LockFile.Libraries.Should().Contain(e => e.Name.Equals("native.child")); } private static byte[] GetTestUtilityResource(string name) From bf21e3ed3cee0a1205c5983f601679e69ac93280 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Thu, 27 May 2021 22:52:41 -0700 Subject: [PATCH 10/13] fix formatting --- .../RestoreCommand/SourceRepositoryDependencyProvider.cs | 2 +- .../NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs index 43d454866fe..d859f17fb94 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/SourceRepositoryDependencyProvider.cs @@ -465,7 +465,7 @@ private IEnumerable GetDependencies( targetFramework, item => item.TargetFramework); - if(dependencyGroup == null && DeconstructFallbackFrameworks(targetFramework) is DualCompatibilityFramework dualCompatibilityFramework) + if (dependencyGroup == null && DeconstructFallbackFrameworks(targetFramework) is DualCompatibilityFramework dualCompatibilityFramework) { dependencyGroup = NuGetFrameworkUtility.GetNearest(packageInfo.DependencyGroups, dualCompatibilityFramework.SecondaryFramework, item => item.TargetFramework); } diff --git a/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs b/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs index eb5bd6b0f23..499a148c43f 100644 --- a/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs +++ b/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs @@ -14,7 +14,7 @@ namespace NuGet.DependencyResolver /// public class LibraryRangeCacheKey : IEquatable { -// TODO NK - Make sure that the *caching* is done with the dual compatibility framework in mind. + // TODO NK - Make sure that the *caching* is done with the dual compatibility framework in mind. public LibraryRangeCacheKey(LibraryRange range, NuGetFramework framework) { From f59de62a222a9f7103724d3edd2f96e615b1a3c1 Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Fri, 28 May 2021 12:25:17 -0700 Subject: [PATCH 11/13] Final clean-up --- .../NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs b/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs index 499a148c43f..0329ac814d5 100644 --- a/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs +++ b/src/NuGet.Core/NuGet.DependencyResolver.Core/LibraryRangeCacheKey.cs @@ -14,8 +14,6 @@ namespace NuGet.DependencyResolver /// public class LibraryRangeCacheKey : IEquatable { - // TODO NK - Make sure that the *caching* is done with the dual compatibility framework in mind. - public LibraryRangeCacheKey(LibraryRange range, NuGetFramework framework) { Framework = framework; From 6b3eb7798178d29e0d26b65537626430284e860b Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Wed, 2 Jun 2021 18:53:53 -0700 Subject: [PATCH 12/13] Use root framework --- src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs b/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs index 52a90a532e8..ffbb47038ae 100644 --- a/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs +++ b/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs @@ -77,7 +77,7 @@ public bool Equals(DualCompatibilityFramework other) return true; } - return Comparer.Equals(this, other) + return Comparer.Equals(RootFramework, other.RootFramework) && Comparer.Equals(SecondaryFramework, other.SecondaryFramework); } @@ -88,7 +88,7 @@ public override int GetHashCode() var combiner = new HashCodeCombiner(); // Ensure that this is different from AssetTargetFallback & FallbackFramework; combiner.AddStringIgnoreCase("multipleCompat"); - combiner.AddObject(Comparer.GetHashCode(this)); + combiner.AddObject(Comparer.GetHashCode(RootFramework)); combiner.AddObject(Comparer.GetHashCode(SecondaryFramework)); _hashCode = combiner.CombinedHash; } From 89d6b55c9377afe84c33e2e68323931fb6a2650b Mon Sep 17 00:00:00 2001 From: Nikolche Kolev Date: Mon, 7 Jun 2021 16:36:37 -0700 Subject: [PATCH 13/13] Cache fallback framework, ignore dual compat framework for csproj. --- .../Utility/MSBuildProjectFrameworkUtility.cs | 16 ++++++++----- .../DualCompatibilityFramework.cs | 10 ++++++-- .../MSBuildProjectFrameworkUtilityTests.cs | 4 ++++ .../DualCompatibilityFrameworkTests.cs | 24 +++++++++++++++++-- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs b/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs index 955105d9de1..6509dc80db4 100644 --- a/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs +++ b/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs @@ -170,14 +170,18 @@ internal static NuGetFramework GetProjectFramework( bool isXnaWindowsPhoneProject, bool isManagementPackProject) { - bool isCppCli = clrSupport?.Equals("NetCore", StringComparison.OrdinalIgnoreCase) == true; + bool isCppCliSet = clrSupport?.Equals("NetCore", StringComparison.OrdinalIgnoreCase) == true; + bool isCppCli = false; // C++ check - if (projectFilePath?.EndsWith(".vcxproj", StringComparison.OrdinalIgnoreCase) == true - && !isCppCli) + if (projectFilePath?.EndsWith(".vcxproj", StringComparison.OrdinalIgnoreCase) == true) { - // The C++ project does not have a TargetFrameworkMoniker property set. - // We hard-code the return value to Native. - return FrameworkConstants.CommonFrameworks.Native; + if (!isCppCliSet) + { + // The C++ project does not have a TargetFrameworkMoniker property set. + // We hard-code the return value to Native. + return FrameworkConstants.CommonFrameworks.Native; + } + isCppCli = true; } // The MP project does not have a TargetFrameworkMoniker property set. diff --git a/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs b/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs index ffbb47038ae..9be00c9fefd 100644 --- a/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs +++ b/src/NuGet.Core/NuGet.Frameworks/DualCompatibilityFramework.cs @@ -24,6 +24,7 @@ public class DualCompatibilityFramework : NuGetFramework public NuGetFramework SecondaryFramework { get; } private int? _hashCode; + private FallbackFramework _fallbackFramework; /// /// Multiple compatbility @@ -48,7 +49,12 @@ public DualCompatibilityFramework(NuGetFramework framework, NuGetFramework secon /// public FallbackFramework AsFallbackFramework() { - return new FallbackFramework(RootFramework, new NuGetFramework[] { SecondaryFramework }); + if (_fallbackFramework == null) + { + _fallbackFramework = new FallbackFramework(RootFramework, new NuGetFramework[] { SecondaryFramework }); + } + + return _fallbackFramework; } private static NuGetFramework ValidateFrameworkArgument(NuGetFramework framework) @@ -87,7 +93,7 @@ public override int GetHashCode() { var combiner = new HashCodeCombiner(); // Ensure that this is different from AssetTargetFallback & FallbackFramework; - combiner.AddStringIgnoreCase("multipleCompat"); + combiner.AddStringIgnoreCase(nameof(DualCompatibilityFramework)); combiner.AddObject(Comparer.GetHashCode(RootFramework)); combiner.AddObject(Comparer.GetHashCode(SecondaryFramework)); _hashCode = combiner.CombinedHash; diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs index a1450c14904..5239000bafd 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs @@ -299,6 +299,10 @@ public void GetProjectFramework_WithCLRSupport_VariousInputs( Assert.Equal(expectedPrimaryShortName, extendedFramework.RootFramework.GetShortFolderName()); Assert.Equal(expectedSecondaryShortName, extendedFramework.SecondaryFramework.GetShortFolderName()); } + else + { + Assert.Null(nugetFramework as DualCompatibilityFramework); + } } /// diff --git a/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs b/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs index c91cfcaf98a..3ef978f76e2 100644 --- a/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Frameworks.Test/DualCompatibilityFrameworkTests.cs @@ -2,8 +2,10 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq; using FluentAssertions; using Xunit; +using static NuGet.Frameworks.FrameworkConstants; namespace NuGet.Frameworks.Test { @@ -42,8 +44,26 @@ public void NuGetFrameworkEquals_WithDualCompatibilityFramework_Succeeds(string public void DualCompatibilityFrameworkEquals_WithNonDualCompatibilityFramework_Succeeds(string shortFrameworkName) { var nugetFramework = NuGetFramework.Parse(shortFrameworkName); - var assetTargetFallback = new DualCompatibilityFramework(nugetFramework, secondaryFramework: NuGetFramework.AnyFramework); - Assert.False(assetTargetFallback.Equals((object)nugetFramework)); + var dualCompatibilityFramework = new DualCompatibilityFramework(nugetFramework, secondaryFramework: NuGetFramework.AnyFramework); + Assert.False(dualCompatibilityFramework.Equals((object)nugetFramework)); + } + + [Fact] + public void AsFallbackFramework_WhenCalledMultipleTimes_CachesFallbackObjectReference() + { + var nugetFramework = CommonFrameworks.Net50; + var dualCompatibilityFramework = new DualCompatibilityFramework(nugetFramework, secondaryFramework: NuGetFramework.AnyFramework); + + FallbackFramework fallbackFramework = dualCompatibilityFramework.AsFallbackFramework(); + + var comparer = new NuGetFrameworkFullComparer(); + Assert.True(comparer.Equals(fallbackFramework, nugetFramework)); + + fallbackFramework.Fallback.Should().HaveCount(1); + fallbackFramework.Fallback.Single().Should().Be(NuGetFramework.AnyFramework); + + FallbackFramework fallbackFramework2 = dualCompatibilityFramework.AsFallbackFramework(); + fallbackFramework.Should().BeSameAs(fallbackFramework2); } } }