forked from handy-computer/transcribe.cpp
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCMakeLists.txt
More file actions
430 lines (391 loc) · 20.9 KB
/
Copy pathCMakeLists.txt
File metadata and controls
430 lines (391 loc) · 20.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
cmake_minimum_required(VERSION 3.16)
# -----------------------------------------------------------------------------
# Version (single source of truth: include/transcribe.h)
# -----------------------------------------------------------------------------
#
# The TRANSCRIBE_VERSION_MAJOR/MINOR/PATCH macros in the public header are the
# one place the library version is defined. Parse them here, before project(),
# so the CMake project version, the shared-library VERSION/SOVERSION, and the
# value transcribe_version() reports all derive from the same source and cannot
# drift. To bump the library version, edit include/transcribe.h.
file(READ "${CMAKE_CURRENT_SOURCE_DIR}/include/transcribe.h" _transcribe_header)
string(REGEX MATCH "define +TRANSCRIBE_VERSION_MAJOR +([0-9]+)" _ "${_transcribe_header}")
set(TRANSCRIBE_VERSION_MAJOR "${CMAKE_MATCH_1}")
string(REGEX MATCH "define +TRANSCRIBE_VERSION_MINOR +([0-9]+)" _ "${_transcribe_header}")
set(TRANSCRIBE_VERSION_MINOR "${CMAKE_MATCH_1}")
string(REGEX MATCH "define +TRANSCRIBE_VERSION_PATCH +([0-9]+)" _ "${_transcribe_header}")
set(TRANSCRIBE_VERSION_PATCH "${CMAKE_MATCH_1}")
if(NOT TRANSCRIBE_VERSION_MAJOR MATCHES "^[0-9]+$" OR
NOT TRANSCRIBE_VERSION_MINOR MATCHES "^[0-9]+$" OR
NOT TRANSCRIBE_VERSION_PATCH MATCHES "^[0-9]+$")
message(FATAL_ERROR
"Failed to parse TRANSCRIBE_VERSION_{MAJOR,MINOR,PATCH} from "
"include/transcribe.h")
endif()
project(transcribe
VERSION ${TRANSCRIBE_VERSION_MAJOR}.${TRANSCRIBE_VERSION_MINOR}.${TRANSCRIBE_VERSION_PATCH}
LANGUAGES C CXX)
# Short git commit for transcribe_version_commit(), captured at configure time.
# Best-effort: source tarballs and non-git trees fall back to "unknown". This is
# a configure-time snapshot (same posture as ggml's GGML_COMMIT); reconfigure to
# refresh it.
set(TRANSCRIBE_BUILD_COMMIT "unknown")
find_package(Git QUIET)
if(GIT_FOUND AND EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/.git")
execute_process(
COMMAND ${GIT_EXECUTABLE} -C "${CMAKE_CURRENT_SOURCE_DIR}" rev-parse --short HEAD
OUTPUT_VARIABLE _transcribe_git_sha
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET)
if(_transcribe_git_sha)
set(TRANSCRIBE_BUILD_COMMIT "${_transcribe_git_sha}")
endif()
endif()
message(STATUS "transcribe version: ${PROJECT_VERSION} (commit ${TRANSCRIBE_BUILD_COMMIT})")
# -----------------------------------------------------------------------------
# Standards
# -----------------------------------------------------------------------------
set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
endif()
include(GNUInstallDirs)
# Hidden symbol visibility by default per PLAN.md.
set(CMAKE_C_VISIBILITY_PRESET hidden)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN ON)
# -----------------------------------------------------------------------------
# Python wheel build (scikit-build-core sets SKBUILD)
# -----------------------------------------------------------------------------
#
# The repo-root pyproject.toml builds the transcribe-cpp-native provider
# package through scikit-build-core. Force the library shape that package
# ships (shared libtranscribe, no tests/examples/tools) and default to the
# official wheel-hygiene posture: no OpenMP and no non-Apple system BLAS, so
# nothing is vendored that collides with PyTorch/NumPy/MKL in-process, and
# Metal shaders embedded so the artifact has no sidecar files. The hygiene
# defaults (not the shape) can be overridden with -D for local source builds
# that want them. CMP0077 is NEW here, so option() below respects these.
if(SKBUILD)
set(TRANSCRIBE_BUILD_SHARED ON)
set(TRANSCRIBE_BUILD_TESTS OFF)
set(TRANSCRIBE_BUILD_EXAMPLES OFF)
set(TRANSCRIBE_BUILD_TOOLS OFF)
if(NOT DEFINED CACHE{TRANSCRIBE_USE_OPENMP})
set(TRANSCRIBE_USE_OPENMP OFF)
endif()
if(NOT DEFINED CACHE{TRANSCRIBE_USE_SYSTEM_BLAS})
set(TRANSCRIBE_USE_SYSTEM_BLAS OFF)
endif()
if(NOT DEFINED CACHE{GGML_METAL_EMBED_LIBRARY})
set(GGML_METAL_EMBED_LIBRARY ON CACHE BOOL "" FORCE)
endif()
endif()
# -----------------------------------------------------------------------------
# Apple Silicon detection (drives Metal default)
# -----------------------------------------------------------------------------
set(TRANSCRIBE_IS_APPLE_SILICON OFF)
if(APPLE AND CMAKE_SYSTEM_PROCESSOR MATCHES "arm64|aarch64")
set(TRANSCRIBE_IS_APPLE_SILICON ON)
endif()
# -----------------------------------------------------------------------------
# Options
# -----------------------------------------------------------------------------
option(TRANSCRIBE_BUILD_TESTS "Build unit / smoke tests" ON)
option(TRANSCRIBE_BUILD_EXAMPLES "Build example CLI" ON)
option(TRANSCRIBE_BUILD_TOOLS "Build inspect / quantize" OFF) # post pass 2
# Numerical-parity validation hooks: reference-mel injection (TRANSCRIBE_MEL_FROM_REF)
# and per-layer tensor dumps (TRANSCRIBE_DUMP_ALL_BLOCKS / _SUB_BLOCKS). OFF for
# release so the hook code is not compiled into shipped binaries — the env-var
# reads, ref-mel loaders, and dump scaffolding all live behind this guard, so
# their names do not even appear in a release binary's strings. The validation
# pipeline (scripts/validate.py, the Modal sweep image) builds with this ON;
# validate.py hard-fails --mel-from-ref against an OFF build rather than
# silently ignoring it.
option(TRANSCRIBE_ENABLE_VALIDATION_HOOKS
"Compile in numerical-parity validation hooks" OFF)
option(TRANSCRIBE_METAL "Enable Metal backend" ${TRANSCRIBE_IS_APPLE_SILICON})
option(TRANSCRIBE_VULKAN "Enable Vulkan backend" OFF) # post-v1
option(TRANSCRIBE_CUDA "Enable CUDA backend" OFF) # opt-in; Linux + nvcc
option(TRANSCRIBE_SANITIZE "Enable ASan + UBSan" OFF)
option(TRANSCRIBE_LTO "Enable LTO in Release" OFF)
# Library link mode and wheel-hygiene switches.
#
# TRANSCRIBE_BUILD_SHARED OFF (default) builds a static libtranscribe + static
# ggml, so `cmake -B build` yields a self-contained transcribe-cli with no
# libtranscribe/libggml to chase at runtime — the posture every porting, test,
# and bench workflow relies on. The Python wheel/provider build sets it ON to
# produce a shared libtranscribe the FFI layer can dlopen.
#
# TRANSCRIBE_USE_OPENMP defaults OFF: we use ggml's native CPU threadpool
# everywhere (see the OpenMP CENTRAL POLICY below — OpenMP's process-global pool
# is a teardown/coexistence liability for an embeddable library, and the native
# pool is now correct on MSVC and under oversubscription). Opt in with
# -DTRANSCRIBE_USE_OPENMP=ON. TRANSCRIBE_USE_SYSTEM_BLAS stays ON for the
# Accelerate/system-BLAS host decoder; official provider wheels pass it OFF so no
# OpenBLAS/MKL runtime is vendored where it can collide with PyTorch/NumPy.
option(TRANSCRIBE_BUILD_SHARED "Build libtranscribe + ggml as shared libraries" OFF)
option(TRANSCRIBE_USE_OPENMP "Use OpenMP for ggml's CPU threadpool" OFF)
option(TRANSCRIBE_USE_SYSTEM_BLAS "Link non-Apple system BLAS for the host decoder" ON)
# Dynamic ggml backends: each backend (CPU, Vulkan, CUDA, ...) becomes a
# separate loadable module living NEXT TO libtranscribe instead of being
# compiled in. This is the provider-directory artifact shape the Python
# wheels ship on Linux/Windows: the Vulkan module is present by default and
# simply fails to load (skipped; CPU keeps working) on machines without a
# Vulkan loader/driver. The host loads the module directory once via
# transcribe_init_backends(). Requires TRANSCRIBE_BUILD_SHARED=ON.
option(TRANSCRIBE_GGML_BACKEND_DL "Build ggml backends as loadable modules" OFF)
# Conservative x86 floor: default GGML_NATIVE plus EVERY x86 SIMD tier to
# OFF so the one binary runs on baseline x86-64 without SIGILL. This is the
# single switch behind every official-wheel CPU posture (the per-flag lists
# used to be mirrored across the root pyproject lanes, the cu12 pyproject,
# and CMakePresets.json — one option, one place to drift).
#
# It is a DEFAULT, not a clamp (same posture as the SKBUILD hygiene knobs
# above): an explicit -DGGML_AVX2=ON etc. on the command line still wins, so
# a user can take the conservative floor and selectively re-enable tiers for
# their machine. With the option OFF (the default) the raw GGML_* flags are
# untouched and fully tunable, exactly as without this option.
#
# Interaction with GGML_CPU_ALL_VARIANTS=ON (the fat-CPU wheel lanes): ggml
# gates the per-flag options behind `if(NOT GGML_CPU_ALL_VARIANTS)`, so
# these are inert there and serve as the explicit floor if ALL_VARIANTS is
# ever disabled. Inert (harmless) on non-x86 targets: ggml's Linux-ARM tier
# list has its own flags.
option(TRANSCRIBE_X86_CONSERVATIVE
"Default GGML_NATIVE and every x86 SIMD tier to OFF (SIGILL-safe baseline; explicit -DGGML_* still wins)" OFF)
# Install rules for non-Python consumers: headers + the ABI digest +
# libtranscribe + the transcribe-link.json link manifest (ggml's own install
# rules supply the ggml libs/headers). This is what the Rust -sys crate's
# build.rs consumes (`cmake --install` into a staging prefix, then link per
# the manifest) — see cmake/transcribe-install.cmake. Default ON for
# top-level builds; OFF as a subproject, and never active under SKBUILD
# (the wheel ships its own component-filtered install rules instead).
if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR AND NOT SKBUILD)
set(_transcribe_install_default ON)
else()
set(_transcribe_install_default OFF)
endif()
option(TRANSCRIBE_INSTALL
"Install headers, libtranscribe, and the link manifest" ${_transcribe_install_default})
# Real-model gated tests. OFF by default because the tests need a
# converted Parakeet GGUF on disk (~2.4 GB) and CI can't ship one.
# When ON, the test reads TRANSCRIBE_PARAKEET_GGUF from the
# environment at run time. See tests/parakeet_real_smoke.cpp.
option(TRANSCRIBE_BUILD_REAL_MODEL_TESTS
"Build tests that load real (multi-GB) model files via env var" OFF)
# -----------------------------------------------------------------------------
# Warnings (no -Werror in v1 per PLAN.md)
# -----------------------------------------------------------------------------
#
# transcribe_apply_warnings(target): apply our project warning flags to one
# target. Used per-target so we don't pollute the vendored ggml subdirectory
# with -Wshadow / -Wpedantic, which fire on a lot of legitimate ggml code.
function(transcribe_apply_warnings tgt)
if(CMAKE_CXX_COMPILER_ID MATCHES "Clang|GNU")
target_compile_options(${tgt} PRIVATE
-Wall
-Wextra
-Wshadow
-Wpedantic
-Wno-unused-parameter
# The output-side TRANSCRIBE_*_INIT macros (timings,
# stream_update, capabilities, whisper_chunk_trace, ...)
# are deliberate zero-fill aggregate initializers:
# struct_size is set, every other field falls to
# initializer zero by C aggregate-init rules. The
# zero-means-absent contract is documented and load-bearing.
# -Wmissing-field-initializers would fire on every use of
# those macros; the warning's signal-to-noise on this
# codebase is too low to justify keeping it on.
-Wno-missing-field-initializers
)
endif()
endfunction()
# Sanitizers must be applied to every target in the build (including ggml)
# so the resulting binary is consistent. add_compile_options /
# add_link_options is the right scope for this.
#
# -fno-sanitize-recover=undefined makes UBSan reports fatal at runtime
# instead of merely printing them. ASan already aborts on error by
# default; UBSan does not, which would otherwise let ctest report
# success while the sanitizer printed real diagnostics.
if(TRANSCRIBE_SANITIZE)
add_compile_options(
-fsanitize=address,undefined
-fno-sanitize-recover=undefined
# ggml's `incr_ptr_aligned` (ggml/src/ggml.c) uses the
# standard "use NULL as base, walk by offsets to compute
# struct layout sizes" trick. UBSan flags this as a
# `pointer-overflow` runtime error even though it's a
# widely-used and safe-in-practice C idiom. Make this
# specific check non-fatal so the encoder run path doesn't
# abort under build-san; real bugs in our own code still
# fail loud (the rest of -fno-sanitize-recover=undefined
# is unaffected). When ggml gets patched upstream this can
# come back out.
-fsanitize-recover=pointer-overflow
-fno-omit-frame-pointer
)
add_link_options(-fsanitize=address,undefined)
endif()
# -----------------------------------------------------------------------------
# Output layout
# -----------------------------------------------------------------------------
#
# Put all binaries in build/bin/ regardless of which subdirectory built them.
# ggml's CMakeLists already does this when invoked standalone; pinning it
# at the top level keeps the same layout when ggml is added as a subdirectory.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# -----------------------------------------------------------------------------
# Vendored ggml
# -----------------------------------------------------------------------------
#
# ggml lives at the repo root (matching whisper.cpp / llama.cpp). The pinned
# upstream SHA is recorded in ggml/UPSTREAM. We map our TRANSCRIBE_* options
# onto ggml's GGML_* options BEFORE add_subdirectory() so ggml's option()
# calls become no-ops and our defaults win.
set(GGML_METAL ${TRANSCRIBE_METAL} CACHE BOOL "" FORCE)
set(GGML_VULKAN ${TRANSCRIBE_VULKAN} CACHE BOOL "" FORCE)
set(GGML_CUDA ${TRANSCRIBE_CUDA} CACHE BOOL "" FORCE)
set(GGML_BLAS OFF CACHE BOOL "" FORCE)
# tinyBLAS (Justine Tunney's llamafile_sgemm CPU kernels): ~29% faster encoder on
# CPU (q8_0 GEMM), numerically WER-equivalent. On by default; CPU-backend only.
set(GGML_LLAMAFILE ON CACHE BOOL "" FORCE)
# Conservative x86 floor (see the option's comment above): one switch fans
# out to GGML_NATIVE plus the full x86 SIMD tier list. Placed BEFORE
# add_subdirectory(ggml) so ggml's own option() defaults never win — but
# NOT forced: a flag the user already put in the cache (-DGGML_AVX2=ON) is
# left alone, so the floor stays selectively tunable per machine.
if(TRANSCRIBE_X86_CONSERVATIVE)
foreach(_simd_flag
GGML_NATIVE
GGML_SSE42 GGML_AVX GGML_AVX_VNNI GGML_AVX2 GGML_BMI2
GGML_FMA GGML_F16C
GGML_AVX512 GGML_AVX512_VBMI GGML_AVX512_VNNI GGML_AVX512_BF16)
if(NOT DEFINED CACHE{${_simd_flag}})
set(${_simd_flag} OFF CACHE BOOL "")
endif()
endforeach()
endif()
# OpenMP — CENTRAL POLICY.
#
# DEFAULT: OFF. We use ggml's native CPU threadpool everywhere; OpenMP is an
# opt-in (-DTRANSCRIBE_USE_OPENMP=ON), not the default. The TRANSCRIBE_USE_OPENMP
# knob simply drives ggml's GGML_OPENMP.
#
# Why native, not OpenMP — OpenMP's runtime pool is process-global and outlives
# any single compute, which is a liability for an embeddable/dlopen'd library:
# - Teardown crash: a binding (node/koffi, ctypes, ...) calls in on a worker
# thread, libgomp/vcomp spawns its pool there, and at process teardown the
# loader unmaps the runtime out from under those still-live pool threads ->
# SIGSEGV / 0xC0000005. The native pool joins its threads per graph_compute,
# so nothing outlives the call and there is nothing to unmap-race.
# - Coexistence: a vendored libgomp/libiomp can collide with numpy/torch/MKL's
# own OpenMP in one process. The native pool vendors no second runtime.
#
# This used to be a per-consumer SPLIT because ggml's native barrier deadlocked
# under MSVC (its spin `relax` was a no-op on MSVC, so a waiter starved an
# un-arrived worker under oversubscription) — OpenMP was the only working
# multi-threaded CPU path on Windows. That barrier is now fixed (ggml-cpu.c:
# YieldProcessor() relax + a bounded-spin ggml_thread_yield fallback), so the
# native pool is correct on MSVC and under oversubscription. The split is gone:
# - Rust binding: no longer needs -DGGML_OPENMP=ON on Windows; native pool.
# - Python wheels: already vendored no OpenMP; multi-threaded CPU now works on
# every platform (the old "CPU-on-Windows unsupported" limitation is lifted).
# - perf: native persistent/ephemeral pool is on par with OpenMP (it is
# llama.cpp's default). Thread count is sized affinity-aware in
# transcribe-batch-util (default_n_threads) to avoid needless oversubscription.
#
# So: honor an explicit -DGGML_OPENMP=... (opt-in); otherwise force OFF.
if(NOT TRANSCRIBE_USE_OPENMP AND NOT DEFINED CACHE{GGML_OPENMP})
set(GGML_OPENMP OFF CACHE BOOL "" FORCE)
endif()
# We don't want ggml's tests / examples leaking into our build tree. They
# default to OFF when ggml is added as a subdirectory (GGML_STANDALONE OFF),
# but pin them anyway so the contract is explicit.
set(GGML_BUILD_TESTS OFF CACHE BOOL "" FORCE)
set(GGML_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE)
# Library link mode. Default OFF (static) keeps the self-contained CLI/test/
# bench build with no libtranscribe/libggml to chase at runtime; the Python
# wheel/provider build sets TRANSCRIBE_BUILD_SHARED=ON for a shared
# libtranscribe (+ libggml) the FFI layer can dlopen. Both ggml and
# libtranscribe follow this single switch. Symbol visibility is already hidden
# by default (set above), so a shared lib exports only the TRANSCRIBE_API
# surface.
set(BUILD_SHARED_LIBS ${TRANSCRIBE_BUILD_SHARED} CACHE BOOL "" FORCE)
# Dynamic backend modules (see the option's comment above). ggml requires
# shared libs for GGML_BACKEND_DL, so gate it on TRANSCRIBE_BUILD_SHARED
# here for a clear configure-time error.
if(TRANSCRIBE_GGML_BACKEND_DL)
if(NOT TRANSCRIBE_BUILD_SHARED)
message(FATAL_ERROR
"TRANSCRIBE_GGML_BACKEND_DL requires TRANSCRIBE_BUILD_SHARED=ON "
"(backend modules are shared libraries loaded at runtime)")
endif()
set(GGML_BACKEND_DL ON CACHE BOOL "" FORCE)
# The package-local default loader resolves the directory that contains
# libtranscribe itself. Install backend modules there too, unless a caller
# explicitly chose a different provider layout and will pass it to
# transcribe_init_backends(dir).
#
# Test emptiness, not DEFINED: ggml declares GGML_BACKEND_DIR as an empty
# cache var of its own, so on any reconfigure it is already DEFINED (as "")
# and a `NOT DEFINED CACHE{...}` guard would skip our override, leaving the
# modules in ggml's default bin/ where the lib-dir loader cannot find them.
# `NOT GGML_BACKEND_DIR` treats an empty/unset value as "ours to set" while
# still respecting a non-empty caller-provided path.
if(NOT GGML_BACKEND_DIR)
# The directory that holds libtranscribe itself — which is where the
# package-local loader scans. That's bin/ on Windows (a DLL is a RUNTIME
# artifact installed to bin, so transcribe.dll lives there) and lib/
# elsewhere. Using lib/ unconditionally would strand the modules away
# from transcribe.dll on Windows, where the loader can't find them.
if(WIN32)
set(GGML_BACKEND_DIR "${CMAKE_INSTALL_BINDIR}" CACHE PATH "" FORCE)
else()
set(GGML_BACKEND_DIR "${CMAKE_INSTALL_LIBDIR}" CACHE PATH "" FORCE)
endif()
endif()
# ggml hard-errors on GGML_NATIVE + GGML_BACKEND_DL (x86): native tuning
# is incompatible with feature-scored modules. A module build is a
# distribution build, so never tune it to the build host.
set(GGML_NATIVE OFF CACHE BOOL "" FORCE)
endif()
# MSVC: use /EHs so exceptions thrown by ggml's C-ABI frames unwind to
# transcribe.cpp's public-entry guards. Apply before ggml and src are added.
if(MSVC)
string(REPLACE "/EHsc" "/EHs" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
if(NOT CMAKE_CXX_FLAGS MATCHES "/EHs")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHs")
endif()
endif()
# ggml's CMake declares its own warning flags; let it.
add_subdirectory(ggml)
# -----------------------------------------------------------------------------
# Subdirs
# -----------------------------------------------------------------------------
add_subdirectory(src)
if(TRANSCRIBE_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()
if(TRANSCRIBE_BUILD_TESTS)
enable_testing()
add_subdirectory(tests)
endif()
if(TRANSCRIBE_BUILD_TOOLS)
add_subdirectory(tools)
endif()
# Python provider-wheel packaging (after subdirs: needs the target list and
# ggml's GGML_AVAILABLE_BACKENDS).
if(SKBUILD)
include(cmake/python-wheel-install.cmake)
endif()
# Consumer install rules (same placement constraint as above).
if(TRANSCRIBE_INSTALL AND NOT SKBUILD)
include(cmake/transcribe-install.cmake)
endif()