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 7105bf0286c..1a6bc1166a8 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
@@ -227,15 +227,10 @@ Copyright (C) 2016 Xamarin. All rights reserved.
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 use a TrimmerRootDescriptor (not TrimmerRootAssembly) to prevent ILLink from
- trimming the designer's resource properties. FixLegacyResourceDesignerStep runs
- after ILLink and needs all properties present when rewriting library assemblies.
- A descriptor (-x) preserves types without making the assembly an entry point,
- which avoids pulling netstandard.dll into the output.
- See https://github.com/dotnet/runtime/issues/126518
-
- TODO: Once dotnet/runtime#126518 is fixed and flows to dotnet/android,
- simplify this to use TrimmerRootAssembly instead of the XML descriptor.
+ No TrimmerRootDescriptor is needed: PreTrimmingFixLegacyDesigner rewrites library
+ assemblies *before* ILLink, replacing designer field loads with calls to the designer
+ assembly's property getters. ILLink then naturally preserves only the referenced
+ properties through its mark step, allowing everything else to be trimmed.
-->
+
<_RemoveRegisterFlag>$(MonoAndroidIntermediateAssemblyDir)shrunk\shrunk.flag
@@ -223,6 +224,61 @@
+
+
+
+ <_PreTrimmingAssembly Include="@(ResolvedFileToPublish)" Condition=" '%(Extension)' == '.dll' and '%(ResolvedFileToPublish.PostprocessAssembly)' == 'true' " />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_PreTrimmingSwappableItem Include="@(ResolvedFileToPublish)"
+ Condition=" '%(Extension)' == '.dll' and Exists('$(IntermediateOutputPath)prelink/%(Filename)%(Extension)') " />
+
+
+
+
+
+/// Runs on assemblies that are about to be
+/// trimmed by ILLink. This rewrites library assemblies so their resource field accesses
+/// (ldsfld) become calls to the designer assembly's property getters.
+///
+/// Running this *before* ILLink means the trimmer sees the rewritten IL and can freely
+/// trim unused designer types/fields. This avoids the need to root the entire designer
+/// assembly during trimming (which causes an APK size regression).
+///
+/// Modified assemblies are written to rather than in-place,
+/// to avoid mutating files in the shared NuGet cache or shared intermediate output paths.
+///
+public class PreTrimmingFixLegacyDesigner : AndroidTask
+{
+ public override string TaskPrefix => "PTD";
+
+ [Required]
+ public ITaskItem [] Assemblies { get; set; } = [];
+
+ [Required]
+ public string TargetName { get; set; } = "";
+
+ [Required]
+ public string OutputDirectory { get; set; } = "";
+
+ public bool Deterministic { get; set; }
+
+ [Output]
+ public ITaskItem []? ModifiedAssemblies { get; set; }
+
+ public override bool RunTask ()
+ {
+ Directory.CreateDirectory (OutputDirectory);
+
+ using var resolver = new DirectoryAssemblyResolver (
+ this.CreateTaskLogger (), loadDebugSymbols: true);
+
+ foreach (var assembly in Assemblies) {
+ var dir = Path.GetFullPath (Path.GetDirectoryName (assembly.ItemSpec) ?? "");
+ if (!resolver.SearchDirectories.Contains (dir)) {
+ resolver.SearchDirectories.Add (dir);
+ }
+ }
+
+ var linkContext = new MSBuildLinkContext (resolver, Log);
+ var fixLegacyStep = new FixLegacyResourceDesignerStep ();
+ fixLegacyStep.Initialize (linkContext);
+
+ var modified = new List ();
+
+ foreach (var item in Assemblies) {
+ // Match the filtering in FixLegacyResourceDesignerStep.ProcessAssembly:
+ // skip the main assembly and framework/BCL assemblies.
+ if (Path.GetFileNameWithoutExtension (item.ItemSpec) == TargetName) {
+ continue;
+ }
+ if (MonoAndroidHelper.IsFrameworkAssembly (item)) {
+ continue;
+ }
+
+ var assembly = resolver.GetAssembly (item.ItemSpec);
+ if (fixLegacyStep.ProcessAssemblyDesigner (assembly)) {
+ var outputPath = Path.Combine (OutputDirectory, Path.GetFileName (item.ItemSpec));
+ Log.LogDebugMessage ($" Writing modified assembly: {outputPath}");
+ assembly.Write (outputPath, new WriterParameters {
+ WriteSymbols = assembly.MainModule.HasSymbols,
+ DeterministicMvid = Deterministic,
+ });
+
+ var outputItem = new TaskItem (outputPath);
+ item.CopyMetadataTo (outputItem);
+ outputItem.SetMetadata ("OriginalPath", item.ItemSpec);
+ modified.Add (outputItem);
+ }
+ }
+
+ ModifiedAssemblies = modified.ToArray ();
+
+ return !Log.HasLoggedErrors;
+ }
+}