- OpenSSH Server (sshd) capability: automatic + started + inbound TCP 22\r
- rsync for Windows (nuket/rsync-windows) in C:\Tools\rsync, on the machine\r
PATH: rsync.exe plus the ssh.exe it runs, out of the release zip for this\r
- architecture (there is no ARM64 zip; ARM64 gets the x64 one, and the\r
- ssh.exe is kept only if it actually starts)\r
+ architecture - x86, x64 and arm64 are all published, so both binaries are\r
+ native everywhere. The ssh.exe is kept only if it actually starts.\r
- Visual Studio Community (C++ desktop workload, x86/x64 AND ARM64 build\r
- tools, Spectre libs, WDK VSIX, Win11 SDK 26100, Clang/LLVM, and - on x64\r
- only - the v141 + Windows XP targeting toolset). VS 2022 on x64, VS 2026\r
- on ARM64; see $VsChannel.\r
- - Windows Driver Kit 10.0.26100\r
+ tools, Spectre libs, Win11 SDK 26100, and - on x64 only - the WDK VSIX,\r
+ Clang/LLVM, and the v141 + Windows XP targeting toolset). VS 2022 on x64,\r
+ VS 2026 on ARM64; see $VsChannel.\r
+ - Windows Driver Kit 10.0.26100 (x64 only)\r
- Windows Performance Toolkit - xperf, wpr and Windows Performance Analyzer\r
(wpa.exe) - on the machine PATH\r
\r
18 (VS 2026) on ARM64, where that toolset cannot exist anyway.\r
\r
ARCHITECTURE. Runs on x64 and on ARM64 (Windows 11 on Arm). On ARM64 the\r
- Visual Studio installer, MSVC and clang-cl are all native ARM64 and\r
- cross-compile ARM64/x64/x86 targets; what changes is called out at each step,\r
- and $HostArch below is what drives it.\r
+ Visual Studio installer and MSVC are native ARM64 and cross-compile\r
+ ARM64/x64/x86 targets.\r
+\r
+ The two architectures deliberately provision DIFFERENT things. x64 is the full\r
+ workstation; ARM64 is a constrained VM set up as a single-compiler build box -\r
+ MSVC only, with clang-cl and the WDK dropped there. Neither is dropped for lack\r
+ of an ARM64 build; see the comments on $ClangComponents and the WDK step.\r
+ What changes is called out at each step, and $HostArch below is what drives it.\r
#>\r
\r
$ErrorActionPreference = 'Stop'\r
# ---------------------------------------------------------------------------\r
# Host architecture\r
#\r
-# RuntimeInformation.OSArchitecture rather than PROCESSOR_ARCHITECTURE: this\r
-# script can be launched by a 32-bit or an emulated x64 PowerShell, either of\r
-# which reports the emulated architecture in the environment variable while this\r
-# API still reports the real one. Values seen here: X64, Arm64, X86.\r
+# Read from the machine environment in the registry, because this script can be\r
+# launched by a 32-bit or an emulated x64 PowerShell and BOTH of the obvious\r
+# sources report the emulated architecture in that case. Measured on this box,\r
+# from the x64 PowerShell an x64 shell resolves to:\r
+# [RuntimeInformation]::OSArchitecture X64 <- wrong\r
+# $env:PROCESSOR_ARCHITECTURE AMD64 <- wrong\r
+# HKLM\...\Session Manager\Environment ARM64 <- right\r
+# OSArchitecture is documented as the OS's architecture, and on .NET Core it is;\r
+# under .NET Framework on Prism it is not, so it is kept only as a fallback.\r
#\r
# $IsArm64 gates:\r
-# - which rsync-windows release zip is fetched (there is no ARM64 one)\r
+# - which rsync-windows release zip is fetched (x86 / x64 / arm64, all native)\r
# - which Visual Studio generation is driven ($VsChannel: 18 on Arm, 17 on x64)\r
-# - the Visual Studio component groups (ARM64 build tools in; the v141/XP\r
-# toolset out, because it has no ARM64-hosted compiler and Microsoft does not\r
-# ship Windows XP targeting for Arm hosts. Clang/LLVM is installed on every\r
-# architecture and is native on Arm, so it is NOT gated here)\r
+# - the Visual Studio component groups: ARM64 build tools in; the v141/XP\r
+# toolset out because it has no ARM64-hosted compiler and Microsoft does not\r
+# ship Windows XP targeting for Arm hosts; Clang/LLVM and the WDK VSIX out by\r
+# policy rather than by limitation - both are available and native on Arm\r
+# - whether the Windows Driver Kit is installed at all\r
# - what the VTune and WPT steps report\r
# ---------------------------------------------------------------------------\r
-$HostArch = [Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString()\r
-$IsArm64 = ($HostArch -eq 'Arm64')\r
+$rawArch = (Get-ItemProperty 'HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager\Environment' -ErrorAction SilentlyContinue).PROCESSOR_ARCHITECTURE\r
+if (-not $rawArch) { $rawArch = [Runtime.InteropServices.RuntimeInformation]::OSArchitecture.ToString() }\r
+$IsArm64 = ($rawArch -eq 'ARM64')\r
+$HostArch = if ($IsArm64) { 'Arm64' } elseif ($rawArch -eq 'AMD64') { 'X64' } else { $rawArch }\r
\r
function Write-Step([string]$Msg) {\r
Write-Host "`n==> $Msg" -ForegroundColor Cyan\r
# ---------------------------------------------------------------------------\r
Write-Step 'rsync for Windows'\r
$RsyncRepo = 'nuket/rsync-windows'\r
-# The release publishes exactly two assets - x64 and x86 - and no ARM64 one, so\r
-# on ARM64 the x64 build is the right pick: it runs under Prism, and an emulated\r
-# x64 rsync still moves data far faster than the ~17MB/s stdin cap that the whole\r
-# reason for using this build is to avoid. (The transfer is I/O-bound on the\r
-# socket, not on emulated CPU.) Selected off $HostArch rather than\r
-# Is64BitOperatingSystem, which answers "true" on ARM64 and so cannot tell the\r
-# two 64-bit cases apart.\r
+# The release publishes x86, x64 and - since v3.5.0-gdeeda96f - arm64, so every\r
+# architecture this script runs on gets a native build. ARM64 used to take the\r
+# x64 zip under emulation, which was defensible (a transfer is bound by the\r
+# socket, not by emulated CPU) but cost the release's ssh.exe: that x64 binary\r
+# could not load the ARM64 libcrypto.dll in System32 and had to be deleted on\r
+# every run. The arm64 asset ships an ARM64 rsync.exe AND an ARM64 ssh.exe, so\r
+# both halves are now native and the fast client survives.\r
+#\r
+# Selected off $HostArch rather than Is64BitOperatingSystem, which answers "true"\r
+# on ARM64 and so cannot tell the two 64-bit cases apart.\r
$RsyncAsset = switch ($HostArch) {\r
'X64' { 'rsync-windows-x64.zip' }\r
- 'Arm64' { 'rsync-windows-x64.zip' }\r
+ 'Arm64' { 'rsync-windows-arm64.zip' }\r
default { 'rsync-windows-x86.zip' }\r
}\r
if ($IsArm64) {\r
- Write-Host ' ARM64: no native asset is published, using the x64 build under emulation.' -ForegroundColor Yellow\r
+ Write-Host ' ARM64: using the native arm64 asset (rsync.exe and ssh.exe both ARM64).' -ForegroundColor Green\r
}\r
# The /releases/latest/download/ redirect rather than the API: unauthenticated\r
# API calls are rate-limited to 60/hour per IP, which a provisioning run behind a\r
if ($v -and ([version]($v -replace '[^0-9.]', '')) -lt [version]'3.8.2') {\r
Write-Warning "$SysCrypto is LibreSSL $v; the release's ssh.exe is built against 3.8.2 (Windows OpenSSH Client 9.5). Update Windows, or expect ssh.exe not to start."\r
}\r
- if ($IsArm64) {\r
- # On ARM64 the presence of libcrypto.dll proves less than it does on\r
- # x64: System32 holds the ARM64 build of it, and the ssh.exe in the\r
- # zip is x64. Whether an emulated x64 process can load that DLL comes\r
- # down to whether it is a plain ARM64 binary or an ARM64X one - not\r
- # something worth deciding by parsing the PE header, when running the\r
- # binary answers it outright. Unpack it, then run it (below).\r
- Write-Host ' ARM64: the x64 ssh.exe will be verified by running it, not by assuming.' -ForegroundColor Yellow\r
- }\r
+ # No architecture special-case here any more: the asset chosen above\r
+ # matches the host, so the release's ssh.exe and System32's libcrypto.dll\r
+ # are the same architecture on every box. The run-it check below still\r
+ # happens on all of them - it is cheap, and it is what caught the ARM64\r
+ # mismatch back when this took the x64 zip.\r
}\r
\r
# Download and unpack beside the targets, not over them, so an interrupted\r
# This matters more than it looks. rsync.exe prefers an ssh.exe sitting in its\r
# own directory, so a present-but-unstartable one does not degrade to the\r
# in-box client - it breaks rsync outright, and the error you get is a remote\r
- # shell that died rather than anything naming ssh.exe. The two ways to land\r
- # there are a missing/old System32 libcrypto.dll (x64 boxes) and an ARM64 box\r
- # whose ARM64 libcrypto cannot be loaded by this x64 binary. Removing it is\r
- # the repair in both cases: rsync then falls back to the ssh on the PATH,\r
- # which on ARM64 is the native in-box client.\r
+ # shell that died rather than anything naming ssh.exe. The way to land there\r
+ # is a missing or too-old System32 libcrypto.dll. Removing it is the repair:\r
+ # rsync then falls back to the ssh on the PATH, which is the in-box client.\r
+ #\r
+ # This also used to fire on every ARM64 run, when the x64 asset was the only\r
+ # 64-bit one published and its ssh.exe could not load the ARM64 libcrypto\r
+ # (exit 0xC0000135, STATUS_DLL_NOT_FOUND). The arm64 asset fixed that at the\r
+ # source; the check stays because it is how that was found in the first place.\r
#\r
# EAP back to Continue for the call: ssh -V writes its version to STDERR, and\r
# under $ErrorActionPreference = 'Stop' a native command's stderr becomes a\r
# $global:LASTEXITCODE is cleared first because an exe that cannot start at\r
# all throws without setting one, and the stale 0 from the previous native\r
# command would otherwise read as success.\r
+ #\r
+ # DRAIN THE OUTPUT BEFORE READING $LASTEXITCODE. `... | Select-Object -First 1`\r
+ # was the original spelling here, and it is a trap: Select-Object stops the\r
+ # pipeline the instant it has its one object, and that is when ssh.exe has\r
+ # WRITTEN its banner - not when it has exited. PowerShell then tears the\r
+ # pipeline down, kills the still-running process, and reports -1. Measured on a\r
+ # healthy binary on an x64 box: 24 of 25 runs reported exit -1 through\r
+ # Select-Object, and 25 of 25 reported 0 when the output was collected first.\r
+ #\r
+ # So this check was deleting a perfectly good ssh.exe on almost every run, on\r
+ # every architecture, and the resulting fallback to the in-box client - the\r
+ # ~17MB/s one this whole step exists to avoid - looked like a property of the\r
+ # release rather than a bug in the test. Collect, THEN take the first line.\r
$SshExe = Join-Path $RsyncDir 'ssh.exe'\r
if ($WantSsh -and (Test-Path $SshExe)) {\r
$prevEap = $ErrorActionPreference\r
$ErrorActionPreference = 'Continue'\r
$global:LASTEXITCODE = $null\r
$sshVer = $null\r
- try { $sshVer = (& $SshExe -V 2>&1 | Select-Object -First 1) } catch { }\r
+ try { $sshOut = (& $SshExe -V 2>&1); $sshVer = $sshOut | Select-Object -First 1 } catch { }\r
finally { $ErrorActionPreference = $prevEap }\r
if ($LASTEXITCODE -eq 0) {\r
Write-Host " ssh.exe runs: $sshVer"\r
\r
# Windows 11 SDK — build number must match the WDK below\r
'Microsoft.VisualStudio.Component.Windows11SDK.26100'\r
-\r
- # WDK Visual Studio extension (VSIX). The silent wdksetup.exe /quiet does NOT\r
- # install this (it only prompts interactively), so it must be added here.\r
- 'Component.Microsoft.Windows.DriverKit'\r
)\r
\r
+# WDK Visual Studio extension (VSIX). The silent wdksetup.exe /quiet does NOT\r
+# install this (it only prompts interactively), so it has to be asked for here.\r
+#\r
+# Follows the WDK itself: x64 only. On ARM64 the kit is not installed (see the\r
+# WDK step for why), and this VSIX on its own is just the driver project\r
+# templates and property pages with no headers, libs or tools behind them.\r
+if (-not $IsArm64) {\r
+ $BaseComponents += 'Component.Microsoft.Windows.DriverKit'\r
+}\r
+\r
# Clang/LLVM toolset (ClangCL, used in CMakePresets.json). Two parts: the Clang\r
# compiler itself, plus the MSBuild integration providing the "ClangCL" toolset.\r
#\r
-# Installed on every architecture, ARM64 included, and native there - not an\r
-# emulated x64 compiler. Two things establish that: the VSIX is\r
+# x64 ONLY, and NOT because ARM64 cannot have it - it can, natively. The VSIX is\r
# productArch=neutral with no chip/machineArch restriction, so the Arm installer\r
-# offers it; and MSVC's VC\Tools\Llvm tree is partitioned by HOST architecture\r
-# (bin = x86, x64\bin = x64, ARM64\bin = ARM64) with genuine ARM64 binaries\r
-# already in ARM64\bin, which is where clang-cl.exe lands. Unlike the v141/XP\r
-# group below, nothing technical is in the way.\r
+# offers it, and MSVC's VC\Tools\Llvm tree is partitioned by HOST architecture\r
+# (bin = x86, x64\bin = x64, ARM64\bin = ARM64) with genuine ARM64 binaries in\r
+# ARM64\bin, which is where clang-cl.exe lands. It was installed and working here\r
+# before this split.\r
#\r
-# This is the compiler diversity the box is after: MSVC and clang-cl over the\r
-# same sources, both native.\r
-$ClangComponents = @(\r
- 'Microsoft.VisualStudio.Component.VC.Llvm.Clang'\r
- 'Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset'\r
-)\r
+# It is dropped on ARM64 because that machine is a constrained VM provisioned as\r
+# a single-compiler build box: MSVC and nothing else. Compiler diversity - MSVC,\r
+# VS clang-cl and upstream LLVM over the same sources - now lives entirely on the\r
+# x64 machine, which is the one with room for three toolchains. Empty here means\r
+# Invoke-VsModify is never called for this group.\r
+$ClangComponents = if ($IsArm64) { @() } else {\r
+ @(\r
+ 'Microsoft.VisualStudio.Component.VC.Llvm.Clang'\r
+ 'Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset'\r
+ )\r
+}\r
\r
# Windows XP targeting (v141_xp toolset, used in CMakePresets.json). The v141\r
# (VS2017) build tools provide the 14.16 compiler that the XP toolset wraps;\r
# offered a hard failure here would cost you the whole toolchain over a component\r
# you can add later from the installer UI.\r
Invoke-VsModify -Label 'base toolset + workload' -Ids $BaseComponents\r
-Invoke-VsModify -Label 'Clang / LLVM' -Ids $ClangComponents -Optional\r
+if ($IsArm64) {\r
+ Write-Step 'Visual Studio: Clang / LLVM'\r
+ Write-Host ' Skipped on ARM64 by choice, not by limitation: a native ARM64 clang-cl is' -ForegroundColor Yellow\r
+ Write-Host ' offered here and works. This box is provisioned with MSVC alone; the' -ForegroundColor Yellow\r
+ Write-Host ' multi-compiler builds belong on the x64 machine.' -ForegroundColor Yellow\r
+} else {\r
+ Invoke-VsModify -Label 'Clang / LLVM' -Ids $ClangComponents -Optional\r
+}\r
if ($IsArm64) {\r
Write-Step 'Visual Studio: Windows XP (v141 + WinXP)'\r
Write-Host ' Skipped on ARM64: the v141 (14.16) toolset ships x86/x64-hosted compilers only,' -ForegroundColor Yellow\r
$clangArm = Test-Path (Join-Path $llvmRoot 'ARM64\bin\clang-cl.exe')\r
$clangX64 = Test-Path (Join-Path $llvmRoot 'x64\bin\clang-cl.exe')\r
$clangX86 = Test-Path (Join-Path $llvmRoot 'bin\clang-cl.exe')\r
- if ($clangArm -or $clangX64 -or $clangX86) {\r
- Write-Host " clang-cl: $(@(if ($clangArm) {'ARM64'}; if ($clangX64) {'x64'}; if ($clangX86) {'x86'}) -join ', ')" -ForegroundColor Green\r
- # On ARM64 the x64 build would still run, under emulation - so say plainly\r
- # whether the NATIVE one is the one that landed.\r
- if ($IsArm64 -and -not $clangArm) {\r
- Write-Warning 'No ARM64-hosted clang-cl - the x64 one would run under emulation. Re-run the Clang/LLVM pass, or add "C++ Clang tools for Windows" in the installer.'\r
+ $clangAny = $clangArm -or $clangX64 -or $clangX86\r
+ if ($IsArm64) {\r
+ # Not installed here by design, so its absence is the expected result and\r
+ # must not read as a fault. Its PRESENCE is the thing worth a line: a box\r
+ # provisioned before this split still carries it, and the component stays\r
+ # until it is explicitly removed - the installer never uninstalls what you\r
+ # simply stopped asking for.\r
+ if ($clangAny) {\r
+ Write-Host ' clang-cl: present but no longer provisioned on ARM64 - left over from an earlier run.' -ForegroundColor Yellow\r
+ Write-Host ' Remove it with: vs_installer.exe modify --installPath "<path>" --remove Microsoft.VisualStudio.Component.VC.Llvm.Clang --remove Microsoft.VisualStudio.Component.VC.Llvm.ClangToolset --quiet' -ForegroundColor Yellow\r
+ } else {\r
+ Write-Host ' clang-cl: n/a on ARM64 (MSVC only by design; use the x64 box for ClangCL).' -ForegroundColor DarkGray\r
}\r
+ } elseif ($clangAny) {\r
+ Write-Host " clang-cl: $(@(if ($clangArm) {'ARM64'}; if ($clangX64) {'x64'}; if ($clangX86) {'x86'}) -join ', ')" -ForegroundColor Green\r
} else {\r
Write-Warning 'clang-cl not found - the ClangCL presets will fail. Add the "C++ Clang tools for Windows" component.'\r
}\r
# Build 26100 matches the Windows 11 SDK installed above.\r
# Provides IddCx (iddcx.h / iddcx.lib) and UMDF 2.x for Indirect Display Drivers.\r
# linkid=2335869 -> WDK 26100.6584 (per Microsoft "Other WDK Downloads").\r
+#\r
+# x64 ONLY. Two reasons, and the second is the one that decided it:\r
+#\r
+# 1. The ARM64 box builds user-mode software; no driver is developed on it.\r
+#\r
+# 2. This exact WDK cannot install next to the SDK that VS 2026 brings. The\r
+# WDK requires the MATCHING Windows SDK revision, and revisions are not\r
+# side-by-side - Include\10.0.26100.0 is one directory whichever revision\r
+# wrote it. VS 2026 installs SDK 26100.7705; linkid=2335869 is WDK\r
+# 26100.6584, which is the kit paired with VS 2022. So wdksetup.exe aborts:\r
+# exit 15605, WER signature "WDK / 10.1.26100.6584 / Install / 0x80070642"\r
+# (0x642 = 1602, ERROR_INSTALL_USEREXIT - a silent install cancelling\r
+# itself on a failed prerequisite check). It is not an ARM64 or emulation\r
+# problem; the same pairing fails on x64.\r
+#\r
+# If a driver ever does need building here, the fix is the WDK that matches\r
+# this generation - Microsoft's "Supported and other WDK downloads" table\r
+# pairs VS 2026 with the 28000.x kit - not this link.\r
+#\r
+# On x64 with VS 2022 the SDK and this WDK are the matched pair, so it installs.\r
# ---------------------------------------------------------------------------\r
$WdkVersion = '10.0.26100'\r
# Both hives. The WDK installer is a 32-bit program, so on x64 it writes under\r
) | ForEach-Object { (Get-ItemProperty $_ -ErrorAction SilentlyContinue).WdkBinRootVersioned } |\r
Where-Object { $_ } | Select-Object -First 1\r
\r
-if ($WdkInstalledRoot -and $WdkInstalledRoot -match [regex]::Escape($WdkVersion)) {\r
+if ($IsArm64) {\r
+ Write-Step 'Windows Driver Kit'\r
+ Write-Host ' Skipped on ARM64: this box builds user-mode software only, and WDK 26100.6584' -ForegroundColor Yellow\r
+ Write-Host ' cannot install beside the 26100.7705 SDK that Visual Studio 2026 brings - the' -ForegroundColor Yellow\r
+ Write-Host ' kit needs the matching SDK revision and they are not side-by-side. See the' -ForegroundColor Yellow\r
+ Write-Host ' comment above this step for the exit code and what to use instead.' -ForegroundColor Yellow\r
+} elseif ($WdkInstalledRoot -and $WdkInstalledRoot -match [regex]::Escape($WdkVersion)) {\r
# Re-running wdksetup.exe for an already-present version returns exit code\r
# 2008 (maintenance mode / nothing to do), which is not a real failure.\r
Write-Step "WDK $WdkVersion already installed - skipping ($WdkInstalledRoot)"\r