fix(void): report pywhispercpp 1.5.0 and load CPU variants at runtime
python3-pywhispercpp was installed as 0.0.0. setup.py passes no version to setup() and takes it from setuptools-scm, which the Void build did not install; the template's PYWHISPERCPP_VERSION export is never read. The app read 0.0.0 as an old binding and skipped GPU device selection. Install setuptools-scm, pin the version with SETUPTOOLS_SCM_PRETEND_VERSION, and make the app treat a 0.0.0 distribution as unknown so the runtime feature probe decides. The CPU backend was built with every SIMD option off, so the real-time factor on an AVX2 host was about 1.7. Build with GGML_BACKEND_DL and GGML_CPU_ALL_VARIANTS instead: one CPU library per instruction-set level, with the x86-64 baseline always shipped. The binding loads the variants from its own directory (patches/), and pre_install places them beside the extension. The app's library preloader skips those variants: they export identical symbols, so preloading them RTLD_GLOBAL would let one variant serve every other. The install smoke asserts that the installed package reports 1.5.0, that the baseline variant holds no AVX code, that the AVX2 variant does, and that the CPU and Vulkan backends load from the package. Fixes #43 Fixes #44
This commit is contained in:
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user