Skip to content

Commit 13a6648

Browse files
committed
Enable publishing FDD for any RID
1 parent b348571 commit 13a6648

File tree

3 files changed

+119
-23
lines changed

3 files changed

+119
-23
lines changed

src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.PackTool.targets

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,30 @@ NOTE: This file is imported from the following contexts, so be aware when writin
4141
<!-- tools are specially-formatted packages, so we tell nuget Pack to not even try to include build output -->
4242
<IncludeBuildOutput>false</IncludeBuildOutput>
4343

44+
<_IsRidSpecific>false</_IsRidSpecific>
45+
<_IsRidSpecific Condition="'$(RuntimeIdentifier)' != '' and '$(RuntimeIdentifier)' != 'any'">true</_IsRidSpecific>
46+
4447
<!-- the publish* properties _can_ be set, but only for the 'inner' RID-specific builds. We need to make sure that for the outer, agnostic build they are unset -->
4548
<!-- RID information is also stripped during Restore, so we need to make sure user
4649
decisions are preserved when Restoring, so that publishing-related packages are implicitly included. -->
47-
<PublishSelfContained Condition="'$(RuntimeIdentifier)' == '' and '$(MSBuildIsRestoring)' != 'true'">false</PublishSelfContained>
50+
<PublishSelfContained Condition="!$(_IsRidSpecific) and '$(MSBuildIsRestoring)' != 'true'">false</PublishSelfContained>
4851
<!-- Have to set SelfContained similarly because PackTool targets are imported _after_ RuntimeIdentifierInference targets, where the Publish* properties are
4952
forwarded to the 'base' properties. -->
50-
<SelfContained Condition="'$(RuntimeIdentifier)' == '' and '$(MSBuildIsRestoring)' != 'true'">false</SelfContained>
51-
<PublishTrimmed Condition="'$(RuntimeIdentifier)' == '' and '$(MSBuildIsRestoring)' != 'true'">false</PublishTrimmed>
52-
<PublishReadyToRun Condition="'$(RuntimeIdentifier)' == '' and '$(MSBuildIsRestoring)' != 'true'">false</PublishReadyToRun>
53-
<PublishSingleFile Condition="'$(RuntimeIdentifier)' == '' and '$(MSBuildIsRestoring)' != 'true'">false</PublishSingleFile>
53+
<SelfContained Condition="!$(_IsRidSpecific) and '$(MSBuildIsRestoring)' != 'true'">false</SelfContained>
54+
<PublishTrimmed Condition="!$(_IsRidSpecific) and '$(MSBuildIsRestoring)' != 'true'">false</PublishTrimmed>
55+
<PublishReadyToRun Condition="!$(_IsRidSpecific) and '$(MSBuildIsRestoring)' != 'true'">false</PublishReadyToRun>
56+
<PublishSingleFile Condition="!$(_IsRidSpecific) and '$(MSBuildIsRestoring)' != 'true'">false</PublishSingleFile>
5457

5558
<!-- We need to know if the inner builds are _intended_ to be AOT even if we then explicitly disable AOT for the outer builds.
5659
Knowing this lets us correctly decide to create the RID-specific inner tools or not when packaging the outer tool. -->
5760
<_InnerToolsPublishAot>false</_InnerToolsPublishAot>
58-
<_InnerToolsPublishAot Condition="'$(RuntimeIdentifier)' == '' and '$(PublishAot)' == 'true'">true</_InnerToolsPublishAot>
59-
<PublishAot Condition="'$(RuntimeIdentifier)' == ''">false</PublishAot>
61+
<_InnerToolsPublishAot Condition="$(_IsRidSpecific) and '$(PublishAot)' == 'true'">true</_InnerToolsPublishAot>
62+
<PublishAot Condition="!$(_IsRidSpecific) and '$(MSBuildIsRestoring)' != 'true'">false</PublishAot>
6063

6164
<!-- we want to ensure that we don't publish any AppHosts for the 'outer', RID-agnostic builds -->
62-
<UseAppHost Condition="'$(RuntimeIdentifier)' == ''">false</UseAppHost>
65+
<UseAppHost Condition="!$(_IsRidSpecific)">false</UseAppHost>
6366
<!-- we want to ensure that we _do_ publish any AppHosts for the 'inner', RID-specific builds -->
64-
<UseAppHost Condition="'$(RuntimeIdentifier)' != ''">true</UseAppHost>
67+
<UseAppHost Condition="$(_IsRidSpecific)">true</UseAppHost>
6568

