Skip to content

Fix MauiFont and MauiSplashScreen assets missing on first/incremental builds#34567

Open
jfversluis wants to merge 4 commits intomainfrom
fix/font-copy-first-build-main
Open

Fix MauiFont and MauiSplashScreen assets missing on first/incremental builds#34567
jfversluis wants to merge 4 commits intomainfrom
fix/font-copy-first-build-main

Conversation

@jfversluis
Copy link
Copy Markdown
Member

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 ProcessMauiFonts and ProcessMauiSplashScreens targets in Microsoft.Maui.Resizetizer.After.targets:

1. Missing Android target ordering (fonts only)

ProcessMauiFonts had no explicit ordering relative to _ComputeAndroidAssetsPaths (the Android SDK target that consumes @(AndroidAsset)). MSBuild AfterTargets/BeforeTargets are not transitive — the existing AfterTargets="ResizetizeCollectItems" did not guarantee execution before _ComputeAndroidAssetsPaths.

2. Incremental build skipping prevents item group population (fonts + splash)

Both ProcessMauiFonts and ProcessMauiSplashScreens used Inputs/Outputs with 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:

Target Incremental Works?
ProcessMauiAssets No (always runs)
ProcessMauiFonts Yes (stamp)
ProcessMauiSplashScreens Yes (stamp)

Fix

Fonts

  1. Add explicit ordering_ComputeAndroidAssetsPaths added to ProcessMauiFontsBeforeTargets on Android
  2. Remove Inputs/Outputs from ProcessMauiFonts — matches the ProcessMauiAssets pattern
  3. Remove unused stamp file_MauiFontStampFile property, Touch task, and FileWrites entry removed

Splash Screens

  1. Remove Inputs/Outputs from ProcessMauiSplashScreens — same fix pattern as fonts
  2. Remove unused stamp file_MauiSplashStampFile property, Touch task, and FileWrites entry removed
  3. Safe to remove because the custom tasks (GenerateSplashAndroidResources, GenerateSplashAssets, GenerateSplashStoryboard) have built-in file-level incrementality via Resizer.IsUpToDate()

Changes

  • src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets

    • Removed _MauiFontStampFile and _MauiSplashStampFile property definitions
    • Added _ComputeAndroidAssetsPaths to ProcessMauiFontsBeforeTargets (Android)
    • Removed Inputs/Outputs from both ProcessMauiFonts and ProcessMauiSplashScreens targets
    • Removed stamp file Touch and FileWrites from both targets
  • src/SingleProject/Resizetizer/test/UnitTests/ProcessMauiFontsTargetTests.cs (new)

    • 7 regression tests verifying both font and splash screen fix structures
    • Tests fail when fixes are reverted, pass when applied

Testing

  • All 575 Resizetizer unit tests pass (7 new + 568 existing, 0 regressions)
  • The 20 pre-existing failures in SkiaSharpAppIconToolsTests are unrelated
  • Regression test cycle verified for both fixes:
    • Fonts: revert → 4/4 fail, restore → 4/4 pass
    • Splash: revert → 3/3 fail, restore → 3/3 pass

Note: Retargeted from net11.0 (previously #34562) to main for .NET 10 servicing. Clean cherry-pick, no conflicts — the targets file is identical between branches.

jfversluis and others added 3 commits March 19, 2026 13:41
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>
Copilot AI review requested due to automatic review settings March 19, 2026 12:43
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 19, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34567

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34567"

@jfversluis jfversluis requested a review from mattleibow March 19, 2026 12:45
@jfversluis jfversluis added the area-single-project Splash Screen, Multi-Targeting, MauiFont, MauiImage, MauiAsset, Resizetizer label Mar 19, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

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 ProcessMauiFonts to run before Android’s _ComputeAndroidAssetsPaths to guarantee @(AndroidAsset) is populated in time.
  • Removed Inputs/Outputs stamp-based incremental skipping from ProcessMauiFonts and ProcessMauiSplashScreens (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.

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>
Copy link
Copy Markdown
Member Author

@jfversluis jfversluis left a comment

Choose a reason for hiding this comment

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

Addressed: renamed ProcessMauiFontsTargetTests.csResizetizeTargetStructureTests.cs to match the class name. See 6e1efe9.

@MauiBot
Copy link
Copy Markdown
Collaborator

MauiBot commented Mar 23, 2026

🤖 AI Summary

📊 Expand Full Review6e1efe9 · Rename test file to match class name
🔍 Pre-Flight — Context & Validation

Issue: #23268 / #23659 / #33092 - Copy font assets only works at second build / Fonts are randomly left out the MSIX package / Compilation randomly missing custom fonts & splashScreen
PR: #34567 - Fix MauiFont and MauiSplashScreen assets missing on first/incremental builds
Platforms Affected: Android, Windows (explicit in issues); PR also asserts iOS coverage via shared Resizetizer target behavior
Files Changed: 1 implementation, 1 test

Key Findings

  • The PR changes only src/SingleProject/Resizetizer/src/nuget/buildTransitive/Microsoft.Maui.Resizetizer.After.targets and adds one xUnit regression test file src/SingleProject/Resizetizer/test/UnitTests/ResizetizeTargetStructureTests.cs.
  • The PR links three issues: Copy font assets only works at second build #23268 (Android first-build font assets missing), [Windows, 9.0 Preview 6] Fonts are randomly left out the MSIX package #23659 (Windows MSIX fonts intermittently missing), and Compilation randomly missing custom fonts & splashScreen #33092 (fonts and splash screens randomly missing on incremental builds).
  • The common root-cause described in the PR is target-body skipping via Inputs/Outputs, which prevents platform item groups from being populated when MSBuild treats the target as up-to-date.
  • For Android fonts specifically, the PR also adds explicit ordering so ProcessMauiFonts runs before _ComputeAndroidAssetsPaths.
  • Added tests are unit tests over the .targets XML structure, not HostApp/UI/device tests. That matters for Gate: the standard UI verification flow is not directly applicable.
  • Inline review feedback only requested a rename to match file/class naming; the PR author addressed that in commit 6e1efe99.
  • CI is mostly green; the combined PR status is pending because the aggregate run includes failing Windows Helix unit-test checks unrelated to the newly added Resizetizer unit test file.

Edge Cases From Issues

  • Android repro is especially sensitive on the first build after deleting bin/obj, and after switching configurations.
  • Windows reports missing fonts in published MSIX output, with MauiAsset working more reliably than MauiFont.
  • Recent issue comments include Android runtime failures where FontManager cannot load a font from assets, consistent with assets not being copied at all.
  • Issue Compilation randomly missing custom fonts & splashScreen #33092 reports splash screens and fonts disappearing together, reinforcing the PR's decision to treat both targets with the same incremental-build fix pattern.

Fix Candidates

# 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 ⚠️ PASS with test rewrite 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 ⚠️ SKIPPED 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.


@MauiBot MauiBot added s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review) labels Mar 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-single-project Splash Screen, Multi-Targeting, MauiFont, MauiImage, MauiAsset, Resizetizer s/agent-approved AI agent recommends approval - PR fix is correct and optimal s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

3 participants