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 b0fc296935d..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,8 +16,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/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Resource.Designer.targets index 5c154d9e05c..e19d986acda 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,11 @@ 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. + + 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 @@ -167,7 +168,7 @@ + DependsOnTargets="GetReferenceAssemblyPaths;_CreatePropertiesCache;_AddResourceDesignerToPublishFiles"> true <_ExtraTrimmerArgs Condition=" '$(_EnableSerializationDiscovery)' != 'false' ">--enable-serialization-discovery $(_ExtraTrimmerArgs) @@ -204,12 +205,6 @@ <_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" Type="Microsoft.Android.Sdk.ILLink.PreserveJavaInterfaces" /> - <_TrimmerCustomSteps - Condition=" '$(AndroidUseDesignerAssembly)' == 'true' " - Include="$(_AndroidLinkerCustomStepAssembly)" - BeforeStep="MarkStep" - Type="MonoDroid.Tuner.FixLegacyResourceDesignerStep" - /> <_TrimmerCustomSteps Condition=" '$(_AndroidTypeMapImplementation)' == 'managed' " Include="$(_AndroidLinkerCustomStepAssembly)" @@ -229,6 +224,25 @@ + + + + <_PreTrimmingAssembly Include="@(ResolvedFileToPublish)" Condition=" '%(Extension)' == '.dll' and '%(ResolvedFileToPublish.PostprocessAssembly)' == 'true' " /> + + + + +/// 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). +/// +public class PreTrimmingFixLegacyDesigner : AndroidTask +{ + public override string TaskPrefix => "PTD"; + + [Required] + public ITaskItem [] Assemblies { get; set; } = []; + + [Required] + public string TargetName { get; set; } = ""; + + public bool Deterministic { get; set; } + + public override bool RunTask () + { + 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); + + 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)) { + Log.LogDebugMessage ($" Writing modified assembly: {item.ItemSpec}"); + assembly.Write (item.ItemSpec, new WriterParameters { + WriteSymbols = assembly.MainModule.HasSymbols, + DeterministicMvid = Deterministic, + }); + } + } + + return !Log.HasLoggedErrors; + } +} 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 c58aa29aa6e..4070a690f88 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 @@ - + diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 04e52ee9839..8a8194f3cfe 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -1420,10 +1420,9 @@ private void OnPaintSurface(object sender, SKPaintSurfaceEventArgs e) using (var b = CreateApkBuilder (Path.Combine ("temp", TestName, app.ProjectName))) { b.BuildLogFile = "build1.log"; b.ThrowOnBuildFailure = false; - // TODO: fix for NativeAOT - if (!addResource && runtime != AndroidRuntime.NativeAOT) { + if (!addResource) { Assert.IsFalse (b.Build (app, doNotCleanupOnUpdate: true), $"Build of {app.ProjectName} should have failed."); - Assert.IsTrue (b.LastBuildOutput.ContainsText (isRelease ? "IL8000" : "XA8000")); + Assert.IsTrue (b.LastBuildOutput.ContainsText ("XA8000")); Assert.IsTrue (b.LastBuildOutput.ContainsText ("@styleable/SKCanvasView"), "Expected '@styleable/SKCanvasView' in build output."); Assert.IsTrue (b.LastBuildOutput.ContainsText ("@styleable/SKCanvasView_ignorePixelScaling"), "Expected '@styleable/SKCanvasView_ignorePixelScaling' in build output."); return;