[TrimmableTypeMap] Generate the typemap before the NativeAOT inner build so the IDE Compile→Sign flow works#11781
Conversation
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>
There was a problem hiding this comment.
Pull request overview
Ensures the NativeAOT trimmable typemap is generated early enough in the outer build so that IDE “Compile → SignAndroidPackage” (separate MSBuild invocations) doesn’t fail when CoreCompile is skipped and the typemap assembly list hasn’t been produced yet.
Changes:
- Adds a NativeAOT-specific target to force
_GenerateTrimmableTypeMapto run before_ResolveAssembliestriggers inner per-RID work. - Documents why the new ordering is needed (IDE split build vs single-invocation CLI 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 a pre-_ResolveAssemblies “ensure typemap generated” target for NativeAOT builds. |
Copilot's findings
- Files reviewed: 1/1 changed files
- Comments generated: 2
…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>
|
Good catch — fixed in e68ee2c. The target now gates on |
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 atobj/.../typemap/typemap-assemblies.txt. The inner build cannot generate that list:_GenerateTrimmableTypeMapis gated to the outer build ('$(_OuterIntermediateOutputPath)' == '')._GenerateTrimmableTypeMap's only trigger isAfterTargets="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) runsCompileandSignAndroidPackageas two separate MSBuild invocations. During theSignAndroidPackagecall the C# is already compiled, soCoreCompileis skipped, the typemap is never generated, and the inner ILC build fails with:A regular command-line
Buildworks because all targets run in one continuous invocation; only the IDE Compile→Sign sequence hits this. Reproduced by theDesignTimeBuildSignAndroidPackage(NativeAOT)test.Fixes #11775.
Fix
Add a target that forces the typemap to be generated in the outer build before
_ResolveAssembliesspawns the inner per-RID ILC build: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_AddTrimmableTypeMapToLinkertarget, 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.