diff --git a/.gitignore b/.gitignore index f7c6f2aa2..7160095bf 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,6 @@ js/app/src/build-info.json *.env muxl.wasm /moderation +go.work +go.work.sum +pkg/streamplacedeps/zz_local_*.go diff --git a/Makefile b/Makefile index e8c331f5b..8010122e4 100644 --- a/Makefile +++ b/Makefile @@ -292,6 +292,13 @@ test-vod: .PHONY: dev-setup dev-setup: $(MAKE) -j16 app-cached dev-setup-meson + $(MAKE) dev-cgo + +# Generate explicit cgo flags (plus a go.work pointing at patched copies of +# the cgo-using deps) so plain `go test ./pkg/...` works with no env vars. +.PHONY: dev-cgo +dev-cgo: + BUILDDIR=$(BUILDDIR) ./hack/gen-cgo-flags.sh .PHONY: dev dev: app-cached $(LEXICON_STAMP) diff --git a/hack/gen-cgo-flags.sh b/hack/gen-cgo-flags.sh new file mode 100755 index 000000000..46602ec7d --- /dev/null +++ b/hack/gen-cgo-flags.sh @@ -0,0 +1,198 @@ +#!/bin/sh +set -eu + +# Generate explicit cgo flags so `go test ./pkg/...` works with no environment +# variables and no pkg-config invocations at build time. Run by `make dev-setup` +# (or `make dev-cgo`) after the meson build exists. +# +# Two kinds of output: +# +# 1. pkg/streamplacedeps/cgoflags__.go — COMMITTED. Replaces the old +# `#cgo pkg-config: streamplacedeps` directive with the expanded link flags, +# expressed ${SRCDIR}-relative so the same checkout works on the host and +# mounted at /app inside the dev containers. Regenerate (and commit) when +# the meson dependency set changes. +# +# 2. Gitignored, machine-local output: +# - .build/cgo// — patched copies of the cgo-using dependencies +# (go-glib, go-gst, lpms). Their `#cgo pkg-config:` directives are +# stripped and replaced with a generated flags file per package, built +# by invoking pkg-config ourselves against the meson build dir. +# - go.work — points the workspace at those copies. Workspace-scoped: +# nothing global, go.mod untouched, delete go.work to opt out. +# - pkg/streamplacedeps/zz_local_cgoflags.go — absolute rpath to the +# build dir so dev binaries run without LD_LIBRARY_PATH (Linux; the +# rpath is baked as old-style DT_RPATH because DT_RUNPATH does not +# cover transitive loads like libgobject -> libffi). + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" + +OS="$(uname -s | tr '[:upper:]' '[:lower:]')" +ARCH="$(uname -m)" +case "$ARCH" in +x86_64) ARCH=amd64 ;; +aarch64 | arm64) ARCH=arm64 ;; +esac +BUILDDIR="${BUILDDIR:-build-$OS-$ARCH}" +case "$BUILDDIR" in +/*) : ;; +*) BUILDDIR="$ROOT/$BUILDDIR" ;; +esac + +if [ ! -d "$BUILDDIR/lib/pkgconfig" ]; then + echo "gen-cgo-flags: $BUILDDIR/lib/pkgconfig not found; run 'make dev-setup' first" >&2 + exit 1 +fi + +PCP="$BUILDDIR/lib/pkgconfig:$BUILDDIR/lib/gstreamer-1.0/pkgconfig" +pc() { + PKG_CONFIG_PATH="$PCP${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}" pkg-config "$@" +} + +relpath() { + python3 -c 'import os,sys; print(os.path.relpath(sys.argv[1], sys.argv[2]))' "$1" "$2" +} + +# --- 1. committed flags for pkg/streamplacedeps ----------------------------- + +SPDIR="$ROOT/pkg/streamplacedeps" +LIBS="$(pc --libs streamplacedeps)" + +# Drop rpath-related tokens (machine-specific; the gitignored local file +# carries them) and make -L paths repo-relative. +clean="" +for tok in $LIBS; do + case "$tok" in + -Wl,-rpath* | -Wl,--disable-new-dtags) continue ;; + esac + clean="$clean $tok" +done +REL="$(relpath "$BUILDDIR" "$SPDIR")" +clean="$(printf '%s' "$clean" | sed "s|$BUILDDIR|\${SRCDIR}/$REL|g" | sed 's/^ *//')" + +write_spdeps() { + # $1 = goos, $2 = goarch, $3 = flags + cat >"$SPDIR/cgoflags_$1_$2.go" <"$SPDIR/zz_local_cgoflags.go" </dev/null || true + + for pkgdir in $(grep -rl '#cgo pkg-config:' "$dst" --include='*.go' | xargs -n1 dirname | sort -u); do + mods="$(grep -h '#cgo pkg-config:' "$pkgdir"/*.go | sed 's/.*#cgo pkg-config: *//' | tr ' ' '\n' | grep -v '^$' | sort -u | tr '\n' ' ')" + if ! cflags="$(pc --cflags $mods 2>/dev/null)" || ! libs="$(pc --libs $mods 2>/dev/null)"; then + echo "gen-cgo-flags: skipping $pkgdir (pkg-config failed for: $mods)" >&2 + continue + fi + srcfile="$(grep -l '#cgo pkg-config:' "$pkgdir"/*.go | grep -v _test.go | head -1)" + pkgname="$(awk '/^package /{print $2; exit}' "$srcfile")" + prel="$(relpath "$BUILDDIR" "$pkgdir")" + cflags="$(printf '%s' "$cflags" | sed "s|$BUILDDIR|\${SRCDIR}/$prel|g")" + libs="$(printf '%s' "$libs" | sed "s|$BUILDDIR|\${SRCDIR}/$prel|g")" + if [ "$OS" = linux ]; then + # -lm: lpms needs libm but ffmpeg's .pc files keep it in Libs.private. + # Absolute rpath: fine to bake here, these copies are regenerated per + # machine (and a dangling rpath is harmless inside the containers). + libs="$libs -lm -Wl,-rpath,$BUILDDIR/lib -Wl,--disable-new-dtags" + fi + perl -ni -e 'print unless /#cgo\s+pkg-config:/' "$pkgdir"/*.go + { + echo "// Code generated by streamplace hack/gen-cgo-flags.sh; DO NOT EDIT." + echo "//" + echo "// Replaces upstream's \`#cgo pkg-config: ...\` directives (stripped from this" + echo "// copy) with flags resolved against the meson build dir at generation time," + echo "// so builds need no pkg-config and no environment variables." + echo "" + echo "package $pkgname" + echo "" + if [ -n "$cflags" ]; then + echo "// #cgo CFLAGS: $cflags" + fi + echo "// #cgo LDFLAGS: $libs" + echo "import \"C\"" + } >"$pkgdir/zz_streamplace_cgoflags.go" + done +done + +# --- 3. go.work -------------------------------------------------------------- + +GOVERSION="$(awk '/^go /{print $2; exit}' "$ROOT/go.mod")" +cat >"$ROOT/go.work" < ./.build/cgo/go-glib + +replace github.com/go-gst/go-gst => ./.build/cgo/go-gst + +replace github.com/livepeer/lpms => ./.build/cgo/lpms +EOF + +echo "gen-cgo-flags: wrote pkg/streamplacedeps/cgoflags_*.go, .build/cgo/, go.work (BUILDDIR=$BUILDDIR)" diff --git a/pkg/streamplacedeps/cgoflags_darwin_amd64.go b/pkg/streamplacedeps/cgoflags_darwin_amd64.go new file mode 100644 index 000000000..cde711c29 --- /dev/null +++ b/pkg/streamplacedeps/cgoflags_darwin_amd64.go @@ -0,0 +1,17 @@ +//go:build !static + +// Code generated by hack/gen-cgo-flags.sh (make dev-cgo); DO NOT EDIT. +// Regenerate and commit when the meson dependency set changes. + +// Link flags for dynamic dev builds, expanded from what used to be +// `#cgo pkg-config: streamplacedeps` so that no pkg-config invocation or +// environment variable is needed. Paths are ${SRCDIR}-relative so the same +// checkout works on the host and mounted at /app in the dev containers. The +// machine-specific rpath lives in the generated, gitignored +// zz_local_cgoflags.go; without it (containers, CI) LD_LIBRARY_PATH still +// works as before. + +package streamplacedeps + +// #cgo LDFLAGS: -L${SRCDIR}/../../build-darwin-amd64/lib -liroh_streamplace -lavfilter -lavformat -lavcodec -lgio-2.0 -lgstcontroller-1.0 -lgstapp-1.0 -lgstpbutils-1.0 -lgstvideo-1.0 -lgstaudio-1.0 -lgstbase-1.0 -lgstreamer-1.0 -lgobject-2.0 -lffi -lglib-2.0 -lintl +import "C" diff --git a/pkg/streamplacedeps/cgoflags_darwin_arm64.go b/pkg/streamplacedeps/cgoflags_darwin_arm64.go new file mode 100644 index 000000000..dd922538a --- /dev/null +++ b/pkg/streamplacedeps/cgoflags_darwin_arm64.go @@ -0,0 +1,17 @@ +//go:build !static + +// Code generated by hack/gen-cgo-flags.sh (make dev-cgo); DO NOT EDIT. +// Regenerate and commit when the meson dependency set changes. + +// Link flags for dynamic dev builds, expanded from what used to be +// `#cgo pkg-config: streamplacedeps` so that no pkg-config invocation or +// environment variable is needed. Paths are ${SRCDIR}-relative so the same +// checkout works on the host and mounted at /app in the dev containers. The +// machine-specific rpath lives in the generated, gitignored +// zz_local_cgoflags.go; without it (containers, CI) LD_LIBRARY_PATH still +// works as before. + +package streamplacedeps + +// #cgo LDFLAGS: -L${SRCDIR}/../../build-darwin-arm64/lib -liroh_streamplace -lavfilter -lavformat -lavcodec -lgio-2.0 -lgstcontroller-1.0 -lgstapp-1.0 -lgstpbutils-1.0 -lgstvideo-1.0 -lgstaudio-1.0 -lgstbase-1.0 -lgstreamer-1.0 -lgobject-2.0 -lffi -lglib-2.0 -lintl +import "C" diff --git a/pkg/streamplacedeps/cgoflags_linux_amd64.go b/pkg/streamplacedeps/cgoflags_linux_amd64.go new file mode 100644 index 000000000..ffa8d8196 --- /dev/null +++ b/pkg/streamplacedeps/cgoflags_linux_amd64.go @@ -0,0 +1,17 @@ +//go:build !static + +// Code generated by hack/gen-cgo-flags.sh (make dev-cgo); DO NOT EDIT. +// Regenerate and commit when the meson dependency set changes. + +// Link flags for dynamic dev builds, expanded from what used to be +// `#cgo pkg-config: streamplacedeps` so that no pkg-config invocation or +// environment variable is needed. Paths are ${SRCDIR}-relative so the same +// checkout works on the host and mounted at /app in the dev containers. The +// machine-specific rpath lives in the generated, gitignored +// zz_local_cgoflags.go; without it (containers, CI) LD_LIBRARY_PATH still +// works as before. + +package streamplacedeps + +// #cgo LDFLAGS: -L${SRCDIR}/../../build-linux-amd64/lib -liroh_streamplace -lavfilter -lavformat -lavcodec -lgio-2.0 -lgstcontroller-1.0 -lgstapp-1.0 -lgstpbutils-1.0 -lgstvideo-1.0 -lgstaudio-1.0 -lgstbase-1.0 -lgstreamer-1.0 -lgobject-2.0 -lffi -lglib-2.0 -lintl +import "C" diff --git a/pkg/streamplacedeps/cgoflags_linux_arm64.go b/pkg/streamplacedeps/cgoflags_linux_arm64.go new file mode 100644 index 000000000..2b0e2d1b6 --- /dev/null +++ b/pkg/streamplacedeps/cgoflags_linux_arm64.go @@ -0,0 +1,17 @@ +//go:build !static + +// Code generated by hack/gen-cgo-flags.sh (make dev-cgo); DO NOT EDIT. +// Regenerate and commit when the meson dependency set changes. + +// Link flags for dynamic dev builds, expanded from what used to be +// `#cgo pkg-config: streamplacedeps` so that no pkg-config invocation or +// environment variable is needed. Paths are ${SRCDIR}-relative so the same +// checkout works on the host and mounted at /app in the dev containers. The +// machine-specific rpath lives in the generated, gitignored +// zz_local_cgoflags.go; without it (containers, CI) LD_LIBRARY_PATH still +// works as before. + +package streamplacedeps + +// #cgo LDFLAGS: -L${SRCDIR}/../../build-linux-arm64/lib -liroh_streamplace -lavfilter -lavformat -lavcodec -lgio-2.0 -lgstcontroller-1.0 -lgstapp-1.0 -lgstpbutils-1.0 -lgstvideo-1.0 -lgstaudio-1.0 -lgstbase-1.0 -lgstreamer-1.0 -lgobject-2.0 -lffi -lglib-2.0 -lintl +import "C" diff --git a/pkg/streamplacedeps/streamplacedeps-dynamic.go b/pkg/streamplacedeps/streamplacedeps-dynamic.go deleted file mode 100644 index 6b8303c9e..000000000 --- a/pkg/streamplacedeps/streamplacedeps-dynamic.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build !static - -// This runs during dynamic builds in dev - -package streamplacedeps - -// #cgo pkg-config: streamplacedeps -import "C"