Skip to content

[Build] Skip XamlC when no XAML files need XamlC processing - #34972

Merged
jfversluis merged 11 commits into
dotnet:net11.0from
davidnguyen-tech:feature/xamlc-skip-empty-sg-xc
Jul 2, 2026
Merged

[Build] Skip XamlC when no XAML files need XamlC processing#34972
jfversluis merged 11 commits into
dotnet:net11.0from
davidnguyen-tech:feature/xamlc-skip-empty-sg-xc

Conversation

@davidnguyen-tech

@davidnguyen-tech davidnguyen-tech commented Apr 15, 2026

Copy link
Copy Markdown
Member

Summary

Skip the XamlC MSBuild target when no XAML files use the XamlC or Runtime inflator — i.e., when all XAML uses the default SourceGen inflator.

Motivation

With SourceGen as the default inflator in .NET 11, the XamlC MSBuild target has nothing to do on a default build: there are no _MauiXaml_XC or _MauiXaml_RT items to process. Previously XamlC still ran (in ValidateOnly=True mode for Debug), loading the assembly via Cecil, parsing XAML, validating, and discarding results — all redundant because the Source Generator already validates XAML at compile time.

Changes

  • Microsoft.Maui.Controls.targets (1 line): Added AND ('@(_MauiXaml_XC)' != '' OR '@(_MauiXaml_RT)' != '') to the XamlC target Condition, so it only runs when there are files that actually need XamlC processing.
  • MSBuildTests.cs:
    • Skip tests (BuildAProject, LinkedFile, RandomXml, RandomEmbeddedResource, NoXamlFiles) assert XamlC.stamp does NOT exist under the default SourceGen inflator.
    • Added XamlCRunsWhenDeprecatedInflatorUsed — a [Theory] covering both MauiXamlInflator=XamlC and =Runtime, asserting XamlC still runs (covers both branches of the new OR condition).
    • Restored the XamlC-subject tests (TargetsShouldSkip, Clean, DesignTimeBuild, AddNewFile; TouchXamlFile kept [Skip]), adapted to explicitly opt into the XamlC inflator via the new XamlCOptIn constant.

Target Ordering

_MauiXamlComputeInflator (which populates _MauiXaml_XC/_MauiXaml_RT) runs before Compile via PrepareResourcesDependsOn. XamlC runs AfterTargets="AfterCompile", so the condition is evaluated after the item groups are fully populated.

Impact

  • Default builds (SourceGen inflator) skip the XamlC target entirely — saves the Cecil load + XAML parse overhead.
  • Explicit MauiXamlInflator=XamlC or MauiXamlInflator=Runtime builds continue to work unchanged.

@github-actions

github-actions Bot commented Apr 15, 2026

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 -- 34972

Or

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

@davidnguyen-tech
davidnguyen-tech changed the base branch from main to net11.0 April 15, 2026 09:47
@davidnguyen-tech
davidnguyen-tech force-pushed the feature/xamlc-skip-empty-sg-xc branch 3 times, most recently from b38cbcf to 00912c6 Compare April 21, 2026 11:13
@davidnguyen-tech
davidnguyen-tech force-pushed the feature/xamlc-skip-empty-sg-xc branch 4 times, most recently from 28daf7d to 921a0c2 Compare April 22, 2026 13:35
davidnguyen-tech added a commit to davidnguyen-tech/maui that referenced this pull request Apr 23, 2026
Replace the single-line header comment with a block that makes explicit the contract the surviving tests protect: SourceGen is the net11.0 default and the XamlC target must not run on default builds. Addresses reviewer feedback on dotnet#34972 that future contributors had no in-file guidance that these are negative tests.

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

kubaflo commented May 24, 2026

Copy link
Copy Markdown
Contributor

/review -b feature/refactor-copilot-yml

@MauiBot

MauiBot commented May 24, 2026

Copy link
Copy Markdown
Collaborator

⚠️ Merge Conflict Detected — This PR has merge conflicts with its target branch. Please rebase onto the target branch and resolve the conflicts.

@kubaflo

kubaflo commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

AI code review for net11.0 target

Verdict: Needs changes (also still a Draft)

Adds a condition so the legacy XamlC MSBuild target is skipped when no XAML files require it (default SourceGen path self-validates). The core idea is reasonable and the test edits correctly assert SourceGen builds no longer emit XamlC.stamp.

