Fix font/splash assets missing on first build and when incremental outputs are deleted - #33919
Conversation
There was a problem hiding this comment.
Pull request overview
This pull request fixes a race condition in Android and Tizen builds where font assets were not copied on the first build. The issue occurred because ProcessMauiFonts used AfterTargets with ResizetizeCollectItems, which is a weak scheduling hint that can be skipped during parallel/concurrent MSBuild processes.
Changes:
- Replaced
ProcessMauiFontsAfterTargetswithProcessMauiFontsBeforeTargetsfor Android, using_ComputeAndroidResourcePathsas the hard dependency - Replaced
ProcessMauiFontsAfterTargetswithProcessMauiFontsBeforeTargetsfor Tizen, usingPrepareResourcesas the hard dependency - Added detailed comments explaining the rationale for using
BeforeTargetsto create hard dependencies
|
/azp run maui-pr-uitests, maui-pr-devicetests |
|
Azure Pipelines successfully started running 2 pipeline(s). |
|
/rebase |
For Android and Tizen, ProcessMauiFonts used AfterTargets (a weak scheduling hint) which could be skipped during concurrent builds (e.g. VS design-time build racing with regular build). Changed to BeforeTargets to create a hard dependency, matching the pattern already used by iOS, Windows, and WPF platforms. Fixes #23268
The ProcessMauiFonts target has Inputs/Outputs incremental checks, which means its entire body is skipped when the stamp file is up-to-date. This includes the platform-specific item registrations (AndroidAsset, BundleResource, etc.), causing fonts to be missing from the app when the target is skipped during concurrent or cached builds (e.g., VS design-time builds running alongside regular builds). Fix: Extract all platform item registrations into a new _CollectMauiFontItems target that has no Inputs/Outputs, ensuring items are always registered regardless of incremental build state. The new target uses DependsOnTargets=ProcessMauiFonts to ensure font files are copied first, and uses predictive path mapping from @(MauiFont) instead of wildcard globbing to avoid collecting stale intermediate files. This follows the same pattern used by ResizetizeImages in the same file. Fixes #23268
…ifestsDependsOn The iOS scheduling uses CollectAppManifestsDependsOn to pull in ProcessMauiFonts. With the split target, this must reference _CollectMauiFontItems instead, which transitively depends on ProcessMauiFonts via DependsOnTargets.
MSBuild output inference evaluates ItemGroups even in skipped targets. The real danger is wildcard globs that depend on task side-effects.
Corrections verified by 6 agents (Sonnet 4.5, Gemini 3 Pro, Codex 5.2, Opus 4.5, GPT-5.1, Sonnet 4): 1. iOS scheduling table: ResizetizeCollectItems uses CollectBundleResourcesDependsOn/CompileImageAssetsDependsOn. ResizetizeImages uses AfterTargets=ResizetizeCollectItems. ProcessMauiFonts now via _CollectMauiFontItems. 2. mauimanifest.stamp has no companion .inputs file. 3. Split platform items into separate font/image tables with correct metadata (Android images use StampFile, not Link).
d6287cf to
fb01628
Compare
|
/review -b feature/refactor-copilot-yml |
PureWeen
left a comment
There was a problem hiding this comment.
Adversarial review
No actionable findings on the current head.
- Consensus: 3 independent reviewers with adversarial consensus + MAUI domain specialist
- What was verified: manifest invalidation before incremental freshness checks, first-build font registration, no-op behavior, deleted font/splash/plist recovery, platform scheduling, and FileWrites cleanup
- Coverage: the changed paths include focused first-build and missing-intermediate-output integration coverage
- Prior feedback: all review threads are resolved; no new actionable discussion was found
The manifest-only output model correctly avoids re-stamping generated assets while preserving recovery when a recorded output is removed.
kubaflo
left a comment
There was a problem hiding this comment.
🔍 AI-generated review (multi-model: Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), on behalf of @kubaflo.
✅ LGTM — 3-model consensus; my prior REQUEST_CHANGES regression is verified resolved
This lifts my earlier REQUEST_CHANGES (the e7ca7fcf "stop over-touching" commit had regressed this PR's own Resizetizer tests — ProcessMauiFonts should have been skipped (up-to-date) failing ×2 on macOS+Windows). The 569d2a0e "preserve manifest-based incremental recovery" commit fixes it, confirmed two independent ways:
1. Code reasoning (unanimous across Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro):
- The missing-output recovery is correctly ordered — the manifest
<Delete>lives in the separate_ReadMauiFontOutputs/_ReadMauiSplashOutputstargets pulled viaDependsOnTargets, which MSBuild always runs before the parent's incremental up-to-date check. It is a genuine pre-check, not dead code inside the skippable body. - Dropping
@(_Maui*Outputs)fromOutputs(leaving only the always-Touch-stamped manifest) eliminates the stale-timestamp false-run, while the manifest-delete-on-!Existsrestores the deleted-output recovery. No false-skip survives: the manifest is re-stamped every execution so its mtime tracks the newest input, and real inputs remain covered by theInputslists. - The
GetTargetStatushelper rewrite is provably correct across multi-TFM / multi-edge builds — it counts onlyTargetSkipReason.OutputsUpToDateand ignores thePreviouslyBuiltSuccessfullyskips emitted for extra request edges (started > 0 && started == upToDateSkips). This is exactly the semantics my prior finding required.
2. Empirical CI (verified, not inferred): at head 569d2a0e, AzDO build 1503832 — both integration legs report Microsoft.Maui.IntegrationTests.dll Passed! Failed: 0, Passed: 69, Total: 69 on macOS (log 956) and windows (log 965). The previously-failing FontsAreCopiedToAndroidAssetsOnFirstBuild and BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing are green. All 30 checks pass.
Non-blocking suggestions (3, all suggestion-severity — no changes required to merge)
- Recovery scope is "missing", not "stale-content" (
After.targets~L710): manifest-delete keys on!Exists(...), so an in-place corrupted output (manifest still present + newer than inputs) won't re-trigger. This is an intentional tradeoff that mirrors the legacyResizetizeImagesstamp and fully restores the deleted-output detection — flagging only to confirm "missing" is the intended scope. - Delete-recovery is CI-validated on the macOS leg only (
ResizetizerTests.cs~L168): the recovery sub-cases are gated to macOS. The_Read*Outputslogic is platform-agnostic MSBuild so this is sufficient for correctness; a lightweight Android-lane delete-recovery assertion would harden against platform-specific scheduling regressions. - Cosmetic (
ResizetizerTests.cs~L380): theif (!TestEnvironment.IsWindows) if (true) return;idiom is a correct-but-unconventional Windows gate that leaves redundant laterIsWindowschecks; worth a future tidy. (TheAdditionalPropertiespropagation + this idiom largely landed via #35575, outside this delta.)
Verdict: LGTM (0 error / 0 warning / 3 suggestion across all three models). The regression that prompted my REQUEST_CHANGES is resolved. Great fix — the manifest-delete pre-check is the right pattern here.
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@PureWeen — new AI review results are available based on this last commit:
569d2a0. 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: ⚠️ SKIPPED
No tests were detected in this PR.
Recommendation: Add tests to verify the fix using the write-tests-agent.
📋 Pre-Flight — Context & Validation
Issue: #23268 - Copy font assets only works at second build; #33092 - Compilation randomly missing custom fonts & splashScreen
PR: #33919 - Fix font/splash assets missing on first build and when incremental outputs are deleted
Platforms Affected: Android primary; shared Resizetizer font/splash build targets also affect iOS/MacCatalyst, Windows App SDK, WPF, and Tizen.
Files Changed: 1 implementation, 1 test, 1 instruction/documentation
Key Findings
- #23268 reports Android Release builds missing
Resources\Fontsassets after deletingbin/objand doing a clean first build; a second build stages the.ttffiles intoobj/.../assets. - #33092 reports intermittent missing custom fonts and Android splash screen output after incremental/rebuild scenarios where generated Resizetizer outputs can be absent while old stamp files remain newer.
- PR #33919 replaces bare font/splash stamp freshness with
mauifont.outputs/mauisplash.outputsmanifests and pre-read targets that delete a manifest when a recorded output is missing. - The PR keeps
ProcessMauiFontsincremental but moves platform font registration into always-run_CollectMauiFontItems; current head also de-duplicates flattened font paths and deletes stale iOS/MacCatalystMauiInfo.plistwhen the last font is removed. - Tests were added in
src/TestUtils/src/Microsoft.Maui.IntegrationTests/ResizetizerTests.cs, though the provided gate result for this run was skipped and must not be re-run. - Impacted UI test categories: NONE. This is build/package-time Resizetizer target logic, not runtime UI behavior.
Code Review Summary
Verdict: NEEDS_DISCUSSION
Confidence: low
Errors: 0 | Warnings: 0 | Suggestions: 1
Key code review findings:
- 💡 Optional broader recovery coverage: the deleted-output recovery test is macOS-gated because it builds Apple TFMs; an Android-only deleted-output test could further harden platform-specific scheduling, but this is not a blocking issue.
- Blast radius: shared MSBuild infrastructure for font/splash processing and platform item registration across Android, iOS/MacCatalyst, Windows, WPF, and Tizen.
- Failure modes probed: first clean Android Release build, second/no-op build, deleted generated outputs, duplicate flattened font names, and removing all fonts.
- CI caveat: authenticated
gh pr checks --requiredwas unavailable in this environment; public check-run fallback was reported green for relevant lanes by the expert reviewer, but required-check membership could not be verified.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #33919 | Manifest-based font/splash freshness plus always-run font item registration with predictive, de-duplicated intermediate font paths. | Microsoft.Maui.Resizetizer.After.targets, ResizetizerTests.cs, Resizetizer instructions |
Original PR |
🔬 Code Review — Deep Analysis
Code Review — PR #33919
Independent Assessment
What this changes:
PR #33919 changes Resizetizer font/splash MSBuild behavior:
- Replaces font/splash stamp files with
mauifont.outputs/mauisplash.outputsmanifests. - Adds
_ReadMauiFontOutputs/_ReadMauiSplashOutputspre-check targets that delete the manifest when a previously generated output is missing. - Splits font platform-item registration into always-run
_CollectMauiFontItems. - Uses predictive font path mapping plus
RemoveDuplicatesbefore platform registration and iOS plist generation. - Adds integration tests for Android first-build font asset packaging and deleted-output recovery.
Inferred motivation:
Fix missing MAUI fonts/splash assets when incremental MSBuild skips processing targets even though platform packaging still needs asset items or generated outputs were deleted.
Approach assessment:
The final head implementation is sound. The manifest-only Outputs model avoids re-touching generated font/splash assets on no-op builds while still detecting missing recorded outputs before MSBuild freshness evaluation.
Reconciliation with PR Narrative
The PR description matches the code:
- #23268: Android Release first-build missing fonts is addressed by always-run
_CollectMauiFontItems. - #33092: missing/deleted generated font/splash outputs are addressed by output manifests and pre-check deletion.
- The later de-duplication and stale
MauiInfo.plisthandling are present in the current diff.
Prior Review Reconciliation
| Prior ❌ Error Finding | Source | Status | Evidence |
|---|---|---|---|
Incremental tests regressed after generated outputs stopped being touched while also listed in Outputs. |
kubaflo review, 2026-07-08 | ✅ Fixed | Current head uses manifest-only outputs at Microsoft.Maui.Resizetizer.After.targets:407 and :547. |
| Duplicate flattened font paths could register duplicate platform assets. | MauiBot / kubaflo earlier comments | ✅ Fixed | _MauiFontOutputUnique and _MauiFontCopiedUnique use RemoveDuplicates before plist/platform registration. |
Stale iOS/MacCatalyst MauiInfo.plist could remain after removing all fonts. |
kubaflo earlier comment | ✅ Fixed | ProcessMauiFonts deletes MauiInfo.plist when @(MauiFont) is empty. |
No unresolved prior ❌ Error findings found on the current head.
Blast Radius
- Runs for all instances: Yes — Resizetizer targets affect MAUI app builds using fonts/splash assets across Android, iOS/MacCatalyst, Windows, WPF, and Tizen.
- Startup impact: No runtime startup impact; build/package-time only.
- Static/shared state: None.
- Cross-platform side effects: Medium risk because shared MSBuild target behavior affects multiple platform packaging pipelines, but the change is scoped and tested in the relevant Resizetizer integration area.
CI Status
gh pr checks --requiredcould not run because GitHub CLI authentication is unavailable.- Public check-run fallback was reported green for the relevant
maui-pr, Build Analysis, integration build, Android, iOS, Windows, pack, and Helix legs by the expert reviewer. - Required-check membership could not be independently verified without authenticated
gh, so CI confidence is partially capped.
Findings
❌ Error
None.
⚠️ Warning
None.
💡 Suggestion — Optional broader recovery coverage
ResizetizerTests.BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing is macOS-gated, which is reasonable for Apple TFMs and currently covers Android+iOS/MacCatalyst from that lane. If this area regresses again, a lighter Android-only deleted-output recovery test could further harden platform-specific scheduling, but I would not block this PR on it.
Failure-Mode Probing
- First clean Android Release build:
_CollectMauiFontItemsruns independently ofProcessMauiFontsincremental execution and registersAndroidAssetitems from predicted intermediate font paths. - Second/no-op build:
ProcessMauiFontscan skip via manifest-onlyOutputs;_CollectMauiFontItemsstill runs to re-register packaging items. - Deleted generated font/splash output:
_ReadMauiFontOutputs/_ReadMauiSplashOutputsread the previous manifest before freshness evaluation and delete it if a recorded file is missing, forcing regeneration. - Duplicate same-name fonts from app + project reference: Flattened intermediate paths are de-duplicated before plist generation and platform registration.
- Removing all fonts: The iOS/MacCatalyst partial
MauiInfo.plistis explicitly deleted, preventing staleUIAppFontsregistration.
Verdict: NEEDS_DISCUSSION
Confidence: low
Summary: Code review found no actionable correctness issues. I’m not marking this as full LGTM only because authenticated required-check status was unavailable, so required CI membership could not be verified directly.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | maui-expert-reviewer | Real generated font files as Outputs, co-located font/splash stamps, Android BeforeTargets; no manifests or split collection target. |
❌ Environment-blocked | 1 file | Focused integration test could not validate Android build because Linux restore required unavailable Microsoft.iOS.Sdk.net10.0_26.0; also weaker for partial splash deletion than PR. |
| 2 | maui-expert-reviewer | Android source-file AndroidAsset registration plus stamp invalidation health checks; no output manifests. |
❌ Environment-blocked; XML valid | 1 file | Android-biased and less complete cross-platform; same environment blocker. |
| 3 | maui-expert-reviewer | Deterministic font path transform inside single ProcessMauiFonts target plus Android BeforeTargets; no manifests. |
❌ Environment-blocked; XML valid | 1 file | Smallest first-build fix, but does not cover deleted-output recovery for #33092. |
| PR | PR #33919 | Manifest-based font/splash freshness, _ReadMaui*Outputs invalidation, and always-run predictive/de-duplicated font item registration. |
1 implementation + tests + instructions | Original PR remains most complete among explored approaches. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| maui-expert-reviewer | 1 | Yes | try-fix-1: real generated files as MSBuild Outputs plus co-located stamps. |
| maui-expert-reviewer | 1 | Yes | try-fix-2: Android source-asset registration plus stamp health-check invalidation. |
| maui-expert-reviewer | 2 | Yes | try-fix-3: deterministic font item transform inside the single font target. |
| maui-expert-reviewer | 2 | No further stronger ideas | Remaining variants are trivial combinations of the above or regress #33092 coverage versus the PR. |
Exhausted: Yes
Selected Fix: PR #33919 — It is the only explored approach that addresses both linked issues (#23268 first-build Android font registration and #33092 deleted font/splash output recovery) with per-output manifests, duplicate flattened-font handling, stale plist cleanup, and added integration tests. None of the alternative candidates was demonstrably better; all focused test runs were blocked by the Linux environment missing Microsoft.iOS.Sdk.net10.0_26.0, and static analysis shows each alternative is less robust than the PR for at least one required failure mode.
🏁 Report — Final Recommendation
Comparative Report — PR #33919
Candidate ranking
| Rank | Candidate | Regression test result | Assessment |
|---|---|---|---|
| 1 | pr |
Gate skipped; added integration tests present in diff | Best overall. Addresses both #23268 first-build font registration and #33092 deleted font/splash output recovery with manifest invalidation, split always-run font item collection, duplicate flattened-font handling, stale plist cleanup, tests, and Resizetizer guidance. |
| 2 | pr-plus-reviewer |
Same as pr |
Equivalent to pr. The expert reviewer found no actionable feedback, so this candidate has no delta over the raw PR fix and does not improve on it. |
| 3 | try-fix-2 |
Environment-blocked / inconclusive; XML valid | Solves the Android first-build path by registering Android fonts from source and adds stamp health checks, but is Android-biased and less precise cross-platform than manifest-based per-output invalidation. |
| 4 | try-fix-1 |
Environment-blocked / inconclusive | Uses real font outputs plus co-located stamps, but can still miss partial splash deletion cases and keeps item registration inside the processing target rather than using the safer split-target pattern. |
| 5 | try-fix-3 |
Environment-blocked / inconclusive; XML valid | Smallest first-build fix family, but it retains stamp-based font/splash freshness and does not address #33092 deleted-output recovery. |
No STEP 5a candidate had a demonstrated regression-test failure; all try-fix candidates were blocked by the local Linux workload environment before the focused Android integration test could validate behavior. Per the ranking rule, any candidate with a failed regression test would rank below passing candidates, but the available evidence here is inconclusive rather than failing.
Comparison notes
pr is the only candidate that covers the full problem space described by the issues and pre-flight analysis. It separates processing from always-run registration, uses output manifests instead of bare stamps for fonts and splash screens, and avoids unnecessary downstream invalidation by touching only the manifest when outputs are unchanged.
pr-plus-reviewer remains useful as a named candidate for the evaluation pipeline, but because the expert reviewer reported no actionable findings and inline-findings.json is empty, it is functionally identical to pr.
The try-fix candidates each explore narrower alternatives. try-fix-2 is the strongest alternative for Android-first-build behavior, but it bypasses intermediate copied fonts only on Android and depends on manually enumerated sentinel outputs for stamp invalidation. try-fix-1 and try-fix-3 are weaker because they either retain stamp gaps or do not comprehensively cover splash/font deletion recovery.
Winner
Winner: pr
The raw PR fix wins because it is complete, cross-platform, and already incorporates the relevant robustness improvements. The expert reviewer found no changes to apply, and every try-fix candidate is either less complete or environment-blocked without evidence of superiority.
🧭 Next Steps — review latest findings
No alternative fix was selected for this run. Review the session findings and CI results before merging.
|
/azp run |
|
Azure Pipelines: Successfully started running 3 pipeline(s). |
This comment has been minimized.
This comment has been minimized.
Tests Failure Analysis
Test Failure Review: Not ready - click to expandOverall verdict: Not ready. Against
Coverage: 162 checks · 151 passing · 11 failing · 0 pending · 0 inaccessible · 1 unmapped · 16 unexplained build legs · 0 unaccounted failing checks · 2 aborted failing checks · 0 canceled-build checks · 0 device-test unverified · 10 unattributed · 5 regressed-vs-base. Deterministic ceiling: Not ready — 5 legs are red on the PR but green on the sampled base builds (deterministic regression), plus 16 unexplained build legs, 2 aborted checks, and 10 unattributed failures. Builds (this PR): maui-pr 1512392, maui-pr-uitests 1512393, maui-pr-devicetests 1512394 (green). Base sampling ( Recommended actionA human should inspect the 5 regressed-vs-base legs — especially the |
|
@AlleSchonWeg can you please check if this fixes your problem? |
It looks good. But the issue appears randomly. So it's difficult to be 100% sure. |
…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>
…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
The Resizetizer copies and registers
MauiFont/MauiSplashScreenassets during the build. Two incremental-build gaps could leave an app packaged without its fonts or splash screen:First build (Android/Tizen) — item registration (
AndroidAsset,BundleResource, …) lived inside the incrementalProcessMauiFontstarget. 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_CollectMauiFontItemstarget that maps font paths predictively from@(MauiFont).Incremental build (all platforms) —
ProcessMauiFonts/ProcessMauiSplashScreenstracked freshness withmauifont.stamp/mauisplash.stampfiles. A stamp could stay newer than a generated output that was later deleted (partialobjclean or concurrent build), so MSBuild skipped the target and the package shipped without the missing font/splash. The fix replaces stamps withmauifont.outputs/mauisplash.outputsmanifests._ReadMauiFontOutputs/_ReadMauiSplashOutputsrun before freshness evaluation, delete the manifest when a listed generated output is missing, and each processor uses the manifest as its soleOutputs. 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 beforeCreatePartialInfoPlistTask, so colliding names (for example an app and aProjectReferenceboth shippingOpenSans.ttf) do not emit duplicateUIAppFontsentries.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 themauitemplate asserts the font lands in the Androidassetsfolder on the first build, then an incremental build confirmsProcessMauiFontsis skipped while the always-run_CollectMauiFontItemsstill registers the asset.ResizetizerTests.BuildRegeneratesFontsAndSplashWhenIntermediateOutputsAreMissing(macOS-gated) — builds Android/iOS/MacCatalyst template targets, deletes only the generated iOS/MacCatalystMauiInfo.plistfiles 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