Skip to content

[TrimmableTypeMap] Pre-generate Mono.Android/Java.Interop typemaps at SDK build time - #12127

Open
simonrozsival wants to merge 39 commits into
mainfrom
dev/simonrozsival/pregenerate-mono-android-typemap
Open

[TrimmableTypeMap] Pre-generate Mono.Android/Java.Interop typemaps at SDK build time#12127
simonrozsival wants to merge 39 commits into
mainfrom
dev/simonrozsival/pregenerate-mono-android-typemap

Conversation

@simonrozsival

@simonrozsivalsimonrozsival commented Jul 15, 2026

Copy link
Copy Markdown
Member

Goal

Pre-generate the trimmable type maps for Mono.Android and Java.Interop at SDK build time (issue #10792), so eligible app builds no longer rescan the framework's Java peer types on every build and startup avoids re-initializing those maps.

Scope

The pre-generated artifacts are used when both of the following are true:

  • AndroidIncludeDebugSymbols=true, which selects per-assembly typemap universes
  • every required pre-generated artifact is present

The universe shape—not whether managed trimming runs—is the eligibility boundary. A linked build with Android debug symbols still uses per-assembly universes and can reuse the artifacts. Builds without Android debug symbols use one shared universe and continue generating over the complete app closure.

Both Crc64 and LowercaseCrc64 apps can use the artifacts: Mono.Android auto-generated peer names explicitly bypass package hashing, and Java.Interop's four mapped peers have explicit JNI names.

Why single-universe builds still generate maps per app

Per-assembly-universe builds give each typemap its own private __TypeMapAnchor. The pre-generated framework DLLs therefore have the same universe shape they would have if generated by the app build. The generated root includes those existing assemblies in its normal aggregate map list. Mono.Android/Java.Interop aliases such as java/lang/Object and java/lang/Throwable remain safe because AggregateTypeMap collects matches across per-assembly universes.

Builds without Android debug symbols deliberately use one shared Java.Lang.Object universe across the complete framework and application closure. This lets MergeCrossAssemblyAliases() coordinate every managed type that maps to the same JNI name into one alias group. A framework-only artifact cannot know about aliases introduced by the application or a binding library, so combining it directly with an independently generated application map could contribute duplicate keys or incomplete alias holders to the shared dictionary.

It would be possible to put the pre-generated framework map and the application map in two separate universes for these builds. The aggregate runtime can resolve aliases across universes, but every runtime lookup would then search both dictionaries. This PR intentionally does not make that tradeoff: single-universe builds prioritize runtime lookup performance over saving framework scan time during the build.

For linked builds that retain Android debug symbols, framework entries remain conditional exactly as emitted in the pre-generated per-assembly maps; the trimmer retains entries whose target managed types survive. The app build still scans application assemblies and applies application-specific manifest/layout rooting to those peers. It does not regenerate framework maps merely to bake those roots into a different copy.

Approach

The framework typemaps are generated once during the SDK/pack build and shipped in Microsoft.Android.Sdk under data/prebuilt-typemap:

  • _Mono.Android.TypeMap.dll and _Java.Interop.TypeMap.dll, each using its normal private per-assembly __TypeMapAnchor
  • mono.android-typemaps.jar, containing the 320 precompiled Mono.Android Java Callable Wrappers
  • mono.android-acw-map.txt, used when generating complete R8 keep rules

Java.Interop contributes four typemap peers (JavaObject, JavaException, JavaProxyObject, and JavaProxyThrowable) but no generated JCWs; all four use explicit JNI names with GenerateJavaPeer=false. Keeping Java.Interop as its own small map exactly matches normal per-assembly generation.

The SDK generation pass indexes the complete framework assembly closure for type resolution while scanning Mono.Android and Java.Interop for peers. The resulting per-assembly map metadata and IL are equivalent to maps generated during an app build using per-assembly universes; only PE identity fields such as MVID/content timestamp differ by generation context.

At app build time, Mono.Android and Java.Interop remain indexed for type resolution but are not rescanned. The generated root references each shipped map via TypeMapAssemblyTarget<__TypeMapAnchor> and includes it in the same ordinally sorted aggregate universe list used by on-the-fly generation.

Additional safeguards:

  • App-generated JCW names are checked against classes already present in mono.android-typemaps.jar, preserving the XA4215 duplicate-name diagnostic.
  • CoreCLR R8 builds using per-assembly universes consume a deterministic merge of the app and framework ACW maps.
  • The generated root fingerprint includes the sorted pre-generated map set.
  • Empty app-peer sets still produce the merged Android manifest and framework root references.

Build-time measurements

Measured on a clean dotnet new maui Android app on an Apple Silicon Mac:

  • Debug net11.0-android
  • AndroidTypeMapImplementation=trimmable
  • Five builds before and five builds after, run in balanced alternating order
  • bin/Debug and obj/Debug removed before every build
  • Every measured build used --no-restore
  • One binlog recorded per build
MeasurementBeforeAfterImprovement
GenerateTrimmableTypeMap task mean5.352s2.933s2.419s / 45.2%
GenerateTrimmableTypeMap task median5.054s2.916s2.138s / 42.3%
Generator task range4.922–6.507s2.787–3.121s
_GenerateTrimmableTypeMap target mean5.359s2.945s2.414s / 45.0%
Whole build mean57.634s55.350s2.284s / 4.0%

“Before” explicitly disables the prebuilt artifacts with _AndroidUsePreGeneratedMonoAndroidTypeMap=false; “after” enables them. The before build generated 82 typemap assemblies, including _Mono.Android.TypeMap.dll and _Java.Interop.TypeMap.dll. The after build generated 80 and consumed both framework typemaps from the SDK pack.

A subsequent 5-build measurement found that adding the per-assembly typemap DLLs initially increased ProcessAssemblies from a 0.122s mean on LLVM-IR builds to 2.731s on trimmable builds. The generated publish items are marked with their known HasMonoAndroidReference=true metadata, avoiding 82 redundant PE opens and assembly-reference scans. With that change, ProcessAssemblies has a 0.122s median (0.162s mean, including a 0.383s cold run), down from a 3.132s median before the optimization.

Verification

  • 842 Microsoft.Android.Sdk.TrimmableTypeMap.Tests pass.
  • 18 focused GenerateTrimmableTypeMapTests pass.
  • Pre-generated Mono.Android and Java.Interop maps match app-generated metadata and IL under both Crc64 and LowercaseCrc64 after normalizing only the MVID and deterministic PE content ID.
  • Focused app-build coverage passes for Debug CoreCLR PublishAot=true, CoreCLR+R8, both package naming policies, and a Full-linked Release build with Android debug symbols reusing the pre-generated maps.
  • Existing CoreCLR/NativeAOT Debug/Release typemap matrix passes for all supported configurations (Debug NativeAOT remains unsupported and skipped).
  • dotnet build Xamarin.Android.slnx and make pack-dotnet pass.
  • The generated SDK package contains both framework typemap assemblies, the 320-class JCW JAR, and the framework ACW map.
  • Repeated CoreCLR+R8 builds using pre-generated maps preserve the merged ACW map and skip _CompileToDalvik when inputs are unchanged.

simonrozsivaland others added 10 commits July 15, 2026 20:23
…root
Toward pre-generating Mono.Android's typemap at SDK build time (issue #10792):
add a generateRootAssembly flag to TrimmableTypeMapGenerator.Execute (and a
GenerateRootAssembly property on the GenerateTrimmableTypeMap task, default true).
When false, the per-assembly typemap DLLs (e.g. _Mono.Android.TypeMap) and JCW
Java sources are emitted, but the root _Microsoft.Android.TypeMaps assembly
(TypeMapAssemblyTarget<T> attributes + TypeMapLoader.Initialize) is not: that
root is emitted by the app build, which will reference the pre-generated
per-assembly typemap alongside the app's own.
Adds a unit test asserting the per-assembly typemap is produced and the root is
omitted when generateRootAssembly is false.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
…nder Java.Lang.Object
Special-case pre-generated framework typemaps (e.g. _Mono.Android.TypeMap, built at
SDK build time per issue #10792) in the root typemap assembly. Such typemaps always
use Java.Lang.Object as their universe anchor, so a single artifact serves both
app-build universe modes:
- RootTypeMapAssemblyGenerator.Generate gains sharedFrameworkTypeMapNames. They are
emitted as [assembly: TypeMapAssemblyTarget<Java.Lang.Object>("name")] regardless of
useSharedTypemapUniverse.
* Merged (Release): they join the single Java.Lang.Object universe, so
GetOrCreateExternalTypeMapping<Java.Lang.Object>() merges them with the app's
entries automatically — no Initialize change.
* Aggregate (Debug): TypeMapLoader.Initialize adds the Java.Lang.Object universe as
element [0] alongside the app's per-assembly __TypeMapAnchor universes.
- Threaded through TrimmableTypeMapGenerator.Execute/GenerateTypeMapAssemblies.
- Aggregate + array maps (maxArrayRank > 0) with a shared framework universe throws
NotSupportedException for now (tracked follow-up).
Adds unit tests asserting the framework typemap is referenced under Java.Lang.Object
(scope Mono.Android) in both modes, the app typemap keeps its own anchor in aggregate
mode, and both produce valid PE. 619 tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
Milestone: generate _Mono.Android.TypeMap.dll (issue #10792) once during the SDK/pack
build instead of rescanning Mono.Android (~8000 types) on every app build.
- GenerateTrimmableTypeMap task: add ForceSharedTypemapUniverse. When true the typemap
always uses the Java.Lang.Object universe anchor regardless of Debug, so the single
pre-generated artifact is consumable by both app-build universe modes.
- build-tools/create-packs/Microsoft.Android.Sdk.TrimmableTypeMap.targets: new
_GeneratePreBuiltMonoAndroidTypeMap target scans the built Mono.Android.dll (framework
assemblies supplied for reference only) with ForceSharedTypemapUniverse=true and
GenerateRootAssembly=false, emitting _Mono.Android.TypeMap.dll + JCW Java sources; and
_AddPreBuiltMonoAndroidTypeMapToPackage ships the DLL in the SDK pack under
data/prebuilt-typemap. Imported by Microsoft.Android.Sdk.proj.
Verified locally: the target produces _Mono.Android.TypeMap.dll (5.4 MB) with no
__TypeMapAnchor (i.e. anchored on Java.Lang.Object), 320 JCW .java files, and no root
_Microsoft.Android.TypeMaps assembly.
JCW jar compilation + app-build consumption follow in later milestones.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
…ack layout
ConfigureLocalWorkload (used by 'make all' and -t:InstallMaui) does not run the NuGet
GetFilesToPackage flow, so the FilesToPackage hook alone did not place the pre-generated
typemap into the local pack. Write _Mono.Android.TypeMap.dll directly into the pack layout
at $(MicrosoftAndroidSdkPackDir)data\prebuilt-typemap\ and run the generation
BeforeTargets=ConfigureLocalWorkload as well as _GenerateXASdkContent, so the artifact is
present for both local workload testing and the NuGet pack.
Verified: after ConfigureLocalWorkload, _Mono.Android.TypeMap.dll (5.4 MB) is present under
bin/.../packs/Microsoft.Android.Sdk.Darwin/<ver>/data/prebuilt-typemap/.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
…pemaps.jar
Compile the pre-generated Java Callable Wrappers (issue #10792) into a jar shipped in the
SDK pack under data/prebuilt-typemap, so the app build can dex them directly instead of
re-generating and re-compiling the ~320 Mono.Android JCWs.
- _CompilePreBuiltMonoAndroidTypeMapJcwJar javac-compiles the generated JCW .java into
mono.android-typemaps.jar. Classpath mirrors GenerateJavaCallableWrappers:
android.jar + mono.android.jar + java_runtime.jar.
- Ship the jar alongside _Mono.Android.TypeMap.dll (FilesToPackage + local pack layout).
Verified: mono.android-typemaps.jar (403 entries) is produced under
bin/.../packs/Microsoft.Android.Sdk.Darwin/<ver>/data/prebuilt-typemap/.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
…map refs in the task
Adds the generator/scanner/task plumbing the app build needs to consume a pre-generated
Mono.Android typemap (issue #10792):
- AssemblyInput gains ScanForPeers. When false the assembly is indexed for base-type
resolution but not scanned for peer emission; JavaPeerScanner skips ScanAssembly for it.
- GenerateTrimmableTypeMap task gains PreGeneratedTypeMapAssemblies: those assemblies are
marked ScanForPeers=false, and their _<Name>.TypeMap names are passed to the generator as
sharedFrameworkTypeMapNames so the generated root references them under Java.Lang.Object.
Adds a unit test asserting a reference-only assembly emits no peers/typemap. 620 tests pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
Wire the app build to use the SDK-shipped Mono.Android typemap (issue #10792) instead of
rescanning Mono.Android's ~8000 types on every build:
- Microsoft.Android.Sdk.TypeMap.Trimmable.targets: when the pre-generated typemap exists in
the pack (data/prebuilt-typemap, gated by $(_AndroidUsePreGeneratedMonoAndroidTypeMap)),
pass Mono.Android to GenerateTrimmableTypeMap as a PreGeneratedTypeMapAssembly (indexed for
resolution, not rescanned); add the pre-built _Mono.Android.TypeMap.dll to the generated
typemap set so it is linked/published and referenced by the root under Java.Lang.Object; and
add mono.android-typemaps.jar to @(AndroidJavaLibrary) for dexing.
- create-packs targets: emit generated JCW .java / .class to obj intermediate so only the
_Mono.Android.TypeMap.dll and jar are packaged.
Verified (Release NativeAOT, _AndroidTypeMapImplementation=trimmable): the app build no longer
generates _Mono.Android.TypeMap (only _Java.Interop/_Microsoft.Android.Runtime.NativeAOT/_NativeAOT
+ root), and the root references the pre-built _Mono.Android.TypeMap.
KNOWN GAP (next milestone): R8 shrinks the pre-built Mono.Android JCWs from classes.dex because
their ACW entries are no longer in the app acw-map (Mono.Android excluded from scan), so no keep
rules are generated. The pre-generated typemap's ACW keep rules must be shipped and fed to R8
before on-device runs will work.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
…r R8 keep rules
With Mono.Android excluded from the app scan, its ACW entries were absent from the app
acw-map, so R8 generated no keep rules and shrank the pre-built Mono.Android JCWs out of
classes.dex. Ship the pre-generated typemap's ACW map and merge it back in:
- create-packs: GenerateTrimmableTypeMap now also writes mono.android-acw-map.txt, shipped in
the pack under data/prebuilt-typemap.
- App build: when the pre-generated typemap is used, append mono.android-acw-map.txt to the
app's acw-map.txt after generation, so the proguard/R8 keep-rule generators (CoreCLR and
NativeAOT) emit -keep rules for the pre-built Mono.Android JCWs.
Verified (Release NativeAOT, trimmable): classes.dex returns to ~255 KB (was ~13 KB when the
JCWs were shrunk), i.e. the pre-built Mono.Android JCWs are retained.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
… fix array rank
On-device (Release NativeAOT, trimmable) the app crashed at startup with BadImageFormatException
in TypeMapLazyDictionary.CreateExternalTypeMap. Two causes, both fixed:
1. Duplicate java/lang/Object in the merged Java.Lang.Object universe: the pre-built Mono.Android
typemap mapped it to Java.Lang.Object while the app-scanned _Java.Interop.TypeMap mapped it to
Java.Interop.JavaObject. MergeCrossAssemblyAliases coordinates these only within a single scan.
Fix: pre-generate Mono.Android AND Java.Interop together (aliases coordinated) and exclude both
from the app scan; ship/reference all pre-built *.TypeMap.dll.
2. Array-rank mismatch: the pre-built typemap used MaxArrayRank=0 while the app root/runtime used
the AOT default (3), so array entries landed in the main universe. Fix: generate the pre-built
typemap with MaxArrayRank=3, and have the root reference the framework typemaps' __ArrayMapRank{N}
universes too.
Verified on a physical arm64 device (API 36): the Release/single-universe trimmable NativeAOT app
launches to MainActivity with no BadImageFormatException / crash. 620 unit tests still pass.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
…egate + array maps)
The aggregate-universe + array-maps (maxArrayRank > 0) combination — i.e. Debug NativeAOT — is
not yet wired for pre-generated framework typemaps and previously threw NotSupportedException,
which would break those builds. Gracefully disable the pre-generation for that combination so the
build falls back to scanning Mono.Android normally (correct, just without the speedup) instead of
failing.
Verified: Debug NativeAOT trimmable now builds (Mono.Android scanned in-app), while Release
NativeAOT (single universe) and CoreCLR Debug (aggregate universe) continue to use the
pre-generated typemap and are device-verified.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc

@simonrozsivalsimonrozsival left a comment

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

⚠️ Needs Changes (self-review via android-reviewer)

Note: posted as a COMMENT review since GitHub disallows REQUEST_CHANGES on one's own PR.

Solid, well-staged implementation of #10792; the design (pre-generate Mono.Android + Java.Interop together under the Java.Lang.Object universe so one artifact serves both universe modes) is sound and device-verified in both modes (Release NativeAOT single-universe; CoreCLR Debug aggregate). A few correctness/robustness items before merge, plus the documented Debug-NativeAOT limitation.

Counts: ❌ 0 · ⚠️ 2 · 💡 2

Positives:

  • Clear milestone-by-milestone commits with informative messages.
  • Good unit coverage for the new generator paths (reference-only assemblies, generateRootAssembly, framework-universe target attributes).
  • Graceful fallback for the unsupported Debug-NativeAOT combo avoids a hard build regression.

Before merge:

  1. ⚠️ Fix incremental Outputs for the pre-generation target (now produces two typemaps).
  2. ⚠️ Document/justify the hardcoded MaxArrayRank="3" coupling.
  3. 💡 File a tracking issue for the arrays-aggregate NotSupportedException path.
  4. 💡 Reconsider mutating the app acw-map.txt in place.

CI hasn't run on this branch yet — not mergeable until the dotnet-android pipeline is green.

Comment threadbuild-tools/create-packs/Microsoft.Android.Sdk.TrimmableTypeMap.targets Outdated
Comment threadbuild-tools/create-packs/Microsoft.Android.Sdk.TrimmableTypeMap.targets Outdated
simonrozsivaland others added 3 commits July 16, 2026 00:39
- Track both pre-built typemaps in _GeneratePreBuiltMonoAndroidTypeMap
Outputs (_Mono.Android.TypeMap.dll + _Java.Interop.TypeMap.dll) so the
incremental state is not broken if the Java.Interop typemap is deleted.
- Replace the hardcoded MaxArrayRank="3" with a documented
_PreBuiltTypeMapMaxArrayRank property explaining it must match the app
build's effective rank (BadImageFormatException otherwise).
- Reference the tracking issue (#12128) from the arrays-aggregate
NotSupportedException and the Debug-NativeAOT fallback comment.
- Stop mutating the app's acw-map.txt in place. Instead build a separate
deterministic acw-map.prebuilt-merged.txt (app map + pre-built framework
map, Overwrite=true) and feed that to the NativeAOT R8 keep-rule
generator, so the app's acw-map.txt stays byte-stable and its own
incremental checks keep working (no double-append).
Verified: 620 TrimmableTypeMap unit tests pass; Release NativeAOT
(trimmable, R8) sample keeps the pre-built JCWs (classes.dex ~255KB, 310
mono.android keep rules) and launches on device without crashing, while
acw-map.txt is no longer mutated.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 867395ec-6705-4ed6-8a68-e25b58d431fc
Resolve trimmable typemap conflicts after array proxy map removal and preserve pre-generated framework typemap support.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove the obsolete array-rank input, package framework typemap DLLs as SDK runtime data without NU5100, and prevent post-trim CoreCLR generation from rescanning framework assemblies.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsival marked this pull request as ready for review August 19, 2026 10:44
CopilotAI lite review requested due to automatic review settings August 19, 2026 10:44

CopilotAI 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 moves generation of the large framework trimmable typemaps (Mono.Android + Java.Interop) from app build-time to SDK/pack build-time, then updates the app build to consume the prebuilt artifacts to reduce incremental build cost and startup initialization.

Changes:

  • Add SDK-pack build targets to pre-generate and package _Mono.Android.TypeMap.dll, _Java.Interop.TypeMap.dll, mono.android-typemaps.jar, and mono.android-acw-map.txt.
  • Update trimmable typemap generation to support (1) skipping peer scanning for “reference-only” assemblies and (2) emitting a root assembly that can also reference prebuilt framework typemaps under Java.Lang.Object.
  • Update app build targets/tests to avoid regenerating framework typemaps and to merge ACW maps for NativeAOT keep-rule generation when using prebuilt artifacts.
Show a summary per file
FileDescription
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.csAdds coverage for generateRootAssembly:false and reference-only scanning.
tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/RootTypeMapAssemblyGeneratorTests.csAdds coverage for shared-framework typemap references anchored on Java.Lang.Object.
src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/TrimmableTypeMapBuildTests.csUpdates build assertions to reflect prebuilt framework typemap consumption.
src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.csAdds task inputs to skip scanning prebuilt assemblies and to control root/shared-universe behavior.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targetsAdds app-build consumption of prebuilt typemap DLLs and precompiled JCW jar; introduces keep-rule ACW map selection.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targetsMerges app + framework ACW maps for R8 keep rules when using prebuilt framework typemap.
src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targetsEnsures post-trim typemap generation also treats framework assemblies as prebuilt reference-only inputs.
src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.csAdds options to skip root emission and to pass shared-framework typemap names to the root generator.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.csSupports “reference-only” inputs: index for type resolution but skip peer emission.
src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/RootTypeMapAssemblyGenerator.csAdds support for emitting TypeMapAssemblyTarget<Java.Lang.Object> entries for shared framework typemaps and adjusts aggregate loader init.
src/Microsoft.Android.Sdk.TrimmableTypeMap/AssemblyInput.csExtends AssemblyInput with ScanForPeers.
build-tools/create-packs/Microsoft.Android.Sdk.TrimmableTypeMap.targetsIntroduces SDK-pack build targets to generate and package prebuilt typemap artifacts + JCW jar.
build-tools/create-packs/Microsoft.Android.Sdk.projImports the new SDK-pack typemap generation targets.

Review details

  • Files reviewed: 13/13 changed files
  • Comments generated: 2
  • Review effort level: Lite

Comment threadbuild-tools/create-packs/Microsoft.Android.Sdk.TrimmableTypeMap.targets Outdated
simonrozsivaland others added 3 commits August 19, 2026 15:00
Mark generated typemap publish items as known Mono.Android references so ProcessAssemblies does not reopen and scan every per-assembly typemap DLL.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Reuse the framework JCWs already shipped in mono.android.jar instead of packaging a duplicate JAR. Generate CoreCLR R8 keep rules from the linked Mono.Android assembly so trimmed framework implementors can still be removed.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsival marked this pull request as draft August 20, 2026 11:09
simonrozsivaland others added 6 commits August 20, 2026 15:25
Use pre-generated framework typemaps only for Debug per-assembly universes, generate Release shared-universe maps from the complete application closure, and package mono.android.jar for the retained pre-generated path.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ccdb863-10f6-4e6d-8cd4-9085e63fda6c
Resolve mono.android.jar through the established platform-jar path before static resources, Java compilation, and dex compilation. Add coverage that verifies a framework listener implementor reaches classes.dex.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ccdb863-10f6-4e6d-8cd4-9085e63fda6c
Compile the framework wrappers generated with the prebuilt typemap into a dedicated SDK jar. These wrappers use registerNatives and avoid the legacy TypeManager.n_activate path that CoreCLR trimmable apps do not export.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ccdb863-10f6-4e6d-8cd4-9085e63fda6c
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: 0ccdb863-10f6-4e6d-8cd4-9085e63fda6c
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@simonrozsival
simonrozsival marked this pull request as ready for review August 21, 2026 10:01
@github-actions

github-actionsBot commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12127

@github-actionsgithub-actionsBot 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.

❌ Reject

Found 3 errors in the pre-generated framework typemap pipeline:

  • valid apps with no managed Java peers do not emit the root typemap assembly needed to activate the prebuilt maps;
  • incremental builds can retain a root assembly whose framework-map references reflect the previous configuration;
  • SDK pack incrementality can leave mono.android-typemaps.jar stale relative to newly generated Java sources.

The separation between framework maps and the app root is otherwise clear, and the added build coverage exercises the normal Debug/Release paths well. The latest Azure build is still in progress with no reported failure in the completed checks, so CI does not yet provide a final result.

Generated by Android PR Reviewer for #12127 · gpt56 · 231.9 AIC · ⌖ 9.36 AIC · ⊞ 25.7K
Comment /review to run again

Comment threadsrc/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs Outdated
Comment threadsrc/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs Outdated
Comment threadbuild-tools/create-packs/Microsoft.Android.Sdk.TrimmableTypeMap.targets Outdated
Generate the root for framework-only maps, content-compare root updates, track the generated Java source set, and require every packaged artifact before enabling pre-generated typemaps.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
MemberAuthor

/review

@github-actions

github-actionsBot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12127

@github-actionsgithub-actionsBot 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.

⚠️ Needs Changes

Found 1 warning: the generated javac response file does not quote Java source paths, so SDK pack builds fail when the checkout or intermediate path contains spaces.

The pre-generated typemap design is otherwise coherent, prior incremental-generation concerns are addressed, and the added generator/build coverage exercises the key Debug, Release, CoreCLR, and NativeAOT paths. All 44 CI checks passed, including the full Azure DevOps matrix and CLA.

Generated by Android PR Reviewer for #12127 · gpt56 · 671.7 AIC · ⌖ 13.5 AIC · ⊞ 25.7K
Comment /review to run again

Comment threadbuild-tools/create-packs/Microsoft.Android.Sdk.TrimmableTypeMap.targets Outdated
simonrozsivaland others added 15 commits August 26, 2026 09:22
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b5948298-9cc2-4aba-aaf6-8a40a21981c9
Quote generated Java source paths and escape Windows path separators so javac response files work when the repository path contains spaces.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: b5948298-9cc2-4aba-aaf6-8a40a21981c9
Preserve reference-only framework scanning alongside the latest typemap resolver, custom-view rooting, and marshal-method optimizations. Keep manifest generation active for the now-reachable empty app-peer case.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Limit pre-generated framework maps to untrimmed Crc64 builds, preserve complete resolver inputs, validate app JCWs against the shipped jar, and keep framework wrappers through untrimmed CoreCLR R8. Cover empty-peer manifests and fallback paths.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Prevent the private override from enabling pre-generated framework maps in trimmed builds, and identify the shipped JCW jar directly in duplicate-name diagnostics.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
PublishAot implies trimming even in Debug, so it must generate framework typemaps locally. Make the helper assert pre-generated-map eligibility directly instead of inferring it from IsRelease.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve current main's fingerprint-based emission skipping while including the shared pre-generated framework map set in root invalidation.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
PublishAot sets PublishTrimmed in the .NET SDK, but Debug Android builds keep AndroidLinkMode=None and do not run ILLink. Gate pre-generated framework maps on AndroidLinkMode and preserve the pre-generated inputs through CoreCLR post-processing.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Generate Mono.Android and Java.Interop with their normal per-assembly anchors, consume them as ordinary aggregate universes, and remove the package naming policy restriction. Keep app-time framework rooting only for linked builds and verify equivalent metadata and IL.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The merged app and framework ACW map already supplies precise R8 keep rules, so a separate trimmable CoreCLR configuration and task switch are unnecessary.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use pre-generated framework maps whenever typemap generation uses per-assembly universes, including trimmed builds with Android debug symbols. Keep shared-universe builds on full app-time generation for cross-assembly alias coordination.
Tighten typemap parity checks to normalize only the MVID and deterministic PE content ID, and clean up the related MSBuild wiring and documentation.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Generate a lightweight linker descriptor from the shipped framework ACW map so manifest- and layout-only framework references survive trimming without rescanning Mono.Android. Propagate the descriptor to per-RID builds and cover PublishAOT and no-op incrementality.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use the merged framework ACW map for both CoreCLR and NativeAOT R8 configuration while limiting the additional dexing input to pre-generated typemap builds. Isolate framework-root stamp invalidation and improve malformed ACW map diagnostics.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Locate NativeAOT ProGuard configurations under their per-RID intermediate directories instead of assuming an outer-build output path.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Sign up for freeto 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.

2 participants

@simonrozsival