[release/11.0.1xx-preview7] [CoreCLR] Stabilize Debug typemaps across C# rebuilds - #12260
Merged
jonathanpeppers merged 1 commit intoJul 29, 2026
Conversation
CoreCLR Debug typemaps embedded application assembly MVIDs. Because an MVID changes on every compilation, a C#-only rebuild rewrote the native typemap even when no Java mappings had changed. That cascaded into native compilation, APK rebuilding, and re-signing on every Fast Deployment cycle. This changes Debug managed-to-Java entries to key on stable assembly full names and removes the Debug MVID table entirely. Release continues to use its MVID-based lookup, and a trimmer feature switch prevents `Assembly.FullName` retrieval and marshalling from being rooted in Release builds. ## Runtime `clr_typemap_managed_to_java` now takes an assembly display name alongside the type name, and `TypeMapper::managed_to_java` is split on `RELEASE` so each configuration only sees the parameter it uses. When `$(RuntimeFeature.ManagedToJavaUsesAssemblyFullName)` is enabled, `JNIEnv.TypemapManagedToJava` skips computing the MVID altogether rather than computing it and discarding it. A null display name is left for the native side to report, which keeps a single diagnostic path for all callers. `TypeMapCecilAdapter.GetRuntimeAssemblyFullName` reproduces reflection's escaping and `PublicKeyToken` formatting so that the build-time name matches what `Assembly.FullName` returns at runtime; a mismatch here would silently break every lookup. ## Tests `IncrementalBuildTest` performs a C#-only change on the CoreCLR Debug path and asserts the typemap and signed APK are byte-for-byte unchanged, and that `_CompileNativeAssemblySources`, `_CreateApplicationSharedLibraries`, `_BuildApkFastDev`, and `_Sign` are all skipped. `FastDeployUpdatesTypeMapAfterAssemblyEdit` is a device test covering three deployments, parameterized over both `FastDeploy` and `FastDeploy2`: 1. Deploy with only `MainActivity`. 2. Add a `SecondActivity`. This is a C#-only edit, but it introduces a new Java-callable type. Fast deployment only syncs managed assemblies, so the Java stubs, `.dex`, typemap, APK and signature must all be rebuilt and the package reinstalled. This confirms a typemap-affecting C# change is picked up automatically, with no clean required. 3. Add a `Console.WriteLine()` to `SecondActivity.OnCreate()`. No Java-callable types change, so the packaging and signing targets are skipped and the APK is not reinstalled. The message is asserted in `logcat` to prove the updated assembly really was deployed rather than merely that nothing was rebuilt. `BuildOutput.IsApkInstalled` now throws below `LoggerVerbosity.Detailed`. It scans for an `Installed Package` message logged at `MessageImportance.Low`, so at a lower verbosity it silently returned `false` no matter what the build did — which broke the new test on CI and had been quietly making some pre-existing `Assert.IsFalse` calls vacuous. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> (cherry picked from commit 9cc92b0)
Contributor
There was a problem hiding this comment.
Pull request overview
Backport to release/11.0.1xx-preview7 that stabilizes CoreCLR Debug typemap generation across C#-only rebuilds by removing MVID dependence, reducing unnecessary native recompiles and APK rebuild/sign cycles during Fast Deployment.
Changes:
- Switch Debug managed-to-Java typemap keys from per-build MVIDs to stable assembly display names (full names) and remove the Debug MVID table from the native typemap.
- Add a runtime feature switch (
ManagedToJavaUsesAssemblyFullName) enabled for Debug to avoid rootingAssembly.FullNamein Release builds. - Add/extend incremental + device tests to verify typemap/APK stability and that Fast Deployment correctly rebuilds packaging only when Java-callable surface changes.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/MSBuildDeviceIntegration/Tests/FastDevTest.cs | Adds a parameterized FastDeploy/FastDeploy2 device test validating typemap-sensitive vs typemap-neutral edits and APK reinstall behavior. |
| src/Xamarin.Android.Build.Tasks/Utilities/TypeMappingDebugNativeAssemblyGeneratorCLR.cs | Updates CoreCLR Debug typemap generation to embed assembly full names (and drops Debug unique-assemblies/MVID table). |
| src/Xamarin.Android.Build.Tasks/Utilities/TypeMapObjectsXmlFile.cs | Persists/loads assembly-full-name in typemap object XML to support Debug full-name-based keys. |
| src/Xamarin.Android.Build.Tasks/Utilities/TypeMapGenerator.cs | Adds AssemblyFullName to debug entries used by typemap generators. |
| src/Xamarin.Android.Build.Tasks/Utilities/TypeMapCecilAdapter.cs | Implements reflection-compatible assembly full name formatting for build-time typemap generation. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Common/BuildOutput.cs | Makes IsApkInstalled throw when verbosity is too low to observe install messages (avoids vacuous assertions). |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LlvmIrGeneratorTests.cs | Adds unit tests verifying build-time assembly full name formatting matches runtime escaping/strong-name formatting. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs | Adds an incremental test asserting typemap + signed APK remain byte-identical after C#-only edit (CoreCLR Debug). |
| src/Xamarin.Android.Build.Tasks/Tasks/GenerateEmptyTypemapStub.cs | Updates Debug empty typemap stub structs to match updated Debug typemap layout. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets | Plumbs host config option to enable ManagedToJavaUsesAssemblyFullName for Debug builds. |
| src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FindTypeMapObjectsStep.cs | Emits assembly-full-name into typemap object XML for Debug scans. |
| src/native/nativeaot/host/internal-pinvoke-stubs.cc | Updates internal pinvoke stub signature to include assemblyFullName parameter. |
| src/native/clr/xamarin-app-stub/application_dso_stub.cc | Removes Debug unique-assemblies fields from the CoreCLR app stub typemap definitions. |
| src/native/clr/include/xamarin-app.hh | Removes Debug TypeMapAssembly and unique_assemblies_count from TypeMap layout. |
| src/native/clr/include/runtime-base/internal-pinvokes.hh | Updates clr_typemap_managed_to_java native signature to include assemblyFullName. |
| src/native/clr/include/host/typemap.hh | Splits TypeMapper::managed_to_java signature by RELEASE vs DEBUG to accept MVID vs assembly full name. |
| src/native/clr/host/typemap.cc | Debug managed-to-Java lookup now hashes typeName + \", \" + assemblyFullName instead of MVID->assembly mapping. |
| src/native/clr/host/internal-pinvokes-clr.cc | Routes the new pinvoke signature to the correct RELEASE/DEBUG TypeMapper::managed_to_java overload. |
| src/Mono.Android/Microsoft.Android.Runtime/RuntimeFeature.cs | Adds ManagedToJavaUsesAssemblyFullName feature switch definition. |
| src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs | Updates LibraryImport signature for clr_typemap_managed_to_java to include optional assembly full name. |
| src/Mono.Android/Android.Runtime/JNIEnv.cs | Skips MVID computation in CoreCLR Debug when full-name keyed typemaps are enabled; passes type.Assembly.FullName. |
rolfbjarne
approved these changes
Jul 29, 2026
Member
Author
jonathanpeppers
merged commit Jul 29, 2026
a12079f
into
release/11.0.1xx-preview7
43 of 45 checks passed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Backport of #12216 to
release/11.0.1xx-preview7.Pull Request
title and
description
should follow the
commit-messages.mdworkflow documentation, and in particular should include:CoreCLR Debug typemaps embedded application assembly MVIDs. Because an MVID changes on every compilation, a C#-only rebuild rewrote the native typemap even when no Java mappings had changed. That cascaded into native compilation, APK rebuilding, and re-signing on every Fast Deployment cycle.
This changes Debug managed-to-Java entries to key on stable assembly full names and removes the Debug MVID table entirely. Release continues to use its MVID-based lookup, and a trimmer feature switch prevents
Assembly.FullNameretrieval and marshalling from being rooted in Release builds.Runtime
clr_typemap_managed_to_javanow takes an assembly display name alongside the type name, andTypeMapper::managed_to_javais split onRELEASEso each configuration only sees the parameter it uses. When$(RuntimeFeature.ManagedToJavaUsesAssemblyFullName)is enabled,JNIEnv.TypemapManagedToJavaskips computing the MVID altogether rather than computing it and discarding it. A null display name is left for the native side to report, which keeps a single diagnostic path for all callers.TypeMapCecilAdapter.GetRuntimeAssemblyFullNamereproduces reflection's escaping andPublicKeyTokenformatting so that the build-time name matches whatAssembly.FullNamereturns at runtime; a mismatch here would silently break every lookup.Tests
IncrementalBuildTestperforms a C#-only change on the CoreCLR Debug path and asserts the typemap and signed APK are byte-for-byte unchanged, and that_CompileNativeAssemblySources,_CreateApplicationSharedLibraries,_BuildApkFastDev, and_Signare all skipped.FastDeployUpdatesTypeMapAfterAssemblyEditis a device test covering three deployments, parameterized over bothFastDeployandFastDeploy2:MainActivity.SecondActivity. This is a C#-only edit, but it introduces a new Java-callable type. Fast deployment only syncs managed assemblies, so the Java stubs,.dex, typemap, APK and signature must all be rebuilt and the package reinstalled. This confirms a typemap-affecting C# change is picked up automatically, with no clean required.Console.WriteLine()toSecondActivity.OnCreate(). No Java-callable types change, so the packaging and signing targets are skipped and the APK is not reinstalled. The message is asserted inlogcatto prove the updated assembly really was deployed rather than merely that nothing was rebuilt.BuildOutput.IsApkInstallednow throws belowLoggerVerbosity.Detailed. It scans for anInstalled Packagemessage logged atMessageImportance.Low, so at a lower verbosity it silently returnedfalseno matter what the build did — which broke the new test on CI and had been quietly making some pre-existingAssert.IsFalsecalls vacuous.