Fix missing font and splash outputs after incremental builds - #35962
Fix missing font and splash outputs after incremental builds#35962jfversluis wants to merge 5 commits into
Conversation
Track generated font and splash outputs alongside their stamp files so missing intermediates invalidate the narrow Resizetizer targets instead of producing packages without resources. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Write font and splash output manifests normally instead of combining WriteOnlyWhenDifferent with an unconditional Touch. The manifests are declared target outputs, so their timestamps should advance when the target runs. 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 -- 35962Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 35962" |
|
/review -b feature/enhanced-reviewer -p android |
|
@jfversluis wanted to test this, but I get: ... so I can't really tell if this works or not. This is #35584. I have tested using |
|
@rokmeglicbit Can you try the workaround here to get past it and see if it fixes the issue we're talking about? #35584 (comment) |
|
@jfversluis looks good on our side. Thank you. |
|
@rokmeglicbit thank you for looking at this! |
kubaflo
left a comment
There was a problem hiding this comment.
Can you please resolve conflicts?
Stop creating separate font and splash stamp files now that the generated output manifests drive incremental checks, add a splash DependsOnTargets extension point, and verify no-op builds skip font and splash targets. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Conflict update: retargeted this bug-fix PR to |
Use a plain multi-target build in the Resizetizer regression test, covering the font and splash intermediate outputs for each built platform instead of Android packaging only. Also make font and splash output manifest writes defensive and consistent with the image output manifest pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
Pushed follow-up commit
|
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
Exclude the generated iOS MauiInfo.plist from the copied-font glob so it is only tracked through the explicit plist output item. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@jfversluis — new AI review results are available based on this last commit:
cd570fc. To request a fresh review after new comments or commits, comment/review rerun.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ⚠️ INCONCLUSIVE
Platform: ANDROID
⚠️ verify-tests-fail.ps1exited before writing a verification report. Diagnostics below.
Exit code: 3
Likely cause:
- Test detection failed — no runnable tests were found in the PR diff.
Gate output log (last 60 lines)
📁 Output directory: CustomAgentLogsTmp/PRState/35962/PRAgent/gate/verify-tests-fail
🔍 Detecting base branch and merge point...
No PR detected, scanning remote branches for closest base...
✅ Base branch: main (via closest-merge-base)
✅ Merge base commit: 4567a055
(1 commits ahead of main)
╔═══════════════════════════════════════════════════════════╗
║ FULL VERIFICATION MODE ║
╠═══════════════════════════════════════════════════════════╣
║ Fix files detected - will verify: ║
║ 1. Tests FAIL without fix ║
║ 2. Tests PASS with fix ║
╚═══════════════════════════════════════════════════════════╝
✅ Fix files (1):
- src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets
🔍 Auto-detecting test filter from changed test files...
⚠️ No tests detected in this PR.
Searched for: UI tests, unit tests, XAML tests, device tests
Consider adding tests via write-tests-agent.
📋 Pre-Flight — Context & Validation
Issue: #33092 - Resizetizer generated font/splash outputs can remain missing after stale stamp-based incremental skip
PR: #35962 - Resizetizer font/splash incremental output tracking
Platforms Affected: android primarily; build-time Resizetizer target logic also affects iOS, MacCatalyst, Windows, Tizen where applicable
Files Changed: 1 implementation, 1 test
Key Findings
- PR replaces
mauifont.stamp/mauisplash.stampwith.outputsmanifests so MSBuild tracks generated font and splash files as incremental outputs. - This mirrors the existing
mauiimage.outputspattern and should rerun targets when generated output folders are deleted but the target metadata remains. - GitHub PR metadata, linked issue, comments, reviews, and required checks could not be fetched because
ghis unauthenticated; the supplied gate result is INCONCLUSIVE and is not treated as a failing fix. - The added integration test validates regenerated intermediate outputs and no-op target skips, but not final Android package contents.
Code Review Summary
Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 1 | Suggestions: 0
Key code review findings:
⚠️ src/TestUtils/src/Microsoft.Maui.IntegrationTests/ResizetizerTests.cs: automated coverage checks regenerated intermediate files, not packaged Android APK asset contents.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #35962 | Replace font/splash stamp files with output manifests and include previously generated files in target Outputs |
Microsoft.Maui.Resizetizer.After.targets, ResizetizerTests.cs |
Original PR; technically sound by local review |
🔬 Code Review — Deep Analysis
Code Review — PR #35962
Independent Assessment
What this changes: Replaces stamp-file-based MSBuild incremental tracking for ProcessMauiFonts and ProcessMauiSplashScreens with manifest-file-based tracking. Each target writes absolute paths of generated outputs to a .outputs manifest and reads that manifest before incremental checks so missing intermediate outputs make the target rerun.
Inferred motivation: A stamp file only proves the target ran. If generated font or splash output folders are deleted while the stamp remains, MSBuild can skip regeneration and later packaging can miss assets.
Reconciliation with PR Narrative
Author claims: Unavailable through GitHub CLI because gh is unauthenticated in this environment. Local squashed branch and diff indicate a Resizetizer incremental build fix with integration coverage.
Agreement/disagreement: Local code evidence supports the inferred fix strategy. PR metadata, comments, and CI could not be fetched.
Prior Review Reconciliation
GitHub review/comment surfaces could not be queried because gh is unauthenticated. No prior ❌ Error findings could be verified locally.
Blast Radius Assessment
- Runs for all instances: Yes — build-time font and splash targets run for MAUI projects when processing is enabled.
- Startup impact: No runtime startup path; this is MSBuild target logic.
- Static/shared state: No; state is per-intermediate-output manifest files.
CI Status
- Required-check result: Undetermined.
gh pr checks/PR metadata cannot run without GitHub CLI authentication, and the provided gate result is INCONCLUSIVE. - Classification: Undetermined.
- Action taken: Confidence capped at low; do not treat gate as a failing fix.
Findings
⚠️ Warning — Automated test checks regenerated intermediate outputs, not packaged APK contents
The test verifies obj/.../resizetizer/f fonts and obj/.../resizetizer/sp splash outputs are regenerated and targets no-op afterward. It does not verify the Android package contains the regenerated assets, while the reported impact appears packaging-visible.
Failure-Mode Probing
- Manifest exists but lists deleted output: target should rerun because the listed output is missing.
- Manifest deleted: target should rerun because the manifest output itself is missing.
- First build: manifest missing, target runs and creates it.
- MacCatalyst without splash assets: empty manifest is sufficient for no-op skip.
Verdict: NEEDS_DISCUSSION
Confidence: low
Summary: The PR approach is technically sound and follows the existing mauiimage.outputs pattern, but CI and PR discussion are unavailable locally. The main review caveat is that the test validates intermediate regeneration rather than final packaged Android assets.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix | In-directory sentinel files under resizetizer/f and resizetizer/sp; font Outputs also include source-derived copied font paths |
2 files | Environment blocked targeted integration test: missing .dotnet/packs/Microsoft.Maui.Sdk. Less robust than PR for partial splash deletion. See ../try-fix-1/content.md. |
|
| 2 | try-fix | Static MSBuild font Outputs plus pre-target splash stamp invalidation when splash output directory is empty/missing |
2 files | Same environment blocker. Less complete cross-platform tracking than PR; iOS font plist and partial splash deletion remain risks. See ../try-fix-2/content.md. |
|
| 3 | try-fix | Preserve original mauifont.stamp/mauisplash.stamp; validate output dirs and delete stale stamps before incremental checks |
2 files | Same environment blocker. Simpler but only detects missing/empty dirs, not partial output deletion. See ../try-fix-3/content.md. |
|
| PR | PR #35962 | Manifest output files (mauifont.outputs, mauisplash.outputs) read before incremental checks and included in target Outputs |
2 files | Original PR; strongest approach by code review because it tracks actual generated outputs rather than directory/sentinel heuristics. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| maui-expert-reviewer | 2 | No | NO NEW IDEAS: manifest approach is the correct pragmatic solution because font/splash generated output paths are not fully statically derivable before the tasks run. Sentinel/static/stamp-validation alternatives are subsets or have known blind spots. |
Test Environment
All candidate test runs used:
pwsh .github/skills/run-integration-tests/scripts/Run-IntegrationTests.ps1 -TestFilter "FullyQualifiedName~BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing" -SkipBuild -SkipInstallEach run reached the integration-test runner and stopped at the same environment blocker:
ERROR: Microsoft.Maui.Sdk packs not found at: /home/vsts/work/1/s/.dotnet/packs/Microsoft.Maui.Sdk
Baseline establishment was also blocked because the shared worktree already had unrelated dirty files outside the PR target/test files. Per autonomous-execution instructions, the blocked baseline phase was recorded and candidate exploration continued without modifying unrelated files.
Exhausted: Yes
Selected Fix: PR #35962 — The PR's manifest-readback approach is not empirically verified in this environment, but it is demonstrably more robust on code merits than the alternatives generated here because it tracks actual generated outputs for both fonts and splash screens. No passing alternative was found, and no candidate was demonstrably better than the PR fix.
📝 Recommended PR Title & Description
Assessment: ✏️ Recommend updating — the description is accurate and complete, but the title should name the Resizetizer component.
Recommended title
Resizetizer: Fix missing font and splash outputs after incremental builds
Recommended description
### Description of Change
Fixes an incremental Resizetizer invalidation gap where `ProcessMauiFonts` and `ProcessMauiSplashScreens` could be skipped even when their generated intermediate outputs were missing. This could produce Android packages without `MauiFont` assets and splash screen rasters after stale/incomplete incremental build state.
The fix tracks generated font and splash outputs in obj-scoped `.outputs` manifests, reads them before the MSBuild incremental check, and includes those generated files in target `Outputs`. If a generated font or splash file disappears after a previous successful build, only the relevant font/splash target reruns. Separate font/splash stamp files are no longer generated.
### Issues Fixed
Fixes #33092
### Related Issues / Scope
| Issue | Relationship |
| --- | --- |
| #25540 | Related Android evidence: intermittent fonts missing from APK/runtime after builds; also reports splash missing when fonts are missing. |
| #28502 | Recent discussion/testing thread. This PR may help only if the repro is actual packaged font/splash omission; it is not intended to fix Windows FontManager fallback/noisy missing-font logging. |
| #26889 | Related custom-font symptoms, but appears broader/naming/Windows/Blazor-specific; linked for awareness, not claimed fixed. |
| #24059 | EmbeddedResource/cache font warning path; distinct from `MauiFont` package omission and not claimed fixed. |
| #21771, #19804 | MAUI library/NuGet asset propagation issues; distinct from stale app Resizetizer intermediates and not claimed fixed. |
| #35584 | Artifact-testing blocker encountered by one tester, unrelated to this fix. |
### Artifact Testing
@danies8 @rokmeglicbit if your recent #28502/#33092 repro involves actual missing packaged fonts/splash resources rather than only Windows fallback logging, please try this PR's artifacts and let us know whether it resolves your issue. The validation here shows Android APK package contents are restored after the stale-incremental state described in #33092.
Requesting review from @jonathanpeppers.
### Validation
- Temporarily reverted this fix and reproduced the Android package omission 3/3 times by keeping the old pre-fix `mauifont.stamp`/`mauisplash.stamp` target outputs newer while deleting generated `obj\...\resizetizer\f` and `obj\...\resizetizer\sp` outputs, then rebuilding an Android APK.
- Reapplied the fix and verified the same stale-output Android rebuild passed 3/3 times.
- After review updates, validated the current target logic by deleting generated font/splash output folders after a successful build while preserving the new output manifests; the Android APK still contained fonts and splash rasters, and a following no-op build skipped both font/splash targets.
- Confirmed APK contents include:
- `assets/OpenSans-Regular.ttf`
- `assets/OpenSans-Semibold.ttf`
- `res/drawable-*-v4/splash.png`
- Built `Microsoft.Maui.IntegrationTests.csproj` with the existing unrelated `CS9336` warning suppressed: `dotnet build .\src\TestUtils\src\Microsoft.Maui.IntegrationTests\Microsoft.Maui.IntegrationTests.csproj -v:minimal -p:NoWarn=CS9336`.
🏁 Report — Final Recommendation
Comparative Analysis — PR #35962
Candidate ranking
| Rank | Candidate | Regression result | Assessment |
|---|---|---|---|
| 1 | pr |
Best code-level fix. Tracks actual generated font and splash outputs using obj-scoped manifests read before MSBuild incremental checks. Handles deleted output manifests, deleted output directories, and partial deletion of previously generated files. | |
| 2 | pr-plus-reviewer |
Equivalent to pr; the expert reviewer produced no actionable findings, so no sandbox changes were applied. Ranked immediately after pr only because it adds no improvement over the submitted fix. |
|
| 3 | try-fix-1 |
Uses in-directory sentinel files plus static font outputs. Simpler, but splash tracking is sentinel-granularity and can miss partial splash output deletion when the sentinel remains. | |
| 4 | try-fix-2 |
Uses static font outputs plus splash stamp invalidation for missing/empty splash directory. Less complete than the PR because iOS font plist output is not included in static font Outputs, and partial splash deletion can be missed when the directory remains non-empty. |
|
| 5 | try-fix-3 |
Preserves stamp files and deletes stale stamps only when generated directories are missing or empty. This catches total loss but not partial deletion when at least one font or splash output remains. |
Regression-test rule application
No candidate had a passing regression result in this environment. The PR gate was explicitly inconclusive, and all try-fix candidate runs were blocked by the same missing local MAUI SDK/workload setup (.dotnet/packs/Microsoft.Maui.Sdk). None of these results is treated as a failing regression test, so ranking is based on code merits and known failure-mode coverage.
Key comparison
The PR's manifest-readback approach is the only candidate that records the actual files produced by the previous successful target execution and feeds those files back into MSBuild Outputs before the next incremental decision. That directly models the failure mode: stale incremental state plus missing generated intermediates.
The try-fix alternatives rely on sentinels, static transforms, or directory emptiness checks. Those approaches can detect broad deletion scenarios, but they do not reliably detect partial output loss across the font/splash output sets. try-fix-2 also narrows font tracking too much for non-Android outputs such as the generated iOS font plist.
Winning candidate
Winner: pr
The raw PR fix is the winning candidate because it is the most complete and robust implementation on code merits, and no candidate produced a passing test result that would outweigh that assessment. pr-plus-reviewer is equivalent because the expert reviewer returned no actionable findings.
🧭 Next Steps — review latest findings
No alternative fix was selected for this run. Review the session findings and CI results before merging.
Closing in favor of #33919, which we're using as the single consolidation point for the Resizetizer font/splash incremental-build fixes. Thanks @jfversluis for the outputs-manifest approach here, and @jonathanpeppers for the thorough review — that work isn't lost. The two PRs fixed orthogonal failure modes in the same targets file:
Rather than merge-conflict two overlapping rewrites of the same regions, I'm porting this PR's outputs-manifest mechanism into #33919, including the review refinements from here:
Full credit for the approach stays attributed to @jfversluis. Follow along in #33919. |
Ports the outputs-manifest incremental invalidation from #35962 (now closed) into this PR so deleting generated font/splash intermediates re-triggers ProcessMauiFonts/ProcessMauiSplashScreens (fixes #33092), complementing the first-build registration fix for #23268. - Replace mauifont.stamp/mauisplash.stamp with mauifont.outputs/mauisplash.outputs manifests, read back via _ReadMauiFontOutputs/_ReadMauiSplashOutputs and fed into each target's Outputs, so a missing generated file invalidates only that target. - Drop the font/splash stamp files entirely (per jonathanpeppers review on #35962). - Add ProcessMauiSplashScreensDependsOnTargets. - De-duplicate fonts by intermediate filename before CreatePartialInfoPlistTask to avoid duplicate UIAppFonts entries (addresses open review suggestion on #33919). - Port the all-platform BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing regression test (macOS-gated; adds iOS/MacCatalyst coverage). - Update resizetizer.instructions.md for the outputs-manifest mechanism. Co-authored-by: Gerald Versluis <939291+jfversluis@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tputs are deleted (#33919) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change The Resizetizer copies and registers `MauiFont` / `MauiSplashScreen` assets during the build. Two incremental-build gaps could leave an app packaged **without** its fonts or splash screen: 1. **First build (Android/Tizen)** — item *registration* (`AndroidAsset`, `BundleResource`, …) lived **inside** the incremental `ProcessMauiFonts` target. On a clean build the target's output-inference glob was empty, so the platform items were never registered and fonts were missing until a *second* build. The fix splits registration into an always-run `_CollectMauiFontItems` target that maps font paths predictively from `@(MauiFont)`. 2. **Incremental build (all platforms)** — `ProcessMauiFonts` / `ProcessMauiSplashScreens` tracked freshness with `mauifont.stamp` / `mauisplash.stamp` files. A stamp could stay newer than a generated output that was later deleted (partial `obj` clean or concurrent build), so MSBuild skipped the target and the package shipped without the missing font/splash. The fix replaces stamps with `mauifont.outputs` / `mauisplash.outputs` manifests. `_ReadMauiFontOutputs` / `_ReadMauiSplashOutputs` run before freshness evaluation, delete the manifest when a listed generated output is missing, and each processor uses the manifest as its sole `Outputs`. This makes only the affected processor rerun without re-stamping unchanged generated assets and unnecessarily invalidating downstream consumers such as Android aapt2. This PR **consolidates** #35962 (closed): it drops the font/splash stamps, adds `ProcessMauiSplashScreensDependsOnTargets`, and de-duplicates fonts by intermediate filename before `CreatePartialInfoPlistTask`, so colliding names (for example an app and a `ProjectReference` both shipping `OpenSans.ttf`) do not emit duplicate `UIAppFonts` entries. The related runtime-side symptom (noisy missing-font fallback logging) is intentionally out of scope here and handled separately in #35963. ### Issues Fixed Fixes #23268 Fixes #33092 ### Tests - `ResizetizerTests.FontsAreCopiedToAndroidAssetsOnFirstBuild` — clean Release build of the `maui` template asserts the font lands in the Android `assets` folder on the **first** build, then an incremental build confirms `ProcessMauiFonts` is skipped while the always-run `_CollectMauiFontItems` still registers the asset. - `ResizetizerTests.BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing` (macOS-gated) — builds Android/iOS/MacCatalyst template targets, deletes only the generated iOS/MacCatalyst `MauiInfo.plist` files and verifies both font processors rerun and restore them, then deletes generated font/splash folders, verifies recovery, and finally verifies a no-op build skips both processors. ### Validation - A focused MSBuild sequence verified initial generation, no-op skipping, regeneration after deleting a recorded output, and a subsequent no-op skip using the same manifest-invalidation protocol. - The full integration workflow could not complete locally because Android workload installation exhausted the shared disk; the updated Build integration test will validate on CI. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Gerald Versluis <939291+jfversluis@users.noreply.github.com> Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
…tputs are deleted (#33919) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change The Resizetizer copies and registers `MauiFont` / `MauiSplashScreen` assets during the build. Two incremental-build gaps could leave an app packaged **without** its fonts or splash screen: 1. **First build (Android/Tizen)** — item *registration* (`AndroidAsset`, `BundleResource`, …) lived **inside** the incremental `ProcessMauiFonts` target. On a clean build the target's output-inference glob was empty, so the platform items were never registered and fonts were missing until a *second* build. The fix splits registration into an always-run `_CollectMauiFontItems` target that maps font paths predictively from `@(MauiFont)`. 2. **Incremental build (all platforms)** — `ProcessMauiFonts` / `ProcessMauiSplashScreens` tracked freshness with `mauifont.stamp` / `mauisplash.stamp` files. A stamp could stay newer than a generated output that was later deleted (partial `obj` clean or concurrent build), so MSBuild skipped the target and the package shipped without the missing font/splash. The fix replaces stamps with `mauifont.outputs` / `mauisplash.outputs` manifests. `_ReadMauiFontOutputs` / `_ReadMauiSplashOutputs` run before freshness evaluation, delete the manifest when a listed generated output is missing, and each processor uses the manifest as its sole `Outputs`. This makes only the affected processor rerun without re-stamping unchanged generated assets and unnecessarily invalidating downstream consumers such as Android aapt2. This PR **consolidates** #35962 (closed): it drops the font/splash stamps, adds `ProcessMauiSplashScreensDependsOnTargets`, and de-duplicates fonts by intermediate filename before `CreatePartialInfoPlistTask`, so colliding names (for example an app and a `ProjectReference` both shipping `OpenSans.ttf`) do not emit duplicate `UIAppFonts` entries. The related runtime-side symptom (noisy missing-font fallback logging) is intentionally out of scope here and handled separately in #35963. ### Issues Fixed Fixes #23268 Fixes #33092 ### Tests - `ResizetizerTests.FontsAreCopiedToAndroidAssetsOnFirstBuild` — clean Release build of the `maui` template asserts the font lands in the Android `assets` folder on the **first** build, then an incremental build confirms `ProcessMauiFonts` is skipped while the always-run `_CollectMauiFontItems` still registers the asset. - `ResizetizerTests.BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing` (macOS-gated) — builds Android/iOS/MacCatalyst template targets, deletes only the generated iOS/MacCatalyst `MauiInfo.plist` files and verifies both font processors rerun and restore them, then deletes generated font/splash folders, verifies recovery, and finally verifies a no-op build skips both processors. ### Validation - A focused MSBuild sequence verified initial generation, no-op skipping, regeneration after deleting a recorded output, and a subsequent no-op skip using the same manifest-invalidation protocol. - The full integration workflow could not complete locally because Android workload installation exhausted the shared disk; the updated Build integration test will validate on CI. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Gerald Versluis <939291+jfversluis@users.noreply.github.com> Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
…tputs are deleted (#33919) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change The Resizetizer copies and registers `MauiFont` / `MauiSplashScreen` assets during the build. Two incremental-build gaps could leave an app packaged **without** its fonts or splash screen: 1. **First build (Android/Tizen)** — item *registration* (`AndroidAsset`, `BundleResource`, …) lived **inside** the incremental `ProcessMauiFonts` target. On a clean build the target's output-inference glob was empty, so the platform items were never registered and fonts were missing until a *second* build. The fix splits registration into an always-run `_CollectMauiFontItems` target that maps font paths predictively from `@(MauiFont)`. 2. **Incremental build (all platforms)** — `ProcessMauiFonts` / `ProcessMauiSplashScreens` tracked freshness with `mauifont.stamp` / `mauisplash.stamp` files. A stamp could stay newer than a generated output that was later deleted (partial `obj` clean or concurrent build), so MSBuild skipped the target and the package shipped without the missing font/splash. The fix replaces stamps with `mauifont.outputs` / `mauisplash.outputs` manifests. `_ReadMauiFontOutputs` / `_ReadMauiSplashOutputs` run before freshness evaluation, delete the manifest when a listed generated output is missing, and each processor uses the manifest as its sole `Outputs`. This makes only the affected processor rerun without re-stamping unchanged generated assets and unnecessarily invalidating downstream consumers such as Android aapt2. This PR **consolidates** #35962 (closed): it drops the font/splash stamps, adds `ProcessMauiSplashScreensDependsOnTargets`, and de-duplicates fonts by intermediate filename before `CreatePartialInfoPlistTask`, so colliding names (for example an app and a `ProjectReference` both shipping `OpenSans.ttf`) do not emit duplicate `UIAppFonts` entries. The related runtime-side symptom (noisy missing-font fallback logging) is intentionally out of scope here and handled separately in #35963. ### Issues Fixed Fixes #23268 Fixes #33092 ### Tests - `ResizetizerTests.FontsAreCopiedToAndroidAssetsOnFirstBuild` — clean Release build of the `maui` template asserts the font lands in the Android `assets` folder on the **first** build, then an incremental build confirms `ProcessMauiFonts` is skipped while the always-run `_CollectMauiFontItems` still registers the asset. - `ResizetizerTests.BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing` (macOS-gated) — builds Android/iOS/MacCatalyst template targets, deletes only the generated iOS/MacCatalyst `MauiInfo.plist` files and verifies both font processors rerun and restore them, then deletes generated font/splash folders, verifies recovery, and finally verifies a no-op build skips both processors. ### Validation - A focused MSBuild sequence verified initial generation, no-op skipping, regeneration after deleting a recorded output, and a subsequent no-op skip using the same manifest-invalidation protocol. - The full integration workflow could not complete locally because Android workload installation exhausted the shared disk; the updated Build integration test will validate on CI. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Gerald Versluis <939291+jfversluis@users.noreply.github.com> Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
…tputs are deleted (#33919) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description of Change The Resizetizer copies and registers `MauiFont` / `MauiSplashScreen` assets during the build. Two incremental-build gaps could leave an app packaged **without** its fonts or splash screen: 1. **First build (Android/Tizen)** — item *registration* (`AndroidAsset`, `BundleResource`, …) lived **inside** the incremental `ProcessMauiFonts` target. On a clean build the target's output-inference glob was empty, so the platform items were never registered and fonts were missing until a *second* build. The fix splits registration into an always-run `_CollectMauiFontItems` target that maps font paths predictively from `@(MauiFont)`. 2. **Incremental build (all platforms)** — `ProcessMauiFonts` / `ProcessMauiSplashScreens` tracked freshness with `mauifont.stamp` / `mauisplash.stamp` files. A stamp could stay newer than a generated output that was later deleted (partial `obj` clean or concurrent build), so MSBuild skipped the target and the package shipped without the missing font/splash. The fix replaces stamps with `mauifont.outputs` / `mauisplash.outputs` manifests. `_ReadMauiFontOutputs` / `_ReadMauiSplashOutputs` run before freshness evaluation, delete the manifest when a listed generated output is missing, and each processor uses the manifest as its sole `Outputs`. This makes only the affected processor rerun without re-stamping unchanged generated assets and unnecessarily invalidating downstream consumers such as Android aapt2. This PR **consolidates** #35962 (closed): it drops the font/splash stamps, adds `ProcessMauiSplashScreensDependsOnTargets`, and de-duplicates fonts by intermediate filename before `CreatePartialInfoPlistTask`, so colliding names (for example an app and a `ProjectReference` both shipping `OpenSans.ttf`) do not emit duplicate `UIAppFonts` entries. The related runtime-side symptom (noisy missing-font fallback logging) is intentionally out of scope here and handled separately in #35963. ### Issues Fixed Fixes #23268 Fixes #33092 ### Tests - `ResizetizerTests.FontsAreCopiedToAndroidAssetsOnFirstBuild` — clean Release build of the `maui` template asserts the font lands in the Android `assets` folder on the **first** build, then an incremental build confirms `ProcessMauiFonts` is skipped while the always-run `_CollectMauiFontItems` still registers the asset. - `ResizetizerTests.BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing` (macOS-gated) — builds Android/iOS/MacCatalyst template targets, deletes only the generated iOS/MacCatalyst `MauiInfo.plist` files and verifies both font processors rerun and restore them, then deletes generated font/splash folders, verifies recovery, and finally verifies a no-op build skips both processors. ### Validation - A focused MSBuild sequence verified initial generation, no-op skipping, regeneration after deleting a recorded output, and a subsequent no-op skip using the same manifest-invalidation protocol. - The full integration workflow could not complete locally because Android workload installation exhausted the shared disk; the updated Build integration test will validate on CI. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Gerald Versluis <939291+jfversluis@users.noreply.github.com> Co-authored-by: Jakub Florkowski <42434498+kubaflo@users.noreply.github.com>
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description of Change
Fixes an incremental Resizetizer invalidation gap where
ProcessMauiFontsandProcessMauiSplashScreenscould be skipped even when their generated intermediate outputs were missing. This could produce Android packages withoutMauiFontassets and splash screen rasters after stale/incomplete incremental build state.The fix tracks generated font and splash outputs in obj-scoped
.outputsmanifests, reads them before the MSBuild incremental check, and includes those generated files in targetOutputs. If a generated font or splash file disappears after a previous successful build, only the relevant font/splash target reruns. Separate font/splash stamp files are no longer generated.Issues Fixed
Fixes #33092
Related Issues / Scope
MauiFontpackage omission and not claimed fixed.Artifact Testing
@danies8 @rokmeglicbit if your recent #28502/#33092 repro involves actual missing packaged fonts/splash resources rather than only Windows fallback logging, please try this PR's artifacts and let us know whether it resolves your issue. The validation here shows Android APK package contents are restored after the stale-incremental state described in #33092.
Requesting review from @jonathanpeppers.
Validation
mauifont.stamp/mauisplash.stamptarget outputs newer while deleting generatedobj\...\resizetizer\fandobj\...\resizetizer\spoutputs, then rebuilding an Android APK.assets/OpenSans-Regular.ttfassets/OpenSans-Semibold.ttfres/drawable-*-v4/splash.pngMicrosoft.Maui.IntegrationTests.csprojwith the existing unrelatedCS9336warning suppressed:dotnet build .\src\TestUtils\src\Microsoft.Maui.IntegrationTests\Microsoft.Maui.IntegrationTests.csproj -v:minimal -p:NoWarn=CS9336.