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
51 lines
1.7 KiB
Python
51 lines
1.7 KiB
Python
"""Preloading must leave ggml's runtime-selected CPU variants to ggml (#44)."""
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
from vocalinux.utils import pywhispercpp_loader
|
|
|
|
|
|
def _preload_names(tmp_path: Path, *names: str) -> list[str]:
|
|
"""Run the preloader over ``names`` in one directory and return what it dlopens."""
|
|
for name in names:
|
|
(tmp_path / name).write_bytes(b"")
|
|
loaded: list[str] = []
|
|
|
|
def record(path: str, mode: int = 0) -> object:
|
|
loaded.append(Path(path).name)
|
|
return object()
|
|
|
|
with (
|
|
patch.object(pywhispercpp_loader, "find_shared_library_dirs", return_value=[str(tmp_path)]),
|
|
patch.object(pywhispercpp_loader, "_PRELOADED_LIBS", []),
|
|
patch.object(pywhispercpp_loader.ctypes, "CDLL", side_effect=record),
|
|
):
|
|
pywhispercpp_loader.preload_shared_libraries()
|
|
return loaded
|
|
|
|
|
|
def test_preload_leaves_runtime_cpu_variants_to_ggml(tmp_path: Path) -> None:
|
|
"""Variants share one symbol table; preloading them RTLD_GLOBAL would interpose them."""
|
|
loaded = _preload_names(
|
|
tmp_path,
|
|
"libggml.so.0",
|
|
"libggml-base.so.0",
|
|
"libggml-cpu-x64.so",
|
|
"libggml-cpu-haswell.so",
|
|
"libggml-vulkan.so",
|
|
"libwhisper.so.1",
|
|
)
|
|
|
|
assert not [name for name in loaded if name.startswith("libggml-cpu-")]
|
|
assert {"libggml.so.0", "libggml-base.so.0", "libggml-vulkan.so", "libwhisper.so.1"} <= set(
|
|
loaded
|
|
)
|
|
|
|
|
|
def test_preload_keeps_the_single_cpu_library_of_a_non_variant_build(tmp_path: Path) -> None:
|
|
"""Without GGML_CPU_ALL_VARIANTS the CPU backend is a dependency and must be preloaded."""
|
|
loaded = _preload_names(tmp_path, "libggml.so.0", "libggml-cpu.so")
|
|
|
|
assert "libggml-cpu.so" in loaded
|