mirror of
https://github.com/ZoiteChat/zoitechat.git
synced 2026-10-20 19:57:15 +00:00
track Python 3.14 updates and latest stable Perl
This commit is contained in:
@@ -30,9 +30,13 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-python@v7
|
- uses: actions/setup-python@v7
|
||||||
with:
|
with:
|
||||||
python-version: '3.14.3'
|
python-version: '3.14'
|
||||||
|
check-latest: true
|
||||||
architecture: ${{ matrix.arch }}
|
architecture: ${{ matrix.arch }}
|
||||||
|
|
||||||
|
- name: Test Windows runtime selection
|
||||||
|
run: python -m unittest discover -s tools/tests -p test_windows_runtimes.py
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
New-Item -Name "deps" -ItemType "Directory" -Force | Out-Null
|
New-Item -Name "deps" -ItemType "Directory" -Force | Out-Null
|
||||||
@@ -125,13 +129,13 @@ jobs:
|
|||||||
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/WinSparkle-20260315.7z -OutFile deps\WinSparkle.7z
|
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/WinSparkle-20260315.7z -OutFile deps\WinSparkle.7z
|
||||||
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle
|
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle
|
||||||
|
|
||||||
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/perl-5.42.0.1-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
|
python tools/prepare-windows-runtimes.py
|
||||||
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.42.0.1\${{ matrix.platform }}
|
if ($LASTEXITCODE -ne 0) { throw 'Could not prepare Windows scripting runtimes' }
|
||||||
|
|
||||||
$pyRoot = $env:pythonLocation
|
$pyRoot = $env:pythonLocation
|
||||||
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
|
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
|
||||||
|
|
||||||
foreach ($pyDir in @("C:\gtk-build\python-3.14.3", "C:\gtk-build\python-3.14")) {
|
foreach ($pyDir in @("C:\gtk-build\python-3.14")) {
|
||||||
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
|
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
|
||||||
$target = Join-Path $pyDir "${{ matrix.platform }}"
|
$target = Join-Path $pyDir "${{ matrix.platform }}"
|
||||||
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
|
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
|
||||||
@@ -162,7 +166,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
|
||||||
|
|
||||||
set "PYTHON_DIR=C:\gtk-build\python-3.14.3\${{ matrix.platform }}"
|
set "PYTHON_DIR=C:\gtk-build\python-3.14\${{ matrix.platform }}"
|
||||||
if not exist "%PYTHON_DIR%\libs\python314.lib" (
|
if not exist "%PYTHON_DIR%\libs\python314.lib" (
|
||||||
echo Missing %PYTHON_DIR%\libs\python314.lib
|
echo Missing %PYTHON_DIR%\libs\python314.lib
|
||||||
dir "%PYTHON_DIR%\libs"
|
dir "%PYTHON_DIR%\libs"
|
||||||
@@ -188,6 +192,13 @@ jobs:
|
|||||||
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
|
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
|
||||||
if errorlevel 1 exit /b 1
|
if errorlevel 1 exit /b 1
|
||||||
|
|
||||||
|
for %%P in (hcpython3 hcperl) do (
|
||||||
|
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\plugins\%%P.dll" (
|
||||||
|
echo Required scripting plugin %%P.dll was not built
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\plugins\hcpython38.dll" (
|
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\plugins\hcpython38.dll" (
|
||||||
echo hcpython38.dll was not built
|
echo hcpython38.dll was not built
|
||||||
exit /b 1
|
exit /b 1
|
||||||
|
|||||||
@@ -31,9 +31,13 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-python@v7
|
- uses: actions/setup-python@v7
|
||||||
with:
|
with:
|
||||||
python-version: '3.14.3'
|
python-version: '3.14'
|
||||||
|
check-latest: true
|
||||||
architecture: ${{ matrix.arch }}
|
architecture: ${{ matrix.arch }}
|
||||||
|
|
||||||
|
- name: Test Windows runtime selection
|
||||||
|
run: python -m unittest discover -s tools/tests -p test_windows_runtimes.py
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: pwsh
|
shell: pwsh
|
||||||
run: |
|
run: |
|
||||||
@@ -133,13 +137,13 @@ jobs:
|
|||||||
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/WinSparkle-20260315.7z -OutFile deps\WinSparkle.7z
|
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/WinSparkle-20260315.7z -OutFile deps\WinSparkle.7z
|
||||||
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle
|
& 7z.exe x deps\WinSparkle.7z -oC:\gtk-build\WinSparkle
|
||||||
|
|
||||||
Download-WithRetry -Url https://github.com/ZoiteChat/gvsbuild/releases/download/zoitechat-2.18.0-pre4/perl-5.42.0.1-${{ matrix.arch }}.7z -OutFile deps\perl-${{ matrix.arch }}.7z
|
python tools/prepare-windows-runtimes.py
|
||||||
& 7z.exe x deps\perl-${{ matrix.arch }}.7z -oC:\gtk-build\perl-5.42.0.1\${{ matrix.platform }}
|
if ($LASTEXITCODE -ne 0) { throw 'Could not prepare Windows scripting runtimes' }
|
||||||
|
|
||||||
$pyRoot = $env:pythonLocation
|
$pyRoot = $env:pythonLocation
|
||||||
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
|
if (-not $pyRoot) { $pyRoot = & python -c "import sys; print(sys.prefix)" }
|
||||||
|
|
||||||
foreach ($pyDir in @("C:\gtk-build\python-3.14.3", "C:\gtk-build\python-3.14")) {
|
foreach ($pyDir in @("C:\gtk-build\python-3.14")) {
|
||||||
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
|
New-Item -Path $pyDir -ItemType Directory -Force | Out-Null
|
||||||
$target = Join-Path $pyDir "${{ matrix.platform }}"
|
$target = Join-Path $pyDir "${{ matrix.platform }}"
|
||||||
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
|
if (Test-Path $target) { Remove-Item $target -Recurse -Force }
|
||||||
@@ -172,7 +176,7 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
|
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat"
|
||||||
|
|
||||||
set "PYTHON_DIR=C:\gtk-build\python-3.14.3\${{ matrix.platform }}"
|
set "PYTHON_DIR=C:\gtk-build\python-3.14\${{ matrix.platform }}"
|
||||||
if not exist "%PYTHON_DIR%\libs\python314.lib" (
|
if not exist "%PYTHON_DIR%\libs\python314.lib" (
|
||||||
echo Missing %PYTHON_DIR%\libs\python314.lib
|
echo Missing %PYTHON_DIR%\libs\python314.lib
|
||||||
dir "%PYTHON_DIR%\libs"
|
dir "%PYTHON_DIR%\libs"
|
||||||
@@ -198,6 +202,13 @@ jobs:
|
|||||||
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
|
msbuild win32\zoitechat.sln /m /verbosity:minimal /p:Configuration=Release /p:Platform=${{ matrix.platform }}
|
||||||
if errorlevel 1 exit /b 1
|
if errorlevel 1 exit /b 1
|
||||||
|
|
||||||
|
for %%P in (hcpython3 hcperl) do (
|
||||||
|
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\plugins\%%P.dll" (
|
||||||
|
echo Required scripting plugin %%P.dll was not built
|
||||||
|
exit /b 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\plugins\hcpython38.dll" (
|
if not exist "..\zoitechat-build\${{ matrix.platform }}\rel\plugins\hcpython38.dll" (
|
||||||
echo hcpython38.dll was not built
|
echo hcpython38.dll was not built
|
||||||
exit /b 1
|
exit /b 1
|
||||||
|
|||||||
@@ -5,6 +5,10 @@ html/
|
|||||||
*.patch
|
*.patch
|
||||||
tags
|
tags
|
||||||
# Win32 generated files
|
# Win32 generated files
|
||||||
|
win32/runtimes.generated.props
|
||||||
|
win32/installer/runtimes.generated.iss
|
||||||
|
/tools/__pycache__/
|
||||||
|
/tools/tests/__pycache__/
|
||||||
plugins/wmpa/wmpa_h.h
|
plugins/wmpa/wmpa_h.h
|
||||||
plugins/wmpa/wmpa_i.c
|
plugins/wmpa/wmpa_i.c
|
||||||
src/htm/obj/*
|
src/htm/obj/*
|
||||||
|
|||||||
@@ -53,6 +53,37 @@ Python path if your install is elsewhere::
|
|||||||
cmd /c mklink /J C:\gtk-build\python-3.14\x64 C:\Python314
|
cmd /c mklink /J C:\gtk-build\python-3.14\x64 C:\Python314
|
||||||
C:\gtk-build\python-3.14\x64\python.exe -m pip install --upgrade pip cffi
|
C:\gtk-build\python-3.14\x64\python.exe -m pip install --upgrade pip cffi
|
||||||
|
|
||||||
|
Windows runtime updates
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
|
||||||
|
Both Windows workflows select the latest stable Python **3.14.x** using
|
||||||
|
``actions/setup-python`` with ``check-latest: true``. Python 3.15 and prereleases
|
||||||
|
are excluded. The separate Python 3.8.10 build for older Windows is retained.
|
||||||
|
|
||||||
|
``tools/prepare-windows-runtimes.py`` selects the highest stable x64 Strawberry
|
||||||
|
Perl version from its official release feed, verifies the portable ZIP against
|
||||||
|
the published SHA-256, and checks the extracted interpreter's version and
|
||||||
|
architecture before building. Development versions (odd Perl minor numbers)
|
||||||
|
are excluded. The generated MSBuild properties select its headers and DLL;
|
||||||
|
the generated Inno include selects the matching MSI, Python installer URL and
|
||||||
|
component descriptions. Build logs record the exact selected versions.
|
||||||
|
|
||||||
|
For a manual build, first install the latest Python 3.14 point release (at least
|
||||||
|
3.14.8), prepare its junction and CFFI as above, then run from the checkout::
|
||||||
|
|
||||||
|
C:\gtk-build\python-3.14\x64\python.exe tools\prepare-windows-runtimes.py
|
||||||
|
|
||||||
|
The script downloads the complete Strawberry portable distribution under
|
||||||
|
``C:\gtk-build``. Generated configuration files are ignored by Git. Running
|
||||||
|
it again refreshes that dependency; builds without generated files retain the
|
||||||
|
original manually configured Perl bundle.
|
||||||
|
|
||||||
|
Updates happen when a new ZoiteChat build runs. An existing installation keeps
|
||||||
|
using its installed compatible Python or Perl; installing/updating ZoiteChat
|
||||||
|
does not force an upgrade of an already detected system runtime. MSI/MSIX
|
||||||
|
continue to package the scripting plugins; the Inno installer offers runtime
|
||||||
|
downloads when the corresponding runtime DLL is missing.
|
||||||
|
|
||||||
If you use different locations, set these environment variables before building
|
If you use different locations, set these environment variables before building
|
||||||
instead of editing ``win32\zoitechat.props``:
|
instead of editing ``win32\zoitechat.props``:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,149 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Prepare current Windows scripting dependencies and matching installer metadata."""
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import hashlib
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
import time
|
||||||
|
import urllib.request
|
||||||
|
from urllib.parse import urlsplit
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
RELEASES_URL = "https://strawberryperl.com/releases.json"
|
||||||
|
RELEASE_ROOT = "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/"
|
||||||
|
MSBUILD_NS = "http://schemas.microsoft.com/developer/msbuild/2003"
|
||||||
|
|
||||||
|
|
||||||
|
def select_perl(releases):
|
||||||
|
"""Compare versions, not publication dates: older branches get newer releases too."""
|
||||||
|
candidates = []
|
||||||
|
for release in releases:
|
||||||
|
version = release.get("version", "")
|
||||||
|
if not re.fullmatch(r"5\.\d+\.\d+\.\d+", version):
|
||||||
|
continue
|
||||||
|
parts = tuple(map(int, version.split(".")))
|
||||||
|
if parts[1] % 2 or release.get("archname") != "MSWin32-x64-multi-thread":
|
||||||
|
continue
|
||||||
|
if not all(kind in release.get("edition", {}) for kind in ("portable", "msi")):
|
||||||
|
continue
|
||||||
|
candidates.append((parts, release))
|
||||||
|
if not candidates:
|
||||||
|
raise ValueError("No stable x64 Strawberry Perl release with portable ZIP and MSI")
|
||||||
|
release = max(candidates, key=lambda item: item[0])[1]
|
||||||
|
for kind, suffix in (("portable", "portable.zip"), ("msi", "msi")):
|
||||||
|
package = release["edition"][kind]
|
||||||
|
expected_name = f"strawberry-perl-{release['version']}-64bit-{suffix}"
|
||||||
|
if kind == "msi":
|
||||||
|
expected_name = f"strawberry-perl-{release['version']}-64bit.msi"
|
||||||
|
url = package.get("url", "")
|
||||||
|
if not url.startswith(RELEASE_ROOT) or urlsplit(url).path.rsplit("/", 1)[-1] != expected_name:
|
||||||
|
raise ValueError(f"Unexpected Strawberry Perl {kind} URL: {url}")
|
||||||
|
if not re.fullmatch(r"[0-9a-fA-F]{64}", package.get("sha256", "")):
|
||||||
|
raise ValueError(f"Missing SHA-256 for Strawberry Perl {kind}")
|
||||||
|
return release
|
||||||
|
|
||||||
|
|
||||||
|
def download(url, destination):
|
||||||
|
for attempt in range(4):
|
||||||
|
try:
|
||||||
|
request = urllib.request.Request(url, headers={"User-Agent": "ZoiteChat-build"})
|
||||||
|
with urllib.request.urlopen(request, timeout=120) as response, destination.open("wb") as output:
|
||||||
|
while chunk := response.read(1024 * 1024):
|
||||||
|
output.write(chunk)
|
||||||
|
return
|
||||||
|
except (OSError, TimeoutError):
|
||||||
|
if attempt == 3:
|
||||||
|
raise
|
||||||
|
time.sleep(2 ** attempt)
|
||||||
|
|
||||||
|
|
||||||
|
def write_metadata(repo, perl_root, release, python_version):
|
||||||
|
parts = release["version"].split(".")
|
||||||
|
perl_lib = f"perl{parts[0]}{parts[1]}"
|
||||||
|
ET.register_namespace("", MSBUILD_NS)
|
||||||
|
project = ET.Element(f"{{{MSBUILD_NS}}}Project")
|
||||||
|
group = ET.SubElement(project, "PropertyGroup")
|
||||||
|
ET.SubElement(group, "PerlPath").text = str(perl_root)
|
||||||
|
ET.SubElement(group, "PerlLib").text = perl_lib
|
||||||
|
ET.ElementTree(project).write(repo / "win32/runtimes.generated.props", encoding="utf-8", xml_declaration=True)
|
||||||
|
# Only numeric versions and validated upstream URLs enter the Inno source.
|
||||||
|
definitions = {
|
||||||
|
"PythonRuntimeVersion": python_version,
|
||||||
|
"PythonRuntimeURL": f"https://www.python.org/ftp/python/{python_version}/python-{python_version}-amd64.exe",
|
||||||
|
"PerlRuntimeVersion": release["version"],
|
||||||
|
"PerlRuntimeURL": release["edition"]["msi"]["url"],
|
||||||
|
"PerlRuntimeDLL": f"{perl_lib}.dll",
|
||||||
|
}
|
||||||
|
(repo / "win32/installer/runtimes.generated.iss").write_text(
|
||||||
|
"// Generated by tools/prepare-windows-runtimes.py\n"
|
||||||
|
+ "".join(f'#define {key} "{value}"\n' for key, value in definitions.items()),
|
||||||
|
encoding="utf-8",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description=__doc__)
|
||||||
|
parser.add_argument("--deps-root", type=Path, default=Path("C:/gtk-build"))
|
||||||
|
args = parser.parse_args()
|
||||||
|
if sys.platform != "win32" or sys.version_info[:2] != (3, 14) or sys.version_info[:3] < (3, 14, 8) or sys.version_info.releaselevel != "final":
|
||||||
|
parser.error("Run with stable x64 Python 3.14.8 or newer within the 3.14 branch")
|
||||||
|
if sys.maxsize <= 2 ** 32:
|
||||||
|
parser.error("The Windows build requires x64 Python")
|
||||||
|
repo = Path(__file__).resolve().parents[1]
|
||||||
|
# Do not let an interrupted refresh leave metadata from an older dependency.
|
||||||
|
for relative in ("win32/runtimes.generated.props", "win32/installer/runtimes.generated.iss"):
|
||||||
|
(repo / relative).unlink(missing_ok=True)
|
||||||
|
args.deps_root.mkdir(parents=True, exist_ok=True)
|
||||||
|
with tempfile.TemporaryDirectory(dir=args.deps_root) as temporary:
|
||||||
|
temporary = Path(temporary)
|
||||||
|
metadata = temporary / "releases.json"
|
||||||
|
download(RELEASES_URL, metadata)
|
||||||
|
release = select_perl(json.loads(metadata.read_text(encoding="utf-8")))
|
||||||
|
archive = temporary / "perl.zip"
|
||||||
|
download(release["edition"]["portable"]["url"], archive)
|
||||||
|
with archive.open("rb") as source:
|
||||||
|
digest = hashlib.file_digest(source, "sha256").hexdigest()
|
||||||
|
if digest.lower() != release["edition"]["portable"]["sha256"].lower():
|
||||||
|
raise ValueError("Strawberry Perl portable ZIP SHA-256 mismatch")
|
||||||
|
# Keep the portable distribution intact, including its companion C DLLs.
|
||||||
|
target = args.deps_root / f"strawberry-perl-{release['version']}" / "x64"
|
||||||
|
extracted = temporary / "extracted"
|
||||||
|
with zipfile.ZipFile(archive) as package:
|
||||||
|
package.extractall(extracted)
|
||||||
|
target.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
if target.exists():
|
||||||
|
shutil.rmtree(target)
|
||||||
|
shutil.move(extracted, target)
|
||||||
|
perl_root = target / "perl"
|
||||||
|
environment = os.environ.copy()
|
||||||
|
paths = [str(perl_root / "bin"), str(target / "c/bin")]
|
||||||
|
environment["PATH"] = os.pathsep.join(paths + [environment.get("PATH", "")])
|
||||||
|
perl_lib = "perl" + "".join(release["version"].split(".")[:2])
|
||||||
|
for required in (perl_root / "bin/perl.exe", perl_root / f"bin/{perl_lib}.dll", perl_root / "lib/CORE/perl.h"):
|
||||||
|
if not required.is_file():
|
||||||
|
raise FileNotFoundError(f"Strawberry Perl is missing {required}")
|
||||||
|
actual = subprocess.check_output(
|
||||||
|
[str(perl_root / "bin/perl.exe"), "-MConfig", "-e", 'print "$Config{version}|$Config{archname}"'],
|
||||||
|
env=environment, text=True,
|
||||||
|
).strip()
|
||||||
|
expected = ".".join(release["version"].split(".")[:3]) + "|MSWin32-x64-multi-thread"
|
||||||
|
if actual != expected:
|
||||||
|
raise ValueError(f"Unexpected Perl runtime: {actual}; expected {expected}")
|
||||||
|
python_version = ".".join(map(str, sys.version_info[:3]))
|
||||||
|
write_metadata(repo, perl_root, release, python_version)
|
||||||
|
if os.environ.get("GITHUB_PATH"):
|
||||||
|
with open(os.environ["GITHUB_PATH"], "a", encoding="utf-8") as output:
|
||||||
|
output.write("\n".join(paths) + "\n")
|
||||||
|
print(f"Windows runtimes: Python {python_version}; Strawberry Perl {release['version']}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
"""Regression coverage for stable release selection and build/installer alignment."""
|
||||||
|
|
||||||
|
import copy
|
||||||
|
import importlib.util
|
||||||
|
from pathlib import Path
|
||||||
|
import tempfile
|
||||||
|
import unittest
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
spec = importlib.util.spec_from_file_location(
|
||||||
|
"windows_runtimes", Path(__file__).resolve().parents[1] / "prepare-windows-runtimes.py"
|
||||||
|
)
|
||||||
|
runtimes = importlib.util.module_from_spec(spec)
|
||||||
|
spec.loader.exec_module(runtimes)
|
||||||
|
|
||||||
|
|
||||||
|
def release(version, arch="MSWin32-x64-multi-thread"):
|
||||||
|
prefix = runtimes.RELEASE_ROOT + "SP_" + version.replace(".", "") + "_64bit/"
|
||||||
|
stem = f"strawberry-perl-{version}-64bit"
|
||||||
|
return {
|
||||||
|
"version": version,
|
||||||
|
"archname": arch,
|
||||||
|
"edition": {
|
||||||
|
"portable": {"url": prefix + stem + "-portable.zip", "sha256": "a" * 64},
|
||||||
|
"msi": {"url": prefix + stem + ".msi", "sha256": "b" * 64},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class RuntimeTests(unittest.TestCase):
|
||||||
|
def test_compares_versions_instead_of_feed_order_or_dates(self):
|
||||||
|
releases = [release("5.40.5.1"), release("5.42.3.1"), release("5.42.0.1")]
|
||||||
|
self.assertEqual(runtimes.select_perl(releases)["version"], "5.42.3.1")
|
||||||
|
|
||||||
|
def test_numeric_patch_order(self):
|
||||||
|
self.assertEqual(runtimes.select_perl([release("5.42.9.1"), release("5.42.10.1")])["version"], "5.42.10.1")
|
||||||
|
|
||||||
|
def test_filters_development_prerelease_and_wrong_architecture(self):
|
||||||
|
releases = [release("5.43.1.1"), release("5.44.0.1-RC1"), release("5.44.0.1", "MSWin32-x86-multi-thread"), release("5.42.3.1")]
|
||||||
|
self.assertEqual(runtimes.select_perl(releases)["version"], "5.42.3.1")
|
||||||
|
|
||||||
|
def test_accepts_next_stable_branch(self):
|
||||||
|
self.assertEqual(runtimes.select_perl([release("5.42.3.1"), release("5.44.0.1")])["version"], "5.44.0.1")
|
||||||
|
|
||||||
|
def test_requires_both_build_and_installer_packages(self):
|
||||||
|
newer = release("5.44.0.1")
|
||||||
|
del newer["edition"]["msi"]
|
||||||
|
self.assertEqual(runtimes.select_perl([newer, release("5.42.3.1")])["version"], "5.42.3.1")
|
||||||
|
|
||||||
|
def test_fails_if_no_suitable_release_exists(self):
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
runtimes.select_perl([release("5.43.1.1")])
|
||||||
|
|
||||||
|
def test_rejects_bad_hashes_untrusted_hosts_and_wrong_packages(self):
|
||||||
|
for kind in ("portable", "msi"):
|
||||||
|
for field, value in (("sha256", ""), ("url", "https://example.org/perl.zip"), ("url", runtimes.RELEASE_ROOT + "SP/strawberry-perl-5.40.5.1-64bit.msi")):
|
||||||
|
with self.subTest(kind=kind, field=field, value=value):
|
||||||
|
invalid = copy.deepcopy(release("5.42.3.1"))
|
||||||
|
invalid["edition"][kind][field] = value
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
runtimes.select_perl([invalid])
|
||||||
|
|
||||||
|
def test_generated_dll_and_installer_follow_selected_release(self):
|
||||||
|
with tempfile.TemporaryDirectory() as directory:
|
||||||
|
repo = Path(directory)
|
||||||
|
(repo / "win32/installer").mkdir(parents=True)
|
||||||
|
selected = release("5.44.1.1")
|
||||||
|
perl_root = Path("C:/gtk-build/Perl & tools/perl")
|
||||||
|
runtimes.write_metadata(repo, perl_root, selected, "3.14.8")
|
||||||
|
props = ET.parse(repo / "win32/runtimes.generated.props")
|
||||||
|
ns = {"m": runtimes.MSBUILD_NS}
|
||||||
|
self.assertEqual(props.find("m:PropertyGroup/m:PerlLib", ns).text, "perl544")
|
||||||
|
self.assertEqual(props.find("m:PropertyGroup/m:PerlPath", ns).text, str(perl_root))
|
||||||
|
installer = (repo / "win32/installer/runtimes.generated.iss").read_text()
|
||||||
|
self.assertIn('#define PerlRuntimeDLL "perl544.dll"', installer)
|
||||||
|
self.assertIn(selected["edition"]["msi"]["url"], installer)
|
||||||
|
self.assertIn("/3.14.8/python-3.14.8-amd64.exe", installer)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -4,6 +4,18 @@
|
|||||||
;#define APPARCH "x64"
|
;#define APPARCH "x64"
|
||||||
;#define PROJECTDIR "C:\...\zoitechat\win32\installer\"
|
;#define PROJECTDIR "C:\...\zoitechat\win32\installer\"
|
||||||
|
|
||||||
|
; CI generates these from the runtimes actually used to build the plugins.
|
||||||
|
#if FileExists(PROJECTDIR + "runtimes.generated.iss")
|
||||||
|
#include PROJECTDIR + "runtimes.generated.iss"
|
||||||
|
#else
|
||||||
|
; Manual builds without the preparation script retain the original Perl bundle.
|
||||||
|
#define PythonRuntimeVersion "3.14.8"
|
||||||
|
#define PythonRuntimeURL "https://www.python.org/ftp/python/3.14.8/python-3.14.8-amd64.exe"
|
||||||
|
#define PerlRuntimeVersion "5.42.0.1"
|
||||||
|
#define PerlRuntimeURL "https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_54201_64bit/strawberry-perl-5.42.0.1-64bit.msi"
|
||||||
|
#define PerlRuntimeDLL "perl542.dll"
|
||||||
|
#endif
|
||||||
|
|
||||||
[Setup]
|
[Setup]
|
||||||
AppName=ZoiteChat
|
AppName=ZoiteChat
|
||||||
AppVersion={#APPVER}
|
AppVersion={#APPVER}
|
||||||
@@ -58,8 +70,8 @@ Name: "plugins\sysinfo"; Description: "SysInfo"; Types: custom; Flags: disableno
|
|||||||
Name: "plugins\upd"; Description: "Update Checker"; Types: normal custom; Flags: disablenouninstallwarning
|
Name: "plugins\upd"; Description: "Update Checker"; Types: normal custom; Flags: disablenouninstallwarning
|
||||||
Name: "langs"; Description: "Language Interfaces"; Types: custom; Flags: disablenouninstallwarning
|
Name: "langs"; Description: "Language Interfaces"; Types: custom; Flags: disablenouninstallwarning
|
||||||
Name: "langs\lua"; Description: "Lua (LuaJIT 2.1)"; Types: normal custom; Flags: disablenouninstallwarning
|
Name: "langs\lua"; Description: "Lua (LuaJIT 2.1)"; Types: normal custom; Flags: disablenouninstallwarning
|
||||||
Name: "langs\perl"; Description: "Perl (Strawberry Perl 5.42.0.1)"; Types: custom; Flags: disablenouninstallwarning
|
Name: "langs\perl"; Description: "Perl (Strawberry Perl {#PerlRuntimeVersion})"; Types: custom; Flags: disablenouninstallwarning
|
||||||
Name: "langs\python"; Description: "Python (Python 3.14.3, or 3.8.10 before Windows 10)"; Types: custom; Flags: disablenouninstallwarning
|
Name: "langs\python"; Description: "Python (Python {#PythonRuntimeVersion}, or 3.8.10 before Windows 10)"; Types: custom; Flags: disablenouninstallwarning
|
||||||
Name: "themes"; Description: "GTK3 Themes"; Types: normal minimal custom; Flags: disablenouninstallwarning
|
Name: "themes"; Description: "GTK3 Themes"; Types: normal minimal custom; Flags: disablenouninstallwarning
|
||||||
Name: "themes\windows10"; Description: "Windows 10"; Types: normal minimal custom; Flags: disablenouninstallwarning
|
Name: "themes\windows10"; Description: "Windows 10"; Types: normal minimal custom; Flags: disablenouninstallwarning
|
||||||
Name: "themes\windows10dark"; Description: "Windows 10 Dark"; Types: normal minimal custom; Flags: disablenouninstallwarning
|
Name: "themes\windows10dark"; Description: "Windows 10 Dark"; Types: normal minimal custom; Flags: disablenouninstallwarning
|
||||||
@@ -349,7 +361,7 @@ begin
|
|||||||
if NeedsLegacyPython() then
|
if NeedsLegacyPython() then
|
||||||
Result := 'https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe'
|
Result := 'https://www.python.org/ftp/python/3.8.10/python-3.8.10-amd64.exe'
|
||||||
else
|
else
|
||||||
Result := 'https://www.python.org/ftp/python/3.14.3/python-3.14.3-amd64.exe';
|
Result := '{#PythonRuntimeURL}';
|
||||||
end;
|
end;
|
||||||
|
|
||||||
function PythonDLL(): String;
|
function PythonDLL(): String;
|
||||||
@@ -387,7 +399,7 @@ begin
|
|||||||
begin
|
begin
|
||||||
|
|
||||||
REDIST := 'https://aka.ms/vs/17/release/vc_redist.x64.exe';
|
REDIST := 'https://aka.ms/vs/17/release/vc_redist.x64.exe';
|
||||||
PERL := 'https://github.com/StrawberryPerl/Perl-Dist-Strawberry/releases/download/SP_54201_64bit/strawberry-perl-5.42.0.1-64bit.msi';
|
PERL := '{#PerlRuntimeURL}';
|
||||||
PY3 := PythonURL();
|
PY3 := PythonURL();
|
||||||
SPELL := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/HexChat.Spelling.Dictionaries.r2.exe';
|
SPELL := 'https://github.com/hexchat/gvsbuild/releases/download/hexchat-2.16.2/HexChat.Spelling.Dictionaries.r2.exe';
|
||||||
|
|
||||||
@@ -399,7 +411,7 @@ begin
|
|||||||
|
|
||||||
if not WizardSilent() then
|
if not WizardSilent() then
|
||||||
begin
|
begin
|
||||||
if WizardIsComponentSelected('langs\perl') and not CheckDLL('perl542.dll') then
|
if WizardIsComponentSelected('langs\perl') and not CheckDLL('{#PerlRuntimeDLL}') then
|
||||||
begin
|
begin
|
||||||
idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi'))
|
idpAddFile(PERL, ExpandConstant('{tmp}\perl.msi'))
|
||||||
end;
|
end;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
|
<Import Project="$(MSBuildThisFileDirectory)runtimes.generated.props" Condition="Exists('$(MSBuildThisFileDirectory)runtimes.generated.props')" />
|
||||||
<PropertyGroup Label="UserMacros">
|
<PropertyGroup Label="UserMacros">
|
||||||
|
|
||||||
<!-- SPECIFY YOUR DEPENDENCY DIRECTORIES HERE -->
|
<!-- SPECIFY YOUR DEPENDENCY DIRECTORIES HERE -->
|
||||||
@@ -24,8 +25,8 @@
|
|||||||
<DepsRoot>$(YourDepsPath)\$(ZoiteChatPlatform)\release</DepsRoot>
|
<DepsRoot>$(YourDepsPath)\$(ZoiteChatPlatform)\release</DepsRoot>
|
||||||
<GendefPath>$(YourGendefPath)</GendefPath>
|
<GendefPath>$(YourGendefPath)</GendefPath>
|
||||||
<WinSparklePath>$(YourWinSparklePath)\$(ZoiteChatPlatform)</WinSparklePath>
|
<WinSparklePath>$(YourWinSparklePath)\$(ZoiteChatPlatform)</WinSparklePath>
|
||||||
<PerlPath>$(YourPerlPath)\$(ZoiteChatPlatform)</PerlPath>
|
<PerlPath Condition="'$(PerlPath)'==''">$(YourPerlPath)\$(ZoiteChatPlatform)</PerlPath>
|
||||||
<PerlLib>perl542</PerlLib>
|
<PerlLib Condition="'$(PerlLib)'==''">perl542</PerlLib>
|
||||||
|
|
||||||
<Python3Path>$(YourPython3Path)\$(ZoiteChatPlatform)</Python3Path>
|
<Python3Path>$(YourPython3Path)\$(ZoiteChatPlatform)</Python3Path>
|
||||||
<Python3Lib>python314</Python3Lib>
|
<Python3Lib>python314</Python3Lib>
|
||||||
|
|||||||
Reference in New Issue
Block a user