Skip to content

[TrimmableTypeMap] Generate the typemap before the NativeAOT inner build so the IDE Compile→Sign flow works#11789

Merged
simonrozsival merged 2 commits into
mainfrom
dev/simonrozsival/fix-nativeaot-typemap-ide-sign
Jun 29, 2026
Merged

[TrimmableTypeMap] Generate the typemap before the NativeAOT inner build so the IDE Compile→Sign flow works#11789
simonrozsival merged 2 commits into
mainfrom
dev/simonrozsival/fix-nativeaot-typemap-ide-sign

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Summary

The NativeAOT trimmable typemap (the generated Java ↔ .NET type map) is consumed by the inner per-RID ILC build — via _AddTrimmableTypeMapAssembliesToIlc_ReadGeneratedTrimmableTypeMapAssemblies — which reads the generated assembly list at obj/.../typemap/typemap-assemblies.txt. The inner build cannot generate that list: _GenerateTrimmableTypeMap is gated to the outer build ('$(_OuterIntermediateOutputPath)' == '').

_GenerateTrimmableTypeMap's only trigger is AfterTargets="CoreCompile". That hook does not fire when compilation is already up-to-date — which is exactly what happens in the IDE's split build, where Visual Studio (BuildingInsideVisualStudio=true) runs Compile and SignAndroidPackage as two separate MSBuild invocations. During the SignAndroidPackage call the C# is already compiled, so CoreCompile is skipped, the typemap is never generated, and the inner ILC build fails with:

Trimmable typemap assembly list '.../typemap/typemap-assemblies.txt' was not found

A regular command-line Build works because all targets run in one continuous invocation; only the IDE Compile→Sign sequence hits this. Reproduced by the DesignTimeBuildSignAndroidPackage(NativeAOT) test.

Fixes #11775.

Fix

Add a target that forces the typemap to be generated in the outer build before _ResolveAssemblies spawns the inner per-RID ILC build:

<Target Name="_EnsureTrimmableTypeMapGeneratedForNativeAotInnerBuild"
    Condition=" '$(RuntimeIdentifier)' == '' "
    BeforeTargets="_ResolveAssemblies"
    DependsOnTargets="_GenerateTrimmableTypeMap" />

Instead of relying on the fragile AfterTargets="CoreCompile" hook, generation is anchored to _ResolveAssemblies, which always runs before the inner build and runs after compilation (it consumes @(IntermediateAssembly)), so the generator still observes the compiled app assembly. This mirrors the CoreCLR _AddTrimmableTypeMapToLinker target, which forces generation before the (outer) ILLink.

Risk

Low and well-scoped: the new target is conditioned to the outer build (RuntimeIdentifier == '') and lives in the NativeAOT trimmable targets file; it only ensures an already-required step has run. No effect on CoreCLR, managed, or MonoVM paths.

Testing

Verified locally: DesignTimeBuildSignAndroidPackage(NativeAOT) now passes (was failing); DesignTimeBuildSignAndroidPackage(CoreCLR) continues to pass.

Sliced out of #11617 for focused review.

simonrozsival and others added 2 commits June 28, 2026 16:45
The NativeAOT trimmable typemap is consumed only in the inner per-RID ILC build
(via _AddTrimmableTypeMapAssembliesToIlc -> _ReadGeneratedTrimmableTypeMapAssemblies),
which cannot generate it: _GenerateTrimmableTypeMap is gated to the outer build
(_OuterIntermediateOutputPath == ''). Its only trigger is AfterTargets="CoreCompile",
which does not fire when compilation is up-to-date - e.g. the IDE's separate
"Compile" then "SignAndroidPackage" invocations (BuildingInsideVisualStudio=true).
The outer build therefore never generated obj/.../typemap/typemap-assemblies.txt and
the inner build failed with "Trimmable typemap assembly list ... was not found".

Force _GenerateTrimmableTypeMap to run in the outer build before _ResolveAssemblies
spawns the inner per-RID build. _ResolveAssemblies runs after compilation (it consumes
@(IntermediateAssembly)), so the generator still observes the compiled app assembly.
Mirrors the CoreCLR _AddTrimmableTypeMapToLinker target, which forces generation before
the (outer) ILLink.

Verified locally: DesignTimeBuildSignAndroidPackage(NativeAOT) now passes (was failing);
DesignTimeBuildSignAndroidPackage(CoreCLR) continues to pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tifier

The "outer build" is the build that is not an inner per-RID build, which is
determined by '$(_OuterIntermediateOutputPath)' == '' — the same discriminator
_GenerateTrimmableTypeMap itself uses. The previous 'RuntimeIdentifier == ''
condition missed the single-RID outer build (e.g. -p:RuntimeIdentifier=android-arm64),
where RuntimeIdentifier is non-empty but the build is still the outer one, leaving
the typemap ungenerated and reproducing the same failure this change fixes.

The inner build receives _OuterIntermediateOutputPath via _ResolveAssemblies
AdditionalProperties, so the target is still correctly skipped there.

Verified locally: DesignTimeBuildSignAndroidPackage(NativeAOT) and (CoreCLR) both pass.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

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 fixes a NativeAOT + trimmable typemap build-order problem that occurs in the IDE “Compile → SignAndroidPackage” split invocation flow. It ensures the outer build generates the typemap (and its typemap-assemblies.txt list) before _ResolveAssemblies spawns the inner per-RID NativeAOT/ILC build that consumes that list, preventing inner-build failures when CoreCompile is skipped as up-to-date.

Changes:

  • Add an outer-build-only target that runs _GenerateTrimmableTypeMap before _ResolveAssemblies for NativeAOT builds, ensuring the typemap assembly list exists for the inner build.
Show a summary per file
File Description
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targets Adds _EnsureTrimmableTypeMapGeneratedForNativeAotInnerBuild to force typemap generation in the outer build prior to _ResolveAssemblies spawning the inner per-RID build.

Copilot's findings

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

@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@simonrozsival simonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). labels Jun 29, 2026
@simonrozsival
simonrozsival merged commit 1dfbf6f into main Jun 29, 2026
41 checks passed
@simonrozsival
simonrozsival deleted the dev/simonrozsival/fix-nativeaot-typemap-ide-sign branch June 29, 2026 14:47
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 ready-to-review This PR is ready to review/merge, I think any CI failures are just flaky (ignorable). trimmable-type-map

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[TrimmableTypeMap] IDE Compile→SignAndroidPackage flow doesn't generate the typemap (DesignTimeBuildSignAndroidPackage)

3 participants