Skip to content

Fix noisy missing font fallback logging - #35963

Closed
jfversluis wants to merge 4 commits into
mainfrom
jfversluis/investigate-fonts-issue
Closed

Fix noisy missing font fallback logging#35963
jfversluis wants to merge 4 commits into
mainfrom
jfversluis/investigate-fonts-issue

Conversation

@jfversluis

@jfversluis jfversluis commented Jun 17, 2026

Copy link
Copy Markdown
Member

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!

Fixes #28502
Fixes #24059

Related reports: #19774, #17872, #13239. Related to the embedded-font warning symptom in #19804, but not the broader NuGet asset packaging issue there.

Description

This reduces noisy missing-font fallback logging when an unregistered font family is resolved as a platform/system fallback rather than as an app font asset.

  • Avoids warning/error log noise for unregistered missing font fallback names on Windows and Android.
  • Avoids Android asset-warning noise for embedded-resource fonts that have already been extracted to the file system.
  • Keeps registered missing fonts logging as warnings so real app font registration issues remain visible.
  • Adds regression coverage for OpenSansRegular and Segoe UI on the affected Windows/Android font manager paths.

Scope

This PR intentionally covers the proven Windows and Android root causes. iOS/MacCatalyst was investigated separately, but the current tests pass without the fix there, so this PR no longer changes iOS/MacCatalyst font manager behavior or claims an iOS/MacCatalyst fix.

Validation

  • dotnet build src\Core\tests\DeviceTests\Core.DeviceTests.csproj -f net10.0-windows10.0.19041.0 -p:WindowsPackageType=None --no-restore -v:minimal
  • Previous Windows repro validation confirmed the patched font manager removes the noisy missing-font fallback errors while preserving fallback source behavior.
  • Previous Android build validation confirmed the font manager changes compile and keep registered missing-font warnings while suppressing unregistered fallback/embedded-resource asset-warning noise.

jfversluis and others added 2 commits June 17, 2026 09:58
Treat missing speculative font candidates as expected fallback misses instead of logging errors, while preserving existing font fallback source behavior.

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

Copy link
Copy Markdown
Contributor

🚀 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 -- 35963

Or

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

@github-actions github-actions Bot added area-fonts Custom fonts and Font related API's platform/android platform/ios platform/macos macOS / Mac Catalyst platform/windows labels Jun 17, 2026
@kubaflo

kubaflo commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/enhanced-reviewer

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jun 17, 2026
@MauiBot MauiBot added 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 Jun 17, 2026
MauiBot

This comment was marked as outdated.

@kubaflo

This comment has been minimized.

@github-actions github-actions Bot added the s/agent-review-in-progress AI review is currently running for this PR label Jul 1, 2026
@MauiBot MauiBot added s/agent-gate-failed AI could not verify tests catch the bug s/agent-fix-win AI found a better alternative fix than the PR labels Jul 1, 2026
@kubaflo

kubaflo commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

/azp run

@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 3 pipeline(s).

@rokmeglicbit

Copy link
Copy Markdown

@kubaflo I could not test my app, but I could test related issue #24059 and I still get the error.

Here is the test app with the 35963 PR included. Save and run on Android.
MauiSampleApplication (2).zip

@kubaflo

kubaflo commented Jul 27, 2026

Copy link
Copy Markdown
Contributor

@rokmeglicbit did you test with this: #35963 (comment) ?

@rokmeglicbit

Copy link
Copy Markdown

@kubaflo yes, it is in the zip project above, the include reference is 35963

<ItemGroup>
	  <PackageReference Include="Microsoft.Maui.Controls" Version="10.0.100-ci.pr35963.26372.101" />
</ItemGroup>

