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
Draft
Fix #678: build WinUI apps on ARM64 dev boxes by shortening long obj/bin output paths#684azchohfi wants to merge 6 commits into
azchohfi wants to merge 6 commits into
Conversation
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>
Contributor
There was a problem hiding this comment.
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.targetsthat 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
Buildtarget with a no-op (emitting a high-importance message), so leaf perf_bench WinUI executables are skipped on ARM64 hosts whilePerfBench.Sharedstill 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>
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>
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>
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>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #678. On an ARM64 dev box whose worktree lives under a long (e.g. OneDrive‑synced) path,
dotnet build Reactor.slnx -c Releasefails for the WinUI app projects:XamlCompiler.exe→MSB3073MakePri.exe→APPX0002/PRI180/PRI210Root cause — MAX_PATH (260), not the OneDrive reparse point
XamlCompiler.exeandMakePri.exeshipped 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 buildinvokes them out‑of‑process (UseXamlCompilerExecutabledefaults true onMSBuildRuntimeType=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 exceedMAX_PATHand they fail (silently —XamlCompilerexits 1 with no output;MakePri's finalresources.primove intobinreturns0x80070003). Inputs that stay under 260 (App.xamletc.) 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=falsein‑proc is unsupported on Core —MSB4216/MSB4027;-p:Platform=x64does 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_PATHAND the project is not undersrc\, redirectBaseIntermediateOutputPath/BaseOutputPathto 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 projectPlatform— 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 undersrc\is never relocated.Scope & safety
Directory.Build.props(+52). (Earlier commits on the branch added then deleted a skip‑based workaround undertests/perf_bench/**; net diff vsmainis the single props file.)src/Reactorand the NuGet package are untouched (excluded by thesrc\gate)./perfgate are unaffected — the host‑arch gate is inert on x64.Validation (real builds on an ARM64 + long‑path box, all exit 0 with binaries produced)
dotnet build Reactor.slnx -c Release(ARM64)StressPerf.Direct(XAML / MSB3073)StressPerf.ReactorOptimized(PRI210)InteractivePool.Bound(perf_bench)src/Reactor-c Release-p:Platform=x64Known limitation — ARM64 local perf harness (follow‑up)
Because the fix must redirect
bin(MakePri moves the finalresources.priintobin, so an obj‑only redirect still hitsPRI210on the long in‑treebinpath — verified), the built.exefor 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/perfgate, which runs on x64 where the gate is inert andbinstays in‑tree. Teaching the harness to resolve the output via MSBuildTargetPathfor ARM64 dev boxes is folded into the broader ARM64 dev‑box follow‑up (#686).Closes #678. Draft pending merge coordination.