diff --git a/.github/workflows/build-pynini.yml b/.github/workflows/build-pynini.yml new file mode 100644 index 00000000000..9297b33ed6e --- /dev/null +++ b/.github/workflows/build-pynini.yml @@ -0,0 +1,126 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +# Based on the wheel-building stage of +# https://github.com/kylebgorman/pynini/blob/v2.1.7/third_party/Dockerfile +name: Build pynini wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/pynini.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-pynini.yml' + - 'docs/packages/pynini.yaml' + - 'patches/pynini/**' + push: + branches: [main] + paths: + - '.github/workflows/build-pynini.yml' + - 'docs/packages/pynini.yaml' + - 'patches/pynini/**' + +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 + OPENFST_VERSION: 1.8.4 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: pynini + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build pynini ${{ 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) }} + python: ["cp312", "cp313", "cp314", "cp314t"] + + env: + PYNINI_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout pynini v${{ env.PYNINI_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: kylebgorman/pynini + ref: v${{ env.PYNINI_VERSION }} + persist-credentials: false + + - name: Checkout python-wheels + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + path: python-wheels + persist-credentials: false + + - name: Apply patches + run: git apply python-wheels/patches/pynini/${{ env.PYNINI_VERSION }}/*.patch + + - 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 }} + # pynini links a system OpenFst rather than bundling it; build it the same + # way upstream's own manylinux Dockerfile does (third_party/Dockerfile). + CIBW_BEFORE_ALL_LINUX: | + set -eux + curl -fsSL -o /tmp/openfst.tar.gz "https://www.openfst.org/twiki/pub/FST/FstDownload/openfst-${{ env.OPENFST_VERSION }}.tar.gz" + tar -xzf /tmp/openfst.tar.gz -C /tmp + cd /tmp/openfst-${{ env.OPENFST_VERSION }} + ./configure --enable-grm + make -j"$(nproc)" + make install + ldconfig + CIBW_TEST_REQUIRES: absl-py + CIBW_TEST_COMMAND: >- + python -c "import pynini; print(pynini.__file__, pynini.__version__)" && + python -c "import importlib.metadata as m; l = sorted(p.name for p in m.files('pynini') if '.dist-info/licenses/' in str(p)); assert l == ['AUTHORS', 'LICENSE', 'LICENSE.openfst'], l" && + cd {project} && + rc=0 && + for t in tests/*_test.py; do + [ "$t" = tests/chatspeak_model_test.py ] || python "$t" || rc=1; + done; + exit $rc + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pynini-${{ env.PYNINI_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish pynini ${{ 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: pynini-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/pynini.yaml b/docs/packages/pynini.yaml new file mode 100644 index 00000000000..6eeb7cbf0e2 --- /dev/null +++ b/docs/packages/pynini.yaml @@ -0,0 +1,6 @@ +package-name: pynini +source-code: https://github.com/kylebgorman/pynini +license: Apache-2.0 +versions: +- version: 2.1.7 + patched: true diff --git a/patches/pynini/2.1.7/0001-ship-OpenFst-s-licence-in-the-wheel-s-dist-info.patch b/patches/pynini/2.1.7/0001-ship-OpenFst-s-licence-in-the-wheel-s-dist-info.patch new file mode 100644 index 00000000000..461fc6e9500 --- /dev/null +++ b/patches/pynini/2.1.7/0001-ship-OpenFst-s-licence-in-the-wheel-s-dist-info.patch @@ -0,0 +1,53 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Ludovic Henry +Date: Mon, 28 Sep 2026 07:47:21 +0000 +Subject: [PATCH] ship OpenFst's licence in the wheel's dist-info + +setup.py links a system OpenFst (fst, fstscript, fstfar, fstfarscript, +fstmpdtscript, fstpdtscript) built separately -- this repo carries no +OpenFst source of its own, and third_party/Dockerfile fetches, builds +and installs it before building the wheel. auditwheel then bundles +those shared libraries into pynini.libs/ inside the wheel, as verified +against the real pynini-2.1.7-cp312-cp312-manylinux_2_27_x86_64. +manylinux_2_28_x86_64.whl from PyPI (165 MB, ~440 MB of libfst*.so +under pynini.libs/). + +setup.py declares no `license_files`, so setuptools' default +LICEN[CS]E*/AUTHORS* root glob is what ships pynini's own LICENSE and +AUTHORS; it never reaches OpenFst's COPYING (Apache-2.0), which only +exists in the OpenFst tarball fetched at build time and is not part of +this checkout. Reproduced against that same real wheel: dist-info/ +licenses/ carries only AUTHORS and LICENSE. + +Restoring OpenFst 1.8.4's COPYING text unchanged as LICENSE.openfst at +the project root is picked up by that same default glob with no +packaging change, landing it in dist-info/licenses/ alongside pynini's +own LICENSE and AUTHORS. + +Upstream-Status: To upstream [no fork of kylebgorman/pynini to push from] + +Signed-off-by: Ludovic Henry +--- + LICENSE.openfst | 13 +++++++++++++ + 1 file changed, 13 insertions(+) + create mode 100644 LICENSE.openfst + +diff --git a/LICENSE.openfst b/LICENSE.openfst +new file mode 100644 +index 0000000..511ac14 +--- /dev/null ++++ b/LICENSE.openfst +@@ -0,0 +1,13 @@ ++Licensed under the Apache License, Version 2.0 (the "License"); ++you may not use these files except in compliance with the License. ++You may obtain a copy of the License at ++ ++ http://www.apache.org/licenses/LICENSE-2.0 ++ ++Unless required by applicable law or agreed to in writing, software ++distributed under the License is distributed on an "AS IS" BASIS, ++WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. ++See the License for the specific language governing permissions and ++limitations under the License. ++ ++Copyright 2005-2024 Google LLC.