* fix(appimage): reuse the Vulkan pywhispercpp build across CI runs The x86_64 Build AppImage step spends most of its time recompiling pywhispercpp with Vulkan. That compile depends on the base image, tool_checksums.txt, and the pywhispercpp pin, not on the commit. Cache the native tree under one key shared by CI, Nightly, and Release. * fix(appimage): reject a partial Vulkan cache and key the build tools A failed copy could leave a tree that still contained libggml-vulkan.so, and the next run would reuse it. Check each copy, and keep the previous cache until the new one is in place. The cache id now also covers the pinned cmake and ninja export and the Vulkan compile flags. The hit path is what the build calls, and a test runs it with a stub compile command. * chore(ci): rerun AppImage against the saved Vulkan cache The review fix changed the native cache id, so run 37586979294 compiled pywhispercpp again and saved appimage-native-X64-ebf478b87986e2f6b9d80c97c2f5869fe3120c2bcd48f5d8f5a65fe06ec03015. This commit leaves the base image, tool checksums, pywhispercpp pin, installer tool pins, and Vulkan compile flags alone.
138 lines
4.6 KiB
Bash
Executable File
138 lines
4.6 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Reuse a pywhispercpp build that already contains libggml-vulkan.so.
|
|
#
|
|
# Sourced by build.sh. A tree without that library is a CPU wheel, and caching
|
|
# one would ship it to the next run that asked for Vulkan.
|
|
#
|
|
# The directory name is the id from native-cache-id.sh plus the architecture.
|
|
# The .so is not portable across either.
|
|
|
|
_NATIVE_CACHE_HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
pywhispercpp_native_cache_dir() {
|
|
local arch="$1"
|
|
local root id
|
|
root="${VOCALINUX_APPIMAGE_CACHE:-${XDG_CACHE_HOME:-$HOME/.cache}/vocalinux-appimage}"
|
|
id="$(bash "$_NATIVE_CACHE_HERE/native-cache-id.sh")"
|
|
printf '%s\n' "$root/pywhispercpp-vulkan-${arch}-${id}"
|
|
}
|
|
|
|
pywhispercpp_cache_has_vulkan() {
|
|
local dir="$1"
|
|
local found
|
|
[ -d "$dir" ] || return 1
|
|
found="$(find "$dir" -name 'libggml-vulkan.so*' -print -quit)"
|
|
[ -n "$found" ]
|
|
}
|
|
|
|
# Copy the pywhispercpp install out of a site-packages directory.
|
|
# Leaves the rest of the prefix (Vocalinux, numpy, …) where it is.
|
|
pywhispercpp_cache_publish() {
|
|
local site="$1" dest="$2"
|
|
local parent staging pattern backup
|
|
# Called from `if !`, which disables set -e for this whole function.
|
|
# A failing cp has to return on its own, or a short write becomes the cache.
|
|
parent="$(dirname "$dest")"
|
|
mkdir -p "$parent" || return 1
|
|
staging="$(mktemp -d "$parent/.pywhispercpp-staging-XXXXXX")" || return 1
|
|
for pattern in \
|
|
"$site/pywhispercpp" \
|
|
"$site"/pywhispercpp-*.dist-info \
|
|
"$site"/pywhispercpp.libs \
|
|
"$site"/_pywhispercpp* \
|
|
"$site"/libggml*.so* \
|
|
"$site"/libwhisper.so*
|
|
do
|
|
[ -e "$pattern" ] || [ -L "$pattern" ] || continue
|
|
if ! cp -a "$pattern" "$staging/"; then
|
|
rm -rf "$staging"
|
|
echo "Failed to copy $(basename "$pattern") into the pywhispercpp cache." >&2
|
|
return 1
|
|
fi
|
|
done
|
|
if ! pywhispercpp_cache_has_vulkan "$staging"; then
|
|
rm -rf "$staging"
|
|
echo "Refusing to cache a pywhispercpp build with no libggml-vulkan.so" >&2
|
|
return 1
|
|
fi
|
|
backup="${dest}.replacing"
|
|
rm -rf "$backup"
|
|
if [ -e "$dest" ]; then
|
|
if ! mv "$dest" "$backup"; then
|
|
rm -rf "$staging"
|
|
echo "Failed to move the previous pywhispercpp cache aside." >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
if ! mv "$staging" "$dest"; then
|
|
echo "Failed to publish the pywhispercpp cache." >&2
|
|
if [ -e "$backup" ] && ! mv "$backup" "$dest"; then
|
|
echo "Also failed to restore the previous pywhispercpp cache." >&2
|
|
fi
|
|
rm -rf "$staging"
|
|
return 1
|
|
fi
|
|
rm -rf "$backup"
|
|
}
|
|
|
|
# Replace whatever pywhispercpp the prefix just installed (the CPU wheel) with
|
|
# the cached Vulkan build. Other packages in site-packages stay.
|
|
pywhispercpp_cache_restore() {
|
|
local src="$1" site="$2"
|
|
if ! pywhispercpp_cache_has_vulkan "$src"; then
|
|
echo "Cached pywhispercpp tree has no libggml-vulkan.so" >&2
|
|
return 1
|
|
fi
|
|
mkdir -p "$site" || return 1
|
|
find "$site" -depth \( \
|
|
-name 'pywhispercpp' -o \
|
|
-name 'pywhispercpp.libs' -o \
|
|
-name 'pywhispercpp-*.dist-info' -o \
|
|
-name '_pywhispercpp*' -o \
|
|
-name 'libggml*.so*' -o \
|
|
-name 'libwhisper.so*' \
|
|
\) -exec rm -rf {} + 2>/dev/null || true
|
|
if ! cp -a "$src"/. "$site/"; then
|
|
echo "Restoring the cached pywhispercpp tree failed." >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 0 when the cached tree is in place. 1 when the caller has to compile.
|
|
pywhispercpp_try_restore() {
|
|
local cache_dir="$1"
|
|
local site="$2"
|
|
local vk_lib
|
|
if ! pywhispercpp_cache_has_vulkan "$cache_dir"; then
|
|
return 1
|
|
fi
|
|
echo "== Reusing pywhispercpp Vulkan build (${cache_dir##*/}) =="
|
|
if pywhispercpp_cache_restore "$cache_dir" "$site"; then
|
|
vk_lib="$(find "$site" -name 'libggml-vulkan.so*' -print -quit 2>/dev/null || true)"
|
|
if [ -n "$vk_lib" ]; then
|
|
echo " found $vk_lib"
|
|
return 0
|
|
fi
|
|
fi
|
|
echo "Cached Vulkan build could not be restored; compiling again." >&2
|
|
return 1
|
|
}
|
|
|
|
# $1 cache dir, $2 site-packages, $3 command to run on a hit, then the compile
|
|
# command. A hit does not run the compile command.
|
|
pywhispercpp_restore_or_run() {
|
|
local cache_dir="$1"
|
|
local site="$2"
|
|
local on_reuse="$3"
|
|
shift 3
|
|
if pywhispercpp_try_restore "$cache_dir" "$site"; then
|
|
"$on_reuse"
|
|
return $?
|
|
fi
|
|
if [ "$#" -eq 0 ]; then
|
|
echo "No compile command given for the pywhispercpp Vulkan build." >&2
|
|
return 1
|
|
fi
|
|
"$@"
|
|
}
|