6669
<!-- If shims are included, we need to make sure we restore for those RIDs so the apphost shims are available during restore/publish.
6770
This means that we need to set RuntimeIdentifiers. However we need to track certain information about the _users_ decisions around RIDs
@@ -77,7 +80,8 @@ NOTE: This file is imported from the following contexts, so be aware when writin
7780
<!-- Tool implementation files are not included in the primary package when the tool has RID-specific packages. So only pack the tool implementation
7881
(and only depend on publish) if there are no RID-specific packages, or if the RuntimeIdentifier is set. -->
7982
<_ToolPackageShouldIncludeImplementation Condition=" '$(PackAsTool)' == 'true' And
80-
('$(_UserSpecifiedToolPackageRids)' == '' Or '$(RuntimeIdentifier)' != '')">true</_ToolPackageShouldIncludeImplementation>
83+
( '$(_UserSpecifiedToolPackageRids)' == ''
84+
or '$(RuntimeIdentifier)' != '')">true</_ToolPackageShouldIncludeImplementation>
8185
<_ToolPackageShouldIncludeImplementation Condition="'$(_ToolPackageShouldIncludeImplementation)' == ''">false</_ToolPackageShouldIncludeImplementation>
8286

8387
<!-- inner builds and non-RID-specific outer builds need publish content-->
@@ -129,14 +133,13 @@ NOTE: This file is imported from the following contexts, so be aware when writin
129133
<!-- Needs to be in a target so we don't need to worry about evaluation order with NativeBinary property -->
130134
<PropertyGroup Condition="'$(ToolEntryPoint)' == ''">
131135
<ToolEntryPoint>$(TargetFileName)</ToolEntryPoint>
132-
<ToolEntryPoint Condition="'$(RuntimeIdentifier)' != '' and '$(UseAppHost)' == 'true' ">$(AssemblyName)$(_NativeExecutableExtension)</ToolEntryPoint>
136+
<ToolEntryPoint Condition="$(_IsRidSpecific) and '$(UseAppHost)' == 'true' ">$(AssemblyName)$(_NativeExecutableExtension)</ToolEntryPoint>
133137
</PropertyGroup>
134138

135139
<!-- inner-build tool packages get a RID suffix -->
136-
<PropertyGroup Condition="'$(_HasRIDSpecificTools)' != '' And '$(RuntimeIdentifier)' != ''">
140+
<PropertyGroup Condition="'$(_HasRIDSpecificTools)' != '' And $(RuntimeIdentifier) != ''">
137141
<PackageId>$(PackageId).$(RuntimeIdentifier)</PackageId>
138142
</PropertyGroup>
139-
140143
</Target>
141144

142145
<Target Name="PackToolImplementation">
@@ -385,14 +388,17 @@ NOTE: This file is imported from the following contexts, so be aware when writin
385388

386389
<!-- Orchestrator for making the N RID-specific tool packages if this Tool supports that mode.
387390
We can't call this for AOT'd tools because we can't AOT cross-architecture and cross-platform in .NET today. -->
388-
<Target Name="_CreateRIDSpecificToolPackages" Condition="'$(RuntimeIdentifier)' == '' and $(_HasRIDSpecificTools) and !$(_InnerToolsPublishAot) and !$(_ToolPackageShouldIncludeImplementation)">
391+
<Target Name="_CreateRIDSpecificToolPackages"
392+
Condition="'$(RuntimeIdentifier)' == ''
393+
and $(_HasRIDSpecificTools)
394+
and !$(_InnerToolsPublishAot)
395+
and !$(_ToolPackageShouldIncludeImplementation)">
389396
<PropertyGroup>
390397
<_PackageRids>$(ToolPackageRuntimeIdentifiers)</_PackageRids>
391398
<_PackageRids Condition="'$(_PackageRids)' == ''">$(RuntimeIdentifiers)</_PackageRids>
392399
</PropertyGroup>
393400

394401
<ItemGroup>
395-
<!-- Build the RID-specific packages.-->
396402
<_rids Include="$(_PackageRids)" />
397403
<_RidSpecificToolPackageProject Include="$(MSBuildProjectFullPath)" AdditionalProperties="RuntimeIdentifier=%(_rids.Identity);" />
398404
</ItemGroup>

test/Microsoft.DotNet.PackageInstall.Tests/EndToEndToolTests.cs

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,94 @@ public void PackagesFrameworkDependentRidSpecificPackagesCorrectly()
223223
foundRids.Should().BeEquivalentTo(expectedRids, "The top-level package should declare all of the RIDs for the tools it contains");
224224
}
225225

