Skip to content

[Microsoft.Android.Build.Tasks] Generate typemap ProGuard rules - #12821

Closed
simonrozsival wants to merge 25 commits into
mainfrom
simonrozsival-trimmed-typemap-proguard
Closed

simonrozsival wants to merge 25 commits into
mainfrom
simonrozsival-trimmed-typemap-proguard

Conversation

@simonrozsival

@simonrozsival simonrozsival commented Sep 18, 2026

Copy link
Copy Markdown
Member

Why

R8 cannot see managed-to-Java JNI references. Keeping every entry in the
pre-trim ACW map preserves Java classes that managed trimming no longer
needs, while reading Register attributes is not a common representation
for all typemap implementations.

Generate Java class roots from the retained typemap instead, using a small
shared intermediate format and runtime-specific adapters.

Related to #10790, #11767, and #12814.

Implementation

All new tasks live in Microsoft.Android.Build.Tasks, under the
Microsoft.Android.Tasks namespace.

Runtime / typemap Retained-key adapter input
CoreCLR / LLVM IR Generated .ll Java-name blobs
CoreCLR / trimmable Linked typemap DLLs and surviving TypeMap attributes
NativeAOT / trimmable Final ILC native objects, selecting Java typemap groups through their fixups

Adapters emit ordinal-sorted, distinct JNI class names, one per line,
UTF-8 without a BOM, with LF endings. Representation-specific aliases and
array keys are normalized before producing the shared class-name file.
Missing or malformed required inputs fail rather than silently producing
an empty retention policy.

<GenerateTypeMapProguardConfiguration/> emits class-only keep rules.
Separate configuration handles runtime bootstrap roots and member
preservation. CoreCLR keeps all members of retained JNI-facing classes;
Java-only dependencies remain eligible for member shrinking. NativeAOT
retains the conservative all-members policy. There is no precise
JNI-method inference or runtime obfuscation work here.

The pipeline runs after the relevant trimming/typemap producer and before
R8. Exact per-RID producer paths are propagated and unioned; source-list
and property changes, deleted outputs, incremental builds and Clean are
covered.

NativeAOT no longer generates or scans DGML graphs for this purpose. Its
object adapter currently requires NDK LLVM tools and is explicitly opt-in.
The private $(_AndroidEnableTypemapR8Trimming) override disables or
selects the new pipeline; supported trimmed CoreCLR R8 builds use it
automatically when the override is unset. Complete
$(ProguardConfigFiles) overrides retain their existing behavior.

The active CoreCLR path enables R8 optimization while keeping obfuscation
disabled. Other runtime paths retain their configuration selection.

Validation

  • 421 focused tests passed with warnings treated as errors and no skips,
    including real NDK object fixtures, linked-assembly extraction, emitted
    LLVM layouts, shared rule generation and MSBuild incremental contracts.
  • Built, installed and launched stock dotnet new android and MAUI
    sample-content applications on ARM64, including Samsung A16 runs for
    both CoreCLR typemaps and NativeAOT with trimmable typemaps.
  • Repeated startup comparisons used matching MIBC profiles where
    applicable. Benchmark/profile artifacts and the reverted precise-member
    proof of concept are not part of this PR.

For MAUI sample-content with full managed trimming, Release/ARM64 and
obfuscation disabled, total uncompressed DEX sizes were:

Runtime / typemap Legacy rules Retained-typemap pipeline
CoreCLR / LLVM IR 4,876,916 bytes 3,314,404 bytes
CoreCLR / trimmable 4,564,308 bytes 3,186,728 bytes
NativeAOT / trimmable 13,070,696 bytes 7,489,768 bytes

CoreCLR comparisons include enabling R8 optimization. With optimization
disabled on both sides, the respective DEX reductions were 3.85% and
0.086%; the NativeAOT reduction remained 42.70%. The Java-policy toggle
did not materially improve startup with matching MIBC profiles already
applied; this is principally a size improvement.

