Files
zoitechat/.github/workflows/windows-build.yml
deepend-tildeclub 83ed374b99 Enhance GTK3 bundle handling in Windows build workflow
Updated PowerShell script to include new functions for GTK3 bundle handling and improved error handling. Adjusted the build process to ensure compatibility with legacy GTK bundles.
2026-02-01 20:39:34 -07:00

389 lines
16 KiB
YAML

name: Windows Build
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
windows_build:
runs-on: windows-2019
permissions:
contents: read
id-token: write
attestations: write
artifact-metadata: write
strategy:
matrix:
platform: [x64, win32]
arch: [x64, x86]
exclude:
- platform: x64
arch: x86
- platform: win32
arch: x64
fail-fast: false
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.14.2"
architecture: ${{ matrix.arch }}
- name: Install Dependencies (GTK3 toolchain + packagers)
env:
GITHUB_TOKEN: ${{ github.token }}
shell: pwsh
run: |
$ErrorActionPreference = "Stop"
New-Item -Name "deps" -ItemType "Directory" -Force | Out-Null
# Inno Setup + IDP (kept as-is)
Invoke-WebRequest http://files.jrsoftware.org/is/5/innosetup-5.5.9-unicode.exe -OutFile deps\innosetup-unicode.exe
& deps\innosetup-unicode.exe /VERYSILENT | Out-Null
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/idpsetup-1.5.1.exe -OutFile deps\idpsetup.exe
& deps\idpsetup.exe /VERYSILENT
# WinSparkle / gendef / perl (kept as-is)
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/gendef-20111031.7z -OutFile deps\gendef.7z
& 7z.exe x deps\gendef.7z -oC:\gtk-build | Out-Null
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/WinSparkle-20151011.7z -OutFile deps\WinSparkle.7z
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle | Out-Null
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/perl-5.20.0-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.20\${{ matrix.platform }} | Out-Null
# ------------------------------------------
# GTK: download wingtk/gvsbuild GTK3 bundle and normalize into:
# C:\gtk-build\gtk\<platform>\release\{bin,include,lib}
#
# IMPORTANT: do NOT assume layout inside the zip. Detect prefix by locating gtk-3*.lib.
# ------------------------------------------
$wantArch = if ("${{ matrix.platform }}" -eq "x64") { "x64" } else { "x86" }
$wantPlat = "${{ matrix.platform }}"
$extractRoot = "C:\_gtk_extract"
if (Test-Path $extractRoot) { Remove-Item $extractRoot -Recurse -Force }
New-Item -Path $extractRoot -ItemType Directory -Force | Out-Null
function Get-PEMachine([string]$path) {
# Returns 0x8664 (x64), 0x014c (x86), or $null if not PE.
try {
$fs = [System.IO.File]::Open($path, 'Open', 'Read', 'ReadWrite')
try {
$br = New-Object System.IO.BinaryReader($fs)
$mz = $br.ReadUInt16()
if ($mz -ne 0x5A4D) { return $null } # "MZ"
$fs.Seek(0x3C, [System.IO.SeekOrigin]::Begin) | Out-Null
$peOff = $br.ReadUInt32()
$fs.Seek($peOff, [System.IO.SeekOrigin]::Begin) | Out-Null
$peSig = $br.ReadUInt32()
if ($peSig -ne 0x00004550) { return $null } # "PE\0\0"
$machine = $br.ReadUInt16()
return $machine
} finally {
$fs.Close()
}
} catch {
return $null
}
}
function Pick-GtkPrefix([string]$root, [string]$arch) {
# Find a plausible GTK3 MSVC import lib anywhere in extracted tree.
$libs = Get-ChildItem -Path $root -Recurse -File -Filter "*.lib" -ErrorAction SilentlyContinue
# Prefer exact well-known names first.
$preferred = @(
'^gtk-3\.0\.lib$',
'^gtk-3-0\.lib$',
'^gtk-3\.lib$'
)
foreach ($rx in $preferred) {
$cand = $libs | Where-Object { $_.Name -match $rx } |
Sort-Object -Property FullName |
Select-Object -First 1
if ($cand) {
# prefix is parent of the lib directory
return (Split-Path -Parent $cand.Directory.FullName)
}
}
# Fallback: any gtk-3*.lib
$cand2 = $libs | Where-Object { $_.Name -match '^gtk-3.*\.lib$' } |
Sort-Object -Property FullName |
Select-Object -First 1
if ($cand2) {
return (Split-Path -Parent $cand2.Directory.FullName)
}
return $null
}
function Ensure-Dir([string]$p) {
if (-not (Test-Path $p)) { New-Item -Path $p -ItemType Directory -Force | Out-Null }
}
function Copy-AliasLib([string]$gtkLibDir, [string]$targetName, [string[]]$sourceNameRegexes) {
$target = Join-Path $gtkLibDir $targetName
if (Test-Path $target) { return }
$allLibs = Get-ChildItem -Path $gtkLibDir -File -Filter "*.lib" -ErrorAction SilentlyContinue
$src = $null
foreach ($rx in $sourceNameRegexes) {
$src = $allLibs | Where-Object { $_.Name -match $rx } | Select-Object -First 1
if ($src) { break }
}
if ($src) {
Copy-Item $src.FullName $target -Force
Write-Host "Alias: $targetName <= $($src.Name)"
} else {
Write-Host "Alias not created: $targetName (no match in $gtkLibDir)"
}
}
function Ensure-GenmarshalWrapper([string]$gtkBinDir, [string]$gtkPrefix, [string]$wantPlat) {
Ensure-Dir $gtkBinDir
# Search *anywhere* under prefix for glib-genmarshal*.exe (layout varies by bundle)
$cands = Get-ChildItem -Path $gtkPrefix -Recurse -File -Filter "glib-genmarshal*.exe" -ErrorAction SilentlyContinue
if (-not $cands -or $cands.Count -eq 0) {
# Also try without extension, just in case the bundle ships a script
$cands2 = Get-ChildItem -Path $gtkPrefix -Recurse -File -Filter "glib-genmarshal*" -ErrorAction SilentlyContinue |
Where-Object { $_.Extension -ne ".pdb" -and $_.Extension -ne ".txt" }
if ($cands2 -and $cands2.Count -gt 0) {
Write-Host "Found non-.exe glib-genmarshal candidates:"
$cands2 | Select-Object -First 20 | ForEach-Object { Write-Host " - $($_.FullName)" }
}
throw "GTK3 bundle extracted, but no glib-genmarshal*.exe found anywhere under detected GTK prefix: $gtkPrefix"
}
$wantMachine = if ($wantPlat -eq "x64") { 0x8664 } else { 0x014c }
# Prefer machine-matching candidates.
$picked = $null
foreach ($c in $cands) {
$m = Get-PEMachine $c.FullName
if ($m -eq $wantMachine) { $picked = $c; break }
}
if (-not $picked) {
# Last resort: just take the first one and hope it's not the wrong arch.
$picked = $cands | Select-Object -First 1
Write-Host "Warning: could not confirm glib-genmarshal arch; using: $($picked.FullName)"
} else {
Write-Host "glib-genmarshal selected: $($picked.FullName)"
}
# The vcxproj calls:
# python.exe "...\bin\glib-genmarshal" ...
# So create a python script *without extension* at that exact path.
$genTarget = Join-Path $gtkBinDir "glib-genmarshal"
$wrapperLines = @(
"import os, subprocess, sys",
"tool = os.path.join(os.path.dirname(__file__), r'$($picked.Name)')",
"sys.exit(subprocess.call([tool] + sys.argv[1:]))"
)
$wrapperLines | Set-Content -Path $genTarget -Encoding ASCII
# Also ensure the actual exe is reachable from bin (copy if it lives elsewhere)
$exeInBin = Join-Path $gtkBinDir $picked.Name
if (-not (Test-Path $exeInBin)) {
Copy-Item $picked.FullName $exeInBin -Force
}
}
$gtkPrefix = $null
$headers = @{
"User-Agent" = "zoitechat-ci"
"Authorization" = "Bearer $env:GITHUB_TOKEN"
"X-GitHub-Api-Version" = "2022-11-28"
}
try {
$release = Invoke-RestMethod -Headers $headers -Uri "https://api.github.com/repos/wingtk/gvsbuild/releases/latest"
# Your logs show assets like:
# GTK3_Gvsbuild_2026.1.0_x64.zip
# GTK3_Gvsbuild_2026.1.0_x64.zip (etc)
$asset = $release.assets |
Where-Object { $_.name -imatch '^GTK3_Gvsbuild_.*_(x64|x86)\.zip$' -and $_.name -imatch "_$wantArch\.zip$" } |
Select-Object -First 1
if (-not $asset) {
Write-Host "Available assets:"
$release.assets | ForEach-Object { Write-Host " - $($_.name)" }
if ($wantArch -eq "x86") {
Write-Host "No GTK3 x86 asset found in wingtk/gvsbuild latest release; win32 build cannot use wingtk bundle."
}
throw "Could not find a GTK3 $wantArch zip in wingtk/gvsbuild latest release."
}
$bundlePath = "deps\gtk3-$wantArch-bundle.zip"
Invoke-WebRequest $asset.browser_download_url -OutFile $bundlePath
Expand-Archive -Force $bundlePath -DestinationPath $extractRoot
$gtkPrefix = Pick-GtkPrefix -root $extractRoot -arch $wantArch
if (-not $gtkPrefix) {
Write-Host "Dumping a few .lib filenames found (for debugging):"
Get-ChildItem -Path $extractRoot -Recurse -File -Filter "*.lib" -ErrorAction SilentlyContinue |
Select-Object -First 50 | ForEach-Object { Write-Host " - $($_.FullName)" }
throw "GTK3 bundle extracted, but could not locate any gtk-3*.lib to determine prefix."
}
Write-Host "Detected GTK prefix: $gtkPrefix"
} catch {
if ($wantArch -eq "x86") {
Write-Host "Wingtk GTK3 discovery failed for win32 (x86). Falling back to legacy GTK bundle (may not be GTK3)."
$gtkPrefix = $null
} else {
throw
}
}
if (-not $gtkPrefix) {
# Legacy bundle path (your original) as last resort for win32 to keep workflow moving.
Invoke-WebRequest https://github.com/zoitechat/gvsbuild/releases/download/zoitechat-2.17.0/gtk-${{ matrix.platform }}-2018-08-29-openssl1.1.7z -OutFile deps\gtk-${{ matrix.platform }}.7z
Ensure-Dir "C:\gtk-build\gtk"
& 7z.exe x deps\gtk-${{ matrix.platform }}.7z -oC:\gtk-build\gtk | Out-Null
} else {
# Normalize GTK location to the path your solution already expects:
# C:\gtk-build\gtk\<platform>\release\...
$normBase = "C:\gtk-build\gtk\$wantPlat"
Ensure-Dir $normBase
$normRel = Join-Path $normBase "release"
if (Test-Path $normRel) {
cmd /c rmdir /s /q "$normRel" | Out-Null
}
New-Item -Path $normRel -ItemType Junction -Value $gtkPrefix | Out-Null
$gtkBin = Join-Path $normRel "bin"
$gtkLib = Join-Path $normRel "lib"
$gtkInc = Join-Path $normRel "include"
Ensure-Dir $gtkBin
Ensure-Dir $gtkLib
Ensure-Dir $gtkInc
# Provide python wrapper "glib-genmarshal" at the exact location vcxproj calls.
Ensure-GenmarshalWrapper -gtkBinDir $gtkBin -gtkPrefix $gtkPrefix -wantPlat $wantPlat
# Compatibility aliases (older vcxproj names)
Copy-AliasLib $gtkLib "gtk-win32-2.0.lib" @(
'^gtk-3\.0\.lib$',
'^gtk-3-0\.lib$',
'^gtk-3\.lib$',
'^gtk-3.*\.lib$'
)
# OpenSSL legacy names (some projects still look for ssleay32/libeay32)
Copy-AliasLib $gtkLib "ssleay32.lib" @('^libssl\.lib$', '^ssl\.lib$')
Copy-AliasLib $gtkLib "libeay32.lib" @('^libcrypto\.lib$', '^crypto\.lib$')
# libxml2 name used by older link lines
Copy-AliasLib $gtkLib "libxml2.lib" @('^libxml2\.lib$', '^libxml2-2\.0\.lib$', '^libxml2.*\.lib$')
# If the GTK bundle included LuaJIT headers somewhere, try to mirror them into include\
$luajitDirs = Get-ChildItem -Path $gtkPrefix -Recurse -Directory -ErrorAction SilentlyContinue |
Where-Object { $_.Name -match '^luajit-2\.1$' -or $_.Name -match '^luajit$' } |
Select-Object -First 1
if ($luajitDirs) {
foreach ($h in @("lua.h","lualib.h","lauxlib.h","luaconf.h")) {
$src = Join-Path $luajitDirs.FullName $h
if (Test-Path $src) {
Copy-Item $src (Join-Path $gtkInc $h) -Force
}
}
}
}
# Resolve python root from setup-python
$pyRoot = $env:pythonLocation
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
# Create BOTH paths because the .vcxproj hard-codes python-3.14\...
foreach ($pyDir in @("C:\gtk-build\python-3.14.2", "C:\gtk-build\python-3.14")) {
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
$target = Join-Path $pyDir "${{ matrix.platform }}"
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
New-Item -Path $pyDir -Name "${{ matrix.platform }}" -ItemType Junction -Value $pyRoot | Out-Null
}
python -m pip install --upgrade pip
python -m pip install cffi
- name: Build
shell: cmd
run: |
call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat"
set "GTKROOT=C:\gtk-build\gtk\${{ matrix.platform }}\release"
set "PYTHON_DIR=C:\gtk-build\python-3.14.2\${{ matrix.platform }}"
if not exist "%PYTHON_DIR%\libs\python314.lib" (
echo Missing %PYTHON_DIR%\libs\python314.lib
dir "%PYTHON_DIR%\libs"
exit /b 1
)
rem Make sure MSVC finds GTK headers/libs via environment too
if exist "%GTKROOT%\include" set "INCLUDE=%GTKROOT%\include;%INCLUDE%"
if exist "%GTKROOT%\lib" set "LIB=%GTKROOT%\lib;%LIB%"
set "LIB=%PYTHON_DIR%\libs;%LIB%"
set "INCLUDE=%PYTHON_DIR%\include;%INCLUDE%"
rem Fix Lua plugin build: include LuaJIT headers from repo if present
if exist "plugins\lua\luajit\src\lua.h" set "INCLUDE=%CD%\plugins\lua\luajit\src;%INCLUDE%"
if exist "plugins\lua\luajit-2.1\src\lua.h" set "INCLUDE=%CD%\plugins\lua\luajit-2.1\src;%INCLUDE%"
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
- name: Preparing Artifacts
shell: cmd
run: |
move ..\zoitechat-build\${{ matrix.platform }}\ZoiteChat*.exe .\
move ..\zoitechat-build .\
- name: Upload Installer
id: upload_installer
uses: actions/upload-artifact@v4
with:
name: Installer ${{ matrix.arch }}
path: ZoiteChat*.exe
- name: Attest Installer (Artifact Attestation)
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
uses: actions/attest-build-provenance@v3
with:
subject-name: Installer ${{ matrix.arch }}
subject-digest: sha256:${{ steps.upload_installer.outputs.artifact-digest }}
- name: Upload Build Files
id: upload_buildfiles
uses: actions/upload-artifact@v4
with:
name: Build Files ${{ matrix.arch }}
path: zoitechat-build
- name: Attest Build Files (Artifact Attestation)
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
uses: actions/attest-build-provenance@v3
with:
subject-name: Build Files ${{ matrix.arch }}
subject-digest: sha256:${{ steps.upload_buildfiles.outputs.artifact-digest }}