From 5559d6430deac9335ca366691bf4af56936c151a Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Sat, 4 Jan 2025 14:11:14 +0100 Subject: [PATCH] ci(windows): build binary packages using the vcpkg package --- .github/workflows/packages.yml | 68 ++++++++++++++++ .../lib/pg_config_vcpkg_stub/__init__.py | 77 ++++++++++++++++++ .../pg_config_vcpkg_stub/__init__.py | 79 +++++++++++++++++++ .../build/pg_config_vcpkg_stub/pyproject.toml | 11 +++ scripts/build/wheel_win32_before_build.bat | 7 ++ 5 files changed, 242 insertions(+) create mode 100644 scripts/build/pg_config_vcpkg_stub/build/lib/pg_config_vcpkg_stub/__init__.py create mode 100644 scripts/build/pg_config_vcpkg_stub/pg_config_vcpkg_stub/__init__.py create mode 100644 scripts/build/pg_config_vcpkg_stub/pyproject.toml create mode 100644 scripts/build/wheel_win32_before_build.bat diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 04ca89968..8c019711c 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -182,3 +182,71 @@ jobs: with: name: macos-${{matrix.pyver}}-macos-${{matrix.macver}}_${{matrix.arch}} path: ./wheelhouse/*.whl + + + build-windows: + runs-on: windows-latest + if: true + + strategy: + fail-fast: false + matrix: + arch: [win_amd64] + pyver: [cp38, cp39, cp310, cp311, cp312, cp313] + + defaults: + run: + shell: bash + + steps: + # there are some other libpq in PATH + - name: Drop spurious libpq in the path + run: rm -rf c:/tools/php C:/Strawberry/c/bin + + - name: Checkout repo + uses: actions/checkout@v4 + + - name: Start PostgreSQL service for test + run: | + $PgSvc = Get-Service "postgresql*" + Set-Service $PgSvc.Name -StartupType manual + $PgSvc.Start() + shell: powershell + + - name: Export GitHub Actions cache environment variables + uses: actions/github-script@v7 + with: + script: | + const path = require('path') + core.exportVariable('ACTIONS_CACHE_URL', process.env.ACTIONS_CACHE_URL || ''); + core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || ''); + core.addPath(path.join(process.env.VCPKG_INSTALLATION_ROOT, 'installed/x64-windows-release/lib')); + core.addPath(path.join(process.env.VCPKG_INSTALLATION_ROOT, 'installed/x64-windows-release/bin')); + + # TODO: patch to psycopg2-binary + # - name: Create the binary package source tree + # run: python3 ./tools/build/copy_to_binary.py + + - name: Build wheels + uses: pypa/cibuildwheel@v2.22.0 + env: + VCPKG_BINARY_SOURCES: "clear;x-gha,readwrite" # cache vcpkg + CIBW_BUILD: ${{matrix.pyver}}-${{matrix.arch}} + CIBW_ARCHS_WINDOWS: AMD64 x86 + CIBW_BEFORE_BUILD_WINDOWS: '.\scripts\build\wheel_win32_before_build.bat' + CIBW_REPAIR_WHEEL_COMMAND_WINDOWS: >- + delvewheel repair -w {dest_dir} + --no-mangle "libiconv-2.dll;libwinpthread-1.dll" {wheel} + CIBW_TEST_REQUIRES: ./psycopg[test] ./psycopg_pool + CIBW_TEST_COMMAND: >- + export PYTHONPATH={project} && + python -c "import tests; tests.unittest.main(defaultTest='tests.test_suite')" + # Note: no fast test because we don't run Windows tests + CIBW_ENVIRONMENT_WINDOWS: >- + PSYCOPG2_TESTDB=postgres + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: windows-${{matrix.pyver}}-${{matrix.arch}} + path: ./wheelhouse/*.whl diff --git a/scripts/build/pg_config_vcpkg_stub/build/lib/pg_config_vcpkg_stub/__init__.py b/scripts/build/pg_config_vcpkg_stub/build/lib/pg_config_vcpkg_stub/__init__.py new file mode 100644 index 000000000..0d2aba295 --- /dev/null +++ b/scripts/build/pg_config_vcpkg_stub/build/lib/pg_config_vcpkg_stub/__init__.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +""" +We use vcpkg in github actions to build psycopg-binary. + +This is a stub to work as `pg_config --libdir` or `pg_config --includedir` to +make it work with vcpkg. + +You will need install vcpkg and set `VCPKG_ROOT `env +and run `vcpkg install libpq:x64-windows-release` before you use this script +""" + +import os +import sys +import logging +import platform +from pathlib import Path +from argparse import ArgumentParser, Namespace, RawDescriptionHelpFormatter + +logger = logging.getLogger() +logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s %(message)s") + + +class ScriptError(Exception): + """Controlled exception raised by the script.""" + + +def _main() -> None: + # only x64-windows + if not (sys.platform == "win32" and platform.machine() == "AMD64"): + raise ScriptError("this script should only be used in x64-windows") + + args = parse_cmdline() + + # on github actions it's `VCPKG_INSTALLATION_ROOT` + if "VCPKG_ROOT" not in os.environ: + raise ScriptError("failed to find VCPKG ROOT path") + + vcpkg_root = Path(os.environ["VCPKG_ROOT"]) + vcpkg_platform_root = (vcpkg_root / "installed/x64-windows-release").resolve() + + if args.libdir: + f = vcpkg_platform_root / "lib/libpq.lib" + if not f.exists(): + raise ScriptError(f"libpq library not found: {f}") + print(vcpkg_platform_root.joinpath("lib")) + + elif args.includedir: + d = vcpkg_platform_root / "include/libpq" + if not d.is_dir(): + raise ScriptError(f"libpq include directory not found: {d}") + print(vcpkg_platform_root.joinpath("include")) + + else: + raise ScriptError("command not handled") + + +def parse_cmdline() -> Namespace: + parser = ArgumentParser( + description=__doc__, formatter_class=RawDescriptionHelpFormatter + ) + g = parser.add_mutually_exclusive_group(required=True) + g.add_argument("--libdir", action="store_true") + g.add_argument("--includedir", action="store_true") + opt = parser.parse_args() + return opt + + +def main() -> None: + try: + _main() + except ScriptError as e: + logger.error("%s", e) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/build/pg_config_vcpkg_stub/pg_config_vcpkg_stub/__init__.py b/scripts/build/pg_config_vcpkg_stub/pg_config_vcpkg_stub/__init__.py new file mode 100644 index 000000000..89935662b --- /dev/null +++ b/scripts/build/pg_config_vcpkg_stub/pg_config_vcpkg_stub/__init__.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +""" +We use vcpkg in github actions to build psycopg-binary. + +This is a stub to work as `pg_config --libdir` or `pg_config --includedir` to +make it work with vcpkg. + +You will need install `vcpkg`, set `VCPKG_ROOT` env, and run `vcpkg install +libpq:x64-windows-release` before using this script. +""" + +import os +import sys +import platform +from pathlib import Path +from argparse import ArgumentParser, Namespace, RawDescriptionHelpFormatter + + +class ScriptError(Exception): + """Controlled exception raised by the script.""" + + +def _main() -> None: + # only x64-windows + if not (sys.platform == "win32" and platform.machine() == "AMD64"): + raise ScriptError("this script should only be used in x64-windows") + + vcpkg_root = os.environ.get( + "VCPKG_ROOT", os.environ.get("VCPKG_INSTALLATION_ROOT", "") + ) + if not vcpkg_root: + raise ScriptError("VCPKG_ROOT/VCPKG_INSTALLATION_ROOT env var not specified") + vcpkg_platform_root = (Path(vcpkg_root) / "installed/x64-windows-release").resolve() + + args = parse_cmdline() + + if args.libdir: + if not (f := vcpkg_platform_root / "lib/libpq.lib").exists(): + raise ScriptError(f"libpq library not found: {f}") + print(vcpkg_platform_root.joinpath("lib")) + + elif args.includedir: + if not (d := vcpkg_platform_root / "include/libpq").is_dir(): + raise ScriptError(f"libpq include directory not found: {d}") + print(vcpkg_platform_root.joinpath("include")) + + else: + raise ScriptError("command not handled") + + +def parse_cmdline() -> Namespace: + parser = ArgumentParser( + description=__doc__, formatter_class=RawDescriptionHelpFormatter + ) + g = parser.add_mutually_exclusive_group(required=True) + g.add_argument( + "--libdir", + action="store_true", + help="show location of object code libraries", + ) + g.add_argument( + "--includedir", + action="store_true", + help="show location of C header files of the client interfaces", + ) + opt = parser.parse_args() + return opt + + +def main() -> None: + try: + _main() + except ScriptError as e: + print(f"ERROR: {e}.", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main() diff --git a/scripts/build/pg_config_vcpkg_stub/pyproject.toml b/scripts/build/pg_config_vcpkg_stub/pyproject.toml new file mode 100644 index 000000000..f776905f2 --- /dev/null +++ b/scripts/build/pg_config_vcpkg_stub/pyproject.toml @@ -0,0 +1,11 @@ +[build-system] +requires = ["setuptools"] +build-backend = "setuptools.build_meta" + +[project] +name = 'pg_config_vcpkg_stub' +version = "0" +description = "see docs string in pg_config_vcpkg_stub for more details" + +[project.scripts] +pg_config = 'pg_config_vcpkg_stub:main' diff --git a/scripts/build/wheel_win32_before_build.bat b/scripts/build/wheel_win32_before_build.bat new file mode 100644 index 000000000..2f80cb731 --- /dev/null +++ b/scripts/build/wheel_win32_before_build.bat @@ -0,0 +1,7 @@ +@echo on + +pip install delvewheel wheel + +vcpkg install libpq:x64-windows-release + +pipx install .\scripts\build\pg_config_vcpkg_stub\