Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
2fb867f
Add retained typemap key diagnostics
simonrozsival Sep 18, 2026
14cc460
Generate class-only ProGuard rules from retained typemap keys
simonrozsival Sep 18, 2026
3e66618
Make shared typemap generator standalone-test compatible
simonrozsival Sep 18, 2026
a3d06fe
Extract retained Java type map keys from NativeAOT DGML
simonrozsival Sep 18, 2026
7ad9648
Extract CoreCLR type map keys from generated LLVM IR
simonrozsival Sep 18, 2026
78d358d
Extract retained Java keys from linked typemap assemblies
simonrozsival Sep 18, 2026
72ad527
Share canonical retained class-name validation with adapters
simonrozsival Sep 18, 2026
2930136
Align DGML key validation with the shared ProGuard consumer
simonrozsival Sep 18, 2026
e675971
Validate linked typemap metadata and canonical class keys
simonrozsival Sep 18, 2026
64ef11d
Move retained-key ProGuard generator to modern build tasks
simonrozsival Sep 18, 2026
679e13a
Move DGML typemap adapter to modern Android build tasks
simonrozsival Sep 18, 2026
36a06f5
Move LLVM typemap extraction to modern Android tasks
simonrozsival Sep 18, 2026
dfcb9c2
Move linked typemap extraction into modern Android build tasks
simonrozsival Sep 18, 2026
96bf376
Assert DGML adapter is loaded from the modern task assembly
simonrozsival Sep 18, 2026
03ca398
Drive R8 class shrinking from retained typemap keys
simonrozsival Sep 18, 2026
5236ed0
Make typemap-driven R8 trimming privately configurable
simonrozsival Sep 18, 2026
ce88759
Replace legacy NativeAOT typemap controls with the shared pipeline
simonrozsival Sep 18, 2026
e1f8869
Handle JNI array entries in retained LLVM typemaps
simonrozsival Sep 18, 2026
c804d42
Accept intrinsic type metadata in NativeAOT DGML
simonrozsival Sep 18, 2026
85e3698
Extract retained NativeAOT typemap keys from native objects
simonrozsival Sep 18, 2026
81f0b03
Scope CoreCLR R8 member retention to retained JNI types
simonrozsival Sep 18, 2026
ef0bcd4
Select NativeAOT Java typemap groups through LLVM fixup symbols
simonrozsival Sep 18, 2026
96555e6
Replace NativeAOT DGML integration with final-object typemap extraction
simonrozsival Sep 18, 2026
8b079f8
Remove remaining DGML-based NativeAOT logic
simonrozsival Sep 18, 2026
87bd3f8
Optimize retained CoreCLR Java code without obfuscating JNI names
simonrozsival Sep 18, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/skills/tests/references/test-catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ These tests can be run immediately with `dotnet test` on the `.csproj`, even if

---

The NativeAOT object/MSBuild integration cases in the trimmable type map suite
also require the NDK `llvm-readobj` and adjacent `llvm-objdump` and `clang` executables. Pass
`-p:_NativeAotLlvmReadObjPath=/path/to/ndk/toolchains/llvm/prebuilt/<host>/bin/llvm-readobj`
(with `.exe` on Windows) to execute those cases; without it, those cases are
reported as skipped. The NativeFormat parser and other typemap unit tests do
not require native tools.

## Host-Side MSBuild Tests (full-build — requires local SDK)

Assembly: `bin/TestDebug/${TFM}/Xamarin.Android.Build.Tests.dll`
Expand Down
4 changes: 4 additions & 0 deletions Documentation/docs-mobile/TOC.yml
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,10 @@
href: messages/xa4325.md
- name: XA4326
href: messages/xa4326.md
- name: XA4327
href: messages/xa4327.md
- name: XA4328
href: messages/xa4328.md
- name: "XA5xxx: GCC and toolchain"
items:
- name: "XA5xxx: GCC and toolchain"
Expand Down
6 changes: 6 additions & 0 deletions Documentation/docs-mobile/building-apps/build-properties.md
Original file line number Diff line number Diff line change
Expand Up @@ -1145,6 +1145,12 @@ An enum-style property that specifies how `r8` obfuscates Java names when

This property does not disable R8 code shrinking.

When managed-trimmed CoreCLR builds use retained typemap rules, all Java
names are preserved regardless of this setting, but R8 optimization remains
enabled. This path uses the optimizing Android defaults together with
`-dontobfuscate` and `--no-minification`; disabling renaming does not disable
optimization.

