Skip to content

tables: add build-tables.yml for riscv64 wheels #5

tables: add build-tables.yml for riscv64 wheels

tables: add build-tables.yml for riscv64 wheels #5

Workflow file for this run

# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# This workflow is based on: https://github.com/PyTables/PyTables/blob/v3.11.1/.github/workflows/wheels.yml
name: Build tables wheels (riscv64)
on:
workflow_dispatch:
inputs:
version:
description: 'tables version to build (git tag without leading v, e.g. 3.11.1)'
required: true
default: '3.11.1'
pull_request:
paths:
- '.github/workflows/build-tables.yml'
- 'patches/tables/**'
concurrency:
group: ${{ github.workflow }}-${{ inputs.version || '3.11.1' }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
permissions:
contents: read # to fetch code (actions/checkout)
env:
# `inputs.version` is empty on pull_request events; default to 3.11.1 there.
TABLES_VERSION: ${{ inputs.version || '3.11.1' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64
# Matches upstream's own `env.HDF5_VERSION` in wheels.yml ("H5Dchunk_iter needs
# at least 1.14.1"). No libaec/szip: upstream's own Linux wheel build doesn't
# enable szip either (only zlib), so this needs no szip counterpart.
HDF5_VERSION: '2.2.0'
jobs:
setup:
uses: $/.github/workflows/_setup.yml
build_wheels:
needs: [setup]
name: Build tables ${{ inputs.version || '3.11.1' }} ${{ matrix.tag }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: ${{ matrix.timeout }}
strategy:
fail-fast: false
matrix:
include:
# setup.py's bdist_wheel_abi3 hardcodes the wheel tag to cp311-abi3
# regardless of which Py_LIMITED_API-built interpreter compiles it, so
# the produced wheel is the same upstream ships. The build floor here is
# cp312, not cp311 like upstream: numexpr (a runtime dependency) only has
# a riscv64 build on this registry from cp312 onward (see
# build-numexpr.yml), and cibuildwheel's abi3 handling builds once on the
# lowest requested identifier and reuses that wheel to test the rest.
- tag: cp311-abi3
build: >-
cp312-manylinux_riscv64 cp313-manylinux_riscv64
cp314-manylinux_riscv64
timeout: 420
- tag: cp314t
build: cp314t-manylinux_riscv64
timeout: 180
steps:
- name: Checkout PyTables v${{ env.TABLES_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: PyTables/PyTables
ref: v${{ env.TABLES_VERSION }}
submodules: true
persist-credentials: false
- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
persist-credentials: false
# hdf5-blosc2/src/blosc2_filter.c is a plain vendored copy (not a git
# submodule) written against the pre-3.0 c-blosc2 API: it fails to
# compile against any current blosc2 (BLOSC2_MAX_DIM was renamed to
# B2ND_MAX_DIM in c-blosc2 3.0, which every blosc2 >= 3.2.0 bundles,
# including the one this registry publishes). See gotcha 269.
- name: Patch tables source
run: git apply python-wheels/patches/tables/${{ env.TABLES_VERSION }}/*.patch
- uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
env:
CIBW_ARCHS: riscv64
CIBW_BUILD: ${{ matrix.build }}
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# Neutralizes pyproject.toml's own before-build, which installs from
# hash-pinned lockfiles with no riscv64 wheels; build-system.requires
# (Cython, numpy, blosc2, ...) resolves normally instead.
CIBW_BEFORE_BUILD: 'true'
# zlib/bzip2-devel, then HDF5 from source, mirroring PyTables' own
# ci/github/get_hdf5.sh (Linux branch) and wheels.yml's HDF5_VERSION.
# No libaec/szip: upstream doesn't enable szip on Linux either.
# LICENSES/BZIP2.txt: bzip2, unlike zlib, isn't on the manylinux policy
# allow-list, so auditwheel vendors it into the wheel; PyTables' own
# license-files glob (LICENSES/*.txt) already covers HDF5/zlib/blosc's
# licenses but ships no bzip2 one, so add it the same way netcdf4's port
# does for its own vendored bzip2 copy.
CIBW_BEFORE_ALL_LINUX: |
set -ex
dnf -y install zlib-devel bzip2-devel
mkdir -p /tmp/hdf5
curl -fsSL "https://github.com/HDFGroup/hdf5/archive/refs/tags/$HDF5_VERSION.tar.gz" | tar xz --strip-components=1 -C /tmp/hdf5
cmake -S /tmp/hdf5 -B /tmp/hdf5/build \
-D CMAKE_BUILD_TYPE=Release \
-D CMAKE_INSTALL_PREFIX=/usr/local \
-D CMAKE_INSTALL_LIBDIR=lib \
-D BUILD_TESTING=OFF \
-D BUILD_STATIC_LIBS=OFF \
-D HDF5_BUILD_EXAMPLES=OFF \
-D HDF5_BUILD_TOOLS=OFF \
-D HDF5_BUILD_UTILS=OFF \
-D HDF5_ENABLE_ZLIB_SUPPORT=ON
cmake --build /tmp/hdf5/build -j "$(nproc)"
cmake --install /tmp/hdf5/build
ldconfig
cp /usr/share/licenses/bzip2-libs/LICENSE "{project}/LICENSES/BZIP2.txt"
CIBW_ENVIRONMENT_LINUX: >-
HDF5_DIR=/usr/local
HDF5_VERSION=${{ env.HDF5_VERSION }}
DISABLE_AVX2=true
CFLAGS=-g0
PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
PIP_ONLY_BINARY=numpy,blosc2,numexpr
# blosc2's shared lib lives inside the separately-installed blosc2
# wheel (a runtime dependency, already on this registry); tables loads
# it via ctypes at import time (tables/__init__.py's _load_blosc2()),
# so auditwheel must not vendor a second copy of it (gotcha 17).
CIBW_REPAIR_WHEEL_COMMAND_LINUX: >-
auditwheel repair -w {dest_dir} {wheel} --exclude 'libblosc2.so*'
# Mirrors upstream's own test_wheels job (wheels.yml): no lzo in the
# check list because upstream's Linux wheels don't build with lzo
# either (their before-all doesn't install lzo-devel).
CIBW_TEST_COMMAND: >-
python -c "import tables; keys = 'zlib bzip2 blosc blosc2'.split(); missing = [key for key in keys if tables.which_lib_version(key) is None]; assert missing == [], missing" &&
python -m tables.tests.test_all -v
- name: Check the wheel ships the extensions and the bundled licences
run: |
python3 - wheelhouse/*.whl <<'EOF'
import sys, zipfile
names = zipfile.ZipFile(sys.argv[1]).namelist()
expected_mods = {
"utilsextension", "hdf5extension", "tableextension",
"_comp_lzo", "_comp_bzip2", "linkextension",
"lrucacheextension", "indexesextension",
}
# The cp311-abi3 leg's extensions are tagged "*.abi3.so"; cp314t
# can't use the stable ABI, so its own leg ships
# "*.cpython-314t-riscv64-linux-gnu.so" instead. Match on the
# module name only, not the interpreter-specific suffix. Excludes
# "tables/libblosc2.so*", the bundled runtime lib (not a
# tables-owned extension module, see CIBW_REPAIR_WHEEL_COMMAND_LINUX
# above).
found_mods = {
n.split("/", 1)[1].split(".", 1)[0]
for n in names
if n.startswith("tables/") and n.endswith(".so")
and not n.startswith("tables/lib")
}
assert found_mods == expected_mods, found_mods
licences = {
n.split(".dist-info/licenses/", 1)[1] for n in names
if ".dist-info/licenses/" in n and not n.endswith("/")
}
# Everything the wheel actually compiles in or links (patches/tables):
# c-blosc and its bundled LZ4/zlib/zstd, HDF5, h5py (credited in
# hdf5extension.pyx/array.py), and our own added BZIP2.txt. Not
# upstream's whole LICENSES/ dir - most of the rest (FastLZ, Snappy,
# Windows-only shims, the docs theme) covers content this build
# never ships.
expected_licences = {
"LICENSE.txt", "LICENSES/BLOSC.txt", "LICENSES/BZIP2.txt",
"LICENSES/H5PY.txt", "LICENSES/HDF5.txt", "LICENSES/LZ4.txt",
"LICENSES/ZLIB.txt", "LICENSES/ZSTD.txt",
}
assert licences == expected_licences, licences
EOF
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: tables-${{ env.TABLES_VERSION }}-${{ matrix.tag }}-manylinux_riscv64
path: ./wheelhouse/*.whl
if-no-files-found: error
publish:
name: Publish tables ${{ inputs.version || '3.11.1' }}
needs: [setup, build_wheels]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: tables-${{ inputs.version || '3.11.1' }}-*-manylinux_riscv64