#!/usr/bin/env bash # Builds the native service, assembles the package layout and produces the ipk. # # ./tools/build.sh # native + stage + package # ./tools/build.sh native # cross-compile the service only # ./tools/build.sh package # assemble and run ares-package # ./tools/build.sh install # ares-install the ipk on the TV # ./tools/build.sh launch # ares-launch the app # ./tools/build.sh logs # tail the service log over ssh # ./tools/build.sh clean # # Needs the webOS NDK (arm-webos-linux-gnueabi buildroot SDK) for the native # part and ares-cli for the packaging part. Point WEBOS_SDK at the SDK if it is # not in the usual place; set DEVICE to the ares device name (default: tv). set -euo pipefail ROOT="$(cd "$(dirname "$0")/.." && pwd)" cd "$ROOT" # `npm install` puts ares-cli here rather than on PATH, so a local install works # without also needing the global one. [ -d "$ROOT/node_modules/.bin" ] && PATH="$ROOT/node_modules/.bin:$PATH" APP_ID=org.webosbrew.audiocap SERVICE_ID=$APP_ID.service BINARY=audiocap-service WEBOS_SDK="${WEBOS_SDK:-$HOME/arm-webos-linux-gnueabi_sdk-buildroot}" DEVICE="${DEVICE:-tv}" BUILD_DIR="$ROOT/build" STAGE_APP="$BUILD_DIR/stage/app" STAGE_SERVICE="$BUILD_DIR/stage/service" OUT_DIR="$ROOT/out" say() { printf '%s\n' "$*"; } step() { printf '\n== %s\n' "$*"; } die() { printf 'error: %s\n' "$*" >&2; exit 1; } version() { python3 - "$ROOT/frontend/appinfo.json" <<'EOF' import json, sys print(json.load(open(sys.argv[1]))["version"]) EOF } check_sdk() { local toolchain="$WEBOS_SDK/share/buildroot/toolchainfile.cmake" if [ ! -f "$toolchain" ]; then cat >&2 </dev/null 2>&1 || die "cmake is not installed" echo "$toolchain" } check_ares() { command -v ares-package >/dev/null 2>&1 || cat >&2 <<'EOF' error: ares-package is not on PATH npm install -g @webosose/ares-cli Then register the TV once (developer mode or the Homebrew Channel's ssh): ares-setup-device --add tv --info "{'host':'192.168.1.20','port':9922,'username':'root'}" EOF command -v ares-package >/dev/null 2>&1 } build_native() { local toolchain toolchain="$(check_sdk)" step "Cross-compiling the service" cmake -S native -B "$BUILD_DIR/native" \ -DCMAKE_TOOLCHAIN_FILE="$toolchain" \ -DCMAKE_BUILD_TYPE=Release cmake --build "$BUILD_DIR/native" --parallel local binary="$BUILD_DIR/native/$BINARY" [ -f "$binary" ] || die "the build produced no $BINARY" say "built $binary" file "$binary" 2>/dev/null | sed 's/^/ /' || true } stage() { step "Staging the package" rm -rf "$BUILD_DIR/stage" mkdir -p "$STAGE_APP" "$STAGE_SERVICE" cp -R "$ROOT/frontend/." "$STAGE_APP/" # The mock only exists so the UI can be opened in a desktop browser. rm -f "$STAGE_APP/js/mock.js" python3 - "$STAGE_APP/index.html" <<'EOF' import re, sys path = sys.argv[1] html = open(path).read() html = re.sub(r'\s*', '', html) open(path, "w").write(html) EOF cp "$ROOT/servicefiles/services.json" "$STAGE_SERVICE/" cp "$ROOT/servicefiles/package.json" "$STAGE_SERVICE/" cp "$ROOT/servicefiles/audiocapautostart" "$STAGE_SERVICE/" chmod +x "$STAGE_SERVICE/audiocapautostart" local binary="$BUILD_DIR/native/$BINARY" [ -f "$binary" ] || die "no service binary; run './tools/build.sh native' first" cp "$binary" "$STAGE_SERVICE/$BINARY" chmod +x "$STAGE_SERVICE/$BINARY" say "app: $STAGE_APP" say "service: $STAGE_SERVICE" } package() { check_ares || exit 1 step "Packaging" mkdir -p "$OUT_DIR" rm -f "$OUT_DIR"/${APP_ID}_*.ipk ares-package "$STAGE_APP" "$STAGE_SERVICE" -o "$OUT_DIR" local ipk ipk="$(ls -t "$OUT_DIR"/${APP_ID}_*.ipk | head -1)" say "" say "$ipk" if command -v sha256sum >/dev/null 2>&1; then sha256sum "$ipk" | sed 's/^/ sha256 /' elif command -v shasum >/dev/null 2>&1; then shasum -a 256 "$ipk" | sed 's/^/ sha256 /' fi } latest_ipk() { ls -t "$OUT_DIR"/${APP_ID}_*.ipk 2>/dev/null | head -1 } install_ipk() { local ipk ipk="$(latest_ipk)" || true [ -n "$ipk" ] || die "no ipk in $OUT_DIR; run './tools/build.sh' first" step "Installing $ipk on device '$DEVICE'" ares-install --device "$DEVICE" "$ipk" say "" say "The service needs root to reach the TV's audio devices. Either open the" say "app and press 'Grant root access', or run it here:" say " ares-shell --device $DEVICE -r \\" say " '/media/developer/apps/usr/palm/services/org.webosbrew.hbchannel.service/elevate-service $SERVICE_ID'" } launch() { step "Launching on '$DEVICE'" ares-launch --device "$DEVICE" "$APP_ID" } logs() { step "Service log from '$DEVICE' (ctrl-c to stop)" # The service keeps its own ring buffer, but journald/pmlog has the crashes. ares-shell --device "$DEVICE" -r \ "tail -f /var/log/messages 2>/dev/null | grep -i audiocap || journalctl -f | grep -i audiocap" } clean() { step "Cleaning" rm -rf "$BUILD_DIR" "$OUT_DIR" say "removed build/ and out/" } case "${1:-all}" in all) build_native; stage; package ;; native) build_native ;; stage) stage ;; package) stage; package ;; install) install_ipk ;; launch) launch ;; logs) logs ;; clean) clean ;; version) version ;; -h|--help) awk 'NR == 1 { next } /^#/ { sub(/^# ?/, ""); print; next } { exit }' "$0" ;; *) die "unknown command '$1' (try --help)" ;; esac