226+
[Fact]
227+
public void PackageToolWithAnyRid()
228+
{
229+
var toolSettings = new TestToolBuilder.TestToolSettings()
230+
{
231+
RidSpecific = true,
232+
IncludeAnyRid = true
233+
};
234+
235+
string toolPackagesPath = ToolBuilder.CreateTestTool(Log, toolSettings);
236+
237+
var packages = Directory.GetFiles(toolPackagesPath, "*.nupkg");
238+
var packageIdentifier = toolSettings.ToolPackageId;
239+
var expectedRids = ToolsetInfo.LatestRuntimeIdentifiers.Split(';');
240+
241+
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");
242+
foreach (string rid in expectedRids)
243+
{
244+
var packageName = $"{toolSettings.ToolPackageId}.{rid}.{toolSettings.ToolPackageVersion}";
245+
var package = packages.FirstOrDefault(p => p.EndsWith(packageName + ".nupkg"));
246+
package.Should().NotBeNull($"Package {packageName} should be present in the tool packages directory")
247+
.And.Satisfy<string>(EnsurePackageIsAnExecutable);
248+
}
249+
250+
// Ensure that the package with the "any" RID is present
251+
var anyRidPackage = packages.FirstOrDefault(p => p.EndsWith($"{packageIdentifier}.any.{toolSettings.ToolPackageVersion}.nupkg"));
252+
anyRidPackage.Should().NotBeNull($"Package {packageIdentifier}.any.{toolSettings.ToolPackageVersion}.nupkg should be present in the tool packages directory")
253+
.And.Satisfy<string>(EnsurePackageIsFdd);
254+
255+
// top-level package should declare all of the rids
256+
var topLevelPackage = packages.First(p => p.EndsWith($"{packageIdentifier}.{toolSettings.ToolPackageVersion}.nupkg"));
257+
var settingsXml = GetToolSettingsFile(topLevelPackage);
258+
var packageNodes = GetRidsInSettingsFile(settingsXml);
259+
260+
packageNodes.Should().BeEquivalentTo([.. expectedRids, "any"], "The top-level package should declare all of the RIDs for the tools it contains");
261+
}
262+
263+
[Fact]
264+
public void InstallAndRunToolFromAnyRid()
265+
{
266+
var toolSettings = new TestToolBuilder.TestToolSettings()
267+
{
268+
IncludeAnyRid = true // will make one package with the "any" RID
269+
};
270+
string toolPackagesPath = ToolBuilder.CreateTestTool(Log, toolSettings);
271+
var packages = Directory.GetFiles(toolPackagesPath, "*.nupkg").Select(p => Path.GetFileName(p)).ToArray();
272+
packages.Should().BeEquivalentTo([
273+
$"{toolSettings.ToolPackageId}.{toolSettings.ToolPackageVersion}.nupkg",
274+
$"{toolSettings.ToolPackageId}.any.{toolSettings.ToolPackageVersion}.nupkg"
275+
], "There should be two packages: one for the tool-wrapper and one for the 'any' RID");
276+
var testDirectory = _testAssetsManager.CreateTestDirectory();
277+
var homeFolder = Path.Combine(testDirectory.Path, "home");
278+
279+
new DotnetToolCommand(Log, "exec", toolSettings.ToolPackageId, "--yes", "--add-source", toolPackagesPath)
280+
.WithEnvironmentVariables(homeFolder)
281+
.WithWorkingDirectory(testDirectory.Path)
282+
.Execute()
283+
.Should().Pass()
284+
.And.HaveStdOutContaining("Hello Tool!");
285+
}
286+
287+
[Fact]
288+
public void InstallAndRunToolFromAnyRidWhenOtherRidsArePresentButIncompatible()
289+
{
290+
var toolSettings = new TestToolBuilder.TestToolSettings()
291+
{
292+
IncludeCurrentRid = false,
293+
RidSpecific = true, // will make one package for each RID except the current RID
294+
IncludeAnyRid = true // will make one package with the "any" RID
295+
};
296+
List<string> expectedRids = [ .. ToolsetInfo.LatestRuntimeIdentifiers.Split(';').Where(rid => rid != RuntimeInformation.RuntimeIdentifier), "any"];
297+
298+
string toolPackagesPath = ToolBuilder.CreateTestTool(Log, toolSettings);
299+
var packages = Directory.GetFiles(toolPackagesPath, "*.nupkg").Select(p => Path.GetFileName(p)).ToArray();
300+
packages.Should().BeEquivalentTo([
301+
$"{toolSettings.ToolPackageId}.{toolSettings.ToolPackageVersion}.nupkg",
302+
.. expectedRids.Select(rid => $"{toolSettings.ToolPackageId}.{rid}.{toolSettings.ToolPackageVersion}.nupkg"),
303+
], $"There should be { 1 + expectedRids.Count } packages: one for the tool-wrapper and one for each RID except the current RID");
304+
var testDirectory = _testAssetsManager.CreateTestDirectory();
305+
var homeFolder = Path.Combine(testDirectory.Path, "home");
306+
307+
new DotnetToolCommand(Log, "exec", toolSettings.ToolPackageId, "--yes", "--add-source", toolPackagesPath)
308+
.WithEnvironmentVariables(homeFolder)
309+
.WithWorkingDirectory(testDirectory.Path)
310+
.Execute()
311+
.Should().Pass()
312+
.And.HaveStdOutContaining("Hello Tool!");
313+
}
226314

