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)
{