This property was introduced in a .NET 10 servicing release. It defaults to
`disabled` in .NET 10 and to `private-members` in .NET 11 and later.

Expand Down
8 changes: 5 additions & 3 deletions Documentation/docs-mobile/messages/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -255,14 +255,16 @@ Either change the value in the AndroidManifest.xml to match the $(SupportedOSPla
+ [XA4316](xa4316.md): Specified input file '{file}' does not exist. Ignoring.
+ [XA4317](xa4317.md): Input file '{file}' does not start with `<replacements/>`. Skipping.
+ [XA4318](xa4318.md): Input file '{file}' could not be read: {message}. Skipping.
+ [XA4319](xa4319.md): No NativeAOT DGML files were provided.
+ [XA4320](xa4320.md): ACW map file '{file}' was not found.
+ [XA4321](xa4321.md): NativeAOT DGML file '{file}' was not found.
+ [XA4319](xa4319.md): Retired: No NativeAOT DGML files were provided.
+ [XA4320](xa4320.md): Retired: ACW map file '{file}' was not found.
+ [XA4321](xa4321.md): Retired: NativeAOT DGML file '{file}' was not found.
+ [XA4322](xa4322.md): Skipping library ProGuard configuration file '{file}' (from {source}) because it contains the unsupported global option '{option}'. Global ProGuard options are only allowed in application projects.
+ [XA4323](xa4323.md): Ignoring directory '{directory}' as it does not exist.
+ [XA4324](xa4324.md): [{arch}] Unable to delete source file '{file}'.
+ [XA4325](xa4325.md): Failed to rewrite managed JNI names for R8. {message}
+ [XA4326](xa4326.md): Unable to safely rewrite a JNI member lookup because its class handle does not have one structurally unambiguous `JNIEnv.FindClass` source.
+ [XA4327](xa4327.md): Could not extract Java type map keys from '{0}': {1}
+ [XA4328](xa4328.md): Could not generate typemap ProGuard configuration from '{0}': {1}

## XA5xxx: GCC and toolchain

Expand Down
21 changes: 4 additions & 17 deletions Documentation/docs-mobile/messages/xa4319.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
---
title: .NET for Android error XA4319
description: XA4319 error code
ms.date: 05/27/2026
ms.date: 09/18/2026
f1_keywords:
- "XA4319"
---

# .NET for Android error XA4319

## Example messages

```
error XA4319: No NativeAOT DGML files were provided.
```

## Issue

The .NET for Android build could not find any NativeAOT DGML files to use when
generating trimmable type map ProGuard configuration.

## Solution

This error is expected only for internal build state inconsistencies. Ensure the
project is building with the intended Runtime Identifier settings and that the
NativeAOT compile produced its DGML scan file.
This diagnostic has been retired. NativeAOT Java trimming reads ILC object
files and does not require dependency graphs. See [XA4327](xa4327.md) for
current typemap extraction failures.
20 changes: 4 additions & 16 deletions Documentation/docs-mobile/messages/xa4320.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,13 @@
---
title: .NET for Android error XA4320
description: XA4320 error code
ms.date: 05/27/2026
ms.date: 09/18/2026
f1_keywords:
- "XA4320"
---

# .NET for Android error XA4320

## Example messages

```
error XA4320: ACW map file '{file}' was not found.
```

## Issue

The .NET for Android build could not find the Android Callable Wrapper (ACW) map
file needed to generate trimmable type map ProGuard configuration.

## Solution

This error is expected only for internal build state inconsistencies. Rebuild
the project from a clean state so the ACW map is regenerated.
This diagnostic has been retired. NativeAOT Java trimming reads ILC object
files instead of joining dependency graphs with an ACW map. See
[XA4327](xa4327.md) for current typemap extraction failures.
21 changes: 4 additions & 17 deletions Documentation/docs-mobile/messages/xa4321.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,13 @@
---
title: .NET for Android error XA4321
description: XA4321 error code
ms.date: 05/27/2026
ms.date: 09/18/2026
f1_keywords:
- "XA4321"
---

# .NET for Android error XA4321

## Example messages

```
error XA4321: NativeAOT DGML file '{file}' was not found.
```

## Issue

The .NET for Android build could not find a NativeAOT DGML scan file needed to
generate trimmable type map ProGuard configuration.

## Solution

This error is expected only for internal build state inconsistencies. Rebuild
the project from a clean state and verify the NativeAOT compile produced the
DGML scan file for the selected Runtime Identifier.
This diagnostic has been retired. NativeAOT Java trimming reads ILC object
files and does not require dependency graphs. See [XA4327](xa4327.md) for
current typemap extraction failures.
37 changes: 37 additions & 0 deletions Documentation/docs-mobile/messages/xa4327.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: .NET for Android error XA4327
description: XA4327 error code
ms.date: 09/18/2026
f1_keywords:
- "XA4327"
---

# .NET for Android error XA4327

## Example message

```
error XA4327: Could not extract Java type map keys from 'typemaps.arm64-v8a.ll': ...
```

## Issue

The build could not read retained Java class names from a NativeAOT object,
a CoreCLR LLVM typemap, or linked trimmable typemap assemblies.
The input is missing, unreadable, malformed, or uses an unsupported representation.
The diagnostic includes the input and the underlying failure.

An empty, valid typemap is supported. A failed extraction is not treated as an
empty typemap, because doing so could remove Java classes required at runtime.

## Solution

Fix the reported input or the earlier task that produces it. If the input was
generated by .NET for Android, clean and rebuild the application. If the error
persists, [report an issue](https://github.com/dotnet/android/issues/new/choose)
with a binary build log and the affected typemap input.

NativeAOT object extraction also requires the Android NDK's `llvm-readobj`
and adjacent `llvm-objdump`.
Install the NDK selected by `GetAndroidDependencies`, or point
`AndroidNdkDirectory` at a complete installation.
33 changes: 33 additions & 0 deletions Documentation/docs-mobile/messages/xa4328.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
title: .NET for Android error XA4328
description: XA4328 error code
ms.date: 09/18/2026
f1_keywords:
- "XA4328"
---

# .NET for Android error XA4328

## Example message

```
error XA4328: Could not generate typemap ProGuard configuration from 'typemap.keys.txt': Line 1 contains an invalid retained Java class name: 'example/*'.
```

## Issue

The class-only ProGuard generator could not read a retained-keys file, found
an invalid class name, received no input files, or could not write its output.
Keys must be UTF-8 Java class names using `/` as the package separator, such as
`example/Outer$Inner`. Wildcards, ProGuard directives, dotted names, type
descriptors, and typemap alias suffixes are not valid records.

A zero-byte input is valid and produces no class keep rules. A missing file is
an error, not an empty input.

## Solution

Fix the reported input or output path and permissions. For files generated by
.NET for Android, clean and rebuild. If the error persists,
[report an issue](https://github.com/dotnet/android/issues/new/choose) with a
binary build log and the affected keys file.
99 changes: 99 additions & 0 deletions Documentation/guides/D8andR8.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,104 @@
This is the D8 and R8 integration specification for .NET for Android.

# Retained typemap class roots

When managed trimming and R8 are enabled, CoreCLR and trimmable NativeAOT
builds derive Java class roots from the final typemap rather than retaining
every class in the ACW map:

| Runtime and typemap | Retained-key source |
| --- | --- |
| NativeAOT, trimmable | Retained external typemap records in each RID's final ILC object |
| CoreCLR, LLVM IR | The generated LLVM Java-name blob, after `GenerateTypeMappings` |
| CoreCLR, trimmable | Surviving `TypeMapAttribute<T>` records in linked typemap assemblies, including empty stubs |

Inner builds return their exact source paths to the outer build. Keys are
unioned across the requested RIDs/ABIs; stale files from other builds are not
discovered by globbing. NativeAOT locates external typemap blobs with
`llvm-readobj`, resolves Java group identities using bounded `llvm-objdump`
relocations, reads their object-file byte ranges, and decodes the retained
NativeFormat keys. It does not require an ACW map or generate dependency graphs.

All three adapters and both ProGuard generators are `Microsoft.Android.Tasks`
tasks in `Microsoft.Android.Build.Tasks.dll`.
The adapters write `typemap.keys.txt` in the outer intermediate directory.
The format is UTF-8 without a BOM, one canonical JNI class name per line,
ordinal-sorted and distinct, with LF endings and a final LF when nonempty.
For example:

```text
android/app/Activity
example/Outer$Inner
```

The assembly and NativeAOT object adapters collapse implementation-specific
numeric aliases before writing this format. Valid JNI array entries contribute
their reference element class; primitive arrays do not contribute a class.
A valid zero-byte file means no retained classes;
missing, unreadable, or unsupported inputs fail the build.

`GenerateTypeMapProguardConfiguration` knows only this format. It accepts a
union of keys files and writes deterministic class-only rules to
`proguard/proguard_project_references.cfg`:

```text
-keep class android.app.Activity
-keep class example.Outer$Inner
```

The class-root generator never emits member rules or global R8 options.
CoreCLR uses a separate `GenerateTypeMapMemberProguardConfiguration` task to
write `proguard/proguard_typemap_members.cfg` from the same retained keys:

```text
-keepclassmembers class android.app.Activity { *; }
-keepclassmembers class example.Outer$Inner { *; }
```

This preserves JNI-facing methods, constructors, and fields without also
preserving every member of Java-only dependencies. R8's member matching
includes inherited accessible methods and superclass fields. The separate
`proguard_typemap_coreclr.cfg` configuration preserves runtime bootstrap
classes and their members, plus framework-driven view members. Dynamic JNI
or reflection access to types or members not represented by retained managed
bindings still needs application/library ProGuard rules.

NativeAOT continues to use `proguard_typemap.cfg`, which retains members of
all surviving classes, including third-party classes, while allowing unused
classes to disappear. Both policies keep explicit runtime bootstrap roots
rather than whole wrapper packages. User Java source retention and
application/library ProGuard rules remain separate.

The temporary private override `_AndroidEnableTypemapR8Trimming` controls this
pipeline. It replaces the old NativeAOT-specific trimming and ProGuard switches.
Leave it unset for automatic CoreCLR behavior, set it to `true` to enable
the pipeline in eligible managed-trimmed CoreCLR/NativeAOT R8 builds, or set it
to `false` to use legacy ACW retention without running the new tasks. Disabling
it avoids NativeAOT object inspection. The switch does not enable R8 or managed
trimming in otherwise ineligible builds.

NativeAOT object inspection currently requires explicit `true` and the Android
NDK's `llvm-readobj` and adjacent `llvm-objdump`, even when using the
workload-provided native linker. The workload's native tools do not yet include them.
`GetAndroidDependencies` includes the NDK for this opt-in configuration.
Leaving the override unset or `false` preserves NativeAOT's no-NDK build path
and keeps all ACW classes.

This pipeline disables all obfuscation, including private-member
obfuscation, with both `-dontobfuscate` and R8's `--no-minification` option.
CoreCLR uses `proguard-android-optimize.txt` so optimization remains enabled
independently of obfuscation. Its JNI class/member keep rules still protect
the names and signatures required by managed code. NativeAOT retains its
existing non-optimizing defaults.
MonoVM, nonshrinking/multidex-only builds, and the existing complete
`ProguardConfigFiles` override keep their previous behavior.

Both extraction and rule generation use their real files as incremental
outputs. A content-sensitive input manifest also tracks source-list and
runtime/trim-policy changes, so removing a RID or switching modes cannot
reuse stale class roots. The outputs are registered in `FileWrites` and
regenerated if deleted.

# What is D8? What is R8?

At a high level, here are the steps that occur during an Android
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<TargetFramework>$(DotNetTargetFramework)</TargetFramework>
<RootNamespace>Microsoft.Android.Tasks</RootNamespace>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<WarningsAsErrors>Nullable</WarningsAsErrors>
<!--
Deliberately *not* the shared tools\ directory. MSBuild probes a task assembly's own
Expand Down Expand Up @@ -52,6 +53,8 @@

<ItemGroup>
<Compile Include="..\Xamarin.Android.Build.Tasks\Utilities\NullableExtensions.cs" Link="Utilities\NullableExtensions.cs" />
<Compile Include="..\Xamarin.Android.Build.Tasks\Utilities\MetadataExtensions.cs" Link="Utilities\MetadataExtensions.cs" />
<Compile Include="..\Xamarin.Android.Build.Tasks\Utilities\DummyCustomAttributeProvider.cs" Link="Utilities\DummyCustomAttributeProvider.cs" />
<Compile Include="..\Xamarin.Android.Build.Tasks\Utilities\ManifestPlaceholderResolver.cs" Link="Utilities\ManifestPlaceholderResolver.cs" />
</ItemGroup>

Expand Down
Loading
Loading