Release Wheels¶
This page is for maintainers preparing v0.4 release-candidate wheels. User-facing installation
instructions belong in the install docs. Low-level implementation notes live next to the scripts in
tools/release_wheels/.
Current Policy¶
Datoviz v0.4 wheels include the Python package, the generated ctypes binding, public C
headers, CMake package files, and the platform native Datoviz runtime library.
The Python layer loads Datoviz through ctypes, so the wheel is independent of the Python C ABI.
Release artifacts are therefore platform wheels tagged:
py3-none-<platform>
For example:
datoviz-0.4.0.dev0-py3-none-manylinux_2_34_x86_64.whl
Shaderc and Vulkan dependency policy:
- Source builds default to
DVZ_ENABLE_SHADERC=AUTO, so runtime GLSL compilation is enabled when shaderc headers are present and unavailable otherwise. - Official release wheels must configure with
DVZ_ENABLE_SHADERC=ON. - Official release wheels must use a Release native build and pass the installed
--release-buildcheck; a native version containing(DEBUG)is not publishable. - Official release wheels disable mimalloc. A wheel loads Datoviz into an existing interpreter, where overriding the process allocator can cross unsafe allocation boundaries with dynamically loaded dependencies such as shaderc.
- Do not commit vendored SDK or runtime payloads such as Vulkan SDK libraries or shaderc dylibs.
- On macOS wheels,
delocaterepairs linked dylib dependencies.DVZ_WHEEL_RUNTIME_DIRSis only for libraries Datoviz loads withdlopen()at runtime. glslcis only a tests/tools dependency. It is not part of the Datoviz runtime GLSL compilation path.
Required Python install-smoke versions are 3.10, 3.11, 3.12, 3.13, and 3.14. Python 3.15 is a prerelease smoke lane and should stay non-blocking until it is appropriate for normal PyPI users.
Target platform artifacts are:
| OS | Architecture | Wheel platform tag |
|---|---|---|
| Linux | x86_64 |
manylinux_2_34_x86_64 |
| Linux | aarch64 |
manylinux_2_34_aarch64 |
| macOS | x86_64 |
macosx_15_0_x86_64 |
| macOS | arm64 |
macosx_15_0_arm64 |
| Windows | AMD64 |
win_amd64 |
| Windows | ARM64 |
win_arm64 |
Local Commands¶
Use the primary just recipes from the repository root.
| Task | Command |
|---|---|
| Print the intended matrix | just wheel-matrix |
Stage the wheel tree from build/ |
just wheel-stage --clean |
| Build the platform wheel | just wheel-build --platform-tag manylinux_2_34_x86_64 |
| Validate wheel version and tag | just wheel-validate --platform-tag manylinux_2_34_x86_64 |
| Inspect wheel contents | just wheel-inspect |
| Inspect native dependencies | just wheel-inspect --native-deps |
| Install-smoke the wheel | just wheel-check --cmake-consumer --qt-probe optional |
| Run the local CI-parity path | just wheel-ci-local manylinux_2_34_x86_64 |
| Run local CI parity after rebuilding native code | just wheel-ci-local manylinux_2_34_x86_64 1 |
| Run the local manylinux Docker proof | just wheel-manylinux-docker x86_64 |
| Check a local TestPyPI candidate | just testpypi-check manylinux_2_34_x86_64 |
| Upload one local wheel to TestPyPI | just testpypi-upload manylinux_2_34_x86_64 dist yes |
The normal local loop is:
just build
just wheel-stage --clean
just wheel-build --platform-tag manylinux_2_34_x86_64
just wheel-validate --platform-tag manylinux_2_34_x86_64
just wheel-inspect --native-deps
just wheel-check --cmake-consumer --qt-probe optional
just wheel-ci-local defaults to using the existing build/ tree. Pass 1 as the second argument
when the native build should be refreshed first. This is useful because packaging changes should be
testable without reconfiguring unrelated local native build options.
For macOS release-tag proof, set the deployment target before building native code and every
packaged dependency. The wheel platform tag must not claim an older macOS version than any bundled
dylib supports; delocate-wheel enforces this and rejects mismatches. Local host-native proofs may
use the host tag, but release evidence must use the matrix tag.
For the arm64 release target:
MACOSX_DEPLOYMENT_TARGET=15.0 just build
DVZ_WHEEL_RUNTIME_DIRS="<shaderc-lib>:<vulkan-loader-lib>:<moltenvk-lib>" \
just wheel-stage --clean
MACOSX_DEPLOYMENT_TARGET=15.0 \
just wheel-build --platform-tag macosx_15_0_arm64
just wheel-validate --platform-tag macosx_15_0_arm64
just wheel-inspect --native-deps
just wheel-check --shaderc --cmake-consumer --qt-probe optional
If delocate-wheel reports that libdatoviz.dylib or copied dependencies have a newer minimum
macOS target, rebuild those inputs with the intended MACOSX_DEPLOYMENT_TARGET or move the proof to
a clean CI/builder environment. Do not retag a newer-target wheel as an older macOS wheel.
Pipeline Stages¶
wheel-stage copies the release payload into build/wheel-stage/:
datoviz/*.pyanddatoviz/experimental/;pyproject.toml;- native Datoviz runtime library from
build/; - public headers and CMake package files used by C consumers;
- platform runtime dependencies that the current wheel policy bundles;
datoviz/_wheel_payload.json, so the backend and validation tools can audit included files.
wheel-build builds from the staged tree, clears stale dist/datoviz-*.whl files, and writes the
final py3-none-<platform> wheel directly through the Datoviz build backend. It no longer creates
py3-none-any first and retags it afterwards. --skip-repair is available only for local backend
diagnostics; release evidence must run the platform repair path.
wheel-validate checks wheel filenames in dist/ against the expected project version and platform
tags. With no --platform-tag, it expects the full release matrix. Use --platform-tag for local
single-platform validation.
wheel-inspect lists packaged files and, with --native-deps, delegates to the platform dependency
tool.
wheel-check creates a clean virtual environment, installs the wheel, and verifies:
import datoviz;import datoviz.raw;python -m datoviz.cli --prefix;- compiler and linker flags from
datoviz.cli; - CMake package discovery and a tiny C consumer executable;
- optional render smoke when
--renderis passed; - optional or required Qt probe depending on
--qt-probe.
Native Dependency Inspection¶
CI should fail early if the expected inspection or repair tool is unavailable:
| OS | Tool |
|---|---|
| Linux | auditwheel |
| macOS | delocate-listdeps from delocate |
| Windows | python -m delvewheel show from delvewheel |
The current local wheel-inspect --native-deps command reports a missing tool but does not fail.
The draft GitHub Actions workflow installs and checks the expected tool before building wheels.
Qt And PyQt¶
The main wheel includes datoviz.qt, but PyQt is optional:
python -m pip install "datoviz[qt]"
The main wheel must still install and import without PyQt. It must not bundle Qt, PyQt, or
datoviz_qtbridge. The datoviz[qt] extra installs PyQt6 only and is insufficient for hosted
rendering without a compatible native bridge. datoviz.qt should fail with a clear diagnostic when
PyQt6, Qt Vulkan support, the optional Qt bridge provider, or platform Vulkan WSI support is
missing.
Maintainer checks should use:
just wheel-check --cmake-consumer --qt-probe optional
Use --qt-probe required only on a machine or CI runner where PyQt6, Qt Vulkan support, and the
Datoviz Qt bridge provider are expected to be present.
RC2 has no packaged Qt bridge provider; its Qt/PyQt path is source-build-only. The RC3 release goal
is a separate provider route, not a base-wheel dependency. Source builds may build
datoviz_qtbridge with DVZ_ENABLE_QT_BRIDGE=ON or AUTO; local and split provider validation
should point datoviz.qt at that bridge with DATOVIZ_QTBRIDGE_LIBRARY. Conda-forge is the
preferred first binary provider channel because it can keep Qt, PyQt, and the bridge on one managed
runtime. PyPI provider wheels should wait until the bridge ABI, runtime-version policy, and
per-platform Qt library layout are proven.
On a Qt-capable source host, validate the split provider route with:
DVZ_CMAKE_ARGS="-DDVZ_ENABLE_QT_BRIDGE=ON" just build
DATOVIZ_QTBRIDGE_LIBRARY=build/qtbridge/libdatoviz_qtbridge.so python -m datoviz.qt
DATOVIZ_QTBRIDGE_LIBRARY=build/qtbridge/libdatoviz_qtbridge.so \
python examples/python/qt/hosted_pyqt.py --smoke-ms 1000
Record the PyQt and bridge Qt runtime versions from that lane. If Qt development files, PyQt6 Vulkan bindings, the bridge library, or platform WSI support are missing, record the specific diagnostic as an environment block rather than treating the base wheel optional probe as provider proof.
GitHub Actions Workflow¶
The v0.4 wheel workflow is manual-only:
.github/workflows/wheels.yml
It has no scheduled trigger. Dispatch it only after the local backend scripts and at least one
targeted branch run have proven the path. Keep .github/workflows-draft/wheels.yml as a staging
reference for major workflow rewrites.
The workflow uses python -m pip wheel with Datoviz release-wheel config settings, not
cibuildwheel. The wheel policy source of truth is [tool.datoviz.wheel] in pyproject.toml and
the backend under tools/datoviz_build_backend/.
Before relying on a run for release evidence:
just wheel-ci-local <host-platform-tag>passes on each maintained host OS;- Linux wheels build and inspect on
x86_64andaarch64; - macOS wheels build and inspect on
x86_64andarm64; - Windows wheels build and inspect on
AMD64andARM64; - host-native Python install smokes pass for Python 3.10 through 3.14;
- the Python 3.15 prerelease lane remains non-blocking;
- Qt probing is optional unless the runner explicitly installs a known-good Qt/PyQt stack;
- artifact names match
wheel-<os>-<arch>; - upload to TestPyPI or PyPI is handled by a separate release workflow.
Local TestPyPI Rehearsal¶
Install twine and configure a TestPyPI token before uploading:
python -m pip install --upgrade twine
For a single locally built wheel:
just testpypi-check manylinux_2_34_x86_64
just testpypi-upload manylinux_2_34_x86_64 dist yes
For a complete wheelhouse:
just testpypi-check-all wheelhouse
just testpypi-upload-all wheelhouse yes
The upload recipes refuse to run unless the final argument is yes. TestPyPI does not allow
overwriting an existing filename/version, so bump the package version before retrying a previously
uploaded candidate.
After the upload, verify the package-index path against the exact successful Wheels run:
gh workflow run package-index-verification.yml --ref v0.4-dev \
-f index=testpypi \
-f version=0.4.0rc2 \
-f wheel_run_id=<wheels-run-id>
workflow_dispatch requires the workflow file to exist on the repository default branch. Before
that is true for a release branch, commit
.github/package-index-verification-request.json on v0.4-dev with the equivalent request:
{
"index": "testpypi",
"version": "0.4.0rc2",
"wheel_run_id": "<wheels-run-id>"
}
Changing that request file triggers the same workflow on the release branch. Do not create or update it until the named version has been uploaded to the selected index.
The six native jobs download Datoviz through the selected package index, require the downloaded filename and SHA-256 to match the corresponding GitHub Actions wheel artifact exactly, and run a lightweight clean installed-package import/version/CLI smoke. They intentionally do not repeat the full rendering and example conformance campaign: exact byte identity transfers that evidence to the indexed files. The report job publishes one consolidated HTML artifact. Retrieve and open it with the verification workflow run ID:
just package-index-report <verification-workflow-run-id>
Use the same workflow with -f index=pypi after public publication and before switching public
installation documentation to the released package.
Known Constraints¶
Cross-arch build and cross-arch execution are different guarantees. Linux aarch64 and Windows
ARM64 should be treated as build and inventory coverage unless a native runner is available.
Local native rebuilds inherit local CMake cache options. If just wheel-ci-local <tag> 1 fails in
just build, fix the native build configuration or run the packaging validation from an existing
known-good build/ tree with:
just wheel-ci-local <tag>
Do not publish artifacts from a dirty or manually patched staging tree. Recreate build/wheel-stage
with just wheel-stage --clean before release validation.
Local Manylinux Docker Proof¶
A normal recent Linux workstation can build and smoke-test a Datoviz wheel, but it may not be valid
manylinux_2_34 release evidence. For example, an Ubuntu 24.04 native build can emit GLIBC 2.38
symbols, which auditwheel correctly constrains to a newer platform tag.
Use the local manylinux Docker route for Linux x86_64 RC evidence:
just wheel-manylinux-docker x86_64
This is the local route to reuse before dispatching or interpreting Linux wheel CI. A native Ubuntu
wheel may be useful for iteration, but it is not enough for manylinux_2_34 RC evidence because it
can pick up newer host glibc symbols.
By default this uses the current upstream PyPA base image:
quay.io/pypa/manylinux_2_34_x86_64:latest
Override the image only for a known equivalent builder:
DATOVIZ_MANYLINUX_IMAGE=rossant/datoviz_manylinux:latest just wheel-manylinux-docker x86_64
The container script installs the missing RPM packages, builds Datoviz with
DVZ_ENABLE_SHADERC=ON, regenerates the Python binding and NumPy-adaptation module, builds the release wheel
through the v0.4 backend, runs auditwheel inspection, and executes the installed-wheel
shaderc/CMake consumer smoke. Output wheels are written to wheelhouse/ and must not be committed.
Set DATOVIZ_MANYLINUX_GENERATE_CTYPES=0 only when intentionally checking an already generated
tree.
Keep the upstream PyPA image as the release source of truth unless a refreshed custom image is
needed for build time. If a custom rossant/datoviz_manylinux image is used for RC evidence, record
its digest, base image, creation date, and package deltas from the upstream PyPA image.
Keep the local Docker route scoped to x86_64 until repeated runs are stable. Do not extend the
recipe to aarch64 only because a platform tag exists; add that path only after a native or
explicitly validated emulated builder can run the installed-wheel smoke.