Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
<Compile Include="..\Xamarin.Android.Build.Tasks\Linker\External\Linker\BaseMarkHandler.cs" Link="External\BaseMarkHandler.cs" />
<Compile Include="..\Xamarin.Android.Build.Tasks\Linker\MonoDroid.Tuner\AndroidLinkConfiguration.cs" Link="MonoDroid.Tuner\AndroidLinkConfiguration.cs" />
<Compile Include="..\Xamarin.Android.Build.Tasks\Linker\MonoDroid.Tuner\Extensions.cs" Link="MonoDroid.Tuner\Extensions.cs" />
<Compile Include="..\Xamarin.Android.Build.Tasks\Linker\MonoDroid.Tuner\FixLegacyResourceDesignerStep.cs" Link="MonoDroid.Tuner\FixLegacyResourceDesignerStep.cs" />
<Compile Include="..\Xamarin.Android.Build.Tasks\Linker\MonoDroid.Tuner\LinkDesignerBase.cs" Link="MonoDroid.Tuner\LinkDesignerBase.cs" />

<!--Other .NET for Android / Java.Interop files-->
<Compile Include="..\..\external\Java.Interop\src\Java.Interop.Tools.Cecil\Java.Interop.Tools.Cecil\CustomAttributeProviderRocks.cs" Link="Java.Interop\CustomAttributeProviderRocks.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -36,14 +29,6 @@ public class FixLegacyResourceDesignerStep : LinkDesignerBase
Dictionary<string, MethodDefinition> lookup;
Dictionary<string, MethodDefinition> 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)
Expand Down Expand Up @@ -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
Expand All @@ -81,7 +65,6 @@ public void ProcessAssembly (AssemblyDefinition assembly, Xamarin.Android.Tasks.

context.IsAssemblyModified |= ProcessAssemblyDesigner (assembly);
}
#endif // !ILLINK

