Skip to content

Commit cf71e29

Browse files
localai-botmudler
andauthored
fix(darwin): fix vibevoice-cpp build linkage + fail-safe go backend packaging (#10276)
* fix(darwin): never package a go backend build tree as a working image The darwin/arm64 vibevoice-cpp image shipped the source tree with a half-built CMake directory (build-libgovibevoicecpp-fallback.so/) and no backend binary, so the backend could never start: run.sh exec'd a vibevoice-cpp binary that was not in the package and LocalAI timed out waiting for the gRPC service. Two durable, backend-agnostic defenses: - backend/go/vibevoice-cpp/Makefile: mirror whisper's cleanup discipline so a partial CMake tree cannot survive into packaging. Run `make purge` before each variant build and `rm -rfv build*` after. The old recipe only removed its build dir after a successful `mv`, so a failed build left the half-built tree behind. - scripts/build/golang-darwin.sh: before creating the OCI image, remove any stray build-* directory and assert that the binary run.sh launches actually exists. A build that produced no binary now fails the job loudly instead of publishing a source tree as a working backend. The binary name is derived from run.sh's `exec $CURDIR/<binary>` line (parakeet-cpp launches parakeet-cpp-grpc, so it is not always ${BACKEND}) with a ${BACKEND} fallback. The underlying native build failure that left vibevoice-cpp half-built still needs to be reproduced and fixed on Apple Silicon; this change ensures such a failure can never again be published as a working image. Refs #10267 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] * fix(vibevoice-cpp): build libvibevoice.a on darwin (link target, not path) The darwin build failed with: No rule to make target 'vibevoice/libvibevoice.a', needed by 'libgovibevoicecpp.so'. Stop. The upstream vibevoice project is added with add_subdirectory(... EXCLUDE_FROM_ALL), so its `vibevoice` static-library target is only built when something links it as a target. The Apple branch linked only `$<TARGET_FILE:vibevoice>` - a bare archive path with no target reference - so CMake never emitted a rule to build libvibevoice.a, while the Linux branch worked because it passes the `vibevoice` target name inside the --whole-archive flags. Link the `vibevoice` target on Apple (establishing the build dependency) and apply -force_load as a separate link option to keep whole-archive semantics so purego can dlsym the vv_capi_* symbols. Refs #10267 Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Assisted-by: Claude:claude-opus-4-8 [Claude Code] --------- Signed-off-by: Ettore Di Giacinto <mudler@localai.io> Co-authored-by: Ettore Di Giacinto <mudler@localai.io>
1 parent a7a7bd6 commit cf71e29

3 files changed

Lines changed: 39 additions & 5 deletions

File tree

backend/go/vibevoice-cpp/CMakeLists.txt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,16 @@ add_library(govibevoicecpp MODULE cpp/govibevoicecpp.cpp)
2626
# vv_capi_* symbols (purego dlopens them by name, nothing in our
2727
# translation unit references them). Force the static archive's
2828
# entire contents into the MODULE so dlsym finds vv_capi_load etc.
29+
#
30+
# Link the `vibevoice` TARGET (not a bare archive path) so CMake builds
31+
# libvibevoice.a first and tracks the dependency: the upstream project is added
32+
# with EXCLUDE_FROM_ALL, so without a target-level link there is no rule to
33+
# build it. Passing only $<TARGET_FILE:vibevoice> as a path on Apple left the
34+
# build with "No rule to make target 'vibevoice/libvibevoice.a'" (issue #10267).
35+
# force_load is then applied as a separate link option.
2936
if(APPLE)
30-
target_link_libraries(govibevoicecpp PRIVATE -Wl,-force_load $<TARGET_FILE:vibevoice>)
37+
target_link_libraries(govibevoicecpp PRIVATE vibevoice)
38+
target_link_options(govibevoicecpp PRIVATE "-Wl,-force_load,$<TARGET_FILE:vibevoice>")
3139
elseif(MSVC)
3240
target_link_libraries(govibevoicecpp PRIVATE vibevoice)
3341
set_property(TARGET govibevoicecpp APPEND PROPERTY LINK_FLAGS "/WHOLEARCHIVE:vibevoice")

backend/go/vibevoice-cpp/Makefile

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,26 +94,30 @@ purge:
9494
# Build all variants (Linux only)
9595
ifeq ($(UNAME_S),Linux)
9696
libgovibevoicecpp-avx.so: sources/vibevoice.cpp
97+
$(MAKE) purge
9798
$(info ${GREEN}I vibevoice-cpp build info:avx${RESET})
9899
SO_TARGET=libgovibevoicecpp-avx.so CMAKE_ARGS="$(CMAKE_ARGS) -DGGML_AVX=on -DGGML_AVX2=off -DGGML_AVX512=off -DGGML_FMA=off -DGGML_F16C=off -DGGML_BMI2=off" $(MAKE) libgovibevoicecpp-custom
99-
rm -rf build-libgovibevoicecpp-avx.so
100+
rm -rfv build*
100101

101102
libgovibevoicecpp-avx2.so: sources/vibevoice.cpp
103+
$(MAKE) purge
102104
$(info ${GREEN}I vibevoice-cpp build info:avx2${RESET})
103105
SO_TARGET=libgovibevoicecpp-avx2.so CMAKE_ARGS="$(CMAKE_ARGS) -DGGML_AVX=on -DGGML_AVX2=on -DGGML_AVX512=off -DGGML_FMA=on -DGGML_F16C=on -DGGML_BMI2=on" $(MAKE) libgovibevoicecpp-custom
104-
rm -rf build-libgovibevoicecpp-avx2.so
106+
rm -rfv build*
105107

106108
libgovibevoicecpp-avx512.so: sources/vibevoice.cpp
109+
$(MAKE) purge
107110
$(info ${GREEN}I vibevoice-cpp build info:avx512${RESET})
108111
SO_TARGET=libgovibevoicecpp-avx512.so CMAKE_ARGS="$(CMAKE_ARGS) -DGGML_AVX=on -DGGML_AVX2=on -DGGML_AVX512=on -DGGML_FMA=on -DGGML_F16C=on -DGGML_BMI2=on" $(MAKE) libgovibevoicecpp-custom
109-
rm -rf build-libgovibevoicecpp-avx512.so
112+
rm -rfv build*
110113
endif
111114

112115
# Build fallback variant (all platforms)
113116
libgovibevoicecpp-fallback.so: sources/vibevoice.cpp
117+
$(MAKE) purge
114118
$(info ${GREEN}I vibevoice-cpp build info:fallback${RESET})
115119
SO_TARGET=libgovibevoicecpp-fallback.so CMAKE_ARGS="$(CMAKE_ARGS) -DGGML_AVX=off -DGGML_AVX2=off -DGGML_AVX512=off -DGGML_FMA=off -DGGML_F16C=off -DGGML_BMI2=off" $(MAKE) libgovibevoicecpp-custom
116-
rm -rf build-libgovibevoicecpp-fallback.so
120+
rm -rfv build*
117121

118122
libgovibevoicecpp-custom: CMakeLists.txt cpp/govibevoicecpp.cpp cpp/govibevoicecpp.h
119123
mkdir -p build-$(SO_TARGET) && \

scripts/build/golang-darwin.sh

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,28 @@ export BUILD_TYPE="${BUILD_TYPE:-metal}"
55
mkdir -p backend-images
66
make -C backend/go/${BACKEND} build
77

8+
BACKEND_DIR="backend/go/${BACKEND}"
9+
10+
# Never package a stray CMake build tree (e.g. build-libgo*-*.so/, a directory
11+
# left behind by a partial native build) into the backend image.
12+
rm -rf "${BACKEND_DIR}"/build-*
13+
14+
# Fail loudly if the build did not produce the backend binary, instead of
15+
# silently packaging the source/build tree as a "backend" that can never start
16+
# (issue #10267: the darwin vibevoice-cpp image shipped sources, no binary).
17+
# run.sh's final `exec $CURDIR/<binary>` is the contract for what gets launched;
18+
# the binary is not always named after the backend (e.g. parakeet-cpp launches
19+
# parakeet-cpp-grpc), so derive it from run.sh and fall back to ${BACKEND}.
20+
RUN_BINARY=""
21+
if [ -f "${BACKEND_DIR}/run.sh" ]; then
22+
RUN_BINARY=$(grep -oE '\$CURDIR/[A-Za-z0-9._-]+' "${BACKEND_DIR}/run.sh" | grep -v 'ld\.so' | tail -1 | sed 's|\$CURDIR/||')
23+
fi
24+
RUN_BINARY="${RUN_BINARY:-${BACKEND}}"
25+
if [ ! -x "${BACKEND_DIR}/${RUN_BINARY}" ]; then
26+
echo "ERROR: ${BACKEND_DIR}/${RUN_BINARY} not found after build; refusing to package a broken backend image (see issue #10267)." >&2
27+
exit 1
28+
fi
29+
830
PLATFORMARCH="${PLATFORMARCH:-darwin/arm64}"
931
IMAGE_NAME="${IMAGE_NAME:-localai/${BACKEND}-darwin}"
1032

0 commit comments

Comments
 (0)