From de9189ffa62b6d65b16f29bb07a46c17c810270d Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 14 Jan 2026 11:14:52 -0800 Subject: [PATCH 01/10] Migrate StripEmbeddedLibraries from ILLink step to AssemblyModifierPipeline --- .../StripEmbeddedLibrariesStep.cs} | 30 ++++++++++--------- .../Microsoft.Android.Sdk.ILLink.targets | 1 - .../Tasks/AssemblyModifierPipeline.cs | 4 +++ .../Xamarin.Android.Build.Tasks.csproj | 1 + 4 files changed, 21 insertions(+), 15 deletions(-) rename src/{Microsoft.Android.Sdk.ILLink/StripEmbeddedLibraries.cs => Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/StripEmbeddedLibrariesStep.cs} (60%) diff --git a/src/Microsoft.Android.Sdk.ILLink/StripEmbeddedLibraries.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/StripEmbeddedLibrariesStep.cs similarity index 60% rename from src/Microsoft.Android.Sdk.ILLink/StripEmbeddedLibraries.cs rename to src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/StripEmbeddedLibrariesStep.cs index 041bead3f92..16d867d4a41 100644 --- a/src/Microsoft.Android.Sdk.ILLink/StripEmbeddedLibraries.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/StripEmbeddedLibrariesStep.cs @@ -1,36 +1,38 @@ -using Mono.Cecil; -using Mono.Linker; -using Mono.Linker.Steps; using System; using System.Linq; +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Utilities; +using Mono.Cecil; using Xamarin.Android.Tasks; namespace MonoDroid.Tuner { - public class StripEmbeddedLibraries : BaseStep + public class StripEmbeddedLibrariesStep : IAssemblyModifierPipelineStep { - protected override void ProcessAssembly (AssemblyDefinition assembly) + public TaskLoggingHelper Log { get; } + + public StripEmbeddedLibrariesStep (TaskLoggingHelper log) { - if (!Annotations.HasAction (assembly)) - return; - var action = Annotations.GetAction (assembly); - if (action == AssemblyAction.Skip || action == AssemblyAction.Delete) - return; + Log = log; + } - if (MonoAndroidHelper.IsFrameworkAssembly (assembly)) + public void ProcessAssembly (AssemblyDefinition assembly, StepContext context) + { + if (context.IsFrameworkAssembly) return; + bool assembly_modified = false; foreach (var mod in assembly.Modules) { foreach (var r in mod.Resources.ToArray ()) { if (ShouldStripResource (r)) { - Context.LogMessage ($" Stripped {r.Name} from {assembly.Name.Name}.dll"); + Log.LogDebugMessage ($" Stripped {r.Name} from {assembly.Name.Name}.dll"); mod.Resources.Remove (r); assembly_modified = true; } } } - if (assembly_modified && action == AssemblyAction.Copy) { - Annotations.SetAction (assembly, AssemblyAction.Save); + if (assembly_modified) { + context.IsAssemblyModified = true; } } diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets index c0c7ff7de09..1d46da787a6 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets @@ -71,7 +71,6 @@ This file contains the .NET 5-specific targets to customize ILLink Type="MonoDroid.Tuner.AddKeepAlivesStep" /> - <_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" AfterStep="CleanStep" Type="MonoDroid.Tuner.StripEmbeddedLibraries" /> <_TrimmerCustomSteps Condition=" '$(AndroidLinkResources)' == 'true' " Include="$(_AndroidLinkerCustomStepAssembly)" diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs index 08a558de918..df35f011c30 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs @@ -140,6 +140,10 @@ protected virtual void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkCont findJavaObjectsStep.Initialize (context); pipeline.Steps.Add (findJavaObjectsStep); + // StripEmbeddedLibrariesStep + var stripEmbeddedLibrariesStep = new StripEmbeddedLibrariesStep (Log); + pipeline.Steps.Add (stripEmbeddedLibrariesStep); + // SaveChangedAssemblyStep var writerParameters = new WriterParameters { DeterministicMvid = Deterministic, diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj index 79f6e450ae3..b0262d3197b 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -55,6 +55,7 @@ + From e07ee421cecce8bc071d63e2e37039fe5ec54666 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 17 Feb 2026 14:33:10 -0800 Subject: [PATCH 02/10] Fix AccessViolationException in _AfterILLinkAdditionalSteps by writing to separate output directory _AfterILLinkAdditionalSteps was reading and writing assemblies to the same file path (@(ResolvedAssemblies) for both SourceFiles and DestinationFiles). This caused a crash in Mono.Cecil when SaveChangedAssemblyStep wrote a modified assembly back to the same memory-mapped file it was read from, triggering an AccessViolationException in PatchRawMethodBody. Fix by writing outputs to $(MonoAndroidIntermediateAssemblyDir), matching the pattern used by _LinkAssembliesNoShrink. This also enables proper file-list-based incrementalism and preserves ILLink outputs for correct incremental builds. --- .../Microsoft.Android.Sdk.AssemblyResolution.targets | 8 ++++---- .../Xamarin.Android.Common.targets | 10 ++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets index b1717d2b0fe..e18943166a8 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets @@ -206,10 +206,10 @@ _ResolveAssemblies MSBuild target. <_ShrunkFrameworkAssemblies Include="@(_ResolvedFrameworkAssemblies)" /> - <_ResolvedAssemblies Include="@(ResolvedAssemblies)" /> - <_ResolvedUserAssemblies Include="@(ResolvedUserAssemblies)" /> - <_ResolvedFrameworkAssemblies Include="@(ResolvedFrameworkAssemblies)" /> - <_ResolvedSymbols Include="@(ResolvedSymbols)" /> + <_ResolvedAssemblies Include="@(ResolvedAssemblies->'$(MonoAndroidIntermediateAssemblyDir)%(DestinationSubPath)')" Condition=" '%(DestinationSubPath)' != '' " /> + <_ResolvedUserAssemblies Include="@(ResolvedUserAssemblies->'$(MonoAndroidIntermediateAssemblyDir)%(DestinationSubPath)')" Condition=" '%(DestinationSubPath)' != '' " /> + <_ResolvedFrameworkAssemblies Include="@(ResolvedFrameworkAssemblies->'$(MonoAndroidIntermediateAssemblyDir)%(DestinationSubPath)')" Condition=" '%(DestinationSubPath)' != '' " /> + <_ResolvedSymbols Include="@(ResolvedSymbols->'$(MonoAndroidIntermediateAssemblyDir)%(DestinationSubPath)')" Condition=" '%(DestinationSubPath)' != '' " /> <_ShrunkFrameworkAssemblies Include="@(_ShrunkAssemblies)" Condition=" '%(_ShrunkAssemblies.FrameworkAssembly)' == 'true' " diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 30e7b9d3813..abfb7ef6c4e 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1456,13 +1456,13 @@ because xbuild doesn't support framework reference assemblies. + Inputs="@(ResolvedAssemblies)" + Outputs="@(ResolvedAssemblies->'$(MonoAndroidIntermediateAssemblyDir)%(DestinationSubPath)')"> - + + + From 91e220c0e2b542c00e5ebf75ca2d0ba7e401af93 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Tue, 17 Feb 2026 17:45:55 -0800 Subject: [PATCH 03/10] Fix NotSupportedException writing crossgen'd (R2R) assemblies by stripping mixed-mode metadata Mono.Cecil cannot write mixed-mode assemblies, throwing NotSupportedException. Before writing, strip R2R metadata (set ILOnly, clear ILLibrary, reset Architecture) matching ILLink's OutputStep behavior. The native R2R code is discarded since the assembly was modified and would need re-crossgen anyway. --- .../Tasks/AssemblyModifierPipeline.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs b/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs index df35f011c30..1de520eb74c 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/AssemblyModifierPipeline.cs @@ -197,6 +197,20 @@ public void ProcessAssembly (AssemblyDefinition assembly, StepContext context) if (context.IsAssemblyModified) { Log.LogDebugMessage ($"Saving modified assembly: {context.Destination.ItemSpec}"); Directory.CreateDirectory (Path.GetDirectoryName (context.Destination.ItemSpec)); + + // Write back pure IL even for crossgen-ed (R2R) assemblies, matching ILLink's OutputStep behavior. + // Mono.Cecil cannot write mixed-mode assemblies, so we strip the R2R metadata before writing. + // The native R2R code is discarded since the assembly has been modified and would need to be + // re-crossgen'd anyway. + foreach (var module in assembly.Modules) { + if (IsCrossgened (module)) { + module.Attributes |= ModuleAttributes.ILOnly; + module.Attributes ^= ModuleAttributes.ILLibrary; + module.Architecture = TargetArchitecture.I386; // I386+ILOnly translates to AnyCPU + module.Characteristics |= ModuleCharacteristics.NoSEH; + } + } + WriterParameters.WriteSymbols = assembly.MainModule.HasSymbols; assembly.Write (context.Destination.ItemSpec, WriterParameters); } else { @@ -219,4 +233,14 @@ void CopyIfChanged (ITaskItem source, ITaskItem destination) File.SetLastWriteTimeUtc (destination.ItemSpec, DateTime.UtcNow); } } + + /// + /// Check if a module has been crossgen-ed (ReadyToRun compiled), matching + /// ILLink's ModuleDefinitionExtensions.IsCrossgened() implementation. + /// + static bool IsCrossgened (ModuleDefinition module) + { + return (module.Attributes & ModuleAttributes.ILOnly) == 0 && + (module.Attributes & ModuleAttributes.ILLibrary) != 0; + } } From 41eea498ec49c02469884c9f99b3baa9f1e59fe5 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 19 Feb 2026 09:44:52 -0800 Subject: [PATCH 04/10] Fix CheckSignApk NativeAOT test to not expect IL3053 warnings on incremental builds The second build in CheckSignApk only changes Strings.xml (a resource file), so IlcCompile is correctly skipped and no IL3053 warnings are produced. Previously this passed by accident because _AfterILLinkAdditionalSteps wrote assemblies in-place to the ILLink output directory, which invalidated IlcCompile's inputs and caused it to unnecessarily re-run. --- .../Tests/Xamarin.Android.Build.Tests/PackagingTest.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs index b0dcc115436..e8d70162a58 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs @@ -560,11 +560,9 @@ public void CheckSignApk ([Values] bool useApkSigner, [Values] bool perAbiApk, [ item.TextContent = () => proj.StringsXml.Replace ("${PROJECT_NAME}", "Foo"); item.Timestamp = null; Assert.IsTrue (b.Build (proj), "Second build failed"); - if (runtime != AndroidRuntime.NativeAOT) { - b.AssertHasNoWarnings (); - } else { - StringAssertEx.Contains ("2 Warning(s)", b.LastBuildOutput, "NativeAOT should produce two IL3053 warnings"); - } + // The second build only changes a resource file, so IlcCompile is correctly + // skipped and no IL3053 warnings are produced on the incremental build. + b.AssertHasNoWarnings (); //Make sure the APKs are signed foreach (var apk in Directory.GetFiles (bin, "*-Signed.apk")) { From 3b353713c0204bb449738b2cc01bda9d52f1c6af Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 19 Feb 2026 09:45:50 -0800 Subject: [PATCH 05/10] Remove unused _AdditionalPostLinkerStepsFlag property This stamp file is no longer used since _AfterILLinkAdditionalSteps was changed to use file-list-based incrementality in e07ee42. --- src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index abfb7ef6c4e..56eb120e84d 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -922,7 +922,6 @@ because xbuild doesn't support framework reference assemblies. <_AndroidLinkFlag>$(IntermediateOutputPath)link.flag <_AndroidApkPerAbiFlagFile>$(IntermediateOutputPath)android\bin\apk_per_abi.flag <_AndroidDebugKeyStoreFlag>$(IntermediateOutputPath)android_debug_keystore.flag - <_AdditionalPostLinkerStepsFlag>$(_AndroidStampDirectory)_AdditionalPostLinkerSteps.stamp <_AcwMapFile>$(IntermediateOutputPath)acw-map.txt <_CustomViewMapFile>$(IntermediateOutputPath)customview-map.txt $(RootNamespace) From da98eb321fcaacfb468f78b7def2ca542366ade4 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 23 Feb 2026 12:59:52 -0800 Subject: [PATCH 06/10] Fix CheckNothingIsDeletedByIncrementalClean by moving _RemoveRegisterFlag to stamp directory The _RemoveRegisterAttribute target's stamp file (shrunk.flag) was located at $(MonoAndroidIntermediateAssemblyDir)shrunk/shrunk.flag, inside the tree globbed by . Since _RemoveRegisterAttribute runs during SignAndroidPackage (after IncrementalClean), the glob didn't capture the stamp on build 1 but did on build 2, causing FileListAbsolute.txt to grow. Fix by: - Moving the stamp to $(_AndroidStampDirectory)_RemoveRegisterAttribute.stamp, following the existing naming convention for stamp files - Adding _RemoveRegisterAttribute to IncrementalCleanDependsOn so it runs before _AddFilesToFileWrites, ensuring the stamp exists when the $(_AndroidStampDirectory)*.stamp glob captures it - Removing the now-unnecessary MakeDir for the old shrunk directory (it was only needed for the flag file, not for shrunk assemblies which go to the publish directory) --- .../targets/Microsoft.Android.Sdk.BuildOrder.targets | 1 + .../Microsoft.Android.Sdk.TypeMap.LlvmIr.targets | 3 +-- .../IncrementalBuildTest.cs | 12 ++++++------ .../Xamarin.Android.Common.targets | 1 - 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index 039bc54f78a..4de4b7712be 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -54,6 +54,7 @@ properties that determine build ordering. _PrepareAssemblies; + _RemoveRegisterAttribute; _CompileDex; $(_AfterCompileDex); _AddFilesToFileWrites; diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets index 5eb1b723ff1..133527d12ec 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets @@ -9,7 +9,7 @@ - <_RemoveRegisterFlag>$(MonoAndroidIntermediateAssemblyDir)shrunk\shrunk.flag + <_RemoveRegisterFlag>$(_AndroidStampDirectory)_RemoveRegisterAttribute.stamp <_BeforeAddStaticResources>$(_BeforeAddStaticResources);_GetMonoPlatformJarPath <_BeforeCompileJava>$(_BeforeCompileJava);_GetMonoPlatformJarPath @@ -325,7 +325,6 @@ - diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs index 573e5ec5755..2bae7f4de98 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/IncrementalBuildTest.cs @@ -143,12 +143,12 @@ public void CheckNothingIsDeletedByIncrementalClean ([Values] bool enableMultiDe FileAssert.Exists (file); File.SetLastWriteTimeUtc (file, DateTime.UtcNow); } - Assert.IsTrue (b.Build (proj, doNotCleanupOnUpdate: true, saveProject: false), "Second should have succeeded"); - b.Output.AssertTargetIsNotSkipped ("_CleanMonoAndroidIntermediateDir"); - var stampFiles = Path.Combine (intermediate, "stamp", "_ResolveLibraryProjectImports.stamp"); - FileAssert.Exists (stampFiles, $"{stampFiles} should exist!"); - var libraryProjectImports = Path.Combine (intermediate, "libraryprojectimports.cache"); - FileAssert.Exists (libraryProjectImports, $"{libraryProjectImports} should exist!"); + Assert.IsTrue (b.Build (proj, doNotCleanupOnUpdate: true, saveProject: false), "Second should have succeeded"); + b.Output.AssertTargetIsNotSkipped ("_CleanMonoAndroidIntermediateDir"); + var stampFiles = Path.Combine (intermediate, "stamp", "_ResolveLibraryProjectImports.stamp"); + FileAssert.Exists (stampFiles, $"{stampFiles} should exist!"); + var libraryProjectImports = Path.Combine (intermediate, "libraryprojectimports.cache"); + FileAssert.Exists (libraryProjectImports, $"{libraryProjectImports} should exist!"); //No changes Assert.IsTrue (b.Build (proj, doNotCleanupOnUpdate: true, saveProject: false), "Third should have succeeded"); diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 56eb120e84d..9f774efbc58 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1917,7 +1917,6 @@ because xbuild doesn't support framework reference assemblies. - Date: Mon, 23 Feb 2026 18:23:10 -0800 Subject: [PATCH 07/10] Fix NativeAOT incremental builds by adding project assembly to ManagedAssemblyToLink MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Android reorders IlcCompileDependsOn so ILLink runs before ComputeIlcCompileInputs. This means @(ManagedBinary) is empty when _ComputeManagedAssemblyForILLink builds @(ManagedAssemblyToLink), so the project assembly is missing and _RunILLink's incremental Inputs check doesn't detect changes to it — causing ILLink to skip on rebuild. Add _AndroidFixManagedAssemblyToLink to inject @(IntermediateAssembly) into @(ManagedAssemblyToLink) after the replacement, and exclude the project assembly from _AndroidILLinkAssemblies to prevent duplicate entries in ResolvedFileToPublish. --- .../Microsoft.Android.Sdk.NativeAOT.targets | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets index 13f050da1b4..8a27229291d 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.NativeAOT.targets @@ -126,6 +126,19 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. + + + + + + + @@ -142,7 +155,10 @@ This file contains the NativeAOT-specific MSBuild logic for .NET for Android. - <_AndroidILLinkAssemblies Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" Condition="Exists('$(IntermediateLinkDir)%(Filename)%(Extension)')" /> + + <_AndroidILLinkAssemblies Include="@(ManagedAssemblyToLink->'$(IntermediateLinkDir)%(Filename)%(Extension)')" Condition="Exists('$(IntermediateLinkDir)%(Filename)%(Extension)') and '%(Filename)' != '$(TargetName)'" /> From 714290a2cf749a509e2ba4d4fc14adf487381660 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 25 Feb 2026 09:16:02 -0800 Subject: [PATCH 08/10] Fix _RemoveRegisterAttribute incremental clean by excluding shrunk/ from FileWrites glob The previous fix (da98eb321) moved the stamp file but also removed MakeDir for the shrunk directory, breaking emulator tests because assemblies were never copied to the shrunk/ subdirectory. Fix by: - Restoring MakeDir for the shrunk directory in both _RemoveRegisterAttribute target definitions (LlvmIr override and Common.targets base) - Excluding shrunk/ from the FileWrites glob in _LinkAssembliesNoShrink and _AfterILLinkAdditionalSteps, since those files are owned by _RemoveRegisterAttribute, not by the assembly linking targets - Adding FileWrites for shrunk/** inside _RemoveRegisterAttribute itself, so the shrunk files are properly tracked by IncrementalClean when the target runs --- .../targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets | 4 ++++ .../Xamarin.Android.Common.targets | 8 ++++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets index 133527d12ec..45b2f7ad46b 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets @@ -325,7 +325,11 @@ + + + + diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 9f774efbc58..fe60c375b46 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -1447,7 +1447,7 @@ because xbuild doesn't support framework reference assemblies. - + @@ -1474,7 +1474,7 @@ because xbuild doesn't support framework reference assemblies. - + @@ -1917,6 +1917,10 @@ because xbuild doesn't support framework reference assemblies. + + + + Date: Wed, 25 Feb 2026 15:31:50 -0800 Subject: [PATCH 09/10] Fix SIGABRT crash by restoring _RemoveRegisterAttribute build ordering to match main Remove _RemoveRegisterAttribute from IncrementalCleanDependsOn (added in da98eb321) which caused it to run before RewriteMarshalMethods, producing assemblies without _mm_wrapper methods and crashing non-AOT configs. Revert _RemoveRegisterFlag path to shrunk/shrunk.flag and remove FileWrites glob from _RemoveRegisterAttribute target, matching main's behavior where shrunk files are never tracked by IncrementalClean. --- .../targets/Microsoft.Android.Sdk.BuildOrder.targets | 1 - .../targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets index 4de4b7712be..039bc54f78a 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.BuildOrder.targets @@ -54,7 +54,6 @@ properties that determine build ordering. _PrepareAssemblies; - _RemoveRegisterAttribute; _CompileDex; $(_AfterCompileDex); _AddFilesToFileWrites; diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets index 45b2f7ad46b..5eb1b723ff1 100644 --- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets +++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets @@ -9,7 +9,7 @@ - <_RemoveRegisterFlag>$(_AndroidStampDirectory)_RemoveRegisterAttribute.stamp + <_RemoveRegisterFlag>$(MonoAndroidIntermediateAssemblyDir)shrunk\shrunk.flag <_BeforeAddStaticResources>$(_BeforeAddStaticResources);_GetMonoPlatformJarPath <_BeforeCompileJava>$(_BeforeCompileJava);_GetMonoPlatformJarPath @@ -327,9 +327,6 @@ ShrunkFrameworkAssemblies="@(_ShrunkAssemblies)" /> - - - From 09f590310075c5d94368c9bc13bd5a37402ea07d Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 26 Feb 2026 18:13:11 -0800 Subject: [PATCH 10/10] Fix MarshalMethodsCollectionScanning test to read from android/assets/ RewriteMarshalMethods now modifies assemblies in android/assets/ instead of linked/ after the path flow change in _AfterILLinkAdditionalSteps. Update the test to read rewritten assemblies from the correct location. --- .../Tests/Xamarin.Android.Build.Tests/MarshalMethodTests.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/MarshalMethodTests.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/MarshalMethodTests.cs index 96ea956e6cc..b4bf4f1d5e6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/MarshalMethodTests.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/MarshalMethodTests.cs @@ -142,7 +142,8 @@ class MyOverriddenGreeter : Com.Xamarin.Android.Test.Msbuildtest.JavaSourceTestE builder.AssertHasNoWarnings (); // Rescan for modified marshal methods - var intermediateReleaseOutputPath = Path.Combine (Root, builder.ProjectDirectory, proj.IntermediateOutputPath, "android-arm64", "linked"); + // RewriteMarshalMethods modifies assemblies in android/assets/ (after _AfterILLinkAdditionalSteps copies them there) + var intermediateReleaseOutputPath = Path.Combine (Root, builder.ProjectDirectory, proj.IntermediateOutputPath, "android", "assets", "arm64-v8a"); var outputReleaseDll = Path.Combine (intermediateReleaseOutputPath, $"{proj.ProjectName}.dll"); xaResolver = new XAAssemblyResolver (Tools.AndroidTargetArch.Arm64, log, false);