[TrimmableTypeMap] Build pipeline: targets, stubs, manifest integration, feature switches#11036
[TrimmableTypeMap] Build pipeline: targets, stubs, manifest integration, feature switches#11036simonrozsival wants to merge 17 commits intomainfrom
Conversation
…or $ to . JniNameToJavaName was only replacing '/' with '.' but JNI also uses '$' for inner/nested classes. Java source uses '.' for both package and inner class separators. Before: android/view/View$OnClickListener → android.view.View$OnClickListener After: android/view/View$OnClickListener → android.view.View.OnClickListener Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…n from Connector
NativeCallbackName was previously set to a simple "n_{managedName}" which
produced incorrect names like "n_OnCreate" instead of the correct
"n_OnCreate_Landroid_os_Bundle_" that the JNI native method expects.
The new GetNativeCallbackName() derives the correct name from the
[Register] Connector field (e.g. "GetOnCreate_Landroid_os_Bundle_Handler"
→ "n_OnCreate_Landroid_os_Bundle_").
Similarly, AddMarshalMethod was not populating DeclaringTypeName and
DeclaringAssemblyName for interface implementation methods. The new
ParseConnectorDeclaringType() extracts these from the Connector's
type qualifier (after the colon).
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract the core generation pipeline (scan → typemaps → JCW → acw-map) from GenerateTrimmableTypeMap into a standalone TrimmableTypeMapGenerator class that takes Action<string> for logging, keeping the TrimmableTypeMap project free of Microsoft.Build.* dependencies. The MSBuild task becomes a thin adapter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The per-assembly acw-map files are consumed by existing targets. Keep GeneratePerAssemblyAcwMaps in the task (it uses MSBuild types like MemoryStreamPool and Files) rather than moving the merged acw-map.txt logic to the generator, which is PR 4 work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Restore GetJavaInteropAssemblyPaths (MonoAndroidHelper.IsMonoAndroidAssembly) filtering to match main's behavior — removing the filter is a build pipeline change that belongs in a later PR. - Add ArgumentNullException checks to TrimmableTypeMapGenerator.Execute(). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…d.Tests Move incremental build, parsing, and empty-input tests to TrimmableTypeMapGeneratorTests (xUnit, uses TestFixtures.dll). Keep MSBuild-specific tests (error handling, Mono.Android integration) in GenerateTrimmableTypeMapTests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The scanner needs all assemblies — not just those with HasMonoAndroidReference. Framework JCW pre-compilation is tracked by #10792; until then we generate JCWs for everything. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This is MSBuild input parsing, not core generator logic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add scanning for assembly-level attributes ([Application], [Activity], [Service], [BroadcastReceiver], [ContentProvider], permissions, etc.) and wire them into the manifest generator via Generate(XDocument?) overload. Replace ManifestModel.cs with individual Scanner/ record types for better composability. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… types Add assembly-level manifest attribute scanning to the TrimmableTypeMap scanner: - AssemblyIndex.ScanAssemblyAttributes(): parses 9 assembly-level attribute types (UsesPermission, UsesLibrary, UsesFeature, UsesConfiguration, Permission, PermissionGroup, PermissionTree, MetaData, Property) - JavaPeerScanner.ScanAssemblyManifestInfo(): aggregates attributes across assemblies - JavaPeerScanner.ToComponentInfo()/HasPublicParameterlessCtor(): attaches component info to scanned peers - ManifestGenerator.Generate(XDocument?): new overload accepting pre-loaded template - ManifestGenerator.CreateDefaultManifest(): extracted from LoadOrCreateManifest - Replace ManifestModel.cs (105-line flat class) with 12 focused record types - Update JavaPeerInfo.ComponentAttribute doc comment Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r-class', 'origin/dev/simonrozsival/fix-native-callback-name', 'origin/dev/simonrozsival/extract-trimmable-typemap-generator' and 'origin/dev/simonrozsival/scanner-manifest-attributes' into dev/simonrozsival/trimmable-typemap-build-pipeline-v2
- Update TrimmableTypeMapGenerator.Execute() with manifest generation, assembly manifest scanning, acw-map writing, and new optional parameters - Add ManifestConfig record to TrimmableTypeMapTypes.cs - Update TrimmableTypeMapResult with AdditionalProviderSources - Update GenerateTrimmableTypeMap MSBuild task with manifest/config properties - Create GenerateEmptyTypemapStub task for LLVM IR native typemap stubs - Create ApplicationRegistration.Trimmable.java (empty registerApplications) - Create Trimmable.CoreCLR.xml preserve list for JNIEnvInit.Initialize - Rewrite Trimmable.targets with full build pipeline (separate generation and _GenerateJavaStubs targets, native stub generation, manifest handling) - Rewrite Trimmable.CoreCLR.targets with ILLink integration and per-ABI assembly store support - Add TrimmableTypeMap=false feature switch to MonoVM and NativeAOT targets - Handle trimmed JNIEnvInit tokens in GenerateNativeApplicationConfigSources Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR wires the TrimmableTypeMap feature into the .NET for Android build pipeline, adding CoreCLR linker/store integration, generating required Java/native stubs, and expanding the typemap generator to emit manifest + merged acw-map outputs.
Changes:
- Reworks trimmable typemap MSBuild targets to generate typemap assemblies/JCWs post-compile, integrate with ILLink, and populate assembly-store inputs.
- Adds new build-time stubs and preserve config (LLVM IR typemap stubs, empty ApplicationRegistration for trimmable path, ILLink root descriptor).
- Extends the TrimmableTypeMap generator/scanner to support manifest generation inputs and updated JNI/override naming behavior, with corresponding test updates.
Reviewed changes
Copilot reviewed 36 out of 36 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Scanner/OverrideDetectionTests.cs | Updates expected native callback name derivation from Connector. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapModelBuilderTests.cs | Adjusts expectations for generated wrapper members. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs | Aligns proxy type wrapper expectations with generator output. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TrimmableTypeMapGeneratorTests.cs | Adds direct unit tests for the core generator pipeline and incremental behavior. |
| tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/JcwJavaSourceGeneratorTests.cs | Updates JNI name conversion and native method naming assertions. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/GenerateTrimmableTypeMapTests.cs | Refactors MSBuild-task tests to match new outputs/behavior. |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs | Converts MSBuild task into adapter delegating to TrimmableTypeMapGenerator, adds manifest/acw-map integration inputs/outputs. |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateNativeApplicationConfigSources.cs | Allows CoreCLR trimmable path to proceed when JNIEnvInit tokens are trimmed (token=0 fallback). |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateEmptyTypemapStub.cs | New task generating per-ABI empty LLVM IR typemap stubs. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.targets | Major build pipeline rewrite for trimmable typemap generation, stub copying, and native-stub prep. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.Trimmable.CoreCLR.targets | Adds ILLink and assembly-store integration for typemap assemblies under CoreCLR. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets | Disables TrimmableTypeMap runtime feature for NativeAOT. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.MonoVM.targets | Disables TrimmableTypeMap runtime feature for MonoVM. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/ApplicationRegistration.Trimmable.java | Adds trimmable-path ApplicationRegistration stub with empty registerApplications(). |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/PreserveLists/Trimmable.CoreCLR.xml | Adds ILLink root descriptor to preserve JNIEnvInit.Initialize. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapTypes.cs | Introduces result/config records for manifest + provider sources. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/TrimmableTypeMapGenerator.cs | Implements end-to-end generator (scan → typemap → JCW → manifest/acw-map outputs). |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/NullableExtensions.cs | Adds NRT-friendly string helpers for netstandard2.0. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ManifestGenerator.cs | Adds Generate overload accepting pre-loaded manifest template and refactors default creation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ManifestModel.cs | Removes old manifest model types (replaced by scanner records). |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/JniSignatureHelper.cs | Fixes nested-class JNI name conversion for Java source output. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs | Adds manifest scanning, component attribute capture, and correct native callback/declaring-type derivation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs | Updates component attribute documentation and usage. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyIndex.cs | Expands attribute decoding to capture component properties, intent-filters, metadata, and assembly-level manifest info. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/AssemblyManifestInfo.cs | Adds aggregated assembly-level manifest data model. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/ComponentInfo.cs | Adds component kind + data model for manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/IntentFilterInfo.cs | Adds intent-filter data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/MetaDataInfo.cs | Adds metadata data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/PermissionInfo.cs | Adds permission data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/PermissionGroupInfo.cs | Adds permission-group data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/PermissionTreeInfo.cs | Adds permission-tree data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/UsesPermissionInfo.cs | Adds uses-permission data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/UsesFeatureInfo.cs | Adds uses-feature data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/UsesLibraryInfo.cs | Adds uses-library data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/UsesConfigurationInfo.cs | Adds uses-configuration data model used by manifest generation. |
| src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/PropertyInfo.cs | Adds manifest property data model used by manifest generation. |
| } else if (attrName == "IntentFilterAttribute") { | ||
| attrInfo ??= new TypeAttributeInfo ("IntentFilterAttribute"); | ||
| attrInfo.IntentFilters.Add (ParseIntentFilterAttribute (ca)); | ||
| } else if (attrName == "MetaDataAttribute") { | ||
| attrInfo ??= new TypeAttributeInfo ("MetaDataAttribute"); | ||
| attrInfo.MetaData.Add (ParseMetaDataAttribute (ca)); |
There was a problem hiding this comment.
IntentFilterAttribute / MetaDataAttribute can initialize attrInfo with a TypeAttributeInfo whose AttributeName is not a component attribute. If those attributes appear before [Activity]/[Service]/…, later component parsing won’t create the correct TypeAttributeInfo subtype (e.g., ApplicationAttributeInfo), and ToComponentInfo() won’t recognize the component kind, so manifest generation may silently drop component data/intent-filters/metadata. Consider keeping component-kind separate from intent-filter/metadata collection (e.g., always create the component TypeAttributeInfo via CreateTypeAttributeInfo() when a component attribute is encountered, and store intent-filters/metadata on a separate structure or on a type-info object that doesn’t overwrite the component attribute identity).
| <Target Name="_PrepareNativeAssemblySources" | ||
| Condition=" '$(_AndroidRuntime)' != 'NativeAOT' " | ||
| Inputs="@(_BuildTargetAbis)" | ||
| Outputs="@(_BuildTargetAbis->'$(_NativeAssemblySourceDir)typemaps.%(Identity).ll')"> |
There was a problem hiding this comment.
_PrepareNativeAssemblySources declares Outputs as typemaps.%(Identity).ll, but GenerateEmptyTypemapStub writes files named typemap.{abi}.ll. This output mismatch breaks MSBuild incremental up-to-date checks (the target will look for non-existent outputs and rerun every build). Update the Outputs pattern (and/or the generated filename) so they match exactly.
| Outputs="@(_BuildTargetAbis->'$(_NativeAssemblySourceDir)typemaps.%(Identity).ll')"> | |
| Outputs="@(_BuildTargetAbis->'$(_NativeAssemblySourceDir)typemap.%(Identity).ll')"> |
| var generator = CreateGenerator (); | ||
| Assert.Throws<ArgumentNullException> (() => generator.Execute ( | ||
| null!, Path.Combine (testDir, "out"), Path.Combine (testDir, "java"), | ||
| new Version (11, 0), new HashSet<string> ())); |
There was a problem hiding this comment.
This test uses the null-forgiving operator (null!) to pass a null argument. The repo guidance is to avoid ! even in tests; prefer a nullable local (e.g., IReadOnlyList<string>? assemblyPaths = null) and pass that, or otherwise structure the test so it doesn’t require !.
…e enable - Fix ParseAttributes: collect IntentFilter/MetaData in local lists and attach after the loop to avoid creating attrInfo with wrong AttributeName when [IntentFilter] appears before [Activity] - Remove redundant #nullable enable from 13 files (project has <Nullable>enable) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…into dev/simonrozsival/trimmable-typemap-build-pipeline-v2
…/simonrozsival/trimmable-typemap-build-pipeline-v2
- Remove redundant #nullable enable from 6 Generator files and task file - Convert string.IsNullOrEmpty to IsNullOrEmpty extension in task Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Build pipeline changes for the trimmable typemap feature. This PR stacks on PRs #11032, #11033, #11034, and #11035 — it includes their changes via a merge commit and adds the build infrastructure on top.
PR 4 commit changes (11 files, +492/-77):
New files
GenerateEmptyTypemapStub.cs— MSBuild task generating empty LLVM IR typemap stubs (typemap.{abi}.ll) per ABI. The native toolchain compiles these into the symbolslibmonodroid.soexpects.ApplicationRegistration.Trimmable.java— EmptyregisterApplications()for the trimmable path (Application/Instrumentation types are activated viaRuntime.registerNatives()+ UCO wrappers).Trimmable.CoreCLR.xml— ILLink preserve descriptor ensuringJNIEnvInit.Initializesurvives trimming (native entry point fromhost.cc).Rewritten targets
Trimmable.targets— Major rewrite:_GenerateTrimmableTypeMaptarget runs afterCoreCompile(uses@(ReferencePath))_GenerateJavaStubsbecomes the orchestration target (copies JCW files, manifest, ApplicationRegistration.java, acw-map.txt)_PrepareNativeAssemblySourcesgenerates empty typemap LLVM stubs viaGenerateEmptyTypemapStub_RemoveRegisterAttributeoverridden as no-op (trimmable path needs[Register]at runtime)_CollectPerAssemblyAcwMapsand_MergeAcwMaps(merged acw-map now written directly by the task)Trimmable.CoreCLR.targets— Major rewrite:_AddTrimmableTypeMapToLinkeradds TypeMap DLLs to ILLink_ConfigureTrimmableTypeMapForLinkerpasses--typemap-entry-assembly_AddTrimmableTypeMapAssembliesToStoreadds per-ABI TypeMap DLLs to assembly store (tries linked/ first, falls back to untrimmed)Feature switches
MonoVM.targets/NativeAOT.targets— AddMicrosoft.Android.Runtime.RuntimeFeature.TrimmableTypeMap=falsefeature switch (trimmable path is CoreCLR-only for now)Modified tasks
GenerateTrimmableTypeMap.cs— Added 14 manifest/config properties,ManifestConfigconstruction, mergedacw-map.txtoutput,AdditionalProviderSourcesoutputTrimmableTypeMapGenerator.cs— Added manifest generation, assembly manifest scanning, acw-map writingTrimmableTypeMapTypes.cs— AddedManifestConfigrecord,AdditionalProviderSourcesto resultGenerateNativeApplicationConfigSources.cs— Gracefully handle trimmed JNIEnvInit tokens whenTargetsCLR(use token 0 for missing tokens)Dependencies
Depends on (merge first):