internal override bool ProcessAssemblyDesigner (AssemblyDefinition assembly)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,43 +4,27 @@
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 {
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,18 +226,43 @@ 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 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.
-->
<Target Name="_AddResourceDesignerToPublishFiles"
Condition=" '$(AndroidUseDesignerAssembly)' == 'True' "
AfterTargets="ComputeResolvedFilesToPublishList"
DependsOnTargets="_SetupDesignerProperties">
<PropertyGroup>
<_DesignerLinkerDescriptor>$(IntermediateOutputPath)_Microsoft.Android.Resource.Designer.xml</_DesignerLinkerDescriptor>
</PropertyGroup>
<WriteLinesToFile
File="$(_DesignerLinkerDescriptor)"
Overwrite="true"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 ⚠️ Performance — The preserve="all" descriptor prevents ILLink from trimming unused resource properties in the designer assembly. In the old ILLink custom step flow, FixLegacyResourceDesignerStep ran before MarkStep, so ILLink could track which designer properties were actually referenced and trim the rest. With the post-trim approach, all properties must survive ILLink intact.

The BuildReleaseArm64XFormsDotNet.MonoVM.apkdesc shows the designer assembly growing from ~19 KB to ~258 KB (~13× increase). For the simple app it's negligible (+400 bytes), but resource-heavy apps will see a larger impact.

The TODO references dotnet/runtime#126518 for switching to TrimmerRootAssembly. However, the _FixRootAssembly target upgrades all TrimmerRootAssembly entries to RootMode="All", so switching wouldn't reduce the preserved surface — it would just simplify the MSBuild XML. Is the size regression considered acceptable for the architectural benefits of removing the ILLink step, or is there a plan to recover trimming of unused designer properties later?

Rule: Don't remove caches without measurement (Postmortem #57)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is accurate, and by design per #11059 (comment).

WriteOnlyWhenDifferent="true"
Lines="&lt;linker&gt;&lt;assembly fullname=&quot;$(_DesignerAssemblyName)&quot;&gt;&lt;type fullname=&quot;*&quot; preserve=&quot;all&quot; /&gt;&lt;/assembly&gt;&lt;/linker&gt;"
/>
<ItemGroup>
<ResolvedFileToPublish Include="$(_GenerateResourceDesignerAssemblyOutput)">
<RelativePath>$(_DesignerAssemblyName).dll</RelativePath>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
<PostprocessAssembly>true</PostprocessAssembly>
<IsTrimmable>true</IsTrimmable>
</ResolvedFileToPublish>
<!-- Preserve all designer types/members via descriptor so ILLink keeps resource
properties. Using a descriptor (not TrimmerRootAssembly) avoids making the
assembly an entry point, which would pull netstandard.dll into the output.
See https://github.com/dotnet/runtime/issues/126518 -->
<TrimmerRootDescriptor Include="$(_DesignerLinkerDescriptor)" />
<FileWrites Include="$(_DesignerLinkerDescriptor)" />
</ItemGroup>
</Target>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@
<Target Name="_PrepareLinking"
Condition=" '$(PublishTrimmed)' == 'true' "
AfterTargets="ComputeResolvedFilesToPublishList"
DependsOnTargets="GetReferenceAssemblyPaths;_CreatePropertiesCache">
DependsOnTargets="GetReferenceAssemblyPaths;_CreatePropertiesCache;_AddResourceDesignerToPublishFiles">
<PropertyGroup>
<TrimmerRemoveSymbols Condition=" '$(AndroidIncludeDebugSymbols)' != 'true' ">true</TrimmerRemoveSymbols>
<_ExtraTrimmerArgs Condition=" '$(_EnableSerializationDiscovery)' != 'false' ">--enable-serialization-discovery $(_ExtraTrimmerArgs)</_ExtraTrimmerArgs>
Expand Down Expand Up @@ -204,12 +204,6 @@
<_TrimmerCustomSteps Include="$(_AndroidLinkerCustomStepAssembly)" Type="Microsoft.Android.Sdk.ILLink.PreserveJavaInterfaces" />
<!-- Custom steps that run after MarkStep -->
<!-- Custom steps that run after CleanStep -->
<_TrimmerCustomSteps
Condition=" '$(AndroidUseDesignerAssembly)' == 'true' "
Include="$(_AndroidLinkerCustomStepAssembly)"
BeforeStep="MarkStep"
Type="MonoDroid.Tuner.FixLegacyResourceDesignerStep"
/>
<_TrimmerCustomSteps
Condition=" '$(_AndroidTypeMapImplementation)' == 'managed' "
Include="$(_AndroidLinkerCustomStepAssembly)"
Expand Down Expand Up @@ -254,7 +248,8 @@
Assemblies="@(_PostTrimmingAssembly)"
AddKeepAlives="$(AndroidAddKeepAlives)"
AndroidLinkResources="$(AndroidLinkResources)"
Deterministic="$(Deterministic)" />
Deterministic="$(Deterministic)"
UseDesignerAssembly="$(AndroidUseDesignerAssembly)" />
</Target>

<!-- Inject _TypeMapKind into the property cache -->
Expand Down
35 changes: 34 additions & 1 deletion src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Microsoft.Android.Build.Tasks;
using Microsoft.Build.Framework;
using Mono.Cecil;
using Mono.Linker;
Comment thread
jonathanpeppers marked this conversation as resolved.
using MonoDroid.Tuner;

namespace Xamarin.Android.Tasks;
Expand All @@ -16,7 +17,7 @@ namespace Xamarin.Android.Tasks;
/// This opens each assembly once (via DirectoryAssemblyResolver with ReadWrite) and
/// runs all registered steps on it, then writes modified assemblies in-place. Currently
/// runs CheckForObsoletePreserveAttributeStep, StripEmbeddedLibrariesStep and
/// (optionally) AddKeepAlivesStep.
/// (optionally) AddKeepAlivesStep and FixLegacyResourceDesignerStep.
///
/// Runs in the inner build after ILLink but before ReadyToRun/crossgen2 compilation,
/// so that R2R images are generated from the already-modified assemblies.
Expand All @@ -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 (
Expand Down Expand Up @@ -100,6 +103,15 @@ public override bool RunTask ()
},
Comment thread
sbomer marked this conversation as resolved.
(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));
}
Comment thread
jonathanpeppers marked this conversation as resolved.

foreach (var (item, assembly) in loadedAssemblies) {
var context = new StepContext (item, item);
Expand All @@ -118,3 +130,24 @@ public override bool RunTask ()
return !Log.HasLoggedErrors;
}
}

