From 70f7f30284cf6821a0299e046e17688ce6d3bb44 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Mon, 30 Mar 2026 16:55:43 -0700 Subject: [PATCH 1/4] [illink] Move FixLegacyResourceDesignerStep to post-trim pipeline Migrate FixLegacyResourceDesignerStep out of the ILLink trimmer process and into PostTrimmingPipeline, continuing the work in #10842 to remove custom ILLink steps. The step now runs after ILLink via a thin wrapper (PostTrimmingFixLegacyResourceDesignerStep) that calls ProcessAssemblyDesigner() directly, matching the former ILLink behavior. - Remove all #if ILLINK conditionals from FixLegacyResourceDesignerStep, LinkDesignerBase, and BaseStep - Remove FixLegacyResourceDesignerStep and LinkDesignerBase from the ILLink project (no longer compiled as a trimmer step) - Add UseDesignerAssembly property to PostTrimmingPipeline task - Wire AndroidUseDesignerAssembly through targets to PostTrimmingPipeline - Remove _TrimmerCustomSteps entry for FixLegacyResourceDesignerStep --- .../Microsoft.Android.Sdk.ILLink.csproj | 2 -- .../Linker/External/Linker.Steps/BaseStep.cs | 4 --- .../FixLegacyResourceDesignerStep.cs | 19 +---------- .../MonoDroid.Tuner/LinkDesignerBase.cs | 20 ++--------- ...crosoft.Android.Sdk.TypeMap.LlvmIr.targets | 9 ++--- .../Tasks/PostTrimmingPipeline.cs | 33 +++++++++++++++++++ .../Xamarin.Android.Build.Tasks.csproj | 2 +- 7 files changed, 39 insertions(+), 50 deletions(-) diff --git a/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj b/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj index 26d2067d1fc..5a8cef363e4 100644 --- a/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj +++ b/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj @@ -17,8 +17,6 @@ - - diff --git a/src/Xamarin.Android.Build.Tasks/Linker/External/Linker.Steps/BaseStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/External/Linker.Steps/BaseStep.cs index c1aa1ded94a..a3a5bb11066 100644 --- a/src/Xamarin.Android.Build.Tasks/Linker/External/Linker.Steps/BaseStep.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/External/Linker.Steps/BaseStep.cs @@ -89,11 +89,7 @@ public virtual void LogMessage (string message) public virtual void LogError (int code, string message) { -#if ILLINK - Context.LogMessage (MessageContainer.CreateCustomErrorMessage (message, code, origin: new MessageOrigin ())); -#else // !ILLINK Context.LogError ($"XA{code}", message); -#endif // !ILLINK } } } diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs index 7606f022e7a..7028aea33e0 100644 --- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/FixLegacyResourceDesignerStep.cs @@ -14,18 +14,11 @@ using Mono.Linker.Steps; using Mono.Tuner; -#if ILLINK -using Resources = Microsoft.Android.Sdk.ILLink.Properties.Resources; -#else // !ILLINK using Resources = Xamarin.Android.Tasks.Properties.Resources; -#endif // ILLINK namespace MonoDroid.Tuner { - public class FixLegacyResourceDesignerStep : LinkDesignerBase -#if !ILLINK - , Xamarin.Android.Tasks.IAssemblyModifierPipelineStep -#endif // !ILLINK + public class FixLegacyResourceDesignerStep : LinkDesignerBase, Xamarin.Android.Tasks.IAssemblyModifierPipelineStep { internal const string DesignerAssemblyName = "_Microsoft.Android.Resource.Designer"; internal const string DesignerAssemblyNamespace = "_Microsoft.Android.Resource.Designer"; @@ -36,14 +29,6 @@ public class FixLegacyResourceDesignerStep : LinkDesignerBase Dictionary lookup; Dictionary lookupCaseInsensitive; - protected override void EndProcess () - { - if (designerAssembly != null) { - LogMessage ($" Setting Action on {designerAssembly.Name} to Link."); - Annotations.SetAction (designerAssembly, AssemblyAction.Link); - } - } - protected override void LoadDesigner () { if (designerLoaded) @@ -72,7 +57,6 @@ protected override void LoadDesigner () } } -#if !ILLINK public void ProcessAssembly (AssemblyDefinition assembly, Xamarin.Android.Tasks.StepContext context) { // Only run this step on non-main user Android assemblies @@ -81,7 +65,6 @@ public void ProcessAssembly (AssemblyDefinition assembly, Xamarin.Android.Tasks. context.IsAssemblyModified |= ProcessAssemblyDesigner (assembly); } -#endif // !ILLINK internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly) { diff --git a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs index 5f7f7fc1f74..6e50b10d7d7 100644 --- a/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs +++ b/src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/LinkDesignerBase.cs @@ -4,16 +4,12 @@ using Mono.Linker; using Mono.Linker.Steps; using System; -using System.Linq; using Xamarin.Android.Tasks; using System.Collections.Generic; using System.Globalization; using Mono.Cecil.Cil; using System.Text.RegularExpressions; using Mono.Collections.Generic; -#if ILLINK -using Microsoft.Android.Sdk.ILLink; -#endif namespace MonoDroid.Tuner { @@ -21,26 +17,14 @@ public abstract class LinkDesignerBase : BaseStep { protected IMetadataResolver Cache => Context; - public -#if !ILLINK - override -#endif - void LogMessage (string message) + public override void LogMessage (string message) { Context.LogMessage (message); } - public -#if !ILLINK - override -#endif - void LogError (int code, string error) + public override void LogError (int code, string error) { -#if ILLINK - Context.LogMessage (MessageContainer.CreateCustomErrorMessage (error, code, origin: new MessageOrigin ())); -#else // !ILLINK Context.LogError ($"XA{code}", error); -#endif // !ILLINK } public virtual AssemblyDefinition Resolve (AssemblyNameReference name) 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 9b8870a8295..da747461e70 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 @@ -198,12 +198,6 @@ <_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" Type="MonoDroid.Tuner.FixAbstractMethodsStep" /> - <_TrimmerCustomSteps - Condition=" '$(AndroidUseDesignerAssembly)' == 'true' " - Include="$(_AndroidLinkerCustomStepAssembly)" - BeforeStep="MarkStep" - Type="MonoDroid.Tuner.FixLegacyResourceDesignerStep" - /> <_TrimmerCustomSteps Condition=" '$(_AndroidTypeMapImplementation)' == 'managed' " Include="$(_AndroidLinkerCustomStepAssembly)" @@ -248,7 +242,8 @@ Assemblies="@(_PostTrimmingAssembly)" AddKeepAlives="$(AndroidAddKeepAlives)" AndroidLinkResources="$(AndroidLinkResources)" - Deterministic="$(Deterministic)" /> + Deterministic="$(Deterministic)" + UseDesignerAssembly="$(AndroidUseDesignerAssembly)" /> diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs index 56fb01b6485..645b4fe4489 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs @@ -6,6 +6,7 @@ using Microsoft.Android.Build.Tasks; using Microsoft.Build.Framework; using Mono.Cecil; +using Mono.Linker; using MonoDroid.Tuner; namespace Xamarin.Android.Tasks; @@ -34,6 +35,8 @@ public class PostTrimmingPipeline : AndroidTask public bool Deterministic { get; set; } + public bool UseDesignerAssembly { get; set; } + public override bool RunTask () { using var resolver = new DirectoryAssemblyResolver ( @@ -70,6 +73,15 @@ public override bool RunTask () }, (msg) => Log.LogDebugMessage (msg))); } + if (UseDesignerAssembly) { + // Create an MSBuildLinkContext so FixLegacyResourceDesignerStep can resolve assemblies + // and log messages. The resolver is owned by the outer 'using' block, so we intentionally + // do not dispose this context (LinkContext.Dispose would double-dispose the resolver). + var linkContext = new MSBuildLinkContext (resolver, Log); + var fixLegacyStep = new FixLegacyResourceDesignerStep (); + fixLegacyStep.Initialize (linkContext); + steps.Add (new PostTrimmingFixLegacyResourceDesignerStep (fixLegacyStep)); + } if (AndroidLinkResources) { var allAssemblies = new List (Assemblies.Length); foreach (var item in Assemblies) { @@ -96,3 +108,24 @@ public override bool RunTask () return !Log.HasLoggedErrors; } } + +/// +/// Thin wrapper around for the post-trimming pipeline. +/// Calls directly, matching the +/// behavior of the former ILLink path which processed all assemblies without StepContext flag filtering. +/// Assemblies without a resource designer are skipped internally by ProcessAssemblyDesigner. +/// +class PostTrimmingFixLegacyResourceDesignerStep : IAssemblyModifierPipelineStep +{ + readonly FixLegacyResourceDesignerStep _inner; + + public PostTrimmingFixLegacyResourceDesignerStep (FixLegacyResourceDesignerStep inner) + { + _inner = inner; + } + + public void ProcessAssembly (AssemblyDefinition assembly, StepContext context) + { + context.IsAssemblyModified |= _inner.ProcessAssemblyDesigner (assembly); + } +} 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 a0ed0a47ead..843e4879257 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj @@ -45,7 +45,7 @@ - + From 77de75d8945d558a96f0aad77a47244abe3920e1 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Wed, 1 Apr 2026 16:18:54 -0700 Subject: [PATCH 2/4] [targets] Fix NativeAOT designer assembly trimming In NativeAOT builds, _AddResourceDesignerToPublishFiles ran after _ComputeManagedAssemblyToLink, so the designer assembly was not in ManagedAssemblyToLink. ILLink skipped the designer assembly entirely and did not rewrite its netstandard references. Fix by adding _AddResourceDesignerToPublishFiles to _PrepareLinking's DependsOnTargets, so the designer is passed to ILLink. --- .../targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 da747461e70..3d9d9713433 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 @@ -160,7 +160,7 @@ + DependsOnTargets="GetReferenceAssemblyPaths;_CreatePropertiesCache;_AddResourceDesignerToPublishFiles"> true <_ExtraTrimmerArgs Condition=" '$(_EnableSerializationDiscovery)' != 'false' ">--enable-serialization-discovery $(_ExtraTrimmerArgs) From f0059fd75f820eedb792e67e0376b35c90421b46 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 2 Apr 2026 09:48:30 -0700 Subject: [PATCH 3/4] Fix merge conflict: remove FixAbstractMethodsStep from ILLink csproj FixAbstractMethodsStep was moved from ILLink to the post-trim pipeline on main (7ae24aab1). The merge conflict resolution incorrectly kept the Compile include, but the file now uses types not available in the ILLink project (IAssemblyModifierPipelineStep, StepContext, Properties.Resources). --- .../Microsoft.Android.Sdk.ILLink.csproj | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj b/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj index 5a8cef363e4..6c4368fd7d2 100644 --- a/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj +++ b/src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj @@ -16,7 +16,6 @@ - From 64d4ed1f29be377559c1d4e9c5771e40d7a6a7d2 Mon Sep 17 00:00:00 2001 From: Sven Boemer Date: Thu, 2 Apr 2026 15:21:08 -0700 Subject: [PATCH 4/4] Root designer assembly to prevent ILLink from trimming resource properties FixLegacyResourceDesignerStep now runs in PostTrimmingPipeline (after ILLink) instead of as an ILLink custom step. Previously, the step ran before MarkStep and rewrote library assemblies to reference designer properties, which caused MarkStep to preserve them. Now that the rewriting happens after ILLink, we must root the designer assembly so all resource properties survive trimming. Add TrimmerRootAssembly for the designer in _AddResourceDesignerToPublishFiles. This keeps IsTrimmable=true (action=link) so ILLink still rewrites the netstandard assembly reference, but roots all types so nothing is trimmed. An alternative approach would be to run FixLegacyResourceDesignerStep before ILLink instead of after. That would allow ILLink to trim unused resource properties from the designer (since the rewritten references would already be in place for MarkStep), but it would also process resource references in assemblies that ILLink may later remove entirely. --- .../Android/Xamarin.Android.Resource.Designer.targets | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets index 5c154d9e05c..a8a666ab598 100644 --- a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets +++ b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets @@ -226,6 +226,10 @@ Copyright (C) 2016 Xamarin. All rights reserved. In additon we MUST set the `PostprocessAssembly` metadata to `true` so that the file is processed by the ILLink step. If we do not do this then the reference to `netstandard.dll` is not replaced with `System.Private.CoreLib` and the app crashes. + + We also add a TrimmerRootAssembly entry to prevent ILLink from trimming the designer's + resource properties. FixLegacyResourceDesignerStep now runs after ILLink and needs + all properties to be present when rewriting library assemblies. --> true true + +