Fix XALNS7015: run FixAbstractMethodsStep before R2R in non-trimmed CoreCLR builds#11027
Fix XALNS7015: run FixAbstractMethodsStep before R2R in non-trimmed CoreCLR builds#11027simonrozsival wants to merge 2 commits intodev/sbomer/merge-pipelinesfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Fixes the CoreCLR Release (non-trimmed) PublishReadyToRun=true build failure where FixAbstractMethodsStep tries to rewrite an already-R2R’d assembly and Mono.Cecil throws NotSupportedException for mixed-mode output.
Changes:
- Run
PostTrimmingPipelinebefore crossgen2/ReadyToRun for both trimmed and non-trimmedPublishReadyToRunbuilds, enablingFixAbstractMethodsonly in the non-trimmed path. - Update
PostTrimmingPipelineto optionally includeFixAbstractMethodsStepand mark processed assemblies as Android/user assemblies inStepContext. - Re-enable previously ignored CoreCLR test cases and add a unit regression test for
FixAbstractMethodsbehavior viaPostTrimmingPipeline.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Tasks/LinkerTests.cs | Adds a regression unit test validating PostTrimmingPipeline can inject a missing abstract-method stub. |
| src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest.cs | Removes the CoreCLR non-trimmed ignore workaround now that the pipeline should run pre-R2R. |
| src/Xamarin.Android.Build.Tasks/Tasks/PostTrimmingPipeline.cs | Adds FixAbstractMethods support and updates StepContext flags so FixAbstractMethodsStep can run. |
| src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets | Extends _PostTrimmingPipeline to also run for non-trimmed R2R builds and wires FixAbstractMethods into the task call. |
...droid.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets
Outdated
Show resolved
Hide resolved
...droid.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.TypeMap.LlvmIr.targets
Outdated
Show resolved
Hide resolved
|
Replying to the Copilot review comments — the implementation was reworked in commits 3-4, so all four comments are now outdated. Resolving them below. |
5e65810 to
6469db8
Compare
…builds Move _LinkAssembliesNoShrink from the outer build into the inner (per-RID) build using BeforeTargets="_PrepareForReadyToRunCompilation". This ensures assembly modifications (FixAbstractMethods, AddKeepAlives, FindJavaObjects, etc.) operate on pure IL assemblies BEFORE crossgen2 creates R2R images, preventing Mono.Cecil from attempting to write mixed-mode assemblies. Changes: - Rewrite _LinkAssembliesNoShrink to run in the inner build, consuming @(ResolvedFileToPublish) instead of @(ResolvedAssemblies), writing to an intermediate linked-noshrink/ directory - Properly classify framework vs user assemblies by known names (matching the trimmed path in _AfterILLinkAdditionalSteps) - Update ResolvedFileToPublish so R2R and publish consume modified copies - Simplify _PrepareAssemblies: populate _ResolvedAssemblies unconditionally from @(ResolvedAssemblies) (no longer redirect through intermediate dir) - Extend _CopySidecarXmlToAssemblyPaths to handle both trimmed (linked/) and non-trimmed (android/linked-noshrink/) sidecar XML files - Remove _LinkAssembliesNoShrink from _LinkAssemblies DependsOnTargets - Remove Assert.Ignore workarounds in BuildTest.SimilarAndroidXAssemblyNames and LinkerTests.AndroidAddKeepAlives for CoreCLR non-trimmed Release builds Fixes: #11025 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
6469db8 to
ebcf2bd
Compare
simonrozsival
left a comment
There was a problem hiding this comment.
🤖 AI Review Summary
Verdict:
Found 2 issues: 1 warning, 1 suggestion — no blocking bugs.
⚠️ MSBuild targets: MissingInputs/Outputson_LinkAssembliesNoShrink— the target runs every inner build even when assemblies haven't changed (Xamarin.Android.Common.targets:1439)- 💡 MSBuild targets: Duplicate
FileWritesglob — the wildcard glob$(_LinkAssembliesNoShrinkDir)**already covers the per-file FileWrites on subsequent lines (Xamarin.Android.Common.targets:1501)
👍 What's great about this PR:
- Correct root cause fix: running assembly modifications before R2R in the inner build prevents the Mono.Cecil
NotSupportedExceptionon mixed-mode assemblies - Proper framework/user assembly classification matching the trimmed path (4 known framework assembly names)
HasMonoAndroidReference=trueis correctly scoped to only the root assembly (%(Filename) == $(TargetName)) instead of blanket-set on all DLLs- Clean generalization of
_CopySidecarXmlToAssemblyPathswith$(_SidecarSubDir)to handle both trimmed (linked/) and non-trimmed (android/linked-noshrink/) paths - Good simplification of
_PrepareAssemblies— unconditional population of_ResolvedAssembliesremoves the confusingMonoAndroidIntermediateAssemblyDirredirect - Test cleanup: removing
Assert.Ignoreworkarounds confirms the fix addresses the root cause
Review generated by android-reviewer from review guidelines.
| --> | ||
| <Target Name="_LinkAssembliesNoShrink" | ||
| BeforeTargets="_PrepareForReadyToRunCompilation" | ||
| Condition=" '$(PublishTrimmed)' != 'true' and '$(_ComputeFilesToPublishForRuntimeIdentifiers)' == 'true' "> |
There was a problem hiding this comment.
🤖 Inputs/Outputs, so it runs on every inner build even when assemblies haven't changed. The trimmed counterpart (_AfterILLinkAdditionalSteps) uses Inputs="$(_LinkSemaphore)" / Outputs="$(_AdditionalPostLinkerStepsFlag)" for incrementality.
For the non-trimmed path, consider using Inputs="$(_AndroidBuildPropertiesCache)" or a similar stamp, and Outputs="$(_LinkAssembliesNoShrinkDir)noshrink.flag" (touched at the end). Without this, SaveChangedAssemblyStep will re-stamp assembly timestamps on every build, cascading through _GenerateJavaStubs → _CompileJava → _CompileToDalvik → _BuildApkEmbed → _Sign.
Rule: Incremental builds (Inputs/Outputs)
| <ItemGroup> | ||
| <ResolvedFileToPublish Remove="@(_LinkNoShrinkAllAssemblies)" /> | ||
| <ResolvedFileToPublish Include="@(_LinkNoShrinkAllAssemblies->'$(_LinkAssembliesNoShrinkDir)%(RelativePath)')" /> | ||
| <FileWrites Include="$(_LinkAssembliesNoShrinkDir)**" /> |
There was a problem hiding this comment.
🤖 💡 MSBuild targets — The glob $(_LinkAssembliesNoShrinkDir)** already covers all files in the directory, including the DLLs, .jlo.xml, and .typemap.xml files listed in the subsequent <FileWrites> block (lines 1505-1507). The per-file entries are redundant. Consider keeping only the glob, or only the per-file entries (which are more explicit and don't risk over-matching).
Rule: Don't duplicate item group transforms
…ileWrites - Add Inputs/Outputs to _LinkAssembliesNoShrink using @(IntermediateAssembly) and $(_AndroidBuildPropertiesCache) as inputs, and a stamp file as output. This prevents SaveChangedAssemblyStep from re-stamping assembly timestamps on every build, which would cascade through downstream targets. - Touch stamp file after successful completion. - Remove duplicate FileWrites entries — the glob $(_LinkAssembliesNoShrinkDir)** already covers all files in the directory. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
/azp run Xamarin.Android-PR |
|
No pipelines are associated with this pull request. |
Summary
Fixes #11025
Rebased on top of #10891 + #11058 (branch:
dev/sbomer/merge-pipelines).In non-trimmed CoreCLR Release builds,
PublishReadyToRun=truecauses the inner build to compile IL assemblies to R2R images (via crossgen2) before_LinkAssembliesNoShrinkruns. R2R assemblies have the ILONLY PE flag cleared, so whenFixAbstractMethodsStepdetects a missing abstract method and marks the assembly modified,SaveChangedAssemblyStepcallsassembly.Write()via Mono.Cecil, which throws:Changes
Fix: move
_LinkAssembliesNoShrinkto run before R2R compilation in the inner buildThe previous
_LinkAssembliesNoShrinktarget ran in the outer build using@(ResolvedAssemblies)and copied modified assemblies to an intermediate directory. This happened too late — the inner build had already produced R2R images by that point.The fix restructures
_LinkAssembliesNoShrinkto run inside the inner build (BeforeTargets="_PrepareForReadyToRunCompilation"):Xamarin.Android.Common.targets: Rewrite_LinkAssembliesNoShrinkto run in the inner build when trimming is disabled. It operates on@(ResolvedFileToPublish)(available in the inner build) instead of@(ResolvedAssemblies)(outer build only). Assemblies are copied to an intermediate directory (linked-noshrink/) rather than modified in-place, sinceResolvedFileToPublishitems may point to shared locations (NuGet cache, runtime packs). After the task runs,ResolvedFileToPublishis updated so R2R and publish consume the modified copies._CopySidecarXmlToAssemblyPathsto handle both trimmed (linked/) and non-trimmed (android/linked-noshrink/) sidecar XML files using a$(_SidecarSubDir)property.Microsoft.Android.Sdk.AssemblyResolution.targets: In_PrepareAssemblies, populate_ResolvedAssembliesetc. unconditionally from@(ResolvedAssemblies)(no longer redirecting through the intermediate directory for non-trimmed builds).Tests
Assert.Ignoreworkarounds inBuildTest.SimilarAndroidXAssemblyNamesandLinkerTests.AndroidAddKeepAlivesfor the CoreCLR Release non-trimmed case — these scenarios now pass.Depends on
_AfterILLinkAdditionalSteps(trimmed path) to the inner build