Skip to content

Use an MSBuild Task for RID selection instead of shelling out #8686

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion Aspire.sln
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
Expand Down Expand Up @@ -675,6 +674,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Azure.Npgsql.EntityF
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.Components.Common.Tests", "tests\Aspire.Components.Common.Tests\Aspire.Components.Common.Tests.csproj", "{30950CEB-2232-F9FC-04FF-ADDCB8AC30A7}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aspire.AppHost.Tasks", "src\Aspire.AppHost.Sdk\Aspire.AppHost.Tasks\Aspire.AppHost.Tasks.csproj", "{AC030549-F2E8-08A5-9B7E-FF116B7E696A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -3961,6 +3962,18 @@ Global
{30950CEB-2232-F9FC-04FF-ADDCB8AC30A7}.Release|x64.Build.0 = Release|Any CPU
{30950CEB-2232-F9FC-04FF-ADDCB8AC30A7}.Release|x86.ActiveCfg = Release|Any CPU
{30950CEB-2232-F9FC-04FF-ADDCB8AC30A7}.Release|x86.Build.0 = Release|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Debug|x64.ActiveCfg = Debug|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Debug|x64.Build.0 = Debug|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Debug|x86.ActiveCfg = Debug|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Debug|x86.Build.0 = Debug|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Release|Any CPU.Build.0 = Release|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Release|x64.ActiveCfg = Release|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Release|x64.Build.0 = Release|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Release|x86.ActiveCfg = Release|Any CPU
{AC030549-F2E8-08A5-9B7E-FF116B7E696A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -4285,6 +4298,7 @@ Global
{192747A2-9338-DECF-5C8C-28EB8E13829B} = {27381127-6C45-4B4C-8F18-41FF48DFE4B2}
{8FCA0CFA-7823-6A2F-342A-107A994915B0} = {C424395C-1235-41A4-BF55-07880A04368C}
{30950CEB-2232-F9FC-04FF-ADDCB8AC30A7} = {C424395C-1235-41A4-BF55-07880A04368C}
{AC030549-F2E8-08A5-9B7E-FF116B7E696A} = {F534D4F8-5E3A-42FC-BCD7-4C2D6060F9C8}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {47DCFECF-5631-4BDE-A1EC-BE41E90F60C4}
Expand Down
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@
<PackageVersion Include="Humanizer.Core" Version="2.14.1" />
<PackageVersion Include="KubernetesClient" Version="16.0.2" />
<PackageVersion Include="JsonPatch.Net" Version="3.3.0" />
<PackageVersion Include="Microsoft.Build.Utilities.Core" Version="17.8.3" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.1" />
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components" Version="4.11.6" />
<PackageVersion Include="Microsoft.FluentUI.AspNetCore.Components.Icons" Version="4.11.6" />
Expand Down
27 changes: 8 additions & 19 deletions src/Aspire.AppHost.Sdk/Aspire.AppHost.Sdk.csproj
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
<Project Sdk="Microsoft.Build.NoTargets">
<Project Sdk="Microsoft.Build.NoTargets">

<Import Project="$(SharedDir)Workload.targets" />

<PropertyGroup>
<PackageTags>aspire apphost sdk</PackageTags>
<Description>.NET Aspire AppHost SDK. Must be referenced by .NET Aspire AppHost projects.</Description>
<TargetsForTfmSpecificContentInPackage>_PublishAndPackRIDTool;$(TargetsForTfmSpecificContentInPackage)</TargetsForTfmSpecificContentInPackage>
<TargetsForTfmSpecificContentInPackage>_AddTaskToPackage;$(TargetsForTfmSpecificContentInPackage)</TargetsForTfmSpecificContentInPackage>
<EnablePackageValidation>false</EnablePackageValidation>
</PropertyGroup>

Expand All @@ -18,27 +18,16 @@
<None Update="SDK\AutoImport.props;SDK\*.targets" Pack="true" PackagePath="Sdk\%(Filename)%(Extension)" CopyToOutputDirectory="PreserveNewest" />
<None Update="SDK\Sdk.in.props" Pack="true" PerformTextReplacement="True" PackagePath="Sdk\Sdk.props" CopyToOutputDirectory="PreserveNewest" />
<None Update="SDK\Sdk.in.targets" Pack="true" PerformTextReplacement="True" PackagePath="Sdk\Sdk.targets" />

<!-- Adding a project reference to the tool to avoid MSBuild having clashes when building this project before the other. -->
<ProjectReference Include="Aspire.RuntimeIdentifier.Tool\Aspire.RuntimeIdentifier.Tool.csproj"
ReferenceOutputAssembly="false"
SkipTargetFrameworkProperties="true"
ExcludeAssets="all"
Private="false" />
<ProjectReference Include="Aspire.AppHost.Tasks\Aspire.AppHost.Tasks.csproj" />
</ItemGroup>

<Target Name="_PublishAndPackRIDTool">
<MSBuild Projects="$(MSBuildThisFileDirectory)Aspire.RuntimeIdentifier.Tool\Aspire.RuntimeIdentifier.Tool.csproj"
Targets="Publish"
Properties="Configuration=$(Configuration);Platform=$(Platform)" />

<ItemGroup>
<_publishContentToPackage Include="$(DotNetOutputPath)Aspire.RuntimeIdentifier.Tool/$(Configuration)/$(DefaultTargetFramework)/publish/**/*" />
</ItemGroup>
<Target Name="_AddTaskToPackage">
<MSBuild Projects="@(ProjectReference)" Targets="Build" Properties="TargetFramework=netstandard2.0">
<Output TaskParameter="TargetOutputs" ItemName="TaskDlls" />

</MSBuild>
<ItemGroup>
<TfmSpecificPackageFile Include="@(_publishContentToPackage)"
PackagePath="tools/$(DefaultTargetFramework)/%(_publishContentToPackage.RecursiveDir)" />
<TfmSpecificPackageFile Include="@(TaskDlls)" PackagePath="tasks/netstandard2.0/%(TaskDlls.RecursiveDir)" />
</ItemGroup>

</Target>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(DefaultTargetFramework)</TargetFramework>
<RollForward>Major</RollForward>
<TargetFramework>netstandard2.0</TargetFramework>
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I chose ns2.0 so that we should work in VS and in CLI. I've tested both and that does seem to be the case.

<UsePublicApiAnalyzers>false</UsePublicApiAnalyzers>
<IsPackable>false</IsPackable>
<EnablePackageValidation>false</EnablePackageValidation>
<UseAppHost>false</UseAppHost>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.CommandLine" />
<PackageReference Include="Microsoft.Build.Utilities.Core" />
<PackageReference Include="NuGet.ProjectModel" />
</ItemGroup>

Expand Down
63 changes: 63 additions & 0 deletions src/Aspire.AppHost.Sdk/Aspire.AppHost.Tasks/PickBestRid.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.Build.Framework;
using Aspire.Hosting.Sdk;
using NuGet.RuntimeModel;

namespace Aspire.RuntimeIdentifier.Tool;

/// <summary>
/// This task uses the given RID graph in a given SDK to pick the best match from among a set of supported RIDs for the current RID
/// </summary>
public sealed class PickBestRid : Microsoft.Build.Utilities.Task
{
/// <summary>
/// The path to the RID graph to read
/// </summary>
[Required]
public string RuntimeGraphPath { get; set; }

/// <summary>
/// The RID of the current process
/// </summary>
[Required]
public string CurrentRid { get; set; }

/// <summary>
/// All of the RIDs that Aspire supports
/// </summary>
[Required]
public string[]? SupportedRids { get; set; }

/// <summary>
/// The solution to the puzzle
/// </summary>
[Output]
public string MatchingRid { get; set; }

/// <summary>
/// Computes the thing
/// </summary>
/// <returns></returns>
public override bool Execute()
{
if (!File.Exists(RuntimeGraphPath))
{
Log.LogError("File {0} does not exist. Please ensure the runtime graph path exists.", RuntimeGraphPath);
return !Log.HasLoggedErrors;
}

RuntimeGraph graph = JsonRuntimeFormat.ReadRuntimeGraph(RuntimeGraphPath);
string? bestRidForPlatform = NuGetUtils.GetBestMatchingRid(graph, CurrentRid, SupportedRids, out bool wasInGraph);

if (!wasInGraph)
{
Log.LogError("Unable to find a matching RID for {0} from among {1} in graph {2}", CurrentRid, string.Join(",", SupportedRids), RuntimeGraphPath);
return !Log.HasLoggedErrors;
}

MatchingRid = bestRidForPlatform;
return true;
}
}
86 changes: 0 additions & 86 deletions src/Aspire.AppHost.Sdk/Aspire.RuntimeIdentifier.Tool/Program.cs

This file was deleted.

55 changes: 9 additions & 46 deletions src/Aspire.AppHost.Sdk/SDK/Sdk.in.targets
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
This defaulting needs to happen in the SDK targets so this metadata affects NuGet Restore.
-->
<ItemGroup Condition="'$(IsAspireHost)' == 'true'">

<ProjectReference Update="@(ProjectReference)">
<IsAspireProjectResource Condition="'%(IsAspireProjectResource)' != 'false'">true</IsAspireProjectResource>

Expand All @@ -43,11 +43,7 @@
<Warning Code="ASPIRE003" Text="$(MSBuildProjectName) is a .NET Aspire AppHost project that requires Visual Studio version 17.10 or above to work correctly. You are using version $(MSBuildVersion)." />
</Target>

<PropertyGroup>
<AspireRidToolRoot>$(MSBuildThisFileDirectory)..\tools\@DefaultTargetFramework@\</AspireRidToolRoot>
<AspireRidToolDirectory>$([MSBuild]::NormalizePath('$(AspireRidToolRoot)\'))</AspireRidToolDirectory>
<AspireRidToolExecutable>$(AspireRidToolDirectory)Aspire.RuntimeIdentifier.Tool.dll</AspireRidToolExecutable>
</PropertyGroup>
<UsingTask AssemblyFile="$(MSBuildThisFileDirectory)../tasks/netstandard2.0/Aspire.AppHost.Tasks.dll" TaskName="PickBestRid" />

<!-- This target extracts the version of Aspire.Hosting.AppHost referenced by the project, and adds
a reference to Aspire.Dashboard.Sdk and Aspire.Hosting.Orchestration for the build-time platform using
Expand All @@ -66,7 +62,7 @@
<_AppHostVersion>%(_AppHostPackageReference.Version)</_AppHostVersion>
</PropertyGroup>

<!-- If the version is still empty, then we assume the project is using Central Package Management,
<!-- If the version is still empty, then we assume the project is using Central Package Management,
and we try to extract the PackageVersion Items. -->
<ItemGroup Condition="'$(_AppHostVersion)' == ''">
<_AppHostPackageReference />
Expand All @@ -91,52 +87,19 @@
</PropertyGroup>

<!-- At this point, we should have the version either by CPM or PackageReference, so we fail if not. -->
<Error Condition="'$(_AppHostVersion)' == ''"
<Error Condition="'$(_AppHostVersion)' == ''"
Text="$(MSBuildProjectName) is a .NET Aspire AppHost project that needs a package reference to Aspire.Hosting.AppHost version 8.2.0 or above to work correctly." />

<!--The following is the list of the RIDs that we currently multitarget for DCP and Dashboard packages-->
<ItemGroup>
<_DashboardAndDCPSupportedTargetingRIDs Include="win-x64" />
<_DashboardAndDCPSupportedTargetingRIDs Include="win-x86" />
<_DashboardAndDCPSupportedTargetingRIDs Include="win-arm64" />
<_DashboardAndDCPSupportedTargetingRIDs Include="linux-x64" />
<_DashboardAndDCPSupportedTargetingRIDs Include="linux-arm64" />
<_DashboardAndDCPSupportedTargetingRIDs Include="osx-x64" />
<_DashboardAndDCPSupportedTargetingRIDs Include="osx-arm64" />
<_DashboardAndDCPSupportedTargetingRIDs Include="win-x64;win-x86;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64" />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a nitpick kind of thing - when the items are declared inline like this they show as one 'thing' in the binlog viewer:
image

this is easier to look at than N separate items.

</ItemGroup>

<!-- If running in .NET Core, DOTNET_HOST_PATH is set to point to the dotnet executable being used
for the build. -->
<PropertyGroup>
<_DotNetHostPath>$(DOTNET_HOST_PATH)</_DotNetHostPath>
</PropertyGroup>

<!-- If running on .NET Framework MSBuild, then DOTNET_HOST_PATH won't be set, so we need to construct
the path using well-known properties. -->
<PropertyGroup Condition="'$(_DotNetHostPath)' == ''">
<_DotNetHostDirectory>$(NetCoreRoot)</_DotNetHostDirectory>
<_DotNetHostFileName>dotnet</_DotNetHostFileName>
<_DotNetHostFileName Condition="'$(OS)' == 'Windows_NT'">dotnet.exe</_DotNetHostFileName>

<_DotNetHostPath>$(_DotNetHostDirectory)\$(_DotNetHostFileName)</_DotNetHostPath>
</PropertyGroup>
Comment on lines -108 to -122
Copy link
Member Author

@baronfel baronfel Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

none of this is necessary if we're not spawning binaries anymore in the SDK-delivered targets


<!-- Call Aspire.RuntimeIdentifier.Tool to get the RIDs for Dashboard and DCP packages -->
<Exec Command="&quot;$(_DotNetHostPath)&quot; exec &quot;$(AspireRidToolExecutable)&quot; --runtimeGraphPath &quot;$(BundledRuntimeIdentifierGraphFile)&quot; --supportedRids &quot;@(_DashboardAndDCPSupportedTargetingRIDs -> '%(Identity)', ',')&quot; --netcoreSdkRuntimeIdentifier &quot;$(NETCoreSdkRuntimeIdentifier)&quot;"
ConsoleToMSBuild="true"
StandardOutputImportance="Low"
IgnoreExitCode="true">
<Output TaskParameter="ExitCode" PropertyName="_AspireRidToolExitCode" />
<Output TaskParameter="ConsoleOutput" ItemName="_AspireRidToolOutput" />
</Exec>

<Error Condition="$(_AspireRidToolExitCode) != 0" Text="Failed to run Aspire.RuntimeIdentifier.Tool. Exit code: $(_AspireRidToolExitCode). Output: @(_AspireRidToolOutput)" />

<PropertyGroup>
<_DashboardAndDcpRID>@(_AspireRidToolOutput)</_DashboardAndDcpRID>
</PropertyGroup>
<!-- Compute the RID for Dashboard and DCP packages -->
<PickBestRid RuntimeGraphPath="$(BundledRuntimeIdentifierGraphFile)" CurrentRid="$(NETCoreSdkRuntimeIdentifier)" SupportedRids="@(_DashboardAndDCPSupportedTargetingRIDs)">
Copy link
Member

@tmds tmds Apr 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could NETCoreSdkPortableRuntimeIdentifier be an alternative to doing a rid graph lookup?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe? I was mostly focused on translating the behaviors that existed before. I like than an explicit lookup will have a clearer error message than a missing implicitly-defined PackageReference.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most use cases are expressed with portable rids.

Either the portable rid comes from the user, or it's the one from the SDK.

For the latter, rather than mapping NETCoreSdkRuntimeIdentifier via the rid graph, NETCoreSdkPortableRuntimeIdentifier can be used.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

On a linux-musl-x64 system, should this use linux-x64 assets? Or would those be binary incompatible?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't use NETCoreSdkPortableRuntimeIdentifier as there are platforms where that won't be correct. This is how we originally had it, where we'd just use that to infer the package name but it didn't work for some distros where that is more specific and we don't cross-compile for it. As an example of the types of things that would show up if we did this are #5486 which this logic was specifically fixing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@joperezr are you sure you were using NETCoreSdkPortableRuntimeIdentifier? (Note: this contains: Portable.)

I'm looking at #5695 and I don't see it.

<Output TaskParameter="MatchingRid" PropertyName="_DashboardAndDcpRID" />
</PickBestRid>


<!-- Now that we have the version, we add the package references -->
<ItemGroup>
<PackageReference Include="Aspire.Dashboard.Sdk.$(_DashboardAndDcpRID)" Version="$(_AppHostVersion)" IsImplicitlyDefined="true" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</PropertyGroup>

<ItemGroup>
<Compile Include="..\..\src\Aspire.AppHost.Sdk\Aspire.RuntimeIdentifier.Tool\NuGetUtils.cs" Link="Product\NuGetUtils.cs" />
<Compile Include="..\..\src\Aspire.AppHost.Sdk\Aspire.AppHost.Tasks\NuGetUtils.cs" Link="Product\NuGetUtils.cs" />
<None Include="$(BundledRuntimeIdentifierGraphFile)" Link="RuntimeIdentifierGraph.json" CopyToOutputDirectory="Always" />
</ItemGroup>

Expand Down