Skip to content

Fix _AfterILLinkAdditionalSteps breaking IlcCompile incrementalism#11509

Open
sbomer wants to merge 3 commits into
mainfrom
dev/sbomer/fix-afterillink-incremental
Open

Fix _AfterILLinkAdditionalSteps breaking IlcCompile incrementalism#11509
sbomer wants to merge 3 commits into
mainfrom
dev/sbomer/fix-afterillink-incremental

Conversation

@sbomer
Copy link
Copy Markdown
Member

@sbomer sbomer commented May 26, 2026

This content was created with assistance from AI.

Summary

_AfterILLinkAdditionalSteps runs AssemblyModifierPipeline which modifies assemblies in-place in linked/. Even when files are unchanged ("Skipped unchanged file"), Cecil opens them with ReadWrite and FileStream.Dispose() flushes the mtime. Since this target runs in the outer project after IlcCompile completes in the inner project, the linked/ timestamps end up newer than native/*.o, causing IlcCompile to re-run on every subsequent build.

Fix

Split _AfterILLinkAdditionalSteps into:

  1. _RunAfterILLinkAdditionalSteps — incremental target that writes to afterlink/ instead of in-place
  2. _AfterILLinkAdditionalSteps — always-run wrapper that updates ResolvedAssemblies, ResolvedUserAssemblies, and ResolvedFrameworkAssemblies itemgroups to point to afterlink/ paths

This follows the established pattern used by ILLink and ILC: write to a separate output location, then update itemgroups (which must happen even when the incremental target is skipped).

Results

Metric Before After
Cold build ~43s ~43s
Incremental no-op publish ~17-21s ~2.5s

Split _AfterILLinkAdditionalSteps into _RunAfterILLinkAdditionalSteps
(incremental, writes to afterlink/) and _AfterILLinkAdditionalSteps
(always runs, updates itemgroups to afterlink/ paths).

Previously, AssemblyModifierPipeline modified assemblies in-place in
linked/, which updated file timestamps even for unchanged files (Cecil
opens with ReadWrite, and FileStream.Dispose flushes the mtime). Since
this target runs in the outer project after IlcCompile completes in the
inner project, the linked/ timestamps ended up newer than native/*.o,
causing IlcCompile to re-run on every subsequent build.

Writing to a separate afterlink/ directory avoids touching linked/ and
preserves IlcCompile's incremental state. Incremental no-op publish
drops from ~17s to ~2.5s.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude:claude-opus-4.6-1m
Copilot AI review requested due to automatic review settings May 26, 2026 20:57
Copy link
Copy Markdown
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

This PR addresses a publish incrementalism regression where _AfterILLinkAdditionalSteps (via AssemblyModifierPipeline) was mutating linked assemblies in-place, updating mtimes and forcing IlcCompile to re-run on subsequent no-op builds. The proposed fix redirects post-ILLink modifications into a separate output directory and adds an always-run wrapper target to retarget downstream itemgroups to those outputs.

Changes:

  • Introduces an afterlink/ intermediate output directory for post-ILLink modified assemblies.
  • Splits the work into an incremental _RunAfterILLinkAdditionalSteps target plus an always-run _AfterILLinkAdditionalSteps wrapper that rewrites Resolved*Assemblies itemgroups.
  • Adds FileWrites tracking for the new output directory.
Show a summary per file
File Description
src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets Splits _AfterILLinkAdditionalSteps and redirects outputs to an afterlink/ directory, then rewrites assembly itemgroups to point to those outputs.

Copilot's findings

  • Files reviewed: 1/1 changed files
  • Comments generated: 2

Comment thread src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
Comment thread src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
@sbomer sbomer self-assigned this May 27, 2026
sbomer and others added 2 commits May 27, 2026 13:14
Verify that _RunAfterILLinkAdditionalSteps is skipped on second build,
the afterlink/ output directory contains assemblies, and the outer
_AfterILLinkAdditionalSteps target always runs to update itemgroups.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude:claude-opus-4.6-1m
The original PR flattened all ResolvedAssemblies into afterlink/ using
%(Filename)%(Extension), which would cause assemblies from different ABIs
to overwrite each other. Use %(DestinationSubPath) instead, matching the
established pattern in _LinkAssembliesNoShrink. This produces per-ABI
subdirectories (e.g. afterlink/arm64-v8a/Foo.dll).

Update test to verify per-ABI subdirectories under afterlink/.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Assisted-by: Claude:claude-opus-4.6-1m
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