param( [string]$PythonPath = "" ) $ErrorActionPreference = "Stop" $projectRoot = [System.IO.Path]::GetFullPath($PSScriptRoot) $buildEnvironment = Join-Path $projectRoot ".build-venv" $buildPython = Join-Path $buildEnvironment "Scripts\python.exe" $requirements = Join-Path $projectRoot "requirements.txt" $spec = Join-Path $projectRoot "wechat_rpa.spec" $versionLine = Select-String -LiteralPath (Join-Path $projectRoot "app_version.py") -Pattern '^APP_VERSION\s*=\s*"([^"]+)"$' if (-not $versionLine) { throw "Cannot read APP_VERSION from app_version.py" } $version = $versionLine.Matches[0].Groups[1].Value $env:WECOM_BUILD_VERSION = $version $env:UV_CACHE_DIR = Join-Path $projectRoot ".uv-cache" function Test-BuildPython([string]$Candidate) { if (-not $Candidate -or -not (Test-Path -LiteralPath $Candidate)) { return $false } & $Candidate -c "import PyInstaller, PySide6, requests, numpy, PIL, pyautogui, pyperclip, win32gui, mcp, rapidocr_onnxruntime" *> $null return $LASTEXITCODE -eq 0 } $candidates = @() if ($PythonPath) { $candidates += [System.IO.Path]::GetFullPath($PythonPath) } try { $launcherPython = (& py -3.11 -c "import sys; print(sys.executable)" 2>$null | Select-Object -Last 1).Trim() if ($launcherPython) { $candidates += $launcherPython } } catch {} if (Test-Path -LiteralPath $buildPython) { $candidates += $buildPython } $python = $candidates | Where-Object { Test-BuildPython $_ } | Select-Object -First 1 if (-not $python) { if (-not (Test-Path -LiteralPath $buildPython)) { & uv venv --python 3.12 $buildEnvironment if ($LASTEXITCODE -ne 0) { throw "Failed to create the EXE build environment" } } & uv pip install --python $buildPython -r $requirements pyinstaller if ($LASTEXITCODE -ne 0) { throw "Failed to install EXE build dependencies" } $python = $buildPython } Write-Output ("Build Python: " + $python) Push-Location $projectRoot try { & $python -m PyInstaller --noconfirm --clean $spec if ($LASTEXITCODE -ne 0) { throw "PyInstaller build failed" } } finally { Pop-Location } $appName = "ZhenAI-WeCom-Assistant-v$version" $appDir = Join-Path $projectRoot "dist\$appName" $exe = Join-Path $appDir "$appName.exe" if (-not (Test-Path -LiteralPath $exe)) { throw ("Build completed but EXE was not found: " + $exe) } $internalDir = Join-Path $appDir "_internal" $requiredRuntimeFiles = @( "PySide6\QtWebEngineProcess.exe", "PySide6\resources\qtwebengine_resources.pak", "PySide6\resources\icudtl.dat", "PySide6\translations\qtwebengine_locales\zh-CN.pak", "VCRUNTIME140.dll" ) foreach ($requiredFile in $requiredRuntimeFiles) { if (-not (Test-Path -LiteralPath (Join-Path $internalDir $requiredFile))) { throw ("Required embedded runtime file is missing: " + $requiredFile) } } Write-Output "Embedded browser and VC runtime files verified" # 有证书时给主程序签名(无证书自动跳过)。签名要在自检前完成, # 保证自检跑的就是最终分发的那个文件。 & (Join-Path $projectRoot "sign_artifact.ps1") -Path $exe if ($LASTEXITCODE -ne 0) { throw "Code signing step failed" } $selfCheck = Start-Process -FilePath $exe -ArgumentList "--packaging-self-check" -PassThru -WindowStyle Hidden try { if (-not $selfCheck.WaitForExit(90000)) { $selfCheck.Kill() throw "Packaged Qt WebEngine self-check timed out" } if ($selfCheck.ExitCode -ne 0) { throw ("Packaged Qt WebEngine self-check failed with exit code " + $selfCheck.ExitCode) } } finally { if ($selfCheck -and -not $selfCheck.HasExited) { $selfCheck.Kill() } } Write-Output "Packaged Qt WebEngine self-check passed" $hash = (Get-FileHash -LiteralPath $exe -Algorithm SHA256).Hash Write-Output ("App folder: " + $appDir) Write-Output ("EXE: " + $exe) Write-Output ("SHA256: " + $hash)