From fdda0fbe082c78e42b2576270e299f951e1b2d23 Mon Sep 17 00:00:00 2001 From: Alex Schumann Date: Sun, 23 Aug 2026 13:26:28 -0700 Subject: [PATCH 1/5] ci: build LuaJIT and fetch the CPython embeddable runtime into the dep stack gvsbuild's luajit recipe runs .\msvcbuild through CreateProcess, which cannot launch a .bat, so build LuaJIT here instead: a pinned v2.1 commit, compiled by msvcbuild.bat under vcvars64 via a generated .cmd (sidestepping cmd /c quote stripping), verified by its artifacts since the batch file's exit code is not trustworthy, and installed into the gvsbuild prefix exactly where poxchat.props already looks (lua51.lib, bin\lua51.dll, include\luajit-2.1). lgi and --enable-gi stay out; the lua plugin needs neither. The python plugin's runtime comes from CPython's embeddable package, pinned by version and sha256 and unpacked to C:\gtk-build\python-embed for the workflow to stage. Its minor version is load-bearing in poxchat.props (Python3Lib), the workflow's setup-python, and poxchat.iss.tt's python313.* names -- the param comment says to bump all four together. --- win32/ci/build-deps.ps1 | 96 ++++++++++++++++++++++++++++++++++++----- 1 file changed, 86 insertions(+), 10 deletions(-) diff --git a/win32/ci/build-deps.ps1 b/win32/ci/build-deps.ps1 index c04a3ec7..609f0630 100644 --- a/win32/ci/build-deps.ps1 +++ b/win32/ci/build-deps.ps1 @@ -8,12 +8,17 @@ solution's UserMacros point at: gtk\\release gvsbuild prefix: GTK4, OpenSSL, libxml2, sqlite, - luajit, libcurl, enchant, and gettext (win32\nls - compiles the .po catalogues with its msgfmt.exe) + libcurl, enchant, and gettext (win32\nls compiles + the .po catalogues with its msgfmt.exe). LuaJIT is + built below and installed into this prefix, which is + where poxchat.props looks for lua51.lib and + include\luajit-2.1 jansson\ static jansson -- DepLibs wants jansson.lib and copy.vcxproj ships no jansson dll libwebsockets\ static libwebsockets, same reasoning WinSparkle\\ prebuilt release, for the upd plugin + python-embed\ CPython embeddable runtime, staged whole into the + tree so the python plugin needs no system Python cert\cacert.pem CA bundle, shipped into the tree as cert.pem Nothing outside -BuildRoot is written, so this is safe to run on a developer @@ -33,6 +38,15 @@ param ( [string] $JanssonTag = 'v2.15.1', [string] $LibWebSocketsTag = 'v4.5.8', [string] $WinSparkleVersion = '0.9.4', + # LuaJIT's v2.1 branch is release-less by policy, so pin a commit rather + # than the floating v2.1.ROLLING tag. + [string] $LuaJITCommit = '1ee778a4e37122d8ca7d5733c590a47dafd6b15c', + # The runtime the python plugin ships. Its minor version is load-bearing + # in three other places -- Python3Lib in win32\poxchat.props, setup-python + # in the workflow, and the python313.* names in poxchat.iss.tt -- so bump + # all four together, and the hash with the version. + [string] $PythonEmbedVersion = '3.13.9', + [string] $PythonEmbedSha256 = '91d828c2da3a029b41699e918674a0cb379c02cf20dab9c501306885f837402a', [string] $CMakeGenerator = 'Visual Studio 17 2022' ) @@ -42,13 +56,12 @@ $ProgressPreference = 'SilentlyContinue' # Invoke-WebRequest crawls with the pro # gvsbuild project names, not pkg-config names. gettext is here for msgfmt.exe, # which win32\nls compiles the .po catalogues with. # -# luajit is absent, and with it lgi (which depends on it) and --enable-gi (which -# exists to produce lgi's typelibs). gvsbuild builds luajit by running -# '.\msvcbuild' through CreateProcess, which cannot launch a .bat file and finds -# nothing without the extension -- it fails identically every time, so the lua -# plugin is out of the first build. Re-adding it means building LuaJIT here the -# way jansson and libwebsockets are built below; the installer script also names -# lua and lgi files unconditionally, so that has to be settled at the same time. +# luajit is deliberately not asked of gvsbuild: it runs '.\msvcbuild' through +# CreateProcess, which cannot launch a .bat file and finds nothing without the +# extension -- it fails identically every time. LuaJIT is built directly below +# instead. lgi and --enable-gi (which exists to produce lgi's typelibs) stay +# out with it: the lua plugin itself needs neither, they were only ever for +# GObject bindings inside Lua scripts. $GvsbuildProjects = @( 'gtk4', 'openssl', @@ -230,6 +243,68 @@ if (Test-Path $lwsStatic) { Copy-Item $lwsStatic (Join-Path $lwsPrefix 'lib\websockets.lib') -Force } +Write-Step 'LuaJIT' +# msvcbuild.bat wants cl on PATH and to be run from src\. Doing the vcvars +# call and the cd inside a generated .cmd sidesteps cmd /c's quote-stripping +# rules, which would otherwise eat the spaces in the Visual Studio path. +# msvcbuild.bat does not reliably set an exit code, so the proof of success is +# the artifacts themselves, checked below. Always a full rebuild, but that is +# under a minute. +$ljSrc = Get-SourceTree "https://github.com/LuaJIT/LuaJIT/archive/$LuaJITCommit.zip" "LuaJIT-$($LuaJITCommit.Substring(0, 12))" +$vswhere = Join-Path ${env:ProgramFiles(x86)} 'Microsoft Visual Studio\Installer\vswhere.exe' +$vsRoot = & $vswhere -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath +if (-not $vsRoot) { + throw 'vswhere found no Visual Studio with the C++ toolset' +} +$vcvars = Join-Path $vsRoot 'VC\Auxiliary\Build\vcvars64.bat' +$ljBuild = Join-Path $srcRoot 'build-luajit.cmd' +@( + "call `"$vcvars`" || exit /b 1", + "cd /d `"$ljSrc\src`" || exit /b 1", + 'call msvcbuild.bat' +) | Set-Content -Path $ljBuild -Encoding ascii +Invoke-Checked 'LuaJIT msvcbuild' { cmd /c $ljBuild } +foreach ($artifact in 'lua51.dll', 'lua51.lib') { + if (-not (Test-Path (Join-Path $ljSrc "src\$artifact"))) { + throw "LuaJIT build produced no $artifact" + } +} +# Install into the gvsbuild prefix, in the layout poxchat.props expects: +# LuaInclude points at include\luajit-2.1, lua.vcxproj links lua51.lib out of +# the shared lib dir, and copy.vcxproj stages bin\lua51.dll when it exists. +$ljInclude = Join-Path $prefix 'include\luajit-2.1' +New-Item -ItemType Directory -Force -Path $ljInclude | Out-Null +Copy-Item (Join-Path $ljSrc 'src\lua51.dll') (Join-Path $prefix 'bin') -Force +Copy-Item (Join-Path $ljSrc 'src\lua51.lib') (Join-Path $prefix 'lib') -Force +foreach ($header in 'lua.h', 'luaconf.h', 'lualib.h', 'lauxlib.h', 'lua.hpp', 'luajit.h') { + Copy-Item (Join-Path $ljSrc "src\$header") $ljInclude -Force +} + +Write-Step "CPython $PythonEmbedVersion embeddable runtime" +# The python plugin is a cffi embedding: hcpython3.dll links python313.dll and +# needs a stdlib beside it at run time. The embeddable package is CPython's +# designed-for-this layout -- python313.dll, python313.zip, python313._pth +# (whose '.' entry keeps the install dir on sys.path, where copy.vcxproj also +# drops _cffi_backend*.pyd), and the stdlib's extension modules. The workflow +# stages this directory into the tree after the copy project runs. +$pyDir = Join-Path $BuildRoot 'python-embed' +$pyZip = Join-Path $srcRoot "python-$PythonEmbedVersion-embed-amd64.zip" +New-Item -ItemType Directory -Force -Path $srcRoot | Out-Null +if (-not (Test-Path $pyZip)) { + $pyUrl = "https://www.python.org/ftp/python/$PythonEmbedVersion/python-$PythonEmbedVersion-embed-amd64.zip" + Write-Host "downloading $pyUrl" + Invoke-WebRequest -Uri $pyUrl -OutFile $pyZip -UseBasicParsing +} +$pyDigest = (Get-FileHash -Path $pyZip -Algorithm SHA256).Hash +if ($pyDigest -ne $PythonEmbedSha256.ToUpper()) { + Remove-Item $pyZip -Force + throw "python embeddable zip hashed $pyDigest, expected $PythonEmbedSha256" +} +if (Test-Path $pyDir) { + Remove-Item $pyDir -Recurse -Force +} +Expand-Archive -Path $pyZip -DestinationPath $pyDir -Force + Write-Step 'WinSparkle' $wsPrefix = Join-Path $BuildRoot "WinSparkle\$Platform" $wsSrc = Get-SourceTree "https://github.com/vslavik/winsparkle/releases/download/v$WinSparkleVersion/WinSparkle-$WinSparkleVersion.zip" "WinSparkle-$WinSparkleVersion" @@ -268,7 +343,8 @@ New-Item -ItemType Directory -Force -Path $certDir | Out-Null Invoke-WebRequest -Uri 'https://curl.se/ca/cacert.pem' -OutFile (Join-Path $certDir 'cacert.pem') -UseBasicParsing Write-Step 'done' -Write-Host "gvsbuild prefix: $prefix" +Write-Host "gvsbuild prefix: $prefix (LuaJIT installed into it)" Write-Host "jansson: $janssonPrefix" Write-Host "libwebsockets: $lwsPrefix" Write-Host "WinSparkle: $wsPrefix" +Write-Host "python runtime: $pyDir" From 543e56dbb1a6135de21df27490b148d4273cdd15 Mon Sep 17 00:00:00 2001 From: Alex Schumann Date: Sun, 23 Aug 2026 13:26:28 -0700 Subject: [PATCH 2/5] ci: build the lua and python3 plugins, stage the bundled Python runtime setup-python moves to 3.13 to match Python3Lib=python313, and installs cffi: generate_plugin.py renders python.c through it at build time, and copy.vcxproj stages _cffi_backend*.pyd out of its site-packages for run time. The new staging step lays the embeddable runtime into the tree after the copy project and before the import check and smoke test, so hcpython3.dll's python313.dll dependency is exercised against the tree that ships. It never overwrites -- gvsbuild's sqlite3.dll is already staged and python's _sqlite3.pyd is content with any current sqlite -- and it drops the package's own interpreter binaries, which nothing runs. poxchat.props' default YourPython3Path said C:\Python314 while Python3Lib said python313; align the default on 3.13. --- .github/workflows/windows-build.yml | 46 ++++++++++++++++++++++++----- win32/poxchat.props | 2 +- 2 files changed, 40 insertions(+), 8 deletions(-) diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index a8a0696d..2aea1fbe 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -59,9 +59,18 @@ jobs: with: submodules: recursive + # 3.13 to match the rest of the python plugin's pins: Python3Lib + # (python313) in win32\poxchat.props, the embeddable runtime in + # build-deps.ps1, and the python313.* names in poxchat.iss.tt. cffi is + # both a build dependency (generate_plugin.py renders python.c through + # it) and the source of the _cffi_backend.pyd that copy.vcxproj stages + # out of this interpreter's site-packages. - uses: actions/setup-python@v5 with: - python-version: '3.12' + python-version: '3.13' + + - name: Install cffi + run: python -m pip install --disable-pip-version-check cffi # The whole C:\gtk-build tree: install prefix, intermediates and source # archives. The intermediates belong in here because gvsbuild's @@ -107,20 +116,43 @@ jobs: if (-not $line) { throw 'no version in meson.build' } "version=$($line.Matches[0].Groups[1].Value)" >> $env:GITHUB_OUTPUT - # Split by target so a failure names the piece that broke. perl, python3 - # and htm (the C# theme manager) are left out until the rest is green: - # they need a matching Strawberry Perl / CPython / .NET on the runner. - # lua is out too, for want of a LuaJIT gvsbuild can build -- see - # build-deps.ps1. + # Split by target so a failure names the piece that broke. perl is + # dropped deliberately, htm (the C# theme manager) needs a .NET toolchain + # nobody misses -- neither ships. lua links the LuaJIT that + # build-deps.ps1 installs into the gvsbuild prefix; python3 links + # setup-python's import library and ships the embeddable runtime staged + # below. - name: Build core run: msbuild win32\poxchat.sln ${{ env.MSBUILD_PROPS }} /p:YourPython3Path=$env:pythonLocation /t:"poxchat\common;poxchat\fe-gtk;poxchat\fe-text" - name: Build plugins - run: msbuild win32\poxchat.sln ${{ env.MSBUILD_PROPS }} /p:YourPython3Path=$env:pythonLocation /t:"plugins\checksum;plugins\exec;plugins\fishlim;plugins\sysinfo;plugins\winamp;plugins\upd;plugins\notifications-winrt;external\libenchant_win8" + run: msbuild win32\poxchat.sln ${{ env.MSBUILD_PROPS }} /p:YourPython3Path=$env:pythonLocation /t:"plugins\checksum;plugins\exec;plugins\fishlim;plugins\sysinfo;plugins\winamp;plugins\upd;plugins\notifications-winrt;external\libenchant_win8;scripting\lua;scripting\python3" - name: Stage the distributable tree run: msbuild win32\poxchat.sln ${{ env.MSBUILD_PROPS }} /p:YourPython3Path=$env:pythonLocation /t:"release\nls;release\copy" + # The embeddable package from build-deps.ps1, minus its interpreter + # binaries (scripts run inside poxchat) and its signature catalog. + # Never overwrite: the tree already stages gvsbuild's sqlite3.dll, and + # python's _sqlite3.pyd is happy with any current sqlite beside it. + # Runs before the import check and smoke test so hcpython3.dll's + # python313.dll dependency is judged against the tree that ships. + - name: Stage the Python runtime + run: | + $rel = "${{ env.BUILD_DIR }}\x64\rel" + $skip = 'python.exe', 'pythonw.exe', 'python.cat' + Get-ChildItem C:\gtk-build\python-embed -File | + Where-Object { $skip -notcontains $_.Name } | + ForEach-Object { + $name = if ($_.Name -eq 'LICENSE.txt') { 'PYTHON-LICENSE.txt' } else { $_.Name } + $dest = Join-Path $rel $name + if (Test-Path $dest) { + Write-Host "keeping staged $name" + } else { + Copy-Item $_.FullName $dest + } + } + # The smoke test below proves the tree runs, but STATUS_DLL_NOT_FOUND does # not say which DLL is missing, so name the whole set first -- same # reasoning as the inventory step: one runner cycle per missing file is diff --git a/win32/poxchat.props b/win32/poxchat.props index f1d87549..5d1838bd 100644 --- a/win32/poxchat.props +++ b/win32/poxchat.props @@ -7,7 +7,7 @@ c:\gtk-build\gtk4 c:\gtk-build\gendef C:\Strawberry\perl - C:\Python314\ + C:\Python313\ c:\gtk-build\WinSparkle C:\Program Files\OpenSSL From 576a9a7bb9247cd1314e7ecbeca5986dee7bf2d7 Mon Sep 17 00:00:00 2001 From: Alex Schumann Date: Sun, 23 Aug 2026 13:26:43 -0700 Subject: [PATCH 3/5] installer: drop perl and the theme manager, ship a self-contained python perl is no longer a supported plugin on Windows and thememan never builds here (it wants a .NET toolchain), so their payloads leave every section they touched: [Components], [Registry] (.hct associations), [Run], [Files], [Icons], and the Pascal [Code] that downloaded Perl/Python/dotnet installers at install time. CheckDLL, CheckVC2013Install and CheckDotNetInstall existed only for those flows and go with them; idp now fetches just vcredist and the spelling dictionaries. python switches from 'requires Python 3.8 on the system' to the CI-staged embeddable runtime: python313.dll and friends become ordinary [Files] entries, and the component joins the normal install type alongside lua (now LuaJIT only -- no lgi payloads, which never build here). Also add the staged DLLs the [Files] list never learned about (sqlite3, the gdk-pixbuf loader chain, and the conditionally staged libcurl/psl/ICU set, mirrored with skipifsourcedoesntexist), so an installed tree matches the portable zip instead of dying on its first missing DLL. --- win32/ci/make-installer.ps1 | 3 +- win32/installer/poxchat.iss.tt | 119 +++++++++++---------------------- 2 files changed, 41 insertions(+), 81 deletions(-) diff --git a/win32/ci/make-installer.ps1 b/win32/ci/make-installer.ps1 index 0a50f7e3..3f9d884a 100644 --- a/win32/ci/make-installer.ps1 +++ b/win32/ci/make-installer.ps1 @@ -9,7 +9,8 @@ separate, skippable step. poxchat.iss.tt targets Inno Setup 5 and #includes idp.iss from the Inno - Download Plugin (which fetches vcredist/perl/python at install time). The + Download Plugin (which fetches vcredist and the spelling dictionaries at + install time). The hosted runners ship Inno Setup 6 and no idp, so -Provision installs both: IS5 from jrsoftware, idp from the mirror hexchat kept on its gvsbuild releases, the upstream host having gone away years ago. diff --git a/win32/installer/poxchat.iss.tt b/win32/installer/poxchat.iss.tt index f07a958b..2b226b7e 100644 --- a/win32/installer/poxchat.iss.tt +++ b/win32/installer/poxchat.iss.tt @@ -59,7 +59,6 @@ Name: "custom"; Description: "Custom Installation"; Flags: iscustom [Components] Name: "libs"; Description: "PoxChat"; Types: normal minimal custom; Flags: fixed Name: "xctext"; Description: "PoxChat-Text"; Types: custom; Flags: disablenouninstallwarning -Name: "xtm"; Description: "PoxChat Theme Manager"; Types: normal custom; Flags: disablenouninstallwarning Name: "icons"; Description: "Create Shortcuts"; Types: custom; Flags: disablenouninstallwarning Name: "icons\desktopicon"; Description: "Create Desktop Shortcut"; Types: custom; Flags: disablenouninstallwarning Name: "icons\quicklaunchicon"; Description: "Create Quick Launch Shortcut"; Types: custom; Flags: disablenouninstallwarning @@ -74,8 +73,7 @@ Name: "plugins\upd"; Description: "Update Checker"; Types: normal custom; Flags: Name: "plugins\winamp"; Description: "Winamp"; Types: custom; Flags: disablenouninstallwarning Name: "langs"; Description: "Language Interfaces"; Types: custom; Flags: disablenouninstallwarning Name: "langs\lua"; Description: "Lua"; Types: normal custom; Flags: disablenouninstallwarning -Name: "langs\perl"; Description: "Perl (requires Perl 5.20)"; Types: custom; Flags: disablenouninstallwarning -Name: "langs\python"; Description: "Python (requires Python 3.8)"; Types: custom; Flags: disablenouninstallwarning +Name: "langs\python"; Description: "Python"; Types: normal custom; Flags: disablenouninstallwarning [Tasks] Name: portable; Description: "Yes"; GroupDescription: "Portable Mode: Stores configuration files within install directory for portable drives."; Flags: unchecked @@ -88,22 +86,10 @@ Root: HKCR; Subkey: "irc\DefaultIcon"; ValueType: string; ValueName: ""; ValueDa Root: HKCR; Subkey: "irc\shell"; ValueType: string; ValueName: ""; ValueData: "open"; Flags: uninsdeletevalue; Tasks: not portable Root: HKCR; Subkey: "irc\shell\open\command"; ValueType: string; ValueName: ""; ValueData: "{app}\poxchat.exe --url=""%1"""; Flags: uninsdeletevalue; Tasks: not portable -Root: HKCR; Subkey: ".hct"; ValueType: none; ValueName: ""; ValueData: ""; Flags: deletekey uninsdeletekey; Components:xtm; Tasks: not portable -Root: HKCR; Subkey: ".hct"; ValueType: string; ValueName: ""; ValueData: "PoxChat Theme File"; Flags: uninsdeletevalue; Components:xtm; Tasks: not portable -Root: HKCR; Subkey: ".hct"; ValueType: string; ValueName: "PoxChat Theme File"; ValueData: ""; Flags: uninsdeletevalue; Components:xtm; Tasks: not portable -Root: HKCR; Subkey: ".hct\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\thememan.exe,0"; Flags: uninsdeletevalue; Components:xtm; Tasks: not portable -Root: HKCR; Subkey: ".hct\shell"; ValueType: string; ValueName: ""; ValueData: "open"; Flags: uninsdeletevalue; Components:xtm; Tasks: not portable -Root: HKCR; Subkey: ".hct\shell\open\command"; ValueType: string; ValueName: ""; ValueData: "{app}\thememan.exe ""%1"""; Flags: uninsdeletevalue; Components:xtm; Tasks: not portable - [Run] Filename: "{app}\poxchat.exe"; Description: "Run PoxChat after closing the Wizard"; Flags: nowait postinstall skipifsilent Filename: "https://github.com/evilnet/poxchat/releases"; Description: "See what's changed"; Flags: shellexec runasoriginaluser postinstall skipifsilent unchecked Filename: "{tmp}\vcredist.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Flags: skipifdoesntexist; Tasks: not portable -Filename: "{tmp}\vcredist2013.exe"; Parameters: "/install /quiet /norestart"; StatusMsg: "Installing Visual C++ Redistributable"; Flags: skipifdoesntexist; Tasks: not portable -Filename: "{tmp}\dotnet4.exe"; Parameters: "/q /norestart"; StatusMsg: "Installing .NET"; Components: xtm; Flags: skipifdoesntexist; Tasks: not portable -Filename: "{tmp}\perl.msi"; StatusMsg: "Installing Perl"; Components: langs\perl; Flags: shellexec skipifdoesntexist; Tasks: not portable -Filename: "{tmp}\python.msi"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable -Filename: "{tmp}\python.exe"; Parameters: "InstallAllUsers=1 PrependPath=1"; StatusMsg: "Installing Python"; Components: langs\python; Flags: shellexec skipifdoesntexist; Tasks: not portable Filename: "{tmp}\spelling-dicts.exe"; Parameters: "/verysilent"; StatusMsg: "Installing Spelling Dictionaries"; Components: spell; Flags: skipifdoesntexist; Tasks: not portable [Files] @@ -155,6 +141,22 @@ Source: "pangocairo-1.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Componen Source: "pangoft2-1.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs Source: "pangowin32-1.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs Source: "zlib1.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "sqlite3.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "jpeg62.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "tiff.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "harfbuzz-subset.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "cairo-gobject-2.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "cairo-script-interpreter-2.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "rsvg-2-2.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "pixman-1-0.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +Source: "libexpat.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: libs +; copy.vcxproj stages these conditionally, so mirror its optionality here +Source: "soup-3.0-0.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: libs +Source: "libcurl.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: libs +Source: "libcurl-x64.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: libs +Source: "psl-5.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: libs +Source: "icuuc*.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: libs +Source: "icudt*.dll"; DestDir: "{app}"; Flags: ignoreversion skipifsourcedoesntexist; Components: libs Source: "plugins\hcnotifications-winrt.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: libs @@ -165,13 +167,8 @@ Source: "lib\enchant-2\*"; DestDir: "{app}\lib\enchant-2"; Flags: ignoreversion ; GTK4 uses CSS theming only - no theme engine DLLs needed -Source: "girepository-1.0-1.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\lua +; LuaJIT only -- lgi and its typelibs are not built, see win32\ci\build-deps.ps1 Source: "lua51.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\lua -Source: "lib\lua\lgi\*.dll"; DestDir: "{app}\lib\lua\lgi"; Flags: ignoreversion; Components: langs\lua -Source: "lib\girepository-1.0\*.typelib"; DestDir: "{app}\lib\girepository-1.0"; Flags: ignoreversion; Components: langs\lua -Source: "share\lua\*.lua"; DestDir: "{app}\share\lua"; Flags: ignoreversion; Components: langs\lua -Source: "share\lua\lgi\*.lua"; DestDir: "{app}\share\lua\lgi"; Flags: ignoreversion; Components: langs\lua -Source: "share\lua\lgi\override\*.lua"; DestDir: "{app}\share\lua\lgi\override"; Flags: ignoreversion; Components: langs\lua Source: "plugins\hclua.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\lua Source: "plugins\hcchecksum.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\checksum @@ -188,16 +185,29 @@ Source: "WinSparkle.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: pl Source: "plugins\hcwinamp.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\winamp Source: "share\system.png"; DestDir: "{app}\share"; Flags: ignoreversion; Components: plugins\sysinfo Source: "plugins\hcsysinfo.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: plugins\sysinfo -Source: "plugins\hcperl.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\perl +; The python plugin is self-contained: hcpython3.dll links python313.dll and +; the CI-staged embeddable runtime beside it supplies the interpreter and +; stdlib -- python313._pth's '.' entry keeps {app} on sys.path, which is where +; *.pyd (the stdlib's extension modules plus cffi's _cffi_backend) live. +; python313 appears by name here on purpose; the version is pinned in +; build-deps.ps1 and the names below move with it. Source: "python\*.py"; DestDir: "{app}\python"; Flags: ignoreversion; Components: langs\python - Source: "plugins\hcpython3.dll"; DestDir: "{app}\plugins"; Flags: ignoreversion; Components: langs\python -Source: "_cffi_backend.cp3*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "python313.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "python3.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "python313.zip"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "python313._pth"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "*.pyd"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "libffi-8.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "libcrypto-3.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "libssl-3.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "vcruntime140.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "vcruntime140_1.dll"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python +Source: "PYTHON-LICENSE.txt"; DestDir: "{app}"; Flags: ignoreversion; Components: langs\python Source: "poxchat.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: libs Source: "poxchat-text.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: xctext -Source: "thememan.exe"; DestDir: "{app}"; Flags: ignoreversion; Components: xtm [Icons] Name: "{group}\PoxChat"; Filename: "{app}\poxchat.exe"; AppUserModelID: "PoxChat.Desktop.Notify"; Tasks: not portable; Check: not WizardNoIcons @@ -206,7 +216,6 @@ Name: "{group}\PoxChat ChangeLog"; Filename: "{app}\changelog.url"; IconFilename Name: "{group}\PoxChat ReadMe"; Filename: "{app}\readme.url"; IconFilename: "{sys}\shell32.dll"; IconIndex: 23; Tasks: not portable; Check: not WizardNoIcons Name: "{group}\PoxChat Config Folder"; Filename: "%APPDATA%\PoxChat\"; Tasks: not portable; Check: not WizardNoIcons Name: "{group}\PoxChat-Text"; Filename: "{app}\poxchat-text.exe"; Components: xctext; Tasks: not portable; Check: not WizardNoIcons -Name: "{group}\PoxChat Theme Manager"; Filename: "{app}\thememan.exe"; Components: xtm; Tasks: not portable; Check: not WizardNoIcons Name: "{group}\Uninstall PoxChat"; Filename: "{uninstallexe}"; Tasks: not portable; Check: not WizardNoIcons Name: "{commondesktop}\PoxChat"; Filename: "{app}\poxchat.exe"; AppUserModelID: "PoxChat.Desktop.Notify"; Components: icons\desktopicon; Tasks: not portable Name: "{commonappdata}\Microsoft\Internet Explorer\Quick Launch\PoxChat"; Filename: "{app}\poxchat.exe"; Components: icons\quicklaunchicon; Tasks: not portable @@ -236,30 +245,12 @@ begin #endif end; -///////////////////////////////////////////////////////////////////// -function CheckDLL(DLLName: String): Boolean; -var - ResultCode: Integer; -begin - if ExecAsOriginalUser(GetSysDir() + 'where.exe', '/Q ' + DLLName, - '', SW_HIDE, ewWaitUntilTerminated, ResultCode) then - Result := ResultCode = 0 // 0 is success - else - Result := False; -end; - ///////////////////////////////////////////////////////////////////// function CheckVCInstall(): Boolean; begin Result := FileExists(GetSysDir() + 'vcruntime140.dll');; end; -function CheckVC2013Install(): Boolean; -begin - Result := FileExists(GetSysDir() + 'msvcr120.dll');; -end; - - ///////////////////////////////////////////////////////////////////// function CheckSpellInstall(): Boolean; var @@ -275,22 +266,14 @@ begin end; ///////////////////////////////////////////////////////////////////// -function CheckDotNetInstall(): Boolean; -begin - Result := RegKeyExists(HKLM, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4'); -end; - -///////////////////////////////////////////////////////////////////// -// Sets up the automatic downloads +// Sets up the automatic downloads. The scripting runtimes ship inside the +// installer these days -- LuaJIT and the embeddable CPython are ordinary +// [Files] entries -- so only vcredist and the spelling dictionaries are +// fetched at install time. ///////////////////////////////////////////////////////////////////// procedure CurPageChanged(CurPageID: Integer); var REDIST: String; - REDIST_2013: String; - PERL: String; - PY2: String; - PY3: String; - DOTNET: String; SPELL: String; begin if(CurPageID = wpReady) then @@ -302,40 +285,16 @@ begin #if APPARCH == "x64" REDIST := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/vcredist_2015_x64.exe'; - REDIST_2013 := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/vcredist_2013_x64.exe'; - PERL := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/Perl.5.20.0.x64.msi'; - PY3 := 'https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe'; #else REDIST := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/vcredist_2015_x86.exe'; - REDIST_2013 := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/vcredist_2013_x86.exe'; - PERL := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/Perl.5.20.0.x86.msi'; - PY3 := 'https://www.python.org/ftp/python/3.8.10/python-3.8.10.exe'; #endif - DOTNET := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/dotnet_40.exe'; SPELL := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/HexChat.Spelling.Dictionaries.r2.exe'; if not CheckVCInstall() then idpAddFile(REDIST, ExpandConstant('{tmp}\vcredist.exe')); - if IsComponentSelected('xtm') and not CheckDotNetInstall() then - idpAddFile(DOTNET, ExpandConstant('{tmp}\dotnet4.exe')); - if IsComponentSelected('spell') and not CheckSpellInstall() then idpAddFile(SPELL, ExpandConstant('{tmp}\spelling-dicts.exe')); - - if not WizardSilent() then - begin - if IsComponentSelected('langs\perl') and not CheckDLL('perl520.dll') then - begin - if not CheckVC2013Install() then - idpAddFile(REDIST_2013, ExpandConstant('{tmp}\vcredist2013.exe')); - - idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi')) - end; - - if IsComponentSelected('langs\python\python3') and not CheckDLL('python38.dll') then - idpAddFile(PY3, ExpandConstant('{tmp}\python.exe')); - end; end; end; end; From 0fecd3d6d8af62a000331180bf960f1e5ab2ece3 Mon Sep 17 00:00:00 2001 From: Alex Schumann Date: Sun, 23 Aug 2026 13:26:43 -0700 Subject: [PATCH 4/5] docs: windows CI builds lua and python now; perl and htm are dropped Rewrite 'Currently left out' into what actually holds: how LuaJIT and the embeddable CPython get into the tree, the four places the Python minor version is pinned, and that perl/thememan are deliberate removals rather than pending work. --- docs/areas/windows-ci-build.md | 44 +++++++++++++++++++++++----------- 1 file changed, 30 insertions(+), 14 deletions(-) diff --git a/docs/areas/windows-ci-build.md b/docs/areas/windows-ci-build.md index c1098f68..639184fd 100644 --- a/docs/areas/windows-ci-build.md +++ b/docs/areas/windows-ci-build.md @@ -9,7 +9,7 @@ when re-enabling one of the targets it currently leaves out. | Piece | Does | |-------|------| -| `win32/ci/build-deps.ps1` | Builds the whole dependency stack into `C:\gtk-build` — gvsbuild for GTK4/OpenSSL/libxml2/sqlite/libcurl/enchant/gettext, then static jansson and libwebsockets, prebuilt WinSparkle, and the CA bundle | +| `win32/ci/build-deps.ps1` | Builds the whole dependency stack into `C:\gtk-build` — gvsbuild for GTK4/OpenSSL/libxml2/sqlite/libcurl/enchant/gettext, then static jansson and libwebsockets, LuaJIT into the gvsbuild prefix, the CPython embeddable runtime, prebuilt WinSparkle, and the CA bundle | | `win32/ci/check-imports.ps1` | Walks PE imports of the staged tree and names every DLL it is missing | | `win32/ci/make-installer.ps1` | Generates `poxchat.iss` from its template and runs Inno Setup 5 | @@ -71,19 +71,35 @@ prefix is a convenient corpus of real 32- and 64-bit PEs to test against. Watch for PowerShell-vs-C reflexes. `for ($i = 0; $i -lt $n; $i++, $p += 40)` is a parse error: the repeat clause takes one expression, not a comma list. -## Currently left out - -- **lua** — gvsbuild builds LuaJIT by running `.\msvcbuild` through - `CreateProcess`, which cannot launch a `.bat` and finds nothing without the - extension. Taking lua back means building LuaJIT here the way jansson and - libwebsockets are built, and lgi and `--enable-gi` come with it. -- **perl, python3, htm** — need a matching Strawberry Perl / CPython / .NET on - the runner. -- **The installer.** `poxchat.iss.tt` names the lua, perl, python and - thememan payloads unconditionally, across `[Files]`, `[Components]`, - `[Registry]`, `[Run]`, `[Icons]` and the Pascal `[Code]` section. Until - those targets build, `-f installer=true` fails at Inno Setup. The portable - zip uploads before the installer step runs, so this costs nothing else. +## The scripting plugins + +- **lua** builds against a LuaJIT that `build-deps.ps1` compiles itself and + installs into the gvsbuild prefix (gvsbuild's own recipe runs `.\msvcbuild` + through `CreateProcess`, which cannot launch a `.bat`, and fails identically + every time). lgi and `--enable-gi` are deliberately not built — the plugin + needs neither, they only ever provided GObject bindings to Lua scripts. +- **python3** links the import library out of setup-python's install and ships + CPython's *embeddable package*, staged whole into the tree, so users need no + system Python. The plugin is a cffi embedding: `generate_plugin.py` needs + `cffi` in the build interpreter, and at run time `_cffi_backend.pyd` + (staged out of that interpreter's site-packages by `copy.vcxproj`) must sit + on sys.path — the `.` entry in `python313._pth` provides that. + + The Python minor version is pinned in four places that must move together: + `python-version` in the workflow, `Python3Lib` in `win32/poxchat.props`, + `$PythonEmbedVersion`/`$PythonEmbedSha256` in `build-deps.ps1`, and the + `python313.*` names in `poxchat.iss.tt`. + +## Deliberately dropped + +- **perl** — dropped as a supported plugin on Windows; it would need a + matching Strawberry Perl on the runner and on every user's machine. +- **htm / thememan** (the C# theme manager) — needs a .NET toolchain, and the + installer no longer offers it. + +The installer template names only payloads the build produces, so +`-f installer=true` is expected to work; the portable zip still uploads before +the installer step, so an installer failure costs nothing else. ## Traps worth remembering From 234b8cf23dbdf081890ba5485b7a617b08e16320 Mon Sep 17 00:00:00 2001 From: Alex Schumann Date: Sun, 23 Aug 2026 15:21:59 -0700 Subject: [PATCH 5/5] ci: publish the Windows artifacts onto releases A release publish now runs the Windows build with the installer leg forced on, and a separate publish job attaches the portable zip and the installer exe to the release -- same shape as release.yml uses for the AppImage and debs. The build job keeps its read-only token; only the upload job gets contents: write. --- .github/workflows/windows-build.yml | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows-build.yml b/.github/workflows/windows-build.yml index 2aea1fbe..6d04ca21 100644 --- a/.github/workflows/windows-build.yml +++ b/.github/workflows/windows-build.yml @@ -5,6 +5,8 @@ on: branches: [master] pull_request: branches: [master] + release: + types: [published] workflow_dispatch: inputs: installer: @@ -212,12 +214,35 @@ jobs: path: ${{ steps.portable.outputs.path }} - name: Build installer - if: inputs.installer + if: inputs.installer || github.event_name == 'release' run: .\win32\ci\make-installer.ps1 -BuildDir "${{ env.BUILD_DIR }}" -Provision - name: Upload installer - if: inputs.installer + if: inputs.installer || github.event_name == 'release' uses: actions/upload-artifact@v4 with: name: poxchat-x64-installer path: ${{ env.BUILD_DIR }}\x64\*.exe + + # Attach the Windows artifacts to the release that triggered the run, the + # same way release.yml attaches the AppImage and debs. A separate job so + # the build keeps its read-only token; only this upload needs write. A + # release run reads the dependency cache from the default branch, so tagging + # after master's own run has completed keeps this off the cold path. + publish: + if: github.event_name == 'release' + needs: build + runs-on: ubuntu-24.04 + permissions: + contents: write + steps: + - uses: actions/download-artifact@v4 + with: + merge-multiple: true + + - name: Upload to the release + uses: softprops/action-gh-release@v2 + with: + files: | + PoxChat-*-portable.zip + PoxChat-*_x64.exe