#!/usr/bin/env bash # The quickstart archive: the `wai` command line and what `wai dev` runs, for one target. # # bash wai/quickstart/build.sh --web [--target ] [--version ] # # Builds, each release and --locked, each from its own workspace: # wai wai/wai-rs, --no-default-features --features quickstart: the mandatory # floor, hello, create, dev and verify; no system codec library # wai-relay the relay (crate joule-moq-relay, joule-code/joule-code-rs) # wai-room the room service (crate joule-moq-room) # wai-remote-host a remote desktop room's host agent # wai-pack the packager # and writes /wai--.tar.gz holding bin/ (those five), share/wai/web/ # (the room client, from --web: bash wai/quickstart/build-web.sh makes it), README.md and # LICENSE, with .sha256 beside it in the form `shasum -a 256 -c` reads. The version # is wai/wai-rs's unless given; the triple is the host's unless given. Extra cargo flags for # the target (a linker, static linking) come from the environment, as cargo reads them. set -euo pipefail usage() { echo "usage: bash wai/quickstart/build.sh --web [--target ] [--version ]" >&2; exit 2; } [ $# -ge 3 ] || usage ROOT=$(cd "$(dirname "$0")/../.." && pwd) OUT=$(mkdir -p "$1" && cd "$1" && pwd); shift WEBDIR="" TRIPLE="" VERSION="" while [ $# -gt 0 ]; do case "$1" in --web) WEBDIR="$2"; shift 2 ;; --target) TRIPLE="$2"; shift 2 ;; --version) VERSION="$2"; shift 2 ;; *) usage ;; esac done [ -n "$WEBDIR" ] && [ -f "$WEBDIR/room/index.html" ] && [ -f "$WEBDIR/pkg/wai_live_web_bg.wasm" ] && [ -f "$WEBDIR/pkg/joule_moq_web_bg.wasm" ] \ || { echo "--web: a directory build-web.sh made (room/, moq/, pkg/)" >&2; exit 2; } HOST=$(rustc -vV | sed -n 's/^host: //p') TRIPLE=${TRIPLE:-$HOST} VERSION=${VERSION:-$(sed -n 's/^version = "\(.*\)"$/\1/p' "$ROOT/wai/wai-rs/Cargo.toml" | head -1)} EXE=""; case "$TRIPLE" in *windows*) EXE=".exe" ;; esac TGT=(--target "$TRIPLE") build() { # : the binary's path, or a failure local dir="$1" bin="$2"; shift 2 ( # Under a target directory shared by every workspace (CARGO_TARGET_DIR), each build gets its # own: wai also builds as a cdylib, so its rlib's file name carries no hash, and a second # feature set of it (the room service's) would overwrite the first in one directory. if [ -n "${CARGO_TARGET_DIR:-}" ]; then export CARGO_TARGET_DIR="$CARGO_TARGET_DIR/quickstart-$bin"; fi cd "$dir" cargo build --locked --release "${TGT[@]}" "$@" >&2 || exit 1 t=$(cargo metadata --format-version 1 --no-deps --locked | python3 -c 'import json,sys; print(json.load(sys.stdin)["target_directory"])') || exit 1 echo "$t/$TRIPLE/release/$bin$EXE" ) } need() { # : copied into the stage, or the script stops local b b=$(build "$@") || { echo "build.sh: the build of $2 failed" >&2; exit 1; } cp "$b" "$STAGE/$NAME/bin/" } NAME="wai-$VERSION-$TRIPLE" STAGE=$(mktemp -d) trap 'rm -rf "$STAGE"' EXIT mkdir -p "$STAGE/$NAME/bin" "$STAGE/$NAME/share/wai" need "$ROOT/wai/wai-rs" wai --no-default-features --features quickstart --bin wai need "$ROOT/joule-code/joule-code-rs" wai-relay -p joule-moq-relay --bin wai-relay need "$ROOT/joule-code/joule-code-rs/crates/joule-moq-room" wai-room --bin wai-room need "$ROOT/joule-code/joule-code-rs/crates/joule-moq-remote" wai-remote-host --bin wai-remote-host need "$ROOT/wai/wai-pack" wai-pack --bin wai-pack cp -R "$WEBDIR" "$STAGE/$NAME/share/wai/web" cp "$ROOT/wai/quickstart/README.md" "$STAGE/$NAME/README.md" cp "$ROOT/wai/LICENSE" "$STAGE/$NAME/LICENSE" chmod 755 "$STAGE/$NAME"/bin/* # Each binary runs before it is packaged, where it can run here. # The wai packaged must hold this checkout's examples, file for file and byte for byte: a stale # binary would ship templates from another commit. if [ "$TRIPLE" = "$HOST" ]; then "$STAGE/$NAME/bin/wai-pack$EXE" fixture list > /dev/null python3 - "$STAGE/$NAME/bin/wai$EXE" "$ROOT/wai/examples" <<'PY' || { echo "build.sh: the packaged wai does not hold this checkout's examples" >&2; exit 1; } import json, os, subprocess, sys, tempfile wai, ex = sys.argv[1], sys.argv[2] with tempfile.TemporaryDirectory() as d: for t in json.loads(subprocess.check_output([wai, "create", "--list", "--json"]))["data"]: out = os.path.join(d, t["template"]) subprocess.check_output([wai, "create", out, "--template", t["template"]]) for f in t["files"]: src = os.path.join(ex, t["template"], f) if os.path.exists(src) and open(src, "rb").read() != open(os.path.join(out, f), "rb").read(): sys.exit(f"{t['template']}/{f} differs") PY fi mkdir -p "$OUT" # The same bytes for the same inputs: sorted names, fixed owner and times. if tar --version 2>/dev/null | grep -q GNU; then tar --sort=name --owner=0 --group=0 --numeric-owner --mtime='2026-01-01 00:00Z' -C "$STAGE" -cf - "$NAME" | gzip -n -9 > "$OUT/$NAME.tar.gz" else tar -C "$STAGE" -cf - "$NAME" | gzip -n -9 > "$OUT/$NAME.tar.gz" fi ( cd "$OUT" && { command -v sha256sum >/dev/null && sha256sum "$NAME.tar.gz" || shasum -a 256 "$NAME.tar.gz"; } > "$NAME.tar.gz.sha256" ) echo "packaged $OUT/$NAME.tar.gz ($(cut -d' ' -f1 "$OUT/$NAME.tar.gz.sha256"), $(du -h "$OUT/$NAME.tar.gz" | cut -f1))"