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:
-
the directory named by the
PIPER_ESPEAKNG_DATA_DIRECTORYenv var, -
the compile-time-baked path (
OUT_DIR/shareat the time the binary was built), -
the current working directory,
-
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).
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.
Want to help? Learn how to contribute to the ZirekHQ docs ›