227315
private void EnsurePackageIsFdd(string packagePath)
228316
{

test/Microsoft.DotNet.PackageInstall.Tests/TestToolBuilder.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4-
using System;
5-
using System.Collections.Generic;
6-
using System.Text;
7-
84
namespace Microsoft.DotNet.PackageInstall.Tests
95
{
106
[CollectionDefinition(nameof(TestToolBuilderCollection))]
@@ -35,10 +31,11 @@ public class TestToolSettings
3531
public bool NativeAOT { get; set { field = value; this.RidSpecific = value; } } = false;
3632
public bool SelfContained { get; set { field = value; this.RidSpecific = value; } } = false;
3733
public bool Trimmed { get; set { field = value; this.RidSpecific = value; } } = false;
38-
public bool IncludeAnyRid { get; set { field = value; this.RidSpecific = value; } } = false;
34+
public bool IncludeAnyRid { get; set { field = value; } } = false;
3935
public bool RidSpecific { get; set; } = false;
36+
public bool IncludeCurrentRid { get; set; } = true;
4037

41-
public string GetIdentifier() => $"{ToolPackageId}-{ToolPackageVersion}-{ToolCommandName}-{(NativeAOT ? "nativeaot" : SelfContained ? "selfcontained" : Trimmed ? "trimmed" : "managed")}{(RidSpecific ? "-specific" : "")}{(IncludeAnyRid ? "-anyrid" : "")}";
38+
public string GetIdentifier() => $"{ToolPackageId}-{ToolPackageVersion}-{ToolCommandName}-{(NativeAOT ? "nativeaot" : SelfContained ? "selfcontained" : Trimmed ? "trimmed" : "managed")}{(RidSpecific ? "-specific" : "")}{(IncludeAnyRid ? "-anyrid" : "")}{(IncludeCurrentRid ? "" : "-no-current-rid")}";
4239
}
4340

4441

@@ -57,13 +54,18 @@ public string CreateTestTool(ITestOutputHelper log, TestToolSettings toolSetting
5754
testProject.AdditionalProperties["ImplicitUsings"] = "enable";
5855
testProject.AdditionalProperties["Version"] = toolSettings.ToolPackageVersion;
5956

60-
var singleRid = RuntimeInformation.RuntimeIdentifier;
61-
var multiRid = toolSettings.IncludeAnyRid ? $"{ToolsetInfo.LatestRuntimeIdentifiers};any" : ToolsetInfo.LatestRuntimeIdentifiers;
57+
var multiRid = toolSettings.IncludeCurrentRid ? ToolsetInfo.LatestRuntimeIdentifiers : ToolsetInfo.LatestRuntimeIdentifiers.Replace(RuntimeInformation.RuntimeIdentifier, string.Empty).Trim(';');
6258

6359
if (toolSettings.RidSpecific)
6460
{
6561
testProject.AdditionalProperties["RuntimeIdentifiers"] = multiRid;
6662
}
63+
if (toolSettings.IncludeAnyRid)
64+
{
65+
testProject.AdditionalProperties["RuntimeIdentifiers"] = testProject.AdditionalProperties.TryGetValue("RuntimeIdentifiers", out var existingRids)
66+
? $"{existingRids};any"
67+
: "any";
68+
}
6769

6870
if (toolSettings.NativeAOT)
6971
{

0 commit comments

Comments
 (0)