From af5e87006814abd90672b106c110ff052c35cdb1 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 11 Sep 2026 13:09:19 +0200 Subject: [PATCH 1/3] cyvcf2: add build-cyvcf2.yml for riscv64 wheels Cython wrapper around htslib (VCF/BCF parsing). htslib (with its htscodecs submodule) is vendored as a git submodule and compiled from source with autoreconf+configure+make, same shape as build-pysam.yml. Drops --enable-libcurl/--enable-s3/--with-libdeflate from upstream's CYVCF2_HTSLIB_CONFIGURE_OPTIONS: no libcurl-devel and no EPEL on the riscv64 manylinux image (as build-pysam.yml/build-pybigwig.yml), and each of those options defaults to a silent "check" in htslib's configure.ac, so dropping them just lets the auto-detect fail closed instead of requesting a feature we cannot build. --- .github/workflows/build-cyvcf2.yml | 114 +++++++++++++ ...codecs-LICENSE-files-at-project-root.patch | 150 ++++++++++++++++++ 2 files changed, 264 insertions(+) create mode 100644 .github/workflows/build-cyvcf2.yml create mode 100644 patches/cyvcf2/0.34.0/0001-Add-htslib-htscodecs-LICENSE-files-at-project-root.patch diff --git a/.github/workflows/build-cyvcf2.yml b/.github/workflows/build-cyvcf2.yml new file mode 100644 index 0000000000..16eda2bd56 --- /dev/null +++ b/.github/workflows/build-cyvcf2.yml @@ -0,0 +1,114 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on the `build_wheels` job of +# https://github.com/brentp/cyvcf2/blob/v0.34.0/.github/workflows/wheels.yml +name: Build cyvcf2 wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'cyvcf2 version to build (git tag, e.g. 0.34.0)' + required: true + default: '0.34.0' + pull_request: + paths: + - '.github/workflows/build-cyvcf2.yml' + +concurrency: + group: ${{ github.workflow }}-${{ inputs.version || '0.34.0' }}-${{ 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 0.34.0 there. + CYVCF2_VERSION: ${{ inputs.version || '0.34.0' }} + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + + build_wheels: + needs: [setup] + name: Build cyvcf2 ${{ inputs.version || '0.34.0' }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 90 + strategy: + fail-fast: false + matrix: + python: ["cp312", "cp313", "cp314", "cp314t"] + + steps: + - name: Checkout cyvcf2 v${{ env.CYVCF2_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: brentp/cyvcf2 + ref: v${{ env.CYVCF2_VERSION }} + submodules: recursive + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch cyvcf2 source + run: git apply python-wheels/patches/cyvcf2/${{ env.CYVCF2_VERSION }}/00*.patch + + - name: Build wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + output-dir: wheelhouse/ + only: ${{ matrix.python }}-manylinux_riscv64 + env: + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + CIBW_BEFORE_ALL_LINUX: >- + dnf install -y autoconf automake libtool cmake ninja-build + zlib-devel bzip2-devel xz-devel + # No libcurl-devel and no EPEL on riscv64 (as build-pysam.yml/build-pybigwig.yml), + # so drop --enable-libcurl/--enable-s3/--with-libdeflate from upstream's own + # options; each defaults to "check" and htslib's configure silently disables it + # when the headers are absent instead of failing. bz2/lzma default to required, + # matching the devel packages installed above. + CIBW_ENVIRONMENT: >- + PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + PIP_ONLY_BINARY=numpy + CYVCF2_HTSLIB_CONFIGURE_OPTIONS="--enable-lzma --enable-bz2" + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_SOURCES: cyvcf2/tests pyproject.toml + CIBW_TEST_COMMAND: python -m pytest -v cyvcf2/tests + + - name: Verify the wheel ships the compiled extension and vendored licences + run: | + python3 - wheelhouse/*.whl <<'EOF' + import sys, zipfile + names = zipfile.ZipFile(sys.argv[1]).namelist() + sos = sorted(n for n in names if n.endswith(".so")) + print("\n".join(sos)) + assert any("cyvcf2/cyvcf2" in n for n in sos), sos + + lic = sorted(n.split("/")[-1] for n in names if ".dist-info/licenses/" in n and not n.endswith("/")) + print("\n".join(lic)) + assert {"LICENSE", "LICENSE.htslib", "LICENSE.htscodecs"} <= set(lic), lic + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: cyvcf2-${{ env.CYVCF2_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish cyvcf2 ${{ inputs.version || '0.34.0' }} + needs: [setup, build_wheels] + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + with: + artifact-pattern: cyvcf2-${{ inputs.version || '0.34.0' }}-*-manylinux_riscv64 diff --git a/patches/cyvcf2/0.34.0/0001-Add-htslib-htscodecs-LICENSE-files-at-project-root.patch b/patches/cyvcf2/0.34.0/0001-Add-htslib-htscodecs-LICENSE-files-at-project-root.patch new file mode 100644 index 0000000000..0435d1875a --- /dev/null +++ b/patches/cyvcf2/0.34.0/0001-Add-htslib-htscodecs-LICENSE-files-at-project-root.patch @@ -0,0 +1,150 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Fri, 11 Sep 2026 13:07:54 +0200 +Subject: [PATCH] Add htslib/htscodecs LICENSE files at project root + +cyvcf2 statically links htslib (and its bundled htscodecs submodule) into +the cyvcf2 extension by default on Linux (CYVCF2_HTSLIB_MODE=BUILTIN in +CMakeLists.txt), but pyproject.toml sets no [project] license-files list. +scikit-build-core's PEP 639 default license_files glob only covers +LICEN[CS]E*/COPYING*/NOTICE*/AUTHORS* at the project root, so neither +htslib's MIT/Expat-and-BSD LICENSE nor htscodecs' BSD LICENSE.md land in +dist-info/licenses/ even though their code ships in every wheel. + +Copy each at the root as LICENSE.htslib and LICENSE.htscodecs, which the +existing default glob already matches with no pyproject.toml change. + +Upstream-Status: To upstream [not submitted from this automated port run; needs a pull request against brentp/cyvcf2] +--- + LICENSE.htscodecs | 45 +++++++++++++++++++++++++++++++ + LICENSE.htslib | 69 +++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 114 insertions(+) + create mode 100644 LICENSE.htscodecs + create mode 100644 LICENSE.htslib + +diff --git a/LICENSE.htscodecs b/LICENSE.htscodecs +new file mode 100644 +index 0000000..14d3778 +--- /dev/null ++++ b/LICENSE.htscodecs +@@ -0,0 +1,45 @@ ++All files except those explicitly listed below are copyright Genome ++Research Limited and are made available under the BSD license. ++ ++> Redistribution and use in source and binary forms, with or without ++> modification, are permitted provided that the following conditions ++> are met: ++> ++> (1) Redistributions of source code must retain the above copyright ++> notice, this list of conditions and the following disclaimer. ++> ++> (2) Redistributions in binary form must reproduce the above copyright ++> notice, this list of conditions and the following disclaimer in ++> the documentation and/or other materials provided with the distribution. ++> ++> (3)The name of the author may not be used to endorse or promote ++> products derived from this software without specific prior written ++> permission. ++> ++> THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR ++> IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED ++> WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++> DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, ++> INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++> (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++> SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, ++> STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING ++> IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++> POSSIBILITY OF SUCH DAMAGE. ++ ++c_range_coder.h is Public Domain, derived from work by Eugene ++Shelwien. ++ ++rANS_byte.h and rANS_word.h are derived from Fabien Giesen's work and ++is Public Domain. https://github.com/rygorous/ryg_rans This work was ++in turn based on the ANS family of entropy encoders as described by ++Jarek Duda's paper: http://arxiv.org/abs/1311.2540 ++ ++> To the extent possible under law, Fabian Giesen has waived all ++> copyright and related or neighboring rights to ryg_rans, as ++> per the terms of the CC0 license: ++> ++> https://creativecommons.org/publicdomain/zero/1.0 ++> ++> This work is published from the United States. +diff --git a/LICENSE.htslib b/LICENSE.htslib +new file mode 100644 +index 0000000..925d47b +--- /dev/null ++++ b/LICENSE.htslib +@@ -0,0 +1,69 @@ ++[Files in this distribution outwith the cram/ subdirectory are distributed ++according to the terms of the following MIT/Expat license.] ++ ++The MIT/Expat License ++ ++Copyright (C) 2012-2023 Genome Research Ltd. ++ ++Permission is hereby granted, free of charge, to any person obtaining a copy ++of this software and associated documentation files (the "Software"), to deal ++in the Software without restriction, including without limitation the rights ++to use, copy, modify, merge, publish, distribute, sublicense, and/or sell ++copies of the Software, and to permit persons to whom the Software is ++furnished to do so, subject to the following conditions: ++ ++The above copyright notice and this permission notice shall be included in ++all copies or substantial portions of the Software. ++ ++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR ++IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, ++FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL ++THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER ++LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING ++FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER ++DEALINGS IN THE SOFTWARE. ++ ++ ++[Files within the cram/ subdirectory in this distribution are distributed ++according to the terms of the following Modified 3-Clause BSD license.] ++ ++The Modified-BSD License ++ ++Copyright (C) 2012-2023 Genome Research Ltd. ++ ++Redistribution and use in source and binary forms, with or without ++modification, are permitted provided that the following conditions are met: ++ ++1. Redistributions of source code must retain the above copyright notice, ++ this list of conditions and the following disclaimer. ++ ++2. Redistributions in binary form must reproduce the above copyright notice, ++ this list of conditions and the following disclaimer in the documentation ++ and/or other materials provided with the distribution. ++ ++3. Neither the names Genome Research Ltd and Wellcome Trust Sanger Institute ++ nor the names of its contributors may be used to endorse or promote products ++ derived from this software without specific prior written permission. ++ ++THIS SOFTWARE IS PROVIDED BY GENOME RESEARCH LTD AND CONTRIBUTORS "AS IS" ++AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE ++DISCLAIMED. IN NO EVENT SHALL GENOME RESEARCH LTD OR ITS CONTRIBUTORS BE LIABLE ++FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, ++OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ++OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ ++ ++[The use of a range of years within a copyright notice in this distribution ++should be interpreted as being equivalent to a list of years including the ++first and last year specified and all consecutive years between them. ++ ++For example, a copyright notice that reads "Copyright (C) 2005, 2007-2009, ++2011-2012" should be interpreted as being identical to a notice that reads ++"Copyright (C) 2005, 2007, 2008, 2009, 2011, 2012" and a copyright notice ++that reads "Copyright (C) 2005-2012" should be interpreted as being identical ++to a notice that reads "Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, ++2011, 2012".] From 30b8d93ea9f6ca15418d89d232e3ffa6ebed2995 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 11 Sep 2026 13:30:49 +0200 Subject: [PATCH 2/3] cyvcf2: stage the installed wheel into the test tree test_reader.py/test_writer.py use a package-relative `from ..cyvcf2 import ...` to reach the compiled extension, which needs cyvcf2/ itself to be a proper package (an __init__.py) for pytest to resolve two levels up from cyvcf2.tests. CIBW_TEST_SOURCES only staged cyvcf2/tests (no cyvcf2/__init__.py), so pytest treated the staged tests/ as its own top-level package and the relative import went past it: 'ImportError: attempted relative import beyond top-level package'. Merge the already-installed wheel into the staged cyvcf2/ directory before running pytest (same cp -a pattern as build-dulwich.yml), so the relative import resolves against the compiled wheel directly. --- .github/workflows/build-cyvcf2.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-cyvcf2.yml b/.github/workflows/build-cyvcf2.yml index 16eda2bd56..18c4c04f96 100644 --- a/.github/workflows/build-cyvcf2.yml +++ b/.github/workflows/build-cyvcf2.yml @@ -81,7 +81,14 @@ jobs: CYVCF2_HTSLIB_CONFIGURE_OPTIONS="--enable-lzma --enable-bz2" CIBW_TEST_REQUIRES: pytest CIBW_TEST_SOURCES: cyvcf2/tests pyproject.toml - CIBW_TEST_COMMAND: python -m pytest -v cyvcf2/tests + # test_reader.py/test_writer.py reach the compiled extension via a + # package-relative `from ..cyvcf2 import ...`, so cyvcf2/tests alone (no + # cyvcf2/__init__.py) isn't a deep enough package for that import to resolve. + # Merge the installed package (the compiled wheel) into the staged tree so the + # relative import lands on it directly, still exercising the compiled .so. + CIBW_TEST_COMMAND: >- + cp -a "$(python -c 'import cyvcf2, os; print(os.path.dirname(cyvcf2.__file__))')/." cyvcf2/ && + python -m pytest -v cyvcf2/tests - name: Verify the wheel ships the compiled extension and vendored licences run: | From 7f127f67933fc63aa06fffb59780c8e49c07430c Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 11 Sep 2026 15:17:19 +0200 Subject: [PATCH 3/3] cyvcf2: also stage cyvcf2.libs/ alongside the merged test tree The auditwheel-repaired extension's RPATH is $ORIGIN-relative to a sibling cyvcf2.libs/ (bundled libbz2/liblzma), which the previous fix didn't copy. --- .github/workflows/build-cyvcf2.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-cyvcf2.yml b/.github/workflows/build-cyvcf2.yml index 18c4c04f96..257bbf9c71 100644 --- a/.github/workflows/build-cyvcf2.yml +++ b/.github/workflows/build-cyvcf2.yml @@ -85,9 +85,13 @@ jobs: # package-relative `from ..cyvcf2 import ...`, so cyvcf2/tests alone (no # cyvcf2/__init__.py) isn't a deep enough package for that import to resolve. # Merge the installed package (the compiled wheel) into the staged tree so the - # relative import lands on it directly, still exercising the compiled .so. + # relative import lands on it directly, still exercising the compiled .so; its + # auditwheel-repaired RPATH is $ORIGIN-relative to a sibling cyvcf2.libs/ + # (bz2/lzma), so stage that alongside it too. CIBW_TEST_COMMAND: >- - cp -a "$(python -c 'import cyvcf2, os; print(os.path.dirname(cyvcf2.__file__))')/." cyvcf2/ && + site_pkg="$(python -c 'import cyvcf2, os; print(os.path.dirname(cyvcf2.__file__))')" && + cp -a "$site_pkg/." cyvcf2/ && + cp -a "$site_pkg/../cyvcf2.libs" . && python -m pytest -v cyvcf2/tests - name: Verify the wheel ships the compiled extension and vendored licences