144 lines
6.9 KiB
PowerShell
144 lines
6.9 KiB
PowerShell
# Compatible with Windows PowerShell 2.0. Reuse a verified full Python install.
|
|
param([switch]$ProbeOnly)
|
|
|
|
$ErrorActionPreference = 'Stop'
|
|
$kitRoot = Split-Path -Parent (Split-Path -Parent $MyInvocation.MyCommand.Path)
|
|
$logDirectory = Join-Path $kitRoot 'logs'
|
|
[void][IO.Directory]::CreateDirectory($logDirectory)
|
|
$runId = Get-Date -Format 'yyyyMMdd-HHmmss-fff'
|
|
$setupLog = Join-Path $logDirectory ('setup-' + $runId + '.log')
|
|
$utf8 = New-Object System.Text.UTF8Encoding($true)
|
|
[IO.File]::WriteAllText($setupLog, '', $utf8)
|
|
$env:PYTHONUTF8 = '1'
|
|
$env:PYTHONIOENCODING = 'utf-8'
|
|
$env:PYTHONHOME = $null
|
|
$env:PYTHONPATH = $null
|
|
[Console]::OutputEncoding = New-Object System.Text.UTF8Encoding($false)
|
|
$targetBits = 32
|
|
if ($env:PROCESSOR_ARCHITECTURE -eq 'AMD64' -or $env:PROCESSOR_ARCHITEW6432 -eq 'AMD64') {
|
|
$targetBits = 64
|
|
}
|
|
|
|
function Write-Log([string]$message) {
|
|
Write-Host $message
|
|
[IO.File]::AppendAllText($setupLog, $message + [Environment]::NewLine, $utf8)
|
|
}
|
|
|
|
function Invoke-Logged([string]$executable, [string[]]$arguments) {
|
|
# PowerShell 2 treats native stderr as ErrorRecord; preserve it in the log
|
|
# without mistaking harmless warnings for a failed process.
|
|
$savedPreference = $ErrorActionPreference
|
|
$ErrorActionPreference = 'Continue'
|
|
try {
|
|
$global:LASTEXITCODE = $null
|
|
& $executable @arguments 2>&1 | ForEach-Object { Write-Log ([string]$_) }
|
|
$code = $global:LASTEXITCODE
|
|
} finally {
|
|
$ErrorActionPreference = $savedPreference
|
|
}
|
|
if ($null -eq $code) { return 1 }
|
|
return $code
|
|
}
|
|
|
|
function Test-Python([string]$candidate) {
|
|
if (-not (Test-Path -LiteralPath $candidate -PathType Leaf)) { return $false }
|
|
Write-Log ('Checking Python: ' + $candidate)
|
|
$probe = 'import sys, struct, venv, ensurepip, ssl, sqlite3; print(sys.version); print(sys.executable); sys.exit(0 if sys.version_info[:3] == (3,8,10) and struct.calcsize(''P'')*8 == ' + $targetBits + ' else 3)'
|
|
$code = Invoke-Logged $candidate @('-I', '-X', 'utf8', '-c', $probe)
|
|
if ($code -ne 0) { Write-Log ('Python rejected; exit code: ' + $code) }
|
|
return ($code -eq 0)
|
|
}
|
|
|
|
function Find-Python {
|
|
$manualFile = Join-Path $kitRoot 'python_path.txt'
|
|
if (Test-Path -LiteralPath $manualFile) {
|
|
$manualPath = [IO.File]::ReadAllText($manualFile).Trim().Trim('"')
|
|
if (Test-Python $manualPath) { return $manualPath }
|
|
throw 'python_path.txt must contain the full path to a working Python 3.8.10 of the required architecture.'
|
|
}
|
|
$candidates = New-Object System.Collections.ArrayList
|
|
[void]$candidates.Add((Join-Path $kitRoot '.python38\python.exe'))
|
|
# Python's official installer registers the full interpreter, even when
|
|
# its files live in an older source kit directory.
|
|
foreach ($registryRoot in @('HKCU:\Software\Python\PythonCore',
|
|
'HKCU:\Software\WOW6432Node\Python\PythonCore',
|
|
'HKLM:\Software\Python\PythonCore',
|
|
'HKLM:\Software\WOW6432Node\Python\PythonCore')) {
|
|
foreach ($tag in @('3.8', '3.8-32')) {
|
|
$keyPath = Join-Path $registryRoot ($tag + '\InstallPath')
|
|
try {
|
|
if (Test-Path -LiteralPath $keyPath) {
|
|
$key = Get-Item -LiteralPath $keyPath
|
|
$executable = [string]$key.GetValue('ExecutablePath')
|
|
$directory = [string]$key.GetValue('')
|
|
if ($executable) { [void]$candidates.Add($executable) }
|
|
if ($directory) { [void]$candidates.Add((Join-Path $directory 'python.exe')) }
|
|
}
|
|
} catch {
|
|
Write-Log ('Registry lookup skipped: ' + $keyPath + ' : ' + $_.Exception.Message)
|
|
}
|
|
}
|
|
}
|
|
foreach ($candidate in ($candidates | Select-Object -Unique)) {
|
|
if (Test-Python $candidate) { return $candidate }
|
|
}
|
|
return $null
|
|
}
|
|
|
|
try {
|
|
Write-Log ('Setup log: ' + $setupLog)
|
|
Write-Log ('Package: ' + $kitRoot)
|
|
Write-Log ('Windows: ' + [Environment]::OSVersion.VersionString)
|
|
Write-Log ('PowerShell: ' + $PSVersionTable.PSVersion.ToString())
|
|
Write-Log ('Required Python: 3.8.10, ' + $targetBits + '-bit')
|
|
$python = Find-Python
|
|
if ($ProbeOnly) {
|
|
if ($python) { Write-Log ('FOUND reusable Python: ' + $python) }
|
|
else { Write-Log 'No working matching Python was found. No installation was attempted.' }
|
|
Write-Log 'DIAGNOSTIC COMPLETE. No dependencies or system settings were changed.'
|
|
exit 0
|
|
}
|
|
if (-not $python) {
|
|
$installerName = 'python-3.8.10.exe'
|
|
if ($targetBits -eq 64) { $installerName = 'python-3.8.10-amd64.exe' }
|
|
$installer = Join-Path $kitRoot ('installers\' + $installerName)
|
|
if (-not (Test-Path -LiteralPath $installer)) { throw ('Installer missing: ' + $installer) }
|
|
$installerLog = Join-Path $logDirectory ('python-installer-' + $runId + '.log')
|
|
$targetDirectory = Join-Path $kitRoot '.python38'
|
|
Write-Log ('No reusable Python found. Starting installer: ' + $installer)
|
|
Write-Log ('Installer log: ' + $installerLog)
|
|
$installerArgs = @('/passive', '/log', ('"' + $installerLog + '"'),
|
|
'InstallAllUsers=0', ('TargetDir="' + $targetDirectory + '"'),
|
|
'Include_launcher=0', 'InstallLauncherAllUsers=0', 'PrependPath=0',
|
|
'Include_doc=0', 'Include_test=0', 'Include_tcltk=0', 'Shortcuts=0', 'AssociateFiles=0')
|
|
$process = Start-Process -FilePath $installer -ArgumentList $installerArgs -Wait -PassThru -WindowStyle Hidden
|
|
$installerCode = $process.ExitCode
|
|
Write-Log ('Python installer exit code: ' + $installerCode)
|
|
if ($installerCode -eq 3010 -or $installerCode -eq 1641) {
|
|
Write-Log 'Windows requested a restart. Restart the computer and run 01_install_env.cmd again.'
|
|
exit $installerCode
|
|
}
|
|
if ($installerCode -ne 0) {
|
|
throw ('Python installer failed with exit code ' + $installerCode + '. See ' + $installerLog)
|
|
}
|
|
# Maintenance mode may leave Python in its original registered folder.
|
|
$python = Find-Python
|
|
if (-not $python) {
|
|
throw 'Installer returned success but no matching Python was found. Keep logs; do not uninstall the old working environment.'
|
|
}
|
|
}
|
|
Write-Log ('Using existing Python: ' + $python)
|
|
Write-Log 'Keep this Python directory while using the build environment.'
|
|
$buildScript = Join-Path $kitRoot 'win7\build.py'
|
|
$code = Invoke-Logged $python @('-I', '-X', 'utf8', $buildScript, 'setup')
|
|
Write-Log ('Environment setup exit code: ' + $code)
|
|
if ($code -ne 0) { exit $code }
|
|
Write-Log 'SETUP COMPLETE. Run 02_build_exe.cmd next.'
|
|
exit 0
|
|
} catch {
|
|
Write-Log ('FAILED: ' + $_.Exception.Message)
|
|
Write-Log ('Details: ' + $_.ScriptStackTrace)
|
|
Write-Log ('Keep the logs folder. Setup log: ' + $setupLog)
|
|
exit 1
|
|
}
|