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 0a46936983fe..48a77ef5f22f 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 @@ -41,43 +41,49 @@ NOTE: This file is imported from the following contexts, so be aware when writin false + + + <_ToolRidsAreOnlyShims>false + <_ToolRidsAreOnlyShims Condition="'$(RuntimeIdentifiers)' == '' and $(PackAsToolShimRuntimeIdentifiers) != '' ">true + <_UserSpecifiedToolPackageRids Condition="'$(ToolPackageRuntimeIdentifiers)' != ''">$(ToolPackageRuntimeIdentifiers) + <_UserSpecifiedToolPackageRids Condition="'$(_UserSpecifiedToolPackageRids)' == ''">$(RuntimeIdentifiers) + <_HasRIDSpecificTools Condition=" '$(_UserSpecifiedToolPackageRids)' != '' ">true + <_HasRIDSpecificTools Condition="'$(_HasRIDSpecificTools)' == ''">false + $(_UserSpecifiedToolPackageRids);$(PackAsToolShimRuntimeIdentifiers) + + <_IsRidSpecific>false + <_IsRidSpecific Condition="'$(RuntimeIdentifier)' != '' and '$(RuntimeIdentifier)' != 'any'">true + + - false + false - false - false - false - false + false + false + false + false <_InnerToolsPublishAot>false - <_InnerToolsPublishAot Condition="'$(RuntimeIdentifier)' == '' and '$(PublishAot)' == 'true'">true - false + <_InnerToolsPublishAot Condition="$(_HasRIDSpecificTools) and '$(PublishAot)' == 'true'">true + false - false + false - true - - - <_ToolRidsAreOnlyShims>false - <_ToolRidsAreOnlyShims Condition="'$(RuntimeIdentifiers)' == '' and $(PackAsToolShimRuntimeIdentifiers) != '' ">true - <_UserSpecifiedToolPackageRids Condition="'$(ToolPackageRuntimeIdentifiers)' != ''">$(ToolPackageRuntimeIdentifiers) - <_UserSpecifiedToolPackageRids Condition="'$(_UserSpecifiedToolPackageRids)' == ''">$(RuntimeIdentifiers) - <_HasRIDSpecificTools Condition=" '$(_UserSpecifiedToolPackageRids)' != '' ">true - <_HasRIDSpecificTools Condition="'$(_HasRIDSpecificTools)' == ''">false - $(_UserSpecifiedToolPackageRids);$(PackAsToolShimRuntimeIdentifiers) + true <_ToolPackageShouldIncludeImplementation Condition=" '$(PackAsTool)' == 'true' And - ('$(_UserSpecifiedToolPackageRids)' == '' Or '$(RuntimeIdentifier)' != '')">true + ( '$(_UserSpecifiedToolPackageRids)' == '' + or '$(RuntimeIdentifier)' != '')">true <_ToolPackageShouldIncludeImplementation Condition="'$(_ToolPackageShouldIncludeImplementation)' == ''">false @@ -129,14 +135,13 @@ NOTE: This file is imported from the following contexts, so be aware when writin $(TargetFileName) - $(AssemblyName)$(_NativeExecutableExtension) + $(AssemblyName)$(_NativeExecutableExtension) - + $(PackageId).$(RuntimeIdentifier) - @@ -385,14 +390,17 @@ NOTE: This file is imported from the following contexts, so be aware when writin - + <_PackageRids>$(ToolPackageRuntimeIdentifiers) <_PackageRids Condition="'$(_PackageRids)' == ''">$(RuntimeIdentifiers) - <_rids Include="$(_PackageRids)" /> <_RidSpecificToolPackageProject Include="$(MSBuildProjectFullPath)" AdditionalProperties="RuntimeIdentifier=%(_rids.Identity);" /> diff --git a/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs b/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs index 67c16da3a37f..63c4aeae4eb4 100644 --- a/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs +++ b/test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs @@ -223,6 +223,94 @@ public void PackagesFrameworkDependentRidSpecificPackagesCorrectly() foundRids.Should().BeEquivalentTo(expectedRids, "The top-level package should declare all of the RIDs for the tools it contains"); } + [Fact] + public void PackageToolWithAnyRid() + { + var toolSettings = new TestToolBuilder.TestToolSettings() + { + RidSpecific = true, + IncludeAnyRid = 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 + 1, "There should be one package for the tool-wrapper, one for the top-level manifest, 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") + .And.Satisfy(EnsurePackageIsAnExecutable); + } + + // Ensure that the package with the "any" RID is present + var anyRidPackage = packages.FirstOrDefault(p => p.EndsWith($"{packageIdentifier}.any.{toolSettings.ToolPackageVersion}.nupkg")); + anyRidPackage.Should().NotBeNull($"Package {packageIdentifier}.any.{toolSettings.ToolPackageVersion}.nupkg should be present in the tool packages directory") + .And.Satisfy(EnsurePackageIsFdd); + + // top-level package should declare all of the rids + var topLevelPackage = packages.First(p => p.EndsWith($"{packageIdentifier}.{toolSettings.ToolPackageVersion}.nupkg")); + var settingsXml = GetToolSettingsFile(topLevelPackage); + var packageNodes = GetRidsInSettingsFile(settingsXml); + + packageNodes.Should().BeEquivalentTo([.. expectedRids, "any"], "The top-level package should declare all of the RIDs for the tools it contains"); + } + + [Fact] + public void InstallAndRunToolFromAnyRid() + { + var toolSettings = new TestToolBuilder.TestToolSettings() + { + IncludeAnyRid = true // will make one package with the "any" RID + }; + string toolPackagesPath = ToolBuilder.CreateTestTool(Log, toolSettings, collectBinlogs: true); + var packages = Directory.GetFiles(toolPackagesPath, "*.nupkg").Select(p => Path.GetFileName(p)).ToArray(); + packages.Should().BeEquivalentTo([ + $"{toolSettings.ToolPackageId}.{toolSettings.ToolPackageVersion}.nupkg", + $"{toolSettings.ToolPackageId}.any.{toolSettings.ToolPackageVersion}.nupkg" + ], "There should be two packages: one for the tool-wrapper and one for the 'any' RID"); + var testDirectory = _testAssetsManager.CreateTestDirectory(); + var homeFolder = Path.Combine(testDirectory.Path, "home"); + + new DotnetToolCommand(Log, "exec", toolSettings.ToolPackageId, "--yes", "--add-source", toolPackagesPath) + .WithEnvironmentVariables(homeFolder) + .WithWorkingDirectory(testDirectory.Path) + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Hello Tool!"); + } + + [Fact] + public void InstallAndRunToolFromAnyRidWhenOtherRidsArePresentButIncompatible() + { + var toolSettings = new TestToolBuilder.TestToolSettings() + { + IncludeCurrentRid = false, + RidSpecific = true, // will make one package for each RID except the current RID + IncludeAnyRid = true // will make one package with the "any" RID + }; + List expectedRids = [ .. ToolsetInfo.LatestRuntimeIdentifiers.Split(';').Where(rid => rid != RuntimeInformation.RuntimeIdentifier), "any"]; + + string toolPackagesPath = ToolBuilder.CreateTestTool(Log, toolSettings, collectBinlogs: true); + var packages = Directory.GetFiles(toolPackagesPath, "*.nupkg").Select(p => Path.GetFileName(p)).ToArray(); + packages.Should().BeEquivalentTo([ + $"{toolSettings.ToolPackageId}.{toolSettings.ToolPackageVersion}.nupkg", + .. expectedRids.Select(rid => $"{toolSettings.ToolPackageId}.{rid}.{toolSettings.ToolPackageVersion}.nupkg"), + ], $"There should be { 1 + expectedRids.Count } packages: one for the tool-wrapper and one for each RID except the current RID"); + var testDirectory = _testAssetsManager.CreateTestDirectory(); + var homeFolder = Path.Combine(testDirectory.Path, "home"); + + new DotnetToolCommand(Log, "exec", toolSettings.ToolPackageId, "--yes", "--add-source", toolPackagesPath) + .WithEnvironmentVariables(homeFolder) + .WithWorkingDirectory(testDirectory.Path) + .Execute() + .Should().Pass() + .And.HaveStdOutContaining("Hello Tool!"); + } private void EnsurePackageIsFdd(string packagePath) { diff --git a/test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs b/test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs index 666ac9ff0fbf..30cbe8b1c762 100644 --- a/test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs +++ b/test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs @@ -1,10 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; -using System.Collections.Generic; -using System.Text; - namespace Microsoft.DotNet.PackageInstall.Tests { [CollectionDefinition(nameof(TestToolBuilderCollection))] @@ -35,10 +31,11 @@ public class TestToolSettings public bool NativeAOT { get; set { field = value; this.RidSpecific = value; } } = false; public bool SelfContained { get; set { field = value; this.RidSpecific = value; } } = false; public bool Trimmed { get; set { field = value; this.RidSpecific = value; } } = false; - public bool IncludeAnyRid { get; set { field = value; this.RidSpecific = value; } } = false; + public bool IncludeAnyRid { get; set { field = value; } } = false; public bool RidSpecific { get; set; } = false; + public bool IncludeCurrentRid { get; set; } = true; - public string GetIdentifier() => $"{ToolPackageId}-{ToolPackageVersion}-{ToolCommandName}-{(NativeAOT ? "nativeaot" : SelfContained ? "selfcontained" : Trimmed ? "trimmed" : "managed")}{(RidSpecific ? "-specific" : "")}{(IncludeAnyRid ? "-anyrid" : "")}"; + public string GetIdentifier() => $"{ToolPackageId}-{ToolPackageVersion}-{ToolCommandName}-{(NativeAOT ? "nativeaot" : SelfContained ? "selfcontained" : Trimmed ? "trimmed" : "managed")}{(RidSpecific ? "-specific" : "")}{(IncludeAnyRid ? "-anyrid" : "")}{(IncludeCurrentRid ? "" : "-no-current-rid")}"; } @@ -57,13 +54,18 @@ public string CreateTestTool(ITestOutputHelper log, TestToolSettings toolSetting testProject.AdditionalProperties["ImplicitUsings"] = "enable"; testProject.AdditionalProperties["Version"] = toolSettings.ToolPackageVersion; - var singleRid = RuntimeInformation.RuntimeIdentifier; - var multiRid = toolSettings.IncludeAnyRid ? $"{ToolsetInfo.LatestRuntimeIdentifiers};any" : ToolsetInfo.LatestRuntimeIdentifiers; + var multiRid = toolSettings.IncludeCurrentRid ? ToolsetInfo.LatestRuntimeIdentifiers : ToolsetInfo.LatestRuntimeIdentifiers.Replace(RuntimeInformation.RuntimeIdentifier, string.Empty).Trim(';'); if (toolSettings.RidSpecific) { testProject.AdditionalProperties["RuntimeIdentifiers"] = multiRid; } + if (toolSettings.IncludeAnyRid) + { + testProject.AdditionalProperties["RuntimeIdentifiers"] = testProject.AdditionalProperties.TryGetValue("RuntimeIdentifiers", out var existingRids) + ? $"{existingRids};any" + : "any"; + } if (toolSettings.NativeAOT) {