Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 174 additions & 0 deletions .github/workflows/build-jxlpy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# This workflow is based on the `wheels` job of
# https://github.com/olokelo/jxlpy/blob/0.9.5/.github/workflows/packaging.yml
name: Build jxlpy wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'Version glob to (re)build; empty builds every version of docs/packages/jxlpy.yaml not released yet'
required: false
default: ''
pull_request:
branches: [main]
paths:
- '.github/workflows/build-jxlpy.yml'
- 'docs/packages/jxlpy.yaml'
push:
branches: [main]
paths:
- '.github/workflows/build-jxlpy.yml'
- 'docs/packages/jxlpy.yaml'

concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

permissions:
contents: read # to fetch code (actions/checkout)

env:
# Tag that upstream's own packaging.yml pins at the jxlpy 0.9.5 release.
LIBJXL_TAG: v0.10.1
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

jobs:
setup:
uses: $/.github/workflows/_setup.yml
with:
package: jxlpy
version: ${{ inputs.version }}

build_wheels:
needs: [setup]
if: needs.setup.outputs.versions != '[]'
name: Build jxlpy ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
python: ["cp310", "cp311", "cp312", "cp313", "cp314"]

env:
JXLPY_VERSION: ${{ matrix.version }}

steps:
- name: Checkout jxlpy v${{ env.JXLPY_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: olokelo/jxlpy
ref: ${{ env.JXLPY_VERSION }}
persist-credentials: false

- 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: LIBJXL_TAG
# setup.py's extra_link_args is a bare `-ljxl -ljxl_threads` with no
# include_dirs/library_dirs, same as upstream's own CIBW_BEFORE_ALL: build
# libjxl from source and install it to the default /usr/local prefix.
# No `-march=`/SIMD flag is passed anywhere in this configure line, so
# highway (vendored, statically linked into libjxl.so) collapses to its
# portable EMU128/SCALAR baseline on riscv64 - same reasoning as
# pillow-jxl-plugin's CIBW_BEFORE_ALL_LINUX comment (gotchas 139/272/279/448).
# -DJPEGXL_ENABLE_EXAMPLES=OFF: the default-on examples/encode_oneshot
# aren't needed to build or install the libjxl.so/libjxl_threads.so
# that setup.py links against.
#
# `git clone --recursive` populates the submodules (incl.
# third_party/skcms) at whatever commit libjxl's *default* branch
# pins them to; `git checkout "$LIBJXL_TAG"` only moves the
# superproject tree - it does not re-sync already-checked-out
# submodules to what that tag pins them to (plain `git checkout`
# never does, with or without --recursive at clone time). Without
# the `git submodule update` below, the build links libjxl_cms.so
# against a skcms commit newer than v0.10.1 actually vendors, which
# restructured its internal namespacing - that produced an
# undefined symbol (skcms_private::baseline::run_program) at import
# time on riscv64 specifically because riscv64 takes skcms's
# generic/portable SIMD-less code path, the one path whose exact
# linkage shape changed between those two skcms commits.
CIBW_BEFORE_ALL_LINUX: >-
git clone --recursive --shallow-submodules https://github.com/libjxl/libjxl.git /tmp/libjxl &&
cd /tmp/libjxl &&
git checkout "$LIBJXL_TAG" &&
git submodule update --init --recursive &&
mkdir build && cd build &&
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_TESTING=OFF
-DJPEGXL_ENABLE_BENCHMARK=OFF -DJPEGXL_ENABLE_TOOLS=OFF -DJPEGXL_ENABLE_DEVTOOLS=OFF
-DJPEGXL_ENABLE_EXAMPLES=OFF .. &&
cmake --build . -- -j$(nproc) &&
cmake --install . &&
cp ./third_party/brotli/*.so* /usr/local/lib &&
ldconfig &&
cat /tmp/libjxl/LICENSE /tmp/libjxl/PATENTS > {package}/LICENSE.libjxl &&
cp /tmp/libjxl/third_party/highway/LICENSE {package}/LICENSE.highway &&
cp /tmp/libjxl/third_party/brotli/LICENSE {package}/LICENSE.brotli &&
cp /tmp/libjxl/third_party/skcms/LICENSE {package}/LICENSE.skcms
CIBW_ENVIRONMENT: LIBRARY_PATH=/usr/local/lib LD_LIBRARY_PATH=/usr/local/lib
# No formal test suite upstream, only examples/*.py; exercise the same
# encode -> decode round trip as examples/encode_simple.py +
# examples/decode_simple.py through the installed wheel.
CIBW_TEST_COMMAND: >-
python -c "import jxlpy;
print('jxlpy', jxlpy.__version__, 'libjxl', jxlpy._jxl_version);
size = (64, 64);
data = bytes([0, 188, 212] * (size[0] * size[1]));
enc = jxlpy.JXLPyEncoder(quality=100, colorspace='RGB', size=size, effort=7);
enc.add_frame(data);
payload = enc.get_output();
assert len(payload) > 0, 'empty encoder output';
dec = jxlpy.JXLPyDecoder(payload);
info = dec.get_info();
assert (info['xsize'], info['ysize']) == size, info;
assert dec.get_colorspace() == 'RGB', dec.get_colorspace();
frame = dec.get_frame();
assert frame is not None and list(frame[:3]) == [0, 188, 212], frame;
print('jxlpy OK')
"

- 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("_jxlpy") and n.endswith(".so") for n in names), whl
licenses = sorted(n.rsplit("/", 1)[1] for n in names if ".dist-info/licenses/" in n)
print(whl, "licenses:", licenses)
for expect in ("LICENSE", "LICENSE.libjxl", "LICENSE.highway", "LICENSE.brotli", "LICENSE.skcms"):
assert expect in licenses, (whl, expect, licenses)
print(whl, "ok")
EOF

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: jxlpy-${{ env.JXLPY_VERSION }}-${{ matrix.python }}-manylinux_riscv64
path: wheelhouse/*.whl
if-no-files-found: error

publish:
name: Publish jxlpy ${{ 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: jxlpy-${{ matrix.version }}-*-manylinux_riscv64
5 changes: 5 additions & 0 deletions docs/packages/jxlpy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package-name: jxlpy
source-code: https://github.com/olokelo/jxlpy
license: MIT
versions:
- version: 0.9.5
Loading