Skip to content
Closed
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 @@ -38,10 +38,9 @@

BackendIdentity records the stable, MAUI-owned key for each built-in backend so
the built-in platforms describe themselves through exactly the same registration
metadata that an external backend NuGet contributes. For recognized platform TFMs
activation still flows through TargetPlatformIdentifier(s); BackendIdentity here is
purely descriptive (built-ins do not declare an ActivationValue, so the neutral-TFM
activation branch never fires for them).
metadata that an external backend NuGet contributes. Recognized platform TFMs
activate through TargetPlatformIdentifier(s), while a neutral TFM can activate the
same registration through BackendIdentity and MauiActiveBackend.
-->
<ItemGroup>
<MauiPlatformSpecificFolder Include="$(AndroidProjectFolder)" TargetPlatformIdentifier="android" BackendIdentity="android" />
Expand All @@ -57,12 +56,11 @@
- TargetPlatformIdentifiers is back-filled from the legacy singular
TargetPlatformIdentifier when only the singular is set.
- ActivationValue is back-filled from BackendIdentity so a backend that
only declares a stable identity key (BackendIdentity="gtk") can be
activated for a neutral TFM via <MauiActiveBackend>gtk</MauiActiveBackend>
without having to repeat the value. Built-in platforms declare a
BackendIdentity but no ActivationValue is derived for them here because
they are TPI-recognized (see the guard below), so they never take the
neutral-activation branch.
declares a stable identity key (BackendIdentity="gtk") can be activated
for a neutral TFM via <MauiActiveBackend>gtk</MauiActiveBackend> without
having to repeat the value. This applies even when the same registration
also declares TargetPlatformIdentifier(s), allowing one item to support
both recognized-TPI and neutral-TFM activation.
- ActivationProperty defaults to the well-known MauiActiveBackend selector
whenever an ActivationValue is present but no explicit property was named.
- _MauiResolvedActivationValue captures the *current value* of the property
Expand Down Expand Up @@ -91,17 +89,10 @@
<TargetPlatformIdentifiers>%(MauiPlatformSpecificFolder.TargetPlatformIdentifier)</TargetPlatformIdentifiers>
</MauiPlatformSpecificFolder>

<!--
Back-fill ActivationValue from BackendIdentity ONLY for folders that are not
already TPI-recognized. Built-in platforms carry both a TargetPlatformIdentifier
and a BackendIdentity; deriving an ActivationValue for them would let a stray
MauiActiveBackend value pull an unrelated built-in folder into a neutral build.
Gating on empty TargetPlatformIdentifier(s) keeps the neutral-activation branch
exclusive to backends that actually rely on it.
-->
<!-- Back-fill ActivationValue without overriding an explicitly supplied value. -->
<MauiPlatformSpecificFolder
Update="@(MauiPlatformSpecificFolder)"
Condition=" '%(MauiPlatformSpecificFolder.ActivationValue)' == '' and '%(MauiPlatformSpecificFolder.BackendIdentity)' != '' and '%(MauiPlatformSpecificFolder.TargetPlatformIdentifier)' == '' and '%(MauiPlatformSpecificFolder.TargetPlatformIdentifiers)' == '' ">
Condition=" '%(MauiPlatformSpecificFolder.ActivationValue)' == '' and '%(MauiPlatformSpecificFolder.BackendIdentity)' != '' ">
<ActivationValue>%(MauiPlatformSpecificFolder.BackendIdentity)</ActivationValue>
</MauiPlatformSpecificFolder>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,19 +154,27 @@
part of the current build configuration AND has not been explicitly
kept by _MauiCollectPlatformSpecificCompileItems above.

The Condition references %(Compile.ExcludeFromCurrentConfiguration)
from inside an Include of a *different* item type — this is MSBuild
cross-item-type batching: the filesystem glob is evaluated once per
unique value of Compile.ExcludeFromCurrentConfiguration. The blanket
<Compile Update> initially marks every $(PlatformsProjectFolder)/**
file true, then active-platform updates flip matching items back to
false. Keep the Condition so only the true batch is removed; otherwise
active-platform files may be removed or inactive files may leak in.
Copy the Compile items marked true to a separate item type, then intersect
those identities with the platform filesystem candidates. This ensures the
removal set contains only Compile items explicitly excluded from the current
configuration. Using temporary item types preserves downstream false metadata
buckets without removing and re-adding active Compile items, which would
perturb ordering and other metadata.
-->
<_MauiCompileItemsMarkedForRemoval
Include="@(Compile->WithMetadataValue('ExcludeFromCurrentConfiguration', 'true'))" />
<_MauiPlatformCompileToRemove
Condition=" '%(Compile.ExcludeFromCurrentConfiguration)' == 'true' "
Include="$(PlatformsProjectFolder)**/*$(DefaultLanguageSourceExtension)"
Exclude="@(_MauiPlatformSpecificCompileItems)" />
<_MauiPlatformCompileNotMarkedForRemoval Include="@(_MauiPlatformCompileToRemove)" />
<_MauiPlatformCompileNotMarkedForRemoval
Remove="@(_MauiCompileItemsMarkedForRemoval)"
MatchOnMetadata="FullPath"
MatchOnMetadataOptions="PathLike" />
<_MauiPlatformCompileToRemove
Remove="@(_MauiPlatformCompileNotMarkedForRemoval)"
MatchOnMetadata="FullPath"
MatchOnMetadataOptions="PathLike" />
<Compile Remove="@(_MauiPlatformCompileToRemove)" />

<!-- Remove all Windows (WinUI) XAML Files from the Windows folder -->
Expand Down
148 changes: 148 additions & 0 deletions src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1410,6 +1410,91 @@ public static class After
StringComparison.OrdinalIgnoreCase);
}