Key findings (description ↔ implementation mismatches)

  • The actual targets condition added is AND ('@(_MauiXaml_XC)' != '' OR '@(_MauiXaml_RT)' != ''), but the PR description states it added AND ('@(_MauiXaml_SG)' != '' OR '@(_MauiXaml_XC)' != ''). These are not equivalent (SG vs RT), and the SG-based version would contradict the new tests (which prove SourceGen now skips XamlC). The description's quoted condition is inaccurate — please reconcile so reviewers can verify intended semantics for Runtime-inflator (_MauiXaml_RT) files.
  • The description claims two new tests were added (XamlCSkippedWhenRuntimeInflatorOnly, XamlCRunsWhenXamlCInflatorUsed), but the diff shows tests were removed (TargetsShouldSkip, Clean, DesignTimeBuild, AddNewFile, TouchXamlFile) and several existing ones flipped to AssertDoesNotExist. No such new tests appear. Please align the description with the actual test changes.
  • Confirm the intended behavior for the Runtime inflator: the condition keeps XamlC running when _MauiXaml_RT is non-empty — is that desired (the motivation text only talks about SG redundancy)?
  • The added comment references ~/.copilot/lessons/dotnet/maui/xamlc-test-opt-in.md, a machine-local path that won't exist for other contributors; consider linking the PR/issue instead.

CI note: AOT integration tests fail on Windows and macOS. Because this PR changes XamlC/build-target behavior and AOT is sensitive to inflator/codegen, these should be verified as not PR-caused before merge.

Confidence: Medium-high on the description/implementation mismatches; the AOT failures need confirmation.

This is an automated, non-approval review comment, not a GitHub approval/request-changes. A human must make the merge decision.

@kubaflo

kubaflo commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

AI code review refresh for net11.0 target

Head reviewed: 710be9ba7fca7cd8f9ff2d3cf5a4496e73ebf946 (this resolves to the current PR head 85e2fd39960c6157f6fe00e8c9f89c67e1ae4249, a rebased commit with the same author timestamp 2026-06-01T19:17:24Z). Target branch: net11.0. State: OPEN / Draft.

Verdict: Needs changes — the newly added test is failing in CI for a PR-caused reason.

This round-two pass focuses on the new commit since the previous fleet review: "Add positive test: XamlC runs when XamlC inflator is used" (which added XamlCRunsWhenXamlCInflatorUsed). That test was a good response to round-one feedback, but as written it does not pass.

Blocking finding

  • XamlCRunsWhenXamlCInflatorUsed fails on every Helix unit-test leg (Windows + macOS, Debug + Release). Root cause from the work-item console log:
    MSBUILD : error MSB1001: Unknown switch.
    Switch: -warnaserror-:MAUI1001
    
    The test calls Build(projectFile, additionalArgs: "-p:MauiXamlInflator=XamlC -warnaserror-:MAUI1001"), but the Build harness invokes MSBuild.exe, and -warnaserror-:<code> is a Roslyn/csc switch, not an MSBuild CLI switch. MSBuild rejects it, the build exits with code 1, and the test fails at Assert.Equal(0, p.ExitCode) (MSBuildTests.cs:255) before it ever reaches the XamlC.stamp assertion. The whole Microsoft.Maui.Controls.Xaml.UnitTests.dll work item is then reported as failed.
    • Suggested fix: drop the invalid switch and instead exclude the code via an MSBuild property, e.g. -p:MSBuildWarningsNotAsErrors=MAUI1001 (or -p:WarningsNotAsErrors=MAUI1001). If MAUI1001 isn't actually escalated to an error in this test build, the switch can simply be removed.

Production change assessment (non-blocking)

  • The targets change itself is sound. Gating the legacy XamlC target with AND ('@(_MauiXaml_XC)' != '' OR '@(_MauiXaml_RT)' != '') correctly skips XamlC on the default SourceGen path and keeps it running for the XamlC/Runtime inflators. _MauiXaml_XC/_MauiXaml_RT are populated in _MauiXamlComputeInflator, which runs (via _MauiInjectXamlCssAdditionalFiles and _MauiAddXamlEmbeddedResources/PrepareResourcesDependsOn) before the AfterCompile-scheduled XamlC target's condition is evaluated, so the items are available. The passing BuildAProject/LinkedFile/RandomXml/RandomEmbeddedResource/NoXamlFiles tests validate the skip path. No production-code regression found.
  • Test-coverage note (non-blocking): the removed DesignTimeBuild, Clean, AddNewFile, and TargetsShouldSkip tests previously covered design-time-build skipping, IncrementalClean/Clean of generated files, and incremental no-op behavior for the XamlC target. That coverage is now gone. Since the XamlC target still carries '$(DesignTimeBuild)' != 'True' and incremental stamp logic, consider re-expressing at least the design-time and incremental cases with MauiXamlInflator=XamlC rather than dropping them.

CI note

Required maui-pr is failing. The Windows/macOS Helix Unit Tests (Debug & Release) failures are directly attributable to this PR (the new XamlCRunsWhenXamlCInflatorUsed test, as detailed above). Separately, AOT macOS, some RunOniOS_…TrimFull legs, and a Build macOS (Release) step are also red; these were not conclusively tied to this change here and should be confirmed as not PR-caused (or pre-existing/infra) before merge.

