Skip to content

[TrimmableTypeMap] Generate per-assembly acw-map.txt from scanner results#10959

Draft
simonrozsival wants to merge 1 commit intodev/simonrozsival/trimmable-typemap-05-msbuild-taskfrom
dev/simonrozsival/trimmable-typemap-acw-map
Draft

[TrimmableTypeMap] Generate per-assembly acw-map.txt from scanner results#10959
simonrozsival wants to merge 1 commit intodev/simonrozsival/trimmable-typemap-05-msbuild-taskfrom
dev/simonrozsival/trimmable-typemap-acw-map

Conversation

@simonrozsival
Copy link
Member

@simonrozsival simonrozsival commented Mar 17, 2026

Part of #10807
Stacked on #10924.

Summary

Adds per-assembly acw-map.{AssemblyName}.txt generation to the trimmable typemap path, enabling _ConvertCustomView and R8 to work with the trimmable build flow.

Background: What is acw-map.txt?

The acw-map.txt file maps .NET managed type names to their corresponding Java/ACW (Android Callable Wrapper) names. It is consumed by:

  • _ConvertCustomView — replaces managed type names with ACW names in layout XML files (e.g., <My.Custom.View><crc64.../MyCustomView>)
  • R8/ProGuard — uses the mapping during code shrinking

Legacy format (from ACWMapGenerator)

The legacy ACWMapGenerator (in ACWMapGenerator.cs) uses Cecil to scan TypeDefinition objects and writes 3 lines per type:

PartialAssemblyQualifiedName;JavaKey    e.g., "Android.App.Activity, Mono.Android;android.app.Activity"
ManagedKey;JavaKey                       e.g., "Android.App.Activity;android.app.Activity"
CompatJniName;JavaKey                    e.g., "android.app.Activity;android.app.Activity"
  • Java keys use dots (not slashes) — Replace('/', '.') is applied
  • Encoding is UTF-8 without BOM (via MemoryStreamPool.CreateStreamWriter() which uses UTF8withoutBOM)
  • Types are sorted by managed name (ordinal)
  • The file is written only when content changes (Files.CopyIfStreamChanged)
  • The consumer LoadMapFile reads lines, splits on ;, and populates a Dictionary<string, string> (first entry wins for duplicates)

Trimmable path: AcwMapWriter

The trimmable path replaces Cecil-based scanning with JavaPeerScanner (SRM-based). JavaPeerInfo already contains all the data needed:

  • ManagedTypeName → managed key
  • JavaName → java key (with /. conversion)
  • CompatJniName → compat JNI key (with /. conversion)
  • AssemblyName → for partial assembly-qualified name

AcwMapWriter writes the exact same 3-line format as the legacy generator, ensuring LoadMapFile consumers see no difference.

Approach

Instead of a separate scanning task, acw-map generation is a side-output of GenerateTrimmableTypeMap — the task already has all JavaPeerInfo records from scanning, so no extra scan pass is needed.

Changes

  • AcwMapWriter — pure formatting logic: JavaPeerInfo → acw-map.txt lines (UTF-8 without BOM, ordinal sort, content-comparison write)
  • GenerateTrimmableTypeMap — new AcwMapDirectory input + PerAssemblyAcwMapFiles output. Groups peers by assembly and writes per-assembly files.
  • _CollectPerAssemblyAcwMaps target — globs per-assembly *.txt files into an item group after _GenerateJavaStubs runs
  • _MergeAcwMaps target — concatenates per-assembly files into the single acw-map.txt. Uses Inputs/Outputs for incrementality and WriteOnlyWhenDifferent to avoid unnecessary downstream rebuilds.
  • 6 unit tests for AcwMapWriter format correctness (261 total tests pass)

Build flow

_GenerateJavaStubs
  └─ GenerateTrimmableTypeMap  → typemap .dlls + JCW .java + acw-map.*.txt
_CollectPerAssemblyAcwMaps     → globs *.txt into @(_PerAssemblyAcwMapFiles)
_MergeAcwMaps                  → concatenates → acw-map.txt
_ConvertCustomView             → reads acw-map.txt (unchanged consumer)

Incrementality

  • _GenerateJavaStubs: stamp-based — skipped when assemblies unchanged
  • AcwMapWriter.WriteToFile: content comparison — unchanged assemblies produce identical files (no timestamp update)
  • _MergeAcwMaps: Inputs="@(_PerAssemblyAcwMapFiles)" / Outputs="$(_AcwMapFile)" — skipped when no per-assembly file is newer
  • WriteOnlyWhenDifferent: merged acw-map.txt only updated when content changes

Future: per-assembly incremental optimization

Per-assembly acw-map files lay the groundwork for #10958 (build time optimizations). When the scan step gains per-assembly Inputs/Outputs incrementality, unchanged assemblies will skip scanning entirely — and their acw-map files will remain untouched, so the merge step will also be skipped.

@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-acw-map branch from 46fbf94 to feec59b Compare March 17, 2026 12:23
…ults

Part of #10807. Stacked on #10924.

The trimmable typemap path needs acw-map.txt for _ConvertCustomView to
fix up custom view names in layout XMLs. This adds acw-map generation as
a side-output of GenerateTrimmableTypeMap — the task already has all
JavaPeerInfo records from scanning, so no extra scan is needed.

Changes:
- AcwMapWriter: converts JavaPeerInfo → acw-map.txt format (3 lines per
  type: partial-asm-qualified, managed, compat-jni — matching legacy format)
- GenerateTrimmableTypeMap: new AcwMapDirectory input + PerAssemblyAcwMapFiles
  output, writes per-assembly acw-map.{AssemblyName}.txt during generation
- Trimmable.targets: _MergeAcwMaps target concatenates per-assembly files
  into the single acw-map.txt consumed by _ConvertCustomView and R8
- 8 unit tests for AcwMapWriter (263 total tests pass)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-acw-map branch from feec59b to 39fc4ab Compare March 17, 2026 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant