Skip to content

build-vosk

build-vosk #7

Workflow file for this run

# 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}"
# 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
# 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
# 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