@echo off\r
\r
@rem ---------------------------------------------------------------------------\r
-@rem setup-windows.bat - provision a fresh Windows box for BlockBox development\r
+@rem setup-windows.bat - provision a fresh Windows box for native development\r
@rem ---------------------------------------------------------------------------\r
\r
-@rem --- Non-admin (per-user) installs + git config ---\r
+@rem --- Non-admin (per-user) winget installs ---\r
winget install Anthropic.ClaudeCode\r
winget install Git.Git\r
+winget install Microsoft.DotNet.SDK.10\r
winget install Microsoft.PowerShell Microsoft.Sysinternals.ProcessExplorer Microsoft.Sysinternals.ProcessMonitor Microsoft.Sysinternals.SDelete Microsoft.VisualStudioCode Microsoft.WindowsTerminal\r
+winget install Oracle.VirtualBox\r
winget install Python.Python.3.13\r
winget install WinMerge.WinMerge\r
-winget install WiXToolset.WiXCLI\r
\r
@rem OpenCppCoverage: native (PE) line coverage for the C++ binaries. run-coverage-occ.py drives the\r
-@rem pytest suite under it to produce an HTML report (BlockBox + the sandbox DLLs build with PDBs,\r
+@rem pytest suite under it to produce an HTML report (the binaries under test build with PDBs,\r
@rem which it reads). The installer elevates via UAC.\r
winget install OpenCppCoverage.OpenCppCoverage\r
\r
-@rem --- Add WinMerge to the user PATH (persists to the HKCU environment) ---\r
-@rem Runs non-elevated, so it updates THIS user's PATH (the elevated script runs\r
-@rem as a different account). Idempotent: only appends if not already present.\r
-powershell -NoProfile -Command "$c = @((Join-Path $env:ProgramFiles 'WinMerge'), (Join-Path ${env:ProgramFiles(x86)} 'WinMerge'), (Join-Path $env:LOCALAPPDATA 'Programs\WinMerge')); $d = $c | Where-Object { Test-Path (Join-Path $_ 'WinMergeU.exe') } | Select-Object -First 1; if (-not $d) { Write-Warning 'WinMerge not found; user PATH unchanged.'; exit 0 }; $u = [Environment]::GetEnvironmentVariable('Path','User'); if (-not $u) { $u = '' }; if (($u -split ';') -notcontains $d) { $new = if ($u.Trim()) { $u.TrimEnd(';') + ';' + $d } else { $d }; [Environment]::SetEnvironmentVariable('Path', $new, 'User'); Write-Host ('Added ' + $d + ' to user PATH (restart your shell to pick it up).') } else { Write-Host ($d + ' already in user PATH.') }"\r
-\r
-@rem --- BinSkim (binary hardening analyzer) - per-user install, no admin needed ---\r
-@rem BinSkim checks the exact mitigations we enable in CMakeLists.txt (CFG/XFG, CET,\r
-@rem ASLR/HighEntropyVA, DEP, /GS, stack cookies, DEPENDENTLOADFLAG, etc.). The\r
-@rem Microsoft.CodeAnalysis.BinSkim NuGet package ships a self-contained win-x64\r
-@rem build, so this needs no .NET SDK/runtime: download the .nupkg (a zip), extract\r
-@rem the win-x64 tool folder to %LOCALAPPDATA%\Programs\BinSkim, and add it to the\r
-@rem user PATH. After restarting the shell: binskim analyze path\to\BlockBox.exe\r
-@rem A failure here only warns (exit 0) so it never aborts the rest of provisioning.\r
-powershell -NoProfile -Command "try { $ErrorActionPreference='Stop'; [Net.ServicePointManager]::SecurityProtocol=[Net.SecurityProtocolType]::Tls12; $dest=Join-Path $env:LOCALAPPDATA 'Programs\BinSkim'; $tmp=Join-Path $env:TEMP ('binskim_'+[guid]::NewGuid().ToString('N')); New-Item -ItemType Directory -Force -Path $tmp | Out-Null; $zip=Join-Path $tmp 'binskim.zip'; Invoke-WebRequest -Uri 'https://www.nuget.org/api/v2/package/Microsoft.CodeAnalysis.BinSkim' -OutFile $zip; Expand-Archive -Path $zip -DestinationPath $tmp -Force; $exe=Get-ChildItem -Path $tmp -Recurse -Filter 'BinSkim.exe' | Where-Object { $_.FullName -match 'win-x64' } | Sort-Object FullName | Select-Object -Last 1; if (-not $exe) { throw 'BinSkim.exe (win-x64) not found in package.' }; if (Test-Path $dest) { Remove-Item -Recurse -Force $dest }; New-Item -ItemType Directory -Force -Path $dest | Out-Null; Copy-Item -Path (Join-Path $exe.Directory.FullName '*') -Destination $dest -Recurse -Force; Remove-Item -Recurse -Force $tmp; $u=[Environment]::GetEnvironmentVariable('Path','User'); if (-not $u) { $u='' }; if (($u -split ';') -notcontains $dest) { $new = if ($u.Trim()) { $u.TrimEnd(';')+';'+$dest } else { $dest }; [Environment]::SetEnvironmentVariable('Path',$new,'User'); Write-Host ('Added '+$dest+' to user PATH (restart your shell to pick it up).') } else { Write-Host ($dest+' already in user PATH.') }; Write-Host ('BinSkim installed to '+$dest) } catch { Write-Warning ('BinSkim install failed: '+$_.Exception.Message); exit 0 }"\r
+@rem --- WiX 5.0.2, pinned on purpose ---\r
+@rem WiX packages our proprietary software into MSIs, and the version is pinned to keep that\r
+@rem free of a fee. 5.0.2 is the last release distributed under the Microsoft Reciprocal\r
+@rem License alone. From 6.0 onward the package also carries OSMFEULA.txt, an Open Source\r
+@rem Maintenance Fee agreement: a monthly fee owed by anyone who uses the PREBUILT BINARIES\r
+@rem as part of revenue-generating activity and has annual gross revenue >= US$10,000.\r
+@rem\r
+@rem It is a fee for the binaries, not a restriction on what we ship - MS-RL is file-scoped\r
+@rem and never reached the MSIs WiX builds, under any version - but 5.0.2 owes nothing.\r
+@rem The 6.x/7.x SOURCE is still MS-RL too, so self-compiling is another way out; a pin is\r
+@rem the cheaper one. This replaces `winget install WiXToolset.WiXCLI`, which has no version\r
+@rem selector and so installs the latest (7.0.0 today, EULA and all).\r
+@rem\r
+@rem PIN THE MSBUILD SIDE TOO. A .wixproj referencing WixToolset.Sdk without a version\r
+@rem resolves to the latest - 7.x, same EULA - and nothing here constrains it. Pin it in the\r
+@rem project: <Project Sdk="WixToolset.Sdk/5.0.2">.\r
+@rem\r
+@rem dotnet.exe is called by full path: winget put the SDK on the machine PATH a few lines\r
+@rem ago, but this cmd session inherited its environment before that and cannot see it.\r
+@rem install-then-update is for re-runs - install fails once the tool is there, and update\r
+@rem then holds it at exactly 5.0.2 - which keeps this script idempotent like the rest.\r
+set "DOTNET_EXE=%ProgramFiles%\dotnet\dotnet.exe"\r
+"%DOTNET_EXE%" tool install --global wix --version 5.0.2 || "%DOTNET_EXE%" tool update --global wix --version 5.0.2\r
\r
-@rem --- Global git identity: EDIT THESE BEFORE RUNNING ---\r
-@rem Replace the placeholders with your own name and email, or comment the two\r
-@rem lines out and set your identity per-repository instead.\r
-git config --global user.name "PLACEHOLDER_NAME"\r
-git config --global user.email "PLACEHOLDER_EMAIL"\r
-git config --global core.sshcommand C:/Windows/System32/OpenSSH/ssh.exe\r
+@rem Report what the pin actually produced. By full path again, and because the shim lands in\r
+@rem a directory this session's PATH predates: expect "5.0.2+<commit>", not 7.x.\r
+"%USERPROFILE%\.dotnet\tools\wix.exe" --version\r
\r
@rem ---------------------------------------------------------------------------\r
@rem No package manager needed for the Windows build\r
@rem --- Elevated installs (VS2022, WDK, system tools) ---\r
@rem The elevated script runs in its own window and logs to setup-windows-uac.log.\r
@rem -PassThru + $p.ExitCode propagates its real exit code back through to ERRORLEVEL.\r
+@rem\r
+@rem -TraceUser passes YOU across the UAC boundary. Accepting that prompt with an\r
+@rem administrator's credentials runs the elevated half AS that administrator, so\r
+@rem it cannot see whose box this is; the account named here is the one it grants\r
+@rem non-elevated ETW collection rights to (xperf / wpr without a UAC prompt).\r
+@rem\r
+@rem The two values go through the environment, and the quotes the child needs\r
+@rem around them are built in PowerShell as [char]34, so the command line below\r
+@rem contains no embedded quote characters at all. Writing them inline as ""..""\r
+@rem works for ONE argument and quietly breaks at two: the quote-state parsing\r
+@rem swallows everything after the first into the -File value, and the elevated\r
+@rem PowerShell dies with "failed because the file does not have a '.ps1'\r
+@rem extension" and exit code -196608 (0xFFFD0000) before it can log a thing.\r
set "UAC_LOG=%~dp0setup-windows-uac.log"\r
if exist "%UAC_LOG%" del "%UAC_LOG%"\r
+set "UAC_SCRIPT=%~dp0setup-windows-with-uac.ps1"\r
+set "UAC_TRACE_USER=%USERDOMAIN%\%USERNAME%"\r
\r
-powershell -NoProfile -Command "$p = Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File','""%~dp0setup-windows-with-uac.ps1""' -Wait -PassThru; exit $p.ExitCode"\r
+powershell -NoProfile -Command "$q = [char]34; $p = Start-Process powershell -Verb RunAs -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File',($q + $env:UAC_SCRIPT + $q),'-TraceUser',($q + $env:UAC_TRACE_USER + $q) -Wait -PassThru; exit $p.ExitCode"\r
set "UAC_RC=%ERRORLEVEL%"\r
\r
@rem --- Surface the elevated session's output (its window has already closed) ---\r
echo [setup-windows] The elevated window may have been cancelled at the UAC prompt.\r
)\r
\r
+@rem --- Non-elevated PowerShell half ---\r
+@rem WinMerge on the user PATH, BinSkim, and the global git config (identity +\r
+@rem core.sshCommand -> a Win32-OpenSSH client, so git shares the Windows\r
+@rem ssh-agent). EDIT THE GIT IDENTITY at the top of setup-windows-no-uac.ps1\r
+@rem before the first run.\r
+@rem\r
+@rem Deliberately NOT elevated: every step writes per-user state (the HKCU PATH,\r
+@rem the .gitconfig under %USERPROFILE%), which the elevated half would write to\r
+@rem the administrator profile instead.\r
+@rem\r
+@rem AFTER the elevated half on purpose: core.sshCommand prefers the ssh.exe that\r
+@rem half unpacks beside rsync.exe - a push through the in-box client is capped\r
+@rem at ~17MB/s - and it can only prefer it once it is on disk. Run either way,\r
+@rem including when the elevated half failed above: nothing here depends on it,\r
+@rem and the fallback is the in-box client that Windows already has.\r
+@rem\r
+@rem Non-fatal: these are conveniences, and the elevated half is the part worth\r
+@rem the UAC prompt. A failure warns and provisioning continues.\r
+powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0setup-windows-no-uac.ps1"\r
+if not "%ERRORLEVEL%"=="0" echo [setup-windows] WARNING: setup-windows-no-uac.ps1 reported a failure ^(see above^); continuing.\r
+\r
if not "%UAC_RC%"=="0" (\r
echo.\r
echo [setup-windows] ELEVATED SETUP FAILED ^(exit code %UAC_RC%^). See log above.\r