/// <summary>
/// Thin wrapper around <see cref="FixLegacyResourceDesignerStep"/> for the post-trimming pipeline.
/// Calls <see cref="FixLegacyResourceDesignerStep.ProcessAssemblyDesigner"/> 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.
/// </summary>
class PostTrimmingFixLegacyResourceDesignerStep : IAssemblyModifierPipelineStep
{
readonly FixLegacyResourceDesignerStep _inner;

public PostTrimmingFixLegacyResourceDesignerStep (FixLegacyResourceDesignerStep inner)
{
_inner = inner;
}

public void ProcessAssembly (AssemblyDefinition assembly, StepContext context)
{
context.IsAssemblyModified |= _inner.ProcessAssemblyDesigner (assembly);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,25 @@
"Size": 3036
},
"classes.dex": {
"Size": 397520
"Size": 400044
},
"lib/arm64-v8a/libassembly-store.so": {
"Size": 3091928
"Size": 3115312
},
"lib/arm64-v8a/libclrjit.so": {
"Size": 3141224
"Size": 3202072
},
"lib/arm64-v8a/libcoreclr.so": {
"Size": 5736320
"Size": 5766640
},
"lib/arm64-v8a/libmonodroid.so": {
"Size": 1375784
"Size": 1365104
},
"lib/arm64-v8a/libmscordaccore.so": {
"Size": 2442416
"Size": 2493552
},
"lib/arm64-v8a/libmscordbi.so": {
"Size": 1894280
"Size": 1902744
},
"lib/arm64-v8a/libSystem.Globalization.Native.so": {
"Size": 71936
Expand All @@ -32,13 +32,13 @@
"Size": 1281696
},
"lib/arm64-v8a/libSystem.Native.so": {
"Size": 105664
"Size": 107904
},
"lib/arm64-v8a/libSystem.Security.Cryptography.Native.Android.so": {
"Size": 165536
},
"lib/arm64-v8a/libxamarin-app.so": {
"Size": 20128
"Size": 20520
},
"META-INF/BNDLTOOL.RSA": {
"Size": 1221
Expand Down Expand Up @@ -74,5 +74,5 @@
"Size": 1904
}
},
"PackageSize": 9176858
"PackageSize": 9258778
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
"Size": 22388
},
"lib/arm64-v8a/lib__Microsoft.Android.Resource.Designer.dll.so": {
"Size": 18288
"Size": 18696
},
"lib/arm64-v8a/lib_Java.Interop.dll.so": {
"Size": 88040
"Size": 88048
},
"lib/arm64-v8a/lib_Mono.Android.dll.so": {
"Size": 118024
"Size": 117928
},
"lib/arm64-v8a/lib_Mono.Android.Runtime.dll.so": {
"Size": 26544
"Size": 26464
},
"lib/arm64-v8a/lib_System.Console.dll.so": {
"Size": 24432
Expand All @@ -35,7 +35,7 @@
"Size": 21632
},
"lib/arm64-v8a/lib_UnnamedProject.dll.so": {
"Size": 20032
"Size": 20144
},
"lib/arm64-v8a/libarc.bin.so": {
"Size": 19176
Expand All @@ -44,7 +44,7 @@
"Size": 36616
},
"lib/arm64-v8a/libmonodroid.so": {
"Size": 1386512
"Size": 1386072
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
"Size": 3124368
Expand All @@ -62,7 +62,7 @@
"Size": 165536
},
"lib/arm64-v8a/libxamarin-app.so": {
"Size": 19840
"Size": 19792
},
"META-INF/BNDLTOOL.RSA": {
"Size": 1221
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
"Size": 3124
},
"classes.dex": {
"Size": 24224
"Size": 25400
},
"lib/arm64-v8a/libUnnamedProject.so": {
"Size": 4968680
"Size": 5056848
},
"META-INF/BNDLTOOL.RSA": {
"Size": 1211
"Size": 1221
},
"META-INF/BNDLTOOL.SF": {
"Size": 1211
Expand Down Expand Up @@ -44,5 +44,5 @@
"Size": 1904
}
},
"PackageSize": 2094050
"PackageSize": 2122722
}
Loading