[Fact]
public void SingleProject_RemovePlatformCompileItems_RemovesOnlyCompileItemsMarkedExcluded()
{
SetUp();
var project = NewElement("Project").WithAttribute("Sdk", "Microsoft.NET.Sdk");
var propertyGroup = NewElement("PropertyGroup");
propertyGroup.Add(NewElement("TargetFramework").WithValue(GetTfm()));
propertyGroup.Add(NewElement("SingleProject").WithValue("true"));
propertyGroup.Add(NewElement("EnableDefaultCompileItems").WithValue("false"));
project.Add(propertyGroup);
AddMauiReferences(project);
AddSingleProjectBeforeTargetsImport(project);

var compileItems = NewElement("ItemGroup");
compileItems.Add(NewElement("Compile").WithAttribute("Include", "Before.cs"));
compileItems.Add(NewElement("Compile").WithAttribute("Include", "Platforms\\Removed\\RemovedMarker.cs"));
compileItems.Add(NewElement("Compile").WithAttribute("Include", "Platforms\\Preserved\\PreservedMarker.cs"));
compileItems.Add(NewElement("Compile").WithAttribute("Include", "After.cs"));
project.Add(compileItems);

WriteFile("Before.cs", @"
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public static class Before
{
public static string Value => ""Before"";
}");

WriteFile("Platforms\\Removed\\RemovedMarker.cs", @"
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public static class RemovedMarker
{
public static string Value => ""Removed"";
}");

WriteFile("Platforms\\Preserved\\PreservedMarker.cs", @"
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public static class PreservedMarker
{
public static string Value => ""Preserved"";
}");

WriteFile("After.cs", @"
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public static class After
{
public static string Value => ""After"";
}");

AddSingleProjectTargetsImport(project);

var downstreamCompileMetadata = NewElement("ItemGroup");
var markActiveCompile = NewElement("Compile").WithAttribute("Update", "Platforms\\Preserved\\PreservedMarker.cs");
markActiveCompile.Add(NewElement("ExcludeFromCurrentConfiguration").WithValue("false"));
downstreamCompileMetadata.Add(markActiveCompile);
project.Add(downstreamCompileMetadata);

var dumpTarget = NewElement("Target")
.WithAttribute("Name", "_TestDumpCompileItems")
.WithAttribute("AfterTargets", "_MauiUnflipKeptCompileItemMetadata");
dumpTarget.Add(NewElement("Message")
.WithAttribute("Importance", "high")
.WithAttribute("Text", "COMPILE_ITEMS: @(Compile->'%(Filename)', '|')"));
project.Add(dumpTarget);

var projectFile = IOPath.Combine(tempDirectory, "test.csproj");
project.Save(projectFile);

var log = Build(projectFile);

var testDll = IOPath.Combine(intermediateDirectory, "test.dll");
AssertExists(testDll, nonEmpty: true);
AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.Before");
AssertTypeDoesNotExist(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.RemovedMarker");
AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.PreservedMarker");
AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.After");
Assert.Contains(
"COMPILE_ITEMS: Before|PreservedMarker|After",
log,
StringComparison.OrdinalIgnoreCase);
}

// Backward compatibility: a folder that declares only the legacy singular
// TargetPlatformIdentifier metadata must continue to match exactly that TPI.
[Theory]
Expand Down Expand Up @@ -1465,6 +1550,69 @@ public static class LegacyIosMarker
AssertTypeDoesNotExist(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.LegacyIosMarker");
}

[Theory]
[InlineData("macos", "", true)]
[InlineData("", "macos", true)]
[InlineData("ios", "macos", false)]
[InlineData("", "gtk", false)]
public void SingleProject_BackendIdentitySupportsRecognizedAndNeutralActivation(
string targetPlatformIdentifier,
string activeBackend,
bool shouldIncludeMacOsFile)
{
SetUp();
var project = NewElement("Project").WithAttribute("Sdk", "Microsoft.NET.Sdk");
var propertyGroup = NewElement("PropertyGroup");
propertyGroup.Add(NewElement("TargetFramework").WithValue(GetTfm()));
propertyGroup.Add(NewElement("SingleProject").WithValue("true"));
project.Add(propertyGroup);
AddMauiReferences(project);
AddSingleProjectBeforeTargetsImport(project);

var customMappings = NewElement("ItemGroup");
customMappings.Add(NewElement("MauiPlatformSpecificFolder")
.WithAttribute("Include", "Platforms\\MacOS\\")
.WithAttribute("TargetPlatformIdentifiers", "macos")
.WithAttribute("BackendIdentity", "macos"));
project.Add(customMappings);

WriteFile("Entry.cs", @"
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public static class Entry
{
public static string Value => ""ok"";
}");

WriteFile("Platforms\\MacOS\\MacOsMarker.cs", @"
namespace Microsoft.Maui.Controls.Xaml.UnitTests;

public static class MacOsMarker
{
public static string Value => ""MacOS"";
}");

AddSingleProjectTargetsImport(project);

var projectFile = IOPath.Combine(tempDirectory, "test.csproj");
project.Save(projectFile);

var args = "";
if (!string.IsNullOrEmpty(targetPlatformIdentifier))
args = $"-p:_SingleProjectTestTargetPlatformIdentifier={targetPlatformIdentifier}";
if (!string.IsNullOrEmpty(activeBackend))
args += $" -p:MauiActiveBackend={activeBackend}";
Build(projectFile, additionalArgs: args);

var testDll = IOPath.Combine(intermediateDirectory, "test.dll");
AssertExists(testDll, nonEmpty: true);

if (shouldIncludeMacOsFile)
AssertTypeExists(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.MacOsMarker");
else
AssertTypeDoesNotExist(testDll, "Microsoft.Maui.Controls.Xaml.UnitTests.MacOsMarker");
}

// Neutral-TFM activation (the GTK scenario from #35021/#36650). On a plain
// net11.0 inner build no TargetPlatformIdentifier is recognized; a backend
// declares a stable BackendIdentity and is activated via the well-known
Expand Down
Loading