Confidence: High on the failing-test root cause (deterministic MSB1001 reproduced on all four Helix legs). Medium on the non–unit-test CI failures, which need separate confirmation.

This is an automated, non-approval review comment — not a GitHub approval or request-changes. A human must make the merge decision.

davidnguyen-tech and others added 7 commits June 2, 2026 15:00
XamlC's validate-only pass (Debug default) is the only build-time
XAML validation for files using the Runtime or XamlC inflators.
SourceGen-only files are validated by XSG itself, so XamlC is
redundant for them.

Gate the XamlC target on '@(_MauiXaml_XC)' != '' OR '@(_MauiXaml_RT)' != ''
(equivalent to '@(_MauiXaml_AsEmbeddedResource)' != ''). Projects
using SourceGen exclusively — the default in net11.0 — now skip
XamlC entirely.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Tests that don't care about XamlC now lock in the SourceGen default:
they assert the build still succeeds and XamlC.stamp is absent.

Tests whose subject is the XamlC target itself (incrementality,
Clean, DesignTimeBuild contrast, AddNewFile) opt in via
-p:MauiXamlInflator=XamlC -p:NoWarn=MAUI1001 so the existing contract
is verifiable. Plan originally called for the SourceGen,XamlC combo,
but for these synthesised projects the source generator emits a
duplicate InitializeComponent (CS0111) when both inflators are
active, so the bare XamlC form with NoWarn=MAUI1001 is used instead
to silence the deprecation warning under the repo-wide
TreatWarningsAsErrors setting.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Extract XamlC opt-in args + rationale to a single const so the
'why bare XamlC, not SourceGen,XamlC' explanation lives in one
place. Apply the same opt-in to TouchXamlFile's body for
future-proofing. Reword the misleading inline comment on the
DesignTimeBuild precondition.

Note: the prior commit message's TreatWarningsAsErrors rationale
for NoWarn=MAUI1001 is slightly inaccurate — the synthesized
projects under tempDirectory write a fresh Directory.Build.props
that does not chain in the repo-root TWAE setting. NoWarn is
kept anyway for noise reduction and to be robust if the chain
ever changes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The XamlC target is gated to SourceGen-default-empty projects in
this PR's targets change. Tests that simulated XamlC running on the
synthesised single-project scaffold (TargetsShouldSkip, Clean,
DesignTimeBuild, AddNewFile, TouchXamlFile) were exercising a code
path that no longer executes by default. Delete them along with the
XamlCOptIn constant. The contract that XamlC does not run by default
is already locked in by BuildAProject, LinkedFile, RandomXml,
RandomEmbeddedResource (AssertDoesNotExist on XamlC.stamp), and by
NoXamlFiles (log-scrape that the target isn't scheduled).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the single-line header comment with a block that makes explicit the contract the surviving tests protect: SourceGen is the net11.0 default and the XamlC target must not run on default builds. Addresses reviewer feedback on dotnet#34972 that future contributors had no in-file guidance that these are negative tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds XamlCRunsWhenXamlCInflatorUsed to verify the condition gate
correctly activates XamlC when _MauiXaml_XC is populated via
-p:MauiXamlInflator=XamlC. This is the positive counterpart to
BuildAProject which proves XamlC is skipped under SourceGen.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The positive test passed '-warnaserror-:MAUI1001' to opt out of treating
the XamlC-deprecation warning as an error. The '/warnaserror-:CODE'
negation form is rejected by the rolled-forward .NET 11 preview.5 MSBuild
(MSB1001: Unknown switch), failing the synthesized build before XamlC
could run. Replace it with the valid '-p:WarningsNotAsErrors=MAUI1001'
property, which expresses the same defensive intent.

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

kubaflo commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

✅ LGTM — no blocking issues found

LGTM — unanimous across all four review models (4/4), no code-level findings.

This is a clean build optimization: it skips the XamlC MSBuild step when no XAML files actually need XamlC processing, avoiding unnecessary work. The change is well-scoped and correct.

⚠️ CI note: there are red legs (6 fail / 29 pass), but they are the recurring infra signature seen across today's PRs (Build Analysis, AOT macOS, RunOniOS_MauiRelease/TrimFull) — unrelated to a XamlC-skip change (these are runtime/AOT integration legs, not build-time XamlC). Please confirm a green/known-flaky run before merge.

Multi-model review (gpt-5.5 · opus-4.8 · opus-4.6 · gemini-3.1-pro). Comments only — not a formal approval.

@davidnguyen-tech

Copy link
Copy Markdown
Member Author

@StephaneDelcroix hello, could you please check if the test coverage looks good?

@PureWeen

Copy link
Copy Markdown
Member

/azp run

@azure-pipelines

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

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants