Skip to content

Fix #678: build WinUI apps on ARM64 dev boxes by shortening long obj/bin output paths#684

Draft
azchohfi wants to merge 6 commits into
mainfrom
azchohfi-fix-perf-bench-arm64-xaml-build
Draft

Fix #678: build WinUI apps on ARM64 dev boxes by shortening long obj/bin output paths#684
azchohfi wants to merge 6 commits into
mainfrom
azchohfi-fix-perf-bench-arm64-xaml-build

Conversation

@azchohfi

@azchohfi azchohfi commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #678. On an ARM64 dev box whose worktree lives under a long (e.g. OneDrive‑synced) path, dotnet build Reactor.slnx -c Release fails for the WinUI app projects:

  • XamlCompiler.exeMSB3073
  • MakePri.exeAPPX0002 / PRI180 / PRI210

Root cause — MAX_PATH (260), not the OneDrive reparse point

XamlCompiler.exe and MakePri.exe shipped in Windows App SDK 2.0.x are x86 / .NET Framework 4.7.2 Win32 tools with no long‑path manifest (the package ships no ARM64/net6.0 build of them). dotnet build invokes them out‑of‑process (UseXamlCompilerExecutable defaults true on MSBuildRuntimeType=Core), and on an ARM64 host they run under x86 emulation. When the repo path prefix is long, the per‑project obj/bin OUTPUT paths these tools read/write exceed MAX_PATH and they fail (silently — XamlCompiler exits 1 with no output; MakePri's final resources.pri move into bin returns 0x80070003). Inputs that stay under 260 (App.xaml etc.) are read fine — only the long obj/bin outputs break.

Proven: relocating obj and bin to a short path lets the same emulated tools succeed and the apps compile to real binaries. (UseXamlCompilerExecutable=false in‑proc is unsupported on Core — MSB4216/MSB4027; -p:Platform=x64 does not help because the same emulated tool still writes the same long output paths.)

Fix

When building on an ARM64 host AND the project directory is long enough to risk crossing MAX_PATH AND the project is not under src\, redirect BaseIntermediateOutputPath/BaseOutputPath to a short, per‑project, machine‑local path under %LOCALAPPDATA%\ReactorBuild\<proj>-<hash>\ (hash of the project dir so sibling worktrees never collide), and exclude the conventional in‑tree obj/bin from the default source globs.

The gate keys off the host architecture (OSArchitecture, overridable via _ReactorBuildHostArch) — not the project Platform — so it is byte‑for‑byte inert on x64 (CI and dev boxes) and on short‑path checkouts, where the default in‑tree obj/bin are used unchanged. The shipping library under src\ is never relocated.

Scope & safety

  • One net file changed: repo‑root Directory.Build.props (+52). (Earlier commits on the branch added then deleted a skip‑based workaround under tests/perf_bench/**; net diff vs main is the single props file.)
  • src/Reactor and the NuGet package are untouched (excluded by the src\ gate).
  • x64 CI and the /perf gate are unaffected — the host‑arch gate is inert on x64.
  • Independent, pre‑existing, ARM64‑dev‑box‑only build‑ergonomics fix. Does not touch the perf‑fix fleet or x64 CI behavior.

Validation (real builds on an ARM64 + long‑path box, all exit 0 with binaries produced)

Build Before After
dotnet build Reactor.slnx -c Release (ARM64) 33 errors / 16 projects 0 errors
StressPerf.Direct (XAML / MSB3073) fail .exe
StressPerf.ReactorOptimized (PRI210) fail .exe
InteractivePool.Bound (perf_bench) fail .exe
src/Reactor -c Release 0/0 0 warn / 0 err (in‑tree obj, not redirected)
perf_bench -p:Platform=x64 ok x64 .exe (gate inert)

Known limitation — ARM64 local perf harness (follow‑up)

Because the fix must redirect bin (MakePri moves the final resources.pri into bin, so an obj‑only redirect still hits PRI210 on the long in‑tree bin path — verified), the built .exe for the StressPerf apps lands under %LOCALAPPDATA%\ReactorBuild\... on an ARM64 host. The perf/startup harness scripts (tests/stress_perf/ci/Run-PerfBenchmark.ps1, run_spec034_bench.ps1) resolve the exe only under <project>\bin\..., so they would not locate it on an ARM64 host. This is not a regression (those apps did not build at all on ARM64 before this fix) and has no effect on the /perf gate, which runs on x64 where the gate is inert and bin stays in‑tree. Teaching the harness to resolve the output via MSBuild TargetPath for ARM64 dev boxes is folded into the broader ARM64 dev‑box follow‑up (#686).

Closes #678. Draft pending merge coordination.

azchohfi and others added 2 commits June 25, 2026 17:27
The perf_bench micro-benchmark apps are WinUI executables whose build
chain invokes x86/native WinUI tooling that has no ARM64-native build in
Windows App SDK 2.0.x and fails under x86 emulation against a OneDrive
"cloud files" reparse-point working tree:

* *.Bound / *.Direct (App.xaml) drive XamlCompiler.exe (x86 / .NET
  Framework 4.7.2; dotnet build always runs it out-of-proc on Core).
  Emulated, it reports `Input JSON file ... doesn't exist!` -> MSB3073.
* *.Reactor / ControlModel reach the PRI/MakePri resource indexer, which
  fails in WinAppSdkExpandPriContent (`... .pri.xml does not exist`,
  APPX0002 / PRI180).

Both share one root cause: an x86/native WinUI build tool that cannot
service this ARM64 + OneDrive tree. Relocating off the reparse path makes
the XAML compiler succeed; the in-proc net6.0 task is unsupported on Core
(MSB4216/MSB4027); -p:Platform=x64 does not help (same emulated tooling,
same paths).

These are non-shipping benchmark apps; CI and the /perf gate build them on
x64 where this never triggers. Gate on the build *host* architecture so an
ARM64 host skips building the perf_bench WinUI apps, while x64 builds are
completely unaffected. Confined to tests/perf_bench/**.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR review (packaging dimension, confirmed by multi-model cross-check)
flagged that the host-architecture gate defaulted to
RuntimeInformation.ProcessArchitecture -- the architecture of the running
MSBuild/dotnet process. On an ARM64 OS driven by an x64 (emulated) .NET
SDK that evaluates to 'X64', so the gate would not trip even though the
x86 WinUI build tooling still fails under emulation, re-introducing the
original failure.

Default PerfBenchHostArchitecture from RuntimeInformation.OSArchitecture
instead, which reports 'Arm64' on an ARM64 box regardless of SDK bitness
and 'X64' on the x64 CI runner (so the gate stays off in CI). The
property name already says "Host"; this makes the value match the intent.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses ARM64 Windows dev-box build failures for the tests/perf_bench/** WinUI benchmark apps by conditionally skipping their builds when the host OS architecture is ARM64, while keeping x64/CI behavior unchanged.

Changes:

  • Added a tests/perf_bench/Directory.Build.targets that re-imports the repo-root targets (preserving root AOT/trim settings) and computes a host-arch gate (PerfBenchHostArchitecture + PerfBenchSkipWinUiAppOnArm64).
  • Added a conditionally imported targets file that replaces the WinUI app Build target with a no-op (emitting a high-importance message), so leaf perf_bench WinUI executables are skipped on ARM64 hosts while PerfBench.Shared still builds.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
tests/perf_bench/Directory.Build.targets Re-imports root targets and defines an ARM64-host gating property; conditionally imports the skip targets only for WinUI WinExe projects.
tests/perf_bench/build/SkipWinUiAppBuild.Arm64.targets Provides a no-op Build target (with an explanatory message) for gated WinUI app projects on ARM64 hosts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Root cause is MAX_PATH (260), not the OneDrive reparse point. The net472
x86 XamlCompiler.exe and MakePri.exe in Windows App SDK 2.0.x have no
long-path manifest and run x86-emulated on an ARM64 host; when the worktree
prefix is long the per-project obj/bin OUTPUT paths exceed 260 and the tools
fail (MSB3073 / APPX0002 / PRI180 / PRI210). Build inputs that stay under
260 (App.xaml) read fine -- only the long obj/bin outputs break.

Real fix: on an ARM64 host, for non-src projects whose path is long enough
to risk crossing MAX_PATH, redirect obj/bin to a short, per-project,
machine-local path under %LOCALAPPDATA% (keyed by a stable hash of the
project dir). The same emulated tools then succeed and the apps compile to
real binaries. Gated on host architecture, so it is byte-for-byte inert on
x64 (CI and dev boxes) and on short-path checkouts; src/ is never relocated.

This replaces the earlier skip workaround (the perf_bench WinUI apps now
actually compile on ARM64 instead of being excluded), and greens the whole
solution build (dotnet build Reactor.slnx -c Release) on an ARM64 dev box.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@azchohfi azchohfi changed the title Fix #678: skip perf_bench WinUI app builds on ARM64 dev-box hosts Fix #678: build WinUI apps on ARM64 dev boxes by shortening long obj/bin output paths Jun 26, 2026
@azchohfi azchohfi requested a review from Copilot June 26, 2026 02:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 2 comments.

Comment thread Directory.Build.props Outdated
Comment thread Directory.Build.props Outdated
Address Copilot review on #684:

- Make the src\ exclusion case-insensitive (OrdinalIgnoreCase) so a drive-letter casing mismatch between MSBuildThisFileDirectory and MSBuildProjectFullPath can never cause the shipping library under src\ to be redirected.

- Quote the StableStringHash() path argument so a project directory containing parentheses or commas (e.g. 'Program Files (x86)') cannot break MSBuild's property-function parser.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment thread Directory.Build.props Outdated
Address Copilot review on #684: explain that 145 is an empirically chosen cutoff (MAX_PATH 260 minus the ~100-115 char obj/bin output subtree the SDK/WinUI toolchain appends below the project dir), so future edits don't accidentally invalidate the gate.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated 1 comment.

Comment thread Directory.Build.props Outdated
Address Copilot review on #684: the gate keyed only off OSArchitecture, so it could activate on non-Windows Arm64 hosts (macOS/Linux) where LOCALAPPDATA is unset, yielding a malformed BaseOutputPath. The failing tools (XamlCompiler/MakePri) are Windows-only, so require OS == Windows_NT and a non-empty LOCALAPPDATA. Inert on non-Windows by construction.

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

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 1 changed files in this pull request and generated no new comments.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Build: perf_bench XAML projects fail on ARM64 — XamlCompiler.exe exits with code 1 (MSB3073)

3 participants