Skip to content

Exclude _wpftmp temp project from design-time markup compilation#11778

Open
anagnorisis2peripeteia wants to merge 1 commit into
dotnet:mainfrom
anagnorisis2peripeteia:fix/wpftmp-duplicate-compile-designtime
Open

Exclude _wpftmp temp project from design-time markup compilation#11778
anagnorisis2peripeteia wants to merge 1 commit into
dotnet:mainfrom
anagnorisis2peripeteia:fix/wpftmp-duplicate-compile-designtime

Conversation

@anagnorisis2peripeteia

@anagnorisis2peripeteia anagnorisis2peripeteia commented Jul 13, 2026

Copy link
Copy Markdown

Description

Under the .NET 10 SDK, a command-line design-time build (-p:DesignTimeBuild=true) of a WPF project that defines a local or internal XAML type fails with NETSDK1022: Duplicate 'Compile' items. This breaks tooling that drives design-time builds (analysis, mutation testing via Stryker.NET/Buildalyzer), and is a regression from the .NET 8 SDK, where the same project builds cleanly.

Fixes #11775.

Root cause

Microsoft.WinFX.targets adds DesignTimeMarkupCompilation to CoreCompileDependsOn when $(BuildingInsideVisualStudio) or $(DesignTimeBuild) is true (the $(DesignTimeBuild) arm was added so command-line design-time builds also surface the generated code).

WPF resolves references to local/internal XAML types by generating a temporary *_wpftmp project (via GenerateTemporaryTargetAssembly) and compiling it; that temporary project already lists the generated code-behind (*.g.cs) explicitly. Because the temporary project inherits the same targets, the new $(DesignTimeBuild) arm now also runs DesignTimeMarkupCompilation inside the temporary project, which adds the same generated files a second time — producing the duplicate Compile items.

Evaluating the temporary project standalone shows each generated file exactly once (the SDK's default glob correctly excludes obj\); the second copy is added at build time only, by the markup compilation running inside the temp project. Reverting just the $(DesignTimeBuild) arm makes the build succeed; reverting the other condition changed in the same file has no effect.

Fix

Exclude the temporary *_wpftmp project from the design-time markup-compilation dependency. It exists solely to compile already-generated code (which it lists explicitly), so it never needed to run markup compilation itself.

<CoreCompileDependsOn Condition="('$(BuildingInsideVisualStudio)' == 'true' Or '$(DesignTimeBuild)' == 'true') And !$(MSBuildProjectName.EndsWith('_wpftmp'))">

Testing — real-behavior before/after proof

Proof mode: before_after. Repro: a WPF class library defining a local XAML type, built with the
identical command on SDK 10.0.301, with only the one-line targets change differing between runs:

dotnet build WpfLib.csproj /restore /t:Clean;Build -p:DesignTimeBuild=true

Before (base Microsoft.WinFX.targets) — FAILS:

error NETSDK1022: Duplicate 'Compile' items were included. ... The duplicate items were:
  '...\obj\Debug\net8.0-windows\LocalControl.g.cs';
  '...\obj\Debug\net8.0-windows\GeneratedInternalTypeHelper.g.cs'
  [...\WpfLib_hfscxcez_wpftmp.csproj]
Build FAILED.        (exit 1, NETSDK1022 x2)

After (this change) — SUCCEEDS:

Build succeeded.     (exit 0, NETSDK1022 = 0)
before (base targets) after (this change)
exit code 1 0
NETSDK1022 2 (LocalControl.g.cs, GeneratedInternalTypeHelper.g.cs) 0
result Build FAILED Build succeeded

Not a false pass: with the fix the real project still performs markup compilation — LocalControl.g.cs
is generated and the temporary _wpftmp project is still created — only the redundant re-compilation
inside _wpftmp is suppressed. The same project builds cleanly on SDK 8.0.420 (unchanged) and now
on SDK 10.0.301. Both sides were captured back-to-back in one run on the same SDK, reverting the
targets file (SHA-256-verified) between them, so the two builds differ only by this one committed line.

Customer Impact

Command-line design-time builds of WPF projects with local/internal XAML types fail on the .NET 10 SDK, breaking tooling that relies on them. Interactive (VS) builds are unaffected. Restores .NET 8 behavior.

Regression?

Yes — regression from the .NET 8 SDK, surfaced by the $(DesignTimeBuild) addition to CoreCompileDependsOn.

Risk

Low. One-line condition change scoped to the internally-generated *_wpftmp project; real projects and the VS/IDE path are unaffected, and the excluded markup compilation was redundant for that project (its generated files are already listed explicitly).


Review history (before/after proof + review dispositions): https://gist.github.com/anagnorisis2peripeteia/9d7b6ef4c25e6e3d706529f916a2f4f7

Microsoft Reviewers: Open in CodeFlow

The temporary *_wpftmp markup-compile project generated by
GenerateTemporaryTargetAssembly exists only to compile the already-generated
code-behind, which it lists explicitly. Under the .NET 10 SDK,
CoreCompileDependsOn now runs DesignTimeMarkupCompilation for command-line
design-time builds ('$(DesignTimeBuild)' == 'true'), and this also fires inside
the _wpftmp project, adding the generated *.g.cs a second time and tripping
NETSDK1022 (Duplicate 'Compile' items).

Exclude _wpftmp projects from the design-time markup-compilation dependency so
the generated files are listed exactly once. Real projects are unaffected and
SDK 8 behavior is unchanged.

Fixes dotnet#11775.
@anagnorisis2peripeteia
anagnorisis2peripeteia requested a review from a team July 13, 2026 11:57
@anagnorisis2peripeteia
anagnorisis2peripeteia requested a review from a team as a code owner July 13, 2026 11:57
@dotnet-policy-service dotnet-policy-service Bot added PR metadata: Label to tag PRs, to facilitate with triage Community Contribution A label for all community Contributions labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Community Contribution A label for all community Contributions PR metadata: Label to tag PRs, to facilitate with triage

Projects

None yet

1 participant