Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions scripts/build-windows-release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,112 @@ function Invoke-Checked {
}
}

# `Arguments` is mandatory *and* `[AllowEmptyCollection()]` rather than defaulted
# to `@()`, so that an explicitly empty list binds while an omitted or null one
# stays a hard binding error. A default would silently turn "forwarded nothing"
# into "forwarded an empty list", which is the confusion #512 came from, so both
# halves of that design are pinned here.
function Invoke-CheckedBindingContractTests {
$recorder = { param([string]$Program, [string[]]$Arguments) return 0 }

$nullRejected = $false
try {
Invoke-Checked "fake-null.exe" $null -Runner $recorder
} catch {
$nullRejected = $true
}
if (-not $nullRejected) {
throw "checked invocation bound a null argument list"
}

# An omitted mandatory parameter *prompts* in an interactive console host, so
# asserting the omission in-process would hang a developer's terminal. An API
# runspace has a host that cannot prompt and reports the binding failure
# instead. The function under test is rebuilt from the live definition's own
# source text, so any edit to the real parameter block is what gets asserted.
$runspace = [powershell]::Create()
$omissionRejected = $false
try {
$null = $runspace.AddScript(@'
param([string]$Body)
Set-Item -LiteralPath function:Invoke-Checked -Value ([scriptblock]::Create($Body))
Invoke-Checked "fake-omitted.exe" -Runner { param([string]$Program, [string[]]$Arguments) return 0 }
'@).AddArgument(${function:Invoke-Checked}.ToString())
try {
$null = $runspace.Invoke()
} catch {
$omissionRejected =
$_.Exception.InnerException -is [System.Management.Automation.ParameterBindingException]
if (-not $omissionRejected) { throw }
}
$omissionRejected = $omissionRejected -or @($runspace.Streams.Error | Where-Object {
$_.Exception -is [System.Management.Automation.ParameterBindingException]
}).Count -gt 0
} finally {
$runspace.Dispose()
}
if (-not $omissionRejected) {
throw "omitting the argument list was not a mandatory-parameter binding error"
}
}

# The fake-runner arm below never executes `& $Program @Arguments`, so on its own
# it cannot catch an edit that stops propagating the child's exit status or stops
# forwarding argv. This arm drives the real branch end to end.
#
# The program it drives is the PowerShell host executing this script. That is the
# one executable guaranteed to exist wherever this script can run, so the same
# assertions execute on the Windows runners and on POSIX developer boxes with no
# platform branch that could silently no-op on one of them (#512).
function Invoke-CheckedRealProcessContractTests {
$pwshPath = (Get-Process -Id $PID).Path
if (-not $pwshPath) {
throw "real-process contract test could not resolve the running PowerShell host"
}
$scratch = Join-Path ([System.IO.Path]::GetTempPath()) `
("vllm-cpp-checked-" + [guid]::NewGuid().ToString("n"))
New-Item -ItemType Directory -Force -Path $scratch | Out-Null
try {
Invoke-Checked $pwshPath @("-NoProfile", "-Command", "exit 0")

$nonzeroRejected = $false
try {
Invoke-Checked $pwshPath @("-NoProfile", "-Command", "exit 3")
} catch {
$nonzeroRejected = $true
if ($_.Exception.Message -notmatch 'exited with status 3$') {
throw "real-process failure did not report the child's own exit status: $($_.Exception.Message)"
}
}
if (-not $nonzeroRejected) {
throw "real-process nonzero exit status was accepted"
}

# Exits 0 only for three *distinct* argv entries, the first of which holds
# a space: joining, re-quoting, truncating or reordering the forwarded
# list all land on a different exit status.
$argvProbe = Join-Path $scratch "argv-probe.ps1"
@'
if ($args.Count -ne 3) { exit 21 }
if ($args[0] -ne 'one two' -or $args[1] -ne 'three' -or $args[2] -ne 'four') { exit 22 }
exit 0
'@ | Set-Content -LiteralPath $argvProbe -Encoding utf8NoBOM
Invoke-Checked $pwshPath @("-NoProfile", "-File", $argvProbe, "one two", "three", "four")

# The production calls this branch exists for forward an explicitly empty
# list to a program that takes no arguments, so drive that shape for real
# rather than only through the fake runner (#512).
$emptyProbe = Join-Path $scratch "empty-probe.ps1"
@'
if ($args.Count -ne 0) { exit 23 }
exit 0
'@ | Set-Content -LiteralPath $emptyProbe -Encoding utf8NoBOM
Invoke-Checked $emptyProbe @()
} finally {
Remove-Item -Recurse -Force -LiteralPath $scratch -ErrorAction SilentlyContinue
}
}

# Most of this script's checked invocations run a test executable that takes no
# arguments, so `Invoke-Checked` must bind an explicitly empty argument list and
# still forward it verbatim (#512).
Expand Down Expand Up @@ -80,6 +186,9 @@ function Invoke-CheckedContractTests {
throw "nonzero $rejectedName-argument exit status was accepted"
}
}

Invoke-CheckedBindingContractTests
Invoke-CheckedRealProcessContractTests
}

function Assert-CrtPolicy {
Expand Down
Loading