From 3deb1375cc4b4449d7fda0aeb3c8496db2a45245 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 21 Sep 2026 11:49:24 +0000 Subject: [PATCH 1/3] vosk: Add version 0.3.45 --- .github/workflows/build-vosk.yml | 239 +++++++++++++++++++++++++++++++ docs/packages/vosk.yaml | 5 + 2 files changed, 244 insertions(+) create mode 100644 .github/workflows/build-vosk.yml create mode 100644 docs/packages/vosk.yaml diff --git a/.github/workflows/build-vosk.yml b/.github/workflows/build-vosk.yml new file mode 100644 index 00000000000..0d9c58aa6e3 --- /dev/null +++ b/.github/workflows/build-vosk.yml @@ -0,0 +1,239 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# vosk publishes no sdist and has no GitHub Actions CI; the wheels come from +# https://github.com/alphacep/vosk-api/blob/v0.3.45/travis/Dockerfile.dockcross-manylinux +# and travis/build-wheels-dockcross.sh, whose CROSS_TRIPLE case already names +# riscv64, built here natively in the manylinux image instead of under dockcross. +name: Build vosk wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/vosk.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-vosk.yml' + - 'docs/packages/vosk.yaml' + push: + branches: [main] + paths: + - '.github/workflows/build-vosk.yml' + - 'docs/packages/vosk.yaml' + +run-name: build-vosk ${{ inputs.version && format('- {0}', inputs.version) || '' }} + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + # Upstream's Dockerfiles clone these three branch heads unpinned; pinned to the + # commits they resolve to, so a rebuild of this version stays reproducible. + KALDI_REF: bc5baf14231660bd50b7d05788865b4ac6c34481 + OPENFST_REF: 18e94e63870ebcf79ebb42b7035cd3cb626ec090 + CLAPACK_REF: f4e1647de96343fb20eea08a9d4f0e0d04c31c51 + OPENBLAS_VERSION: v0.3.20 + VOSK_MODEL: vosk-model-small-en-us-0.15 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: vosk + version: ${{ inputs.version }} + + build_wheels: + name: Build vosk ${{ matrix.version }} py3-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 720 + needs: [setup] + if: needs.setup.outputs.versions != '[]' + + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + + env: + VOSK_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout alphacep/vosk-api + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: alphacep/vosk-api + ref: v${{ env.VOSK_VERSION }} + path: vosk-api + persist-credentials: false + + - name: Build wheel + run: | + mkdir -p wheelhouse + set -o pipefail + docker run --rm -i --network=host \ + -v "${GITHUB_WORKSPACE}:/work" \ + -w /work \ + -e KALDI_REF \ + -e OPENFST_REF \ + -e CLAPACK_REF \ + -e OPENBLAS_VERSION \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash <<'SCRIPT' 2>&1 | tee build.log + set -eux + + dnf install -y --disablerepo=extras --setopt=install_weak_deps=False autoconf automake libtool + + fetch() { + mkdir -p "$2" && git -C "$2" init -q . + git -C "$2" fetch -q --depth 1 "$1" "$3" + git -C "$2" checkout -q FETCH_HEAD + } + + fetch https://github.com/alphacep/kaldi /opt/kaldi "${KALDI_REF}" + cd /opt/kaldi/tools + git clone -q --depth 1 -b "${OPENBLAS_VERSION}" --single-branch https://github.com/xianyi/OpenBLAS + fetch https://github.com/alphacep/clapack /opt/kaldi/tools/clapack "${CLAPACK_REF}" + fetch https://github.com/alphacep/openfst /opt/kaldi/tools/openfst "${OPENFST_REF}" + + # TARGET is what upstream's build-dockcross-manylinux.sh passes per architecture + # (ARMV8 there); RISCV64_GENERIC is the only riscv64 target of OpenBLAS 0.3.20 + # whose kernels are plain C, C910V being RVV 0.7.1. + make -C OpenBLAS ONLY_CBLAS=1 TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 -j "$(nproc)" all + make -C OpenBLAS ONLY_CBLAS=1 TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 PREFIX="$(pwd)/OpenBLAS/install" install + + mkdir -p clapack/BUILD && cd clapack/BUILD + # clapack declares cmake_minimum_required(VERSION 2.6), which the image's CMake 4 refuses. + cmake -DCMAKE_POLICY_VERSION_MINIMUM=3.5 .. + make -j "$(nproc)" -C F2CLIBS + make -j "$(nproc)" -C BLAS + make -j "$(nproc)" -C SRC + find . -name "*.a" | xargs cp -t ../../OpenBLAS/install/lib + + cd /opt/kaldi/tools/openfst + autoreconf -i + CFLAGS="-g -O3" ./configure --prefix=/opt/kaldi/tools/openfst --enable-static --enable-shared \ + --enable-far --enable-ngram-fsts --enable-lookahead-fsts --with-pic --disable-bin + make -j "$(nproc)" + make install + + cd /opt/kaldi/src + ./configure --mathlib=OPENBLAS_CLAPACK --shared --use-cuda=no + sed -i 's: -O1 : -O3 :g' kaldi.mk + make -j "$(nproc)" online2 lm rnnlm + + cd /work/vosk-api/src + KALDI_ROOT=/opt/kaldi OPENFST_ROOT=/opt/kaldi/tools/openfst \ + OPENBLAS_ROOT=/opt/kaldi/tools/OpenBLAS/install make -j "$(nproc)" + + export PATH="/opt/python/cp312-cp312/bin:${PATH}" + export PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ + python -m pip install -q auditwheel cffi requests setuptools srt tqdm websockets wheel + + # libvosk.so statically links all four, and upstream's wheels carry no licence at all. + cd /work/vosk-api + cp COPYING python/LICENSE + cp /opt/kaldi/COPYING python/LICENSE.kaldi + cp /opt/kaldi/tools/openfst/COPYING python/LICENSE.openfst + cp /opt/kaldi/tools/OpenBLAS/LICENSE python/LICENSE.openblas + cp /opt/kaldi/tools/clapack/COPYING python/LICENSE.clapack + + VOSK_SOURCE=/work/vosk-api python -m pip wheel /work/vosk-api/python \ + --no-deps --no-build-isolation -w /work/dist + auditwheel repair --plat manylinux_2_39_riscv64 -w /work/wheelhouse /work/dist/*.whl + SCRIPT + + - name: Upload build log + if: failure() + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: vosk-${{ env.VOSK_VERSION }}-build-log + path: build.log + + - name: Check the wheel bundles libvosk.so and stays interpreter-agnostic + run: | + python3 - wheelhouse/*.whl <<'EOF' + import sys, zipfile + for whl in sys.argv[1:]: + assert "-py3-none-" in whl and whl.endswith("manylinux_2_39_riscv64.whl"), whl + names = zipfile.ZipFile(whl).namelist() + assert "vosk/libvosk.so" in names, names + assert not any(n.endswith(".so") and ".cpython-" in n for n in names), names + # Everything but libstdc++ is linked statically, so auditwheel vendors nothing; + # a vosk.libs/ here would mean the wheel ships a library with no licence text. + assert not any(n.startswith("vosk.libs/") for n in names), names + for want in ("LICENSE", "LICENSE.kaldi", "LICENSE.openfst", "LICENSE.openblas", "LICENSE.clapack"): + assert any(n.endswith("/" + want) for n in names), (want, names) + print(whl, "ok") + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: vosk-${{ env.VOSK_VERSION }}-py3-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + - name: Test wheel + run: | + docker run --rm -i --network=host \ + -v "${GITHUB_WORKSPACE}:/work" \ + -e PIP_EXTRA_INDEX_URL=https://pypi.riseproject.dev/simple/ \ + -e VOSK_MODEL \ + "${MANYLINUX_RISCV64_IMAGE}" \ + bash <<'SCRIPT' + set -eux + + cd /tmp + for pytag in cp312-cp312 cp313-cp313 cp314-cp314 cp314-cp314t; do + PYTHON_BIN="/opt/python/${pytag}/bin/python" + "${PYTHON_BIN}" -m pip install -q /work/wheelhouse/*.whl + "${PYTHON_BIN}" - /work/vosk-api/python/example/test.wav <<'PY' + import json + import os + import sys + import wave + + from vosk import Model, KaldiRecognizer, SetLogLevel + + SetLogLevel(-1) + model = Model(model_name=os.environ["VOSK_MODEL"]) + wf = wave.open(sys.argv[1], "rb") + rec = KaldiRecognizer(model, wf.getframerate()) + rec.SetWords(True) + while True: + data = wf.readframes(4000) + if not data: + break + rec.AcceptWaveform(data) + result = json.loads(rec.FinalResult()) + print(result["text"]) + assert "zero" in result["text"].split(), result + assert len(result["result"]) >= 10, result + PY + done + SCRIPT + + publish: + name: Publish vosk ${{ 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: vosk-${{ matrix.version }}-py3-manylinux_riscv64 diff --git a/docs/packages/vosk.yaml b/docs/packages/vosk.yaml new file mode 100644 index 00000000000..14783004ae0 --- /dev/null +++ b/docs/packages/vosk.yaml @@ -0,0 +1,5 @@ +package-name: vosk +source-code: https://github.com/alphacep/vosk-api +license: Apache-2.0 +versions: +- version: 0.3.45 From dc33eb03a7f0602f0803ca00ca1a05b0a6415ee6 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 21 Sep 2026 12:31:38 +0000 Subject: [PATCH 2/3] vosk: Build OpenBLAS with -Wno-error=implicit-function-declaration OpenBLAS 0.3.20's exports/gensymbol generates a linktest.c that calls every exported symbol with no prototype in scope. GCC 14 diagnoses an implicit function declaration as an error by default, and the rule's own -w only inhibits warnings, so the shared-library target fails: linktest.c:169:1: error: implicit declaration of function 'cblas_ztrmv' make[1]: *** [Makefile:190: ../libopenblas_riscv64_generic-r0.3.20.so] Error 1 Kaldi's configure requires libopenblas.so to exist for --mathlib=OPENBLAS_CLAPACK, so the shared target cannot simply be skipped. COMMON_OPT is the supported knob that Makefile.system folds into CFLAGS; -O2 restores the default it replaces. --- .github/workflows/build-vosk.yml | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build-vosk.yml b/.github/workflows/build-vosk.yml index 0d9c58aa6e3..b59e013560b 100644 --- a/.github/workflows/build-vosk.yml +++ b/.github/workflows/build-vosk.yml @@ -104,11 +104,14 @@ jobs: fetch https://github.com/alphacep/clapack /opt/kaldi/tools/clapack "${CLAPACK_REF}" fetch https://github.com/alphacep/openfst /opt/kaldi/tools/openfst "${OPENFST_REF}" - # TARGET is what upstream's build-dockcross-manylinux.sh passes per architecture - # (ARMV8 there); RISCV64_GENERIC is the only riscv64 target of OpenBLAS 0.3.20 - # whose kernels are plain C, C910V being RVV 0.7.1. - make -C OpenBLAS ONLY_CBLAS=1 TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 -j "$(nproc)" all - make -C OpenBLAS ONLY_CBLAS=1 TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 PREFIX="$(pwd)/OpenBLAS/install" install + # RISCV64_GENERIC is 0.3.20's only riscv64 target with plain C kernels (C910V is RVV 0.7.1); + # upstream's build-dockcross-manylinux.sh passes ARMV8 here. COMMON_OPT adds + # -Wno-error=implicit-function-declaration for exports/gensymbol's prototype-less linktest.c, + # which GCC 14 rejects as an error that the rule's own -w cannot suppress; -O2 is the default it replaces. + make -C OpenBLAS ONLY_CBLAS=1 TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 \ + COMMON_OPT="-O2 -Wno-error=implicit-function-declaration" -j "$(nproc)" all + make -C OpenBLAS ONLY_CBLAS=1 TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 \ + COMMON_OPT="-O2 -Wno-error=implicit-function-declaration" PREFIX="$(pwd)/OpenBLAS/install" install mkdir -p clapack/BUILD && cd clapack/BUILD # clapack declares cmake_minimum_required(VERSION 2.6), which the image's CMake 4 refuses. From 44636bf587251088a4552c5799ed77b520342c07 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Mon, 21 Sep 2026 12:40:32 +0000 Subject: [PATCH 3/3] vosk: Install OpenBLAS headers without ONLY_CBLAS ONLY_CBLAS=1 implies NO_LAPACKE, and Makefile.install gates the lapacke.h copy behind 'ifndef NO_LAPACKE'. Passing it to the install step as well as to the build left the install prefix without lapacke.h, and matrix/kaldi-blas.h includes it unconditionally on the HAVE_OPENBLAS path: ../matrix/kaldi-blas.h:100:12: fatal error: lapacke.h: No such file or directory make: *** [Makefile:164: matrix] Error 2 Upstream's Dockerfile.manylinux and Dockerfile.dockcross-manylinux both pass ONLY_CBLAS=1 to 'all' only and omit it from 'install'; that asymmetry is what puts the header in place, so mirror it. --- .github/workflows/build-vosk.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-vosk.yml b/.github/workflows/build-vosk.yml index b59e013560b..4f7393a4077 100644 --- a/.github/workflows/build-vosk.yml +++ b/.github/workflows/build-vosk.yml @@ -110,7 +110,10 @@ jobs: # which GCC 14 rejects as an error that the rule's own -w cannot suppress; -O2 is the default it replaces. make -C OpenBLAS ONLY_CBLAS=1 TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 \ COMMON_OPT="-O2 -Wno-error=implicit-function-declaration" -j "$(nproc)" all - make -C OpenBLAS ONLY_CBLAS=1 TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 \ + # install drops ONLY_CBLAS, exactly as upstream's two Dockerfiles do: it implies + # NO_LAPACKE, and Makefile.install only copies lapacke.h without it - which + # matrix/kaldi-blas.h includes unconditionally under HAVE_OPENBLAS. + make -C OpenBLAS TARGET=RISCV64_GENERIC HOSTCC=gcc USE_LOCKING=1 USE_THREAD=0 \ COMMON_OPT="-O2 -Wno-error=implicit-function-declaration" PREFIX="$(pwd)/OpenBLAS/install" install mkdir -p clapack/BUILD && cd clapack/BUILD