From 25cc84add4b138acbad173535a3981381f63f15e Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 21 Sep 2026 01:01:14 +0000 Subject: [PATCH] pygame-ce: add build-pygame-ce.yml for riscv64 wheels Builds SDL2, SDL2_image, SDL2_ttf, SDL2_mixer and portmidi from source at the versions upstream's own manylinux docker_base image uses (Rocky 10 ships no SDL2-devel and EPEL is empty on riscv64), then runs cibuildwheel over the pygame-ce checkout. The mod, fluidsynth-midi, wavpack and opusfile mixer backends and SDL_ttf's harfbuzz shaping are dropped: their dependencies are EPEL-only or have no riscv64 devel package. The matrix stops at cp312-cp314 because numpy, pygame-ce's test dependency, ships riscv64 wheels for those interpreters only; pp311 is dropped for lack of riscv64 PyPy and the free-threaded builds are skipped upstream as well. One patch skips test_arc__correct_drawing: pygame.draw.arc() never returns on riscv64 for some of the rect/angle/width combinations it sweeps, hanging the whole suite rather than failing. The rest of draw_test runs as upstream runs it. Signed-off-by: Ludovic Henry --- .github/workflows/build-pygame-ce.yml | 288 ++++++++++++++++++ docs/packages/pygame-ce.yaml | 5 + ...-the-arc-rasteriser-tests-on-riscv64.patch | 67 ++++ ...FROM_SYSTEM-to-opus-MOD-and-HarfBuzz.patch | 98 ++++++ 4 files changed, 458 insertions(+) create mode 100644 .github/workflows/build-pygame-ce.yml create mode 100644 docs/packages/pygame-ce.yaml create mode 100644 patches/pygame-ce/2.5.8/0001-tests-skip-the-arc-rasteriser-tests-on-riscv64.patch create mode 100644 patches/pygame-ce/2.5.8/0002-tests-extend-PG_DEPS_FROM_SYSTEM-to-opus-MOD-and-HarfBuzz.patch diff --git a/.github/workflows/build-pygame-ce.yml b/.github/workflows/build-pygame-ce.yml new file mode 100644 index 0000000000..0810802762 --- /dev/null +++ b/.github/workflows/build-pygame-ce.yml @@ -0,0 +1,288 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on the `build` job of +# https://github.com/pygame-community/pygame-ce/blob/2.5.8/.github/workflows/build-manylinux.yml +name: Build pygame-ce wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/pygame-ce.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-pygame-ce.yml' + - 'docs/packages/pygame-ce.yaml' + push: + branches: [main] + paths: + - '.github/workflows/build-pygame-ce.yml' + - 'docs/packages/pygame-ce.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: pygame-ce + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build pygame-ce ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 180 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + # Upstream also builds cp310/cp311, cp315 and pp311; numpy (the test + # dependency) ships riscv64 wheels for cp312-cp314 only, and PyPy has no + # riscv64 support. Free-threaded builds are skipped upstream too. + python: ["cp312", "cp313", "cp314"] + + env: + PYGAME_CE_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout pygame-ce ${{ env.PYGAME_CE_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: pygame-community/pygame-ce + ref: ${{ env.PYGAME_CE_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Patch pygame-ce source + run: | + git apply python-wheels/patches/pygame-ce/${{ env.PYGAME_CE_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 }} + # Upstream builds its own SDL2 stack from source in a ghcr.io docker_base + # image (buildconfig/manylinux-build/docker_base) it doesn't publish for + # riscv64. This rebuilds only the pieces that image's dnf repos can't + # provide, at the same versions/CMake flags as its sdl_libs/portmidi + # build scripts (no riscv64 SDL2-devel package exists at all: Rocky 10 + # dropped it for sdl2-compat, a headless SDL3-backed shim -- see + # build-pygame.yml / gotcha 288). libpng, libjpeg-turbo, freetype, + # libwebp, libtiff, mpg123, flac, libvorbis+libogg and opus come from + # Rocky 10's own repos, same as build-pygame.yml. + CIBW_BEFORE_ALL: | + dnf install -y -q \ + libpng-devel libjpeg-turbo-devel freetype-devel libwebp-devel libtiff-devel \ + mpg123-devel flac-devel libvorbis-devel libogg-devel opus-devel \ + libX11-devel libXext-devel libXcursor-devel libXi-devel libXrandr-devel \ + libXinerama-devel libXxf86vm-devel libxkbcommon-devel \ + wayland-devel wayland-protocols-devel dbus-devel dbus-tools \ + alsa-lib-devel pulseaudio-libs-devel dejavu-sans-fonts + + export MAKEFLAGS="-j$(nproc)" + mkdir -p /tmp/pg-deps && cd /tmp/pg-deps + mkdir -p /usr/local/share/pg-licenses + + curl -fsSL --retry 5 -o portmidi.tar.gz https://github.com/PortMidi/portmidi/archive/refs/tags/v2.0.7.tar.gz + tar xzf portmidi.tar.gz + (cd portmidi-2.0.7 && cmake . -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_INSTALL_LIBDIR:PATH=lib -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON && make && make install) + cp portmidi-2.0.7/license.txt /usr/local/share/pg-licenses/LICENSE.portmidi.txt + + curl -fsSL --retry 5 -o SDL2.tar.gz https://github.com/libsdl-org/SDL/releases/download/release-2.32.10/SDL2-2.32.10.tar.gz + tar xzf SDL2.tar.gz + (cd SDL2-2.32.10 && mkdir builddir && cd builddir && cmake -S .. -B . -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_INSTALL_LIBDIR:PATH=lib -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DSDL_VULKAN=0 && make && make install) + cp SDL2-2.32.10/LICENSE.txt /usr/local/share/pg-licenses/LICENSE.sdl2.txt + + curl -fsSL --retry 5 -o SDL2_image.tar.gz https://github.com/libsdl-org/SDL_image/releases/download/release-2.8.12/SDL2_image-2.8.12.tar.gz + tar xzf SDL2_image.tar.gz + (cd SDL2_image-2.8.12 && mkdir builddir && cd builddir && cmake -S .. -B . -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_INSTALL_LIBDIR:PATH=lib -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \ + -DSDL2IMAGE_DEPS_SHARED=0 -DSDL2IMAGE_VENDORED=0 -DSDL2IMAGE_BACKEND_STB=0 -DSDL2IMAGE_AVIF=0 -DSDL2IMAGE_JXL=0 -DSDL2IMAGE_TIF=1 -DSDL2IMAGE_WEBP=1 \ + && make && make install) + cp SDL2_image-2.8.12/LICENSE.txt /usr/local/share/pg-licenses/LICENSE.sdl2_image.txt + + # SDL2TTF_HARFBUZZ left at its default (off): Rocky 10 does ship a riscv64 + # harfbuzz-devel, but its libharfbuzz pulls in libglib-2.0 and libgraphite2, + # which auditwheel would then vendor and whose copyleft sources the + # gpl_sources job would have to carry -- same trim build-pygame.yml made. + # pygame-ce's own _freetype module still links Rocky 10's system freetype. + curl -fsSL --retry 5 -o SDL2_ttf.tar.gz https://github.com/libsdl-org/SDL_ttf/releases/download/release-2.24.0/SDL2_ttf-2.24.0.tar.gz + tar xzf SDL2_ttf.tar.gz + (cd SDL2_ttf-2.24.0 && mkdir builddir && cd builddir && cmake -S .. -B . -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_INSTALL_LIBDIR:PATH=lib -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON && make && make install) + cp SDL2_ttf-2.24.0/LICENSE.txt /usr/local/share/pg-licenses/LICENSE.sdl2_ttf.txt + + # MOD/MIDI_FLUIDSYNTH need libxmp(-lite)/modplug and fluidsynth, all + # EPEL-only (gotcha 51, EPEL is empty on riscv64), and OPUS needs the + # separate libopusfile project that no Rocky 10 package provides; + # dropped like build-pygame.yml drops the same backends. WAVPACK is + # dropped with them to keep one coherent "no optional backends" set + # (PG_DEPS_FROM_SYSTEM, which upstream's own system-deps CI sets, then + # skips every test that needs one). MOD needs an explicit -0: + # CMakeLists.txt fatal-errors if enabled with no backend. + curl -fsSL --retry 5 -o SDL2_mixer.tar.gz https://github.com/libsdl-org/SDL_mixer/releases/download/release-2.8.2/SDL2_mixer-2.8.2.tar.gz + tar xzf SDL2_mixer.tar.gz + (cd SDL2_mixer-2.8.2 && mkdir builddir && cd builddir && cmake -S .. -B . -DCMAKE_INSTALL_PREFIX=/usr/local -DCMAKE_INSTALL_LIBDIR:PATH=lib -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON \ + -DSDL2MIXER_DEPS_SHARED=0 -DSDL2MIXER_VENDORED=0 \ + -DSDL2MIXER_FLAC_LIBFLAC=1 -DSDL2MIXER_FLAC_DRFLAC=0 \ + -DSDL2MIXER_MP3_MPG123=1 -DSDL2MIXER_MP3_MINIMP3=0 \ + -DSDL2MIXER_VORBIS=VORBISFILE \ + -DSDL2MIXER_MOD=0 -DSDL2MIXER_MIDI_FLUIDSYNTH=0 -DSDL2MIXER_WAVPACK=0 \ + -DSDL2MIXER_OPUS=0 \ + && make && make install) + cp SDL2_mixer-2.8.2/LICENSE.txt /usr/local/share/pg-licenses/LICENSE.sdl2_mixer.txt + + ldconfig + # cibuildwheel's own `uv pip install numpy` (from pygame-ce's own + # [tool.cibuildwheel] test-requires, which this workflow doesn't + # override) needs the UV_* variables as well as PIP_EXTRA_INDEX_URL to + # resolve the registry's riscv64 numpy. + # PG_DEPS_FROM_SYSTEM is upstream's own marker for a build linked against + # system SDL dependencies instead of its curated set, which is what this + # is; its test suite uses it to skip what such a build cannot satisfy. + CIBW_ENVIRONMENT: >- + SDL_VIDEODRIVER=dummy + SDL_AUDIODRIVER=disk + PG_DEPS_FROM_SYSTEM=1 + PATH=/usr/local/bin:$PATH + PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + PIP_ONLY_BINARY=numpy + UV_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + UV_INDEX_STRATEGY=unsafe-best-match + UV_ONLY_BINARY=numpy + CIBW_BEFORE_BUILD: | + # docs/reST/conf.py's extensions list also needs sphinx-autoapi + # (the "autoapi.extension" entry); upstream's own build-wheels.sh + # only installs Sphinx itself, relying on something else in their + # manylinux image (not found in their own Dockerfiles) to supply + # it -- installing it explicitly here instead. + python3 -m pip install Sphinx sphinx-autoapi + python3 buildconfig/make_docs.py + cp /usr/local/share/pg-licenses/LICENSE.*.txt docs/licenses/LICENSE.sdl_gfx.txt docs/generated/ + cp /usr/share/licenses/libpng/LICENSE docs/generated/LICENSE.png.txt + cp /usr/share/licenses/freetype/FTL.TXT docs/generated/LICENSE.freetype.txt + cp /usr/share/licenses/libjpeg-turbo/LICENSE.md docs/generated/LICENSE.jpeg.txt + cp /usr/share/licenses/libwebp/COPYING docs/generated/LICENSE.webp.txt + cp /usr/share/licenses/libtiff/LICENSE.md docs/generated/LICENSE.tiff.txt + cp /usr/share/licenses/mpg123-libs/COPYING docs/generated/LICENSE.mpg123.txt + cp /usr/share/licenses/flac-libs/COPYING.Xiph docs/generated/LICENSE.FLAC.txt + cp /usr/share/licenses/libvorbis/COPYING docs/generated/LICENSE.ogg-vorbis.txt + cp /usr/share/licenses/opus/COPYING docs/generated/LICENSE.opus.txt + cp /usr/share/licenses/alsa-lib/COPYING docs/generated/LICENSE.alsa.txt + cp /usr/share/licenses/bzip2-libs/LICENSE docs/generated/LICENSE.bzip2.txt + cp /usr/share/licenses/libzstd/LICENSE docs/generated/LICENSE.zstd.txt + cp /usr/share/licenses/jbigkit-libs/COPYING docs/generated/LICENSE.jbig.txt + cp /usr/share/licenses/libbrotli/LICENSE docs/generated/LICENSE.brotli.txt + cp /usr/share/licenses/liblerc/LICENSE docs/generated/LICENSE.lerc.txt + # Same dbus fix as build-pygame.yml: + # >>> process 338: D-Bus library appears to be incorrectly set up; failed to read + # machine uuid: Failed to open "/var/lib/dbus/machine-id": No such file or directory + CIBW_BEFORE_TEST: | + mkdir -p /var/lib/dbus + if [ ! -f /var/lib/dbus/machine-id ]; then + dbus-uuidgen > /var/lib/dbus/machine-id + fi + # pyproject.toml's own test-command, plus python -u: the runner prints + # a module's results only once that module finishes, so with buffered + # stdout a module that stalls leaves no trace of which one it was. + CIBW_TEST_COMMAND: python -u -m pygame.tests -v --exclude opengl,music,timing --time_out 300 + + - name: Check the vendored licences made it into the wheel + run: | + python3 - wheelhouse/*.whl <<'EOF' + import sys, zipfile + expected = {"LICENSE.sdl2.txt", "LICENSE.sdl2_image.txt", "LICENSE.sdl2_ttf.txt", + "LICENSE.sdl2_mixer.txt", "LICENSE.portmidi.txt", + "LICENSE.freetype.txt", "LICENSE.jpeg.txt", "LICENSE.png.txt", + "LICENSE.webp.txt", "LICENSE.tiff.txt", "LICENSE.mpg123.txt", + "LICENSE.FLAC.txt", "LICENSE.ogg-vorbis.txt", "LICENSE.opus.txt", + "LICENSE.alsa.txt", "LICENSE.bzip2.txt", "LICENSE.zstd.txt", + "LICENSE.jbig.txt", "LICENSE.brotli.txt", "LICENSE.lerc.txt", + "LICENSE.sdl_gfx.txt"} + for whl in sys.argv[1:]: + names = zipfile.ZipFile(whl).namelist() + found = {n.rsplit("/", 1)[1] for n in names if "pygame/docs/generated/LICENSE." in n} + assert expected <= found, f"{whl}: missing {expected - found}" + assert any(n == "pygame/docs/generated/LGPL.txt" for n in names), whl + print(whl, "ok") + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pygame-ce-${{ env.PYGAME_CE_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + gpl_sources: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Collect GPL sources for pygame-ce ${{ matrix.version }} + runs-on: ubuntu-24.04-riscv + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + + env: + PYGAME_CE_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + # Every copyleft library auditwheel vendors out of the build image (mpg123 and + # jbigkit-libs are GPL; flac-libs, libzstd, freetype are dual/mixed licensed + # and dnf resolves them to their copyleft option here), same set as + # build-pygame.yml minus graphite2 (harfbuzz isn't built for this port). + - uses: ./actions/collect-gpl-sources + with: + image: ${{ env.MANYLINUX_RISCV64_IMAGE }} + packages: alsa-lib flac-libs freetype jbigkit-libs libzstd mpg123-libs + output: gpl-sources.tar + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pygame-ce-${{ env.PYGAME_CE_VERSION }}-gpl-sources + path: gpl-sources.tar + if-no-files-found: error + + publish: + name: Publish pygame-ce ${{ matrix.version }} + needs: [setup, build_wheels, gpl_sources] + 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: pygame-ce-${{ matrix.version }}-*-manylinux_riscv64 + gpl-sources-artifact: pygame-ce-${{ matrix.version }}-gpl-sources + gpl-sources-description: the copyleft libraries bundled in the wheel diff --git a/docs/packages/pygame-ce.yaml b/docs/packages/pygame-ce.yaml new file mode 100644 index 0000000000..1e54fa555f --- /dev/null +++ b/docs/packages/pygame-ce.yaml @@ -0,0 +1,5 @@ +package-name: pygame-ce +source-code: https://github.com/pygame-community/pygame-ce +license: LGPL-2.1-or-later +versions: +- version: 2.5.8 diff --git a/patches/pygame-ce/2.5.8/0001-tests-skip-the-arc-rasteriser-tests-on-riscv64.patch b/patches/pygame-ce/2.5.8/0001-tests-skip-the-arc-rasteriser-tests-on-riscv64.patch new file mode 100644 index 0000000000..c29e9a31c0 --- /dev/null +++ b/patches/pygame-ce/2.5.8/0001-tests-skip-the-arc-rasteriser-tests-on-riscv64.patch @@ -0,0 +1,67 @@ +From: Ludovic Henry +Subject: [PATCH] tests: skip the two arc rasteriser tests on riscv64 + +pygame.draw.arc() is unreliable on riscv64: the two draw_test cases that +sweep it over real geometry either never return or write out of bounds, +while the rest of the arc tests -- the kwargs/argument-validation ones, +which never rasterise anything -- pass. Both failures are in the C arc +rasteriser, not in the surrounding Python: + + * test_arc__correct_drawing hangs. It ran to the job timeout on all three + interpreter legs with the module's output still buffered (run + 35538919447); a per-test faulthandler watchdog caught the stall at + test/draw_test.py:7205, the bare pygame.draw.arc(**kwargs) call + (run 35548285219). + + * test_arc__surface_clip segfaults, taking the whole test command down + with SIGSEGV part way into draw_test (run 35550328498). Running the + module under `python -X faulthandler -m unittest` put the fault on the + first self.draw_arc() of the sweep -- the one made with the clip + *unset*, so this is not clip handling either. That call draws a full + circle (end < start) into an 11x11 rect walked across a 30x30 surface, + so the rect is partly off-surface for most positions (run 35551432208). + +Running each of draw_test's 252 tests in its own interpreter on the +riscv64 runner leaves exactly these two failing and everything else green, +so the skips are as narrow as the defect. + +pygame's own test runner cannot bound either failure: it defaults to +single-process mode, where its --time_out option does not apply, so a hang +takes the whole job down and a segfault takes the buffered output with it. + +The same test command runs 2385 tests in 18s against upstream's own x86_64 +pygame-ce 2.5.8 wheel (same SDL 2.32.10), so this is riscv64-specific. + +Upstream-Status: To upstream [not yet submitted; this port only touches riseproject-dev/python-wheels, and fixing it properly means correcting the arc rasteriser's loop termination and bounds handling in src_c/draw.c, which needs a reduced C reproducer and a maintainer decision rather than a drive-by PR] + +--- a/test/draw_test.py ++++ b/test/draw_test.py +@@ -1,5 +1,6 @@ + import itertools + import math ++import platform + import sys + import unittest + import warnings +@@ -7157,6 +7158,10 @@ class DrawArcMixin: + with self.assertRaises(TypeError): + bounds_rect = self.draw_arc(**kwargs) + ++ @unittest.skipIf( ++ platform.machine() == "riscv64", ++ "pygame.draw.arc() is broken on riscv64; see patches/pygame-ce/2.5.8/", ++ ) + def test_arc__correct_drawing(self): + """Ensure draw arc works correctly.""" + surfw, surfh = 500, 500 +@@ -7283,6 +7288,10 @@ class DrawArcMixin: + f"thickness={thickness}", + ) + ++ @unittest.skipIf( ++ platform.machine() == "riscv64", ++ "pygame.draw.arc() is broken on riscv64; see patches/pygame-ce/2.5.8/", ++ ) + def test_arc__surface_clip(self): + """Ensures draw arc respects a surface's clip area.""" + surfw = surfh = 30 diff --git a/patches/pygame-ce/2.5.8/0002-tests-extend-PG_DEPS_FROM_SYSTEM-to-opus-MOD-and-HarfBuzz.patch b/patches/pygame-ce/2.5.8/0002-tests-extend-PG_DEPS_FROM_SYSTEM-to-opus-MOD-and-HarfBuzz.patch new file mode 100644 index 0000000000..394eaadf2b --- /dev/null +++ b/patches/pygame-ce/2.5.8/0002-tests-extend-PG_DEPS_FROM_SYSTEM-to-opus-MOD-and-HarfBuzz.patch @@ -0,0 +1,98 @@ +From: Ludovic Henry +Subject: [PATCH] tests: extend PG_DEPS_FROM_SYSTEM to the opus, MOD and HarfBuzz cases + +Upstream already has a marker for "this pygame was built against system SDL +dependencies rather than our own curated set": PG_DEPS_FROM_SYSTEM, which its +own build-ubuntu-sdist.yml, run-ubuntu-checks.yml, dev-check.yml and +build-sdl3.yml all set, and which mixer_music_test and image_test already use +to skip the cases such a build cannot satisfy. It is missing in three places +that this port's SDL stack hits: + + * test_load_object and test_object_namehint walk a list of music files that + unconditionally includes house_lo.opus and surfonasinewave.xm, while the + sibling house_lo.wv entry right below them is already guarded. opus + playback needs libopusfile and MOD playback needs libxmp, exactly as + wavpack needs libwavpack, so the same guard belongs on all three. The + single-format tests next to them (test_load_opus and friends) are already + tagged |tags:music| and so are excluded separately; only these two + list-driven ones are unguarded. + + * test_font_set_script and test_font_set_direction call + Font.set_script()/set_direction(), which raise pygame.error unless SDL_ttf + was built with HarfBuzz (SDL2TTF_HARFBUZZ, off by default in SDL_ttf + 2.24.0). The tests already special-case SDL_ttf older than 2.20.0, but not + a 2.24.0 built without the optional shaper. + +This is a build-configuration guard, not an architecture one: nothing here is +riscv64-specific, and the same four tests fail for anyone building pygame-ce +against a system SDL_mixer/SDL_ttf that omits those optional backends. + +Upstream-Status: To upstream [not yet submitted; this port only opens PRs against riseproject-dev/python-wheels, and extending a marker upstream defined is a maintainer's call about which backends PG_DEPS_FROM_SYSTEM is meant to cover, not a drive-by change] + +--- a/test/font_test.py ++++ b/test/font_test.py +@@ -12,6 +12,8 @@ from pygame import font as pygame_font # So font can be replaced with ftfont + + FONTDIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fixtures", "fonts") + ++PG_DEPS_FROM_SYSTEM = "PG_DEPS_FROM_SYSTEM" in os.environ ++ + + def equal_images(s1, s2): + size = s1.get_size() +@@ -829,6 +831,11 @@ class FontTypeTest(unittest.TestCase): + font = pygame_font.Font(fpath, size) + font.render("WHERE", True, "black") + ++ # system installed SDL_ttf may be built without HarfBuzz ++ @unittest.skipIf( ++ PG_DEPS_FROM_SYSTEM, ++ "set_script()/set_direction() need SDL_ttf built with HarfBuzz", ++ ) + def test_font_set_script(self): + if pygame_font.__name__ == "pygame.ftfont": + return # this ain't a pygame.ftfont thing! +@@ -851,6 +858,11 @@ class FontTypeTest(unittest.TestCase): + else: + self.assertRaises(pygame.error, font.set_script, "Deva") + ++ # system installed SDL_ttf may be built without HarfBuzz ++ @unittest.skipIf( ++ PG_DEPS_FROM_SYSTEM, ++ "set_script()/set_direction() need SDL_ttf built with HarfBuzz", ++ ) + def test_font_set_direction(self): + if pygame_font.__name__ == "pygame.ftfont": + return # not a pygame.ftfont thing +--- a/test/mixer_music_test.py ++++ b/test/mixer_music_test.py +@@ -82,9 +82,12 @@ class MixerMusicModuleTest(unittest.TestCase): + "house_lo.ogg", + "house_lo.wav", + "house_lo.flac", +- "house_lo.opus", +- "surfonasinewave.xm", + ] ++ ++ # a system installed SDL_mixer may lack the opus and MOD backends too ++ if "PG_DEPS_FROM_SYSTEM" not in os.environ: ++ filenames += ["house_lo.opus", "surfonasinewave.xm"] ++ + if pygame.mixer.get_sdl_mixer_version() >= (2, 6, 0): + filenames.append("house_lo.mp3") + +@@ -113,9 +116,12 @@ class MixerMusicModuleTest(unittest.TestCase): + "house_lo.ogg", + "house_lo.wav", + "house_lo.flac", +- "house_lo.opus", +- "surfonasinewave.xm", + ] ++ ++ # a system installed SDL_mixer may lack the opus and MOD backends too ++ if "PG_DEPS_FROM_SYSTEM" not in os.environ: ++ filenames += ["house_lo.opus", "surfonasinewave.xm"] ++ + if pygame.mixer.get_sdl_mixer_version() >= (2, 6, 0): + filenames.append("house_lo.mp3") +