From 008aadc5908795e2ddc72e879d7fe984d4887a0e Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 7 Sep 2026 03:50:26 +0200 Subject: [PATCH 1/2] ta-lib: add build-ta-lib.yml for riscv64 wheels --- .github/workflows/build-ta-lib.yml | 129 ++++++++++++++++++ ...-cover-the-vendored-TA-Lib-C-license.patch | 41 ++++++ 2 files changed, 170 insertions(+) create mode 100644 .github/workflows/build-ta-lib.yml create mode 100644 patches/ta-lib/0.7.1/0001-Widen-license-files-to-cover-the-vendored-TA-Lib-C-license.patch diff --git a/.github/workflows/build-ta-lib.yml b/.github/workflows/build-ta-lib.yml new file mode 100644 index 000000000..b038a17b3 --- /dev/null +++ b/.github/workflows/build-ta-lib.yml @@ -0,0 +1,129 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on: https://github.com/ta-lib/ta-lib-python/blob/v0.7.1/.github/workflows/wheels.yml +name: Build ta-lib wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'ta-lib version to build (git tag without leading v, e.g. 0.7.1)' + required: true + default: '0.7.1' + pull_request: + paths: + - '.github/workflows/build-ta-lib.yml' + - 'patches/ta-lib/**' + +concurrency: + group: ${{ github.workflow }}-${{ inputs.version || '0.7.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 0.7.1 there. + TA_LIB_VERSION: ${{ inputs.version || '0.7.1' }} + # The TA-Lib C library version fetched by tools/build_talib_linux.sh; a + # separate pin from TA_LIB_VERSION, mirroring upstream's own workflow. + TALIB_C_VER: '0.7.1' + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + + build_wheels: + needs: [setup] + name: Build ta-lib ${{ inputs.version || '0.7.1' }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + # Upstream's own CIBW_SKIP excludes cp313t/cp314t; the extension + # isn't declared free-threading safe. + python: ["cp312", "cp313", "cp314"] + + steps: + - name: Checkout ta-lib-python v${{ env.TA_LIB_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: ta-lib/ta-lib-python + ref: v${{ env.TA_LIB_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch ta-lib-python source + run: git apply python-wheels/patches/ta-lib/${{ env.TA_LIB_VERSION }}/*.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_ENVIRONMENT_PASS_LINUX: TALIB_C_VER CFLAGS + CFLAGS: -D_GNU_SOURCE + CIBW_BEFORE_ALL_LINUX: | + curl -fsSL -o talib-$TALIB_C_VER.tar.gz https://github.com/TA-Lib/ta-lib/archive/refs/tags/v$TALIB_C_VER.tar.gz + mkdir -p ta-lib-$TALIB_C_VER + tar xzf talib-$TALIB_C_VER.tar.gz --strip-components=1 -C ta-lib-$TALIB_C_VER + cd ta-lib-$TALIB_C_VER + mkdir -p include/ta-lib/ + cp include/*.h include/ta-lib/ + cmake -S . -B _build -D CMAKE_BUILD_TYPE=Release -D CMAKE_INSTALL_PREFIX=/project/ta-lib-install + cmake --build _build -j"$(nproc)" + cmake --install _build + cp LICENSE /project/LICENSE.ta-lib-c + CIBW_ENVIRONMENT_LINUX: >- + TA_LIBRARY_PATH=/project/ta-lib-install/lib + TA_INCLUDE_PATH=/project/ta-lib-install/include + PIP_NO_BUILD_ISOLATION=false + PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + PIP_ONLY_BINARY=numpy + CIBW_REPAIR_WHEEL_COMMAND_LINUX: | + LD_LIBRARY_PATH="/project/ta-lib-install/lib:$LD_LIBRARY_PATH" auditwheel repair -w {dest_dir} {wheel} + CIBW_BUILD_FRONTEND: build + CIBW_TEST_REQUIRES: pytest pandas + # test_polars.py imports polars unconditionally; no riscv64 polars + # wheel exists on PyPI or our registry to install it with. + CIBW_TEST_COMMAND: >- + cd .. && + pytest -k "not RSI and not threading" --ignore={project}/tests/test_polars.py {project}/tests + + - name: Check the wheel ships the extension and both licences + run: | + python3 - wheelhouse/*.whl <<'EOF' + import sys, zipfile + for whl in sys.argv[1:]: + names = zipfile.ZipFile(whl).namelist() + assert any(n.startswith("talib/") and n.endswith(".so") for n in names), whl + licences = {n.rsplit("/", 1)[1] for n in names if ".dist-info/licenses/" in n} - {""} + assert licences == {"LICENSE", "LICENSE.ta-lib-c"}, (whl, licences) + print(whl, "ok") + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: ta-lib-${{ env.TA_LIB_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: ./wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish ta-lib ${{ inputs.version || '0.7.1' }} + needs: [setup, build_wheels] + permissions: + contents: write + pull-requests: write + uses: $/.github/workflows/_publish-wheel.yml + with: + artifact-pattern: ta-lib-${{ inputs.version || '0.7.1' }}-*-manylinux_riscv64 diff --git a/patches/ta-lib/0.7.1/0001-Widen-license-files-to-cover-the-vendored-TA-Lib-C-license.patch b/patches/ta-lib/0.7.1/0001-Widen-license-files-to-cover-the-vendored-TA-Lib-C-license.patch new file mode 100644 index 000000000..464728516 --- /dev/null +++ b/patches/ta-lib/0.7.1/0001-Widen-license-files-to-cover-the-vendored-TA-Lib-C-license.patch @@ -0,0 +1,41 @@ +From a041ab3466109942a11a2e857d85eda50e57b45b Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Mon, 7 Sep 2026 03:46:48 +0200 +Subject: [PATCH] Widen license-files to cover the vendored TA-Lib C licence + +license-files = ["LICENSE"] is an explicit PEP 639 list, so it replaces +setuptools' default LICEN[CS]E* glob rather than adding to it. The Linux, +macOS and Windows wheels dynamically link libta-lib, built from the +TA-Lib C library release that tools/build_talib_linux.sh (and its macOS/ +Windows equivalents) downloads at build time from TA-Lib/ta-lib, which is +licensed under the 3-clause BSD licence whose clause 2 requires the +copyright notice to be reproduced in binary redistributions -- but the +built wheel ships only ta-lib-python's own LICENSE. + +Widening the list to also match LICENSE.* lets a LICENSE.ta-lib-c dropped +at the project root reach dist-info/licenses/ with no other packaging +change. + +Upstream-Status: To upstream [not yet submitted; the same gap exists in every ta-lib wheel on PyPI, so this needs a maintainer discussion rather than a drive-by PR] + +Signed-off-by: Ludovic Henry +--- + pyproject.toml | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/pyproject.toml b/pyproject.toml +index dce4d7d..f609395 100644 +--- a/pyproject.toml ++++ b/pyproject.toml +@@ -7,7 +7,7 @@ name = "TA-Lib" + version = "0.7.1" + description = "Python wrapper for TA-Lib" + readme = "README.md" +-license-files = ["LICENSE"] ++license-files = ["LICENSE", "LICENSE.*"] + authors = [ + {name = "John Benediktsson", email = "mrjbq7@gmail.com"} + ] +-- +2.50.1 (Apple Git-155) + From a89e0b6dec1f80821a9ed3343c70e52b283f3a39 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 7 Sep 2026 04:12:00 +0200 Subject: [PATCH 2/2] build-ta-lib: pin pandas to wheel-only resolution pandas 3.0.5 is on PyPI without a riscv64 wheel yet, so unconstrained pip resolution picks its sdist and fails to build it (no meson-python in the test virtualenv). PIP_ONLY_BINARY=numpy,pandas keeps resolution on the newest version that has a riscv64 wheel on our registry. --- .github/workflows/build-ta-lib.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build-ta-lib.yml b/.github/workflows/build-ta-lib.yml index b038a17b3..d5962f640 100644 --- a/.github/workflows/build-ta-lib.yml +++ b/.github/workflows/build-ta-lib.yml @@ -89,7 +89,7 @@ jobs: TA_INCLUDE_PATH=/project/ta-lib-install/include PIP_NO_BUILD_ISOLATION=false PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ - PIP_ONLY_BINARY=numpy + PIP_ONLY_BINARY=numpy,pandas CIBW_REPAIR_WHEEL_COMMAND_LINUX: | LD_LIBRARY_PATH="/project/ta-lib-install/lib:$LD_LIBRARY_PATH" auditwheel repair -w {dest_dir} {wheel} CIBW_BUILD_FRONTEND: build