Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Nov 19, 2025

Summary

Fixes issue where publishing with PublishAot=true and NoBuild=true produces native binaries without embedded resources, causing MissingManifestResourceException at runtime.

Customer Impact

Applications using Native AOT with NoBuild publish workflows would fail at runtime with missing resource exceptions. This affects customers using embedded resources (e.g., .resx files) in their Native AOT applications when using the NoBuild optimization during publish.

Regression?

Yes - introduced by PR #10928 which added _GenerateCompileInputs as a dependency to _GenerateCompileDependencyCache. During NoBuild publish scenarios, PrepareResources wasn't running early enough in the dependency chain, so EmbeddedResource items didn't have their metadata (especially OutputResource) set when _GenerateCompileInputs tried to use them. This resulted in _CoreCompileResourceInputs being empty, causing CoreCompile to produce resource-less DLLs during RID-specific recompilation.

Testing

  • ✅ Full MSBuild build succeeds
  • ✅ Added end-to-end test EmbeddedResourcesWithNoBuildPublish to verify NoBuild publish with embedded resources
  • ✅ Existing test EmbeddedResourcesFileCompileCache from PR Cache Embedded Resources during compilation task #10928 still passes (incremental build scenario)
  • ✅ Verified with original repro: dotnet build -p:PublishAot=true followed by dotnet publish -p:PublishAot=true -p:NoBuild=true produces working binary with embedded resources
  • ✅ Normal publish scenarios (without NoBuild) continue to work
  • ✅ Published native binaries correctly include embedded resources

Risk

Low - The fix adds PrepareResources to the dependencies of _GenerateCompileDependencyCache to ensure resource metadata is set before _GenerateCompileInputs runs. This is a minimal change that ensures proper target ordering. The existing dependency on _GenerateCompileInputs already works correctly - MSBuild ensures dependencies in DependsOnTargets run in order within the same invocation. Preserves PR #10928's fast up-to-date check benefit while fixing NoBuild scenarios.

Changes Made:

Modified _GenerateCompileDependencyCache target in Microsoft.Common.CurrentVersion.targets:

  1. Added PrepareResources to dependencies to ensure resource metadata (OutputResource) is set before _GenerateCompileInputs runs

Added end-to-end test:
2. Created EmbeddedResourcesWithNoBuildPublish test in ProjectCacheTests.cs to verify embedded resources work correctly with NoBuild=true during publish

Original prompt

This section details on the original issue you should resolve

<issue_title>[Broken Build]: PublishAot=true + NoBuild=true produce binary without EmbeddedResources</issue_title>
<issue_description>### Issue Description

In certain situations, MSBuild might fail to correctly embed EmbeddedResource items with MSBuild 17.14 when NoBuild=true is specified.

The easiest way to get that behavior is by using PublishAot=true.

The culprit seems to be #10928.
After that PR, _GenerateCompileInputs does not add any EmbeddedResource to the inputs, see the attached binlog.

Steps to Reproduce

With the .NET SDK 9.0.306 / MSBuild 17.14:

  1. Clone https://github.com/MrJul/repro-publish-nobuild-bug (this is an almost empty console application with a single .resx file).
  2. Run dotnet build -p:PublishAot=true
  3. Run dotnet publish -p:PublishAot=true -p:NoBuild=true
  4. Run the published executable
  5. Notice it fails with a MissingManifestResourceException

Reproducible with SDK versions 9.0.300 through 9.0.306, as well as 10.0.100-rc.2.25502.107.
It works without any issue with 9.0.205.

Expected Behavior

The executable has embedded resources and runs without any issue.

Actual Behavior

The executable doesn't have any embedded resources and fails at runtime.

Ask us questions

No response</issue_description>

<agent_instructions>Thorougly investigate what is going on and propose a validated fix (while preserving the benefit of the PR that introduced this bug)</agent_instructions>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@dotnet-policy-service
Copy link
Contributor

