Skip to content

Move RemoveResourceDesigner trimmer step to post-trim pipeline#10977

Open
sbomer wants to merge 8 commits intomainfrom
dev/sbomer/remove-resource-designer
Open

Move RemoveResourceDesigner trimmer step to post-trim pipeline#10977
sbomer wants to merge 8 commits intomainfrom
dev/sbomer/remove-resource-designer

Conversation

@sbomer
Copy link
Member

@sbomer sbomer commented Mar 19, 2026

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
    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)

sbomer added 7 commits March 16, 2026 11:10
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.
@sbomer sbomer self-assigned this Mar 23, 2026
@sbomer sbomer marked this pull request as ready for review March 24, 2026 22:11
Copilot AI review requested due to automatic review settings March 24, 2026 22:11
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

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 AndroidLinkResources support to PostTrimmingPipeline and runs RemoveResourceDesignerStep post-trim when enabled.
  • Removes RemoveResourceDesignerStep/GetAssembliesStep from ILLink custom steps wiring in Microsoft.Android.Sdk.TypeMap.LlvmIr.targets.
  • Stops compiling/removes ILLink-side sources (RemoveResourceDesignerStep link and GetAssembliesStep).

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" />
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
<Compile Include="Linker\MonoDroid.Tuner\AndroidLinkConfiguration.cs" />

Copilot uses AI. Check for mistakes.
Comment on lines +146 to +147
designer = new TypeDefinition ("_Linker.Generated", "Resource", TypeAttributes.Public | TypeAttributes.AnsiClass);
designer.BaseType = new TypeDefinition ("System", "Object", TypeAttributes.Public | TypeAttributes.AnsiClass);
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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);

Copilot uses AI. Check for mistakes.
Comment on lines +107 to 117
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;
}
}
}
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
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 ();
}

Copilot uses AI. Check for mistakes.
assembly.CustomAttributes.Remove (designerAttribute);
void FixUpdateIdValuesBody (MethodDefinition method)
{
List<Instruction> finalInstructions = new List<Instruction> ();
Copy link

Copilot AI Mar 24, 2026

Choose a reason for hiding this comment

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

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.

Suggested change
List<Instruction> finalInstructions = new List<Instruction> ();

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants