diff --git a/.github/workflows/build-pyjpegls.yml b/.github/workflows/build-pyjpegls.yml new file mode 100644 index 000000000000..370461d43d1e --- /dev/null +++ b/.github/workflows/build-pyjpegls.yml @@ -0,0 +1,131 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on upstream's own cibuildwheel-driven +# https://github.com/pydicom/pyjpegls/blob/v1.5.1/.github/workflows/release-wheels.yml, +# narrowed to Linux riscv64, with the tests of its pr-tests.yml. +name: Build pyjpegls wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/pyjpegls.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-pyjpegls.yml' + - 'docs/packages/pyjpegls.yaml' + push: + branches: [main] + paths: + - '.github/workflows/build-pyjpegls.yml' + - 'docs/packages/pyjpegls.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: pyjpegls + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build pyjpegls ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 60 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + # Mirrors upstream's own pr-tests.yml/release-wheels.yml matrix + # (3.9-3.13). Upstream ships no cp314 or cp314t wheel, has no + # free-threaded CI env, and the Cython extension declares no + # Py_mod_gil slot, so both are left out rather than shipped + # untested (gotcha 33/127). + python: ["cp39", "cp310", "cp311", "cp312", "cp313"] + + env: + PYJPEGLS_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout pyjpegls v${{ env.PYJPEGLS_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: pydicom/pyjpegls + ref: v${{ env.PYJPEGLS_VERSION }} + submodules: true + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + # The extension compiles in the vendored CharLS (BSD-3-Clause) but the + # wheel ships no licence at all, for either project. + - name: Patch pyjpegls source + run: | + git apply python-wheels/patches/pyjpegls/${{ env.PYJPEGLS_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_ENVIRONMENT: >- + PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + PIP_ONLY_BINARY=numpy + CIBW_TEST_REQUIRES: pytest + CIBW_TEST_COMMAND: pytest --pyargs jpeg_ls.tests + + - name: Check the wheel contents + run: | + python3 - wheelhouse/*.whl <<'EOF' + import sys, zipfile + for whl in sys.argv[1:]: + names = zipfile.ZipFile(whl).namelist() + assert any(n.startswith("_CharLS") and n.endswith(".so") for n in names), whl + for lic in ("LICENSE", "LICENSE.charls"): + assert any(n.endswith(".dist-info/licenses/" + lic) for n in names), (whl, lic) + print(whl, "ok") + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pyjpegls-${{ env.PYJPEGLS_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish pyjpegls ${{ 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: pyjpegls-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/pyjpegls.yaml b/docs/packages/pyjpegls.yaml new file mode 100644 index 000000000000..1d62c003f37a --- /dev/null +++ b/docs/packages/pyjpegls.yaml @@ -0,0 +1,5 @@ +package-name: pyjpegls +source-code: https://github.com/pydicom/pyjpegls +license: MIT +versions: +- version: 1.5.1 diff --git a/patches/pyjpegls/1.5.1/0001-package-the-CharLS-and-pyjpegls-licences-in-the-built-wheel.patch b/patches/pyjpegls/1.5.1/0001-package-the-CharLS-and-pyjpegls-licences-in-the-built-wheel.patch new file mode 100644 index 000000000000..d886dbdafb4d --- /dev/null +++ b/patches/pyjpegls/1.5.1/0001-package-the-CharLS-and-pyjpegls-licences-in-the-built-wheel.patch @@ -0,0 +1,98 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Tue, 13 Oct 2026 12:00:00 +0200 +Subject: [PATCH] package the CharLS and pyjpegls licences in the built wheel + +Upstream-Status: To upstream [not yet submitted; every pyjpegls wheel on PyPI has the same gap, so it needs a maintainer discussion rather than a drive-by PR] + +setup.py compiles lib/charls/src/*.cpp (CharLS, vendored by the +team-charls/charls submodule pinned at v2.4.2) straight into the +_CharLS extension, so every wheel redistributes CharLS in binary form. +Its BSD-3-Clause licence requires the copyright notice to travel with +the binary, but the repository's only licence text is license.txt +(lowercase), which carries pyjpegls's own MIT text and nothing for +CharLS. + +Worse, setuptools' default license-files auto-discovery glob +(LICENSE*/LICENCE*/COPYING*/NOTICE*/AUTHORS*) is case-sensitive on +Linux and never matches license.txt, so the built wheel ships no +licence at all, for either project. + +Add LICENSE as an uppercase copy of the existing license.txt (MIT, +verbatim) and LICENSE.charls with the vendored copy's licence text +(BSD-3-Clause, verbatim from team-charls/charls's LICENSE.md) at the +project root: setuptools' default glob then picks up both and copies +them into the wheel's dist-info/licenses/, so no pyproject.toml change +is needed. +--- + LICENSE | 23 +++++++++++++++++++++++ + LICENSE.charls | 28 ++++++++++++++++++++++++++++ + 2 files changed, 51 insertions(+) + create mode 100644 LICENSE + create mode 100644 LICENSE.charls + +diff --git a/LICENSE b/LICENSE +new file mode 100644 +index 0000000..03de66a +--- /dev/null ++++ b/LICENSE +@@ -0,0 +1,23 @@ ++ ++pyjpegls - JPEG-LS for Python via CharLS C++ Library ++ ++The MIT License (MIT) ++ ++Copyright (c) 2013 Pierre V. Villeneuve, pierre.villeneuve@gmail.com ++ ++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. +diff --git a/LICENSE.charls b/LICENSE.charls +new file mode 100644 +index 0000000..bbd8e6c +--- /dev/null ++++ b/LICENSE.charls +@@ -0,0 +1,28 @@ ++CharLS ++Copyright (c) 2007, Jan de Vaan and Victor Derks ++All rights reserved. ++ ++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 name of the copyright holder 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 THE COPYRIGHT HOLDERS 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 THE COPYRIGHT HOLDER OR 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. +-- +2.43.0