fix(void): report pywhispercpp 1.5.0 and load CPU variants at runtime (#49)
CI / security (push) Successful in 1m9s
CI / lint (push) Successful in 23s
CI / ai-review (push) Skipped

Fixes #43
Fixes #44
This commit was merged in pull request #49.
This commit is contained in:
2026-11-06 18:51:30 +00:00
parent b0d8afad92
commit 0c48db5b5e
11 changed files with 376 additions and 66 deletions
+87 -28
View File
@@ -3,7 +3,8 @@
Spec #16 packages four carried templates in void-packages layout under
``packaging/void``; this ticket owns the hard one, ``python3-pywhispercpp``,
built from the pinned PyPI source distribution with the Vulkan backend,
shared libraries, and portable CPU flags. Ticket #28 builds the container
shared libraries, runtime-selected CPU variants (#44), and a version pinned
through setuptools-scm (#43). Ticket #28 builds the container
smoke gate on top; everything asserted here runs offline from sources
already in the repo.
@@ -24,21 +25,27 @@ BUILD_SH = VOID_DIR / "build-release.sh"
INSTALL_SH = REPO_ROOT / "install.sh"
UV_LOCK = REPO_ROOT / "uv.lock"
#: ggml SIMD options pinned off for the plain x86-64 baseline (#957).
#: GGML_NATIVE=OFF alone is not portable: while ggml's native default is on,
#: every GGML_* SIMD option stays on and still compiles -mavx2 -mfma.
#: Mirrors PORTABLE_GGML_OPTIONS in tests/test_flatpak_packaging.py and the
#: CMAKE_ARGS line in packaging/flatpak/python3-dependencies.yaml.
PORTABLE_GGML_OPTIONS = (
"GGML_NATIVE",
"GGML_SSE42",
"GGML_AVX",
"GGML_AVX2",
"GGML_BMI2",
"GGML_FMA",
"GGML_F16C",
#: CMake arguments of the CPU backend (#44): one library per CPU level, loaded
#: at run time, with the x86-64 baseline always shipped. GGML_NATIVE must stay
#: OFF: ggml refuses it with GGML_BACKEND_DL, and it would compile the builder's
#: instruction set into every variant (#938).
REQUIRED_CMAKE_ARGS = (
"-DGGML_NATIVE=OFF",
"-DGGML_BACKEND_DL=ON",
"-DGGML_CPU_ALL_VARIANTS=ON",
"-DBUILD_SHARED_LIBS=ON",
)
#: The binding patch that loads the backend modules from the extension's directory.
BINDING_PATCH = (
VOID_DIR
/ "srcpkgs"
/ "python3-pywhispercpp"
/ "patches"
/ "load-ggml-backends-beside-module.patch"
)
INSTALL_SMOKE = VOID_DIR / "container-release.sh"
#: install_requires of pywhispercpp's setup.py, mapped to the Void binary
#: package names. Each name was verified to exist as srcpkgs/<name>/template
#: in current void-packages (2026-11-05); the container build re-verifies via
@@ -126,21 +133,57 @@ def test_build_style_is_python3_module() -> None:
assert re.search(r"^build_style=python3-module$", _template(), re.M)
def test_portable_cpu_flags_pin_every_ggml_simd_option_off() -> None:
"""No host-native tuning: the plain x86-64 baseline, like the Flatpak."""
text = _template()
for flag in PORTABLE_GGML_OPTIONS:
assert f"-D{flag}=OFF" in text, (
f"template does not pin {flag} off; the package inherits the "
"builder's instruction set and SIGILLs on older CPUs (#938, #957)"
)
def _code_lines() -> str:
"""Template with comment lines stripped; flags live in code, not prose."""
return "\n".join(line for line in _template().splitlines() if not line.lstrip().startswith("#"))
def test_cpu_backend_is_built_as_runtime_selected_variants() -> None:
"""#44: every CPU level is its own library, and the x86-64 baseline ships."""
text = _code_lines()
for arg in REQUIRED_CMAKE_ARGS:
assert arg in text, f"template does not pass {arg}"
def test_no_simd_option_is_forced_on_for_the_whole_build() -> None:
"""A global SIMD switch would give every variant one instruction set (#938, #957)."""
text = _code_lines()
for feature in ("NATIVE", "SSE42", "AVX", "AVX2", "BMI2", "FMA", "F16C", "AVX512"):
assert f"-DGGML_{feature}=ON" not in text, f"GGML_{feature} forced on for every variant"
def test_backends_are_copied_beside_the_extension_before_install() -> None:
"""#44: whisper.cpp writes the modules to build/bin, which setup.py does not install."""
text = _template()
assert "pre_install()" in text
assert "build/temp.*/_pywhispercpp/bin/libggml-*.so" in text
assert "build/lib.*/" in text
assert "msg_error" in text, "the build must fail loudly when no backend module was built"
def test_binding_loads_the_backends_beside_the_extension() -> None:
"""#44: ggml's default search misses the package directory, so the binding loads it."""
assert BINDING_PATCH.is_file(), f"missing binding patch: {BINDING_PATCH}"
text = BINDING_PATCH.read_text(encoding="utf-8")
assert "--- a/src/main.cpp" in text and "+++ b/src/main.cpp" in text
assert " PYBIND11_MODULE(_pywhispercpp, m) {" in text
assert "+ load_ggml_backends_beside_module();" in text
assert "+ ggml_backend_load_all_from_path(dir.c_str());" in text
def test_install_smoke_checks_version_and_cpu_variants() -> None:
"""#43 and #44: the installed binding reports the pinned version, and its variants load."""
text = INSTALL_SMOKE.read_text(encoding="utf-8")
version = re.search(r"^version=(\S+)", _template(), re.M)
assert version, "template declares no version"
assert (
f'[ "$PWC_VERSION" = "{version.group(1)}" ]' in text
), "the install smoke must assert the installed pywhispercpp reports the template version"
assert "libggml-cpu-x64.so" in text and "libggml-cpu-haswell.so" in text
assert "objdump -d" in text, "the baseline variant must be checked for AVX code"
assert "libggml.so.0" in text, "the backend registry lives in libggml"
def test_no_native_tuning_or_cuda_anywhere() -> None:
"""Void has no CUDA toolkit package; portable means portable."""
text = _code_lines()
@@ -181,15 +224,31 @@ def test_build_time_deps_cover_setup_py() -> None:
text = _template()
host = re.search(r"^hostmakedepends=(.*)$", text, re.M)
assert host, "template declares no hostmakedepends"
for dep in ("cmake", "pkg-config", "python3-setuptools", "python3-wheel"):
for dep in (
"cmake",
"pkg-config",
"python3-setuptools",
"python3-setuptools_scm",
"python3-wheel",
):
assert dep in host.group(1), f"{dep} missing from hostmakedepends"
make = re.search(r"^makedepends=(.*)$", text, re.M)
assert make and "python3-devel" in make.group(1), "compiled extension needs python3-devel"
def test_version_is_injected_for_the_offline_build() -> None:
"""The sdist carries no version.txt; setup.py reads PYWHISPERCPP_VERSION."""
assert "PYWHISPERCPP_VERSION" in _template()
def test_version_is_pinned_through_setuptools_scm() -> None:
"""#43: setup() gets no version; setuptools-scm supplies it, so the build must provide it."""
host = re.search(r"^hostmakedepends=(.*)$", _template(), re.M)
assert host and "python3-setuptools_scm" in host.group(
1
), "without setuptools-scm the build reports version 0.0.0"
assert (
"SETUPTOOLS_SCM_PRETEND_VERSION=${version}" in _code_lines()
), "the xbps build root has no git metadata; pin the template version"
installer = (REPO_ROOT / "requirements" / "installer-build.txt").read_text(encoding="utf-8")
assert re.search(
r"^setuptools-scm==", installer, re.M
), "the installer build tools pin setuptools-scm as well"
def test_build_fetches_nothing_beyond_the_sdist() -> None: