Fix MauiFont and MauiSplashScreen assets missing on first/incremental builds#34567
Fix MauiFont and MauiSplashScreen assets missing on first/incremental builds#34567jfversluis wants to merge 4 commits intomainfrom
Conversation
ProcessMauiFonts used Inputs/Outputs for incremental builds, which meant the target body was skipped when the stamp file was up-to-date. Unlike ResizetizeImages (which uses LibraryResourceDirectories — a persistent directory reference), ProcessMauiFonts adds individual AndroidAsset items that only exist when the target body executes. When skipped, the items were never added, causing fonts to be missing from the APK. Additionally, ProcessMauiFonts had no explicit ordering relative to _ComputeAndroidAssetsPaths (the Android SDK target that consumes AndroidAsset items). MSBuild AfterTargets/BeforeTargets are not transitive, so the existing AfterTargets on ResizetizeCollectItems did not guarantee execution before _ComputeAndroidAssetsPaths. Fix: - Remove Inputs/Outputs from ProcessMauiFonts (matches ProcessMauiAssets pattern which works reliably). The Copy task already uses SkipUnchangedFiles=true for file-level efficiency. - Add _ComputeAndroidAssetsPaths to ProcessMauiFontsBeforeTargets on Android for explicit ordering. - Remove unused _MauiFontStampFile property and related Touch/FileWrites. Fixes #23268 Related: #33092, #23659 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Structural xUnit tests that verify the MSBuild targets file has the correct configuration to prevent the font copy bug (#23268): - ProcessMauiFonts must not have Inputs/Outputs attributes - Android section must order ProcessMauiFonts before _ComputeAndroidAssetsPaths - ProcessMauiFonts follows same pattern as working ProcessMauiAssets All 4 tests fail when the fix is reverted, pass when applied. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Remove Inputs/Outputs from ProcessMauiSplashScreens target to prevent splash screens from silently disappearing on incremental builds. Same root cause as the font fix (#23268): when MSBuild skips the target body, platform item groups (BundleResource, ContentWithTargetPath, etc.) are never populated. Safe to remove because the custom tasks (GenerateSplashAndroidResources, GenerateSplashAssets, GenerateSplashStoryboard) have built-in file-level incrementality via Resizer.IsUpToDate(). Also rename test class to ResizetizeTargetStructureTests and add 3 new splash screen regression tests (7 total). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34567Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34567" |
There was a problem hiding this comment.
Pull request overview
Fixes intermittent missing MauiFont and MauiSplashScreen assets (notably on first / incremental builds) by adjusting Resizetizer’s MSBuild target ordering on Android and ensuring the targets always execute so they reliably populate platform item groups.
Changes:
- Updated
ProcessMauiFontsto run before Android’s_ComputeAndroidAssetsPathsto guarantee@(AndroidAsset)is populated in time. - Removed
Inputs/Outputsstamp-based incremental skipping fromProcessMauiFontsandProcessMauiSplashScreens(and removed the associated stamp plumbing). - Added unit tests that assert the required MSBuild target structure to prevent regressions.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets | Ensures fonts/splash targets run reliably and in correct order, avoiding skipped target bodies that would omit platform item groups. |
| src/SingleProject/Resizetizer/test/UnitTests/ProcessMauiFontsTargetTests.cs | Adds regression tests validating the targets file structure (no Inputs/Outputs on the relevant targets; Android ordering includes _ComputeAndroidAssetsPaths). |
You can also share your feedback on Copilot code review. Take the survey.
src/SingleProject/Resizetizer/test/UnitTests/ResizetizeTargetStructureTests.cs
Show resolved
Hide resolved
Rename ProcessMauiFontsTargetTests.cs to ResizetizeTargetStructureTests.cs to match the contained class name and reflect its broader scope covering both font and splash screen target structure. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
jfversluis
left a comment
There was a problem hiding this comment.
Addressed: renamed ProcessMauiFontsTargetTests.cs → ResizetizeTargetStructureTests.cs to match the class name. See 6e1efe9.
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #34567 | Remove target-level stamp-based incrementality for ProcessMauiFonts and ProcessMauiSplashScreens, remove related stamp plumbing, and add Android ordering to run fonts before _ComputeAndroidAssetsPaths |
⏳ PENDING (Gate) | src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets, src/SingleProject/Resizetizer/test/UnitTests/ResizetizeTargetStructureTests.cs |
Original PR |
🚦 Gate — Test Verification
Gate Result: ⚠️ SKIPPED
Platform: android
Mode: Full Verification
- Tests FAIL without fix: N/A
- Tests PASS with fix: N/A
No TestCases.HostApp / TestCases.Shared.Tests UI test coverage was added in this PR, so the standard Gate flow from pr-gate.md is not applicable. This PR adds xUnit regression tests for Resizetizer target structure rather than HostApp verification tests.
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | Keep stamp-file plumbing, but remove only target Inputs/Outputs; also add Android _ComputeAndroidAssetsPaths ordering |
✅ PASS | 1 file | Narrowest passing code diff, but keeps extra stamp-file work/plumbing with little remaining value |
| 2 | try-fix (claude-sonnet-4.6) | PR-like fix plus new direct splash-screen BeforeTargets ordering hook on Android |
✅ PASS | 1 file | Passes tests, but appears broader than necessary because current evidence only requires direct ordering for fonts |
| 3 | try-fix (gpt-5.3-codex) | Keep target incrementality and add rehydration targets to repopulate item groups when skipped | 2 files | Interesting design, but it required rewriting the regression tests, so it is not a stronger candidate than the PR | |
| 4 | try-fix (gemini-3-pro-preview) | Same family as attempt 2: remove target Inputs/Outputs and add direct splash-screen ordering hook |
✅ PASS | 1 file | Confirms attempt-2 convergence |
| 5 | try-fix (claude-opus-4.6, round 2) | Split each target into a private incremental core plus a public non-incremental item-population target | ✅ PASS | 1 file | Strong alternative preserving generation incrementality, but substantially more invasive and harder to maintain |
| 6 | try-fix (claude-sonnet-4.6, round 2) | Extract projection into separate unconditional projection targets while keeping public targets as transforms | ✅ PASS | 1 file | Similar benefits to attempt 5, but still much more complex than the PR |
| 7 | try-fix (gpt-5.3-codex, round 3) | Pivot generated outputs into built-in content/copy protocols | ✅ PASS | 1 file | In practice converged toward evaluation/projection changes rather than a clearly superior repository-fit solution |
| 8 | try-fix (claude-sonnet-4.6, round 3) | Evaluation-time ItemGroup transforms for platform projection | ✅ PASS | 1 file | Technically passes tests, but is the most invasive architectural departure and least attractive for servicing |
| PR | PR #34567 | Remove target Inputs/Outputs, remove related stamp plumbing, and add Android _ComputeAndroidAssetsPaths ordering |
✅ PASS (local xUnit regression run) | 2 files | Best balance of clarity, minimality, and codebase fit |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 1 | N/A | Initial independent attempt completed |
| claude-sonnet-4.6 | 1 | N/A | Initial independent attempt completed |
| gpt-5.3-codex | 1 | N/A | Initial independent attempt completed |
| gemini-3-pro-preview | 1 | N/A | Initial independent attempt completed |
| claude-opus-4.6 | 2 | Yes | Keep incrementality in private core target; move population to non-incremental public target |
| claude-sonnet-4.6 | 2 | Yes | Move platform projection out of target bodies into unconditional logic/evaluation-time structure |
| gpt-5.3-codex | 2 | No | NO NEW IDEAS |
| gemini-3-pro-preview | 2 | No | NO NEW IDEAS |
| claude-opus-4.6 | 3 | No | NO NEW IDEAS |
| claude-sonnet-4.6 | 3 | Yes | Evaluation-time ItemGroup transform variant |
| gpt-5.3-codex | 3 | Yes | Built-in content/copy protocol variant |
| gemini-3-pro-preview | 3 | No | NO NEW IDEAS |
Exhausted: Yes — three rounds completed; later ideas converged on increasingly invasive projection/externalization variants rather than cleaner repository-fit fixes.
Selected Fix: PR — it solves the verified regression class, keeps the implementation understandable, removes now-unnecessary stamp plumbing, and avoids the complexity of the stronger-but-heavier incremental-preservation alternatives.
📋 Report — Final Recommendation
✅ Final Recommendation: APPROVE
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | Reviewed PR, linked issues (#23268, #23659, #33092), comments, and changed files |
| Gate | Standard HostApp gate flow did not apply; PR adds xUnit regression tests rather than TestCases HostApp coverage | |
| Try-Fix | ✅ COMPLETE | 8 attempts total, 7 direct-pass candidates without rewriting tests |
| Report | ✅ COMPLETE |
Summary
This PR fixes the right failure mode in Microsoft.Maui.Resizetizer.After.targets: target-level Inputs/Outputs were allowing ProcessMauiFonts and ProcessMauiSplashScreens to be skipped before they repopulated platform item groups. The PR removes that target-level skipping, adds the needed Android ordering for fonts, and adds a focused xUnit regression suite to lock the structure in place.
I ran the PR's regression class locally with dotnet test src/SingleProject/Resizetizer/test/UnitTests/Resizetizer.UnitTests.csproj --filter FullyQualifiedName~ResizetizeTargetStructureTests, and all 7 tests passed.
Root Cause
The bug is not that the underlying copy/generation tasks are always wrong; it's that the outer MSBuild targets can be considered up-to-date and skipped entirely. When that happens, the item-group population inside those targets (AndroidAsset, BundleResource, ContentWithTargetPath, LibraryResourceDirectories, etc.) also gets skipped, leaving later stages with missing assets even though prior outputs exist.
Fix Quality
I explored 8 alternative fixes across 4 models. Several passed, including designs that preserve generation incrementality by splitting targets or externalizing projection logic, but they were materially more complex and less appropriate for a servicing fix. The only simpler variant than the PR kept the stamp-file plumbing solely as diagnostics; I do not think that extra plumbing is worth retaining once the targets no longer depend on stamp-based skipping.
On balance, the PR is the best candidate: it is clear, appropriately scoped, and validated by the added regression tests.
Description
Fixes #23268
Fixes #23659
Fixes #33092
Font assets (
MauiFont) and splash screens (MauiSplashScreen) are intermittently missing from builds — particularly on the first build or when switching configurations. These are P/1 bugs affecting many users across multiple .NET versions and platforms (Android, iOS, Windows).Root Cause Analysis
Two issues in
ProcessMauiFontsandProcessMauiSplashScreenstargets inMicrosoft.Maui.Resizetizer.After.targets:1. Missing Android target ordering (fonts only)
ProcessMauiFontshad no explicit ordering relative to_ComputeAndroidAssetsPaths(the Android SDK target that consumes@(AndroidAsset)). MSBuildAfterTargets/BeforeTargetsare not transitive — the existingAfterTargets="ResizetizeCollectItems"did not guarantee execution before_ComputeAndroidAssetsPaths.2. Incremental build skipping prevents item group population (fonts + splash)
Both
ProcessMauiFontsandProcessMauiSplashScreensusedInputs/Outputswith stamp files for incremental builds. When MSBuild skips the target body, the ItemGroups that register assets with each platform (AndroidAsset, BundleResource, ContentWithTargetPath, etc.) may not be populated — causing fonts/splash screens to silently disappear from the build output.Compare with the working target in the same file:
ProcessMauiAssetsProcessMauiFontsProcessMauiSplashScreensFix
Fonts
_ComputeAndroidAssetsPathsadded toProcessMauiFontsBeforeTargetson AndroidInputs/OutputsfromProcessMauiFonts— matches theProcessMauiAssetspattern_MauiFontStampFileproperty,Touchtask, andFileWritesentry removedSplash Screens
Inputs/OutputsfromProcessMauiSplashScreens— same fix pattern as fonts_MauiSplashStampFileproperty,Touchtask, andFileWritesentry removedGenerateSplashAndroidResources,GenerateSplashAssets,GenerateSplashStoryboard) have built-in file-level incrementality viaResizer.IsUpToDate()Changes
src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets_MauiFontStampFileand_MauiSplashStampFileproperty definitions_ComputeAndroidAssetsPathstoProcessMauiFontsBeforeTargets(Android)Inputs/Outputsfrom bothProcessMauiFontsandProcessMauiSplashScreenstargetssrc/SingleProject/Resizetizer/test/UnitTests/ProcessMauiFontsTargetTests.cs(new)Testing
SkiaSharpAppIconToolsTestsare unrelated