The MAUI template used the same compatible preview6 dependency pin on both
sides because its original preview7 dependencies produced CS1705 against
the locally built SDK. Trimming warnings were not suppressed.
NativeAOT/full sample-content builds retain a Dashboard-loading limitation
with both rule policies; this PR does not claim to fix it.

Scope and review organization

Visual Studio 2026 provides the supported hosting for these .NET tasks.
Standalone .NET Framework CLI MSBuild is outside the support scope; it is
not a readiness blocker for this change.

NativeAOT remains opt-in until the native-object tooling can be supplied
without requiring an NDK. NativeAOT with the LLVM typemap is unsupported
and is excluded from the results.

This changeset is being split into a stack of smaller review units. This
source PR will remain open until the replacement stack is complete and
verified.

simonrozsival and others added 25 commits September 18, 2026 09:03
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Parse the named Java type-name blobs in release and debug CoreCLR LLVM IR, supporting emitted byte strings and legacy integer arrays. Validate required inputs and blob data before writing canonical sorted keys, and cover emitter output and malformed inputs with focused task tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Inspect surviving assembly TypeMapAttribute metadata and exercise emitted assemblies through the pinned real ILLink in standalone tests. Source-link retained-key tasks for repeatable standalone coverage.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Reject absent inputs, invalid assembly metadata and malformed typemap attributes. Reuse canonical name validation after decimal alias normalization. Resolve all fixture references for strict real-ILLink coverage and source-link the LLVM adapter for standalone tests.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Relocate ExtractTypeMapKeysFromLlvmIr to Microsoft.Android.Build.Tasks and use its source-linked shared diagnostics. Move the emitter-backed NUnit fixture to the modern task test project using its existing aliased reference to the LLVM emitter. Preserve the task API, key format, and parser behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Link existing metadata helpers into the modern task assembly and test its production binaries directly instead of source-linking task copies.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Wire the selected modern adapter after its final producer, union per-RID inputs, generate class-only roots, and keep member retention and non-obfuscation policy separate. Track source lists and mode changes while preserving legacy and explicit override paths.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Honor _AndroidEnableTypemapR8Trimming without a default: false selects legacy retention and avoids automatic DGML, while true/unset retain supported pipeline eligibility. Invalidate stale generated rules across switch transitions.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Use _AndroidEnableTypemapR8Trimming as the sole product switch. Consume DGML only through retained-key extraction; retain optimized-only automatic selection and explicit codegen graph opt-in without the old NativeAOT switches.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Map object arrays to their canonical element class and omit primitive arrays, which do not represent Java class definitions. Keep malformed descriptors and numeric aliases invalid for LLVM inputs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
ILC emits built-in types such as int32 without assembly-qualified names. Ignore the known intrinsic labels while preserving strict validation of malformed managed identities.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep class-root generation separate from scoped JNI member rules and preserve explicit CoreCLR runtime bootstrap members. Leave NativeAOT's existing policy unchanged. Normalize the modern task assembly path for incremental inputs and cover no-op/deleted-output builds plus real inherited JNI method/field access and Java-only dependency removal.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Stream relevant LLVM symbol records and bound relocation queries to avoid whole-symbol/relocation JSON allocations.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Read each RID's actual NativeObject with explicit NDK LLVM tools, select Java groups structurally, and preserve no-NDK default builds through the private opt-in. Remove automatic graphs and retire DGML diagnostics. Reconcile scoped CoreCLR member rules and normalized incremental inputs.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Keep runtime interface-collection assertions and graph-free pipeline regressions, while removing obsolete graph analysis, fixtures, serialization, and guidance.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Select the optimizing Android R8 defaults for the active CoreCLR typemap pipeline while preserving -dontobfuscate, --no-minification and all keep rules. Leave NativeAOT, legacy modes and complete configuration overrides unchanged. Cover actual config selection and R8 metadata, and allow optimizer inlining of Java-only helpers in the JNI device regression.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival
simonrozsival marked this pull request as ready for review September 18, 2026 14:47
Copilot AI lite review requested due to automatic review settings September 18, 2026 14:47

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.

Copilot review overview

🟡 Changes recommended

Five moderate issues remain in exception handling, target gating/override behavior, and NativeAOT test capability detection.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

This PR generates retained-typemap-based ProGuard rules for CoreCLR and opt-in NativeAOT builds, replacing broad ACW/DGML retention.

Changes:

  • Adds LLVM IR, assembly, and NativeAOT object typemap extraction.
  • Updates MSBuild/R8 integration and incremental build behavior.
  • Adds rule-generation tasks, tests, diagnostics, and documentation.
  • Review found five unresolved issues requiring changes.
File Summary
tests/​MSBuildDeviceIntegration/​Tests/​ScopedTypeMapR8Tests.cs Device R8 coverage
tests/​MSBuildDeviceIntegration/​Tests/​InterfaceCollectionTests.cs Interface collection coverage
tests/​MSBuildDeviceIntegration/​Tests/​InterfaceCollectionRootingTests.cs Rooting coverage
tests/​MSBuildDeviceIntegration/​Resources/​InterfaceCollectionApp/​rooting.dgml.xml DGML fixture
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​TypeMapTaskBuildEngine.cs Test build-engine helpers
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​NativeAotObjectIntegrationTests.cs NativeAOT integration tests; clang capability check needs updating
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests.csproj Test project configuration
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​Generator/​NativeAotObjectTestFixture.Tool.cs NativeAOT tool fixture
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​Generator/​NativeAotObjectTestFixture.cs NativeAOT object fixture
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​Generator/​ExtractTypeMapKeysFromLlvmIrArrayTests.cs LLVM key extraction tests
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​Generator/​ExtractTypeMapKeysFromAssembliesTests.cs Assembly key extraction tests
src/​Xamarin.Android.Build.Tasks/​Xamarin.Android.D8.targets Tracks generated ProGuard inputs
src/​Xamarin.Android.Build.Tasks/​Xamarin.Android.Common.targets R8 property and pipeline integration
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​Utilities/​BaseTest.cs Build test utilities
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​TrimmableTypeMapBuildTests.cs Trimmable typemap build coverage
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​Tasks/​R8Tests.cs R8 task tests
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​Tasks/​GenerateNativeAotProguardConfigurationTests.cs NativeAOT rule-generation tests
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​BuildTest2.cs Build regression coverage
src/​Xamarin.Android.Build.Tasks/​Tasks/​R8.cs Applies typemap R8 policies
src/​Xamarin.Android.Build.Tasks/​Tasks/​GenerateNativeAotProguardConfiguration.cs NativeAOT configuration handling
src/​Xamarin.Android.Build.Tasks/​Resources/​proguard_typemap.cfg Shared ProGuard rules
src/​Xamarin.Android.Build.Tasks/​Resources/​proguard_typemap_coreclr.cfg CoreCLR ProGuard rules
src/​Xamarin.Android.Build.Tasks/​Resources/​proguard_trimmable_nativeaot.cfg NativeAOT ProGuard rules
src/​Xamarin.Android.Build.Tasks/​Properties/​Resources.resx Localized resources
src/​Xamarin.Android.Build.Tasks/​Properties/​Resources.Designer.cs Generated resource accessors
src/​Xamarin.Android.Build.Tasks/​Microsoft.Android.Sdk/​targets/​Microsoft.Android.Sdk.TypeMap.Trimmable.NativeAOT.targets NativeAOT pipeline
src/​Xamarin.Android.Build.Tasks/​Microsoft.Android.Sdk/​targets/​Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets CoreCLR pipeline
src/​Xamarin.Android.Build.Tasks/​Microsoft.Android.Sdk/​targets/​Microsoft.Android.Sdk.TypeMap.Proguard.targets ProGuard pipeline; eligibility and override guards need correction
src/​Xamarin.Android.Build.Tasks/​Microsoft.Android.Sdk/​targets/​Microsoft.Android.Sdk.TypeMap.LlvmIr.targets LLVM typemap pipeline
src/​Xamarin.Android.Build.Tasks/​Microsoft.Android.Sdk/​targets/​Microsoft.Android.Sdk.AssemblyResolution.targets Assembly resolution integration
src/​Microsoft.Android.Build.Tasks/​Utilities/​TypeMapKey.cs Typemap key representation
src/​Microsoft.Android.Build.Tasks/​Utilities/​TypeMapClassName.cs JNI class-name normalization
src/​Microsoft.Android.Build.Tasks/​Utilities/​NativeAotTypeMapReader.cs NativeAOT typemap parsing
src/​Microsoft.Android.Build.Tasks/​Tests/​Microsoft.Android.Build.Tasks.Tests/​GenerateTypeMapProguardConfigurationTests.cs Rule-generation tests
src/​Microsoft.Android.Build.Tasks/​Tests/​Microsoft.Android.Build.Tasks.Tests/​ExtractTypeMapKeysFromLlvmIrTests.cs LLVM extraction tests
src/​Microsoft.Android.Build.Tasks/​Tasks/​GenerateTypeMapProguardConfiguration.cs Generates class keep rules
src/​Microsoft.Android.Build.Tasks/​Tasks/​GenerateTypeMapMemberProguardConfiguration.cs Generates member keep rules
src/​Microsoft.Android.Build.Tasks/​Tasks/​ExtractTypeMapKeysFromNativeAotObject.cs Extracts NativeAOT keys; malformed UTF-8 handling needs correction
src/​Microsoft.Android.Build.Tasks/​Tasks/​ExtractTypeMapKeysFromLlvmIr.cs Extracts LLVM keys; malformed UTF-8 handling needs correction
src/​Microsoft.Android.Build.Tasks/​Tasks/​ExtractTypeMapKeysFromAssemblies.cs Extracts assembly keys
src/​Microsoft.Android.Build.Tasks/​Microsoft.Android.Build.Tasks.csproj Task project configuration
Documentation/​guides/​D8andR8.md R8 pipeline documentation
Documentation/​docs-mobile/​TOC.yml Documentation navigation
Documentation/​docs-mobile/​messages/​xa4328.md Diagnostic documentation
Documentation/​docs-mobile/​messages/​xa4327.md Diagnostic documentation
Documentation/​docs-mobile/​messages/​xa4321.md Diagnostic documentation
Documentation/​docs-mobile/​messages/​xa4320.md Diagnostic documentation
Documentation/​docs-mobile/​messages/​xa4319.md Diagnostic documentation
Documentation/​docs-mobile/​messages/​index.md Diagnostic index
Documentation/​docs-mobile/​building-apps/​build-properties.md Build property documentation
.github/​skills/​tests/​references/​test-catalog.md Test prerequisite documentation
Files not reviewed (1)
  • src/Xamarin.Android.Build.Tasks/Properties/Resources.Designer.cs: Generated file

Comment on lines +122 to +124
Condition=" '$(_AndroidEnableTypemapR8Trimming)' != 'false' and '$(PublishTrimmed)' == 'true' and '$(AndroidLinkTool)' == 'r8' and '$(_ComputeFilesToPublishForRuntimeIdentifiers)' != 'true' and ('$(_AndroidRuntime)' == 'CoreCLR' or ('$(_AndroidRuntime)' == 'NativeAOT' and '$(AndroidTypeMapImplementation)' == 'trimmable' and '$(_AndroidEnableTypemapR8Trimming)' == 'true')) "
Inputs="@(_AndroidTypeMapKeyFile);$(_AndroidTypeMapTasksAssemblyFile);$(_AndroidBuildPropertiesCache)"
Outputs="$(_ProguardProjectConfiguration)">
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

r8-rules Typemap-derived ProGuard and R8 rules

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants