go-whisper-api/Makefile
admin b5c083e06f
Some checks failed
CodeQL / Analyze (go) (push) Successful in 6m28s
Docker Image / build-docker (push) Failing after 13m26s
Lint and Testing / lint (push) Successful in 11m17s
Lint and Testing / test (push) Successful in 11m17s
Lint and Testing / golangci (push) Successful in 2m40s
first commit
2026-06-04 18:10:52 +07:00

281 lines
12 KiB
Makefile

EXECUTABLE := go-whisper-api
GO ?= go
GOFILES := $(shell find . -name "*.go" -type f)
HAS_GO = $(shell hash $(GO) > /dev/null 2>&1 && echo "GO" || echo "NOGO" )
WHISPER_CPP := $(abspath third_party/whisper.cpp)
WHISPER_BUILD := $(WHISPER_CPP)/build
WHISPER_VENDOR := $(WHISPER_CPP)/bindings/go
WHISPER_LIBDIR := $(WHISPER_BUILD)/src:$(WHISPER_BUILD)/ggml/src
RUNTIME_LIB_DIR := $(abspath lib)
# $ORIGIN/lib — binary next to lib/ (e.g. ./go-whisper-api + ./lib/)
# $ORIGIN/../lib — binary in bin/ (e.g. bin/go-whisper-api + lib/)
RUNTIME_RPATH := -Wl,-rpath,$$ORIGIN/lib:$$ORIGIN/../lib
ifneq ($(shell uname), Darwin)
EXTLDFLAGS = -extldflags "$(RUNTIME_RPATH)"
else
EXTLDFLAGS =
endif
ifeq ($(HAS_GO), GO)
GOPATH ?= $(shell $(GO) env GOPATH)
export PATH := $(GOPATH)/bin:$(PATH)
CGO_EXTRA_CFLAGS := -DSQLITE_MAX_VARIABLE_NUMBER=32766
CGO_CFLAGS ?= $(shell $(GO) env CGO_CFLAGS) $(CGO_EXTRA_CFLAGS)
endif
ifeq ($(OS), Windows_NT)
GOFLAGS := -v -buildmode=exe
EXECUTABLE ?= $(EXECUTABLE).exe
else ifeq ($(OS), Windows)
GOFLAGS := -v -buildmode=exe
EXECUTABLE ?= $(EXECUTABLE).exe
else
GOFLAGS := -v
EXECUTABLE ?= $(EXECUTABLE)
endif
ifneq ($(DRONE_TAG),)
VERSION ?= $(DRONE_TAG)
else
VERSION ?= $(shell git describe --tags --always || git rev-parse --short HEAD)
endif
TAGS ?=
UNAME_M := $(shell uname -m)
ifeq ($(UNAME_M),x86_64)
SHERPA_LIBARCH := x86_64-unknown-linux-gnu
endif
ifeq ($(UNAME_M),aarch64)
SHERPA_LIBARCH := aarch64-unknown-linux-gnu
endif
SHERPA_LINUX_VER := $(shell awk '/sherpa-onnx-go-linux/ {print $$2; exit}' go.mod)
SHERPA_LIBDIR := $(GOPATH)/pkg/mod/github.com/k2-fsa/sherpa-onnx-go-linux@$(SHERPA_LINUX_VER)/lib/$(SHERPA_LIBARCH)
ifneq ($(shell uname), Darwin)
EXTLDFLAGS_SHERPA = -extldflags "$(RUNTIME_RPATH)"
EXTLDFLAGS_XLM = -extldflags "$(RUNTIME_RPATH)"
else
EXTLDFLAGS_SHERPA =
EXTLDFLAGS_XLM =
endif
GOLDFLAGS ?= -X 'main.Version=$(VERSION)'
INCLUDE_PATH := $(WHISPER_CPP)/include:$(WHISPER_CPP)/ggml/include:$(WHISPER_VENDOR):$(INCLUDE_PATH)
LIBRARY_PATH := $(WHISPER_LIBDIR):$(LIBRARY_PATH)
export LD_LIBRARY_PATH := $(WHISPER_LIBDIR):$(LD_LIBRARY_PATH)
ifdef WHISPER_CUBLAS
CGO_CFLAGS += -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/opt/cuda/include -I$(CUDA_PATH)/targets/$(UNAME_M)-linux/include
CGO_CXXFLAGS += -DGGML_USE_CUBLAS -I/usr/local/cuda/include -I/opt/cuda/include -I$(CUDA_PATH)/targets/$(UNAME_M)-linux/include
EXTLDFLAGS = -extldflags "-lcuda -lcublas -lculibos -lcudart -lcublasLt -lpthread -ldl -lrt -L/usr/local/cuda/lib64 -L/opt/cuda/lib64 -L$(CUDA_PATH)/targets/$(UNAME_M)-linux/lib"
build: $(EXECUTABLE)
$(EXECUTABLE): $(GOFILES)
CGO_CXXFLAGS=${CGO_CXXFLAGS} CGO_CFLAGS=${CGO_CFLAGS} C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} $(GO) build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(GOLDFLAGS)' -o bin/$@
endif
MODEL_URL ?= https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.en.bin
MODEL_PATH ?= models/ggml-tiny.en.bin
VAD_MODEL ?= silero-v6.2.0
VAD_MODEL_PATH ?= models/ggml-silero-v6.2.0.bin
all: build
PUNCT_MODEL_URL ?= https://github.com/k2-fsa/sherpa-onnx/releases/download/punctuation-models/sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8.tar.bz2
PUNCT_MODEL_DIR ?= models/punctuation/ct-transformer-zh-en-int8
XLM_PUNCT_DIR ?= models/punctuation/xlm-roberta
XLM_HF_REPO ?= Salama1429/xlm-roberta_punctuation_fullstop_truecase
XLM_MODEL_CONFIG_SRC ?= config/xlm-roberta-model.yaml
ORT_LIB_SRC ?= $(shell $(GO) env GOMODCACHE 2>/dev/null)/github.com/k2-fsa/sherpa-onnx-go-linux@$(SHERPA_LINUX_VER)/lib/$(SHERPA_LIBARCH)/libonnxruntime.so
# Copy runtime .so into ./lib/. Binary rpath: $ORIGIN/lib or $ORIGIN/../lib (see RUNTIME_RPATH).
# Use cp -n where possible: existing root-owned libs in lib/ must not break the build.
install-runtime-libs: dependency
@mkdir -p "$(RUNTIME_LIB_DIR)"
@cp -an "$(WHISPER_BUILD)/src"/libwhisper.so* "$(RUNTIME_LIB_DIR)/" 2>/dev/null || true
@cp -an "$(WHISPER_BUILD)/ggml/src"/libggml*.so* "$(RUNTIME_LIB_DIR)/" 2>/dev/null || true
@echo "Whisper/ggml libs ready in $(RUNTIME_LIB_DIR)/"
install-ort-lib:
@mkdir -p "$(RUNTIME_LIB_DIR)"
@if [ ! -f "$(ORT_LIB_SRC)" ]; then echo "missing $(ORT_LIB_SRC); run: go mod download"; exit 1; fi
@dest="$(RUNTIME_LIB_DIR)/libonnxruntime.so"; \
if [ -f "$$dest" ] && cmp -s "$(ORT_LIB_SRC)" "$$dest"; then \
echo "libonnxruntime.so already up to date in $(RUNTIME_LIB_DIR)/"; \
elif cp -f "$(ORT_LIB_SRC)" "$$dest" 2>/dev/null; then \
echo "Installed $$dest"; \
elif [ -f "$$dest" ] && cmp -s "$(ORT_LIB_SRC)" "$$dest"; then \
echo "libonnxruntime.so present in $(RUNTIME_LIB_DIR)/ (unchanged, not writable)"; \
else \
echo "cannot install libonnxruntime.so to $$dest"; \
echo "fix: sudo chown -R $$USER:$$(id -gn) $(RUNTIME_LIB_DIR)"; \
exit 1; \
fi
# XLM punctuation links -lsentencepiece; bundle .so for hosts without libsentencepiece0 package.
SP_LIB_DIRS := /usr/lib/x86_64-linux-gnu /usr/lib/aarch64-linux-gnu /usr/lib64 /usr/lib
install-sp-lib:
@mkdir -p "$(RUNTIME_LIB_DIR)"
@found=0; \
for d in $(SP_LIB_DIRS); do \
if [ -e "$$d/libsentencepiece.so.0" ] || [ -L "$$d/libsentencepiece.so.0" ]; then \
cp -an "$$d"/libsentencepiece.so* "$(RUNTIME_LIB_DIR)/" 2>/dev/null || true; \
found=1; \
break; \
fi; \
done; \
if [ "$$found" = "0" ]; then \
echo "libsentencepiece.so.0 not found; install: sudo apt-get install libsentencepiece0"; \
exit 1; \
fi
@test -e "$(RUNTIME_LIB_DIR)/libsentencepiece.so.0" || (echo "missing $(RUNTIME_LIB_DIR)/libsentencepiece.so.0 after install-sp-lib"; exit 1)
@echo "Sentencepiece libs ready in $(RUNTIME_LIB_DIR)/"
# If lib/*.so were created as root (e.g. manual cp with sudo), reclaim ownership for builds.
fix-lib-perms:
@if [ -d "$(RUNTIME_LIB_DIR)" ]; then \
chown -R "$$USER:$$(id -gn)" "$(RUNTIME_LIB_DIR)" 2>/dev/null || \
sudo chown -R "$$USER:$$(id -gn)" "$(RUNTIME_LIB_DIR)"; \
echo "Ownership of $(RUNTIME_LIB_DIR)/ updated"; \
fi
install-runtime-libs-xlm: install-runtime-libs install-ort-lib install-sp-lib
# Fail fast before deploy if ./lib is incomplete (lib/ is not in git: *.so is gitignored).
verify-runtime-libs-xlm:
@test -f bin/$(EXECUTABLE) || (echo "missing bin/$(EXECUTABLE); run: make build-xlm"; exit 1)
@test -f "$(RUNTIME_LIB_DIR)/libonnxruntime.so" || (echo "missing $(RUNTIME_LIB_DIR)/libonnxruntime.so; run: make install-runtime-libs-xlm"; exit 1)
@test -e "$(RUNTIME_LIB_DIR)/libsentencepiece.so.0" || (echo "missing $(RUNTIME_LIB_DIR)/libsentencepiece.so.0; run: make install-sp-lib"; exit 1)
@test -e "$(RUNTIME_LIB_DIR)/libwhisper.so.1" || (echo "missing $(RUNTIME_LIB_DIR)/libwhisper.so.1; run: make install-runtime-libs"; exit 1)
@echo "Runtime libs OK in $(RUNTIME_LIB_DIR)/"
RUNTIME_TARBALL := dist/go-whisper-api-runtime-$(shell uname -m).tar.gz
package-runtime-xlm: verify-runtime-libs-xlm
@mkdir -p dist
tar -czf "$(RUNTIME_TARBALL)" bin/$(EXECUTABLE) lib
@echo "Created $(RUNTIME_TARBALL) — on prod: tar -xzf ... -C /opt/go-whisper-api (keeps bin/ and lib/)"
# Copy bundled label config (needs write access to $(XLM_PUNCT_DIR); fix with: sudo chown -R $$USER models/punctuation)
install-xlm-punctuation-config:
@mkdir -p $(XLM_PUNCT_DIR)
@cp "$(XLM_MODEL_CONFIG_SRC)" "$(XLM_PUNCT_DIR)/config.yaml"
@echo "Installed $(XLM_PUNCT_DIR)/config.yaml"
download-xlm-punctuation-model: install-xlm-punctuation-config
@mkdir -p $(XLM_PUNCT_DIR)
@for f in model.onnx sp.model; do \
if [ ! -f "$(XLM_PUNCT_DIR)/$$f" ]; then \
echo "Downloading $$f from $(XLM_HF_REPO)..."; \
curl -fL "https://huggingface.co/$(XLM_HF_REPO)/resolve/main/$$f" -o "$(XLM_PUNCT_DIR)/$$f"; \
else \
echo "Already have $(XLM_PUNCT_DIR)/$$f"; \
fi; \
done
DIAR_SEG_URL ?= https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-segmentation-models/sherpa-onnx-pyannote-segmentation-3-0.tar.bz2
DIAR_EMB_URL ?= https://github.com/k2-fsa/sherpa-onnx/releases/download/speaker-recongition-models/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx
DIAR_DIR ?= models/diarization
download-diarization-models:
@mkdir -p $(DIAR_DIR)
@if [ ! -f "$(DIAR_DIR)/pyannote-segmentation-3-0/model.onnx" ]; then \
echo "Downloading speaker segmentation model..."; \
curl -fL "$(DIAR_SEG_URL)" -o /tmp/diar-seg.tar.bz2; \
tar -xjf /tmp/diar-seg.tar.bz2 -C $(DIAR_DIR); \
rm -f /tmp/diar-seg.tar.bz2; \
else \
echo "Segmentation model present"; \
fi
@if [ ! -f "$(DIAR_DIR)/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx" ]; then \
echo "Downloading speaker embedding model..."; \
curl -fL "$(DIAR_EMB_URL)" -o "$(DIAR_DIR)/3dspeaker_speech_eres2net_base_sv_zh-cn_3dspeaker_16k.onnx"; \
else \
echo "Embedding model present"; \
fi
download-punctuation-model:
@mkdir -p models/punctuation
@if [ ! -f "$(PUNCT_MODEL_DIR)/model.int8.onnx" ]; then \
echo "Downloading punctuation model..."; \
curl -fL "$(PUNCT_MODEL_URL)" -o /tmp/punct-model.tar.bz2; \
tar -xjf /tmp/punct-model.tar.bz2 -C models/punctuation; \
rm -f /tmp/punct-model.tar.bz2; \
if [ -d models/punctuation/sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8 ]; then \
mv models/punctuation/sherpa-onnx-punct-ct-transformer-zh-en-vocab272727-2024-04-12-int8 "$(PUNCT_MODEL_DIR)"; \
fi; \
else \
echo "Punctuation model already exists: $(PUNCT_MODEL_DIR)/model.int8.onnx"; \
fi
download-model:
@mkdir -p models
@if [ ! -f "$(MODEL_PATH)" ]; then \
echo "Downloading $(MODEL_PATH)..."; \
curl -fL "$(MODEL_URL)" -o "$(MODEL_PATH)"; \
else \
echo "Model already exists: $(MODEL_PATH)"; \
fi
download-vad-model:
@mkdir -p models
@if [ ! -f "$(VAD_MODEL_PATH)" ]; then \
echo "Downloading VAD model $(VAD_MODEL) to models/..."; \
./third_party/whisper.cpp/models/download-vad-model.sh $(VAD_MODEL) models; \
else \
echo "VAD model already exists: $(VAD_MODEL_PATH)"; \
fi
clone:
@[ -d third_party/whisper.cpp ] || git clone https://github.com/appleboy/whisper.cpp.git third_party/whisper.cpp
dependency: clone
@echo Build whisper
@if [ ! -f "$(WHISPER_BUILD)/src/libwhisper.so" ] && [ ! -f "$(WHISPER_BUILD)/src/libwhisper.a" ]; then \
cmake -S "$(WHISPER_CPP)" -B "$(WHISPER_BUILD)" -DCMAKE_BUILD_TYPE=Release && \
cmake --build "$(WHISPER_BUILD)" --config Release -j$$(nproc 2>/dev/null || echo 4); \
else \
echo "whisper library already built in $(WHISPER_BUILD)"; \
fi
test:
@C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} $(GO) test -v -cover -coverprofile coverage.txt ./... && echo "\n==>\033[32m Ok\033[m\n" || exit 1
install: $(GOFILES)
C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} $(GO) install -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(GOLDFLAGS)'
build: install-runtime-libs $(EXECUTABLE)
# Build with sherpa-onnx (punctuation + speaker diarization)
build-sherpa:
@$(MAKE) build TAGS=sherpa
# XLM-RoBERTa punctuation (47 languages); requires libsentencepiece-dev
build-xlm:
@$(MAKE) install-runtime-libs-xlm
@$(MAKE) build TAGS=xlm
$(EXECUTABLE): $(GOFILES)
ifneq (,$(findstring xlm,$(TAGS)))
C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=$(SHERPA_LIBDIR):${LIBRARY_PATH} $(GO) build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS_XLM) -s -w $(GOLDFLAGS)' -o bin/$@
else ifneq (,$(findstring sherpa,$(TAGS)))
C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=$(SHERPA_LIBDIR):${LIBRARY_PATH} $(GO) build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS_SHERPA) -s -w $(GOLDFLAGS)' -o bin/$@
else
C_INCLUDE_PATH=${INCLUDE_PATH} LIBRARY_PATH=${LIBRARY_PATH} $(GO) build -v -tags '$(TAGS)' -ldflags '$(EXTLDFLAGS)-s -w $(GOLDFLAGS)' -o bin/$@
endif
clean:
$(GO) clean -x -i ./...
rm -rf coverage.txt $(EXECUTABLE) $(DIST) bin/$(EXECUTABLE)
clean-whisper:
rm -rf "$(WHISPER_BUILD)"
version:
@echo $(VERSION)