Skip to content
Merged
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
107 changes: 107 additions & 0 deletions .github/workflows/build-praat-parselmouth.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# This workflow is based on the `wheels` job of upstream's own cibuildwheel config:
# https://github.com/YannickJadoul/Parselmouth/blob/v0.4.7/.github/workflows/wheels.yml
name: Build praat-parselmouth wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'praat-parselmouth version to build (git tag without leading v, e.g. 0.4.7)'
required: true
default: '0.4.7'
pull_request:
paths:
- '.github/workflows/build-praat-parselmouth.yml'
- 'patches/praat-parselmouth/**'

concurrency:
group: ${{ github.workflow }}-${{ inputs.version || '0.4.7' }}-${{ 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.4.7 there.
PARSELMOUTH_VERSION: ${{ inputs.version || '0.4.7' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

jobs:
setup:
uses: $/.github/workflows/_setup.yml

build_wheels:
needs: [setup]
name: Build praat-parselmouth ${{ inputs.version || '0.4.7' }} cp312/cp313/cp314-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 480 # ~1M lines of vendored Praat C/C++ (clapack, gsl, glpk, flac, portaudio, espeak, vorbis, opusfile)

steps:
- name: Checkout Parselmouth v${{ env.PARSELMOUTH_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: YannickJadoul/Parselmouth
ref: v${{ env.PARSELMOUTH_VERSION }}
# praat/ and pybind11/ are vendored directly; only extern/fmt is a real submodule.
submodules: recursive
persist-credentials: false

- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
persist-credentials: false

- name: Patch praat-parselmouth source
run: git apply python-wheels/patches/praat-parselmouth/${{ env.PARSELMOUTH_VERSION }}/*.patch

- name: Pin cmake build dependency
# Mirrors upstream's own CI (which rewrites pyproject.toml the same way at
# build time): cmake 4 dropped compatibility with cmake_minimum_required
# versions below 3.5, which the vendored pybind11 (3.5) and fmt (3.1...3.18)
# CMakeLists.txt rely on.
run: sed -i 's/"cmake>=3.18"/"cmake>=3.18,<4"/' pyproject.toml

- name: Build and test wheels
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
with:
output-dir: wheelhouse/
env:
CIBW_ARCHS: riscv64
CIBW_BUILD: cp312-manylinux_riscv64 cp313-manylinux_riscv64 cp314-manylinux_riscv64
# No musllinux riscv64 numpy wheel on the registry yet (workflow-anatomy.md).
CIBW_SKIP: "*musllinux*"
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# Builds the heavy vendored `praat` static lib once for the whole job
# instead of once per interpreter (gotcha 15), exactly like upstream's
# own CIBW_BEFORE_ALL.
CIBW_BEFORE_ALL_LINUX: |
cmake -S . -B build_dependencies
cmake --build build_dependencies --target praat -j "$(nproc)"
CIBW_BEFORE_BUILD: rm -rf _skbuild
CIBW_ENVIRONMENT: >-
PARSELMOUTH_EXTRA_CMAKE_ARGS="-DPREBUILT_DEPENDENCIES=$(pwd)/build_dependencies"
CMAKE_BUILD_PARALLEL_LEVEL=4
PIP_ONLY_BINARY=numpy,cmake
PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/
CIBW_TEST_REQUIRES: "pytest<8 pytest-lazy-fixture tgt future"
CIBW_TEST_COMMAND: pytest {project}/tests

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

publish:
name: Publish praat-parselmouth ${{ inputs.version || '0.4.7' }}
needs: [setup, build_wheels]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: praat-parselmouth-${{ inputs.version || '0.4.7' }}-manylinux_riscv64
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
From fe441a9a966150645b0ec88f0bc62b7c22f4b797 Mon Sep 17 00:00:00 2001
From: Ludovic Henry <git@ludovic.dev>
Date: Fri, 11 Sep 2026 12:13:56 +0200
Subject: [PATCH] test: tolerate FP rounding differences in test_xs's
grid-formula recomputation

Upstream-Status: To upstream [not submitted; this port's automation may not open issues or PRs on repos other than riseproject-dev/python-wheels]

test_xs re-derives each Sampled's x grid in Python (x1 + dx * arange(nx))
and compares it to the C++-computed xs()/x_grid()/x_bins() with strict
floating-point equality. On riscv64 the two independently-computed
expressions differ in their last bit or two (compiler FMA fusion
differences between GCC's riscv64 codegen and numpy's separate
multiply-then-add), so the exact-equality assert fails even though the
values agree to machine precision. Reproduces on riscv64 only; not seen
on the x86_64/aarch64/arm64/windows runners upstream's own CI covers.

Switch the three assertions to np.allclose with a tolerance far above
double-precision rounding error but far below any real functional
divergence.
---
tests/test_sampled.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/test_sampled.py b/tests/test_sampled.py
index fc4c141..7b7cc93 100644
--- a/tests/test_sampled.py
+++ b/tests/test_sampled.py
@@ -21,9 +21,9 @@ import numpy as np


def test_xs(sampled):
- assert np.all(sampled.xs() == sampled.x1 + sampled.dx * np.arange(sampled.nx))
- assert np.all(sampled.x_grid() == sampled.x1 + sampled.dx * (np.arange(sampled.nx + 1) - 0.5))
- assert np.all(sampled.x_bins() == np.vstack((sampled.x_grid()[:-1], sampled.x_grid()[1:])).T)
+ assert np.allclose(sampled.xs(), sampled.x1 + sampled.dx * np.arange(sampled.nx), rtol=1e-9, atol=1e-12)
+ assert np.allclose(sampled.x_grid(), sampled.x1 + sampled.dx * (np.arange(sampled.nx + 1) - 0.5), rtol=1e-9, atol=1e-12)
+ assert np.allclose(sampled.x_bins(), np.vstack((sampled.x_grid()[:-1], sampled.x_grid()[1:])).T, rtol=1e-9, atol=1e-12)


def test_len(sampled):
--
2.50.1 (Apple Git-155)