wheels, abi3, platform tags
Building and shipping
Two classes of binary ship in the Python package: portable ctypes libraries that run everywhere, and a macOS-only native extension built abi3 so one wheel covers every Python version. How each is built, tagged, and published.
Two classes of binary
pip install kinogaki ships compiled code, and there are two kinds of it with opposite packaging needs:
- The portable ctypes libraries (
libkinogaki_core,libkinogaki_platform). Plain C++ shared libraries with no Python C-API surface, built with the headless null backend. They run on any CPython 3.x and on every OS. They carry the model andkinogaki serve. - The native extension (
_ui). A real CPython C-extension behindkinogaki run, linked against Cocoa and Metal. It builds on macOS only, and it touches the Python C-API, so it is version-sensitive in a way the ctypes libraries are not.
The whole packaging design follows from that split.
abi3: one wheel for every Python
A normal C-extension is locked to one CPython minor version, because it reaches into Python internals that change shape between releases. You would ship one build per version. The native extension is instead compiled against the Limited API (Py_LIMITED_API), which restricts it to a subset CPython guarantees stable across versions. The result is an abi3 wheel: one compiled extension that loads on every CPython 3.10 and up, including versions not released yet. This is what cryptography ships, and it is why the macOS wheel is built once rather than once per Python version.
What ships per platform
- macOS (
cp310-abi3-macosx_*_arm64): the ctypes libraries plus the_uinative-window extension. Built natively on a Mac, thendelocatevendors FreeType into the wheel. - Linux (
py3-none-manylinux_*_x86_64): the ctypes libraries only. Built in a manylinux Docker container, repaired byauditwheel.
The macOS wheel is cp310-abi3 because it carries the version-stable extension. The Linux wheel is py3-none because it has no Python extension at all, only the version-agnostic ctypes libraries; off macOS the extension is simply absent and kinogaki.ui degrades cleanly. A wheel must never be tagged py3-none-any (pure Python), because that would offer a macOS build to a Linux box; setup.py forces a platform tag on every wheel.
How a release runs
The whole release is one command that runs on the developer's own machine. It builds the binaries, packages each wheel, installs each into a clean environment and renders a document so a broken wheel never reaches PyPI, and then publishes with a token. The repair step (delocate on macOS, auditwheel on Linux) vendors the non-system shared libraries (such as FreeType) into the wheel, so an end user needs nothing outside pip.
Where it runs, and why local
Building locally is a deliberate cost choice. GitHub Actions, which numpy, scipy, and cryptography all use with cibuildwheel, is free and unlimited only on public repositories; on a private repository it bills per minute and macOS costs roughly ten times Linux. AWS is worse for occasional macOS builds (EC2 Mac requires a dedicated host with a 24-hour minimum). For a private repository making occasional releases, building on the Mac you already own plus Linux in Docker, and publishing with a token, costs nothing. If push-button CI is ever wanted, the cheapest path is to make just the build repository public (the source can stay private), which makes GitHub Actions free again, and use cibuildwheel with Trusted Publishing exactly as the big projects do.