diff --git a/.gitignore b/.gitignore index 6d88cac..ab15505 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ cmake_install.cmake install_manifest.txt *.cmake !CMakeLists.txt +!/cmake/*.cmake # Python __pycache__/ diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index ff7c76a..30134f0 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -227,6 +227,9 @@ These are the touch points the German addition went through, in order: **HTTP server (`src/server/`).** A Rust crate (axum + tokio) that links `libkokoro` through hand-written C API declarations (`ffi.rs`) and serves an OpenAI-compatible API. CMake builds it with cargo (`KOKORO_BUILD_SERVER`) and copies `kokoro-server` next to `libkokoro`; `build.rs` links against the library and sets the rpath. +`cmake/KokoroRustTarget.cmake` maps the CMake target (not the host) to a Rust triple that cargo builds with `--target`. +`-DKOKORO_RUST_TARGET=` overrides it. Targets with no mapping (Android, iOS, macOS universal) default the +server OFF, and a triple whose Rust standard library is missing fails at configure time. - `Engine` (`engine.rs`) owns the one context; `Audio` frees the samples with `kokoro_audio_free` on drop. - `AppState` (`routes.rs`) keeps the engine behind a `tokio::sync::Mutex`. A request waits for the lock without a thread, then synthesizes in `spawn_blocking`, which also reads the thread-local `kokoro_last_error()`. diff --git a/CMakeLists.txt b/CMakeLists.txt index a060404..21d707c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -187,27 +187,55 @@ if (CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR) else() set(KOKORO_IS_TOP_LEVEL OFF) endif() -# HTTP server: a Rust crate in src/server, built by cargo against libkokoro and copied next to it. -option(KOKORO_BUILD_SERVER "Build kokoro-server (needs a Rust toolchain)" ${KOKORO_IS_TOP_LEVEL}) +# HTTP server: a Rust crate in src/server, built by cargo for the CMake target's Rust triple and copied next to +# libkokoro. +include(cmake/KokoroRustTarget.cmake) +set(KOKORO_RUST_TARGET "" CACHE STRING "Rust target triple for kokoro-server (empty: derived from the CMake target)") +if (KOKORO_RUST_TARGET) + set(KOKORO_RUST_TRIPLE "${KOKORO_RUST_TARGET}") +else() + kokoro_rust_target(KOKORO_RUST_TRIPLE) +endif() +if (KOKORO_IS_TOP_LEVEL AND KOKORO_RUST_TRIPLE) + set(kokoro_server_default ON) +else() + set(kokoro_server_default OFF) +endif() +option(KOKORO_BUILD_SERVER "Build kokoro-server (needs a Rust toolchain)" ${kokoro_server_default}) if (KOKORO_BUILD_SERVER) + if (NOT KOKORO_RUST_TRIPLE) + message(FATAL_ERROR "kokoro-server: no Rust target triple for ${CMAKE_SYSTEM_NAME}/${CMAKE_SYSTEM_PROCESSOR}; " + "set -DKOKORO_RUST_TARGET= or configure with -DKOKORO_BUILD_SERVER=OFF") + endif() find_program(CARGO cargo HINTS "$ENV{CARGO_HOME}/bin" "$ENV{HOME}/.cargo/bin" "$ENV{USERPROFILE}/.cargo/bin") if (NOT CARGO) message(FATAL_ERROR "cargo not found: install Rust (https://rustup.rs) or configure with -DKOKORO_BUILD_SERVER=OFF") endif() + kokoro_require_rust_std("${CARGO}" "${KOKORO_RUST_TRIPLE}") + message(STATUS "kokoro-server Rust target: ${KOKORO_RUST_TRIPLE}") set(KOKORO_CARGO_TARGET_DIR "${CMAKE_BINARY_DIR}/cargo") set(KOKORO_CARGO_PROFILE "$,dev,release>") + # cargo --target puts artifacts in //. + set(KOKORO_CARGO_OUT_DIR "${KOKORO_CARGO_TARGET_DIR}/${KOKORO_RUST_TRIPLE}/$,debug,release>") set(KOKORO_CARGO_ENV "CARGO_TARGET_DIR=${KOKORO_CARGO_TARGET_DIR}" "KOKORO_LIB_DIR=$" "KOKORO_INSTALL_LIBDIR=${CMAKE_INSTALL_LIBDIR}") + # Non-MSVC-ABI cross toolchains: link with the CMake C compiler unless the caller already chose a linker. + string(TOUPPER "${KOKORO_RUST_TRIPLE}" kokoro_triple_env) + string(REPLACE "-" "_" kokoro_triple_env "${kokoro_triple_env}") + if (CMAKE_CROSSCOMPILING AND NOT KOKORO_RUST_TRIPLE MATCHES "-msvc$" + AND NOT DEFINED ENV{CARGO_TARGET_${kokoro_triple_env}_LINKER}) + list(APPEND KOKORO_CARGO_ENV "CARGO_TARGET_${kokoro_triple_env}_LINKER=${CMAKE_C_COMPILER}") + endif() set(KOKORO_SERVER_EXE "kokoro-server${CMAKE_EXECUTABLE_SUFFIX}") add_custom_target(kokoro_server ALL COMMAND ${CMAKE_COMMAND} -E env ${KOKORO_CARGO_ENV} - "${CARGO}" build --locked --profile ${KOKORO_CARGO_PROFILE} + "${CARGO}" build --locked --profile ${KOKORO_CARGO_PROFILE} --target ${KOKORO_RUST_TRIPLE} --manifest-path "${CMAKE_CURRENT_SOURCE_DIR}/src/server/Cargo.toml" COMMAND ${CMAKE_COMMAND} -E copy_if_different - "${KOKORO_CARGO_TARGET_DIR}/$,debug,release>/${KOKORO_SERVER_EXE}" + "${KOKORO_CARGO_OUT_DIR}/${KOKORO_SERVER_EXE}" "$/${KOKORO_SERVER_EXE}" COMMENT "Building kokoro-server with cargo" VERBATIM diff --git a/README.md b/README.md index f49ff0f..bab3c03 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ A high-performance, lightweight C++ inference implementation of the [Kokoro](htt - **CMake** (3.15+) - **Ninja** (recommended; `uv run bootstrap.py build` uses it when it is in `PATH`) - **C++ compiler** (C++17 support required) -- **Rust** (1.80+, via [rustup](https://rustup.rs)): builds `kokoro-server`; configure with `-DKOKORO_BUILD_SERVER=OFF` to skip it +- **Rust** (1.80+, via [rustup](https://rustup.rs)): builds `kokoro-server` for the CMake target (cross builds need `rustup target add `); configure with `-DKOKORO_BUILD_SERVER=OFF` to skip it - **[uv](https://docs.astral.sh/uv/)**: runs `bootstrap.py` and `voice_tool.py`, and installs their Python dependencies (Python 3.10+, click) into `.venv` on first use ## Setup @@ -270,7 +270,7 @@ MIT - **CMake** (3.15+) - **Ninja**(推荐;`uv run bootstrap.py build` 在 `PATH` 中找到它时使用) - **C++ 编译器** (需要支持 C++17) -- **Rust**(1.80+,通过 [rustup](https://rustup.rs) 安装):用于构建 `kokoro-server`;配置时加 `-DKOKORO_BUILD_SERVER=OFF` 可跳过 +- **Rust**(1.80+,通过 [rustup](https://rustup.rs) 安装):为 CMake 目标平台构建 `kokoro-server`(交叉编译需先运行 `rustup target add `);配置时加 `-DKOKORO_BUILD_SERVER=OFF` 可跳过 - **[uv](https://docs.astral.sh/uv/)**:用于运行 `bootstrap.py` 和 `voice_tool.py`,首次运行时会将其 Python 依赖(Python 3.10+、click)安装到 `.venv` ## 准备 @@ -496,7 +496,7 @@ Implementación ligera y de alto rendimiento en C++ de la inferencia del modelo - **CMake** (3.15+) - **Ninja** (recomendado; `uv run bootstrap.py build` lo usa si está en el `PATH`) - **Compilador de C++** (con soporte de C++17) -- **Rust** (1.80+, con [rustup](https://rustup.rs)): compila `kokoro-server`; configura con `-DKOKORO_BUILD_SERVER=OFF` para omitirlo +- **Rust** (1.80+, con [rustup](https://rustup.rs)): compila `kokoro-server` para la plataforma destino de CMake (la compilación cruzada necesita `rustup target add `); configura con `-DKOKORO_BUILD_SERVER=OFF` para omitirlo - **[uv](https://docs.astral.sh/uv/)**: ejecuta `bootstrap.py` y `voice_tool.py`, e instala sus dependencias de Python (Python 3.10+, click) en `.venv` la primera vez ## Preparación @@ -743,7 +743,7 @@ Eine schnelle, schlanke C++-Implementierung der Inferenz des TTS-Modells [Kokoro - **CMake** (3.15+) - **Ninja** (empfohlen; `uv run bootstrap.py build` verwendet es, wenn es im `PATH` liegt) - **C++-Compiler** (mit C++17-Unterstützung) -- **Rust** (1.80+, über [rustup](https://rustup.rs)): baut `kokoro-server`; mit `-DKOKORO_BUILD_SERVER=OFF` konfigurieren, um es auszulassen +- **Rust** (1.80+, über [rustup](https://rustup.rs)): baut `kokoro-server` für die CMake-Zielplattform (Cross-Builds brauchen `rustup target add `); mit `-DKOKORO_BUILD_SERVER=OFF` konfigurieren, um es auszulassen - **[uv](https://docs.astral.sh/uv/)**: führt `bootstrap.py` und `voice_tool.py` aus und installiert beim ersten Aufruf ihre Python-Abhängigkeiten (Python 3.10+, click) in `.venv` ## Einrichtung diff --git a/cmake/KokoroRustTarget.cmake b/cmake/KokoroRustTarget.cmake new file mode 100644 index 0000000..085e359 --- /dev/null +++ b/cmake/KokoroRustTarget.cmake @@ -0,0 +1,74 @@ +# Maps the CMake target (not the host) to a Rust target triple for kokoro-server. + +# MSVC ABI: cl, clang-cl, and GNU-frontend clang++ targeting *-windows-msvc (where CMake leaves MSVC false). +function(_kokoro_msvc_abi out_var) + if (MSVC OR CMAKE_CXX_SIMULATE_ID STREQUAL "MSVC") + set(${out_var} TRUE PARENT_SCOPE) + else() + set(${out_var} FALSE PARENT_SCOPE) + endif() +endfunction() + +# Rust arch for the target, or "" when unknown or when several macOS architectures are requested. +function(_kokoro_rust_arch out_var) + _kokoro_msvc_abi(msvc_abi) + if (msvc_abi) + set(arch_id "${MSVC_CXX_ARCHITECTURE_ID}") + elseif (APPLE AND CMAKE_OSX_ARCHITECTURES) + set(arch_id "${CMAKE_OSX_ARCHITECTURES}") + else() + set(arch_id "${CMAKE_SYSTEM_PROCESSOR}") + endif() + string(TOLOWER "${arch_id}" arch_id) + if (arch_id MATCHES "^(x64|x86_64|amd64)$") + set(arch x86_64) + elseif (arch_id MATCHES "^(arm64|aarch64)$") + set(arch aarch64) + elseif (arch_id MATCHES "^(x86|i[3-6]86)$") + set(arch i686) + else() + set(arch "") + endif() + set(${out_var} "${arch}" PARENT_SCOPE) +endfunction() + +# Rust vendor-os(-env) suffix for the target, or "" when unsupported (Android, iOS, BSDs, ...). +function(_kokoro_rust_os out_var) + _kokoro_msvc_abi(msvc_abi) + if (WIN32 AND msvc_abi) + set(os pc-windows-msvc) + elseif (WIN32 AND MINGW) + set(os pc-windows-gnu) + elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") + set(os apple-darwin) + elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(os unknown-linux-gnu) + else() + set(os "") + endif() + set(${out_var} "${os}" PARENT_SCOPE) +endfunction() + +# Sets out_var to the Rust triple for the CMake target, or "" when there is no mapping. +function(kokoro_rust_target out_var) + _kokoro_rust_arch(arch) + _kokoro_rust_os(os) + if (arch AND os) + set(${out_var} "${arch}-${os}" PARENT_SCOPE) + else() + set(${out_var} "" PARENT_SCOPE) + endif() +endfunction() + +# Fails configuration unless the rustc next to cargo has the standard library for the triple. +function(kokoro_require_rust_std cargo triple) + get_filename_component(cargo_dir "${cargo}" DIRECTORY) + find_program(KOKORO_RUSTC rustc HINTS "${cargo_dir}") + execute_process(COMMAND "${KOKORO_RUSTC}" --print target-libdir --target "${triple}" + OUTPUT_VARIABLE libdir OUTPUT_STRIP_TRAILING_WHITESPACE + RESULT_VARIABLE status ERROR_QUIET) + if (NOT status EQUAL 0 OR NOT IS_DIRECTORY "${libdir}") + message(FATAL_ERROR "Rust has no standard library for ${triple}: run `rustup target add ${triple}`, " + "set -DKOKORO_RUST_TARGET=, or configure with -DKOKORO_BUILD_SERVER=OFF") + endif() +endfunction() diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4f66705..1e9c4cf 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -35,15 +35,15 @@ if (KOKORO_BUILD_SERVER) # Windows has no rpath: put kokoro.dll and the ONNX Runtime DLLs next to cargo's test executables. add_custom_target(server_test_dlls ALL COMMAND ${CMAKE_COMMAND} -E make_directory - "${KOKORO_CARGO_TARGET_DIR}/$,debug,release>/deps" + "${KOKORO_CARGO_OUT_DIR}/deps" COMMAND ${CMAKE_COMMAND} -E copy_if_different $ ${ONNXRUNTIME_RUNTIME_DLLS} - "${KOKORO_CARGO_TARGET_DIR}/$,debug,release>/deps" + "${KOKORO_CARGO_OUT_DIR}/deps" VERBATIM) add_dependencies(server_test_dlls kokoro) endif() add_test(NAME server COMMAND ${CMAKE_COMMAND} -E env ${server_test_env} - "${CARGO}" test --locked --profile ${KOKORO_CARGO_PROFILE} + "${CARGO}" test --locked --profile ${KOKORO_CARGO_PROFILE} --target ${KOKORO_RUST_TRIPLE} --manifest-path "${PROJECT_SOURCE_DIR}/src/server/Cargo.toml") endif()