Files
chanora/tools/build-linux.sh
T

229 lines
8.3 KiB
Bash
Executable File

#!/usr/bin/env bash
# Chanora Linux build helper.
#
# Run this on a Linux host (Ubuntu 22.04+ / Debian bookworm+ / Fedora 38+) that
# has already completed the prerequisites in §4 of docs/release/linux-build.md:
# - Rust stable with x86_64-unknown-linux-gnu target
# - Flutter 3.41.9+ stable on PATH
# - CMake 3.13+, pkg-config, GTK3 dev headers, clang
# - System libopus development package (libopus-dev / opus-devel)
# - Optional: CHANORA_ONNXRUNTIME_SHARED_LIB=/abs/path/libonnxruntime.so
# to bundle ONNX Runtime into the Linux app under bundle/lib/
# - flutter_rust_bridge_codegen 2.12.0 (only if regenerating bindings)
#
# Usage (from the repo root ~/chanora):
# ./tools/build-linux.sh
# ./tools/build-linux.sh --no-rust # skip Rust rebuild (cached)
# ./tools/build-linux.sh --regenerate-bindings # rerun FRB codegen first
# ./tools/build-linux.sh --version v0.2.0-beta.1
#
# Produces:
# target/x86_64-unknown-linux-gnu/release/libchanora_bridge.so
# apps/chanora_flutter/build/linux/x64/release/bundle/chanora_flutter
# chanora-<version>-linux-x64.tar.gz (release bundle)
set -euo pipefail
VERSION="v0.2.0-beta.1"
SKIP_RUST=0
REGEN=0
TARGET="x86_64-unknown-linux-gnu"
ONNXRUNTIME_SHARED_LIB="${CHANORA_ONNXRUNTIME_SHARED_LIB:-}"
while [[ $# -gt 0 ]]; do
case "$1" in
--version) VERSION="$2"; shift 2;;
--no-rust) SKIP_RUST=1; shift;;
--regenerate-bindings) REGEN=1; shift;;
--target) TARGET="$2"; shift 2;;
*) echo "unknown arg: $1" >&2; exit 2;;
esac
done
cd "$(dirname "$0")/.."
REPO_ROOT="$(pwd)"
bar() { printf '%s\n' '========================================================================'; }
bar
echo "Chanora Linux build"
echo "Repo root : $REPO_ROOT"
echo "Version : $VERSION"
echo "Target : $TARGET"
echo "Build Rust: $([ "$SKIP_RUST" = 1 ] && echo skip || echo yes)"
echo "Bundle ORT: ${ONNXRUNTIME_SHARED_LIB:-<not set>}"
bar
# ---------- 1. Verify toolchain ----------
echo "[1/6] Verify toolchain"
for cmd in cargo rustc flutter cmake pkg-config clang gcc; do
if ! command -v "$cmd" >/dev/null; then
echo " ERROR: '$cmd' not on PATH. See docs/release/linux-build.md." >&2
exit 1
fi
printf " %-12s : %s\n" "$cmd" "$($cmd --version 2>&1 | head -1)"
done
# Verify GTK3 dev headers
if ! pkg-config --exists gtk+-3.0 2>/dev/null; then
echo " ERROR: gtk+-3.0 dev headers not found. Install libgtk-3-dev (Debian/Ubuntu) or gtk3-devel (Fedora)." >&2
exit 1
fi
# Verify libopus development files. tsclientlib's audio feature pulls in
# audiopus, which links libopus for Linux builds/tests.
if ! pkg-config --exists opus 2>/dev/null; then
echo " ERROR: opus pkg-config metadata not found." >&2
echo " Install the Opus development package (libopus-dev on" >&2
echo " Debian/Ubuntu or opus-devel on Fedora)." >&2
exit 1
fi
printf " %-12s : %s\n" "opus" "$(pkg-config --modversion opus)"
# Verify the exact PipeWire pkg-config modules the primary Linux voice backend uses.
# `pipewire-0.3` is not the module name exposed by distro packages; the crates
# resolve `libpipewire-0.3` and `libspa-0.2`.
if ! pkg-config --exists libpipewire-0.3 2>/dev/null; then
echo " ERROR: libpipewire-0.3 pkg-config metadata not found." >&2
echo " Install PipeWire development headers (for example pipewire-devel" >&2
echo " on Fedora or libpipewire-0.3-dev on Debian/Ubuntu)." >&2
exit 1
fi
if ! pkg-config --exists libspa-0.2 2>/dev/null; then
echo " ERROR: libspa-0.2 pkg-config metadata not found." >&2
echo " Install the PipeWire SPA development headers packaged with" >&2
echo " your distro's PipeWire development package." >&2
exit 1
fi
printf " %-12s : %s\n" "libpipewire" "$(pkg-config --modversion libpipewire-0.3)"
printf " %-12s : %s\n" "libspa" "$(pkg-config --modversion libspa-0.2)"
# Verify PulseAudio development files used by the fallback Linux voice backend.
if ! pkg-config --exists libpulse 2>/dev/null; then
echo " ERROR: libpulse pkg-config metadata not found." >&2
echo " Install PulseAudio development headers (for example libpulse-dev" >&2
echo " on Debian/Ubuntu, pulseaudio-libs-devel on Fedora, or libpulse" >&2
echo " on Arch Linux)." >&2
exit 1
fi
if ! pkg-config --exists libpulse-simple 2>/dev/null; then
echo " ERROR: libpulse-simple pkg-config metadata not found." >&2
echo " Install PulseAudio simple API development headers." >&2
exit 1
fi
printf " %-12s : %s\n" "libpulse" "$(pkg-config --modversion libpulse)"
printf " %-12s : %s\n" "pulse-simple" "$(pkg-config --modversion libpulse-simple)"
# Verify the optional ONNX Runtime shared library when explicitly requested.
if [[ -n "$ONNXRUNTIME_SHARED_LIB" && ! -f "$ONNXRUNTIME_SHARED_LIB" ]]; then
echo " ERROR: CHANORA_ONNXRUNTIME_SHARED_LIB does not exist:" >&2
echo " $ONNXRUNTIME_SHARED_LIB" >&2
exit 1
fi
# Verify Rust target
if ! rustup target list --installed | grep -q "^$TARGET$"; then
echo " Adding rustup target $TARGET..."
rustup target add "$TARGET"
fi
# ---------- 2. Add Linux platform if missing ----------
FLUTTER_APP="$REPO_ROOT/apps/chanora_flutter"
if [[ ! -d "$FLUTTER_APP/linux" ]]; then
echo "[2/6] Add Linux platform to apps/chanora_flutter"
(cd "$FLUTTER_APP" && flutter create --platforms=linux .)
else
echo "[2/6] Linux platform already present in apps/chanora_flutter"
fi
# ---------- 3. (Optional) regenerate FRB bindings ----------
if [[ $REGEN -eq 1 ]]; then
echo "[3/6] Regenerate flutter_rust_bridge bindings"
if ! command -v flutter_rust_bridge_codegen >/dev/null; then
echo " flutter_rust_bridge_codegen not on PATH; install it first." >&2
exit 1
fi
flutter_rust_bridge_codegen generate
else
echo "[3/6] Skipping FRB codegen (use --regenerate-bindings if api.rs changed)"
fi
# ---------- 4. Build the Rust cdylib ----------
BRIDGE_SO="$REPO_ROOT/target/$TARGET/release/libchanora_bridge.so"
if [[ $SKIP_RUST -eq 1 ]]; then
echo "[4/6] Skipping Rust build (cached)"
if [[ ! -f "$BRIDGE_SO" ]]; then
echo " ERROR: --no-rust but $BRIDGE_SO not found. Run without --no-rust first." >&2
exit 1
fi
else
echo "[4/6] cargo build --release -p chanora_bridge --target $TARGET"
export CMAKE_POLICY_VERSION_MINIMUM=3.5
export LIBOPUS_STATIC=0
cargo build --release --target "$TARGET" -p chanora_bridge
fi
echo " Bridge .so: $BRIDGE_SO"
if [[ ! -f "$BRIDGE_SO" ]]; then
echo " ERROR: $BRIDGE_SO not found after build." >&2
exit 1
fi
# ---------- 5. flutter build linux --release ----------
echo "[5/6] flutter build linux --release"
cd "$FLUTTER_APP"
export CHANORA_BRIDGE_SHARED_LIB="$BRIDGE_SO"
flutter pub get
flutter build linux --release
BUNDLE_DIR="$FLUTTER_APP/build/linux/x64/release/bundle"
EXE="$BUNDLE_DIR/chanora_flutter"
if [[ ! -f "$EXE" ]]; then
echo " ERROR: $EXE not found after flutter build." >&2
exit 1
fi
echo " Bundle dir : $BUNDLE_DIR"
echo " EXE : $EXE"
echo " Bridge .so : $BUNDLE_DIR/lib/libchanora_bridge.so"
if [[ ! -f "$BUNDLE_DIR/lib/libchanora_bridge.so" ]]; then
echo " ERROR: bridge shared library missing from Linux bundle:" >&2
echo " $BUNDLE_DIR/lib/libchanora_bridge.so" >&2
exit 1
fi
if [[ -n "$ONNXRUNTIME_SHARED_LIB" ]]; then
if [[ -f "$BUNDLE_DIR/lib/libonnxruntime.so" ]]; then
echo " ORT .so : $BUNDLE_DIR/lib/libonnxruntime.so"
else
echo " ERROR: ONNX Runtime bundle file missing after flutter build:" >&2
echo " $BUNDLE_DIR/lib/libonnxruntime.so" >&2
exit 1
fi
else
echo " ORT .so : not bundled (set CHANORA_ONNXRUNTIME_SHARED_LIB to include it)"
fi
# ---------- 6. Package the bundle ----------
echo "[6/6] Package release tarball"
cd "$REPO_ROOT"
TAR_NAME="chanora-$VERSION-linux-x64.tar.gz"
TAR_PATH="$REPO_ROOT/$TAR_NAME"
rm -f "$TAR_PATH"
# Bundle everything in the bundle directory
tar czf "$TAR_PATH" -C "$FLUTTER_APP/build/linux/x64/release/bundle" .
echo " Tarball : $TAR_PATH"
echo " Size : $(du -h "$TAR_PATH" | cut -f1)"
bar
echo "Linux build complete."
bar
echo "Artefacts:"
echo " Rust lib:"
echo " target/$TARGET/release/libchanora_bridge.so"
echo " Bundle dir:"
echo " $BUNDLE_DIR/"
echo " Tarball:"
echo " $TAR_PATH"