dotfiles: make the Windows setup work natively on ARM64
Run on Windows 11 on Arm as well as x64, preferring native ARM64 builds
everywhere one exists and saying plainly what is left emulated and why.
Two bugs found on an ARM64 box, both pre-existing:
- vswhere was queried unscoped, so `-products *` returned whichever VS
was newest and handed that path to a bootstrapper of a different
generation. On a machine with VS 2026 installed, a 17.x bootstrapper
was told to `modify --installPath <an 18.x install>` and every pass
targeted the wrong product. Get-VsInstallPath now scopes by version.
- the WDK probe read only the WOW6432Node hive. Which hive a kit lands
in has varied, and reading one made an installed WDK look absent,
costing a needless multi-GB reinstall on every run. Read both.
Visual Studio:
- $VsChannel splits by architecture - 17 (VS 2022) on x64, where the
v141/XP toolset is the reason to pin a generation, and 18 (VS 2026)
on ARM64, where that toolset cannot run at all.
- the aka.ms path segment differs per generation: /release/ for 17,
/stable/ for 18. This is not cosmetic - aka.ms/vs/18/release/... is
not a 404, it redirects to Bing and returns 200 with HTML, so a wrong
guess downloads a web page and fails later at Start-Process. The
download is now checked for an MZ header.
- name VC.Tools.x86.x64 and VC.Tools.ARM64 explicitly rather than
relying on --includeRecommended, which resolves differently per host:
on Arm the x64 cross-compiler is not implied. Same for ATL/Spectre.
- the v141/XP group is skipped on Arm; Clang/LLVM is installed on both.
- verification widened from "did v141 land" to which MSVC host
toolchains, whether clang-cl is native, and v141 on x64 only.
Compiler diversity: MSVC and clang-cl from Visual Studio, plus upstream
LLVM (resolves to LLVM-<ver>-woa64.exe on Arm). Two Clang majors on
independent schedules, all three native.
Native utilities, verified by reading PE COFF headers rather than
trusting package metadata:
- Visual Studio bundles an x64 ninja.exe even on ARM64, and VsDevCmd
prepends its directory, so the emulated one wins inside a Developer
prompt - the shell C++ builds run in. Ninja is re-invoked per edge in
the build graph, so this is the one place emulation is paid over and
over. Install the native build and document the PATH precedence.
- add standalone CMake: VS's copy is already ARM64 but only reaches the
PATH inside a Developer prompt, and the build line here assumes a
bare `cmake`.
- Sysinternals ships every architecture in one zip; the ARM64 build is
procexp64a.exe, not the default-named procexp.exe.
rsync: no ARM64 asset exists, so Arm takes the x64 zip (a transfer is
socket-bound, not CPU-bound). Its ssh.exe links against a System32
libcrypto.dll that is an ARM64 binary there, so it may not load at all -
and rsync PREFERS an ssh.exe in its own directory, meaning a broken one
does not degrade to the in-box client, it breaks rsync outright. Run it
after unpacking and delete it if it will not start.
VirtualBox is skipped on Arm (a hypervisor is kernel-mode; its x64 driver
cannot load on an ARM64 kernel), which also makes the Windows 7 x86 test
VM unreachable. VTune reports n/a rather than a download link.
setup-windows-no-uac.ps1 gains an ArchAudit step that reports the real
machine type of every provisioned tool, resolved PATH-first. Emulated
with a listed reason prints as expected; emulated without one is flagged
with a remedy. That is what found the ninja problem.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ak6VzfGyaocrykX1LhNMwt