Skip to content

Conversation

@baronfel
Copy link
Member

@baronfel baronfel commented Nov 15, 2025

Fixes dotnet/runtime#51667

There was a gap when restoring a project for a RID and then publishing it for that same RID with --no-restore where if that project only set PublishTrimmed or PublishAOT, the runtime packs for that RID would not be restored. This would lead to the publish command failing saying that required runtime packs weren't available. There were a couple workarounds:

  • explicitly specify all RuntimeIdentifiers that you would like the project to Publish for
    • this causes Restore to correctly download all of the packs for the various RIDs up-front based on the publishing modality you choose
  • add PublishSelfContained or SelfContained to the project
    • this would trigger the existing 'if self contained then get the right runtime packs' behavior that we wanted in the first place

Along the way I added some tests to cover the new scenarios, and extracted out a documentation spec to capture how we think ProcessFrameworkReferences should behave, modulo any bugs. In the future we should be able to reference this when answering questions about what the expected behavior is.

I also made a tasks.slnf to make it easier to iterate on the SDK's MSBuild Tasks and their tests in isolation. This massively helps the VSCode user experiences, especially on the testing inner loop.

…also bring in the runtime packs when targeting a single RID.

This fixes a gap where 'dotnet restore' for a RID followed by 'dotnet publish --no-restore' for that RID in trimming/AOT scenarios wouldn't work.
Copilot AI review requested due to automatic review settings November 15, 2025 19:10
Comment on lines +87 to +89
<ItemGroup>
<ProjectCapability Remove="TestContainer" />
</ItemGroup>
Copy link
Member Author

Choose a reason for hiding this comment

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

Oh, this is a bugfix from the DevKit folks (thanks @peterwald!) that unblocks the DevKit test explorer experience.

Because this project references xunit (thanks Arcade!) the xunit package adds this capability. Because the project has this capability, DevKit tries to load it as a test. Because this project is a library and doesn't have all of the test dependencies, it fails to load and crashes the test-discovery process.

Copilot finished reviewing on behalf of baronfel November 15, 2025 19:15
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This pull request fixes a gap in runtime pack resolution where projects using PublishTrimmed or PublishAot without SelfContained=true would fail during publish with --no-restore because required runtime packs weren't downloaded during restore.

Key Changes:

  • Introduced DeploymentModelRequiresRuntimeComponents property that consolidates logic for determining when runtime packs are needed, now including RequiresILLinkPack (set when trimming is enabled) alongside SelfContained, ReadyToRunEnabled, and PublishAot
  • Refactored KnownRuntimePack struct to use C# 12 primary constructor and readonly properties for better maintainability
  • Added comprehensive unit tests covering various publish scenarios including trimming and AOT without self-contained deployment
  • Created documentation specification detailing expected behavior of ProcessFrameworkReferences task across different deployment scenarios

Reviewed Changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs Core fix to runtime pack resolution logic; refactored struct to use modern C# features; introduced constants and helper properties for better code clarity
src/Tasks/Microsoft.NET.Build.Tasks.UnitTests/ProcessFrameworkReferencesTests.cs Added comprehensive test coverage for PublishTrimmed, PublishAot, and ReadyToRun scenarios with and without SelfContained; added support for ReadyToRunEnabled in test configuration
documentation/specs/ProcessFrameworkReferences-outputs.md New specification document describing expected outputs for different deployment scenarios and documenting the fix for issue #51667
test/Microsoft.NET.TestFramework/Microsoft.NET.TestFramework.csproj Removed TestContainer project capability to prevent VS from treating the framework project as a test container
tasks.slnf New solution filter for convenient development focused on build tasks projects

baronfel and others added 3 commits November 15, 2025 13:21
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Comment on lines +178 to +184
/// <summary>
/// We have several deployment models that require the use of runtime assets of various kinds.
/// This member helps identify when any of those models are in use, because we've had bugs in the past
/// where we didn't properly account for all of them.
/// </summary>
private bool DeploymentModelRequiresRuntimeComponents =>
SelfContained || ReadyToRunEnabled || PublishAot || RequiresILLinkPack;
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 member is the actual fix. Every other change is tests, infra, debugging utility, clarity, etc.

@baronfel
Copy link
Member Author

I'll need to add some more tests covering the end-to-end, because while this change does cause ProcessFrameworkReferences to correctly include the relevant runtime packs for Trimmed-and-up scenarios:

image

the subsequent behavior of the ILLink targets implies that the SelfContained property itself needs to be set. There's a bit of circularity here because those targets get produced from the runtime. I think we don't want to continue to increase our reliance on SelfContained directly. I suspect this behavior of SelfContained in the ProcessFrameworkReferences Task is the reason for our over-reliance on both it and the _IsPublishing mechanism. Adding the following Target to my test project to 'patch up' the behavior of the ILLink targets to support a more broad concept of 'is this project using one of the self-contained modalities' made the publish compile as expected:

	<Target Name="PatchSelfContainedForILLinkSupport" BeforeTargets="PrepareForILLink">
		<PropertyGroup>
			<SelfContained Condition="'$(SelfContained)' == '' and (
				'$(PublishSelfContained)' == 'true' or
				'$(PublishTrimmed)' == 'true' or
				'$(PublishAOT)' == 'true'
			)">true</SelfContained>
		</PropertyGroup>
	</Target>

I strongly suspect that if we expanded this check in the ILLink Targets:

   <Target Name="PrepareForILLink"
           DependsOnTargets="_ComputeManagedAssemblyToLink">
     ... 
    <!-- The defaults currently root non-framework assemblies, which
         is a no-op for portable apps. If we later support more ways
         to customize the behavior we can allow linking portable apps
         in some cases. -->
    <NETSdkError Condition="'$(SelfContained)' != 'true'" ResourceName="ILLinkNotSupportedError" />
    ...
  </Target>

Then we might even be able to get away with a significant amount of _IsPublishing-related hackiness, like this in the SDK RuntimeIdentifierInference:

  <!-- Edit SelfContained to match the value of PublishSelfContained if we are publishing.
       This Won't affect t:/Publish (because of _IsPublishing), and also won't override a global SelfContained property.-->
  <PropertyGroup Condition="'$(_IsPublishing)' == 'true' and ('$(PublishSelfContained)' == 'true' or '$(PublishSelfContained)' == 'false')">
    <SelfContained>$(PublishSelfContained)</SelfContained>
  </PropertyGroup>

All of this is just being set so that the runtime packs are around.

@baronfel
Copy link
Member Author

Nit: per #51766 (comment) PublishSelfContained doesn't trigger RuntimePack download as expected - need to cover this scenario too.

<SelfContained>true</SelfContained>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<ReadyToRunEnabled>true</ReadyToRunEnabled>
<ReadyToRunUseCrossgen2>true</ReadyToRunUseCrossgen2>
Copy link
Member

Choose a reason for hiding this comment

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

Is there a minimum runtime pack version that the current .NET SDK is compatible with? Does the current .NET SDK still support publishing for .NET 5 in all flavors?

UseCrossgen2 property was a thing in .NET 5 where crossgen2 was in preview and setting this property to true was used to opt-in into the preview. It has no purpose (it is always true) in .NET 6+.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[AppleAppBuilder] arm64 Catalyst apps need to codesign bundled .dylibs

2 participants