Repository navigation
Fix MauiXaml will generate source for every xaml file without respect to its condition by configuration/target - #37176
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 37176Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 37176" |
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
|
Hey there @@KarthikRajaKalaimani! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
|
/azp run maui-pr-uitests , maui-pr-devicetests |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
|
Azure Pipelines: Successfully started running 1 pipeline(s). There may be pipelines that require an authorized user to comment /azp run to run. |
There was a problem hiding this comment.
Pull request overview
This PR fixes a .NET MAUI Single Project MSBuild issue where MauiXaml default globs (**/*.xaml) cause XAML source generation to see platform-specific XAML from all Platforms/<Platform> folders in every inner build, leading to duplicate generated members when the same x:Class is used per platform. The change updates the SingleProject filtering target to remove non-active-platform XAML from MauiXaml (and corresponding EmbeddedResource) based on $(TargetPlatformIdentifier), and adds MSBuild-based regression tests to validate the behavior.
Changes:
- Extend
_MauiRemovePlatformCompileItemsto also remove non-active-platform XAML underPlatforms/Android,Platforms/iOS,Platforms/MacCatalyst, andPlatforms/TizenfromMauiXaml. - Add an MSBuild regression test that asserts only the active platform’s
Platforms/<Platform>/*.xamlremains in@(MauiXaml)(plus shared XAML), and thatPlatforms/Windows/*.xamlis always removed.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets | Remove non-active platform Platforms/<Platform>/*.xaml from MauiXaml based on $(TargetPlatformIdentifier) to prevent cross-platform sourcegen duplicates. |
| src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs | Add MSBuild regression tests that log and assert the post-filter @(MauiXaml) identities (active platform retained; Windows always removed). |
Suppressed comments (1)
src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs:699
- To make the added Tizen InlineData meaningful, include the Tizen folder in the platformFolders array so the test creates a Platforms/Tizen/TestPage.xaml variant to validate retain/remove behavior.
var platformFolders = new[] { "Android", "iOS", "MacCatalyst" };
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 5 findings
See inline comments for details.
| https://github.com/dotnet/maui/issues/36951. | ||
| --> | ||
| <_MauiXamlToRemove | ||
| Condition="'$(AndroidProjectFolder)' != '' and '$(TargetPlatformIdentifier)' != 'android'" |
There was a problem hiding this comment.
🔍 AI-Generated Review (multi-model)
[minor] Complexity Reduction — The '$(AndroidProjectFolder)' != '' guard (and its iOS/MacCatalyst/Tizen siblings on lines 72, 75, 78) can never be false. Microsoft.Maui.Controls.SingleProject.Before.targets lines 11-28 unconditionally default every *ProjectFolder property and then run $([MSBuild]::EnsureTrailingSlash(...)) over it, so all five are guaranteed non-empty by the time this target evaluates. The guard mirrors the pre-existing Windows line so it is not a bug, but it reads as a meaningful precondition when it is dead.
Separately, Before.targets line 32 already defines a MauiPlatformSpecificFolder item carrying TargetPlatformIdentifier metadata per folder. Driving these four near-identical blocks off that item (e.g. a single _MauiXamlToRemove Include="%(MauiPlatformSpecificFolder.Identity)**/*.xaml" Condition="'%(MauiPlatformSpecificFolder.TargetPlatformIdentifier)' != '$(TargetPlatformIdentifier)'") would collapse them into one rule and mean a future platform folder does not need a fifth hand-written copy. Non-blocking.
kubaflo
left a comment
There was a problem hiding this comment.
Could you please check the ai's suggestions?
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated no new comments.
Suppressed comments (2)
src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs:684
- BuildMauiXamlFilterProject() calls SetUp() without passing a test name, so the temp directory is based on the helper method name rather than the calling test. This makes all callers (and all Theory iterations) share the same temp/obj directories, which can cause state bleed and makes failures harder to diagnose.
string BuildMauiXamlFilterProject(string targetPlatformIdentifier, string[] platformFolders)
{
SetUp();
src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs:51
- The comment about sharing the same x:Class across all Platforms/ variants appears unrelated to the Xaml.CustomView constant (which is only used for CustomView.xaml in this test file). As written it’s likely to mislead future readers about what this constant is validating.
// Deliberately shares the same x:Class across all Platforms/<Platform> variants, to
// mirror the common per-platform partial-page pattern from https://github.com/dotnet/maui/issues/36951.
}
|
/azp run maui-pr-uitests , maui-pr-devicetests |
|
Azure Pipelines: Successfully started running 2 pipeline(s). |
addressed Ai summary concerns. |
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 4 findings
See inline comments for details.
| </ContentView>"; | ||
| } | ||
|
|
||
| // Deliberately shares the same x:Class across all Platforms/<Platform> variants, to |
There was a problem hiding this comment.
🔍 AI-Generated Review (multi-model)
[moderate] Regression Prevention — This comment is factually wrong for the code it is attached to. It sits at the end of the Xaml fixture class (after the CustomView/MainPage constants) and claims the fixture "deliberately shares the same x:Class across all Platforms/ variants", but neither new test uses these constants: BuildMauiXamlFilterProject writes "<x/>" placeholders (line 1159) that contain no x:Class at all. As written the comment tells the next reader that the duplicate-x:Class condition from #36951 is covered by this fixture when it is not. It also breaks the brace indentation of the enclosing class. Either delete it or move a corrected version next to the placeholder-writing code.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@KarthikRajaKalaimani — new AI review results are available based on commit
3ffdbf6.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate Result: ✅ PASSED
Platform: IOS · Base: main · Merge base: 5d86a00a
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
📄 MSBuildTests MSBuildTests |
✅ FAIL — 112s | ✅ PASS — 40s |
🔴 Without fix — 📄 MSBuildTests: FAIL ✅ · 112s
Error-relevant lines (filtered from the build log):
at Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_RetainsActivePlatformMauiXaml(String targetPlatformIdentifier, String activePlatformFolder) in /_/src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs:line 1097
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeDirectByRefWithFewArgs(Object obj, Span`1 copyOfArgs, BindingFlags invokeAttr)
at InvokeStub_MSBuildTests.SingleProject_RemovePlatformCompileItems_RetainsActivePlatformMauiXaml(Object, Span`1)
at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
🟢 With fix — 📄 MSBuildTests: PASS ✅ · 40s
(no coded error found; showing last 1200 chars)
(useLinuxFolder: False) [643 ms]
Passed SingleProject_SingularPlatformFolderMetadataRemainsBackwardCompatible(targetPlatformIdentifier: "maccatalyst", shouldIncludeLegacyFile: False) [640 ms]
Passed SingleProject_SingularPlatformFolderMetadataRemainsBackwardCompatible(targetPlatformIdentifier: "android", shouldIncludeLegacyFile: False) [662 ms]
Passed SingleProject_SingularPlatformFolderMetadataRemainsBackwardCompatible(targetPlatformIdentifier: "ios", shouldIncludeLegacyFile: True) [660 ms]
Passed SingleProject_CodesignEntitlementsRespected [548 ms]
Passed SingleProject_DefaultEntitlementsUsedWhenNoCustomSet [518 ms]
Passed SingleProject_BuiltInPlatformFoldersCanBeExtended [689 ms]
Passed DesignTimeBuild [1 s]
Passed SingleProject_RemovePlatformCompileItems_AlwaysRemovesWindowsMauiXaml [520 ms]
Passed RandomEmbeddedResource [678 ms]
[xUnit.net 00:00:32.06] TouchXamlFile [SKIP]
[xUnit.net 00:00:32.06] source gen changes
[xUnit.net 00:00:32.06] Finished: Microsoft.Maui.Controls.Xaml.UnitTests
Passed TargetsShouldSkip [1 s]
Skipped TouchXamlFile [1 ms]
Test Run Successful.
Total tests: 44
Passed: 42
Skipped: 2
Total time: 32.3444 Seconds
📁 Fix files reverted (1 files)
src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets
📋 Pre-Flight — Context & Validation
PR #37176 Pre-Flight
Context
- PR:
Fix MauiXaml will generate source for every xaml file without respect to its condition by configuration/target - Issue: #36951
- Base:
main - Review branch:
pr-review-37176at squashed commit6770c4192d - Platform requested for this phase: iOS
- Gate: Passed before STEP 5a; do not rerun gate verification or create/overwrite
gate/content.md.
Issue #36951 reports that XAML source generation consumes every MauiXaml item even when a file belongs to a non-active target/configuration. When identical x:Class values are used under multiple Platforms/<Platform> folders, leaked items generate duplicate attributes and InitializeComponent methods (CS0579/CS0111).
Existing PR Approach
The PR changes two files:
src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets- Extends
_MauiXamlToRemove, previously Windows-only, with four hard-coded globs. - Removes Android, iOS, MacCatalyst, and Tizen XAML whenever
$(TargetPlatformIdentifier)does not equal the corresponding platform. - Keeps the existing unconditional Windows removal because Windows-folder XAML is WinUI markup.
- Extends
src/Controls/tests/Xaml.UnitTests/MSBuild/MSBuildTests.cs- Adds an MSBuild fixture that materializes platform XAML items and inspects the items surviving
_MauiRemovePlatformCompileItems. - Adds a four-row active-platform theory and a Windows regression fact.
- Adds an MSBuild fixture that materializes platform XAML items and inspects the items surviving
The alternative candidates must use a different root-cause strategy rather than relocating the same per-platform hard-coded checks. Relevant existing infrastructure on main includes MauiPlatformSpecificFolder, _MauiNormalizePlatformSpecificFolders, and _MauiCollectPlatformSpecificCompileItems; candidates should account for custom/shared platform folders as well as built-in folders.
Bounded Tests
Primary test:
dotnet test src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj --filter "FullyQualifiedName~Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_RetainsActivePlatformMauiXaml"Mandatory regression test:
dotnet test src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj --filter "FullyQualifiedName~Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_AlwaysRemovesWindowsMauiXaml"The two may be run in one invocation with:
dotnet test src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj --filter "FullyQualifiedName~Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_RetainsActivePlatformMauiXaml|FullyQualifiedName~Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_AlwaysRemovesWindowsMauiXaml"No full suite is permitted. Each candidate gets one implementation/test pass and at most one focused correction/retest.
Environment Constraints
The worktree contains unrelated pre-existing modifications under .github/ and eng/. Candidates must preserve them, modify only the issue-specific source/test files when necessary, use EstablishBrokenBaseline.ps1 for baseline/restore, and leave the PR branch restored after capturing artifacts.
🔬 Code Review — Deep Analysis
Expert Evaluation — Raw PR Fix
Verdict: NEEDS_CHANGES
Confidence: low
The PR correctly places XAML filtering in _MauiRemovePlatformCompileItems, before XAML files are consumed by source generation, and the trusted Gate confirms that the submitted fix reproduces and resolves the reported built-in-platform failure. However, the implementation builds a union of hard-coded removal globs without the active-platform allow-list used by the adjacent Compile filtering logic.
Findings
❌ Error — shared platform folders can lose active-platform XAML
Microsoft.Maui.Controls.SingleProject.targets:143-153 independently removes each non-active built-in folder. The platform-folder properties are user-configurable and may identify the same directory. For example, if both iOSProjectFolder and MacCatalystProjectFolder point to Platforms\Apple\, a Mac Catalyst build skips the Mac Catalyst rule but the iOS rule still removes the shared folder. The active XAML is then absent while its code-behind remains, causing a build failure. The adjacent Compile path avoids this by excluding the current platform's collected items from its removal set.
⚠️ Warning — custom platform-folder mappings remain unfiltered
The fix does not use the existing @(MauiPlatformSpecificFolder) registry. XAML in a custom folder such as Platforms\Apple with TargetPlatformIdentifiers="ios;maccatalyst" can still leak into unrelated inner builds even though its C# items are scoped correctly.
⚠️ Warning — tests do not cover conflicting/shared mappings
The new theory covers only mutually exclusive built-in folders, so it cannot detect the over-removal above. A shared multi-TPI folder test is needed to distinguish correct active-item preservation from the submitted union of deny-list rules.
💡 Suggestion — correct the fixture comment
The comment at MSBuildTests.cs:49 says the new fixture shares an x:Class, but BuildMauiXamlFilterProject writes <x/> placeholders and does not use those constants. It should be removed or corrected.
External Output Contract
| Consumer token/pattern | Producer location | Producer emission condition | Consumer assumption | Ordinary negative case | Downstream effect |
|---|---|---|---|---|---|
MAUIXAML_ITEMS: |
_TestDumpMauiXamlItems generated by BuildMauiXamlFilterProject |
Emitted at high importance after _MauiRemovePlatformCompileItems runs |
The line contains the post-filter MauiXaml identities |
If the target is skipped, no line is emitted | The helper returns an empty string and the required SharedPage.xaml assertion fails; the test fails closed |
Blast Radius and Failure Modes
- The target is shipped through
buildTransitiveand runs for every MAUI single-project build with default items enabled. - The effect is build-time item mutation rather than runtime startup or static-state behavior.
- Built-in, distinct platform folders behave as intended.
- An empty
TargetPlatformIdentifierremoves platform XAML consistently with the existingCompilebehavior. - Shared or aliased platform folders can remove active XAML, and custom registered platform folders are not handled.
Evidence and Review Reconciliation
- Trusted Gate: PASSED — tests fail without the fix and pass with the submitted fix; not rerun.
- Required-check status: undetermined because GitHub CLI authentication was unavailable.
- Prior review status: undetermined because the three GitHub review/comment surfaces could not be queried.
The shared build-infrastructure blast radius and unavailable CI/prior-review evidence cap confidence at low. The actionable findings are persisted separately in inline-findings.json.
🛠️ Try-Fix — Analysis & Comparison
STEP 5a Try-Fix Aggregate — PR #37176
Bounded to two sequential candidates. Gate verification was not rerun.
Candidate 1 — Metadata-Driven Allow-List
Model: claude-opus-5
Result: Pass — 5/5 tests
Correction/retest: Not used
Self-review: 1 moderate finding; 0 critical/major/minor
Detailed artifacts: CustomAgentLogsTmp/PRState/37176/PRAgent/try-fix/attempt-1/
Full narrative: CustomAgentLogsTmp/PRState/37176/PRAgent/try-fix-1/content.md
Approach
Candidate 1 reframed the root cause as MauiXaml never participating in the generic
MauiPlatformSpecificFolder registry already used by the Compile pipeline. It inverted the PR's
hard-coded deny-list into a metadata-driven allow-list:
- Collect active XAML from every registered folder whose normalized
TargetPlatformIdentifiersincludes the currentTargetPlatformIdentifier. - Treat XAML under
$(PlatformsProjectFolder)and every registered platform folder as removal
candidates. - Remove the candidates except the active set.
- Preserve the pre-existing unconditional Windows removal.
Unlike the PR, the candidate adds no platform-name conditions and covers custom/shared registered
folders and relocated platform folders.
Diff
diff --git a/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets b/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets
index d0f3d1aaef..4cef168570 100644
--- a/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets
+++ b/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets
@@ -129,6 +129,42 @@
<_MauiXamlToRemove
Condition="'$(WindowsProjectFolder)' != ''"
Include="$(WindowsProjectFolder)**/*.xaml" />
+
+ <!--
+ MauiXaml carries no ExcludeFromCurrentConfiguration metadata, so unlike Compile it was
+ never filtered per configuration: every platform folder's *.xaml stayed in MauiXaml (and
+ therefore in the XAML SourceGen's AdditionalFiles) for every inner build/TFM. Pages that
+ reuse the same x:Class per platform - the standard per-platform partial-page pattern -
+ then emitted duplicate InitializeComponent/attribute definitions (CS0579/CS0111).
+ See https://github.com/dotnet/maui/issues/36951.
+
+ This mirrors the Compile pipeline above (blanket removal + explicit keep-list) instead of
+ deny-listing individual platforms: everything under $(PlatformsProjectFolder) or under a
+ registered MauiPlatformSpecificFolder is a removal candidate, and only folders claiming
+ the active $(TargetPlatformIdentifier) are kept. That also covers custom/shared folders
+ (e.g. Platforms/Apple declared with TargetPlatformIdentifiers="ios;maccatalyst"), folders
+ relocated outside Platforms/ via $(<Platform>ProjectFolder), and folders for platforms
+ this targets file does not know about.
+
+ The TPI matching contract (EnsureTrailingSlash on the folder identity, whitespace- and
+ case-insensitive ';'-delimited list containment, empty list meaning "always keep") is
+ identical to _MauiCollectPlatformSpecificCompileItems - see the comment there. An empty
+ $(TargetPlatformIdentifier) (design-time/netstandard evaluation) keeps nothing, matching
+ how the Compile removal above behaves for those builds.
+
+ Windows is deliberately NOT special-cased here: the unconditional Windows include above
+ is a separate contribution to the same _MauiXamlToRemove list, so WinUI markup is still
+ removed even when 'windows' is the active platform and the keep-list retains it.
+ -->
+ <_MauiActivePlatformXaml
+ Include="$([MSBuild]::EnsureTrailingSlash('%(MauiPlatformSpecificFolder.Identity)'))**/*.xaml"
+ Condition=" '%(MauiPlatformSpecificFolder.TargetPlatformIdentifiers)' == '' or ('$(TargetPlatformIdentifier)' != '' and $([System.Text.RegularExpressions.Regex]::Replace(';%(MauiPlatformSpecificFolder.TargetPlatformIdentifiers);', '\s+', '').ToLowerInvariant().Contains(';$(TargetPlatformIdentifier.ToLowerInvariant());')) )" />
+ <_MauiPlatformXamlCandidate Include="$(PlatformsProjectFolder)**/*.xaml" />
+ <_MauiPlatformXamlCandidate Include="$([MSBuild]::EnsureTrailingSlash('%(MauiPlatformSpecificFolder.Identity)'))**/*.xaml" />
+ <_MauiXamlToRemove
+ Include="@(_MauiPlatformXamlCandidate)"
+ Exclude="@(_MauiActivePlatformXaml)" />
+
<EmbeddedResource Remove="@(_MauiXamlToRemove)" />
<MauiXaml Remove="@(_MauiXamlToRemove)" />
</ItemGroup>Tests
dotnet test src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj --filter "FullyQualifiedName~Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_RetainsActivePlatformMauiXaml|FullyQualifiedName~Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_AlwaysRemovesWindowsMauiXaml"Result: Failed: 0, Passed: 5, Skipped: 0, Total: 5.
Analysis
The alternative passed in its first implementation/test pass. Inline review found one moderate
backward-compatibility risk: blanket $(PlatformsProjectFolder)**/*.xaml removal also removes XAML
from unregistered shared folders such as Platforms/Shared/Colors.xaml. A project can register
that folder with an empty TargetPlatformIdentifiers list to keep it, but this is broader behavior
than the PR and may require migration guidance. The worktree was restored after artifact capture.
Candidate 2 — Item-Level Configuration Ownership
Model: gpt-5.6-sol
Result: Fail — 0/5 tests on both permitted runs
Correction/retest: Used once
Self-review: 1 critical finding; 0 major/moderate/minor
Detailed artifacts: CustomAgentLogsTmp/PRState/37176/PRAgent/try-fix/attempt-2/
Full narrative: CustomAgentLogsTmp/PRState/37176/PRAgent/try-fix-2/content.md
Approach
Candidate 2 treated absent item-level configuration ownership as the root cause. At
_MauiRemovePlatformCompileItems execution, it annotated built-in platform MauiXaml items with
an internal _MauiTargetPlatformIdentifier, then attempted one generic metadata predicate that
removed items whose owner differed from $(TargetPlatformIdentifier). Unregistered shared XAML
was intentionally left untouched, and the existing unconditional Windows removal remained.
This differed from both preceding designs: it did not use the PR's four conditional removal globs,
and it did not construct Candidate 1's active/candidate sets, use the platform-folder registry as
its mechanism, or blanket-remove unregistered Platforms/**/*.xaml.
Final Diff
diff --git a/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets b/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets
index d0f3d1aaef..3f594ec9ae 100644
--- a/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets
+++ b/src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets
@@ -33,6 +33,7 @@
Update="$(TizenProjectFolder)**/*$(DefaultLanguageSourceExtension)">
<ExcludeFromCurrentConfiguration>false</ExcludeFromCurrentConfiguration>
</Compile>
+
</ItemGroup>
<!--
@@ -104,6 +105,23 @@
<!-- Removals -->
<ItemGroup>
+ <!-- Record configuration ownership at execution time so items added by earlier targets are included. -->
+ <MauiXaml Condition=" '$(AndroidProjectFolder)' != '' " Update="$(AndroidProjectFolder)**/*.xaml">
+ <_MauiTargetPlatformIdentifier>android</_MauiTargetPlatformIdentifier>
+ </MauiXaml>
+ <MauiXaml Condition=" '$(iOSProjectFolder)' != '' " Update="$(iOSProjectFolder)**/*.xaml">
+ <_MauiTargetPlatformIdentifier>ios</_MauiTargetPlatformIdentifier>
+ </MauiXaml>
+ <MauiXaml Condition=" '$(MacCatalystProjectFolder)' != '' " Update="$(MacCatalystProjectFolder)**/*.xaml">
+ <_MauiTargetPlatformIdentifier>maccatalyst</_MauiTargetPlatformIdentifier>
+ </MauiXaml>
+ <MauiXaml Condition=" '$(WindowsProjectFolder)' != '' " Update="$(WindowsProjectFolder)**/*.xaml">
+ <_MauiTargetPlatformIdentifier>windows</_MauiTargetPlatformIdentifier>
+ </MauiXaml>
+ <MauiXaml Condition=" '$(TizenProjectFolder)' != '' " Update="$(TizenProjectFolder)**/*.xaml">
+ <_MauiTargetPlatformIdentifier>tizen</_MauiTargetPlatformIdentifier>
+ </MauiXaml>
+
<!--
Remove every Compile item under $(PlatformsProjectFolder) that is not
part of the current build configuration AND has not been explicitly
@@ -129,6 +147,9 @@
<_MauiXamlToRemove
Condition="'$(WindowsProjectFolder)' != ''"
Include="$(WindowsProjectFolder)**/*.xaml" />
+ <_MauiXamlToRemove
+ Condition=" '%(MauiXaml._MauiTargetPlatformIdentifier)' != '' and '%(MauiXaml._MauiTargetPlatformIdentifier)' != '$(TargetPlatformIdentifier)' "
+ Include="%(MauiXaml.Identity)" />
<EmbeddedResource Remove="@(_MauiXamlToRemove)" />
<MauiXaml Remove="@(_MauiXamlToRemove)" />
</ItemGroup>Tests
dotnet test src/Controls/tests/Xaml.UnitTests/Controls.Xaml.UnitTests.csproj --filter "FullyQualifiedName~Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_RetainsActivePlatformMauiXaml|FullyQualifiedName~Microsoft.Maui.Controls.MSBuild.UnitTests.MSBuildTests.SingleProject_RemovePlatformCompileItems_AlwaysRemovesWindowsMauiXaml"- Initial implementation:
Failed: 5, Passed: 0, Skipped: 0, Total: 5. - Focused correction (
Include="@(MauiXaml)"toInclude="%(MauiXaml.Identity)"):
Failed: 5, Passed: 0, Skipped: 0, Total: 5.
Failure Analysis
The initial metadata batch expanded the complete @(MauiXaml) list for a matching batch and
removed shared/active items. The single permitted correction switched to the batched identity,
but target-time MSBuild batching still did not isolate each removal: logs showed shared XAML
removed in several configurations and all platform XAML retained in another. Inline review
therefore recorded one critical logic/correctness finding. The correction allowance was exhausted,
the candidate was marked Fail, and the worktree was restored.
Aggregate Outcome
Two candidates were attempted, which exhausts the STEP 5a limit:
| Candidate | Mechanism | Result | Review signal |
|---|---|---|---|
| 1 | Registry-driven active allow-list | Pass (5/5) | One moderate compatibility risk |
| 2 | Item-level ownership metadata and predicate | Fail (0/5 twice) | One critical batching defect |
Candidate 1 is the only empirically successful alternative, but its broader handling of
unregistered Platforms/ XAML must be weighed against the PR's narrower behavior in STEP 5b.
📝 PR Finalize — Recommended Title & Description
Assessment: ✏️ Recommend updating — the current title is grammatically unclear and lacks a platform/component prefix, while the description can more precisely connect the submitted target change and its MSBuild regression coverage.
Recommended title
[All] XAML build: Filter MauiXaml items by target platform
Recommended description
### Issue Details
MauiXaml source generation processes XAML files from non-active platform folders instead of respecting the current target platform.
### Root Cause
- `Microsoft.Maui.Controls.Globs.props` unconditionally globs `**/*.xaml` into the `MauiXaml` item group for every inner build/TFM.
- `Microsoft.Maui.Controls.SingleProject.targets` already gates `Compile` (`.cs`) items under `Platforms/<Platform>/` through `%(Compile.ExcludeFromCurrentConfiguration)`, but its XAML-removal logic only removed Windows `.xaml` (WinUI markup) from `MauiXaml`.
- XAML under `Platforms/Android`, `Platforms/iOS`, `Platforms/MacCatalyst`, and `Platforms/Tizen` therefore leaked into every inner build. When multiple platform folders reused the same `x:Class`—a supported per-platform pattern—XAML SourceGen produced duplicate attributes, fields, and `InitializeComponent` methods (`CS0579`/`CS0111`).
### Description of Change
Updated `src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets`, shipped through `buildTransitive` in `Microsoft.Maui.Controls.Build.Tasks`, to extend `_MauiRemovePlatformCompileItems`.
For each built-in Android, iOS, Mac Catalyst, and Tizen project folder, the submitted change removes its `.xaml` files from `MauiXaml` and `EmbeddedResource` when that platform does not match `$(TargetPlatformIdentifier)`. The pre-existing unconditional Windows-folder removal remains unchanged because Windows-folder XAML is WinUI markup rather than MAUI XAML.
Added MSBuild regression coverage in `MSBuildTests.cs` for:
- retaining the active Android, iOS, Mac Catalyst, or Tizen `MauiXaml` item while removing the other built-in platform items;
- preserving shared XAML outside platform folders;
- continuing to remove Windows XAML even for a Windows target.
### Issues Fixed
Fixes #36951
### Platforms Tested
- [x] Android
- [x] Windows
- [x] iOS
- [x] Mac
| Before | After |
|---|---|
| <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" width="600" height="300" /> | <img src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" width="600" height="300" alt="Platform-specific MauiXaml items filtered after the fix" /> |
🏁 Report — Final Recommendation
⚠️ Final Recommendation: REQUEST CHANGES
Winner: pr-plus-reviewer
pr-plus-reviewer is the only candidate that combines a passing targeted regression run with coverage of the expert review's shared-folder failure mode and without try-fix-1's unregistered-XAML compatibility regression. The submitted PR should adopt this candidate's registry-driven filtering and tests before merge.
Comparative Ranking
| Rank | Candidate | Validation | Review result | Assessment |
|---|---|---|---|---|
| 1 | pr-plus-reviewer |
PASS — 10/10 in the required candidate sandbox | Resolves the expert error and both warnings | Uses MauiPlatformSpecificFolder as the source of truth, excludes active mappings, supports custom/shared folders, and deliberately leaves unregistered shared XAML unchanged. |
| 2 | try-fix-1 |
PASS — 5/5 | One moderate backward-compatibility finding | Correctly uses a metadata-driven active allow-list, but blanket-removes every $(PlatformsProjectFolder)**/*.xaml, including unregistered shared resources that previously survived. |
| 3 | pr |
Trusted Gate PASSED; submitted focused cases pass | NEEDS_CHANGES — one error, two warnings, one suggestion | Fixes the reported distinct built-in-folder scenario, but independent hard-coded globs can remove active XAML when platform-folder properties overlap and do not handle custom MauiPlatformSpecificFolder mappings. |
| 4 | try-fix-2 |
FAIL — 0/5 on both permitted runs | One critical batching defect | Item metadata batching did not isolate removal identities; active/shared files were removed in some configurations while non-active files survived in another. Per the contract, this regression-test failure ranks below passing candidates. |
Why pr-plus-reviewer Wins
The existing registry already normalizes built-in and extensibility mappings into TargetPlatformIdentifiers. Collecting active *.xaml from that registry and excluding it from registered-folder removal mirrors the proven Compile allow-list model without enumerating platform names. The added tests distinguish all material alternatives:
- overlapping
iOSProjectFolderandMacCatalystProjectFoldervalues retain active Mac Catalyst XAML; - a custom
ios;maccatalystfolder is retained for its active targets and removed for Android; - unregistered
Platforms/SharedXAML remains untouched; - the original Android/iOS/MacCatalyst/Tizen cases and unconditional Windows behavior remain covered.
Evidence and Remaining Uncertainty
- Trusted Gate: PASSED; it was not rerun.
pr-plus-reviewertargeted validation: 10 passed, 0 failed, run once from/Users/cloudtest/vss/_work/_temp/pr-37176-pr-plus-reviewer.diff --checkagainst the immutable candidate baseline passed before validation.- Required GitHub check state and prior-review reconciliation remain undetermined because GitHub CLI authentication was unavailable during the expert pass. This uncertainty does not alter the request-changes recommendation because the raw PR has a concrete correctness defect.
The winning patch is persisted in pr-plus-reviewer/reviewer.patch; the full candidate against the PR base is in pr-plus-reviewer/candidate.patch.
📱 UI Tests — Button,Label,Layout
Detected UI test categories: Button,Label,Layout
❌ Deep UI tests — 354 passed, 1 failed, 8 skipped across 3 categories on platform-pool agent (replaces in-process counts above).
🧪 UI Test Execution Results (deep, platform pool)
| Category | Tests | Snapshot diffs |
|---|---|---|
Button |
71/72 (1 skipped) ✓ | — |
Label |
90/92 (2 skipped) ✓ | — |
Layout |
193/199 (1 ❌, 5 skipped) | — |
🔍 AI analysis of failures — PR-related vs unrelated
🔍 AI-generated triage (GitHub Copilot CLI) — a heuristic judgement of whether each deep UI test failure is connected to this PR's changes. Verify before relying on it.
Likely unrelated: the failures appear pre-existing, flaky, or infrastructure.
- ● Unrelated — Layout element lookup (~1 test):
EditorsScrollingPageTestfailed with a generic AppiumNoSuchElementExceptionin an editor/layout area untouched by the PR, while the build-target change retains active iOS XAML for this iOS run.
❌ Layout — 1 failed test
EditorsScrollingPageTest
OpenQA.Selenium.NoSuchElementException : An element could not be located on the page using the given search parameters.; For documentation on this error, please visit: https://www.selenium.dev/documentation/webdriver/troubleshooting/errors#no-such-element-exception
at OpenQA.Selenium.WebDriver.UnpackAndThrowOnError(Response errorResponse, String commandToExecute)
at OpenQA.Selenium.WebDriver.ExecuteAsync(String driverCommandToExecute, Dictionary`2 parameters)
at OpenQA.Selenium.Appium.AppiumDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters)
at OpenQA.Selenium.WebDriver.FindElement(String mechanism, String value)
at OpenQA.Selenium.Appium.AppiumDriver.FindElement(String by, String value)
at OpenQA.Selenium.Appium.MobileBy.FindElement(ISearchContext context)
at OpenQA.Selenium.WebDriver.FindElement(By by)
at OpenQA.Selenium.Appium.AppiumDriver.FindElement(By by)
at Microsoft.Maui.TestCases.Tests.KeyboardScrolling.CloseiOSEditorKeyboard(AppiumDr
...
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)
🧭 Next Steps — reviewer patch required (pr-plus-reviewer)
The reviewer-enhanced candidate won, so the submitted PR still needs those changes.
Why: The reviewer-enhanced PR candidate passed all 10 targeted cases and fixes the submitted implementation's active-XAML over-removal for overlapping folders. It also supports registered custom/shared mappings without try-fix-1's compatibility regression for unregistered shared XAML.
Apply PRAgent/pr-plus-reviewer/reviewer.patch from the CopilotLogs
artifact (or follow the report's Required submitted-PR change), push the update, and run
the review again.
… to its condition by configuration/target (#37176) <!-- 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! ### Issue Details MauiXaml will generate source for every xaml file without respect to its condition by configuration/target ### Root Cause • Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml item group unconditionally for every inner build/TFM. • Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms/<Platform>/ via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen . • Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern). ### Description of Change src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app) Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged. ### Issues Fixed Fixes #36951 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" Width="600" Height="300" /> | <img width="600" height="300" alt="image" src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" /> |
… to its condition by configuration/target (#37176) <!-- 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! ### Issue Details MauiXaml will generate source for every xaml file without respect to its condition by configuration/target ### Root Cause • Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml item group unconditionally for every inner build/TFM. • Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms/<Platform>/ via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen . • Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern). ### Description of Change src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app) Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged. ### Issues Fixed Fixes #36951 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" Width="600" Height="300" /> | <img width="600" height="300" alt="image" src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" /> |
… to its condition by configuration/target (#37176) <!-- 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! ### Issue Details MauiXaml will generate source for every xaml file without respect to its condition by configuration/target ### Root Cause • Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml item group unconditionally for every inner build/TFM. • Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms/<Platform>/ via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen . • Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern). ### Description of Change src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app) Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged. ### Issues Fixed Fixes #36951 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" Width="600" Height="300" /> | <img width="600" height="300" alt="image" src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" /> |
… to its condition by configuration/target (#37176) <!-- 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! ### Issue Details MauiXaml will generate source for every xaml file without respect to its condition by configuration/target ### Root Cause • Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml item group unconditionally for every inner build/TFM. • Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms/<Platform>/ via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen . • Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern). ### Description of Change src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app) Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged. ### Issues Fixed Fixes #36951 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" Width="600" Height="300" /> | <img width="600" height="300" alt="image" src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" /> |
… to its condition by configuration/target (#37176) <!-- 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! ### Issue Details MauiXaml will generate source for every xaml file without respect to its condition by configuration/target ### Root Cause • Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml item group unconditionally for every inner build/TFM. • Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms/<Platform>/ via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen . • Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern). ### Description of Change src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app) Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged. ### Issues Fixed Fixes #36951 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" Width="600" Height="300" /> | <img width="600" height="300" alt="image" src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" /> |
… to its condition by configuration/target (#37176) <!-- 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! ### Issue Details MauiXaml will generate source for every xaml file without respect to its condition by configuration/target ### Root Cause • Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml item group unconditionally for every inner build/TFM. • Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms/<Platform>/ via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen . • Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern). ### Description of Change src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app) Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged. ### Issues Fixed Fixes #36951 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" Width="600" Height="300" /> | <img width="600" height="300" alt="image" src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" /> |
… to its condition by configuration/target (#37176) <!-- 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! ### Issue Details MauiXaml will generate source for every xaml file without respect to its condition by configuration/target ### Root Cause • Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml item group unconditionally for every inner build/TFM. • Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms/<Platform>/ via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen . • Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern). ### Description of Change src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app) Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged. ### Issues Fixed Fixes #36951 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" Width="600" Height="300" /> | <img width="600" height="300" alt="image" src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" /> |
… to its condition by configuration/target (#37176) <!-- 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! ### Issue Details MauiXaml will generate source for every xaml file without respect to its condition by configuration/target ### Root Cause • Microsoft.Maui.Controls.Globs.props globs **\*.xaml into the MauiXaml item group unconditionally for every inner build/TFM. • Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms/<Platform>/ via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen . • Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern). ### Description of Change src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app) Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged. ### Issues Fixed Fixes #36951 **Tested the behavior in the following platforms.** - [x] Android - [x] Windows - [x] iOS - [x] Mac | Before | After | |---------|--------| | <img src="https://github.com/user-attachments/assets/a0fd36d9-2094-4948-a645-c3c0d1bf0e4e" Width="600" Height="300" /> | <img width="600" height="300" alt="image" src="https://github.com/user-attachments/assets/510d9024-dacc-4447-821d-29e7129ed844" /> |
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!
Issue Details
MauiXaml will generate source for every xaml file without respect to its condition by configuration/target
Root Cause
• Microsoft.Maui.Controls.Globs.props globs ***.xaml into the MauiXaml item group unconditionally for every inner build/TFM.
• Microsoft.Maui.Controls.SingleProject.targets correctly gates Compile ( .cs ) items under Platforms// via %(Compile.ExcludeFromCurrentConfiguration) , but its XAML-removal logic only ever stripped Windows .xaml (WinUI markup) out of MauiXaml . It never removed non-active-platform .xaml under Platforms/Android , Platforms/iOS , Platforms/MacCatalyst , or Platforms/Tizen .
• Consequence: the XAML SourceGen consumes MauiXaml as AdditionalFiles regardless of platform, so every platform's xaml leaked into every inner build — producing duplicate InitializeComponent /attribute/field declarations whenever multiple platforms reused the same class name (a standard, supported pattern).
Description of Change
src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.SingleProject.targets (shipped via buildTransitive in Microsoft.Maui.Controls.Build.Tasks → applies to every MAUI app)
Extended the _MauiRemovePlatformCompileItems target's _MauiXamlToRemove computation (previously Windows-only) to also cover Android/iOS/MacCatalyst/Tizen: for each built-in platform folder, its .xaml files are queued for removal from MauiXaml / EmbeddedResource unless it matches the currently active $(TargetPlatformIdentifier) . The pre-existing unconditional Windows-folder removal (WinUI markup, not MAUI XAML) is left unchanged.
Issues Fixed
Fixes #36951
Tested the behavior in the following platforms.