Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,7 @@
<_ResizetizerOutputsFile>$(_ResizetizerIntermediateOutputPath)mauiimage.outputs</_ResizetizerOutputsFile>
<_ResizetizerStampFile>$(_ResizetizerIntermediateOutputPath)mauiimage.stamp</_ResizetizerStampFile>
<_MauiFontInputsFile>$(_ResizetizerIntermediateOutputPath)mauifont.inputs</_MauiFontInputsFile>
<_MauiFontStampFile>$(_ResizetizerIntermediateOutputPath)mauifont.stamp</_MauiFontStampFile>
<_MauiSplashInputsFile>$(_ResizetizerIntermediateOutputPath)mauisplash.inputs</_MauiSplashInputsFile>
<_MauiSplashStampFile>$(_ResizetizerIntermediateOutputPath)mauisplash.stamp</_MauiSplashStampFile>
<_MauiManifestStampFile>$(_ResizetizerIntermediateOutputPath)mauimanifest.stamp</_MauiManifestStampFile>

<_ResizetizerIntermediateOutputRoot>$(_ResizetizerIntermediateOutputPath)resizetizer\</_ResizetizerIntermediateOutputRoot>
Expand Down Expand Up @@ -175,6 +173,11 @@
$(ProcessMauiFontsAfterTargets);
ResizetizeCollectItems;
</ProcessMauiFontsAfterTargets>

<ProcessMauiFontsBeforeTargets>
$(ProcessMauiFontsBeforeTargets);
_ComputeAndroidAssetsPaths;
</ProcessMauiFontsBeforeTargets>
</PropertyGroup>

<!-- Windows App SDK -->
Expand Down Expand Up @@ -397,9 +400,7 @@
</Target>

<Target Name="ProcessMauiSplashScreens"
Condition="'$(EnableMauiSplashScreenProcessing)' == 'true'"
Inputs="$(MSBuildThisFileFullPath);$(_ResizetizerTaskAssemblyName);$(_MauiSplashInputsFile);@(MauiSplashScreen)"
Outputs="$(_MauiSplashStampFile)">
Condition="'$(EnableMauiSplashScreenProcessing)' == 'true'">

<PropertyGroup>
<_MauiHasSplashScreens>false</_MauiHasSplashScreens>
Expand Down Expand Up @@ -505,21 +506,15 @@
<TizenTpkUserIncludeFiles Include="@(_MauiSplashScreens)" TizenTpkSubDir="shared\res\splash" />
</ItemGroup>

<!-- Stamp file for Outputs -->
<MakeDir Directories="$(_ResizetizerIntermediateOutputPath)"/>
<Touch Files="$(_MauiSplashStampFile)" AlwaysCreate="True" />

<!-- Include splash assets as filewrites so they don't get rm'd -->
<ItemGroup>
<FileWrites Include="@(_MauiSplashAssets)" />
<FileWrites Include="$(_MauiSplashStampFile)" />
</ItemGroup>

</Target>

<Target Name="ProcessMauiFonts"
Condition="'$(EnableMauiFontProcessing)' == 'true'"
Inputs="$(MSBuildThisFileFullPath);$(_ResizetizerTaskAssemblyName);$(_MauiFontInputsFile);@(MauiFont)"
Outputs="$(_MauiFontStampFile)"
AfterTargets="$(ProcessMauiFontsAfterTargets)"
BeforeTargets="$(ProcessMauiFontsBeforeTargets)"
DependsOnTargets="$(ProcessMauiFontsDependsOnTargets)">
Expand Down Expand Up @@ -595,12 +590,8 @@
<TizenTpkUserIncludeFiles Include="@(_MauiFontCopied)" Condition="'@(_MauiFontCopied)' != ''" TizenTpkSubDir="res\fonts\" />
</ItemGroup>

<!-- Touch/create our stamp file for outputs -->
<Touch Files="$(_MauiFontStampFile)" AlwaysCreate="True" />

<!-- Include our fonts and stamp file as filewrites so they don't get rm'd -->
<!-- Include our fonts as filewrites so they don't get rm'd -->
<ItemGroup>
<FileWrites Include="$(_MauiFontStampFile)" />
<FileWrites Include="@(_MauiFontCopied)" />
</ItemGroup>
</Target>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
using System;
using System.IO;
using System.Linq;
using System.Xml.Linq;
using Xunit;
using Xunit.Abstractions;