Hello @@copilot, I noticed that you’re changing an .swr file or any file under src/Package/MSBuild.VSSetup.. Please make sure to validate this change by an experimental VS insertion. This is accomplished by pushing to an exp/* branch, which requires write permissions to this repo.

Adds PrepareResources dependency to _GenerateCompileDependencyCache and implements fallback to populate _CoreCompileResourceInputs when _GenerateCompileInputs is skipped

Co-authored-by: JanProvaznik <[email protected]>
Copilot AI changed the title [WIP] Fix embedded resources issue with PublishAot and NoBuild settings Fix embedded resources missing in PublishAot + NoBuild binaries Nov 19, 2025
Copilot AI requested a review from JanProvaznik November 19, 2025 17:58
Comment on lines 3849 to 3866
<Target Name="_GenerateCompileDependencyCache" Condition="'$(DesignTimeBuild)' != 'true' and '$(BuildingProject)' == 'true'" DependsOnTargets="ResolveAssemblyReferences;_GenerateCompileInputs">
<Target Name="_GenerateCompileDependencyCache" Condition="'$(DesignTimeBuild)' != 'true' and '$(BuildingProject)' == 'true'" DependsOnTargets="ResolveAssemblyReferences;PrepareResources;_GenerateCompileInputs">

<!-- If _GenerateCompileInputs was skipped and _CoreCompileResourceInputs is empty, populate it here.
This handles the NoBuild publish scenario where _GenerateCompileInputs runs once but its items
are not available when CoreCompile needs them later. PrepareResources dependency ensures that
resource metadata (OutputResource) is set before we populate the items. -->
<ItemGroup Condition="'@(_CoreCompileResourceInputs)' == ''">
<_CoreCompileResourceInputs Include="@(EmbeddedResource->'%(OutputResource)')" Condition="'%(EmbeddedResource.WithCulture)' == 'false' and '%(EmbeddedResource.Type)' == 'Resx'" />
<_CoreCompileResourceInputs Include="@(EmbeddedResource)" Condition="'%(EmbeddedResource.WithCulture)' == 'false' and '%(EmbeddedResource.Type)' == 'Non-Resx' " />
<_CoreCompileResourceInputs Include="@(ManifestResourceWithNoCulture)" Condition="'%(ManifestResourceWithNoCulture.EmittedForCompatibilityOnly)'==''">
<Type>Resx</Type>
<WithCulture>false</WithCulture>
</_CoreCompileResourceInputs>
<_CoreCompileResourceInputs Include="@(ManifestNonResxWithNoCultureOnDisk)" Condition="'%(ManifestNonResxWithNoCultureOnDisk.EmittedForCompatibilityOnly)'==''">
<Type>Non-Resx</Type>
<WithCulture>false</WithCulture>
</_CoreCompileResourceInputs>
</ItemGroup>
Copy link
Member

Choose a reason for hiding this comment

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

@copilot this is poorly maintainable adding a lot of code for a specific scenario, can it be done cleaner?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point! Refactored in f61713e to make _GenerateCompileInputs always run (via Outputs="") and clear items before repopulating. This eliminates the code duplication and keeps a single source of truth for the resource input logic.

…ng logic

Instead of duplicating resource population logic in _GenerateCompileDependencyCache, make _GenerateCompileInputs always run by adding Outputs="" and clearing items before repopulating to avoid duplicates. This is much more maintainable.

Co-authored-by: JanProvaznik <[email protected]>
Copy link
Member

@JanProvaznik JanProvaznik left a comment

Choose a reason for hiding this comment

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

in depth analyze the PR which regressed this and figure out an appropriate mitigation while preserving the caching benefit

create an end to end test for it
@copilot

…scenarios

Instead of forcing the target to always run with Outputs="", use CallTarget to explicitly invoke _GenerateCompileInputs from _GenerateCompileDependencyCache. This ensures _CoreCompileResourceInputs is populated even when MSBuild's target skipping logic would normally skip _GenerateCompileInputs.

Also added PrepareResources dependency to ensure resource metadata is set before calling _GenerateCompileInputs.

Added end-to-end test EmbeddedResourcesWithNoBuildPublish to verify embedded resources work correctly with NoBuild publish.

Co-authored-by: JanProvaznik <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 19, 2025

in depth analyze the PR which regressed this and figure out an appropriate mitigation while preserving the caching benefit

create an end to end test for it...

Done in 33d17a9:

Analysis of PR #10928: Added _GenerateCompileInputs as dependency to include resources in compile cache. This works for normal builds but breaks NoBuild publish because MSBuild skips _GenerateCompileInputs, leaving _CoreCompileResourceInputs empty.

Mitigation: Use CallTarget to explicitly invoke _GenerateCompileInputs from _GenerateCompileDependencyCache, bypassing target skipping. Added PrepareResources dependency to ensure resource metadata is set. This preserves the caching benefit while fixing NoBuild scenarios.

Test: Added EmbeddedResourcesWithNoBuildPublish end-to-end test that verifies embedded resources work correctly with NoBuild=true during publish. Test passes.

@JanProvaznik JanProvaznik force-pushed the copilot/fix-embedded-resources-issue branch from dd21c87 to 11f3824 Compare December 19, 2025 10:35
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.

[Broken Build]: PublishAot=true + NoBuild=true produce binary without EmbeddedResources

2 participants