diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/CreateWindowsSdkKnownFrameworkReferences.cs b/src/Tasks/Microsoft.NET.Build.Tasks/CreateWindowsSdkKnownFrameworkReferences.cs index 94a65656ee68..7b2e2a433f50 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/CreateWindowsSdkKnownFrameworkReferences.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/CreateWindowsSdkKnownFrameworkReferences.cs @@ -55,6 +55,9 @@ protected override void ExecuteCore() else { var normalizedTargetFrameworkVersion = ProcessFrameworkReferences.NormalizeVersion(new Version(TargetFrameworkVersion)); + + var knownFrameworkReferencesByWindowsSdkVersion = new Dictionary>(); + foreach (var supportedWindowsVersion in WindowsSdkSupportedTargetPlatformVersions) { var windowsSdkPackageVersion = supportedWindowsVersion.GetMetadata("WindowsSdkPackageVersion"); @@ -62,18 +65,38 @@ protected override void ExecuteCore() if (!string.IsNullOrEmpty(windowsSdkPackageVersion)) { var minimumNETVersion = supportedWindowsVersion.GetMetadata("MinimumNETVersion"); + Version normalizedMinimumVersion = new Version(0, 0, 0); if (!string.IsNullOrEmpty(minimumNETVersion)) { - var normalizedMinimumVersion = ProcessFrameworkReferences.NormalizeVersion(new Version(minimumNETVersion)); + normalizedMinimumVersion = ProcessFrameworkReferences.NormalizeVersion(new Version(minimumNETVersion)); if (normalizedMinimumVersion > normalizedTargetFrameworkVersion) { continue; } } - knownFrameworkReferences.Add(CreateKnownFrameworkReference(windowsSdkPackageVersion, TargetFrameworkVersion, supportedWindowsVersion.ItemSpec)); + if (!Version.TryParse(supportedWindowsVersion.ItemSpec, out var windowsSdkVersionParsed)) + { + continue; + } + + if (!knownFrameworkReferencesByWindowsSdkVersion.ContainsKey(windowsSdkVersionParsed)) + { + knownFrameworkReferencesByWindowsSdkVersion[windowsSdkVersionParsed] = new(); + } + + knownFrameworkReferencesByWindowsSdkVersion[windowsSdkVersionParsed].Add((normalizedMinimumVersion, CreateKnownFrameworkReference(windowsSdkPackageVersion, TargetFrameworkVersion, supportedWindowsVersion.ItemSpec))); + } } + + foreach (var knownFrameworkReferencesForSdkVersion in knownFrameworkReferencesByWindowsSdkVersion.Values) + { + // If there are multiple WindowsSdkSupportedTargetPlatformVersion items for the same Windows SDK version, choose the one with the highest minimum version. + // That way it is possible to use older packages when targeting older versions of .NET, and newer packages for newer versions of .NET + var highestMinimumVersion = knownFrameworkReferencesForSdkVersion.Max(t => t.minimumNetVersion); + knownFrameworkReferences.AddRange(knownFrameworkReferencesForSdkVersion.Where(t => t.minimumNetVersion == highestMinimumVersion).Select(t => t.knownFrameworkReference)); + } } KnownFrameworkReferences = knownFrameworkReferences.ToArray(); diff --git a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildAWindowsDesktopProject.cs b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildAWindowsDesktopProject.cs index 267654705889..b6a6ec6e22a4 100644 --- a/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildAWindowsDesktopProject.cs +++ b/src/Tests/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildAWindowsDesktopProject.cs @@ -403,6 +403,55 @@ public void ItUsesCorrectWindowsSdkPackVersion(string targetFramework, bool? use var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework + useWindowsSDKPreview + windowsSdkPackageVersion); + string referencedWindowsSdkVersion = GetReferencedWindowsSdkVersion(testAsset); + + // The patch version of the Windows SDK Ref pack will change over time, so we use a '*' in the expected version to indicate that and replace it with + // the 4th part of the version number of the resolved package. + if (expectedWindowsSdkPackageVersion.Contains('*')) + { + expectedWindowsSdkPackageVersion = expectedWindowsSdkPackageVersion.Replace("*", new Version(referencedWindowsSdkVersion).Revision.ToString()); + } + + referencedWindowsSdkVersion.Should().Be(expectedWindowsSdkPackageVersion); + } + + [WindowsOnlyTheory] + [InlineData("net5.0-windows10.0.22000.0", "10.0.22000.25")] + [InlineData("net6.0-windows10.0.22000.0", "10.0.22000.26")] + [InlineData("net6.0-windows10.0.19041.0", "10.0.19041.25")] + public void ItUsesTheHighestMatchingWindowsSdkPackageVersion(string targetFramework, string expectedWindowsSdkPackageVersion) + { + var testProject = new TestProject() + { + TargetFrameworks = targetFramework + }; + + var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework) + .WithProjectChanges(project => + { + // Add items for available SDK versions for test + var testItems = XElement.Parse(@" + + + + + + + + + + "); + + project.Root.Add(testItems); + }); + + string referencedWindowsSdkVersion = GetReferencedWindowsSdkVersion(testAsset); + referencedWindowsSdkVersion.Should().Be(expectedWindowsSdkPackageVersion); + + } + + private string GetReferencedWindowsSdkVersion(TestAsset testAsset) + { var getValueCommand = new GetValuesCommand(testAsset, "PackageDownload", GetValuesCommand.ValueType.Item); getValueCommand.ShouldRestore = false; getValueCommand.DependsOnTargets = "_CheckForInvalidConfigurationAndPlatform;CollectPackageDownloads"; @@ -419,15 +468,7 @@ public void ItUsesCorrectWindowsSdkPackVersion(string targetFramework, bool? use packageDownloadVersion[0].Should().Be('['); packageDownloadVersion.Last().Should().Be(']'); - // The patch version of the Windows SDK Ref pack will change over time, so we use a '*' in the expected version to indicate that and replace it with - // the 4th part of the version number of the resolved package. - var trimmedPackageDownloadVersion = packageDownloadVersion.Substring(1, packageDownloadVersion.Length - 2); - if (expectedWindowsSdkPackageVersion.Contains('*')) - { - expectedWindowsSdkPackageVersion = expectedWindowsSdkPackageVersion.Replace("*", new Version(trimmedPackageDownloadVersion).Revision.ToString()); - } - - trimmedPackageDownloadVersion.Should().Be(expectedWindowsSdkPackageVersion); + return packageDownloadVersion.Substring(1, packageDownloadVersion.Length - 2); } private string GetPropertyValue(TestAsset testAsset, string propertyName)