From 686efaf93a63969351ff0bbde7edad33c2d3dea7 Mon Sep 17 00:00:00 2001 From: Daniel Plaisted Date: Tue, 14 Jan 2025 15:44:32 -0500 Subject: [PATCH] Fix issue handling transitive framework references for pruning in VS design-time builds, and add tests --- .../GetPackagesToPrune.cs | 11 +++- .../GivenThatWeWantToResolveConflicts.cs | 51 +++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/GetPackagesToPrune.cs b/src/Tasks/Microsoft.NET.Build.Tasks/GetPackagesToPrune.cs index 141520c8d9cf..07d087be1721 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/GetPackagesToPrune.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/GetPackagesToPrune.cs @@ -65,11 +65,18 @@ public override int GetHashCode() protected override void ExecuteCore() { + + // Filter out transitive framework references. Normally they wouldn't be passed to this task, but in Visual Studio design-time builds + // the ResolvePackageAssets and AddTransitiveFrameworkReferences targets may have already run. Filtering these references out should + // avoid a bug similar to https://github.com/dotnet/sdk/issues/14641 + var filteredFrameworkReferences = FrameworkReferences.Where( + i => i.GetMetadata("IsTransitiveFrameworkReference") is string transitiveVal && !transitiveVal.Equals("true", StringComparison.OrdinalIgnoreCase)).ToList(); + CacheKey key = new() { TargetFrameworkIdentifier = TargetFrameworkIdentifier, TargetFrameworkVersion = TargetFrameworkVersion, - FrameworkReferences = FrameworkReferences.Select(i => i.ItemSpec).ToHashSet() + FrameworkReferences = filteredFrameworkReferences.Select(i => i.ItemSpec).ToHashSet() }; // Cache framework package values per build @@ -84,7 +91,7 @@ protected override void ExecuteCore() Dictionary packagesToPrune = new(); - var frameworkPackages = FrameworkPackages.GetFrameworkPackages(nugetFramework, FrameworkReferences.Select(fr => fr.ItemSpec).ToArray(), TargetingPackRoot) + var frameworkPackages = FrameworkPackages.GetFrameworkPackages(nugetFramework, filteredFrameworkReferences.Select(fr => fr.ItemSpec).ToArray(), TargetingPackRoot) .SelectMany(packages => packages); foreach (var kvp in frameworkPackages) diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToResolveConflicts.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToResolveConflicts.cs index bb3754ab3e7f..02b49229bab8 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToResolveConflicts.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToResolveConflicts.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.Json.Nodes; using System.Text.RegularExpressions; using NuGet.Common; using NuGet.Frameworks; @@ -301,5 +302,55 @@ public void PlatformPackagesCanBePruned(bool prunePackages) lockFileTarget.Libraries.Should().Contain(library => library.Name.Equals("System.Text.Json", StringComparison.OrdinalIgnoreCase)); } } + + [Fact] + public void TransitiveFrameworkReferencesDoNotAffectPruning() + { + var referencedProject = new TestProject("ReferencedProject") + { + TargetFrameworks = ToolsetInfo.CurrentTargetFramework, + IsExe = false + }; + referencedProject.PackageReferences.Add(new TestPackageReference("System.Text.Json", "8.0.0")); + referencedProject.FrameworkReferences.Add("Microsoft.AspNetCore.App"); + + var testProject = new TestProject() + { + TargetFrameworks = ToolsetInfo.CurrentTargetFramework + }; + + testProject.AdditionalProperties["RestoreEnablePackagePruning"] = "True"; + testProject.ReferencedProjects.Add(referencedProject); + + var testAsset = _testAssetsManager.CreateTestProject(testProject); + + new BuildCommand(testAsset).Execute().Should().Pass(); + + var getItemsCommand1 = new MSBuildCommand(testAsset, "AddPrunePackageReferences"); + var itemsResult1 = getItemsCommand1.Execute("-getItem:PrunePackageReference"); + itemsResult1.Should().Pass(); + + var items1 = ParseItemsJson(itemsResult1.StdOut); + + var getItemsCommand2 = new MSBuildCommand(testAsset, "ResolvePackageAssets;AddTransitiveFrameworkReferences;AddPrunePackageReferences"); + var itemsResult2 = getItemsCommand2.Execute("-getItem:PrunePackageReference"); + itemsResult2.Should().Pass(); + + var items2 = ParseItemsJson(itemsResult2.StdOut); + + items2.Should().BeEquivalentTo(items1); + + static List> ParseItemsJson(string json) + { + List> ret = new(); + var root = JsonNode.Parse(json); + var items = (JsonArray) root["Items"]["PrunePackageReference"]; + foreach (var item in items) + { + ret.Add(new KeyValuePair((string)item["Identity"], (string)item["Version"])); + } + return ret; + } + } } }