Skip to content

[TrimmableTypeMap] R8 shrinks bound library Java types out of classes.dex on trimmable NativeAOT (missing proguard keeps) #11835

Description

@simonrozsival

Summary

On the trimmable typemap + NativeAOT path, R8 (release shrinking) removes bound library Java types from classes.dex because the proguard config that keeps them is excluded and the trimmable path's replacement keep-config is empty. This causes BindingWithAndroidJavaSource to fail (a library's Java class is missing from the app's dex), and more generally means user/library Java types packaged via a binding project can be shrunk out of a trimmable NativeAOT app.

This is currently latent on main because NativeAOT still defaults to the managed typemap. It surfaces once NativeAOT defaults to trimmable (see the stacked PR that makes trimmable the NativeAOT default).

Repro

Build tests, Xamarin.Android.Build.Tests:

BindingWithAndroidJavaSource(NativeAOT)

Failure:

`.../App/obj/Release/android/bin/classes.dex` should include `Lcom/xamarin/android/test/msbuildtest/JavaSourceJarTest;`!
Expected: True
But was:  False

Minimal shape: a binding/class library that contributes a Java class (via @(AndroidJavaSource) / a bound jar) referenced by an app, built Release for NativeAOT with the trimmable typemap.

Root cause (verified via binlog)

  1. Release build ⇒ the dex is produced by R8 (shrinking on).
  2. R8 does receive the library jars (obj/Release/lp/1/jl/libs/*.jar) as JavaLibrariesToEmbed — the inputs are correct.
  3. But no keep rule preserves the library Java types, so R8 shrinks JavaSourceJarTest, JavaSourceTestExtension, and JavaSourceTestInterface out of the dex.
  4. The only proguard config that keeps them is proguard_project_references.cfg ($(_ProguardProjectConfiguration)), which contains:
    -keep class com.xamarin.android.test.msbuildtest.JavaSourceTestInterface { *; }
    -keep class com.xamarin.android.test.msbuildtest.JavaSourceJarTest { *; }
    -keep class com.xamarin.android.test.msbuildtest.JavaSourceTestExtension { *; }
    
    …and it is excluded from R8 on trimmable NativeAOT in Xamarin.Android.Common.targets:
    <_ProguardConfiguration Include="$(_ProguardProjectConfiguration)"
        Condition=" '$(AndroidLinkTool)' != '' and ('$(_AndroidRuntime)' != 'NativeAOT' or '$(_AndroidTypeMapImplementation)' != 'trimmable') " />
  5. The trimmable path is meant to generate keeps into proguard_project_primary.cfg instead, but on this build that file is empty — only:
    # ACW keep rules are generated from NativeAOT ILC metadata.
    
    The ACW-from-ILC generation does not cover raw java-source-jar classes or bound library Java types, so nothing keeps them.

Net: R8 gets the jars but no keep ⇒ library Java types are dropped from the dex.

Why it wasn't caught earlier

The original monolithic PR (#11617) had the same _ProguardProjectConfiguration exclusion and already defaulted NativeAOT to trimmable, and it did not skip BindingWithAndroidJavaSource. So the proguard-keep generation for trimmable NativeAOT appears to have regressed between #11617 and the current typemap-manager base branch (the extracted/refined stack), which now emits an empty proguard_project_primary.cfg for this scenario.

Suggested fixes (need a decision)

  • (A) Stop excluding proguard_project_references.cfg on trimmable NativeAOT. That file already contains the correct, complete keep rules produced by the standard ACW-map path. Simple one-line change. Risk: the exclusion was deliberate — confirm it doesn't over-keep and undermine trimming/size goals.
  • (B) Make the trimmable-NativeAOT proguard generator (GenerateNativeAotProguardConfiguration / _GenerateTrimmableTypeMapProguardConfiguration in Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targets) emit keeps for library/bound Java types into the primary config. More work; preserves the intended design.

Recommend diffing the NativeAOT proguard generation between #11617 and the current base to see exactly what regressed, which should decide A vs B.

Environment

  • Local make prepare && make all build of the typemap-default-nativeaot branch.
  • Verified by decompiling/binlog: R8 command included the lp/1/jl/libs/*.jar inputs and --pg-conf proguard_xamarin.cfg / proguard_project_primary.cfg / aapt_rules.txt but not proguard_project_references.cfg; proguard_project_primary.cfg was empty apart from the comment.

Metadata

Metadata

Assignees

No one assigned

    Labels

    needs-triageIssues that need to be assigned.

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions