mirror of
https://github.com/mudler/LocalAI.git
synced 2026-07-30 09:57:57 -04:00
Bespoke C++ Darwin path like ds4 and privacy-filter: an includeDarwin matrix
entry, a backends/audio-cpp-darwin make target, a gated workflow step, and the
metal image entries plus metal/metal-darwin-arm64 capability keys in the
backend gallery.
The build script deliberately does NOT reassemble the package the way
privacy-filter-darwin.sh does. It runs the backend's own `make package` and
copies the result, so the Darwin package keeps the root-level layout the Linux
one has: grpc-server, run.sh, the ggml objects and assets/ in one directory,
with lib/ for the dylib closure. Hand-assembling would drop assets/, and
assets/ is what makes the bundled: model paths resolve with nothing downloaded.
The dylib walk is a full transitive closure rather than the single level ds4
and llama-cpp do, because Homebrew's grpc++ pulls libgrpc, abseil, upb, cares
and OpenSSL that grpc-server does not link itself, and a level-1 walk ships a
package that only works on a machine that already has Homebrew grpc.
Two fixes folded in, both in the backend Makefile:
- an EMPTY CUDA_MAJOR_VERSION fell through to the CUDA 12 architecture list,
which contains 120a-real and so needs nvcc >= 12.8. A local
BUILD_TYPE=cublas build on a 12.0-12.7 host failed to compile where
upstream's documented default (native) worked. EMPTY now maps to native,
12 and 13 keep their lists, and any other non-empty value is an error on
cublas builds. CI always passes a major, so CI is unaffected.
- the Darwin branch now points CMake at Homebrew's keg-only libomp. AppleClang
ships no OpenMP runtime and nothing is symlinked into /opt/homebrew, so
FindOpenMP finds neither the library nor the header, and audio.cpp calls
find_package(OpenMP REQUIRED) whenever ENGINE_ENABLE_OPENMP is on. Without
the hint the macOS build would have died at configure time. If the keg is
absent the build disables OpenMP instead of failing.
Assisted-by: Claude:claude-opus-5 [Claude Code]
Signed-off-by: Ettore Di Giacinto <mudler@localai.io>
155 lines
7.4 KiB
Makefile
155 lines
7.4 KiB
Makefile
# audio.cpp backend Makefile.
|
|
#
|
|
# Upstream pin lives below in the AUDIO_CPP_VERSION variable, so
|
|
# .github/bump_deps.sh can find and update it, matching the llama-cpp / ds4
|
|
# convention. That script seds every line matching the variable name followed by
|
|
# an assignment, so this comment deliberately spells the name on its own: a
|
|
# comment repeating the full assignment token gets rewritten and mangled by the
|
|
# first auto-bump (backend/cpp/ds4/Makefile shows the damage). The clone
|
|
# recipe is a make target (not a prepare.sh) so 'make purge && make' is a clean
|
|
# rebuild and so the bump bot can see the pin.
|
|
|
|
AUDIO_CPP_VERSION?=e800d435d130dc776baf6f3e6129bb62b1495c89
|
|
AUDIO_CPP_REPO?=https://github.com/0xShug0/audio.cpp
|
|
|
|
CURRENT_MAKEFILE_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
|
|
BUILD_DIR := build
|
|
|
|
BUILD_TYPE ?=
|
|
NATIVE ?= false
|
|
JOBS ?= $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
|
|
|
|
UNAME_S := $(shell uname -s)
|
|
|
|
# AUDIOCPP_DEPLOYMENT_BUILD compiles the model_specs/*.json catalog into
|
|
# engine_runtime, so the shipped package needs no model_specs directory and a
|
|
# safetensors model tree still resolves its family spec.
|
|
CMAKE_ARGS ?= -DCMAKE_BUILD_TYPE=Release -DAUDIOCPP_DEPLOYMENT_BUILD=ON
|
|
|
|
# CMAKE_CUDA_ARCHITECTURES must be set explicitly for a cublas build, and this
|
|
# is not a tuning knob: upstream's CMakeLists sets CUDA_ARCHITECTURES to
|
|
# `native` on the engine_runtime target whenever the root-scope variable is
|
|
# unset (audio.cpp/CMakeLists.txt, the `if (CMAKE_CUDA_ARCHITECTURES)` branch
|
|
# next to the istft/torch_random .cu sources), and docs/build/linux.md says so
|
|
# outright: "Leave CMAKE_CUDA_ARCHITECTURES unset to build for the GPUs present
|
|
# at build time (native)". No CI runner has a GPU, so `native` has nothing to
|
|
# enumerate. ggml's own default (external/ggml/src/ggml-cuda/CMakeLists.txt)
|
|
# does not rescue this: it list(APPEND)s in the ggml subdirectory scope, which
|
|
# never reaches the root scope where the engine_runtime property is decided.
|
|
#
|
|
# The values below are ggml's list for the matching toolkit, copied rather than
|
|
# invented, so the two targets compile for exactly the same set:
|
|
# - CUDA 13 drops the Maxwell/Pascal/Volta virtual archs (50/61/70).
|
|
# - 121a-real needs CUDA >= 12.9, so the CUDA 12 list (built against 12.8)
|
|
# stops at 120a-real.
|
|
# - `a`-suffixed archs are used rather than ggml's rejected 120f-virtual: the
|
|
# `f` suffix needs CMake >= 3.31.8, and Ubuntu Noble ships 3.28.3. The
|
|
# 3.28 validator (Modules/Internal/CMakeCUDAArchitecturesValidate.cmake)
|
|
# accepts `[0-9]+a?(-real|-virtual)?`.
|
|
#
|
|
# Setting it here also pins ggml's copy, since its default is guarded by
|
|
# `if (NOT DEFINED CMAKE_CUDA_ARCHITECTURES)`. CUDA_MAJOR_VERSION is the CI
|
|
# build-arg, forwarded by Dockerfile.audio-cpp.
|
|
#
|
|
# An EMPTY major maps to `native`, NOT to the CUDA 12 list. Only CI declares a
|
|
# major; a developer running `BUILD_TYPE=cublas make` locally declares none, and
|
|
# the CUDA 12 list contains 120a-real, which needs nvcc >= 12.8. Falling through
|
|
# to it turned every local build on a CUDA 12.0-12.7 host into a compile error,
|
|
# where upstream's documented behaviour ("Leave CMAKE_CUDA_ARCHITECTURES unset
|
|
# to build for the GPUs present at build time") worked. `native` restores that.
|
|
# It does require a GPU to enumerate, so the escape hatch for a GPU-less local
|
|
# cross-build is to set CUDA_ARCHITECTURES on the command line, which the ?=
|
|
# assignments below leave untouched.
|
|
CUDA_MAJOR_VERSION ?=
|
|
ifeq ($(CUDA_MAJOR_VERSION),13)
|
|
CUDA_ARCHITECTURES ?= 75-virtual;80-virtual;86-real;89-real;120a-real;121a-real
|
|
else ifeq ($(CUDA_MAJOR_VERSION),12)
|
|
CUDA_ARCHITECTURES ?= 50-virtual;61-virtual;70-virtual;75-virtual;80-virtual;86-real;89-real;120a-real
|
|
else ifeq ($(CUDA_MAJOR_VERSION),)
|
|
CUDA_ARCHITECTURES ?= native
|
|
else ifeq ($(BUILD_TYPE),cublas)
|
|
# Gated on cublas so that `make clean` with a stray CUDA_MAJOR_VERSION in the
|
|
# environment still works; the variable means nothing to any other build.
|
|
$(error CUDA_MAJOR_VERSION=$(CUDA_MAJOR_VERSION) has no architecture list here (12 and 13 do). Leave it empty for a native build, or pass CUDA_ARCHITECTURES explicitly.)
|
|
endif
|
|
|
|
ifeq ($(BUILD_TYPE),cublas)
|
|
CMAKE_ARGS += -DENGINE_ENABLE_CUDA=ON "-DCMAKE_CUDA_ARCHITECTURES=$(CUDA_ARCHITECTURES)"
|
|
else ifeq ($(BUILD_TYPE),vulkan)
|
|
CMAKE_ARGS += -DENGINE_ENABLE_VULKAN=ON
|
|
else ifeq ($(UNAME_S),Darwin)
|
|
# Metal. ggml embeds the shader library by default (GGML_METAL_EMBED_LIBRARY
|
|
# defaults to GGML_METAL), so the package needs no .metallib beside the
|
|
# binary. Darwin builds go through scripts/build/audio-cpp-darwin.sh.
|
|
CMAKE_ARGS += -DENGINE_ENABLE_METAL=ON
|
|
# AppleClang ships no OpenMP runtime and Homebrew's libomp is keg-only, so
|
|
# neither libomp.dylib nor omp.h is symlinked into /opt/homebrew and CMake's
|
|
# FindOpenMP cannot find them on its own (the workflow's `brew link libomp`
|
|
# is a no-op for a keg-only formula, and its failure is swallowed).
|
|
# audio.cpp calls find_package(OpenMP REQUIRED COMPONENTS CXX) whenever
|
|
# ENGINE_ENABLE_OPENMP is ON, so with no hint the macOS build dies at
|
|
# configure time before compiling anything. OpenMP_ROOT is honoured by the
|
|
# find_library/find_path calls inside FindOpenMP under CMP0074, which is NEW
|
|
# here because audio.cpp requires CMake 3.20.
|
|
#
|
|
# If the keg is absent, turn OpenMP off rather than fail: the tree's only
|
|
# <omp.h> include is guarded by #ifdef _OPENMP and a #pragma omp without
|
|
# -fopenmp is simply ignored, so an OpenMP-less build is correct, just
|
|
# single-threaded in the host-side DSP loops.
|
|
ifeq ($(origin LIBOMP_PREFIX),undefined)
|
|
LIBOMP_PREFIX := $(shell brew --prefix libomp 2>/dev/null)
|
|
endif
|
|
ifneq ($(and $(wildcard $(LIBOMP_PREFIX)/lib/libomp.dylib),$(wildcard $(LIBOMP_PREFIX)/include/omp.h)),)
|
|
CMAKE_ARGS += -DOpenMP_ROOT=$(LIBOMP_PREFIX)
|
|
else
|
|
CMAKE_ARGS += -DENGINE_ENABLE_OPENMP=OFF
|
|
endif
|
|
else
|
|
# Portable Linux CPU. Upstream wires this to GGML_BACKEND_DL +
|
|
# GGML_CPU_ALL_VARIANTS + $ORIGIN rpath, so one build serves every CPU
|
|
# tier instead of an AVX-tier image fan-out.
|
|
CMAKE_ARGS += -DENGINE_ENABLE_CPU_ALL_VARIANTS=ON
|
|
endif
|
|
|
|
ifneq ($(NATIVE),true)
|
|
CMAKE_ARGS += -DENGINE_ENABLE_NATIVE_CPU=OFF
|
|
endif
|
|
|
|
.PHONY: all grpc-server package test test-engine clean purge
|
|
all: grpc-server
|
|
|
|
# Clone the upstream source at the pinned commit. The directory is the target
|
|
# so make only re-clones when it is missing. After bumping AUDIO_CPP_VERSION,
|
|
# run 'make purge && make' to refetch.
|
|
audio.cpp:
|
|
mkdir -p audio.cpp
|
|
cd audio.cpp && \
|
|
git init -q && \
|
|
git remote add origin $(AUDIO_CPP_REPO) && \
|
|
git fetch --depth 1 origin $(AUDIO_CPP_VERSION) && \
|
|
git checkout FETCH_HEAD
|
|
|
|
grpc-server: audio.cpp
|
|
mkdir -p $(BUILD_DIR)
|
|
cd $(BUILD_DIR) && cmake $(CMAKE_ARGS) $(CURRENT_MAKEFILE_DIR) && \
|
|
cmake --build . --config Release -j $(JOBS)
|
|
cp $(BUILD_DIR)/grpc-server grpc-server
|
|
|
|
package: grpc-server
|
|
bash package.sh
|
|
|
|
test:
|
|
@echo "audio-cpp: standalone unit tests run from the repo root via 'make test-backend-cpp'"
|
|
|
|
# Engine-linked tests. Needs the upstream checkout and a full engine build.
|
|
test-engine: audio.cpp
|
|
mkdir -p $(BUILD_DIR)
|
|
cd $(BUILD_DIR) && cmake $(CMAKE_ARGS) -DAUDIO_CPP_GRPC_BUILD_TESTS=ON $(CURRENT_MAKEFILE_DIR) && \
|
|
cmake --build . --config Release -j $(JOBS) && ctest --output-on-failure --no-tests=error
|
|
|
|
clean:
|
|
rm -rf $(BUILD_DIR) grpc-server package
|
|
|
|
purge: clean
|
|
rm -rf audio.cpp
|