From de481945eb665e634f8c8862e27d6c49292f2adc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 09:19:53 +0000 Subject: [PATCH 1/3] Initial plan From 83ddb03ebfb5e02e54e46c9a353b7b08edb0a613 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 2 Apr 2026 09:31:39 +0000 Subject: [PATCH 2/3] Enable R2R when publishing with custom Configuration value for CoreCLR Update the PublishReadyToRun condition in Microsoft.Android.Sdk.CoreCLR.targets to also check _IsPublishing, so R2R is enabled either when Configuration is Release OR when the project is being published via dotnet publish. This fixes the scenario where customers use custom Configuration values (e.g. dotnet publish -c MyCustomConfig) and R2R was not enabled for CoreCLR. Fixes https://github.com/dotnet/android/issues/9944 Agent-Logs-Url: https://github.com/dotnet/android/sessions/09eff3bb-5a27-4d30-9025-395f55206033 Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../Microsoft.Android.Sdk.CoreCLR.targets | 4 +-- .../Xamarin.Android.Build.Tests/BuildTest2.cs | 30 +++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets index 705237961ab..a74ffe62b34 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.CoreCLR.targets @@ -13,8 +13,8 @@ This file contains the CoreCLR-specific MSBuild logic for .NET for Android. - - true + + true true <_IsPublishing Condition=" '$(_IsPublishing)' == '' and '$(PublishReadyToRun)' == 'true' ">true true diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs index 759d6a2f855..99c7012d309 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs @@ -150,6 +150,36 @@ public void BasicApplicationPublishReadyToRun ([Values] bool isComposite, [Value $"ReadyToRun image not found in {assemblyName}.dll! ManagedNativeHeaderDirectory should not be empty!"); } + [Test] + public void PublishReadyToRunCustomConfiguration ([Values ("android-arm64")] string rid) + { + var proj = new XamarinAndroidApplicationProject (releaseConfigurationName: "CustomRelease") { + IsRelease = true, // Uses "CustomRelease" as the configuration, not "Release" + }; + + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetProperty ("RuntimeIdentifier", rid); + proj.SetProperty ("AndroidEnableAssemblyCompression", "false"); + proj.SetProperty ("_IsPublishing", "true"); // Simulates `dotnet publish` + + var b = CreateApkBuilder (); + Assert.IsTrue (b.Build (proj), "Build should have succeeded."); + + var assemblyName = proj.ProjectName; + var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, rid, $"{proj.PackageName}-Signed.apk"); + FileAssert.Exists (apk); + + var helper = new ArchiveAssemblyHelper (apk, true); + var abi = MonoAndroidHelper.RidToAbi (rid); + Assert.IsTrue (helper.Exists ($"assemblies/{abi}/{assemblyName}.dll"), $"{assemblyName}.dll should exist in apk!"); + + using var stream = helper.ReadEntry ($"assemblies/{assemblyName}.dll"); + stream.Position = 0; + using var peReader = new System.Reflection.PortableExecutable.PEReader (stream); + Assert.IsTrue (peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size > 0, + $"ReadyToRun image not found in {assemblyName}.dll! ManagedNativeHeaderDirectory should not be empty!"); + } + [Test] public void NativeAOT () { From 7701798b1bc4682aff89705c4ec9dbc17f5f8da7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 7 May 2026 14:40:09 +0000 Subject: [PATCH 3/3] Rewrite test to use DotNetCLI.Publish with custom Configuration Replace the test that set _IsPublishing directly with an end-to-end test in XASdkTests that calls DotNetCLI.Publish with Configuration=CustomRelease, then verifies the produced APK contains R2R data via PE header inspection. Agent-Logs-Url: https://github.com/dotnet/android/sessions/0ec75c40-8151-4570-84dc-84db66c5d887 Co-authored-by: jonathanpeppers <840039+jonathanpeppers@users.noreply.github.com> --- .../Xamarin.Android.Build.Tests/BuildTest2.cs | 30 ----------------- .../Xamarin.Android.Build.Tests/XASdkTests.cs | 32 +++++++++++++++++++ 2 files changed, 32 insertions(+), 30 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs index 99c7012d309..759d6a2f855 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs @@ -150,36 +150,6 @@ public void BasicApplicationPublishReadyToRun ([Values] bool isComposite, [Value $"ReadyToRun image not found in {assemblyName}.dll! ManagedNativeHeaderDirectory should not be empty!"); } - [Test] - public void PublishReadyToRunCustomConfiguration ([Values ("android-arm64")] string rid) - { - var proj = new XamarinAndroidApplicationProject (releaseConfigurationName: "CustomRelease") { - IsRelease = true, // Uses "CustomRelease" as the configuration, not "Release" - }; - - proj.SetRuntime (AndroidRuntime.CoreCLR); - proj.SetProperty ("RuntimeIdentifier", rid); - proj.SetProperty ("AndroidEnableAssemblyCompression", "false"); - proj.SetProperty ("_IsPublishing", "true"); // Simulates `dotnet publish` - - var b = CreateApkBuilder (); - Assert.IsTrue (b.Build (proj), "Build should have succeeded."); - - var assemblyName = proj.ProjectName; - var apk = Path.Combine (Root, b.ProjectDirectory, proj.OutputPath, rid, $"{proj.PackageName}-Signed.apk"); - FileAssert.Exists (apk); - - var helper = new ArchiveAssemblyHelper (apk, true); - var abi = MonoAndroidHelper.RidToAbi (rid); - Assert.IsTrue (helper.Exists ($"assemblies/{abi}/{assemblyName}.dll"), $"{assemblyName}.dll should exist in apk!"); - - using var stream = helper.ReadEntry ($"assemblies/{assemblyName}.dll"); - stream.Position = 0; - using var peReader = new System.Reflection.PortableExecutable.PEReader (stream); - Assert.IsTrue (peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size > 0, - $"ReadyToRun image not found in {assemblyName}.dll! ManagedNativeHeaderDirectory should not be empty!"); - } - [Test] public void NativeAOT () { diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs index e3c049a1295..3a863bd0fb2 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/XASdkTests.cs @@ -4,6 +4,7 @@ using System.Linq; using System.Xml.Linq; using System.Reflection; +using System.Reflection.PortableExecutable; using System.Text; using NUnit.Framework; using Xamarin.Android.Tools; @@ -279,6 +280,37 @@ public void DotNetPublishDefaultValues ([Values] bool isRelease, [Values] Androi Assert.IsTrue (dotnet.Publish (), "`dotnet publish` should succeed"); } + [Test] + public void DotNetPublishReadyToRunCustomConfiguration () + { + const string runtimeIdentifier = "android-arm64"; + var proj = new XamarinAndroidApplicationProject (); + proj.SetRuntime (AndroidRuntime.CoreCLR); + proj.SetProperty (KnownProperties.RuntimeIdentifier, runtimeIdentifier); + proj.SetProperty ("AndroidEnableAssemblyCompression", "false"); + + var builder = CreateDllBuilder (); + builder.Save (proj); + var dotnet = new DotNetCLI (Path.Combine (Root, builder.ProjectDirectory, proj.ProjectFilePath)); + Assert.IsTrue (dotnet.Publish (parameters: new [] { "Configuration=CustomRelease" }), "`dotnet publish -c CustomRelease` should succeed"); + + var assemblyName = proj.ProjectName; + // Configuration=CustomRelease produces output under bin/CustomRelease/ + var publishDirectory = Path.Combine (Root, builder.ProjectDirectory, "bin", "CustomRelease", runtimeIdentifier, "publish"); + var apkSigned = Path.Combine (publishDirectory, $"{proj.PackageName}-Signed.apk"); + FileAssert.Exists (apkSigned); + + var helper = new ArchiveAssemblyHelper (apkSigned, true); + var abi = MonoAndroidHelper.RidToAbi (runtimeIdentifier); + Assert.IsTrue (helper.Exists ($"assemblies/{abi}/{assemblyName}.dll"), $"{assemblyName}.dll should exist in apk!"); + + using var stream = helper.ReadEntry ($"assemblies/{assemblyName}.dll"); + stream.Position = 0; + using var peReader = new PEReader (stream); + Assert.IsTrue (peReader.PEHeaders.CorHeader.ManagedNativeHeaderDirectory.Size > 0, + $"ReadyToRun image not found in {assemblyName}.dll! ManagedNativeHeaderDirectory should not be empty!"); + } + [Test] public void DotNetPublish ([Values] bool isRelease, [ValueSource (nameof(Get_DotNetTargetFrameworks_Data))] object[] data) {