Please download .zip and try it yourself. Maybe I did something wrong :(

kubaflo added a commit that referenced this pull request Jul 28, 2026
…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>
kubaflo added a commit that referenced this pull request Jul 29, 2026
…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>
@jfversluis

Copy link
Copy Markdown
Member Author

Probably already mitigated/fixed by #33919 lets wait for that to land and see if we then still need to do anything here.

@jfversluis jfversluis closed this Aug 7, 2026
kubaflo added a commit that referenced this pull request Aug 7, 2026
…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>
kubaflo added a commit that referenced this pull request Aug 12, 2026
…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>
@rokmeglicbit

Copy link
Copy Markdown

@jfversluis did you try the testing and it works?
What is the new PR version, so I can test the zip also?

I guess issue 23268 is duplicate of this pull/35963, so testing above .zip would also confirm issue 23268?

@jfversluis

Copy link
Copy Markdown
Member Author

@rokmeglicbit I think #33919 is part of SR10, so please see if that make this better.

This one (35963) wasn't really a fix but rather making the logging less noisy. Which can still be useful, but the actual root of the issue was the font not being found which is fixed by 33919. The latter is merged and should be available now, making the need for the fix in this issue less needed. Test it out and let me know. I typically don't see pings on closed issues, I get enough notifications as it is, but for this one please feel free to reach out to me directly. My email should be on my GitHub profile. Thanks!

@rokmeglicbit

Copy link
Copy Markdown

If this helps.
I used jonathanpeppers -
#36974 (reply in thread) to enable verbose logging for Mono runtime. I only get two font warnings now, I guess they are correct @jfversluis ?

Microsoft.Maui.FontManager: Warning: Unable to load font '/data/user/0/com.companyname.mauisampleapplication/cache/Maui Material Assets.ttf' from assets.

Java.Lang.RuntimeException: Font asset not found /data/user/0/com.companyname.mauisampleapplication/cache/Maui Material Assets.ttf
at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference type, JniMethodInfo method, JniArgumentValue* args)
at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeObjectMethod(String encodedMember, JniArgumentValue* parameters)
at Android.Graphics.Typeface.CreateFromAsset(AssetManager mgr, String path)
at Microsoft.Maui.FontManager.LoadTypefaceFromAsset(String fontfamily, Boolean warning) in /_/src/Core/src/Fonts/FontManager.Android.cs:line 133
--- End of managed Java.Lang.RuntimeException stack trace ---
java.lang.RuntimeException: Font asset not found /data/user/0/com.companyname.mauisampleapplication/cache/Maui Material Assets.ttf
at android.graphics.Typeface.createFromAsset(Typeface.java:1198)
at crc640ec207abc449b2ca.ShellFragmentContainer.n_onCreateView(Native Method)
at crc640ec207abc449b2ca.ShellFragmentContainer.onCreateView(ShellFragmentContainer.java:37)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3119)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:577)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:286)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2214)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2109)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:2002)
at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:317)
at androidx.viewpager2.adapter.FragmentStateAdapter.placeFragmentInViewHolder(FragmentStateAdapter.java:342)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:273)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:73)
at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:8483)
at androidx.recyclerview.widget.RecyclerView$6.addView(RecyclerView.java:993)
at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:131)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:9546)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:9504)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:9491)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1736)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1682)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:747)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4737)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:4459)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:5011)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:535)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at com.google.android.material.appbar.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:149)
at com.google.android.material.appbar.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:43)
at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:2365)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:960)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at androidx.drawerlayout.widget.DrawerLayout.onLayout(DrawerLayout.java:1273)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1880)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1640)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:776)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:5554)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4638)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3351)
at android.view.ViewRootImpl$TraversalCallback.onVsync(ViewRootImpl.java:11454)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1664)
at android.view.Choreographer.doCallbacks(Choreographer.java:1252)
at android.view.Choreographer.doFrame(Choreographer.java:1209)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1640)
at android.os.Handler.handleCallback(Handler.java:1095)
at android.os.Handler.dispatchMessageImpl(Handler.java:135)
at android.os.Handler.dispatchMessage(Handler.java:125)
at android.os.Looper.loopOnce(Looper.java:296)
at android.os.Looper.loop(Looper.java:397)
at android.app.ActivityThread.main(ActivityThread.java:9523)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:575)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:939)

08-21 10:22:28.643 D/Mono (12024): Running class .cctor for Microsoft.Maui.FontFile from '/data/data/com.companyname.mauisampleapplication/files/.override/arm64-v8a/Microsoft.Maui.dll'
08-21 10:22:28.644 I/monodroid-timing(12024): [1/6] Typemap.managed_to_java: end, total time; elapsed: 0:0::18148
Microsoft.Maui.FontManager: Warning: Unable to load font 'OpenSans-Regular.ttf' from assets.

Java.Lang.RuntimeException: Font asset not found OpenSans-Regular.ttf
at Java.Interop.JniEnvironment.StaticMethods.CallStaticObjectMethod(JniObjectReference type, JniMethodInfo method, JniArgumentValue* args)
at Java.Interop.JniPeerMembers.JniStaticMethods.InvokeObjectMethod(String encodedMember, JniArgumentValue* parameters)
at Android.Graphics.Typeface.CreateFromAsset(AssetManager mgr, String path)
at Microsoft.Maui.FontManager.LoadTypefaceFromAsset(String fontfamily, Boolean warning) in /_/src/Core/src/Fonts/FontManager.Android.cs:line 133
--- End of managed Java.Lang.RuntimeException stack trace ---
java.lang.RuntimeException: Font asset not found OpenSans-Regular.ttf
at android.graphics.Typeface.createFromAsset(Typeface.java:1198)
at crc640ec207abc449b2ca.ShellFragmentContainer.n_onCreateView(Native Method)
at crc640ec207abc449b2ca.ShellFragmentContainer.onCreateView(ShellFragmentContainer.java:37)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:3119)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:577)
at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:286)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:2214)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:2109)
at androidx.fragment.app.FragmentManager.execSingleAction(FragmentManager.java:2002)
at androidx.fragment.app.BackStackRecord.commitNow(BackStackRecord.java:317)
at androidx.viewpager2.adapter.FragmentStateAdapter.placeFragmentInViewHolder(FragmentStateAdapter.java:342)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:273)
at androidx.viewpager2.adapter.FragmentStateAdapter.onViewAttachedToWindow(FragmentStateAdapter.java:73)
at androidx.recyclerview.widget.RecyclerView.dispatchChildAttached(RecyclerView.java:8483)
at androidx.recyclerview.widget.RecyclerView$6.addView(RecyclerView.java:993)
at androidx.recyclerview.widget.ChildHelper.addView(ChildHelper.java:131)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addViewInt(RecyclerView.java:9546)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:9504)
at androidx.recyclerview.widget.RecyclerView$LayoutManager.addView(RecyclerView.java:9491)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1736)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1682)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:747)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4737)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:4459)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:5011)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:535)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at com.google.android.material.appbar.HeaderScrollingViewBehavior.layoutChild(HeaderScrollingViewBehavior.java:149)
at com.google.android.material.appbar.ViewOffsetBehavior.onLayoutChild(ViewOffsetBehavior.java:43)
at com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior.onLayoutChild(AppBarLayout.java:2365)
at androidx.coordinatorlayout.widget.CoordinatorLayout.onLayout(CoordinatorLayout.java:960)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at androidx.drawerlayout.widget.DrawerLayout.onLayout(DrawerLayout.java:1273)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
at android.widget.LinearLayout.layoutHorizontal(LinearLayout.java:1880)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1640)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1891)
at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1729)
at android.widget.LinearLayout.onLayout(LinearLayout.java:1638)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:776)
at android.view.View.layout(View.java:26064)
at android.view.ViewGroup.layout(ViewGroup.java:6595)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:5554)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4638)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3351)
at android.view.ViewRootImpl$TraversalCallback.onVsync(ViewRootImpl.java:11454)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1664)
at android.view.Choreographer.doCallbacks(Choreographer.java:1252)
at android.view.Choreographer.doFrame(Choreographer.java:1209)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1640)
at android.os.Handler.handleCallback(Handler.java:1095)
at android.os.Handler.dispatchMessageImpl(Handler.java:135)
at android.os.Handler.dispatchMessage(Handler.java:125)
at android.os.Looper.loopOnce(Looper.java:296)
at android.os.Looper.loop(Looper.java:397)
at android.app.ActivityThread.main(ActivityThread.java:9523)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:575)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:939)

kubaflo added a commit that referenced this pull request Aug 27, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 2, 2026
…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!

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.

Fixes #23268
Fixes #33092

- `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.

- 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>
kubaflo added a commit that referenced this pull request Sep 3, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 3, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 4, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 6, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 7, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 7, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 8, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 8, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 9, 2026
…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>
kubaflo added a commit that referenced this pull request Sep 9, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-fonts Custom fonts and Font related API's platform/android platform/ios platform/macos macOS / Mac Catalyst platform/windows s/agent-fix-pr-picked AI could not beat the PR fix - PR is the best among all candidates s/agent-gate-failed AI could not verify tests catch the bug s/agent-reviewed PR was reviewed by AI agent workflow (full 4-phase review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Log - Missing Fonts report [Android] AddEmbeddedResourceFont Shows Exception Warning in Output Window

5 participants