e06e1cadad
Конфиг сервера из env-secrets (не только из файла), клон rustdesk переопределяем через RUSTDESK_SRC_DIR (на S:, C: раннера тесный). Workflow: сборка на GUSIK-PC + attach установщика к Gitea Release на v*. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
160 lines
11 KiB
PowerShell
160 lines
11 KiB
PowerShell
<#
|
||
Локальная сборка преднастроенного RustDesk-клиента под Windows (GUSIK-PC).
|
||
Клон rustdesk@пин -> preconfigure (вшить сервер+ключ) -> vcpkg install -> python build.py --flutter -> dist/.
|
||
|
||
Тулчейн (ставится один раз, см. docs/CODEMAP.md):
|
||
Rust 1.75 (rustup), VS Build Tools 2022 (MSVC+CMake+libclang), Flutter 3.24.5, vcpkg@пин.
|
||
Пути тулчейна можно переопределить через env FLUTTER_HOME / VCPKG_ROOT.
|
||
|
||
Запуск: pwsh -File scripts\build-windows.ps1
|
||
Опции: -Clean снести клон и склонировать заново
|
||
-SkipBuild только клон+preconfigure (без компиляции)
|
||
#>
|
||
[CmdletBinding()]
|
||
param([switch]$Clean, [switch]$SkipBuild)
|
||
$ErrorActionPreference = 'Stop'
|
||
$root = Split-Path -Parent $PSScriptRoot
|
||
$dist = Join-Path $root 'dist' # $src задаётся ниже (переопределяемо через RUSTDESK_SRC_DIR)
|
||
|
||
# --- пины ---
|
||
$pins = @{}
|
||
Get-Content (Join-Path $root 'upstream.txt') | Where-Object { $_ -match '^\s*[A-Z]' } | ForEach-Object {
|
||
$k,$v = $_ -split '=',2; $pins[$k.Trim()] = $v.Trim()
|
||
}
|
||
$TAG=$pins['RUSTDESK_TAG']; $REPO=$pins['RUSTDESK_REPO']; $TRIPLET=$pins['VCPKG_TRIPLET']
|
||
Write-Host "== RustDesk custom build == tag=$TAG triplet=$TRIPLET" -ForegroundColor Cyan
|
||
|
||
# --- пути тулчейна ---
|
||
$FlutterHome = if ($env:FLUTTER_HOME) { $env:FLUTTER_HOME } else { 'S:\dev\flutter' }
|
||
$VcpkgRoot = if ($env:VCPKG_ROOT) { $env:VCPKG_ROOT } else { 'S:\dev\vcpkg' }
|
||
$BashExe = 'C:\Program Files\Git\bin\bash.exe'
|
||
# Клон rustdesk (с многогиговым target/) — переопределяемо на S: (в CI checkout может лежать на C:).
|
||
$src = if ($env:RUSTDESK_SRC_DIR) { $env:RUSTDESK_SRC_DIR } else { Join-Path $root 'rustdesk' }
|
||
|
||
# --- конфиг сервера: из env (CI secrets) ИЛИ config/server.env (локально) ---
|
||
if (-not ($env:RUSTDESK_SERVER -and $env:RUSTDESK_PUB_KEY)) {
|
||
$envFile = Join-Path $root 'config\server.env'
|
||
if (-not (Test-Path $envFile)) { throw "Нет ни env RUSTDESK_SERVER/RUSTDESK_PUB_KEY (CI secrets), ни $envFile (локально — скопируй server.env.example)." }
|
||
Get-Content $envFile | Where-Object { $_ -match '^\s*[A-Z].*=' } | ForEach-Object {
|
||
$k,$v = $_ -split '=',2; Set-Item -Path "Env:$($k.Trim())" -Value $v.Trim()
|
||
}
|
||
}
|
||
if (-not $env:RUSTDESK_SERVER -or -not $env:RUSTDESK_PUB_KEY) { throw "Не заданы RUSTDESK_SERVER/RUSTDESK_PUB_KEY." }
|
||
|
||
# --- импорт VS dev-env (MSVC/CMake/Windows SDK в PATH) ---
|
||
$vsw = "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe"
|
||
$vsPath = & $vsw -latest -products * -property installationPath
|
||
if (-not $vsPath) { throw "VS Build Tools не найдены (vswhere)." }
|
||
$vcvars = Join-Path $vsPath 'VC\Auxiliary\Build\vcvars64.bat'
|
||
Write-Host "Импорт VS dev-env: $vcvars" -ForegroundColor DarkCyan
|
||
cmd /c "`"$vcvars`" >nul && set" | ForEach-Object { if ($_ -match '^([^=]+)=(.*)$') { Set-Item "Env:$($matches[1])" $matches[2] } }
|
||
|
||
# CIDR в NO_PROXY (напр. 192.168.5.0/24) ломает WinHTTP-загрузчик vcpkg (WinHttpSetOption 87).
|
||
# У машины есть прямой интернет -> убираем прокси-env для процесса сборки (системные настройки не трогаем).
|
||
foreach ($p in 'HTTP_PROXY','HTTPS_PROXY','NO_PROXY','ALL_PROXY','http_proxy','https_proxy','no_proxy') { Set-Item "Env:$p" $null -EA SilentlyContinue }
|
||
|
||
# --- env тулчейна на сессию ---
|
||
$env:VCPKG_ROOT = $VcpkgRoot
|
||
$env:VCPKGRS_TRIPLET = $TRIPLET # для vcpkg-rs крейтов (статик-триплет)
|
||
# libclang для bindgen: VS-бандл его НЕ содержит -> ставим LLVM отдельно (S:\dev\LLVM).
|
||
$clangCands = @('S:\dev\LLVM\bin','C:\Program Files\LLVM\bin', (Join-Path $vsPath 'VC\Tools\Llvm\x64\bin'))
|
||
$env:LIBCLANG_PATH = ($clangCands | Where-Object { Test-Path (Join-Path $_ 'libclang.dll') } | Select-Object -First 1)
|
||
$env:PATH = "$FlutterHome\bin;$VcpkgRoot;$env:PATH"
|
||
|
||
# --- preflight ---
|
||
function Check($n,$ok,$hint){ if($ok){Write-Host " [ok] $n" -ForegroundColor Green}else{Write-Host " [нет] $n — $hint" -ForegroundColor Yellow}; return $ok }
|
||
Write-Host "Preflight:" -ForegroundColor Cyan
|
||
$all = $true
|
||
$all = (Check 'git' ([bool](Get-Command git -EA SilentlyContinue)) 'Git for Windows') -and $all
|
||
$all = (Check 'git-bash' (Test-Path $BashExe) "нет $BashExe") -and $all
|
||
$all = (Check 'python' ([bool](Get-Command python -EA SilentlyContinue)) 'Python 3') -and $all
|
||
$all = (Check "rust $($pins['RUST_VERSION'])" ([bool]((rustup toolchain list) -match $pins['RUST_VERSION'])) "rustup toolchain install $($pins['RUST_VERSION'])") -and $all
|
||
$all = (Check 'flutter' (Test-Path "$FlutterHome\bin\flutter.bat") "Flutter в $FlutterHome") -and $all
|
||
$all = (Check 'vcpkg' (Test-Path "$VcpkgRoot\vcpkg.exe") "vcpkg в $VcpkgRoot") -and $all
|
||
$all = (Check 'cl.exe' ([bool](Get-Command cl.exe -EA SilentlyContinue)) 'MSVC из vcvars') -and $all
|
||
$all = (Check 'libclang' ([bool]$env:LIBCLANG_PATH -and (Test-Path (Join-Path $env:LIBCLANG_PATH 'libclang.dll'))) 'нет libclang.dll — установи LLVM 15 в S:\dev\LLVM') -and $all
|
||
if (-not $all) { throw "Тулчейн неполный — см. docs\CODEMAP.md." }
|
||
|
||
# --- клон/сброс до пина ---
|
||
if ($Clean -and (Test-Path $src)) { Remove-Item -Recurse -Force $src }
|
||
if (-not (Test-Path $src)) {
|
||
Write-Host "Клонирую rustdesk@$TAG (+submodule)..." -ForegroundColor Cyan
|
||
git clone --branch $TAG --depth 1 --recurse-submodules --shallow-submodules $REPO $src
|
||
} else {
|
||
Write-Host "Сбрасываю патчи до чистого $TAG..." -ForegroundColor Cyan
|
||
git -C $src checkout -- .
|
||
git -C $src submodule foreach --recursive 'git checkout -- .'
|
||
}
|
||
Push-Location $src
|
||
rustup override set $pins['RUST_VERSION'] | Out-Null
|
||
rustup component add rustfmt --toolchain "$($pins['RUST_VERSION'])-x86_64-pc-windows-msvc" | Out-Null # codegen зовёт rustfmt
|
||
Pop-Location
|
||
|
||
# --- preconfigure: вшить сервер+ключ ---
|
||
Write-Host "Preconfigure (сервер+ключ)..." -ForegroundColor Cyan
|
||
$srcBash = $src -replace '\\','/' # git-bash не любит backslash в [ -f ... ]
|
||
$preBash = ("$root/scripts/preconfigure.sh") -replace '\\','/'
|
||
& $BashExe $preBash $srcBash
|
||
if ($LASTEXITCODE) { throw "preconfigure.sh упал ($LASTEXITCODE)" }
|
||
if ($SkipBuild) { Write-Host "SkipBuild — стоп после preconfigure." -ForegroundColor Yellow; return }
|
||
|
||
# --- flutter_rust_bridge codegen ---
|
||
# RustDesk НЕ коммитит src/bridge_generated.rs — его генерит flutter_rust_bridge_codegen.
|
||
# Без него cargo build падает: E0583 (нет модуля bridge_generated) + E0277 (EventToUI: IntoIntoDart).
|
||
Write-Host "flutter_rust_bridge codegen..." -ForegroundColor Cyan
|
||
Push-Location $src
|
||
$env:RUSTC_BOOTSTRAP = '1' # cargo-expand требует nightly-фич; на stable — через RUSTC_BOOTSTRAP
|
||
# Инструменты собираем stable-тулчейном (1.75 может не собрать свежие tool-крейты); бинарь standalone.
|
||
if (-not (Get-Command cargo-expand -EA SilentlyContinue)) {
|
||
cargo +stable install cargo-expand --version $pins['CARGO_EXPAND_VERSION'] --locked
|
||
}
|
||
$frb = Join-Path $env:USERPROFILE '.cargo\bin\flutter_rust_bridge_codegen.exe'
|
||
if (-not (Test-Path $frb)) {
|
||
cargo +stable install flutter_rust_bridge_codegen --version $pins['FRB_VERSION'] --features uuid --locked
|
||
}
|
||
Push-Location flutter; flutter pub get; Pop-Location
|
||
# ffigen (внутри codegen) ищет libclang.dll в дефолтных путях -> явно указываем корень LLVM (S:\dev\LLVM).
|
||
$llvmRoot = (Split-Path $env:LIBCLANG_PATH) -replace '\\','/'
|
||
& $frb --rust-input ./src/flutter_ffi.rs --dart-output ./flutter/lib/generated_bridge.dart --c-output ./flutter/macos/Runner/bridge_generated.h --llvm-path $llvmRoot
|
||
$frbRc = $LASTEXITCODE
|
||
# Проверяем ОБЕ стороны: rust + dart (+ freezed). rust-файл создаётся до dart, поэтому одного его мало.
|
||
$genOk = ($frbRc -eq 0) -and (Test-Path 'src/bridge_generated.rs') -and (Test-Path 'flutter/lib/generated_bridge.dart') -and (Test-Path 'flutter/lib/generated_bridge.freezed.dart')
|
||
if (-not $genOk) { Pop-Location; throw "codegen неполный (rc=$frbRc): нет dart-моста generated_bridge(.freezed).dart" }
|
||
Write-Host " bridge_generated.rs + generated_bridge.dart + .freezed.dart созданы." -ForegroundColor Green
|
||
Pop-Location
|
||
|
||
# --- vcpkg install (манифест из vcpkg.json; первый раз долго: ffmpeg/aom) ---
|
||
Write-Host "vcpkg install ($TRIPLET) — первый раз ОЧЕНЬ долго (ffmpeg/aom из исходников)..." -ForegroundColor Cyan
|
||
Push-Location $src
|
||
& "$VcpkgRoot\vcpkg.exe" install --triplet $TRIPLET --x-install-root="$VcpkgRoot\installed"
|
||
if ($LASTEXITCODE) { Pop-Location; throw "vcpkg install упал ($LASTEXITCODE)" }
|
||
Pop-Location
|
||
|
||
# --- сборка клиента (без портативной упаковки: её build.py делает через `python3`,
|
||
# а на GUSIK-PC python3 = Store-заглушка -> падает; пакуем отдельно реальным python) ---
|
||
Write-Host "python build.py --flutter --skip-portable-pack..." -ForegroundColor Cyan
|
||
Push-Location $src
|
||
python build.py --flutter --skip-portable-pack
|
||
$rc = $LASTEXITCODE
|
||
Pop-Location
|
||
if ($rc) { throw "build.py упал ($rc)" }
|
||
$rel = 'flutter/build/windows/x64/runner/Release'
|
||
if (-not (Test-Path "$src/$rel/rustdesk.exe")) { throw "клиент не собран: нет $rel/rustdesk.exe" }
|
||
|
||
# --- портативный установщик (generate.py реальным python) ---
|
||
Write-Host "Упаковка портативного установщика (generate.py)..." -ForegroundColor Cyan
|
||
Push-Location "$src\libs\portable"
|
||
python -m pip install -q -r requirements.txt
|
||
python generate.py -f "../../$rel/" -o . -e "../../$rel/rustdesk.exe"
|
||
$genRc = $LASTEXITCODE
|
||
Pop-Location
|
||
if ($genRc) { throw "generate.py упал ($genRc)" }
|
||
|
||
# --- артефакт в dist ---
|
||
New-Item -ItemType Directory -Force $dist | Out-Null
|
||
$packer = "$src\target\release\rustdesk-portable-packer.exe"
|
||
if (-not (Test-Path $packer)) { throw "не найден rustdesk-portable-packer.exe после generate.py" }
|
||
$outExe = Join-Path $dist "rustdesk-$TAG-install.exe"
|
||
Copy-Item $packer $outExe -Force
|
||
Write-Host "ГОТОВО: $outExe ($([math]::Round((Get-Item $outExe).Length/1MB,1)) MB)" -ForegroundColor Green
|