namespace Microsoft.Maui.Resizetizer.Tests
{
/// <summary>
/// Verifies the MSBuild target structure in Microsoft.Maui.Resizetizer.After.targets
/// to prevent regression of the "fonts missing on first build" bug (#23268) and
/// the "splash screens randomly missing" bug (#33092).
///
/// Root cause: ProcessMauiFonts and ProcessMauiSplashScreens used Inputs/Outputs
/// for incremental builds. When the target was skipped (stamp file up-to-date),
/// platform item groups (AndroidAsset, BundleResource, etc.) were never populated
/// — causing fonts/splash screens to silently disappear from build output.
/// </summary>
public class ResizetizeTargetStructureTests
{
static readonly XNamespace MSBuildNs = "http://schemas.microsoft.com/developer/msbuild/2003";

readonly ITestOutputHelper _output;
readonly XDocument _targetsDoc;

public ResizetizeTargetStructureTests(ITestOutputHelper output)
{
_output = output;

// Navigate from test output dir (artifacts/bin/.../net10.0/) to repo root
var repoRoot = Path.GetFullPath(Path.Combine(
Directory.GetCurrentDirectory(), "..", "..", "..", "..", ".."));
var targetsFilePath = Path.Combine(repoRoot,
"src", "SingleProject", "Resizetizer", "src", "nuget",
"buildTransitive", "Microsoft.Maui.Resizetizer.After.targets");

Assert.True(File.Exists(targetsFilePath),
$"Targets file not found at: {targetsFilePath}");
_output.WriteLine($"Loading targets from: {targetsFilePath}");

_targetsDoc = XDocument.Load(targetsFilePath);
}

XElement FindTarget(string name) =>
_targetsDoc.Root!
.Elements(MSBuildNs + "Target")
.FirstOrDefault(t => t.Attribute("Name")?.Value == name);

XElement FindAndroidPropertyGroup() =>
_targetsDoc.Root!
.Elements(MSBuildNs + "PropertyGroup")
.FirstOrDefault(pg =>
{
var cond = pg.Attribute("Condition")?.Value;
return cond != null
&& cond.Contains("_ResizetizerIsAndroidApp", StringComparison.Ordinal)
&& !cond.Contains("_ResizetizerIsiOSApp", StringComparison.Ordinal);
});

// ──────────────────────────────────────────────────────────
// ProcessMauiFonts — #23268
// ──────────────────────────────────────────────────────────

[Fact]
public void ProcessMauiFonts_ShouldNotHaveInputsAttribute()
{
var target = FindTarget("ProcessMauiFonts");
Assert.NotNull(target);
Assert.True(target.Attribute("Inputs") is null,
"ProcessMauiFonts must not use Inputs for incremental builds. " +
"See https://github.com/dotnet/maui/issues/23268");
}

[Fact]
public void ProcessMauiFonts_ShouldNotHaveOutputsAttribute()
{
var target = FindTarget("ProcessMauiFonts");
Assert.NotNull(target);
Assert.True(target.Attribute("Outputs") is null,
"ProcessMauiFonts must not use Outputs for incremental builds. " +
"See https://github.com/dotnet/maui/issues/23268");
}

[Fact]
public void Android_ProcessMauiFontsBeforeTargets_ShouldIncludeComputeAndroidAssetsPaths()
{
var androidPG = FindAndroidPropertyGroup();
Assert.NotNull(androidPG);

var beforeTargets = androidPG.Element(MSBuildNs + "ProcessMauiFontsBeforeTargets");
Assert.NotNull(beforeTargets);
Assert.Contains("_ComputeAndroidAssetsPaths", beforeTargets.Value, StringComparison.Ordinal);
}

[Fact]
public void ProcessMauiFonts_MatchesWorkingProcessMauiAssetsPattern()
{
var assetsTarget = FindTarget("ProcessMauiAssets");
Assert.NotNull(assetsTarget);
Assert.Null(assetsTarget.Attribute("Inputs"));
Assert.Null(assetsTarget.Attribute("Outputs"));

var fontsTarget = FindTarget("ProcessMauiFonts");
Assert.NotNull(fontsTarget);
Assert.Null(fontsTarget.Attribute("Inputs"));
Assert.Null(fontsTarget.Attribute("Outputs"));
}

// ──────────────────────────────────────────────────────────
// ProcessMauiSplashScreens — #33092
// ──────────────────────────────────────────────────────────

/// <summary>
/// ProcessMauiSplashScreens must NOT have Inputs. Same root cause as fonts:
/// when skipped, platform item groups (LibraryResourceDirectories,
/// BundleResource, ContentWithTargetPath) may not be populated, causing
/// splash screens to randomly disappear from builds.
/// The custom tasks (GenerateSplashAndroidResources, etc.) have built-in
/// file-level incrementality via Resizer.IsUpToDate(), so removing
/// Inputs/Outputs does not cause expensive re-rendering.
/// </summary>
[Fact]
public void ProcessMauiSplashScreens_ShouldNotHaveInputsAttribute()
{
var target = FindTarget("ProcessMauiSplashScreens");
Assert.NotNull(target);
Assert.True(target.Attribute("Inputs") is null,
"ProcessMauiSplashScreens must not use Inputs for incremental builds. " +
"See https://github.com/dotnet/maui/issues/33092");
}

[Fact]
public void ProcessMauiSplashScreens_ShouldNotHaveOutputsAttribute()
{
var target = FindTarget("ProcessMauiSplashScreens");
Assert.NotNull(target);
Assert.True(target.Attribute("Outputs") is null,
"ProcessMauiSplashScreens must not use Outputs for incremental builds. " +
"See https://github.com/dotnet/maui/issues/33092");
}

/// <summary>
/// All three content-producing targets should follow the same pattern
/// as ProcessMauiAssets (no Inputs/Outputs).
/// </summary>
[Fact]
public void AllContentTargets_FollowProcessMauiAssetsPattern()
{
foreach (var targetName in new[] { "ProcessMauiAssets", "ProcessMauiFonts", "ProcessMauiSplashScreens" })
{
var target = FindTarget(targetName);
Assert.NotNull(target);
Assert.True(target.Attribute("Inputs") is null,
$"{targetName} must not use Inputs. See #23268 / #33092");
Assert.True(target.Attribute("Outputs") is null,
$"{targetName} must not use Outputs. See #23268 / #33092");
}
}
}
}
Loading