[XAML] Enable Incremental Hot Reload by default in Debug - #36682
Conversation
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 36682Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 36682" |
|
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
Enables XAML Incremental Hot Reload (XIHR) by default for Debug builds by changing the MSBuild default for EnableMauiIncrementalHotReload in Microsoft.Maui.Controls.targets, while keeping the default off for non-Debug configurations.
Changes:
- Default
EnableMauiIncrementalHotReloadtotruewhen$(Configuration) == Debug. - Keep the fallback default
EnableMauiIncrementalHotReloadtofalsewhen not explicitly set. - Update the surrounding MSBuild comment describing XIHR defaulting behavior.
| <!-- XAML Incremental Hot Reload (XIHR): on by default for .NET 11 projects (Preview 7). Hot reload is a | ||
| dev-time feature; when enabled it emits per-page registry calls and keeps the runtime feature | ||
| switch on. Default off everywhere — set <EnableMauiIncrementalHotReload>true</...> to opt in. | ||
| Default-on is planned for a later preview (see the .NET 11 Hot Reload plan). Resolves to an | ||
| explicit true/false so the value always flows to the source generator and runtime config. --> | ||
| switch on, so it defaults on for Debug builds only and stays off for Release/publish, where the | ||
| registry calls and runtime switch trim away. Set <EnableMauiIncrementalHotReload>false</...> to | ||
| opt out (legacy XAML Hot Reload remains available as a fallback). Resolves to an explicit | ||
| true/false so the value always flows to the source generator and runtime config. --> |
|
|
…tes (#36683) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ### Description Hardens the XAML Incremental Hot Reload (XIHR, #34338) `DataTemplate` source-gen so it produces valid code across real-world XAML. This is a **prerequisite for enabling XIHR by default** (#36682) — with XIHR on, several repo projects (`Essentials.AI.Sample`, `Controls.TestCases.HostApp`, `Controls.Xaml.UnitTests`) currently fail to build. XIHR emits a `DataTemplate`'s content as a stably-named `LoadTemplate_{line}_{pos}` local function so Edit-and-Continue has a stable anchor (#36482). That function was **hoisted** to the top of the generated method via `AddLocalMethod`, which breaks in two ways once XIHR is actually enabled: - **Duplicate method (CS0128 / CS8321).** A template value set more than once in the same scope — e.g. a `required` `DataTemplate` property, which the generator sets both in the object initializer *and* as an assignment — emitted the named function **twice** → `error CS0128: 'LoadTemplate_L_P' is already defined` (+ `CS8321` unused). - **Out-of-scope references (CS0103 / CS1503).** Hoisting to the method top **lost the lambda's lexical scope**, so a template body that referenced enclosing locals (the `DataTemplate` variable, name scopes, resources) generated references to names that don't exist at that scope → `CS0103`, with a cascading `CS1503`. These stayed latent because MAUI ships XIHR **opt-in (default off)**, so no repo project built through this path until default-on was attempted. ### Fix Emit the named local function **inline at the point of use** (not hoisted), which restores the exact lexical scope the anonymous lambda had, and **reserve each method name once per compilation unit** so it is declared a single time — every set-site just re-points `LoadTemplate` at that one function. Also removes ~25 lines of buffering. Non-HR builds are unchanged (still an anonymous lambda). ### Tests - New regression test `DataTemplate_HotReload_SetMultipleTimes_EmitsSingleNamedMethod` — a `required` `DataTemplate` property under XIHR. Verified it **fails without the fix** (`CS0128 'LoadTemplate_9_18' already defined`) and **passes with it**. - Full `SourceGen.UnitTests` suite green (**452/452**), including all existing #36482 XIHR tests. - Verified locally that `Controls.Xaml.UnitTests` builds with `EnableMauiIncrementalHotReload=true` with no `LoadTemplate` errors (the CS0128/CS8321 pair is gone). ### Related - Prerequisite for #36682 (enable XIHR by default in Debug). - Follow-up hardening of #34338 / #36482. --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
XAML Incremental Hot Reload (XIHR, #34338) shipped opt-in behind EnableMauiIncrementalHotReload, defaulting to false everywhere. Per the .NET 11 Hot Reload plan (Phase 3, Preview 7), turn it on by default. The property now defaults to true for Debug builds and stays false for Release/publish, so the per-page registry calls and the Microsoft.Maui.RuntimeFeature.IsIncrementalHotReloadEnabled runtime switch trim away in shipped apps. This mirrors the existing EnableMauiXamlDiagnostics Debug-gating pattern in the same file. Opt out with <EnableMauiIncrementalHotReload>false</...>; legacy XAML Hot Reload remains available as a fallback. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
be29c94 to
8a2f3be
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.
Comments suppressed due to low confidence (1)
src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets:17
- The comment is time-bound and overly specific (".NET 11 projects (Preview 7)") even though this targets file applies generally. This will quickly become outdated and makes it harder to understand the current contract. Consider describing the behavior in terms of Debug vs non-Debug configurations without referencing preview milestones.
<!-- XAML Incremental Hot Reload (XIHR): on by default for .NET 11 projects (Preview 7). Hot reload is a
dev-time feature; when enabled it emits per-page registry calls and keeps the runtime feature
switch on, so it defaults on for Debug builds only and stays off for Release/publish, where the
registry calls and runtime switch trim away. Set <EnableMauiIncrementalHotReload>false</...> to
opt out (legacy XAML Hot Reload remains available as a fallback). Resolves to an explicit
| <EnableMauiIncrementalHotReload Condition="'$(EnableMauiIncrementalHotReload)' == '' and '$(Configuration)' == 'Debug'">true</EnableMauiIncrementalHotReload> | ||
| <EnableMauiIncrementalHotReload Condition="'$(EnableMauiIncrementalHotReload)' == ''">false</EnableMauiIncrementalHotReload> |
Enabling XIHR by default (#36682) makes Debug builds omit the legacy ResourceProvider2 hot-reload fallback (InitializeComponentRuntime), which XIHR intentionally supersedes (InitializeComponentCodeWriter gates it on !EnableIncrementalHotReload). Three Xaml.UnitTests exercise that legacy path and broke under the new default: - HotReloadWorks(SourceGen): the ResourceProvider2 update no longer applies - HotReloadSupportForXSG: InitializeComponentRuntime no longer in the .xsg.cs - TestSourceGenInflator: InitializeComponent IL length changed (394 -> 307) Pin these tests to EnableMauiIncrementalHotReload=false (per-file MauiXaml metadata for the two compiled-in XAML files, and a build arg for the nested-build MSBuild test) so they keep covering the legacy fallback regardless of the default. XIHR-on codegen is covered by SourceGen.UnitTests. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/Microsoft.Maui.Controls.targets:16
- The updated XIHR comment says it "stays off for Release/publish", but the actual default is based only on
$(Configuration) == 'Debug'.dotnet publish -c Debug(or any non-Release publish) would still default this totrue, so the comment is misleading. Consider rewording to describe the real condition (Debug configuration vs non-Debug), or add an explicit publish check if that is the intent.
switch on, so it defaults on for Debug builds only and stays off for Release/publish, where the
registry calls and runtime switch trim away. Set <EnableMauiIncrementalHotReload>false</...> to
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!
Description
XAML Incremental Hot Reload (XIHR) shipped in #34338 as opt-in, with
EnableMauiIncrementalHotReloaddefaulting tofalseeverywhere. Per the .NET 11 Hot Reload plan (Phase 3 — Default-On, Preview 7), this PR turns it on by default.What changed
A single MSBuild default in
Microsoft.Maui.Controls.targets:EnableMauiIncrementalHotReloadnow defaults totrueforDebugbuilds and staysfalseforRelease/publish.MauiXamlHotReloadresolves toSourceGen(instead ofLegacy) for Debug builds.Hot reload is a dev-time feature: gating default-on to
Debugkeeps the per-page registry calls and theMicrosoft.Maui.RuntimeFeature.IsIncrementalHotReloadEnabledruntime switch off in shipped apps, so they trim away inRelease/publish (theRuntimeHostConfigurationOptionis emitted withTrim="true"). This mirrors the existingEnableMauiXamlDiagnosticsDebug-gating pattern a few lines below in the same file.Users can still opt out with
<EnableMauiIncrementalHotReload>false</EnableMauiIncrementalHotReload>; legacy XAML Hot Reload remains available as a fallback (VS / VS Code toggle).Behavior matrix
EnableMauiIncrementalHotReloadMauiXamlHotReloadtrueSourceGenfalseLegacyfalsefalseLegacytruetrueSourceGenTesting
Validated the MSBuild default cascade across all of the above scenarios (Debug→on/SourceGen, Release→off/Legacy, empty config→off, explicit opt-out/opt-in both respected). No public API changes; templates don't set the flag, so new projects pick up the Debug default automatically. Existing SourceGen unit tests (which set the flag per-file) continue to cover both on and off code-generation paths.