Skip to content

[TrimmableTypeMap] Build pipeline: targets, scanner, task, assembly store#10980

Closed
simonrozsival wants to merge 31 commits intomainfrom
dev/simonrozsival/trimmable-typemap-build-pipeline
Closed

[TrimmableTypeMap] Build pipeline: targets, scanner, task, assembly store#10980
simonrozsival wants to merge 31 commits intomainfrom
dev/simonrozsival/trimmable-typemap-build-pipeline

Conversation

@simonrozsival
Copy link
Member

@simonrozsival simonrozsival commented Mar 20, 2026

Summary

Build pipeline for the trimmable typemap. Wires up manifest generation, assembly store,
and JCW generation. HelloWorld app runs end-to-end on device with button Click events.

Closes #10807.
Closes #10800.

Depends on #10991 (ManifestGenerator), #10967 (runtime).

Scanner extensions (AssemblyIndex.cs, JavaPeerInfo.cs, JavaPeerScanner.cs)

  • ComponentInfo, IntentFilterInfo, MetaDataInfo data model for type-level component attributes
  • AssemblyManifestInfo with ScanAssemblyAttributes() for assembly-level attrs
  • HasPublicParameterlessCtor via SRM method signature inspection (XA4213)
  • ScanAssemblyManifestInfo() returns assembly-level data alongside peer list

GenerateTrimmableTypeMap task

JCW generation fixes

  • JniNameToJavaName: converts $. for inner class references in Java source (javac requires ., not $)
  • GetNativeCallbackName: splits [Register] Connector string on : before checking Get…Handler pattern
  • ParseConnectorDeclaringType: extracts the Invoker type from the Connector so UCO wrappers resolve callbacks on the correct type

Trimmer size regression prevention

  • Set TrimmableTypeMap=false via RuntimeHostConfigurationOption in MonoVM.targets and NativeAOT.targets so ILLink can eliminate trimmable typemap code paths
  • Add ILLink substitutions for IsMonoRuntime and IsCoreClrRuntime
  • TrimmableTypeMap uses private ctor + Initialize() + singleton pattern — no type references leak outside feature guards
  • TrimmableTypeMapTypeManager uses TrimmableTypeMap.Instance singleton (no constructor parameter)
  • _RemoveRegisterAttribute: empty target — [Register] attributes needed at runtime for TryGetJniNameForType

MSBuild targets

