diff --git a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets index 2b6b6e8268f..a1cb1da4e3c 100644 --- a/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets +++ b/src/NuGet.Core/NuGet.Build.Tasks/NuGet.targets @@ -939,6 +939,7 @@ Copyright (c) .NET Foundation. All rights reserved. $(TargetFrameworkIdentifier) $(TargetFrameworkVersion) $(TargetFrameworkMoniker) + $(TargetFrameworkProfile) $(TargetPlatformIdentifier) $(TargetPlatformVersion) $(TargetPlatformMinVersion) 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 e69de29bb2d..41651d04ab5 100644 --- a/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt +++ b/src/NuGet.Core/NuGet.Commands/PublicAPI/net472/PublicAPI.Unshipped.txt @@ -0,0 +1 @@ +static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetFrameworkIdentifier, string targetFrameworkVersion, string targetFrameworkProfile, string targetPlatformIdentifier, string targetPlatformVersion, string targetPlatformMinVersion) -> NuGet.Frameworks.NuGetFramework \ No newline at end of file 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 e69de29bb2d..41651d04ab5 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 @@ -0,0 +1 @@ +static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetFrameworkIdentifier, string targetFrameworkVersion, string targetFrameworkProfile, string targetPlatformIdentifier, string targetPlatformVersion, string targetPlatformMinVersion) -> NuGet.Frameworks.NuGetFramework \ No newline at end of file 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 e69de29bb2d..41651d04ab5 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 @@ -0,0 +1 @@ +static NuGet.Commands.MSBuildProjectFrameworkUtility.GetProjectFramework(string projectFilePath, string targetFrameworkMoniker, string targetFrameworkIdentifier, string targetFrameworkVersion, string targetFrameworkProfile, string targetPlatformIdentifier, string targetPlatformVersion, string targetPlatformMinVersion) -> NuGet.Frameworks.NuGetFramework \ No newline at end of file diff --git a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs index 23ac451493c..0e757cf7948 100644 --- a/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs +++ b/src/NuGet.Core/NuGet.Commands/RestoreCommand/Utility/MSBuildRestoreUtility.cs @@ -426,6 +426,7 @@ private static IEnumerable GetTargetFrameworkInforma var targetFrameworkIdentifier = item.GetProperty("TargetFrameworkIdentifier"); var targetFrameworkVersion = item.GetProperty("TargetFrameworkVersion"); var targetFrameworkMoniker = item.GetProperty("TargetFrameworkMoniker"); + var targetFrameworkProfile = item.GetProperty("TargetFrameworkProfile"); var targetPlatformIdentifier = item.GetProperty("TargetPlatformIdentifier"); var targetPlatformVersion = item.GetProperty("TargetPlatformVersion"); var targetPlatformMinVersion = item.GetProperty("TargetPlatformMinVersion"); @@ -437,18 +438,19 @@ private static IEnumerable GetTargetFrameworkInforma } uniqueIds.Add(targetAlias); - IEnumerable targetFramework = MSBuildProjectFrameworkUtility.GetProjectFrameworkStrings( - projectFilePath: filePath, - targetFrameworks: null, - targetFramework: null, - targetFrameworkMoniker: targetFrameworkMoniker, - targetPlatformIdentifier: targetPlatformIdentifier, - targetPlatformVersion: targetPlatformVersion, - targetPlatformMinVersion: targetPlatformMinVersion); + NuGetFramework targetFramework = MSBuildProjectFrameworkUtility.GetProjectFramework( + projectFilePath: filePath, + targetFrameworkMoniker: targetFrameworkMoniker, + targetFrameworkIdentifier: targetFrameworkIdentifier, + targetFrameworkVersion: targetFrameworkVersion, + targetFrameworkProfile: targetFrameworkProfile, + targetPlatformIdentifier: targetPlatformIdentifier, + targetPlatformVersion: targetPlatformVersion, + targetPlatformMinVersion: targetPlatformMinVersion); var targetFrameworkInfo = new TargetFrameworkInformation() { - FrameworkName = NuGetFramework.Parse(targetFramework.Single()), + FrameworkName = targetFramework, TargetAlias = targetAlias }; if (restoreType == ProjectStyle.PackageReference) diff --git a/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs b/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs index b3f459ba2b2..4a3d2d29964 100644 --- a/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs +++ b/src/NuGet.Core/NuGet.Commands/Utility/MSBuildProjectFrameworkUtility.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NuGet.Common; using NuGet.Frameworks; @@ -34,6 +35,36 @@ public static IEnumerable GetProjectFrameworkStrings( isXnaWindowsPhoneProject: false); } + /// + /// Given the properties from an msbuild project file and a the file path, infer the target framework. + /// This method prioritizes projects without a framework, such as vcxproj and accounts for the mismatching arguments in UAP projects, where the TFI and TFV are set but should be ignored. + /// Likewise, this method will *ignore* unnecessary properties, such as TPI and TPV when profiles are used, and frameworks that do not support platforms have some default values. + /// + /// The inferred framework. Unsupported otherwise. + public static NuGetFramework GetProjectFramework( + string projectFilePath, + string targetFrameworkMoniker, + string targetFrameworkIdentifier, + string targetFrameworkVersion, + string targetFrameworkProfile, + string targetPlatformIdentifier, + string targetPlatformVersion, + string targetPlatformMinVersion) + { + return GetProjectFramework( + projectFilePath, + targetFrameworkMoniker, + targetFrameworkIdentifier, + targetFrameworkVersion, + targetFrameworkProfile, + targetPlatformIdentifier, + targetPlatformVersion, + targetPlatformMinVersion, + isXnaWindowsPhoneProject: false, + isManagementPackProject: false, + GetAsNuGetFramework); + } + /// /// Determine the target framework of an msbuild project. /// @@ -47,6 +78,37 @@ public static IEnumerable GetProjectFrameworkStrings( string targetPlatformMinVersion, bool isXnaWindowsPhoneProject, bool isManagementPackProject) + { + return GetProjectFrameworks( + projectFilePath, + targetFrameworks, + targetFramework, + targetFrameworkMoniker, + targetFrameworkIdentifier: null, + targetFrameworkVersion: null, + targetFrameworkProfile: null, + targetPlatformIdentifier, + targetPlatformVersion, + targetPlatformMinVersion, + isXnaWindowsPhoneProject, + isManagementPackProject, + GetAsFrameworkString); + } + + internal static IEnumerable GetProjectFrameworks( + string projectFilePath, + string targetFrameworks, + string targetFramework, + string targetFrameworkMoniker, + string targetFrameworkIdentifier, + string targetFrameworkVersion, + string targetFrameworkProfile, + string targetPlatformIdentifier, + string targetPlatformVersion, + string targetPlatformMinVersion, + bool isXnaWindowsPhoneProject, + bool isManagementPackProject, + Func valueFactory) { var frameworks = new SortedSet(StringComparer.OrdinalIgnoreCase); @@ -55,7 +117,7 @@ public static IEnumerable GetProjectFrameworkStrings( if (frameworks.Count > 0) { - return frameworks; + return frameworks.Select(e => valueFactory(e)); } // TargetFramework property @@ -65,26 +127,49 @@ public static IEnumerable GetProjectFrameworkStrings( { frameworks.Add(currentFrameworkString); - return frameworks; + return frameworks.Select(e => valueFactory(e)); } + return new T[] { GetProjectFramework( + projectFilePath, + targetFrameworkMoniker, + targetFrameworkIdentifier, + targetFrameworkVersion, + targetFrameworkProfile, + targetPlatformIdentifier, + targetPlatformVersion, + targetPlatformMinVersion, + isXnaWindowsPhoneProject, + isManagementPackProject, + valueFactory) }; + } + + internal static T GetProjectFramework( + string projectFilePath, + string targetFrameworkMoniker, + string targetFrameworkIdentifier, + string targetFrameworkVersion, + string targetFrameworkProfile, + string targetPlatformIdentifier, + string targetPlatformVersion, + string targetPlatformMinVersion, + bool isXnaWindowsPhoneProject, + bool isManagementPackProject, + Func valueFactory) + { // C++ check 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. - frameworks.Add("Native, Version=0.0"); - - return frameworks; + return valueFactory("Native, Version=0.0"); } // The MP project does not have a TargetFrameworkMoniker property set. // We hard-code the return value to SCMPInfra. if (isManagementPackProject) { - frameworks.Add("SCMPInfra, Version=0.0"); - - return frameworks; + return valueFactory("SCMPInfra, Version=0.0"); } // UAP/Windows store projects @@ -113,24 +198,23 @@ public static IEnumerable GetProjectFrameworkStrings( platformIdentifier = FrameworkConstants.FrameworkIdentifiers.Windows; } - frameworks.Add($"{platformIdentifier}, Version={platformVersion}"); - - return frameworks; + return valueFactory($"{platformIdentifier}, Version={platformVersion}"); } if (!string.IsNullOrEmpty(platformVersion) && StringComparer.OrdinalIgnoreCase.Equals(platformIdentifier, "UAP")) { // Use the platform id and versions, this is done for UAP projects - frameworks.Add($"{platformIdentifier}, Version={platformVersion}"); - - return frameworks; + return valueFactory($"{platformIdentifier}, Version={platformVersion}"); } // TargetFrameworkMoniker - currentFrameworkString = MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkMoniker); + var currentFrameworkString = MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkMoniker); + var trimmedTargetFrameworkIdentifier = MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkIdentifier); + var trimmedTargetFrameworkVersion = MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkVersion); + var hasTFIandTFV = !string.IsNullOrEmpty(trimmedTargetFrameworkIdentifier) && !string.IsNullOrEmpty(trimmedTargetFrameworkVersion); - if (!string.IsNullOrEmpty(currentFrameworkString)) + if (!string.IsNullOrEmpty(currentFrameworkString) || hasTFIandTFV) { // XNA project lies about its true identity, reporting itself as a normal .NET 4.0 project. // We detect it and changes its target framework to Silverlight4-WindowsPhone71 @@ -138,30 +222,23 @@ public static IEnumerable GetProjectFrameworkStrings( && ".NETFramework,Version=v4.0".Equals(currentFrameworkString, StringComparison.OrdinalIgnoreCase)) { currentFrameworkString = "Silverlight,Version=v4.0,Profile=WindowsPhone71"; + return valueFactory(currentFrameworkString); } - // Workaround until https://github.com/NuGet/Home/issues/9871 is ready. - if (!string.IsNullOrEmpty(platformVersion)) - { - var framework = NuGetFramework.Parse(currentFrameworkString); - if (framework.Version.Major >= 5 && framework.Framework.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, StringComparison.OrdinalIgnoreCase)) - { - currentFrameworkString = framework.GetShortFolderName() + $"-{platformIdentifier}{platformVersion}"; - } - } + NuGetFramework framework = hasTFIandTFV ? + NuGetFramework.ParseComponents( + trimmedTargetFrameworkIdentifier, + trimmedTargetFrameworkVersion, + MSBuildStringUtility.TrimAndGetNullForEmpty(targetFrameworkProfile), + MSBuildStringUtility.TrimAndGetNullForEmpty(targetPlatformIdentifier), + MSBuildStringUtility.TrimAndGetNullForEmpty(targetPlatformVersion)) : + NuGetFramework.Parse(currentFrameworkString); - frameworks.Add(currentFrameworkString); - - return frameworks; + return valueFactory(framework); } - // Default to unsupported it no framework can be found. - if (frameworks.Count < 1) - { - frameworks.Add(NuGetFramework.UnsupportedFramework.ToString()); - } - - return frameworks; + // Default to unsupported it no framework was found. + return valueFactory(NuGetFramework.UnsupportedFramework); } /// @@ -221,16 +298,37 @@ public static NuGetFramework GetProjectFrameworkReplacement(NuGetFramework frame return framework; } - private static string GetPropertyOrNull(string propertyName, IDictionary projectProperties) + /// + /// Get a NuGetFramework out of the passed object. The argument is expected to either be a or . + /// + private static NuGetFramework GetAsNuGetFramework(object arg) + { + if (arg is NuGetFramework) + { + return (NuGetFramework)arg; + } + if (arg is string frameworkString) + { + return NuGetFramework.Parse(frameworkString); + } + throw new ArgumentException("Unexpected object type"); + } + + /// + /// Get a roundtrippable framework string out of the passed object. The argument is expected to either be a or . + /// + private static string GetAsFrameworkString(object arg) { - string value; - if (projectProperties.TryGetValue(propertyName, out value) - && !string.IsNullOrEmpty(value)) + if (arg is string) + { + return (string)arg; + } + if (arg is NuGetFramework framework) { - return value; + return framework.DotNetFrameworkName; } - return null; + throw new ArgumentException("Unexpected object type"); } } } diff --git a/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs b/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs index e9779ff4497..84697e00896 100644 --- a/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs +++ b/src/NuGet.Core/NuGet.Frameworks/NuGetFrameworkFactory.cs @@ -79,8 +79,15 @@ public static NuGetFramework ParseComponents(string targetFrameworkIdentifier, s } /// - /// Creates a NuGetFramework from individual components, using the given mappings + /// Creates a NuGetFramework from individual components, using the given mappings. + /// This method may have individual component preference, as described in the remarks. /// + /// + /// Profiles and TargetPlatforms can't mix. As such the precedence order is profile over target platforms (TPI, TPV). + /// .NETCoreApp,Version=v5.0 and later do not support profiles. + /// Target Platforms are ignored for any frameworks not supporting them. + /// This allows to handle the old project scenarios where the TargetPlatformIdentifier and TargetPlatformVersion may be set to Windows and v7.0 respectively. + /// internal static NuGetFramework ParseComponents(string targetFrameworkIdentifier, string targetFrameworkVersion, string targetFrameworkProfile, string targetPlatformIdentifier, string targetPlatformVersion, IFrameworkNameProvider mappings) { if (string.IsNullOrEmpty(targetFrameworkIdentifier)) @@ -166,48 +173,46 @@ internal static NuGetFramework ParseComponents(string targetFrameworkIdentifier, } } - if (!string.IsNullOrEmpty(targetPlatformVersion)) + // Profiles take precedence over TPI/TPV + if (string.IsNullOrEmpty(profile)) { - targetPlatformVersion = targetPlatformVersion.TrimStart('v'); - if (targetPlatformVersion.IndexOf('.') < 0) + if (version.Major >= 5 && + StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, framework)) { - targetPlatformVersion += ".0"; - } + if (!string.IsNullOrEmpty(targetPlatformVersion)) + { + targetPlatformVersion = targetPlatformVersion.TrimStart('v'); + if (targetPlatformVersion.IndexOf('.') < 0) + { + targetPlatformVersion += ".0"; + } - if (!Version.TryParse(targetPlatformVersion, out platformVersion)) - { - throw new ArgumentException(string.Format( - CultureInfo.CurrentCulture, - Strings.InvalidPlatformVersion, - targetPlatformVersion)); + if (!Version.TryParse(targetPlatformVersion, out platformVersion)) + { + throw new ArgumentException(string.Format( + CultureInfo.CurrentCulture, + Strings.InvalidPlatformVersion, + targetPlatformVersion)); + } + } + result = new NuGetFramework(framework, version, targetPlatformIdentifier ?? string.Empty, platformVersion); } - } - - if (version.Major >= 5 - && StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, framework)) - { - if (!string.IsNullOrEmpty(profile)) + else { - throw new ArgumentException(string.Format( - CultureInfo.CurrentCulture, - Strings.FrameworkDoesNotSupportProfiles, - profile - )); + result = new NuGetFramework(framework, version); } - - result = new NuGetFramework(framework, version, targetPlatformIdentifier ?? string.Empty, platformVersion); } else { - if (!string.IsNullOrEmpty(targetPlatformIdentifier)) + if (version.Major >= 5 && + StringComparer.OrdinalIgnoreCase.Equals(FrameworkConstants.FrameworkIdentifiers.NetCoreApp, framework)) { throw new ArgumentException(string.Format( CultureInfo.CurrentCulture, - Strings.FrameworkDoesNotSupportPlatforms, - targetPlatformIdentifier + Strings.FrameworkDoesNotSupportProfiles, + profile )); } - result = new NuGetFramework(framework, version, profile); } diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs index 3ccbb63c56e..718114e29fe 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildProjectFrameworkUtilityTests.cs @@ -1,10 +1,9 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; using NuGet.Frameworks; using Xunit; @@ -226,6 +225,68 @@ public void MSBuildProjectFrameworkUtility_VerifyFallbackToUnsupported() Assert.Equal(NuGetFramework.UnsupportedFramework, framework); } + [Theory] + [InlineData(".NETFramework,Version=v.4.5", ".NETFramework", "v4.5", "", "", "", "", "net45")] + [InlineData(null, ".NETFramework", "v4.5", "", "", "", "", "net45")] + [InlineData(".NETFramework,Version=v.4.5", ".NETFramework", "v4.5", "client", "", "", "", "net45-client")] + [InlineData(".NETCoreApp,Version=v.5.0", ".NETCoreApp", "v5.0", "", "android", "10", "", "net5.0-android10.0")] + [InlineData(".NETCoreApp,Version=v.5.0", ".NETCoreApp", "v5.0", "", "ios", "21.0", "", "net5.0-ios21.0")] + [InlineData(null, ".NETCoreApp", "v6.0", "", "ios", "21.0", "", "net6.0-ios21.0")] + [InlineData(null, ".NETCoreApp", "v6.0", "", "ios", "v21.0", "", "net6.0-ios21.0")] + [InlineData(null, null, null, "", "ios", "v21.0", "", "unsupported")] + [InlineData(null, ".NETCoreApp", "v6.0", "", "UAP", "10.0.1.2", "", "uap10.0.1.2")] + [InlineData(null, ".NETCoreApp", "v6.0", "", "UAP", "10.0.1.2", "10.0.1.3", "uap10.0.1.3")] + [InlineData(".NETCoreApp,Version=v3.0", ".NETCoreApp", "v3.0", "", "android", "10", "", "netcoreapp3.0")] + public void GetProjectFramework_WithCanonicalProperties_Succeeds( + string targetFrameworkMoniker, + string targetFrameworkIdentifier, + string targetFrameworkVersion, + string targetFrameworkProfile, + string targetPlatformIdentifier, + string targetPlatformVersion, + string targetPlatformMinVersion, + string expectedShortName) + { + var nugetFramework = MSBuildProjectFrameworkUtility.GetProjectFramework( + + projectFilePath: @"C:\csproj", + targetFrameworkMoniker, + + targetFrameworkIdentifier, + targetFrameworkVersion, + targetFrameworkProfile, + targetPlatformIdentifier, + targetPlatformVersion, + targetPlatformMinVersion); + + Assert.Equal(expectedShortName, nugetFramework.GetShortFolderName()); + } + + [Theory] + [InlineData(null, ".NETCoreApp", "v6.0", "", "ios", "5.0-preview.3", "")] + [InlineData(null, ".NETCoreApp", "v6.0-preview.3", "", "ios", "5.0", "")] + [InlineData(".NETCoreApp,Version=v.5.0", ".NETCoreApp", "v5.0", "NET50CannotHaveProfiles", "android", "10", "")] + public void GetProjectFramework_WithInvalidInput_Throws( + string targetFrameworkMoniker, + string targetFrameworkIdentifier, + string targetFrameworkVersion, + string targetFrameworkProfile, + string targetPlatformIdentifier, + string targetPlatformVersion, + string targetPlatformMinVersion) + { + Assert.ThrowsAny(() => MSBuildProjectFrameworkUtility.GetProjectFramework( + projectFilePath: @"C:\csproj", + targetFrameworkMoniker, + targetFrameworkIdentifier, + targetFrameworkVersion, + targetFrameworkProfile, + targetPlatformIdentifier, + targetPlatformVersion, + targetPlatformMinVersion)); + } + + /// /// Test helper /// diff --git a/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildRestoreUtilityTests.cs b/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildRestoreUtilityTests.cs index a9141b95c77..45f648c6222 100644 --- a/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildRestoreUtilityTests.cs +++ b/test/NuGet.Core.Tests/NuGet.Commands.Test/MSBuildRestoreUtilityTests.cs @@ -3766,6 +3766,146 @@ public void MSBuildRestoreUtility_GetPackageSpec_CPVM_NoVersionChecks() } } + [Fact] + public void MSBuildRestoreUtility_GetPackageSpec_MultiTargettingWithNet5_UsesIndividualProperties() + { + using (var workingDir = TestDirectory.Create()) + { + // Arrange + var project1Root = Path.Combine(workingDir, "a"); + var project1Path = Path.Combine(project1Root, "a.csproj"); + var outputPath1 = Path.Combine(project1Root, "obj"); + var fallbackFolder = Path.Combine(project1Root, "fallback"); + var packagesFolder = Path.Combine(project1Root, "packages"); + var project1UniqueName = "482C20DE-DFF9-4BD0-B90A-BD3201AA351A"; + var items = new List>(); + + var net60Alias = "net5.0"; + var net50WithPlatformAlias = "net50-android21.0"; + + items.Add(new Dictionary() + { + { "Type", "ProjectSpec" }, + { "ProjectName", "a" }, + { "ProjectStyle", "PackageReference" }, + { "OutputPath", outputPath1 }, + { "ProjectUniqueName", project1UniqueName }, + { "ProjectPath", project1Path }, + { "Sources", "https://nuget.org/a/index.json;https://nuget.org/b/index.json" }, + { "FallbackFolders", fallbackFolder }, + { "PackagesPath", packagesFolder }, + { "CrossTargeting", "true" }, + }); + + items.Add(new Dictionary() + { + { "Type", "TargetFrameworkInformation" }, + { "ProjectUniqueName", project1UniqueName }, + { "PackageTargetFallback", "" }, + { "TargetFramework", net50WithPlatformAlias }, + { "TargetFrameworkIdentifier", ".NETCoreApp" }, + { "TargetFrameworkVersion", "v5.0" }, + { "TargetFrameworkMoniker", ".NETCoreApp,Version=v5.0" }, + { "TargetPlatformIdentifier", "android" }, + { "TargetPlatformVersion", "29.0" }, + }); + + items.Add(new Dictionary() + { + { "Type", "TargetFrameworkInformation" }, + { "ProjectUniqueName", project1UniqueName }, + { "PackageTargetFallback", "" }, + { "TargetFramework", net60Alias }, + { "TargetFrameworkIdentifier", ".NETCoreApp" }, + { "TargetFrameworkVersion", "v6.0" }, + { "TargetFrameworkMoniker", ".NETCoreApp,Version=v6.0" }, + { "TargetPlatformIdentifier", "" }, + { "TargetPlatformVersion", "" }, + }); + + var wrappedItems = items.Select(CreateItems).ToList(); + + // Act + var dgSpec = MSBuildRestoreUtility.GetDependencySpec(wrappedItems); + var project1Spec = dgSpec.Projects.Single(); + + var net60Framework = project1Spec.TargetFrameworks.Single(e => e.TargetAlias.Equals(net60Alias)); + var net50Android = project1Spec.TargetFrameworks.Single(e => e.TargetAlias.Equals(net50WithPlatformAlias)); + + // Assert + net60Framework.FrameworkName.Framework.Should().Be(FrameworkConstants.FrameworkIdentifiers.NetCoreApp); + net60Framework.FrameworkName.Version.Should().Be(new Version("6.0.0.0")); + net60Framework.FrameworkName.HasPlatform.Should().BeFalse(); + + net50Android.FrameworkName.Framework.Should().Be(FrameworkConstants.FrameworkIdentifiers.NetCoreApp); + net50Android.FrameworkName.Version.Should().Be(new Version("5.0.0.0")); + net50Android.FrameworkName.HasPlatform.Should().BeTrue(); + net50Android.FrameworkName.Platform.Should().Be("android"); + net50Android.FrameworkName.PlatformVersion.Should().Be(new Version("29.0.0.0")); + } + } + + [Fact] + public void MSBuildRestoreUtility_GetPackageSpec_SingleTargetingFrameworkWithProfile_UsesIndividualProperties() + { + using (var workingDir = TestDirectory.Create()) + { + // Arrange + var project1Root = Path.Combine(workingDir, "a"); + var project1Path = Path.Combine(project1Root, "a.csproj"); + var outputPath1 = Path.Combine(project1Root, "obj"); + var fallbackFolder = Path.Combine(project1Root, "fallback"); + var packagesFolder = Path.Combine(project1Root, "packages"); + var project1UniqueName = "482C20DE-DFF9-4BD0-B90A-BD3201AA351A"; + var items = new List>(); + + var alias = "net5.0"; + var profile = "Client"; + + items.Add(new Dictionary() + { + { "Type", "ProjectSpec" }, + { "ProjectName", "a" }, + { "ProjectStyle", "PackageReference" }, + { "OutputPath", outputPath1 }, + { "ProjectUniqueName", project1UniqueName }, + { "ProjectPath", project1Path }, + { "Sources", "https://nuget.org/a/index.json;https://nuget.org/b/index.json" }, + { "FallbackFolders", fallbackFolder }, + { "PackagesPath", packagesFolder }, + { "CrossTargeting", "true" }, + }); + + items.Add(new Dictionary() + { + { "Type", "TargetFrameworkInformation" }, + { "ProjectUniqueName", project1UniqueName }, + { "PackageTargetFallback", "" }, + { "TargetFramework", alias }, + { "TargetFrameworkIdentifier", ".NETFramework" }, + { "TargetFrameworkVersion", "v4.0" }, + { "TargetFrameworkMoniker", ".NETFramework,Version=v4.0" }, + { "TargetFrameworkProfile", profile }, + { "TargetPlatformIdentifier", "" }, + { "TargetPlatformVersion", "" }, + }); + + var wrappedItems = items.Select(CreateItems).ToList(); + + // Act + var dgSpec = MSBuildRestoreUtility.GetDependencySpec(wrappedItems); + var project1Spec = dgSpec.Projects.Single(); + + var net60Framework = project1Spec.TargetFrameworks.Single(e => e.TargetAlias.Equals(alias)); + + // Assert + net60Framework.FrameworkName.Framework.Should().Be(FrameworkConstants.FrameworkIdentifiers.Net); + net60Framework.FrameworkName.Version.Should().Be(new Version("4.0.0.0")); + net60Framework.FrameworkName.Profile.Should().Be(profile); + net60Framework.FrameworkName.HasPlatform.Should().BeFalse(); + } + } + private static IDictionary CreateProject(string root, string uniqueName) { var project1Path = Path.Combine(root, "a.csproj"); diff --git a/test/NuGet.Core.Tests/NuGet.Frameworks.Test/NuGetFrameworkParseComponentsTest.cs b/test/NuGet.Core.Tests/NuGet.Frameworks.Test/NuGetFrameworkParseComponentsTest.cs index 90f77c89829..3d506be4f64 100644 --- a/test/NuGet.Core.Tests/NuGet.Frameworks.Test/NuGetFrameworkParseComponentsTest.cs +++ b/test/NuGet.Core.Tests/NuGet.Frameworks.Test/NuGetFrameworkParseComponentsTest.cs @@ -64,6 +64,16 @@ public void NuGetFramework_ProfileName(string tfm, string tfv, string tfp, strin [InlineData("net5.0-tvos1.0", ".NETCoreApp", "v5.0", null, "tvos", "1.0")] [InlineData("net5.0-windows10.0", ".NETCoreApp", "v5.0", null, "windows", "10.0")] [InlineData("net5.0-macos10.15.2.3", ".NETCoreApp", "v5.0", null, "macos", "10.15.2.3")] + [InlineData("unsupported", "unsupported", null, null, null, null)] + // Scenarios where certain properties are ignored. + [InlineData("netcoreapp3.0", ".NETCoreApp", "v3.0", null, "macos", "10.15.2.3")] + [InlineData("netcoreapp3.0", ".NETCoreApp", "v3.0", null, "macos", null)] + [InlineData("netcoreapp3.1", ".NETCoreApp", "v3.1", null, null, "10.15.2.3")] + [InlineData("netcoreapp3.1-client", ".NETCoreApp", "v3.1", "client", null, "10.15.2.3")] + [InlineData("netcoreapp3.1-client", ".NETCoreApp", "v3.1", "client", null, null)] + [InlineData("netcoreapp3.0-client", ".NETCoreApp", "v3.0", "client", "Windows", "7.0")] + [InlineData("netcoreapp3.0", ".NETCoreApp", "v3.0", null, "Windows", "7.0")] + public void NuGetFramework_ParseToShortName(string expected, string tfi, string tfv, string tfp, string tpi, string tpv) { // Arrange @@ -88,6 +98,7 @@ public void NuGetFramework_ParseToShortName(string expected, string tfi, string [InlineData("net", "472", null, "ios", "14.0", "net472.0-ios14.0")] // Pre-Net5.0 ERA + [InlineData(".NETCoreApp", "v3.0", null, "Windows", "7.0", ".NETCoreApp,Version=v3.0")] [InlineData(".NETFramework", "v4.5", null, null, null, ".NETFramework,Version=v4.5")] [InlineData(".NETFramework", "v2.0", null, null, null, ".NETFramework,Version=v2.0")] [InlineData(".NETFramework", "4.0", null, null, null, ".NETFramework,Version=v4.0")] @@ -125,11 +136,28 @@ public void NuGetFramework_ParseToShortName(string expected, string tfi, string [InlineData(".NETCoreApp", "1.5", null, null, null, ".NETCoreApp,Version=v1.5")] [InlineData(".NETCoreApp", "2", null, null, null, ".NETCoreApp,Version=v2.0")] [InlineData(".NETCoreApp", "3", null, null, null, ".NETCoreApp,Version=v3.0")] + [InlineData("unsupported", null, null, null, null, "Unsupported,Version=v0.0")] + // Scenarios where certain properties are ignored. + [InlineData(".NETCoreApp", "v3.0", null, "macos", "10.15.2.3", ".NETCoreApp,Version=v3.0")] + [InlineData(".NETCoreApp", "v3.0", null, "macos", null, ".NETCoreApp,Version=v3.0")] + [InlineData(".NETCoreApp", "v3.1", null, null, "10.15.2.3", ".NETCoreApp,Version=v3.1")] + [InlineData(".NETCoreApp", "v3.1", "client", null, "10.15.2.3", ".NETCoreApp,Version=v3.1,Profile=Client")] + [InlineData(".NETCoreApp", "v3.1", "client", null, null, ".NETCoreApp,Version=v3.1,Profile=Client")] + [InlineData(".NETCoreApp", "v3.0", "client", "Windows", "7.0", ".NETCoreApp,Version=v3.0,Profile=Client")] public void NuGetFramework_Basic(string tfi, string tfv, string tfp, string tpi, string tpv, string fullName) { string output = NuGetFramework.ParseComponents(tfi, tfv, tfp, tpi, tpv).DotNetFrameworkName; Assert.Equal(fullName, output); } + + [Theory] + [InlineData(null, "v1.0", null, "android", "v21.0")] + [InlineData(".NETCoreApp", "vklmnfkjdfn5.0", null, null, null)] + [InlineData(".NETCoreApp", "v5.0", null, "plat", "badversion")] + public void NuGetFramework_WithInvalidProperties_Throws(string tfi, string tfv, string tfp, string tpi, string tpv) + { + Assert.ThrowsAny(() => NuGetFramework.ParseComponents(tfi, tfv, tfp, tpi, tpv)); + } } }