From 3bf1a2b2e6082a80e3f63df722e9efc4989bc3f9 Mon Sep 17 00:00:00 2001 From: Chet Husk Date: Thu, 19 Jun 2025 18:09:02 -0500 Subject: [PATCH] Keep Publish* properties across Restore so that we can gather all required implicit packages --- .../targets/Microsoft.NET.PackTool.targets | 12 +++-- .../EndToEndToolTests.cs | 49 ++++++++++++++++++- .../TestToolBuilder.cs | 9 +++- 3 files changed, 63 insertions(+), 7 deletions(-) diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets index 83e3f08c54a6..2b7615f39f0d 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets @@ -42,13 +42,15 @@ NOTE: This file is imported from the following contexts, so be aware when writin false - false + + false - false - false - false - false + false + false + false + false diff --git a/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs b/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs index 2dd0275db5e3..e05ed084199e 100644 --- a/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs +++ b/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs @@ -154,7 +154,7 @@ public void PackagesMultipleToolsWithASingleInvocation() { var packageName = $"{toolSettings.ToolPackageId}.{rid}.{toolSettings.ToolPackageVersion}"; var package = packages.FirstOrDefault(p => p.EndsWith(packageName + ".nupkg")); - packages.Should().NotBeNull($"Package {packageName} should be present in the tool packages directory"); + package.Should().NotBeNull($"Package {packageName} should be present in the tool packages directory"); } // top-level package should declare all of the rids @@ -170,6 +170,53 @@ public void PackagesMultipleToolsWithASingleInvocation() .Select(e => (e as XElement)!.Attributes().First(a => a.Name == "RuntimeIdentifier").Value); packageNodes.Should().BeEquivalentTo(expectedRids, "The top-level package should declare all of the RIDs for the tools it contains"); } + + [Fact] + public void PackagesMultipleTrimmedToolsWithASingleInvocation() + { + var toolSettings = new TestToolBuilder.TestToolSettings() + { + Trimmed = true + }; + string toolPackagesPath = ToolBuilder.CreateTestTool(Log, toolSettings); + + var packages = Directory.GetFiles(toolPackagesPath, "*.nupkg"); + var packageIdentifier = toolSettings.ToolPackageId; + var expectedRids = ToolsetInfo.LatestRuntimeIdentifiers.Split(';'); + + packages.Length.Should().Be(expectedRids.Length + 1, "There should be one package for the tool-wrapper and one for each RID"); + foreach (string rid in expectedRids) + { + var packageName = $"{toolSettings.ToolPackageId}.{rid}.{toolSettings.ToolPackageVersion}"; + var package = packages.FirstOrDefault(p => p.EndsWith(packageName + ".nupkg")); + package.Should().NotBeNull($"Package {packageName} should be present in the tool packages directory"); + EnsurePackageLacksTrimmedDependency(package!, "System.Xml.dll"); + } + + // top-level package should declare all of the rids + var topLevelPackage = packages.First(p => p.EndsWith($"{packageIdentifier}.{toolSettings.ToolPackageVersion}.nupkg")); + using var zipArchive = ZipFile.OpenRead(topLevelPackage); + var nuspecEntry = zipArchive.GetEntry($"tools/{ToolsetInfo.CurrentTargetFramework}/any/DotnetToolSettings.xml")!; + var stream = nuspecEntry.Open(); + var xml = XDocument.Load(stream, LoadOptions.None); + var packageNodes = + (xml.Root!.Nodes() + .First(n => n is XElement e && e.Name == "RuntimeIdentifierPackages") as XElement)!.Nodes() + .Where(n => (n as XElement)!.Name == "RuntimeIdentifierPackage") + .Select(e => (e as XElement)!.Attributes().First(a => a.Name == "RuntimeIdentifier").Value); + packageNodes.Should().BeEquivalentTo(expectedRids, "The top-level package should declare all of the RIDs for the tools it contains"); + } + + /// + /// Opens the nupkg and verifies that it does not contain a dependency on the given dll. + /// + private void EnsurePackageLacksTrimmedDependency(string packagePath, string dll) + { + using var zipArchive = ZipFile.OpenRead(packagePath); + zipArchive.Entries.Should().NotContain( + e => e.FullName.EndsWith(dll, StringComparison.OrdinalIgnoreCase), + $"The package {Path.GetFileName(packagePath)} should not contain a dependency on {dll}."); + } } static class EndToEndToolTestExtensions diff --git a/test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs b/test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs index 0e601b0ca5f4..ff69a2f5c71c 100644 --- a/test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs +++ b/test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs @@ -34,8 +34,9 @@ public class TestToolSettings public bool NativeAOT { get; set; } = false; public bool SelfContained { get; set; } = false; + public bool Trimmed { get; set; } = false; - public string GetIdentifier() => $"{ToolPackageId}-{ToolPackageVersion}-{ToolCommandName}-{(NativeAOT ? "nativeaot" : SelfContained ? "selfcontained" : "managed")}"; + public string GetIdentifier() => $"{ToolPackageId}-{ToolPackageVersion}-{ToolCommandName}-{(NativeAOT ? "nativeaot" : SelfContained ? "selfcontained" : Trimmed ? "trimmed" : "managed")}"; } @@ -66,6 +67,12 @@ public string CreateTestTool(ITestOutputHelper log, TestToolSettings toolSetting testProject.AdditionalProperties["RuntimeIdentifiers"] = ToolsetInfo.LatestRuntimeIdentifiers; } + if (toolSettings.Trimmed) + { + testProject.AdditionalProperties["PublishTrimmed"] = "true"; + testProject.AdditionalProperties["RuntimeIdentifiers"] = ToolsetInfo.LatestRuntimeIdentifiers; + } + testProject.SourceFiles.Add("Program.cs", "Console.WriteLine(\"Hello Tool!\");"); var testAssetManager = new TestAssetsManager(log);