From 94d71d0dc6143c5544291a95a9ae1d5a8da312e3 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 10 Sep 2026 22:08:25 +0200 Subject: [PATCH 1/3] pymupdfb: add build-pymupdfb.yml for riscv64 wheels --- .github/workflows/build-pymupdfb.yml | 117 +++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 .github/workflows/build-pymupdfb.yml diff --git a/.github/workflows/build-pymupdfb.yml b/.github/workflows/build-pymupdfb.yml new file mode 100644 index 000000000..961893dab --- /dev/null +++ b/.github/workflows/build-pymupdfb.yml @@ -0,0 +1,117 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# PyMuPDFb is pymupdf/PyMuPDF's own pipcl/setup.py build, run with +# PYMUPDF_SETUP_FLAVOUR=bd instead of the default: a thin split-wheel of MuPDF's +# shared libraries, built the same way scripts/gh_release.py builds it upstream. +name: Build pymupdfb wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'pymupdfb version (pymupdf/PyMuPDF git tag, e.g. 1.24.10)' + required: true + default: '1.24.10' + pull_request: + paths: + - '.github/workflows/build-pymupdfb.yml' + +concurrency: + group: ${{ github.workflow }}-${{ inputs.version || '1.24.10' }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + PYMUPDFB_VERSION: ${{ inputs.version || '1.24.10' }} + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + + build_wheels: + needs: [setup] + name: Build pymupdfb ${{ inputs.version || '1.24.10' }} py3-none-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + # musllinux is skipped: it needs the same libclang-via-dnf workaround below, + # which build-pymupdf.yml also only exercises on manylinux. + timeout-minutes: 360 + + steps: + - name: Checkout pymupdf/PyMuPDF ${{ env.PYMUPDFB_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: pymupdf/PyMuPDF + ref: ${{ env.PYMUPDFB_VERSION }} + persist-credentials: false + + - name: Build wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + output-dir: wheelhouse/ + # setup.py's get_requires_for_build_wheel() always adds swig/libclang + # regardless of flavour, and the resulting wheel is py3-none-*, so one + # CPython selector covers it. + only: cp312-manylinux_riscv64 + env: + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + # As in build-pymupdf.yml: the `libclang` PyPI wheel has no riscv64 + # build, so use the image's own libclang.so via the pure-Python + # `clang` bindings instead (clang-devel supplies the unversioned + # symlink clang.cindex dlopens). + CIBW_BEFORE_ALL_LINUX: dnf install -y clang-devel + CIBW_ENVIRONMENT: >- + PYMUPDF_SETUP_FLAVOUR=bd + PYMUPDF_SETUP_LIBCLANG=clang + XCFLAGS=-D__LITTLE_ENDIAN__ + XCXXFLAGS=-D__LITTLE_ENDIAN__ + # Upstream's own gh_release.py disables repair for this flavour: + # auditwheel finds nothing to add and exits non-zero. setup.py's + # pipcl already tags the wheel from the image's own AUDITWHEEL_PLAT. + CIBW_REPAIR_WHEEL_COMMAND_LINUX: '' + + - name: Check the wheel carries riscv64 MuPDF libraries + run: | + python3 - wheelhouse/*.whl <<'EOF' + import sys, zipfile + + EM_RISCV = 243 + expected_prefixes = ("pymupdf/libmupdf.so", "pymupdf/libmupdfcpp.so") + + for whl in sys.argv[1:]: + with zipfile.ZipFile(whl) as zf: + names = zf.namelist() + found = {p: None for p in expected_prefixes} + for name in names: + for p in expected_prefixes: + if name.startswith(p) and found[p] is None: + found[p] = name + assert all(found.values()), (whl, found, names) + for p, name in found.items(): + blob = zf.read(name) + assert blob[:4] == b"\x7fELF", (name, blob[:4]) + machine = int.from_bytes(blob[18:20], "little") + assert machine == EM_RISCV, (name, machine) + print(name, "riscv64 ELF") + assert any(n.endswith(".dist-info/COPYING") for n in names), names + print(whl, "ok") + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pymupdfb-${{ env.PYMUPDFB_VERSION }}-py3-none-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish pymupdfb ${{ inputs.version || '1.24.10' }} + needs: [setup, build_wheels] + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + with: + artifact-pattern: pymupdfb-${{ inputs.version || '1.24.10' }}-*-manylinux_riscv64 From afe104a970f2f34e0386f60441124279cd53ab71 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 10 Sep 2026 23:04:01 +0200 Subject: [PATCH 2/3] pymupdfb: pin clang<21 for mupdf's own libclang-based codegen (gotcha 346) --- .github/workflows/build-pymupdfb.yml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-pymupdfb.yml b/.github/workflows/build-pymupdfb.yml index 961893dab..31e5aaf0c 100644 --- a/.github/workflows/build-pymupdfb.yml +++ b/.github/workflows/build-pymupdfb.yml @@ -61,11 +61,13 @@ jobs: # As in build-pymupdf.yml: the `libclang` PyPI wheel has no riscv64 # build, so use the image's own libclang.so via the pure-Python # `clang` bindings instead (clang-devel supplies the unversioned - # symlink clang.cindex dlopens). + # symlink clang.cindex dlopens). Pinned below <21: 21.x's `File` + # gained __eq__ with no __hash__, so mupdf's own File-keyed parse + # cache dies with `TypeError: unhashable type: 'File'` (gotcha 346). CIBW_BEFORE_ALL_LINUX: dnf install -y clang-devel CIBW_ENVIRONMENT: >- PYMUPDF_SETUP_FLAVOUR=bd - PYMUPDF_SETUP_LIBCLANG=clang + PYMUPDF_SETUP_LIBCLANG=clang==20.1.5 XCFLAGS=-D__LITTLE_ENDIAN__ XCXXFLAGS=-D__LITTLE_ENDIAN__ # Upstream's own gh_release.py disables repair for this flavour: From 2bdcaec88976c06e9b1aeb80d75993adff1cc335 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 10 Sep 2026 23:29:28 +0200 Subject: [PATCH 3/3] pymupdfb: quote the clang==20.1.5 pin so cibuildwheel's bashlex doesn't misparse it as a nested assignment --- .github/workflows/build-pymupdfb.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-pymupdfb.yml b/.github/workflows/build-pymupdfb.yml index 31e5aaf0c..83b2b28b7 100644 --- a/.github/workflows/build-pymupdfb.yml +++ b/.github/workflows/build-pymupdfb.yml @@ -67,7 +67,7 @@ jobs: CIBW_BEFORE_ALL_LINUX: dnf install -y clang-devel CIBW_ENVIRONMENT: >- PYMUPDF_SETUP_FLAVOUR=bd - PYMUPDF_SETUP_LIBCLANG=clang==20.1.5 + PYMUPDF_SETUP_LIBCLANG="clang==20.1.5" XCFLAGS=-D__LITTLE_ENDIAN__ XCXXFLAGS=-D__LITTLE_ENDIAN__ # Upstream's own gh_release.py disables repair for this flavour: