Files
lgtv_audio_cap/tools/docker-build.sh
T
Rene KievitsandClaude Opus 5 3e4d0e17bc Build the service in a container, and fix what the target compiler found
The openlgtv NDK is a Linux toolchain with no macOS or Windows build, so
tools/build.sh could not produce a binary anywhere else. docker-build.sh
bakes the SDK into an image and compiles there; packaging and deploy stay
on the host, where the TV is reachable. The SDK ships aarch64 as well as
x86_64, so the image picks the one matching the daemon and Apple Silicon
builds natively rather than under emulation.

Cross-compiling for real turned up three things the host compiler did
not:

  sink_hyperhdr_viz.c  read p->width and p->height to format the error
                       message after free(p)
  sink_hyperhdr.c      an SDP connection line of 128 bytes cannot hold
                       "IN IP4 " plus a 127-byte host plus "/255", so a
                       long hostname would silently lose its TTL suffix
  common/log.c         the log body was sized to the whole ring line,
                       leaving nothing for the prefix; budget for it so
                       the bound is provable rather than left to
                       snprintf

A clean cross-compile is now warning-free, and readelf confirms the
design rule holds: luna-service2, glib, PmLogLib and libc, with no
libpulse or libasound.

Also: @webosose/ares-cli was pinned to ^3.0.0, which does not exist
(latest is 2.4.0), so npm install failed outright. build.sh now puts
node_modules/.bin on PATH so a local install is enough.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-26 10:29:09 +02:00

110 lines
3.6 KiB
Bash
Executable File

#!/usr/bin/env bash
# Cross-compiles the service inside a container, for hosts that cannot run the
# NDK directly. The openlgtv buildroot SDK is a Linux toolchain, so on macOS or
# Windows this is the only way to build the native part.
#
# ./tools/docker-build.sh # build the service into build/native/
# ./tools/docker-build.sh shell # a prompt inside the SDK, for poking about
# ./tools/docker-build.sh clean # drop the image
#
# The SDK is baked into an image, so it is downloaded once (~350 MB) and every
# build after the first starts instantly. There are aarch64 and x86_64 builds
# of the SDK and the image picks the one matching the container, so this runs
# natively on Apple Silicon rather than under emulation.
#
# NDK_RELEASE pins the SDK release; DOCKER is podman if you prefer.
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"
DOCKER="${DOCKER:-docker}"
NDK_RELEASE="${NDK_RELEASE:-webos-a38c582}"
IMAGE="${IMAGE:-lgtv-audiocap-sdk:$NDK_RELEASE}"
SDK=/opt/webos-sdk
TOOLCHAIN="$SDK/share/buildroot/toolchainfile.cmake"
BUILD_DIR=build/native
say() { printf '%s\n' "$*"; }
step() { printf '\n== %s\n' "$*"; }
die() { printf 'error: %s\n' "$*" >&2; exit 1; }
command -v "$DOCKER" >/dev/null 2>&1 || die "$DOCKER is not installed"
build_image() {
if "$DOCKER" image inspect "$IMAGE" >/dev/null 2>&1; then
return
fi
step "Building the SDK image (once, ~350 MB download)"
# The arch is resolved inside the build so the image is right for whatever
# the daemon runs, without the caller having to know.
"$DOCKER" build --tag "$IMAGE" - <<EOF
FROM debian:bookworm-slim
RUN apt-get update \\
&& apt-get install -y --no-install-recommends \\
ca-certificates curl cmake make file python3 \\
&& rm -rf /var/lib/apt/lists/*
RUN case "\$(uname -m)" in \\
aarch64|arm64) arch=aarch64 ;; \\
x86_64|amd64) arch=x86_64 ;; \\
*) echo "no webOS SDK for \$(uname -m)" >&2; exit 1 ;; \\
esac \\
&& mkdir -p $SDK \\
&& curl -fL "https://github.com/openlgtv/buildroot-nc4/releases/download/$NDK_RELEASE/arm-webos-linux-gnueabi_sdk-buildroot-\$arch.tar.gz" \\
| tar xz -C $SDK --strip-components=1 \\
&& $SDK/relocate-sdk.sh
EOF
}
# --user keeps build/ owned by the caller instead of root. HOME has to point
# somewhere writable or cmake complains about its package registry.
run() {
"$DOCKER" run --rm \
--volume "$ROOT:/src" \
--workdir /src \
--user "$(id -u):$(id -g)" \
--env HOME=/tmp \
"$@"
}
compile() {
build_image
# A cache from a differently placed toolchain cannot be reused.
if [ -f "$BUILD_DIR/CMakeCache.txt" ] \
&& ! grep -q "CMAKE_TOOLCHAIN_FILE:.*=$TOOLCHAIN" "$BUILD_DIR/CMakeCache.txt"; then
say "discarding a cmake cache from a different toolchain"
rm -rf "$BUILD_DIR"
fi
step "Cross-compiling the service"
run --interactive=false "$IMAGE" sh -c "
set -e
cmake -S native -B $BUILD_DIR \
-DCMAKE_TOOLCHAIN_FILE=$TOOLCHAIN \
-DCMAKE_BUILD_TYPE=Release
cmake --build $BUILD_DIR --parallel
file $BUILD_DIR/audiocap-service
"
[ -f "$BUILD_DIR/audiocap-service" ] || die "the build produced no binary"
say ""
say "built $BUILD_DIR/audiocap-service"
say "now: ./tools/build.sh package"
}
case "${1:-build}" in
build) compile ;;
shell)
build_image
step "A shell in the SDK image; the toolchain is at $SDK"
run --interactive --tty "$IMAGE" bash
;;
clean)
"$DOCKER" image rm "$IMAGE" 2>/dev/null && say "removed $IMAGE" || say "no image to remove"
;;
-h|--help)
awk 'NR == 1 { next } /^#/ { sub(/^# ?/, ""); print; next } { exit }' "$0" ;;
*) die "unknown command '$1' (try --help)" ;;
esac