Skip to content

Commit 3cf90e5

Browse files
authored
ci: build and ship cp310/cp311 wheels (linux/windows/macos) to PyPI + Release (#160)
1 parent 1020262 commit 3cf90e5

5 files changed

Lines changed: 186 additions & 207 deletions

File tree

.github/workflows/ci.yml

Lines changed: 178 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,20 @@
4747
# bit-exact-validated on every merge — without paying LTO's ~2-3x
4848
# build cost on PRs. Separate ccache namespace + artifact prefix
4949
# (…-lto-…) so they never perturb the fast PR build or its seed.
50+
# build-legacy-{linux,windows,macos} : the cp310 + cp311 version-specific
51+
# (non-abi3) wheels. 3.10/3.11 cannot be abi3 (stable-ABI member
52+
# constants are 3.12+), so they ship as six per-version wheels across
53+
# the three desktop OSes. RELEASE-ONLY: built only when a Release is
54+
# published (where `publish` ships them), so they add nothing to
55+
# per-merge CI cost. FVTK_ABI3=0 selects the legacy path.
5056
# publish : RELEASE-ONLY (if: github.event_name == 'release'). Uploads the
5157
# SHIPPED wheels to PyPI via trusted publishing (OIDC, no stored
52-
# token) — the LTO Linux wheel (build-lto) plus the optimized macOS +
53-
# Windows wheels (build-other-os) — gated on the LTO bit-exact +
54-
# pixel-exact legs being green. A final step then attaches those same
55-
# wheels to the GitHub Release (gh release upload) so every wheel is
56-
# downloadable from the Release page too, not only PyPI. Wheels-only
57-
# (fvtk_backend has no sdist).
58+
# token) — the four abi3 wheels (LTO Linux x86_64 + aarch64 + macOS +
59+
# Windows) PLUS the six legacy cp310/cp311 wheels — gated on the LTO
60+
# bit-exact + pixel-exact legs and the legacy legs being green. A final
61+
# step then attaches those same ten wheels to the GitHub Release (gh
62+
# release upload) so every wheel is downloadable from the Release page
63+
# too, not only PyPI. Wheels-only (fvtk_backend has no sdist).
5864
name: CI
5965

6066
on:
@@ -616,16 +622,157 @@ jobs:
616622
path: wheelhouse/*.whl
617623
if-no-files-found: error
618624

625+
# === Legacy version-specific wheels (cp310 + cp311) ========================
626+
# 3.10/3.11 CANNOT be abi3: PyMemberDef + the Py_T_*/Py_READONLY member
627+
# constants the heap-type wrappers emit only entered the stable ABI in 3.12
628+
# (gh-93274). So they ship as version-specific (non-abi3) wheels — six in all:
629+
# cp310 + cp311 x {Linux, Windows, macOS}. The backend emits the legacy static
630+
# per-version wheel automatically when the build interpreter is < 3.12 or
631+
# FVTK_ABI3=0 (set belt-and-suspenders below), so there is NO new build logic.
632+
#
633+
# RELEASE-ONLY: these build only when a Release is published — where `publish`
634+
# collects their artifacts and ships them to PyPI + the Release alongside the four
635+
# abi3 wheels. They are deliberately kept off push-to-main / PRs / workflow_dispatch
636+
# / merge_group so they add nothing to per-merge CI cost (validated at release
637+
# time; the cibuildwheel smoke import per leg is the validation, not a numeric gate).
638+
build-legacy-linux:
639+
name: build legacy linux (manylinux2014, cp310+cp311)
640+
# RELEASE-ONLY: the legacy cp310/cp311 wheels build only when a Release is
641+
# published (where `publish` ships them). Kept off push-to-main / PRs / dispatch
642+
# so they never add to per-merge CI cost; they are validated at release time.
643+
if: github.event_name == 'release'
644+
# Two non-abi3 cp legs + LTO link is the heaviest build; generous cap, still
645+
# well below the 360-min default so a hung leg can't drain paid runners.
646+
runs-on: ${{ vars.FVTK_RUNNER_LINUX || 'ubuntu-latest' }}
647+
timeout-minutes: 120
648+
steps:
649+
- uses: actions/checkout@v5
650+
with:
651+
fetch-depth: 0 # VTK derives its version from full history
652+
- name: Build wheels + offscreen smoke (cibuildwheel)
653+
uses: pypa/cibuildwheel@v3.1.4
654+
env:
655+
# Version-specific wheels for 3.10 + 3.11 (each builds + tests on its own
656+
# interpreter; no abi3 dedup). musllinux skipped (Alpine not a target).
657+
CIBW_BUILD: "cp310-* cp311-*"
658+
CIBW_SKIP: "*-musllinux_*"
659+
# Broader baseline than the shipped abi3 wheel (manylinux_2_28):
660+
# manylinux2014 == CentOS 7 == glibc 2.17, matching stock vtk's wheels.
661+
CIBW_MANYLINUX_X86_64_IMAGE: "manylinux2014"
662+
CIBW_ARCHS_LINUX: "x86_64"
663+
# OVERRIDE the pyproject before-all (AlmaLinux-8/dnf specific, installs
664+
# mold+lld that do NOT exist on CentOS 7). On el7 we use yum, drop
665+
# mold/lld, and rely on the gold linker (FVTK_FAST_LINKER below). EPEL is
666+
# enabled first for xvfb + a couple of mesa devel packages.
667+
CIBW_BEFORE_ALL_LINUX: >-
668+
yum install -y epel-release &&
669+
yum install -y
670+
mesa-libGL-devel mesa-libEGL-devel mesa-libOSMesa-devel
671+
mesa-libGLU-devel libglvnd-devel
672+
libX11-devel libXcursor-devel libXt-devel libXext-devel
673+
mesa-dri-drivers xorg-x11-server-Xvfb ccache git &&
674+
mkdir -p /ccache && chmod 0777 /ccache
675+
CIBW_BEFORE_BUILD_LINUX: "{project}/ci/cibw/before-build.sh"
676+
# Toolchain accommodations for CentOS 7 / GCC 10.2.1: FVTK_ABI3=0 forces
677+
# the legacy per-version path; gold linker (mold absent on el7); LTO + -O3
678+
# kept (GCC 10 OK); the GCC>=12 unity/array-split levers auto-disable.
679+
CIBW_ENVIRONMENT_LINUX: >-
680+
FVTK_ABI3=0
681+
FVTK_FAST_LINKER=gold
682+
CCACHE_DIR=/ccache
683+
CMAKE_C_COMPILER_LAUNCHER=ccache
684+
CMAKE_CXX_COMPILER_LAUNCHER=ccache
685+
CCACHE_MAXSIZE=30G
686+
# Default repair stamps manylinux_2_28; legacy wheels are manylinux2014.
687+
CIBW_REPAIR_WHEEL_COMMAND_LINUX: "auditwheel repair --plat manylinux2014_x86_64 -w {dest_dir} {wheel}"
688+
- uses: actions/upload-artifact@v4
689+
with:
690+
name: fvtk-wheels-legacy-linux
691+
path: wheelhouse/*.whl
692+
if-no-files-found: error
693+
694+
build-legacy-windows:
695+
name: build legacy windows (cp310+cp311)
696+
# RELEASE-ONLY: the legacy cp310/cp311 wheels build only when a Release is
697+
# published (where `publish` ships them). Kept off push-to-main / PRs / dispatch
698+
# so they never add to per-merge CI cost; they are validated at release time.
699+
if: github.event_name == 'release'
700+
runs-on: ${{ vars.FVTK_RUNNER_WINDOWS || 'windows-latest' }}
701+
timeout-minutes: 120
702+
steps:
703+
- uses: actions/checkout@v5
704+
with:
705+
fetch-depth: 0
706+
# Ninja does NOT auto-run vcvars; ci/cmake/windows.cmake pins cl, so cl must
707+
# be discoverable (same as the abi3 Windows leg).
708+
- name: Set up MSVC (vcvars) for the Ninja build
709+
uses: ilammy/msvc-dev-cmd@v1
710+
with:
711+
arch: x64
712+
- name: Build wheels + smoke (cibuildwheel)
713+
uses: pypa/cibuildwheel@v3.1.4
714+
env:
715+
CIBW_BUILD: "cp310-* cp311-*"
716+
# CIBW_ENVIRONMENT REPLACES the pyproject [tool.cibuildwheel.windows.
717+
# environment] table, so re-declare the Windows build vars: the MSVC
718+
# init-cache (link-closure restore for trimmed render classes), the MSVC
719+
# toolchain gate, and FVTK_ABI3=0 for the legacy per-version path.
720+
CIBW_ENVIRONMENT: "FVTK_ABI3=0 FVTK_CMAKE_INIT=ci/cmake/windows.cmake FVTK_FORCE_MSVC=ON"
721+
- uses: actions/upload-artifact@v4
722+
with:
723+
name: fvtk-wheels-legacy-windows
724+
path: wheelhouse/*.whl
725+
if-no-files-found: error
726+
727+
build-legacy-macos:
728+
name: build legacy macos (cp310+cp311)
729+
# RELEASE-ONLY: the legacy cp310/cp311 wheels build only when a Release is
730+
# published (where `publish` ships them). Kept off push-to-main / PRs / dispatch
731+
# so they never add to per-merge CI cost; they are validated at release time.
732+
if: github.event_name == 'release'
733+
runs-on: ${{ vars.FVTK_RUNNER_MACOS || 'macos-14' }}
734+
timeout-minutes: 120
735+
steps:
736+
- uses: actions/checkout@v5
737+
with:
738+
fetch-depth: 0
739+
- name: Build wheels + smoke (cibuildwheel)
740+
uses: pypa/cibuildwheel@v3.1.4
741+
env:
742+
CIBW_BUILD: "cp310-* cp311-*"
743+
# CIBW_ENVIRONMENT REPLACES the pyproject [tool.cibuildwheel.macos.
744+
# environment] table, so re-declare every macOS build var (init-cache,
745+
# target OS, deployment target + host-platform tag, ccache launchers) and
746+
# add FVTK_ABI3=0. archs/before-build/repair/test-command are NOT in the
747+
# environment table, so they survive from pyproject (arm64, delocate+strip
748+
# repair, smoke_min) unchanged.
749+
CIBW_ENVIRONMENT: >-
750+
FVTK_ABI3=0
751+
FVTK_CMAKE_INIT=ci/cmake/macos.cmake
752+
FVTK_TARGET_OS=macos
753+
MACOSX_DEPLOYMENT_TARGET=11.0
754+
_PYTHON_HOST_PLATFORM=macosx-11.0-arm64
755+
CMAKE_C_COMPILER_LAUNCHER=ccache
756+
CMAKE_CXX_COMPILER_LAUNCHER=ccache
757+
- uses: actions/upload-artifact@v4
758+
with:
759+
name: fvtk-wheels-legacy-macos
760+
path: wheelhouse/*.whl
761+
if-no-files-found: error
762+
619763
publish:
620764
name: Publish to PyPI (trusted publishing)
621765
# Only on a published GitHub Release, and only after the SHIPPED-config legs
622766
# are green: build-lto (the -O3 + LTO Linux x86_64 wheel that ships) validated
623767
# by bitexact-lto + renderexact-lto, plus the optimized macOS/Windows wheels
624-
# from build-other-os and the aarch64 wheel from build-arm. The fast non-LTO
625-
# gate (build/bitexact/renderexact) is not a dependency — its wheel is never
626-
# published.
768+
# from build-other-os and the aarch64 wheel from build-arm. Also waits on the
769+
# three legacy legs (build-legacy-linux/windows/macos) so the cp310/cp311
770+
# version-specific wheels ship in the same release as the abi3 wheels. The fast
771+
# non-LTO gate (build/bitexact/renderexact) is not a dependency — its wheel is
772+
# never published.
627773
if: github.event_name == 'release'
628-
needs: [build-lto, build-arm, build-other-os, bitexact-lto, renderexact-lto]
774+
needs: [build-lto, build-arm, build-other-os, bitexact-lto, renderexact-lto,
775+
build-legacy-linux, build-legacy-windows, build-legacy-macos]
629776
runs-on: ${{ vars.FVTK_RUNNER_LINUX_SMALL || 'ubuntu-latest' }}
630777
# Artifact download + PyPI upload only; cap low.
631778
timeout-minutes: 15
@@ -666,12 +813,32 @@ jobs:
666813
with:
667814
name: fvtk-wheels-windows-latest
668815
path: dist
816+
# The six legacy (cp310 + cp311) wheels — Linux, Windows, macOS — into the
817+
# SAME dist/. They ship to PyPI and (via the attach step below) to the Release
818+
# alongside the four abi3 wheels. Distinct artifact names, no glob collision.
819+
- name: Download legacy Linux wheels (cp310+cp311)
820+
uses: actions/download-artifact@v4
821+
with:
822+
name: fvtk-wheels-legacy-linux
823+
path: dist
824+
- name: Download legacy Windows wheels (cp310+cp311)
825+
uses: actions/download-artifact@v4
826+
with:
827+
name: fvtk-wheels-legacy-windows
828+
path: dist
829+
- name: Download legacy macOS wheels (cp310+cp311)
830+
uses: actions/download-artifact@v4
831+
with:
832+
name: fvtk-wheels-legacy-macos
833+
path: dist
669834
- name: List + sanity-check the release wheels
670835
run: |
671836
ls -l dist
672837
n=$(ls dist/*.whl 2>/dev/null | wc -l)
673838
echo "collected $n wheel(s)"
674-
test "$n" -ge 1 || { echo "::error::no wheels to publish"; exit 1; }
839+
# 4 abi3 (linux x86_64 + aarch64, macOS, Windows) + 6 legacy
840+
# (cp310/cp311 x linux/windows/macos) == 10 expected.
841+
test "$n" -ge 10 || { echo "::error::expected >=10 wheels (4 abi3 + 6 legacy), found $n"; exit 1; }
675842
- name: Publish to PyPI
676843
uses: pypa/gh-action-pypi-publish@release/v1
677844
with:

.github/workflows/wheels-cibuildwheel.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ jobs:
7777
# leaves the SDK resource toolchain (rc.exe / mt.exe) off PATH — the C
7878
# compiler probe then fails its link step ("RC Pass 1: no such file or
7979
# directory", CMAKE_MT-NOTFOUND) and cmake declares cl "broken". This
80-
# mirrors ci.yml's build-other-os, wheels-legacy.yml, and wheels-test.yml,
80+
# mirrors ci.yml's build-other-os + build-legacy-windows and wheels-test.yml,
8181
# which all run this step; this release workflow was the only Windows
8282
# builder missing it. ci/cmake/windows.cmake pins CMAKE_CXX_COMPILER=cl, so
8383
# cl must be on PATH regardless.

0 commit comments

Comments
 (0)