param( [int]$Port = 8797, [string]$BindAddress = "127.0.0.1" ) $ErrorActionPreference = "Stop" $root = Split-Path -Parent $PSScriptRoot $runtime = Join-Path $root "runtime" $logs = Join-Path $runtime "logs" $cache = Join-Path $runtime "cache\python" New-Item -ItemType Directory -Force -Path $logs, $cache | Out-Null $launcher = Get-Command py -ErrorAction SilentlyContinue if (-not $launcher) { $launcher = Get-Command python -ErrorAction Stop } # PowerShell 5.1 cannot start a child process when the host supplies both # Windows' canonical "Path" key and an additional case-variant "PATH" key. $environmentKeys = @([Environment]::GetEnvironmentVariables().Keys) if (($environmentKeys -ccontains "Path") -and ($environmentKeys -ccontains "PATH")) { [Environment]::SetEnvironmentVariable("PATH", $null, [EnvironmentVariableTarget]::Process) } $env:PYTHONPYCACHEPREFIX = $cache $stdout = Join-Path $logs "server-$Port.log" $stderr = Join-Path $logs "server-$Port.err.log" function Get-ListeningProcessId { param([int]$LocalPort) $pattern = "^\s*TCP\s+\S+:$LocalPort\s+\S+\s+LISTENING\s+(\d+)\s*$" $match = netstat -ano -p TCP | Select-String -Pattern $pattern | Select-Object -First 1 if ($match) { return [int]$match.Matches[0].Groups[1].Value } return $null } $pidFile = Join-Path $runtime "server-$Port.pid" $serverProcessId = Get-ListeningProcessId -LocalPort $Port if ($serverProcessId) { Set-Content -LiteralPath $pidFile -Value $serverProcessId -Encoding ascii Write-Output "Already running PID ${serverProcessId}: http://${BindAddress}:$Port/" Write-Output "Logs: $stdout" exit 0 } $childCommand = "& '$($launcher.Source.Replace("'", "''"))' -u server.py --host '$($BindAddress.Replace("'", "''"))' --port $Port 1> '$($stdout.Replace("'", "''"))' 2> '$($stderr.Replace("'", "''"))'" $encodedCommand = [Convert]::ToBase64String([Text.Encoding]::Unicode.GetBytes($childCommand)) $process = Start-Process ` -FilePath (Join-Path $PSHOME "powershell.exe") ` -ArgumentList @("-NoProfile", "-ExecutionPolicy", "Bypass", "-EncodedCommand", $encodedCommand) ` -WorkingDirectory $root ` -WindowStyle Hidden ` -PassThru $serverProcessId = $null for ($attempt = 0; $attempt -lt 100; $attempt++) { $listeningProcessId = Get-ListeningProcessId -LocalPort $Port if ($listeningProcessId) { $serverProcessId = $listeningProcessId break } if ($process.HasExited) { break } Start-Sleep -Milliseconds 100 } if (-not $serverProcessId) { $detail = Get-Content -LiteralPath $stderr -Raw -ErrorAction SilentlyContinue throw "Server did not listen on port $Port. $detail" } Set-Content -LiteralPath $pidFile -Value $serverProcessId -Encoding ascii Write-Output "Started PID ${serverProcessId}: http://${BindAddress}:$Port/" Write-Output "Logs: $stdout"