From 923e10922400939cf1a8669acd0f767b7e30e8ed Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 22 Jul 2026 09:37:41 -0500 Subject: [PATCH 1/7] [build] Default interpreter only for MonoVM CoreCLR Debug builds currently inherit UseInterpreter=true even though the interpreter is a MonoVM feature. Gate the Debug default on UseMonoRuntime while preserving explicit property values. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 599ee730-f9a4-4c0b-9d97-1c51427a6711 --- .../targets/Microsoft.Android.Sdk.DefaultProperties.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index b7a35e2e08d..c50a0074dc5 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -73,7 +73,7 @@ None - true + true true From 5bd9bbf3d12f5c2b21611b824342867935b367d5 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Wed, 22 Jul 2026 09:45:16 -0500 Subject: [PATCH 2/7] [build] Check the effective Android runtime Use the already-computed _AndroidRuntime property when deciding whether to default UseInterpreter. This avoids enabling the interpreter for NativeAOT when both PublishAot and UseMonoRuntime are true. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 599ee730-f9a4-4c0b-9d97-1c51427a6711 --- .../targets/Microsoft.Android.Sdk.DefaultProperties.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets index c50a0074dc5..3701c41be58 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets @@ -73,7 +73,7 @@ None - true + true true From b35ddc9db259ef4b731fea6f364855fe09dec11a Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Jul 2026 07:39:51 -0500 Subject: [PATCH 3/7] [tests] Update CoreCLR interpreter expectation CoreCLR no longer defaults UseInterpreter to true in Debug builds, so DOTNET_MODIFIABLE_ASSEMBLIES should not be set. Update the device integration test to assert the new expected behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 599ee730-f9a4-4c0b-9d97-1c51427a6711 --- .../Tests/InstallAndRunTests.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 4ff7544eefb..66e77e1ec62 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -2212,14 +2212,12 @@ public void FastDeployEnvironmentFiles ([Values] bool isRelease, [Values] bool e logcatOutput, "The Environment variable \"DOTNET_DiagnosticPorts\" was not set to expected value \"127.0.0.1:9000,connect,nosuspend\"." ); - // NOTE: set when $(UseInterpreter) is true, default for Debug mode - if (!isRelease) { - StringAssert.Contains ( - "DOTNET_MODIFIABLE_ASSEMBLIES=Debug", - logcatOutput, - "The Environment variable \"DOTNET_MODIFIABLE_ASSEMBLIES\" was not set." - ); - } + // NOTE: set when $(UseInterpreter) is true, which is not the default for CoreCLR + StringAssert.DoesNotContain ( + "DOTNET_MODIFIABLE_ASSEMBLIES=Debug", + logcatOutput, + "The Environment variable \"DOTNET_MODIFIABLE_ASSEMBLIES\" was unexpectedly set." + ); } [Test] From 73f63c13ba481c9bc9e5be7c55622ab12068a953 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Jul 2026 07:41:11 -0500 Subject: [PATCH 4/7] [tests] Explicitly enable interpreter coverage Keep FastDeployEnvironmentFiles focused on verifying that DOTNET_MODIFIABLE_ASSEMBLIES reaches the running app. Explicitly opt CoreCLR Debug builds into UseInterpreter now that it is no longer the default. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 599ee730-f9a4-4c0b-9d97-1c51427a6711 --- .../Tests/InstallAndRunTests.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 66e77e1ec62..f8c62702f28 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -2163,6 +2163,9 @@ public void FastDeployEnvironmentFiles ([Values] bool isRelease, [Values] bool e } }; proj.SetRuntime (runtime); + if (!isRelease) { + proj.SetProperty ("UseInterpreter", "true"); + } proj.SetProperty ("DiagnosticAddress", "127.0.0.1"); proj.SetProperty ("DiagnosticPort", "9000"); proj.SetProperty ("DiagnosticSuspend", "false"); @@ -2212,12 +2215,14 @@ public void FastDeployEnvironmentFiles ([Values] bool isRelease, [Values] bool e logcatOutput, "The Environment variable \"DOTNET_DiagnosticPorts\" was not set to expected value \"127.0.0.1:9000,connect,nosuspend\"." ); - // NOTE: set when $(UseInterpreter) is true, which is not the default for CoreCLR - StringAssert.DoesNotContain ( - "DOTNET_MODIFIABLE_ASSEMBLIES=Debug", - logcatOutput, - "The Environment variable \"DOTNET_MODIFIABLE_ASSEMBLIES\" was unexpectedly set." - ); + // NOTE: set when $(UseInterpreter) is true + if (!isRelease) { + StringAssert.Contains ( + "DOTNET_MODIFIABLE_ASSEMBLIES=Debug", + logcatOutput, + "The Environment variable \"DOTNET_MODIFIABLE_ASSEMBLIES\" was not set." + ); + } } [Test] From 6e81beed100f7521447c98773437cae0805233be Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Jul 2026 07:50:47 -0500 Subject: [PATCH 5/7] [build] Preserve modifiable assemblies for Debug Android hot reload requires DOTNET_MODIFIABLE_ASSEMBLIES=Debug even when the CoreCLR runtime does not use the interpreter. Generate the variable whenever the debug libmonodroid.so is selected, update the host expectation, and retain the device test's existing behavior. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 599ee730-f9a4-4c0b-9d97-1c51427a6711 --- .../Tests/Xamarin.Android.Build.Tests/BuildTest.cs | 2 +- .../Xamarin.Android.Common.targets | 2 +- tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs | 5 +---- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index d5641a715e9..19e59c2c1fe 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -1748,7 +1748,7 @@ public void EnvironmentVariables (bool useInterpreter, string packageNamingPolic var values = new List { "mono.enable_assembly_preload=0", }; - if (useInterpreter) + if (!isRelease) values.Add ("DOTNET_MODIFIABLE_ASSEMBLIES=Debug"); if (!string.IsNullOrEmpty (diagnosticConfiguration)) values.Add ($"DOTNET_DiagnosticPorts={diagnosticConfiguration}"); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 2690ef12a13..a43f1074ca1 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1651,7 +1651,7 @@ because xbuild doesn't support framework reference assemblies. <_GeneratedAndroidEnvironment Include="mono.enable_assembly_preload=0" Condition=" '$(AndroidEnablePreloadAssemblies)' != 'True' " /> - <_GeneratedAndroidEnvironment Include="DOTNET_MODIFIABLE_ASSEMBLIES=Debug" Condition=" '$(AndroidIncludeDebugSymbols)' == 'true' and '$(AndroidUseInterpreter)' == 'true' " /> + <_GeneratedAndroidEnvironment Include="DOTNET_MODIFIABLE_ASSEMBLIES=Debug" Condition=" '$(AndroidIncludeDebugSymbols)' == 'true' " /> <_GeneratedAndroidEnvironment Include="DOTNET_DiagnosticPorts=$(DiagnosticConfiguration)" Condition=" '$(DiagnosticConfiguration)' != '' " /> <_GeneratedAndroidEnvironment Include="DOTNET_EnableCrashReport=1" Condition=" '$(EnableCrashReport)' == 'true' and '$(UseMonoRuntime)' != 'true' " /> diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index f8c62702f28..939941bea72 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -2163,9 +2163,6 @@ public void FastDeployEnvironmentFiles ([Values] bool isRelease, [Values] bool e } }; proj.SetRuntime (runtime); - if (!isRelease) { - proj.SetProperty ("UseInterpreter", "true"); - } proj.SetProperty ("DiagnosticAddress", "127.0.0.1"); proj.SetProperty ("DiagnosticPort", "9000"); proj.SetProperty ("DiagnosticSuspend", "false"); @@ -2215,7 +2212,7 @@ public void FastDeployEnvironmentFiles ([Values] bool isRelease, [Values] bool e logcatOutput, "The Environment variable \"DOTNET_DiagnosticPorts\" was not set to expected value \"127.0.0.1:9000,connect,nosuspend\"." ); - // NOTE: set when $(UseInterpreter) is true + // NOTE: set when the debug version of libmonodroid.so is used if (!isRelease) { StringAssert.Contains ( "DOTNET_MODIFIABLE_ASSEMBLIES=Debug", From e03bd16be19ffb844f1036e8890c332a18cca2d4 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Jul 2026 07:51:16 -0500 Subject: [PATCH 6/7] [tests] Name the debug-symbol property Update the environment-variable test comment to identify AndroidIncludeDebugSymbols as the direct condition. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 599ee730-f9a4-4c0b-9d97-1c51427a6711 --- tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 939941bea72..f854e6f70c6 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -2212,7 +2212,7 @@ public void FastDeployEnvironmentFiles ([Values] bool isRelease, [Values] bool e logcatOutput, "The Environment variable \"DOTNET_DiagnosticPorts\" was not set to expected value \"127.0.0.1:9000,connect,nosuspend\"." ); - // NOTE: set when the debug version of libmonodroid.so is used + // NOTE: set when $(AndroidIncludeDebugSymbols) is true if (!isRelease) { StringAssert.Contains ( "DOTNET_MODIFIABLE_ASSEMBLIES=Debug", From abbe43969f07a87b0fb1b2ee81a49c2e7aa7f199 Mon Sep 17 00:00:00 2001 From: Jonathan Peppers Date: Thu, 23 Jul 2026 07:53:15 -0500 Subject: [PATCH 7/7] [tests] Remove stale interpreter parameter DOTNET_MODIFIABLE_ASSEMBLIES now follows AndroidIncludeDebugSymbols, so the environment-file test no longer needs to vary UseInterpreter. Remove that parameter and the NativeAOT skip tied to the old expectation. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 599ee730-f9a4-4c0b-9d97-1c51427a6711 --- .../Xamarin.Android.Build.Tests/BuildTest.cs | 30 ++++++------------- 1 file changed, 9 insertions(+), 21 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs index 19e59c2c1fe..4f8a42ff111 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs @@ -1687,17 +1687,16 @@ static IEnumerable Get_EnvironmentVariablesData () var ret = new List (); foreach (AndroidRuntime runtime in new[] { AndroidRuntime.CoreCLR, AndroidRuntime.NativeAOT }) { - AddTestData (true, "LowercaseMD5", "", runtime, runtime == AndroidRuntime.CoreCLR); - AddTestData (true, "LowercaseCrc64", "", runtime, false); - AddTestData (false, "", "127.0.0.1:9000,suspend,connect", runtime, false); + AddTestData ("LowercaseMD5", "", runtime, runtime == AndroidRuntime.CoreCLR); + AddTestData ("LowercaseCrc64", "", runtime, false); + AddTestData ("", "127.0.0.1:9000,suspend,connect", runtime, false); } return ret; - void AddTestData (bool useInterpreter, string packageNamingPolicy, string diagnosticConfiguration, AndroidRuntime runtime, bool enableCrashReport) + void AddTestData (string packageNamingPolicy, string diagnosticConfiguration, AndroidRuntime runtime, bool enableCrashReport) { ret.Add (new object[] { - useInterpreter, packageNamingPolicy, diagnosticConfiguration, runtime, @@ -1708,34 +1707,23 @@ void AddTestData (bool useInterpreter, string packageNamingPolicy, string diagno [Test] [TestCaseSource (nameof (Get_EnvironmentVariablesData))] - public void EnvironmentVariables (bool useInterpreter, string packageNamingPolicy, string diagnosticConfiguration, AndroidRuntime runtime, bool enableCrashReport) + public void EnvironmentVariables (string packageNamingPolicy, string diagnosticConfiguration, AndroidRuntime runtime, bool enableCrashReport) { - // NativeAOT supports neither the interpreter nor debug builds, but what we test here is - // environment file creation and contents, and that's relevant to NativeAOT too + // NativeAOT does not support debug builds, but environment file creation and contents are relevant to NativeAOT too. bool isRelease = runtime == AndroidRuntime.NativeAOT; if (IgnoreUnsupportedConfiguration (runtime, release: isRelease)) { return; } - if (runtime == AndroidRuntime.NativeAOT) { - if (packageNamingPolicy == "LowercaseMD5") { - Assert.Ignore ("NativeAOT does not support the 'LowercaseMD5' package naming policy."); - return; - } - - // TODO: investigate and fix this. For some reason, `DOTNET_MODIFIABLE_ASSEMBLIES=Debug` is not - // in the environment variables file - if (useInterpreter && packageNamingPolicy == "LowercaseCrc64" && diagnosticConfiguration == "") { - Assert.Ignore ("NativeAOT doesn't put the DOTNET_MODIFIABLE_ASSEMBLIES=Debug variable in the environment file."); - return; - } + if (runtime == AndroidRuntime.NativeAOT && packageNamingPolicy == "LowercaseMD5") { + Assert.Ignore ("NativeAOT does not support the 'LowercaseMD5' package naming policy."); + return; } var proj = new XamarinAndroidApplicationProject { IsRelease = isRelease, }; proj.SetRuntime (runtime); - proj.SetProperty ("UseInterpreter", useInterpreter.ToString ()); proj.SetProperty ("EnableCrashReport", enableCrashReport.ToString ()); if (!string.IsNullOrEmpty (packageNamingPolicy)) proj.SetProperty ("AndroidPackageNamingPolicy", packageNamingPolicy);