Skip to content

[TrimmableTypeMap] Don't stage shrunk assemblies into the shared runtime pack on NativeAOT#11780

Open
simonrozsival wants to merge 1 commit into
dotnet:mainfrom
simonrozsival:dev/simonrozsival/fix-removeregister-shrunk-pack
Open

[TrimmableTypeMap] Don't stage shrunk assemblies into the shared runtime pack on NativeAOT#11780
simonrozsival wants to merge 1 commit into
dotnet:mainfrom
simonrozsival:dev/simonrozsival/fix-removeregister-shrunk-pack

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Summary

For PublishTrimmed Android builds, _RemoveRegisterAttribute (in Xamarin.Android.Common.targets) copies every @(_ResolvedAssemblies) into its @(_ShrunkAssemblies) location. For the framework/user assemblies those shrunk destinations live inside the shared NuGet runtime pack, e.g.:

.../microsoft.netcore.app.runtime.nativeaot.android-arm64/<ver>/runtimes/android-arm64/lib/net11.0/shrunk/System.Xml.ReaderWriter.dll

Because multiple test projects — and the parallel per-RID inner builds — all target those same shared files, they race and fail intermittently on a clean pack cache with:

error XACIC7028: System.IO.FileNotFoundException: Could not find file
'.../lib/net11.0/shrunk/System.Xml.ReaderWriter.dll'
  at Microsoft.Android.Build.Tasks.Files.CopyIfChanged(String, String)
  at Xamarin.Android.Tasks.CopyIfChanged.RunTask()

This surfaced as intermittent CI failures in CheckCodeBehindIsGenerated(NativeAOT) and CheckOldResourceDesignerWithWrongCasingIsRemoved(True,NativeAOT). It does not reproduce locally because shrunk/ files left over from previous builds mask the missing source.

Fixes #11776.

Fix

On NativeAOT the managed framework/user assemblies are compiled to a native shared library by ILC and are never packaged, so these shrunk copies are only ever used as incremental-build inputs — CollectAssemblyFilesToCompress (the only task that reads their content) is already gated to '$(_AndroidRuntime)' != 'NativeAOT'.

This PR overrides _RemoveRegisterAttribute in the trimmable typemap path to, for NativeAOT, redirect the shrunk copies to a project-local intermediate directory ($(MonoAndroidIntermediateAssemblyDir)shrunk\) instead of the shared runtime pack. They are still produced with CopyIfChanged, so the copies keep stable timestamps (only rewritten when content actually changes), which preserves the @(_ShrunkAssemblies)/@(_ShrunkFrameworkAssemblies) incremental up-to-date checks — while nothing is ever written into the shared pack.

CoreCLR (and any other trimmable runtime) keeps the base behavior because it still packages the managed assemblies and therefore needs the shrunk copies in place.

Risk

Low and well-scoped:

  • Only the '$(_AndroidRuntime)' == 'NativeAOT' branch changes behavior; CoreCLR-trimmable replicates the base target exactly.
  • No effect on the default managed/MonoVM/CoreCLR paths.

Testing

Verified locally (both runtimes) that CheckCodeBehindIsGenerated, CheckOldResourceDesignerWithWrongCasingIsRemoved, and CSProjUserFileChanges (incremental) pass, and that the runtime pack's shrunk/ directory is no longer written for NativeAOT builds. Validated on CI in the context of the larger trimmable-typemap work (the two failing tests turned green and stayed green).

Sliced out of #11617 for focused review.

…ime pack on NativeAOT

CheckCodeBehindIsGenerated(NativeAOT) and
CheckOldResourceDesignerWithWrongCasingIsRemoved(True,NativeAOT) failed
intermittently in CI with:

  error XACIC7028: System.IO.FileNotFoundException: Could not find file
  '.../microsoft.netcore.app.runtime.nativeaot.android-arm64/.../lib/net11.0/shrunk/System.Xml.ReaderWriter.dll'
  at _RemoveRegisterAttribute (Xamarin.Android.Common.targets).

The base _RemoveRegisterAttribute copies every @(_ResolvedAssemblies) into
its @(_ShrunkAssemblies) location. For PublishTrimmed builds the framework/
user shrunk destinations live *inside the shared NuGet runtime pack*
(.../runtimes/<rid>/lib/net11.0/shrunk/). Because multiple test projects and
the parallel per-RID inner builds all target those same shared files, they
race and fail intermittently with FileNotFoundException on a clean pack cache.
Locally the failure is masked by shrunk/ files left over from previous builds.

On NativeAOT these managed framework/user assemblies are compiled to a native
shared library by ILC and are never packaged, so the shrunk copies are only
ever used as incremental-build inputs (CollectAssemblyFilesToCompress, the one
task that reads their content, is already gated to non-NativeAOT). Override
_RemoveRegisterAttribute in the trimmable typemap path to redirect the shrunk
copies to a project-local intermediate directory instead of the shared pack.
They are still produced with CopyIfChanged so the copies keep stable
timestamps (preserving the @(_ShrunkAssemblies)/@(_ShrunkFrameworkAssemblies)
incremental up-to-date checks), but nothing is ever written into the shared
runtime pack. CoreCLR keeps the base behavior since it still packages the
managed assemblies.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings June 28, 2026 14:42
@simonrozsival simonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Jun 28, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

This PR prevents PublishTrimmed NativeAOT builds in the trimmable typemap path from copying “shrunk” assemblies into the shared runtime pack (NuGet cache), which can cause cross-project/per-RID parallel build races and intermittent XACIC7028 failures. It does so by overriding _RemoveRegisterAttribute to redirect the “shrunk” copy destinations into a project-local intermediate directory for NativeAOT, while keeping the existing behavior for CoreCLR/trimmable runtimes that still package managed assemblies.

Changes:

  • Add an override of _RemoveRegisterAttribute in the trimmable typemap targets.
  • For '$(_AndroidRuntime)' == 'NativeAOT', rewrite @(_ShrunkAssemblies) / @(_ShrunkFrameworkAssemblies) / @(_ShrunkUserAssemblies) to point under $(MonoAndroidIntermediateAssemblyDir)shrunk\... instead of the shared runtime pack.
  • Preserve the existing copy behavior (CopyIfChanged) for non-NativeAOT trimmable runtimes.
Show a summary per file
File Description
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets Overrides _RemoveRegisterAttribute to redirect shrunk assembly staging to a project-local intermediate path for NativeAOT, avoiding writes into shared runtime packs.

Copilot's findings

  • Files reviewed: 1/1 changed files
  • Comments generated: 0

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

Labels

copilot `copilot-cli` or other AIs were used to author this trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] _RemoveRegisterAttribute fails on trimmed framework assembly (CheckAaptError... NativeAOT)

2 participants