Trimmable.targets

  • _GenerateTrimmableTypeMap: AfterTargets="CoreCompile" with Inputs/Outputs for incremental builds
  • _GenerateJavaStubs: assembly store wiring, TypeMap DLLs to _ResolvedAssemblies
  • Path normalization for macOS (Replace('\','/'))

Trimmable.CoreCLR.targets

  • ILLink integration: ManagedAssemblyToLink, --typemap-entry-assembly
  • _AddTrimmableTypeMapAssembliesToStore: per-ABI batched, linked/ to typemap/ fallback

Tests

  • 296/296 unit tests pass
  • HelloWorld verified on emulator: Activity, layout XML, FindViewById<Button>, Click events
  • PublishTrimmed=true (ILLink inner build) verified: builds and runs successfully
  • BuildReleaseArm64SimpleDotNet.MonoVM apkdesc baseline updated

Other

File Purpose
GenerateEmptyTypemapStub.cs LLVM IR stub symbols (uses Files.CopyIfStringChanged)
GenerateNativeApplicationConfigSources.cs Token=0 fallback gated behind TargetsCLR
Trimmable.CoreCLR.xml Preserves JNIEnvInit.Initialize
TypeMapAssemblyEmitter.cs Self-application attribute on proxy types
HelloWorld sample _AndroidTypeMapImplementation=trimmable

Part of #10807 (build pipeline, targets, manifest integration, native stubs)

@simonrozsival simonrozsival changed the title [TrimmableTypeMap] Build pipeline: targets, scanner, generator fixes [WIP][TrimmableTypeMap] Build pipeline: targets, scanner, generator fixes Mar 20, 2026
@simonrozsival simonrozsival changed the title [WIP][TrimmableTypeMap] Build pipeline: targets, scanner, generator fixes [TrimmableTypeMap] Build pipeline + manifest generation Mar 21, 2026
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-build-pipeline branch from e2214d6 to 483dc03 Compare March 21, 2026 06:54
@simonrozsival simonrozsival changed the title [TrimmableTypeMap] Build pipeline + manifest generation [TrimmableTypeMap] Build pipeline: targets, scanner, generator fixes Mar 21, 2026
@simonrozsival simonrozsival added copilot `copilot-cli` or other AIs were used to author this trimmable-type-map labels Mar 21, 2026
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-build-pipeline branch 2 times, most recently from 12a1c2b to 4d55775 Compare March 21, 2026 09:12
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-runtime-pr branch from 76a490e to aad2ddb Compare March 21, 2026 09:14
@simonrozsival simonrozsival changed the title [TrimmableTypeMap] Build pipeline: targets, scanner, generator fixes [TrimmableTypeMap] Build pipeline + manifest generation Mar 21, 2026
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-build-pipeline branch 4 times, most recently from af78ed0 to 5a4a1f3 Compare March 21, 2026 21:31
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-build-pipeline branch from 5a4a1f3 to 2bdc637 Compare March 21, 2026 22:11
@simonrozsival simonrozsival changed the base branch from dev/simonrozsival/trimmable-typemap-runtime-pr to dev/simonrozsival/trimmable-typemap-manifest-generation March 21, 2026 22:14
@simonrozsival simonrozsival changed the title [TrimmableTypeMap] Build pipeline + manifest generation [TrimmableTypeMap] Build pipeline: targets, scanner, task, assembly store Mar 21, 2026
@simonrozsival simonrozsival marked this pull request as ready for review March 21, 2026 22:38
Copilot AI review requested due to automatic review settings March 21, 2026 22:38
simonrozsival and others added 9 commits March 25, 2026 17:16
…t providers

- Fix Categories parsing: remove TryGetNamedArgument<string> guard that
  always returns false for string[] properties. Directly iterate named
  arguments with IReadOnlyCollection<> cast.
- Delete empty ManifestModel.cs placeholder (all types in JavaPeerInfo.cs).
- Document ApplicationRegistration.java: registerApplications() is
  intentionally empty in the trimmable path (types activated via
  registerNatives + UCO wrappers, not Runtime.register).
- Add TODO for multi-process per-provider .java generation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Enables ILLink to trim TrimmableTypeMap code paths when ILLink runs
(e.g. with PublishTrimmed=true or AOT). Without this, the feature switch
is unknown to the trimmer and both branches are preserved.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
get_TargetType is no longer emitted on proxy types — it's inherited
from the generic base class JavaPeerProxy<T>. Update test assertions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use $(IntermediateOutputPath) instead of manually constructing the path
from $(BaseIntermediateOutputPath)$(Configuration)/$(TargetFramework)/.
The manual construction breaks when AppendTargetFrameworkToOutputPath
is false, as in the test infrastructure.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Add $(_AndroidManifestAbs) to _GenerateTrimmableTypeMap Inputs so
   manifest changes trigger re-generation.
2. Use @(ReferencePath->Count()) instead of '@(ReferencePath)' != ''
   to avoid string-joining hundreds of assembly paths.
3. Add XA4212 and XA4217 error codes for duplicate/conflicting
   [Application] attributes (replacing bare Log.LogError calls).
4. Add copied Java files, ApplicationRegistration.java, AndroidManifest,
   and acw-map.txt to @(FileWrites) so IncrementalClean won't delete them.
5. Add Inputs/Outputs to _PrepareNativeAssemblySources so MSBuild can
   skip it on incremental builds.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Pass AcwMapOutputFile to GenerateTrimmableTypeMap so AcwMapWriter
produces real managed→Java mappings for _ConvertCustomView and layout
fixup. The <Touch> in _GenerateJavaStubs now only creates an empty
placeholder when _GenerateTrimmableTypeMap was skipped. The generated
acw-map.txt is tracked in @(FileWrites) from both targets.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Move business logic into a core class in the TrimmableTypeMap project.
The MSBuild task becomes a thin adapter. Tests instantiate the generator
directly — no MSBuild ceremony needed for unit testing.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-build-pipeline branch from 6f42b4d to 384a5a8 Compare March 25, 2026 16:16
- Remove commented-out lines in TypeMapAssemblyGeneratorTests and TypeMapModelBuilderTests
- Replace WriteLinesToFile inline Java with a static ApplicationRegistration.Trimmable.java file
- Remove UseMonoRuntime from HelloWorld.DotNet.csproj (not needed)
- Move MSBuildPackageReferenceVersion to eng/Versions.props (centralize with other package versions)
- Fix TrimmableTypeMap feature switch in NativeAOT.targets: Value=true (NativeAOT uses trimmable typemap)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Member Author

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

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

🤖 AI Review Summary

Verdict: ⚠️ Needs Changes

Found 5 issues: 2 errors, 2 warnings, 1 suggestion.

  • Localization — Error messages hardcoded as strings instead of Properties.Resources.XA#### (TrimmableTypeMapGenerator.cs:247,261,263)
  • Platform-specific — Backslash path separators in cross-platform target will break the Mac/Linux build (Microsoft.Android.Sdk.TypeMap.Trimmable.targets:103,105,108)
  • ⚠️ Code organization — ~13 new public types added to JavaPeerInfo.cs; one type per file (JavaPeerInfo.cs:322+)
  • ⚠️ Nullablestring.IsNullOrEmpty(x) should use the .IsNullOrEmpty() extension (multiple files)
  • 💡 PatternsTrimmableTypeMapResult record exposes List<string> in public API; prefer IReadOnlyList<string>

👍 Great use of sealed record types for data carriers, proper #nullable enable, thorough XML docs, and clean Inputs/Outputs for incremental builds. The GetNativeCallbackName/ParseConnectorDeclaringType refactoring is well-reasoned.

⚠️ CI: Linux build passed ✅, but Mac and Windows builds are still pending — do not merge until they are green.


Review generated by android-reviewer from review guidelines.

- Use Properties.Resources.XA#### for error messages (XA4212, XA4213, XA4217)
  - Add Properties/Resources.resx + Resources.Designer.cs to TrimmableTypeMap project
  - Remove XA4212/XA4217 from Xamarin.Android.Build.Tasks resources (they are
    only used in TrimmableTypeMapGenerator, not in any Build.Tasks code path)

- Fix cross-platform path separator bugs in Trimmable.targets
  - Replace \**\*.java glob with /**/*.java
  - Replace android\src\ destination paths with android/src/
  - Replace ..\ TrimmerRootDescriptor path with ../

- Split 13 new types out of JavaPeerInfo.cs (one type per file)
  - ComponentInfo.cs  (ComponentKind + ComponentInfo)
  - IntentFilterInfo.cs
  - MetaDataInfo.cs
  - AssemblyManifestInfo.cs
  - ManifestAttributeInfo.cs  (Permission*, UsesPermission, UsesFeature,
                                UsesLibrary, UsesConfiguration, PropertyInfo)
  - TrimmableTypeMapTypes.cs  (ManifestConfig + TrimmableTypeMapResult)

- Replace string.IsNullOrEmpty() static calls with .IsNullOrEmpty() extension
  - Add NullableExtensions.cs to TrimmableTypeMap project
  - ManifestGenerator.cs: 6 occurrences
  - TrimmableTypeMapGenerator.cs: 3 occurrences

- Change List<string> to IReadOnlyList<string> in TrimmableTypeMapResult

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copy link
Member Author

@simonrozsival simonrozsival left a comment

Choose a reason for hiding this comment

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

🤖 AI Review Summary

Verdict: ⚠️ Needs Changes

Found 4 issues: 1 error + 2 warnings + 1 suggestion.

  • MSBuild targets: AcwMapOutputFile passed to task but no such property exists → MSB4064 build error (Microsoft.Android.Sdk.TypeMap.Trimmable.targets:77)
  • ⚠️ Code organization: 8 types in ManifestAttributeInfo.cs — one type per file (Scanner/ManifestAttributeInfo.cs:7)
  • ⚠️ Code organization: ManifestGenerator unnecessarily made public — new helpers default to internal (Generator/ManifestGenerator.cs:16)
  • 💡 MSBuild targets: AfterTargets="CoreCompile" runs even if CoreCompile fails; consider $(MSBuildLastTaskResult) guard

👍 The overall architecture is solid: AndroidTask with correct TaskPrefix, [Required] property conventions followed, error codes from Properties.Resources, IsNullOrEmpty() extension used consistently throughout, Files.CopyIfStringChanged in GenerateEmptyTypemapStub, and FileWrites tracked correctly for incremental builds. The JniNameToJavaName fix for $. in inner class names is correct and well-tested.


Review generated by android-reviewer from review guidelines.

ManifestPlaceholders="$(AndroidManifestPlaceholders)"
CheckedBuild="$(_AndroidCheckedBuild)"
ApplicationJavaClass="$(AndroidApplicationJavaClass)"
AcwMapOutputFile="$(IntermediateOutputPath)acw-map.txt">
Copy link
Member Author

Choose a reason for hiding this comment

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

🤖 ❌ MSBuild targetsAcwMapOutputFile is passed here but the GenerateTrimmableTypeMap C# task class has no such property. MSBuild will fail with MSB4064 ("The 'GenerateTrimmableTypeMap' task does not support the parameter 'AcwMapOutputFile'"). Either add the property to the task class, or remove this line if it was left from an earlier refactor.

Rule: Incremental builds / task parameter accuracy

simonrozsival and others added 4 commits March 25, 2026 18:40
- Fix AcwMapOutputFile: add property to GenerateTrimmableTypeMap task,
  wire through TrimmableTypeMapGenerator.Execute, and write acw-map.txt
  via AcwMapWriter so _ConvertCustomView and _UpdateAndroidResgen get
  real managed→Java mappings
- Split ManifestAttributeInfo.cs: move each of the 8 types to its own
  file (one type per file rule)
- Make ManifestGenerator internal: no external consumers; tests use
  InternalsVisibleTo
- Return IReadOnlyList<string> from public Generate overloads (was IList)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…peMapImplementation=trimmable

The unconditional Value="true" was causing all non-trimmable NativeAOT builds
to fail: ILLink eliminated the legacy binary typemap code path (expecting the
trimmable typemap assembly to be present), but the typemap DLLs are only
generated when _AndroidTypeMapImplementation=trimmable.

When _AndroidTypeMapImplementation=trimmable, Microsoft.Android.Sdk.TypeMap.Trimmable.targets
(imported conditionally) already adds RuntimeHostConfigurationOption Value="true",
so NativeAOT.targets should not also set it unconditionally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The trimmable typemap targets don't support MonoVM yet, so
explicitly set UseMonoRuntime=false for the HelloWorld build
in Linux and package test stages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- GenerateTrimmableTypeMapTests: assert empty arrays instead of null
  (the task returns [].ToArray(), not null, for empty results)
- Update MonoVM apkdesc baseline: ILLink now trims dead trimmable
  typemap branches thanks to RuntimeFeature.TrimmableTypeMap=false

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@grendello grendello removed their request for review March 26, 2026 13:14
@simonrozsival simonrozsival force-pushed the dev/simonrozsival/trimmable-typemap-build-pipeline branch from 89052b9 to 3c1d9d7 Compare March 26, 2026 14:08
simonrozsival and others added 2 commits March 27, 2026 08:02
…ensions, make new types internal

- Remove '!' on acwMapOutputPath (line 73) — flow analysis handles nullability
- Use .IsNullOrEmpty() instead of 'is not null && .Length > 0' for consistency
- Use .IsNullOrEmpty() extension in AssemblyIndex.TryGetNameFromDecodedAttribute
- Avoid re-enumeration: store group.ToList() result and use .Count property
- Make new types internal: TrimmableTypeMapGenerator, ManifestConfig,
  TrimmableTypeMapResult, AssemblyManifestInfo, and scanner model types
  (PermissionInfo, UsesPermissionInfo, etc.)
- Add InternalsVisibleTo for Xamarin.Android.Build.Tasks
- Keep ComponentInfo, IntentFilterInfo, MetaDataInfo public (referenced by
  public JavaPeerInfo.ComponentAttribute)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…lyfill conflict

InternalsVisibleTo exposes the internal NotNullWhenAttribute polyfill from
the netstandard2.0 TrimmableTypeMap assembly, which conflicts with Build.Tasks'
own polyfill from Java.Interop/NullableAttributes.cs. Remove the IVT and keep
TrimmableTypeMapGenerator, ManifestConfig, TrimmableTypeMapResult public instead.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@simonrozsival
Copy link
Member Author

Closing in favor of splitting into smaller, focused PRs. Will open 5 fresh PRs from main with clean history.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

copilot `copilot-cli` or other AIs were used to author this trimmable-type-map

Projects

None yet

3 participants