Building a distributable binary

dengjen-espeak-rs-sys’s build script compiles eSpeak NG’s data files (dictionaries, voices, phoneme tables) and, since it needs some directory to build into, generates them inside `target/<profile>/build/dengjen-espeak-rs-sys-<hash>/out/…​ — an ephemeral, cache-keyed path that disappears the moment target/ is deleted or the crate is rebuilt with a different hash. Copying only the compiled binary out of target/ and discarding the rest (e.g. cargo build --release followed by rm -rf target) breaks it at runtime with:

Failed to initialize eSpeak-ng. Try setting `PIPER_ESPEAKNG_DATA_DIRECTORY`
to the directory that contains the `espeak-ng-data` directory.

To make this easy, the build script bakes that build-cache path into a compile-time constant inside the compiled dengjen-espeak-rs-sys crate, instead of copying data next to the binary. At runtime, dengjen-espeak-rs looks for espeak-ng-data (in this order) in:

  1. the directory named by the PIPER_ESPEAKNG_DATA_DIRECTORY env var,

  2. the compile-time-baked path (OUT_DIR/share at the time the binary was built),

  3. the current working directory,

  4. the directory containing the running executable.

So for a plain cargo build --release / cargo run --release on the same machine, it just works — no env var needed, since (2) already resolves to the data baked in during that build.

That baked-in path is only valid on the machine and build cache that produced the binary: it lives under target/, so it disappears the moment target/ is deleted (rm -rf target) or the crate rebuilds with a different build hash, and it never travels if you copy just the binary elsewhere. To ship a binary elsewhere, copy espeak-ng-data alongside it explicitly and point PIPER_ESPEAKNG_DATA_DIRECTORY at it at runtime:

cargo build --release
mkdir -p dist
cp target/release/<your-binary> dist/
DATA_DIR=$(ls -td target/release/build/dengjen-espeak-rs-sys-*/out/share/espeak-ng-data | head -n1)
cp -r "$DATA_DIR" dist/
PIPER_ESPEAKNG_DATA_DIRECTORY=dist/ ./dist/<your-binary>

dengjen-espeak-rs-sys-* matches a build-hash suffix that changes across profiles/toolchains; ls -td …​ | head -n1 picks the most recently modified match if more than one such directory exists (stale ones from earlier builds).

Gotchas

If you encounter linking errors such as

error LNK2019: unresolved external symbol __std_mismatch_1 referenced in function "private: class onnxruntime::common::Status

make sure your Visual Studio is >= 17.11 (update through the Visual Studio Installer).

Cross-compiling for Linux arm64 (e.g. Raspberry Pi)

sudo apt-get install crossbuild-essential-arm64 cmake pkg-config libssl-dev
rustup target add aarch64-unknown-linux-gnu
CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \
  cargo build --release --target aarch64-unknown-linux-gnu

No CMAKE_TOOLCHAIN_FILE, sysroot variable, or separately-built libasound is needed — the cmake/cc crates already recognize the standard aarch64-linux-gnu-* cross-compiler naming convention that crossbuild-essential-arm64 installs, and eSpeak-ng’s own CMake build skips its tests and native-only intonation data when CMAKE_CROSSCOMPILING is set. pkg-config/ libssl-dev are only needed on the host, for the openssl-sys build script (a build-time dependency of ort), not for the arm64 target itself.