From f918e40da700f12bc3a98696470c6d9c51f852f9 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 15 Oct 2026 14:39:01 +0000 Subject: [PATCH 1/2] finufft: add riscv64 wheel build scikit-build-core/CMake build of libfinufft.so, loaded via ctypes (hence the py3-none ABI tag despite being a genuine per-platform compiled artifact, confirmed from the published x86_64 wheel's contents). FFTW is fetched and built from source by CMake, no external riscv64-missing dependency. Overrides FINUFFT_ARCH_FLAGS to empty on riscv64 (upstream hardcodes -march=x86-64 in pyproject.toml for all platforms otherwise). Adds a licence patch: upstream declares no license-files, so the wheel ships neither its own LICENSE nor FFTW's (GPL-2.0-or-later, statically linked). --- .github/workflows/build-finufft.yml | 133 ++++++++++++++++++ docs/packages/finufft.yaml | 5 + .../finufft/2.5.1/0001-license-files.patch | 22 +++ 3 files changed, 160 insertions(+) create mode 100644 .github/workflows/build-finufft.yml create mode 100644 docs/packages/finufft.yaml create mode 100644 patches/finufft/2.5.1/0001-license-files.patch diff --git a/.github/workflows/build-finufft.yml b/.github/workflows/build-finufft.yml new file mode 100644 index 000000000000..2869821de805 --- /dev/null +++ b/.github/workflows/build-finufft.yml @@ -0,0 +1,133 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# Mirrors the Linux leg of upstream's own [tool.cibuildwheel] config in +# pyproject.toml: a scikit-build-core/CMake build of libfinufft.so (loaded via +# ctypes, hence the py3-none ABI tag despite being a genuine per-platform +# compiled build - confirmed by inspecting the published x86_64 wheel, which +# carries finufft/libfinufft.so plus FFTW static libs CMake fetches and builds +# from source). wheel.py-api = "py3" collapses the matrix to one interpreter. +name: Build finufft wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/finufft.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-finufft.yml' + - 'docs/packages/finufft.yaml' + push: + branches: [main] + paths: + - '.github/workflows/build-finufft.yml' + - 'docs/packages/finufft.yaml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: finufft + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build finufft ${{ matrix.version }} py3-none-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + + env: + FINUFFT_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout flatironinstitute/finufft v${{ env.FINUFFT_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: flatironinstitute/finufft + ref: v${{ env.FINUFFT_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch finufft source + run: git apply python-wheels/patches/finufft/${{ env.FINUFFT_VERSION }}/*.patch + + - uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + package-dir: python/finufft + output-dir: wheelhouse/ + env: + CIBW_ARCHS: riscv64 + CIBW_BUILD: cp312-manylinux_riscv64 + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + CIBW_CONFIG_SETTINGS: > + "cmake.define.FINUFFT_ARCH_FLAGS"="" + # Licence notices for the patch above: upstream's own LICENSE lives at the repo + # root, outside package-dir, and FFTW (statically linked, GPL-2.0-or-later) ships + # no licence text of its own. + CIBW_BEFORE_BUILD: >- + cp {project}/../../LICENSE {project}/LICENSE && + curl -fsSL --retry 5 https://raw.githubusercontent.com/FFTW/fftw3/master/COPYING -o {project}/LICENSE.fftw + CIBW_ENVIRONMENT: PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + CIBW_TEST_REQUIRES: pytest pytest-mock + CIBW_TEST_ENVIRONMENT: PIP_ONLY_BINARY=numpy + CIBW_TEST_COMMAND: pytest {project}/test + + - name: Check the compiled library and licences made it into the wheel + run: | + python3 - wheelhouse/*.whl <<'EOF' + import sys, zipfile + for whl in sys.argv[1:]: + names = zipfile.ZipFile(whl).namelist() + assert "finufft/libfinufft.so" in names, names + licences = {n.split(".dist-info/licenses/", 1)[1] for n in names + if ".dist-info/licenses/" in n and not n.endswith("/")} + assert licences == {"LICENSE", "LICENSE.fftw"}, licences + print(whl, "ok") + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: finufft-${{ env.FINUFFT_VERSION }}-py3-none-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish finufft ${{ matrix.version }} + needs: [setup, build_wheels] + if: needs.setup.outputs.versions != '[]' + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + secrets: + app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} + with: + artifact-pattern: finufft-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/finufft.yaml b/docs/packages/finufft.yaml new file mode 100644 index 000000000000..9d1b90e47f87 --- /dev/null +++ b/docs/packages/finufft.yaml @@ -0,0 +1,5 @@ +package-name: finufft +source-code: https://github.com/flatironinstitute/finufft +license: Apache-2.0 +versions: +- version: 2.5.1 diff --git a/patches/finufft/2.5.1/0001-license-files.patch b/patches/finufft/2.5.1/0001-license-files.patch new file mode 100644 index 000000000000..bff8b034bad6 --- /dev/null +++ b/patches/finufft/2.5.1/0001-license-files.patch @@ -0,0 +1,22 @@ +Upstream-Status: Inappropriate [licensing gap, not riscv64-specific, but we distribute the wheel so compliance is ours] + +Upstream declares no license/license-files, so scikit-build-core embeds no +LICENSE in the built wheel (confirmed on the published x86_64 wheel). The +build also statically links FFTW (GPL-2.0-or-later, fetched from source by +CMake) into libfinufft.so, so its licence text needs to ship too; a +CIBW_BEFORE_BUILD step stages it as LICENSE.fftw for the license-files glob +to pick up. + +diff --git a/python/finufft/pyproject.toml b/python/finufft/pyproject.toml +index 0000000..0000000 100644 +--- a/python/finufft/pyproject.toml ++++ b/python/finufft/pyproject.toml +@@ -22,6 +22,8 @@ authors = [ + {name = "Alex Barnett"}] + maintainers = [{name = "Alex Barnett", email = "abarnett@flatironinstitute.org"}] + description = "Python interface to FINUFFT" ++license = "Apache-2.0" ++license-files = ["LICENSE*"] + classifiers = [ + "License :: OSI Approved :: Apache Software License", + "Programming Language :: Python :: 3", From 6106f0cc52b0b5430189e55e4fb52cea1e1bf1f6 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Sun, 25 Oct 2026 18:03:48 +0000 Subject: [PATCH 2/2] finufft: fix cibuildwheel rejecting upstream's musllinux_1_1 alias Upstream's pyproject.toml pins tool.cibuildwheel.linux.musllinux-x86_64-image to the "musllinux_1_1" alias, which cibuildwheel 4.x no longer resolves. cibuildwheel validates that value at config-load time regardless of the selected CIBW_ARCHS/CIBW_BUILD, so the riscv64-only, manylinux-only build failed before even starting. Override it to a supported alias. --- .github/workflows/build-finufft.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/build-finufft.yml b/.github/workflows/build-finufft.yml index 2869821de805..22cde212e55b 100644 --- a/.github/workflows/build-finufft.yml +++ b/.github/workflows/build-finufft.yml @@ -83,6 +83,10 @@ jobs: CIBW_ARCHS: riscv64 CIBW_BUILD: cp312-manylinux_riscv64 CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + # Upstream's own pyproject.toml pins musllinux-x86_64-image to the + # "musllinux_1_1" alias, which cibuildwheel 4.x rejects at config-load + # time for any build, even this riscv64-only, musllinux-less one. + CIBW_MUSLLINUX_X86_64_IMAGE: musllinux_1_2 CIBW_CONFIG_SETTINGS: > "cmake.define.FINUFFT_ARCH_FLAGS"="" # Licence notices for the patch above: upstream's own LICENSE lives at the repo