Skip to content

Skip unchanged trimmable typemap assembly emission - #12599

Open
simonrozsival wants to merge 5 commits into
mainfrom
simonrozsival-incremental-typemap-emission
Open

Skip unchanged trimmable typemap assembly emission#12599
simonrozsival wants to merge 5 commits into
mainfrom
simonrozsival-incremental-typemap-emission

Conversation

@simonrozsival

Copy link
Copy Markdown
Member

Stacked on #12598.

Summary

  • Build the final per-assembly typemap model first and persist a SHA-256 fingerprint for every output.
  • Skip expensive PE emission when the model fingerprint and output DLL are unchanged.
  • Include the generator module identity, runtime version, shared-universe mode, and every emitter-consumed model field in the fingerprint.
  • Fingerprint the root typemap separately from its ordered per-assembly reference list, so it regenerates when assemblies are added or removed but not for unrelated per-assembly content changes.
  • Keep cross-assembly alias peers in the owner assembly model, so changing an alias invalidates the owner even when the owner's source assembly did not change.
  • Preserve the existing default full-detail scanner behavior and byte-identical generated output.

Incremental benchmark

Release scratch harness over TestFixtures.dll, median of 10 batches of 100 runs. Both paths perform assembly scanning and model construction; the incremental path skips unchanged per-assembly and root PE emission.

Path Wall time Allocations
Full emission 424.1 ms 256.7 MiB
Unchanged incremental 249.1 ms 156.8 MiB
Change -41.3% -38.9%

Correctness

  • Standalone Microsoft.Android.Sdk.TrimmableTypeMap.Tests: 779 passed.
  • Added regressions for unchanged-output skipping, byte-identical callback emission, cross-assembly alias owner invalidation, and root reference-list invalidation.
  • Added task coverage that verifies a second run reports skipped _Mono.Android.TypeMap PE emission and preserves the output timestamp.
  • The modified targets file parses as valid XML.

Full-build task/integration validation was unavailable because this worktree has no local Android SDK build (bin/Debug/dotnet/dotnet / bin/Release/dotnet/dotnet). A direct build attempt reached missing bootstrap/local environment prerequisites (including no usable JDK), so no full SDK build was started.

simonrozsival and others added 2 commits August 31, 2026 13:20
Reduce scanner and emitter allocations by caching metadata, avoiding unused MCW method scans, and eliminating intermediate buffers.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Persist versioned model fingerprints so incremental builds only emit typemap PE assemblies whose final model changed, while retaining alias-owner and root-reference invalidation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI lite review requested due to automatic review settings August 31, 2026 12:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🔵 Needs a closer look

Review tier: Lite
Findings: 1 Medium severity

New issues introduced by this change (1)
Severity Finding
Medium severity src/​Xamarin.Android.Build.Tasks/​Tasks/​GenerateTrimmableTypeMap.cs⚠️ Operational implicationsReadTypeMapFingerprints() treats the fingerprints file as…
What changed in this PR

Adds an incremental-build path to trimmable typemap generation by fingerprinting the typemap content model and skipping PE emission when fingerprints and on-disk outputs are unchanged. This fits into the build pipeline by reducing repeated work in GenerateTrimmableTypeMap while preserving byte-identical outputs when generation does occur.

Changes:

  • Introduces per-assembly and root typemap incremental fingerprints (including generator identity + emitter-consumed model fields) and a callback to decide whether to emit each output.
  • Persists typemap fingerprints to disk and wires the file into the MSBuild task + targets so subsequent builds can skip unchanged PE emission.
  • Adds/extends unit + task tests to validate skipping behavior, byte identity, and invalidation scenarios (alias ownership + assembly-set changes).
File Description
tests/​Microsoft.Android.Sdk.TrimmableTypeMap.Tests/​Generator/​TrimmableTypeMapGeneratorTests.cs Adds generator-level tests covering skip/all-skip, byte identity, alias invalidation, and root regeneration on assembly-set changes.
src/​Xamarin.Android.Build.Tasks/​Tests/​Xamarin.Android.Build.Tests/​Tasks/​GenerateTrimmableTypeMapTests.cs Extends task test to verify second run skips PE emission (and preserves output timestamp) when fingerprints are persisted.
src/​Xamarin.Android.Build.Tasks/​Tasks/​GenerateTrimmableTypeMap.cs Persists and consults typemap fingerprints to skip PE emission; updates assembly writing flow to support “skipped but still output” assemblies.
src/​Xamarin.Android.Build.Tasks/​Microsoft.Android.Sdk/​targets/​Microsoft.Android.Sdk.TypeMap.Trimmable.targets Wires the fingerprints file into the task and ensures it’s tracked for cleaning via @(FileWrites).
src/​Microsoft.Android.Sdk.TrimmableTypeMap/​TrimmableTypeMapGenerator.cs Adds an optional callback for incremental emission decisions; computes per-assembly/root fingerprints before PE emission.
src/​Microsoft.Android.Sdk.TrimmableTypeMap/​Generator/​TypeMapAssemblyGenerator.cs Refactors generation to separate model creation/fingerprinting from PE emission.
src/​Microsoft.Android.Sdk.TrimmableTypeMap/​Generator/​MetadataHelper.cs Implements incremental fingerprint computation for per-assembly and root typemap outputs.

Comment thread src/Xamarin.Android.Build.Tasks/Tasks/GenerateTrimmableTypeMap.cs Outdated
Base automatically changed from simonrozsival-optimize-trimmable-typemap to main August 31, 2026 15:40
Persist versioned model fingerprints so incremental builds only emit typemap PE assemblies whose final model changed, while retaining alias-owner and root-reference invalidation.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@jonathanpeppers
jonathanpeppers force-pushed the simonrozsival-incremental-typemap-emission branch from fa94514 to 51e0fdf Compare August 31, 2026 15:40
@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12599

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

❌ Reject

Found 1 error in the incremental-clean integration. The model-first fingerprinting and selective PE emission are otherwise well-structured, with good focused coverage for fingerprint stability, alias ownership, root invalidation, and byte-identical output.

CI is still in progress: 42 checks have passed, with the Java.Interop macOS lane running and the aggregate check queued. No failures are currently reported.

Generated by Android PR Reviewer for #12599 · gpt56 · 155.6 AIC · ⌖ 19.9 AIC · ⊞ 25.7K
Comment /review to run again

Keep the fingerprint cache registered during no-op builds and treat unreadable or malformed cache contents as a regeneration signal.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
@simonrozsival

Copy link
Copy Markdown
Member Author

/review

@github-actions

github-actions Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Android PR Reviewer completed successfully!

Generated by Android PR Reviewer for #12599

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Review summary

Verdict: Needs changes

I found no high-confidence correctness defects in the implementation. The fingerprints cover the emitter-consumed model, root membership invalidation, generator identity, runtime version, and shared-universe mode; skipped assemblies remain represented in the expected output set. I left one inline testing suggestion for the missing-output recovery path.

CI: Azure DevOps build 1575463 is red in Windows MSBuild lanes 1, 6, and 8 and macOS lanes 2, 3, and 10. The Azure CLI could not initialize its required writable profile in this environment, so I could not attribute those failures from the timeline/logs. These failures should be resolved or confirmed unrelated before merge.

Findings: 0 bugs · 0 warnings · 1 suggestion

Generated by Android PR Reviewer for #12599 · gpt56 · 290.9 AIC · ⌖ 9 AIC · ⊞ 25.7K
Comment /review to run again

var secondWriteTime = File.GetLastWriteTimeUtc (typeMapPath);
Assert.AreEqual (firstWriteTime, secondWriteTime,
"Typemap assembly should NOT be rewritten when content hasn't changed.");
Assert.IsTrue (messages.Any (message => message.Message?.Contains ("_Mono.Android.TypeMap: unchanged, skipping emission", StringComparison.Ordinal) == true),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🤖 💡 Testing: Please also cover the persisted-cache recovery path where the fingerprint matches but the output DLL is missing. After the first execution, delete typeMapPath, execute the task again, and assert that the DLL is recreated rather than reported as unchanged. ShouldGenerateTypeMapAssembly explicitly treats a missing output as dirty, and that behavior is important because these fingerprints survive across builds.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants