Move RemoveResourceDesigner trimmer step to post-trim pipeline#10977
Move RemoveResourceDesigner trimmer step to post-trim pipeline#10977
RemoveResourceDesigner trimmer step to post-trim pipeline#10977Conversation
Migrate AddKeepAlivesStep out of the ILLink custom step pipeline into a standalone MSBuild task that runs AfterTargets="ILLink", following the same pattern established by StripEmbeddedLibraries in #10894. Core IL-rewriting logic is extracted into AddKeepAlivesHelper, shared by both the new task (trimmed builds) and the existing pipeline step (non-trimmed builds via LinkAssembliesNoShrink).
Replace DefaultAssemblyResolver with DirectoryAssemblyResolver (ReadWrite=true) to avoid file handle conflicts on Windows. The directory resolver caches all assemblies (both explicit and dependency-resolved), preventing duplicate file opens that caused IOException when the same assembly was opened as both a dependency and a primary target.
Replace standalone AddKeepAlives and StripEmbeddedLibraries MSBuild tasks with a single PostTrimmingPipeline task that opens assemblies once (via DirectoryAssemblyResolver with ReadWrite) and runs both modifications in a single pass. Extract StripEmbeddedLibrariesStep as an IAssemblyModifierPipelineStep for reuse.
…Step pattern Use List<IAssemblyModifierPipelineStep> with StripEmbeddedLibrariesStep and PostTrimmingAddKeepAlivesStep instead of calling helpers directly. Move the IsFrameworkAssembly check into StripEmbeddedLibrariesStep.ProcessAssembly and remove all outer-loop filtering so each step handles its own guards internally.
…line Migrate RemoveResourceDesignerStep from an ILLink custom step to a post-trimming IAssemblyModifierPipelineStep, following the same pattern used for StripEmbeddedLibrariesStep and PostTrimmingAddKeepAlivesStep. - Add PostTrimmingRemoveResourceDesignerStep with self-contained logic from LinkDesignerBase/RemoveResourceDesignerStep, using Action<string> for logging instead of ILLink's Context.LogMessage - Add AndroidLinkResources property to PostTrimmingPipeline task and pre-load all assemblies when enabled (the step needs a two-phase scan) - Remove RemoveResourceDesignerStep and GetAssembliesStep from ILLink custom steps in targets - Remove AndroidLinkConfiguration.cs and RemoveResourceDesignerStep.cs from ILLink csproj (no longer needed there) - Delete GetAssembliesStep.cs (dead code, only served RemoveResourceDesignerStep)
…eDesignerStep The old ILLink RemoveResourceDesignerStep is no longer compiled into Xamarin.Android.Build.Tasks, so the PostTrimming prefix is unnecessary. Also remove AndroidLinkConfiguration.cs from compilation as it was only used by the old ILLink step.
There was a problem hiding this comment.
Pull request overview
Moves the RemoveResourceDesigner logic out of ILLink custom steps and into the post-trimming PostTrimmingPipeline, aligning it with other post-trim assembly modifiers in Xamarin.Android.Build.Tasks.
Changes:
- Adds
AndroidLinkResourcessupport toPostTrimmingPipelineand runsRemoveResourceDesignerSteppost-trim when enabled. - Removes
RemoveResourceDesignerStep/GetAssembliesStepfrom ILLink custom steps wiring inMicrosoft.Android.Sdk.TypeMap.LlvmIr.targets. - Stops compiling/removes ILLink-side sources (
RemoveResourceDesignerSteplink andGetAssembliesStep).
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj | Updates compile includes for linker/tuner steps (and surfaces now-dead code). |
| src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs | Adds AndroidLinkResources option and runs resource-designer removal post-trim. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets | Removes ILLink custom steps and passes AndroidLinkResources into the post-trim task. |
| src/Xamarin.Android.Build.Tasks/Linker/MonoDroid.Tuner/RemoveResourceDesignerStep.cs | Refactors step to run as an IAssemblyModifierPipelineStep with internal logging. |
| src/Microsoft.Android.Sdk.ILLink/Microsoft.Android.Sdk.ILLink.csproj | Stops linking RemoveResourceDesignerStep.cs into the ILLink project. |
| src/Microsoft.Android.Sdk.ILLink/GetAssembliesStep.cs | Deletes the now-dead ILLink step used to collect assemblies. |
| <Compile Include="Linker\MonoDroid.Tuner\StripEmbeddedLibrariesStep.cs" /> | ||
| <Compile Include="Linker\MonoDroid.Tuner\PostTrimmingAddKeepAlivesStep.cs" /> | ||
| <Compile Include="Linker\MonoDroid.Tuner\RemoveResourceDesignerStep.cs" /> | ||
| <Compile Include="Linker\MonoDroid.Tuner\AndroidLinkConfiguration.cs" /> |
There was a problem hiding this comment.
AndroidLinkConfiguration.cs appears to be unused now (no references found in the repository after removing the ILLink step). Keeping it compiled adds dead code (and a lingering Mono.Linker dependency surface) inside Xamarin.Android.Build.Tasks. Consider removing it from the project (and deleting the file) if nothing else consumes it.
| <Compile Include="Linker\MonoDroid.Tuner\AndroidLinkConfiguration.cs" /> |
| designer = new TypeDefinition ("_Linker.Generated", "Resource", TypeAttributes.Public | TypeAttributes.AnsiClass); | ||
| designer.BaseType = new TypeDefinition ("System", "Object", TypeAttributes.Public | TypeAttributes.AnsiClass); |
There was a problem hiding this comment.
In the fallback path where no ResourceDesignerAttribute is found and an unresolved member reference is detected, this creates a new _Linker.Generated.Resource TypeDefinition but never adds it to any module’s Types collection, and sets BaseType to a new TypeDefinition instead of a TypeReference in the same module. As written, the log says the type is being added, but the assembly won’t actually change (and ProcessAssemblyDesigner will still report the assembly as modified). Add the generated type to assembly.MainModule.Types (or the appropriate module) and set BaseType via module.TypeSystem.Object/ImportReference, or avoid returning true unless the assembly is really mutated.
| designer = new TypeDefinition ("_Linker.Generated", "Resource", TypeAttributes.Public | TypeAttributes.AnsiClass); | |
| designer.BaseType = new TypeDefinition ("System", "Object", TypeAttributes.Public | TypeAttributes.AnsiClass); | |
| var module = assembly.MainModule; | |
| designer = new TypeDefinition ("_Linker.Generated", "Resource", TypeAttributes.Public | TypeAttributes.AnsiClass); | |
| designer.BaseType = module.TypeSystem.Object; | |
| module.Types.Add (designer); |
| if (attribute.HasProperties) | ||
| { | ||
| foreach (var p in attribute.Properties) | ||
| { | ||
| if (p.Name == "IsApplication" && (bool)p.Argument.Value == (mainApplication ? mainApplication : (bool)p.Argument.Value)) | ||
| { | ||
| designerFullName = attribute.ConstructorArguments[0].Value.ToString (); | ||
| break; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
The IsApplication property check effectively becomes “match any value” when mainApplication is false ((bool)p.Argument.Value == (mainApplication ? mainApplication : (bool)p.Argument.Value) is always true in that case). That means non-main assemblies can incorrectly match an application designer attribute and then fail to locate the type, pushing the code into the member-ref fallback. Consider simplifying this to compare against mainApplication (or explicitly handle the ‘property missing’ case) so the correct designer is selected deterministically.
| if (attribute.HasProperties) | |
| { | |
| foreach (var p in attribute.Properties) | |
| { | |
| if (p.Name == "IsApplication" && (bool)p.Argument.Value == (mainApplication ? mainApplication : (bool)p.Argument.Value)) | |
| { | |
| designerFullName = attribute.ConstructorArguments[0].Value.ToString (); | |
| break; | |
| } | |
| } | |
| } | |
| bool? isApplication = null; | |
| if (attribute.HasProperties) | |
| { | |
| foreach (var p in attribute.Properties) | |
| { | |
| if (p.Name == "IsApplication") | |
| { | |
| isApplication = (bool)p.Argument.Value; | |
| break; | |
| } | |
| } | |
| } | |
| bool matches; | |
| if (isApplication.HasValue) { | |
| matches = isApplication.Value == mainApplication; | |
| } else { | |
| // When IsApplication is not specified, treat the attribute as matching | |
| // only for the main application assembly. | |
| matches = mainApplication; | |
| } | |
| if (matches) { | |
| designerFullName = attribute.ConstructorArguments[0].Value.ToString (); | |
| } |
| assembly.CustomAttributes.Remove (designerAttribute); | ||
| void FixUpdateIdValuesBody (MethodDefinition method) | ||
| { | ||
| List<Instruction> finalInstructions = new List<Instruction> (); |
There was a problem hiding this comment.
FixUpdateIdValuesBody allocates finalInstructions but never uses it. Removing the unused variable will keep this method clearer and avoid dead-code warnings if analyzers are enabled.
| List<Instruction> finalInstructions = new List<Instruction> (); |
Migrate RemoveResourceDesignerStep from an ILLink custom step to a
post-trimming IAssemblyModifierPipelineStep, following the same pattern
used for StripEmbeddedLibrariesStep and PostTrimmingAddKeepAlivesStep.
from LinkDesignerBase/RemoveResourceDesignerStep, using Action
for logging instead of ILLink's Context.LogMessage
pre-load all assemblies when enabled (the step needs a two-phase scan)
custom steps in targets
from ILLink csproj (no longer needed there)