Skip to content

Commit 1dfbf6f

Browse files
[TrimmableTypeMap] Generate the typemap before the NativeAOT inner build so the IDE Compile→Sign flow works (#11789)
### 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: ```xml <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.
1 parent 10d3a5d commit 1dfbf6f

1 file changed

Lines changed: 25 additions & 0 deletions

File tree

src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targets

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,31 @@
4141
</ItemGroup>
4242
</Target>
4343

44+
<!--
45+
Force the typemap to be generated in the OUTER build (the build that is not an inner
46+
per-RID build, i.e. '$(_OuterIntermediateOutputPath)' == '') before _ResolveAssemblies
47+
spawns the inner per-RID ILC build. The inner build consumes the typemap assembly list
48+
(via _AddTrimmableTypeMapAssembliesToIlc -> _ReadGeneratedTrimmableTypeMapAssemblies)
49+
but cannot generate it: _GenerateTrimmableTypeMap is gated to the outer build (it requires
50+
'$(_OuterIntermediateOutputPath)' == ''). _GenerateTrimmableTypeMap's own AfterTargets="CoreCompile"
51+
trigger does not fire when compilation is up-to-date - e.g. the IDE's separate "Compile" then
52+
"SignAndroidPackage" invocations (BuildingInsideVisualStudio=true) - leaving the file ungenerated
53+
and the inner build failing with "Trimmable typemap assembly list ... was not found".
54+
This mirrors the CoreCLR _AddTrimmableTypeMapToLinker target, which forces generation before the
55+
(outer) ILLink. _ResolveAssemblies runs after compilation (it consumes @(IntermediateAssembly)), so
56+
the generator still observes the compiled app assembly.
57+
58+
The condition matches on '$(_OuterIntermediateOutputPath)' == '' (the same discriminator
59+
_GenerateTrimmableTypeMap uses) rather than 'RuntimeIdentifier == ''' so that single-RID outer
60+
builds (e.g. -p:RuntimeIdentifier=android-arm64) are covered too; the inner build sets
61+
_OuterIntermediateOutputPath via _ResolveAssemblies AdditionalProperties, so this target is
62+
correctly skipped there.
63+
-->
64+
<Target Name="_EnsureTrimmableTypeMapGeneratedForNativeAotInnerBuild"
65+
Condition=" '$(_OuterIntermediateOutputPath)' == '' "
66+
BeforeTargets="_ResolveAssemblies"
67+
DependsOnTargets="_GenerateTrimmableTypeMap" />
68+
4469
<Target Name="_CollectTrimmableNativeAotDgmlFiles"
4570
Condition=" '$(PublishTrimmed)' == 'true' and '$(_ProguardProjectConfiguration)' != '' ">
4671
<ItemGroup>

0 commit comments

Comments
 (0)