diff --git a/src/Build.UnitTests/Evaluation/Expander_Tests.cs b/src/Build.UnitTests/Evaluation/Expander_Tests.cs index 8501b7297f9..9864deef770 100644 --- a/src/Build.UnitTests/Evaluation/Expander_Tests.cs +++ b/src/Build.UnitTests/Evaluation/Expander_Tests.cs @@ -4295,6 +4295,19 @@ private void TestPropertyFunction(string expression, string propertyName, string result.ShouldBe(expected); } + [Theory] + [InlineData("net6.0", "netstandard2.0", "")] + [InlineData("net6.0-windows", "netstandard2.0", "")] + [InlineData("net6.0-windows", "net6.0", "net6.0-windows")] + [InlineData("netstandard2.0;net6.0", "net6.0", "net6.0")] + [InlineData("netstandard2.0;net6.0-windows", "net6.0", "net6.0-windows")] + [InlineData("netstandard2.0;net6.0-windows", "net6.0;netstandard2.0;net472", "netstandard2.0%3bnet6.0-windows")] + [InlineData("netstandard2.0;net472", "net6.0;netstandard2.0;net472", "netstandard2.0%3bnet472")] + public void PropertyFunctionFilterTargetFrameworks(string incoming, string filter, string expected) + { + TestPropertyFunction($"$([MSBuild]::FilterTargetFrameworks('{incoming}', '{filter}'))", "_", "_", expected); + } + [Fact] public void ExpandItemVectorFunctions_GetPathsOfAllDirectoriesAbove() { diff --git a/src/Build/Evaluation/IntrinsicFunctions.cs b/src/Build/Evaluation/IntrinsicFunctions.cs index ce0f37bbd56..cf17320a952 100644 --- a/src/Build/Evaluation/IntrinsicFunctions.cs +++ b/src/Build/Evaluation/IntrinsicFunctions.cs @@ -549,6 +549,11 @@ internal static string GetTargetPlatformVersion(string tfm, int versionPartCount return NuGetFramework.Value.GetTargetPlatformVersion(tfm, versionPartCount); } + internal static string FilterTargetFrameworks(string incoming, string filter) + { + return NuGetFramework.Value.FilterTargetFrameworks(incoming, filter); + } + internal static bool AreFeaturesEnabled(Version wave) { return ChangeWaves.AreFeaturesEnabled(wave); diff --git a/src/Build/Utilities/NuGetFrameworkWrapper.cs b/src/Build/Utilities/NuGetFrameworkWrapper.cs index 9d3546fcf37..e7760b0ae76 100644 --- a/src/Build/Utilities/NuGetFrameworkWrapper.cs +++ b/src/Build/Utilities/NuGetFrameworkWrapper.cs @@ -2,8 +2,11 @@ // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; +using System.Text; using Microsoft.Build.Framework; using Microsoft.Build.Shared; @@ -27,6 +30,7 @@ internal class NuGetFrameworkWrapper private static PropertyInfo VersionProperty; private static PropertyInfo PlatformProperty; private static PropertyInfo PlatformVersionProperty; + private static PropertyInfo AllFrameworkVersionsProperty; public NuGetFrameworkWrapper() { @@ -47,6 +51,7 @@ public NuGetFrameworkWrapper() VersionProperty = NuGetFramework.GetProperty("Version"); PlatformProperty = NuGetFramework.GetProperty("Platform"); PlatformVersionProperty = NuGetFramework.GetProperty("PlatformVersion"); + AllFrameworkVersionsProperty = NuGetFramework.GetProperty("AllFrameworkVersions"); } catch { @@ -91,5 +96,42 @@ private string GetNonZeroVersionParts(Version version, int minVersionPartCount) var nonZeroVersionParts = version.Revision == 0 ? version.Build == 0 ? version.Minor == 0 ? 1 : 2 : 3 : 4; return version.ToString(Math.Max(nonZeroVersionParts, minVersionPartCount)); } + + public string FilterTargetFrameworks(string incoming, string filter) + { + IEnumerable<(string originalTfm, object parsedTfm)> incomingFrameworks = ParseTfms(incoming); + IEnumerable<(string originalTfm, object parsedTfm)> filterFrameworks = ParseTfms(filter); + StringBuilder tfmList = new StringBuilder(); + + // An incoming target framework from 'incoming' is kept if it is compatible with any of the desired target frameworks on 'filter' + foreach (var l in incomingFrameworks) + { + if (filterFrameworks.Any(r => + (FrameworkProperty.GetValue(l.parsedTfm) as string).Equals(FrameworkProperty.GetValue(r.parsedTfm) as string, StringComparison.OrdinalIgnoreCase) && + (((Convert.ToBoolean(AllFrameworkVersionsProperty.GetValue(l.parsedTfm))) && (Convert.ToBoolean(AllFrameworkVersionsProperty.GetValue(r.parsedTfm)))) || + ((VersionProperty.GetValue(l.parsedTfm) as Version) == (VersionProperty.GetValue(r.parsedTfm) as Version))))) + { + if (tfmList.Length == 0) + { + tfmList.Append(l.originalTfm); + } + else + { + tfmList.Append($";{l.originalTfm}"); + } + } + } + + return tfmList.ToString(); + + IEnumerable<(string originalTfm, object parsedTfm)> ParseTfms(string desiredTargetFrameworks) + { + return desiredTargetFrameworks.Split(new char[] {';'}, StringSplitOptions.RemoveEmptyEntries).Select(tfm => + { + (string originalTfm, object parsedTfm) parsed = (tfm, Parse(tfm)); + return parsed; + }); + } + } } }