bdfab8e0d5
windows-drivers / probe-and-proto (push) Successful in 24s
apple / swift (push) Successful in 1m4s
windows-drivers / driver-build (push) Successful in 1m8s
android / android (push) Successful in 4m4s
ci / rust (push) Successful in 4m39s
ci / web (push) Successful in 50s
ci / docs-site (push) Successful in 53s
apple / screenshots (push) Successful in 5m10s
windows-host / package (push) Failing after 5m35s
deb / build-publish (push) Successful in 2m29s
decky / build-publish (push) Successful in 13s
docker / build-push (--build-arg FEDORA_VERSION=44, ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora44-rpm) (push) Successful in 5s
docker / build-push (., web/Dockerfile, punktfunk-web) (push) Successful in 5s
docker / build-push (ci, ci/fedora-rpm.Dockerfile, punktfunk-fedora-rpm) (push) Successful in 4s
docker / build-push (ci, ci/rust-ci.Dockerfile, punktfunk-rust-ci) (push) Successful in 4s
docker / build-push (docs-site, docs-site/Dockerfile, punktfunk-docs) (push) Successful in 3s
ci / bench (push) Successful in 4m42s
rpm / build-publish (bazzite, punktfunk-fedora-rpm) (push) Successful in 8m57s
docker / deploy-docs (push) Successful in 17s
rpm / build-publish (fedora-44, punktfunk-fedora44-rpm) (push) Successful in 8m46s
The pf-vdisplay virtual-display driver shipped as a checked-in PREBUILT binary
that went stale - two field failures on a fresh install (live-repro'd on a
German-locale Dell laptop):
* Bug A (every box): a repo-wide rename edited the vendored pf_vdisplay.inf
but never re-signed pf_vdisplay.cat, so the catalog stopped covering the INF
-> `pnputil /add-driver` fails SPAPI_E_FILE_HASH_NOT_IN_CATALOG -> driver
never installs -> every session dies "pf-vdisplay driver interface not
found".
* the prebuilt binary also predated IOCTL_SET_RENDER_ADAPTER (added to the
driver source after the vendor freeze) that the host needs to pin the IDD
render GPU on hybrid/Optimus boxes.
Fix: build the driver FROM SOURCE every release (build-pf-vdisplay.ps1, wired
into pack-host-installer.ps1) so .dll/.inf/.cat are always in lockstep and
current driver features ship. The runner's clang 22 made the driver's pinned
bindgen 0.71 emit opaque structs (157 layout-assert errors), so bump the
vendored wdk-sys/wdk-build bindgen 0.71 -> 0.72 (+ lock). The build self-signs
the driver per build (installer trusts the bundled .cer); a stable
DRIVER_CERT_PFX_B64 secret can override.
* Bug B (non-English boxes): the installer runs install-pf-vdisplay.ps1 etc.
via powershell.exe (5.1), which reads a BOM-less script in the ANSI codepage
- an em-dash's trailing 0x94 byte becomes a curly quote on German
Windows-1252 and the script aborts "unterminated string", so the driver
never installed (the gamepad script survived only because it was already
ASCII). Scrub every installer-run .ps1/.cmd to ASCII + add a CI gate that
fails on any non-ASCII so it can't regress.
* Bug C (upgrades): nothing stopped the OLD web console before re-registering
its task, so a stale server kept :3000 (the new one restart-looped on
EADDRINUSE) and served a broken old bundle (500 on /login). Stop + reap it
(runtime-agnostic, by the :3000 listener owner) in web-setup.ps1 and in the
.iss before the file copy + on uninstall.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
138 lines
7.8 KiB
PowerShell
138 lines
7.8 KiB
PowerShell
<#
|
|
.SYNOPSIS
|
|
Build + sign the pf-vdisplay UMDF IddCx virtual-display driver FROM SOURCE, in CI, and stage it for the
|
|
host installer. This REPLACES the old vendored-prebuilt-binary model (packaging/windows/pf-vdisplay/) -
|
|
the binary went stale (frozen mid-June while the driver source kept moving), which silently shipped two
|
|
field bugs: (1) the catalog no longer covered the edited INF (pnputil SPAPI_E_FILE_HASH_NOT_IN_CATALOG on
|
|
every box), and (2) the binary predated IOCTL_SET_RENDER_ADAPTER that the host needs to pin the IDD render
|
|
GPU on hybrid/Optimus boxes. Building every release from source keeps the .dll/.inf/.cat in lockstep and
|
|
ships current driver features.
|
|
|
|
.DESCRIPTION
|
|
Mirrors packaging/windows/drivers/deploy-dev.ps1 but for CI (release build, output to -Out, cert from a
|
|
secret OR a fresh self-signed). Steps: cargo build (the wdk-sys/windows-drivers-rs driver workspace) ->
|
|
CLEAR the FORCE_INTEGRITY PE bit (wdk-build links /INTEGRITYCHECK, which a non-EV cert can't satisfy) ->
|
|
sign the .dll -> stampinf a strictly-increasing DriverVer into the INF -> Inf2Cat the catalog -> sign the
|
|
catalog -> export the public .cer. Output (-Out): pf_vdisplay.{dll,inf,cat} + punktfunk-driver.cer.
|
|
|
|
Requires the WDK build env: cargo + the x64 MSVC toolset, an LLVM compatible with the driver's bindgen
|
|
(>= 0.72 supports current clang), LIBCLANG_PATH, and the Windows 10/11 WDK (the runner has these). Sets
|
|
Version_Number for wdk-build if the caller didn't.
|
|
|
|
.EXAMPLE
|
|
pwsh -File build-pf-vdisplay.ps1 -Out C:\t\pfvd -DriverVer 9.9.0626.1612
|
|
#>
|
|
[CmdletBinding()]
|
|
param(
|
|
[string]$DriversDir = (Join-Path $PSScriptRoot 'drivers'),
|
|
[Parameter(Mandatory = $true)][string]$Out,
|
|
[string]$DriverVer, # default: 9.9.MMdd.HHmm (strictly-increasing)
|
|
[string]$CertPfxB64 = $env:DRIVER_CERT_PFX_B64, # optional stable driver-signing cert (CI secret)
|
|
[string]$CertPassword = $env:DRIVER_CERT_PASSWORD,
|
|
[switch]$SkipBuild # reuse an existing target\...\release\pf_vdisplay.dll
|
|
)
|
|
$ErrorActionPreference = 'Stop'
|
|
$ProgressPreference = 'SilentlyContinue'
|
|
$PSNativeCommandUseErrorActionPreference = $false
|
|
|
|
$DriversDir = (Resolve-Path $DriversDir).Path
|
|
$inx = Join-Path $DriversDir 'pf-vdisplay\pf_vdisplay.inx'
|
|
$clear = Join-Path $PSScriptRoot 'clear-force-integrity.ps1'
|
|
if (-not (Test-Path $inx)) { throw "no pf_vdisplay.inx under $DriversDir" }
|
|
|
|
# --- WDK build env (wdk-build needs Version_Number; bindgen needs LIBCLANG_PATH) --------------
|
|
if (-not $env:Version_Number) { $env:Version_Number = '10.0.26100.0' }
|
|
if (-not $env:LIBCLANG_PATH -and (Test-Path 'C:\Program Files\LLVM\bin\libclang.dll')) {
|
|
$env:LIBCLANG_PATH = 'C:\Program Files\LLVM\bin'
|
|
}
|
|
# Isolate the driver's CARGO_TARGET_DIR from the host's (CI sets a shared C:\t): the driver is a
|
|
# SEPARATE workspace (own [workspace] + an explicit --target via .cargo/config), so give it its own
|
|
# output tree both to avoid cross-workspace churn and to make the .dll path predictable here.
|
|
$drvTarget = Join-Path (Split-Path -Parent $Out) 'pfvd-target'
|
|
$dll = Join-Path $drvTarget 'x86_64-pc-windows-msvc\release\pf_vdisplay.dll'
|
|
|
|
# --- 1. build (release) -----------------------------------------------------------------------
|
|
if (-not $SkipBuild) {
|
|
Write-Host "==> cargo build --release (pf-vdisplay) in $DriversDir (target -> $drvTarget)"
|
|
$prevTarget = $env:CARGO_TARGET_DIR
|
|
$env:CARGO_TARGET_DIR = $drvTarget
|
|
Push-Location $DriversDir
|
|
& cargo build --release
|
|
$rc = $LASTEXITCODE
|
|
Pop-Location
|
|
if ($prevTarget) { $env:CARGO_TARGET_DIR = $prevTarget } else { Remove-Item Env:\CARGO_TARGET_DIR -ErrorAction SilentlyContinue }
|
|
if ($rc -ne 0) { throw "pf-vdisplay cargo build failed ($rc)" }
|
|
}
|
|
if (-not (Test-Path $dll)) { throw "driver not built: $dll" }
|
|
|
|
# --- 2. WDK sign tools ------------------------------------------------------------------------
|
|
$kits = 'C:\Program Files (x86)\Windows Kits\10\bin'
|
|
function Find-Tool([string]$name, [string]$arch) {
|
|
(Get-ChildItem "$kits\*\$arch\$name" -ErrorAction SilentlyContinue | Sort-Object FullName | Select-Object -Last 1).FullName
|
|
}
|
|
$signtool = Find-Tool 'signtool.exe' 'x64'
|
|
$stampinf = Find-Tool 'stampinf.exe' 'x64'
|
|
$inf2cat = Find-Tool 'Inf2Cat.exe' 'x86'
|
|
foreach ($t in @($signtool, $stampinf, $inf2cat)) {
|
|
if (-not $t) { throw 'a WDK tool (signtool/stampinf/Inf2Cat) was not found - install the Windows 10/11 WDK.' }
|
|
}
|
|
|
|
# --- 3. signing cert (supplied stable pfx OR fresh self-signed) -------------------------------
|
|
$cleanupCert = $null
|
|
if ($CertPfxB64) {
|
|
Write-Host '==> signing with supplied driver cert (DRIVER_CERT_PFX_B64)'
|
|
$pfx = Join-Path $Out '..\driver-signing.pfx'
|
|
[IO.File]::WriteAllBytes($pfx, [Convert]::FromBase64String($CertPfxB64))
|
|
$sec = if ($CertPassword) { ConvertTo-SecureString $CertPassword -AsPlainText -Force } else { $null }
|
|
$signArgs = @('/f', $pfx); if ($CertPassword) { $signArgs += @('/p', $CertPassword) }
|
|
$pubForCer = if ($sec) { Get-PfxCertificate -FilePath $pfx -Password $sec } else { Get-PfxCertificate -FilePath $pfx }
|
|
}
|
|
else {
|
|
Write-Host '==> no DRIVER_CERT_PFX_B64 -> generating a fresh self-signed driver cert (the installer trusts the bundled .cer at install time)'
|
|
$cleanupCert = New-SelfSignedCertificate -Type CodeSigningCert -Subject 'CN=punktfunk-driver' `
|
|
-CertStoreLocation Cert:\CurrentUser\My -KeyExportPolicy Exportable -NotAfter (Get-Date).AddYears(10)
|
|
$signArgs = @('/sha1', $cleanupCert.Thumbprint)
|
|
$pubForCer = $cleanupCert
|
|
}
|
|
|
|
# --- 4. stage + clear FORCE_INTEGRITY + sign + cat --------------------------------------------
|
|
if (Test-Path $Out) { Remove-Item $Out -Recurse -Force }
|
|
New-Item -ItemType Directory -Force -Path $Out | Out-Null
|
|
$sDll = Join-Path $Out 'pf_vdisplay.dll'
|
|
$sInf = Join-Path $Out 'pf_vdisplay.inf'
|
|
$sCat = Join-Path $Out 'pf_vdisplay.cat'
|
|
$sCer = Join-Path $Out 'punktfunk-driver.cer'
|
|
Copy-Item $dll $sDll -Force
|
|
Copy-Item $inx $sInf -Force # stampinf rewrites this copy in place
|
|
|
|
# Clear FORCE_INTEGRITY BEFORE signing (it edits the PE, invalidating any signature).
|
|
& powershell -NoProfile -ExecutionPolicy Bypass -File $clear -Path $sDll | Out-Null
|
|
|
|
if (-not $DriverVer) { $now = Get-Date; $DriverVer = '9.9.{0}.{1}' -f $now.ToString('MMdd'), $now.ToString('HHmm') }
|
|
|
|
& $signtool sign /fd SHA256 @signArgs $sDll | Out-Null
|
|
if ($LASTEXITCODE -ne 0) { throw "signtool sign (dll) failed ($LASTEXITCODE)" }
|
|
& $stampinf -f $sInf -d '*' -a 'amd64' -u '2.15.0' -v $DriverVer | Out-Null
|
|
& $inf2cat /driver:$Out /os:10_X64 /uselocaltime | Out-Null
|
|
if (-not (Test-Path $sCat)) { throw "Inf2Cat did not produce $sCat" }
|
|
& $signtool sign /fd SHA256 @signArgs $sCat | Out-Null
|
|
if ($LASTEXITCODE -ne 0) { throw "signtool sign (cat) failed ($LASTEXITCODE)" }
|
|
Export-Certificate -Cert $pubForCer -FilePath $sCer | Out-Null
|
|
if ($cleanupCert) { Remove-Item "Cert:\CurrentUser\My\$($cleanupCert.Thumbprint)" -Force -ErrorAction SilentlyContinue }
|
|
|
|
# --- 5. guard: assert the freshly-built catalog covers the inf + dll ---------------------------
|
|
# (Built-from-source can't drift, but this catches a botched stampinf/Inf2Cat ordering before it ships.)
|
|
$cat = Test-FileCatalog -CatalogFilePath $sCat -Path $Out -FilesToSkip 'pf_vdisplay.cat', 'punktfunk-driver.cer' -Detailed -ErrorAction SilentlyContinue
|
|
if ($cat) {
|
|
$covered = @($cat.CatalogItems.Keys)
|
|
foreach ($need in @('pf_vdisplay.inf', 'pf_vdisplay.dll')) {
|
|
if (-not ($covered | Where-Object { $_ -like "*$need" })) {
|
|
throw "catalog coverage guard: $need is NOT in $sCat (stampinf/Inf2Cat ordering bug?)"
|
|
}
|
|
}
|
|
Write-Host " catalog covers pf_vdisplay.inf + pf_vdisplay.dll (status=$($cat.Status))"
|
|
}
|
|
|
|
Write-Host "==> built + signed pf-vdisplay DriverVer=$DriverVer -> $Out"
|
|
Get-ChildItem $Out -File | ForEach-Object { " $($_.Name) ($($_.Length) bytes)" }
|