From 1e7c18b2c8a6a4d2f98640494619fdfd160e9cc1 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Sat, 18 Jul 2026 10:37:54 +0200 Subject: [PATCH] fix(packaging): install punktfunk-host on Ubuntu 24.04 LTS via a noble builder that bundles FFmpeg 8 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The host .deb was built on the Ubuntu 26.04 rust-ci image, so dpkg-shlibdeps baked in `Depends: libavcodec62` (FFmpeg 8) and a glibc-2.41 floor — making it uninstallable on Ubuntu 24.04 LTS (FFmpeg 6.1 / libavcodec60, glibc 2.39; apt reports the deps as "too recent"). The source floor (ffmpeg-next 8, libavcodec >=61 APIs) means a straight 24.04 rebuild would fail too. Build the host on Ubuntu 24.04 instead — lowering the glibc floor to 2.39 so one binary runs on 24.04 -> 26.04 — and bundle a from-source LGPL FFmpeg 8 into the package so it no longer depends on the distro libav*. Everything else the host links is soname-compatible on 24.04 (opus is vendored via cmake; NVENC/libcuda are dlopen-only, never link-time), and the only FFmpeg encoders used are *_nvenc / *_vaapi (software H.264 fallback is the BSD-2 openh264 crate, not FFmpeg libx264), so an LGPL build keeps the bundle license-clean. - ci/rust-ci-noble.Dockerfile (new): ubuntu:24.04 builder; nv-codec-headers + FFmpeg 8 (--enable-nvenc --enable-vaapi, shared) -> /opt/ffmpeg; PKG_CONFIG_PATH. - packaging/debian/build-deb.sh: BUNDLE_FFMPEG=1 copies libav*/libsw*/libpostproc into /usr/lib/punktfunk-host, patchelf-sets the rpath ($ORIGIN per-lib + binary --force-rpath), feeds them to dpkg-shlibdeps (captures libva2/libdrm2), and drops the libav* sonames from Depends. Normal (non-bundle) path unchanged. - .gitea/workflows/deb.yml: split into build-publish (client/web/scripting on the 26.04 image) and build-publish-host (noble image, BUNDLE_FFMPEG=1); parallel, identical version step, same apt distribution -> one universal host .deb. - .gitea/workflows/docker.yml: build+push punktfunk-rust-ci-noble. - packaging/debian/README.md: document the 24.04 LTS path + bundled local build. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitea/workflows/deb.yml | 137 +++++++++++++++++++++++++++++----- .gitea/workflows/docker.yml | 6 ++ ci/rust-ci-noble.Dockerfile | 85 +++++++++++++++++++++ packaging/debian/README.md | 33 +++++++- packaging/debian/build-deb.sh | 65 ++++++++++++++-- 5 files changed, 299 insertions(+), 27 deletions(-) create mode 100644 ci/rust-ci-noble.Dockerfile diff --git a/.gitea/workflows/deb.yml b/.gitea/workflows/deb.yml index 0274b654..b9c65a07 100644 --- a/.gitea/workflows/deb.yml +++ b/.gitea/workflows/deb.yml @@ -1,8 +1,18 @@ -# Build the punktfunk-host and punktfunk-client .debs and publish them to Gitea's Debian -# package registry, so Ubuntu boxes get new builds via `apt update && apt upgrade`. Runs -# inside the same Ubuntu 26.04 rust-ci builder image as ci.yml, so dpkg-shlibdeps pins the -# runtime lib package names (libavcodec62, libpipewire-0.3-0t64, …) to exactly what the -# target boxes run. +# Build the punktfunk .debs and publish them to Gitea's Debian package registry, so Ubuntu +# boxes get new builds via `apt update && apt upgrade`. Two jobs, both publishing to the same +# apt distribution/component: +# +# build-publish — client + web + scripting, on the Ubuntu 26.04 rust-ci image (the client +# needs 24.04-absent libs: SDL3, GTK4 ≥ 4.20). +# build-publish-host — the HOST, on the Ubuntu 24.04 rust-ci-noble image with a from-source +# FFmpeg 8 BUNDLED into the .deb. This lowers the host's glibc floor to 2.39 +# and removes the hard `Depends: libavcodec62`, so the ONE host .deb installs +# on Ubuntu 24.04 LTS through 26.04. (A 26.04-built host .deb is uninstallable +# on 24.04 — the reason this job exists; see packaging/debian/README.md.) +# +# Both compute VERSION identically (scripts/ci/pf-version.sh is deterministic per commit), so the +# host and client packages always share a version line. The release-attach helpers are race-safe +# (ensure_release create-or-fetch; upsert_asset only conflicts on same-name), so the jobs run parallel. # # Registry (public, unom org): https://git.unom.io/unom/-/packages # Box setup (once): see packaging/debian/README.md @@ -83,22 +93,15 @@ jobs: key: cargo-target-v3-${{ env.rustc }}-${{ hashFiles('Cargo.lock') }} restore-keys: cargo-target-v3-${{ env.rustc }}- - - name: Build release host + client + - name: Build release clients env: - PUNKTFUNK_BUILD_VERSION: ${{ env.VERSION }} # stamped into the binary (build.rs) + PUNKTFUNK_BUILD_VERSION: ${{ env.VERSION }} # stamped into the binaries (build.rs) run: | git config --global --add safe.directory "$PWD" # punktfunk-client-session is the Vulkan/Skia streamer the shell execs for a connect — - # both client binaries must ship (build-client-deb.sh installs both). - # --features punktfunk-host/nvenc: the direct-SDK NVENC path (real RFI + recovery anchor on - # Linux NVIDIA; design/linux-direct-nvenc.md). AMD/Intel-safe — NVENC/CUDA is dlopen'd at - # runtime (no link-time dep; identical DT_NEEDED to a plain build), and the encoder is only - # constructed for a CUDA capture frame + PUNKTFUNK_NVENC_DIRECT, never on VAAPI hosts. - # --features punktfunk-host/vulkan-encode: the AMD/Intel twin — raw VK_KHR_video_encode_h265 - # with real RFI (design/linux-vulkan-video-encode.md). Pure Rust ash (no new lib / link dep); - # default on for HEVC (PUNKTFUNK_VULKAN_ENCODE=0 → libav VAAPI), failed open falls back to VAAPI. - cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \ - -p punktfunk-host -p punktfunk-client-linux -p punktfunk-client-session + # both client binaries must ship (build-client-deb.sh installs both). The HOST is built + # separately in the build-publish-host job (Ubuntu 24.04 image + bundled FFmpeg 8). + cargo build --release --locked -p punktfunk-client-linux -p punktfunk-client-session - name: Build + smoke-boot web console (bun preset) # Gate the .deb on a real bun boot: the punktfunk-web .deb runs the Nitro `bun` preset @@ -128,7 +131,7 @@ jobs: - name: Build .debs run: | export PATH="$HOME/.bun/bin:$PATH" - VERSION="$VERSION" bash packaging/debian/build-deb.sh + # host .deb is built in build-publish-host (Ubuntu 24.04 image); this job ships the rest. VERSION="$VERSION" bash packaging/debian/build-client-deb.sh # Reuse CI's bun for the vendored runtime (matches the amd64 runner) instead of downloading. VERSION="$VERSION" BUN_BIN="$(command -v bun || true)" bash packaging/debian/build-web-deb.sh @@ -168,3 +171,101 @@ jobs: for DEB in dist/*.deb; do upsert_asset "$RID" "$DEB" done + + # --------------------------------------------------------------------------------------------- + # The HOST .deb — built on Ubuntu 24.04 (noble) with a from-source FFmpeg 8 BUNDLED, so it installs + # on Ubuntu 24.04 LTS through 26.04 (see the file header + packaging/debian/README.md). Runs in + # parallel with build-publish and publishes to the SAME distribution/component; the version step is + # byte-identical so host and clients share a version line. + build-publish-host: + runs-on: ubuntu-24.04 + container: + image: git.unom.io/unom/punktfunk-rust-ci-noble:latest + timeout-minutes: 90 + steps: + - uses: actions/checkout@v4 + + - name: Version + channel + run: | + git config --global --add safe.directory "$PWD" + eval "$(bash scripts/ci/pf-version.sh)" # -> PF_BASE (one minor ahead of the latest stable tag) + SHORT=$(echo "$GITHUB_SHA" | cut -c1-8) + case "$GITHUB_REF" in + refs/tags/v*) V="${GITHUB_REF_NAME#v}"; DIST=stable ;; + *) V="${PF_BASE}~ci${GITHUB_RUN_NUMBER}.g${SHORT}"; DIST=canary ;; + esac + echo "VERSION=$V" >> "$GITHUB_ENV" + echo "DISTRIBUTION=$DIST" >> "$GITHUB_ENV" + echo "host package version $V -> apt distribution '$DIST'" + + # dpkg-shlibdeps/dpkg-deb + patchelf are baked into rust-ci-noble; python3 is for the + # release-attach helper. Re-install defensively so the job stays green against the PREVIOUS + # image on the same push (docker.yml bootstrap lag) — a no-op once the image ships them. + - name: dpkg-dev + patchelf + python3 + run: | + apt-get update + apt-get install -y --no-install-recommends dpkg-dev patchelf python3 + + - name: Cache keys + run: echo "rustc=$(rustc --version | cut -d' ' -f2)" >> "$GITHUB_ENV" + - uses: actions/cache@v4 + with: + path: | + /usr/local/cargo/registry + /usr/local/cargo/git + key: cargo-home-${{ hashFiles('Cargo.lock') }} + restore-keys: cargo-home- + - uses: actions/cache@v4 + with: + path: target + # Own key: this target dir is built against 24.04's glibc/toolchain and must NOT share + # ci.yml's 26.04 target cache (mixing would poison both). + key: cargo-target-noble-v1-${{ env.rustc }}-${{ hashFiles('Cargo.lock') }} + restore-keys: cargo-target-noble-v1-${{ env.rustc }}- + + - name: Build release host + env: + PUNKTFUNK_BUILD_VERSION: ${{ env.VERSION }} # stamped into the binary (build.rs) + run: | + git config --global --add safe.directory "$PWD" + # Same features the old combined build used: --nvenc (direct-SDK NVENC, real RFI on NVIDIA; + # NVENC/CUDA is dlopen'd — no link dep, so this image needs no libcuda stub) + --vulkan-encode + # (raw VK_KHR_video_encode_h265 on AMD/Intel, pure ash). punktfunk-tray also ships in the host + # .deb (build-deb.sh builds+installs it). ffmpeg-sys-next links the image's bundled FFmpeg 8 + # via PKG_CONFIG_PATH (set in rust-ci-noble). + cargo build --release --locked --features punktfunk-host/nvenc,punktfunk-host/vulkan-encode \ + -p punktfunk-host -p punktfunk-tray + + - name: Build host .deb (FFmpeg bundled) + # BUNDLE_FFMPEG=1 copies the image's /opt/ffmpeg libav* into the package and repoints the + # binary's rpath, so there is no `Depends: libavcodec62` to block install on 24.04. FFMPEG_PREFIX + # defaults to /opt/ffmpeg (the image's ENV also sets it). + run: | + VERSION="$VERSION" BUNDLE_FFMPEG=1 bash packaging/debian/build-deb.sh + + - name: Publish to the Gitea apt registry + env: + TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: | + for DEB in dist/*.deb; do + echo "uploading $DEB" + NAME=$(dpkg-deb -f "$DEB" Package) + VER=$(dpkg-deb -f "$DEB" Version) + ARCH=$(dpkg-deb -f "$DEB" Architecture) + curl -fsS -o /dev/null --user "enricobuehler:$TOKEN" -X DELETE \ + "https://$REGISTRY/api/packages/$OWNER/debian/pool/$DISTRIBUTION/$COMPONENT/$NAME/$VER/$ARCH" || true + curl -fsS --user "enricobuehler:$TOKEN" --upload-file "$DEB" \ + "https://$REGISTRY/api/packages/$OWNER/debian/pool/$DISTRIBUTION/$COMPONENT/upload" + done + echo "published host to $OWNER/debian $DISTRIBUTION/$COMPONENT" + + - name: Attach the host .deb to the Gitea release (stable tags only) + if: startsWith(gitea.ref, 'refs/tags/v') + env: + GITEA_TOKEN: ${{ secrets.REGISTRY_TOKEN }} + run: | + . scripts/ci/gitea-release.sh + RID=$(ensure_release "$GITHUB_REF_NAME" "$GITHUB_REF_NAME" auto) + for DEB in dist/*.deb; do + upsert_asset "$RID" "$DEB" + done diff --git a/.gitea/workflows/docker.yml b/.gitea/workflows/docker.yml index bd141942..9ae958ae 100644 --- a/.gitea/workflows/docker.yml +++ b/.gitea/workflows/docker.yml @@ -39,6 +39,12 @@ jobs: - image: punktfunk-rust-ci dockerfile: ci/rust-ci.Dockerfile context: ci + # Ubuntu 24.04 LTS host builder: same purpose as rust-ci but lowers the host .deb's glibc + # floor to 2.39 and bundles a from-source FFmpeg 8, so the package installs on 24.04 LTS + # (rust-ci's 26.04 build is uninstallable there). Consumed by deb.yml's build-publish-host job. + - image: punktfunk-rust-ci-noble + dockerfile: ci/rust-ci-noble.Dockerfile + context: ci - image: punktfunk-fedora-rpm dockerfile: ci/fedora-rpm.Dockerfile context: ci diff --git a/ci/rust-ci-noble.Dockerfile b/ci/rust-ci-noble.Dockerfile new file mode 100644 index 00000000..64cea533 --- /dev/null +++ b/ci/rust-ci-noble.Dockerfile @@ -0,0 +1,85 @@ +# LTS builder for the punktfunk HOST .deb — Ubuntu 24.04 (noble), the current Ubuntu LTS. +# +# WHY THIS EXISTS (see packaging/debian/README.md → "Ubuntu 24.04 LTS"): +# The default builder (ci/rust-ci.Dockerfile) is Ubuntu 26.04, so the host .deb it produces bakes +# in a glibc 2.41 floor and a hard `Depends: libavcodec62, …` (FFmpeg 8). Ubuntu 24.04 LTS ships +# glibc 2.39 and FFmpeg 6.1 (libavcodec60), so that .deb is uninstallable there — apt reports the +# deps as "too recent". Building the host on 24.04 instead lowers the glibc floor to 2.39 (the +# binary then runs on 24.04 → 26.04), and the ONE library 24.04 is too old for — FFmpeg — is built +# from source here and BUNDLED into the .deb (packaging/debian/build-deb.sh, BUNDLE_FFMPEG=1), so +# the package no longer depends on the distro's libav* at all. Everything else the host links +# (PipeWire, Wayland, xkbcommon, GL/EGL/GBM, Vulkan; opus is vendored via cmake) is soname-compatible +# on 24.04, so this ONE universal host .deb replaces the 26.04-built one for every Ubuntu user. +# +# libcuda is deliberately NOT provided: the host dlopen's libcuda.so.1 at runtime (pf-zerocopy / +# pf-encode) and never link-imports it, so — unlike the full-workspace rust-ci image, which builds +# tests that DO link a cuda stub — this host-only build needs no NVIDIA driver package. NVENC/EGL +# come from whatever driver the target runs, out of band. +# +# Rebuilt+pushed by .gitea/workflows/docker.yml (matrix: punktfunk-rust-ci-noble); consumed by the +# `build-publish-host` job in .gitea/workflows/deb.yml. Bootstrap: like rust-ci, the first deb.yml +# run after this image is added uses the image from a PRIOR docker.yml push — seed it once manually +# (docker build -f ci/rust-ci-noble.Dockerfile -t … ci && docker push) before the host job can run. +FROM ubuntu:24.04 +ENV DEBIAN_FRONTEND=noninteractive + +RUN apt-get update && apt-get install -y --no-install-recommends \ + # toolchain + bindgen; nodejs runs the JS actions (checkout/cache); unzip for the rustup installer's deps + build-essential clang libclang-dev pkg-config cmake git curl ca-certificates nodejs unzip \ + # .deb assembly: dpkg-shlibdeps/dpkg-deb; patchelf repoints the binary's rpath at the bundled FFmpeg + dpkg-dev patchelf \ + # FFmpeg 8 build deps: nasm (asm), VAAPI (libva/libdrm) so the built libav* keep the AMD/Intel + # encode backend the host auto-selects; zlib (libavformat). NVENC needs only headers (below), dlopen'd. + nasm libva-dev libdrm-dev zlib1g-dev \ + # host link deps present on 24.04 with sonames compatible up to 26.04 + libpipewire-0.3-dev libwayland-dev libxkbcommon-dev \ + libgl-dev libegl-dev libgbm-dev libvulkan-dev \ + && rm -rf /var/lib/apt/lists/* + +# --- FFmpeg 8 from source -> /opt/ffmpeg (shared libs + .pc files) ---------------------------------- +# libavcodec.so.62, matching the 26.04 line's soname so the host behaves identically. This is an +# LGPL build (no --enable-gpl / --enable-nonfree) so bundling the .so's into an MIT/Apache .deb stays +# license-clean — LGPL's relink clause is satisfied by dynamic linking, and the only encoders the host +# calls (h264/hevc/av1 _nvenc + _vaapi, plus scale_vaapi/hwmap filters; software H.264 fallback is the +# BSD-2 openh264 crate, NOT FFmpeg libx264) are all LGPL-compatible. +# Sourced from the official FFmpeg GitHub mirror by release tag, NOT ffmpeg.org: the CI build network +# can't reach ffmpeg.org (curl times out) but reaches github.com fine. The `nX.Y` tag pins the version +# (n8.0 -> libavcodec 62); bump it to move FFmpeg. Immutable-tag clone, so no separate checksum needed. +ARG FFMPEG_TAG=n8.0 +# nv-codec-headers must MATCH the FFmpeg version: its `master` is NVENC SDK 13, which renamed +# NV_ENC_CLOCK_TIMESTAMP_SET.countingType -> countingTypeLSB and won't compile against FFmpeg 8.0's +# nvenc.c. Pin the last SDK-12 tag (has the field FFmpeg 8.0 expects). Bump alongside FFMPEG_TAG. +ARG NVHDR_TAG=n12.2.72.0 +RUN set -eux; \ + # nv-codec-headers: the NVENC/NVDEC headers FFmpeg's --enable-nvenc needs (headers only, no lib — + # the driver is dlopen'd at runtime). Installs ffnvcodec.pc under /usr/local/lib/pkgconfig. + git clone --depth 1 --branch "$NVHDR_TAG" https://github.com/FFmpeg/nv-codec-headers.git /tmp/nvhdr; \ + make -C /tmp/nvhdr install PREFIX=/usr/local; \ + git clone --depth 1 --branch "$FFMPEG_TAG" https://github.com/FFmpeg/FFmpeg.git /tmp/ffmpeg; \ + cd /tmp/ffmpeg; \ + PKG_CONFIG_PATH=/usr/local/lib/pkgconfig ./configure \ + --prefix=/opt/ffmpeg \ + --enable-shared --disable-static \ + --disable-doc --disable-programs --disable-debug \ + --enable-nvenc --enable-vaapi \ + --extra-cflags=-I/usr/local/include --extra-ldflags=-L/usr/local/lib; \ + make -j"$(nproc)"; make install; \ + cd /; rm -rf /tmp/ffmpeg /tmp/nvhdr; \ + # sanity: the soname we expect to bundle (libavcodec.so.62 on FFmpeg 8) + test -e /opt/ffmpeg/lib/libavcodec.so.62 + +# ffmpeg-sys-next discovers FFmpeg via pkg-config; point it at the bundled build. PKG_CONFIG_PATH is +# PREPENDED to pkg-config's default dirs (not a replacement — that's PKG_CONFIG_LIBDIR), so PipeWire / +# Wayland / libva / … still resolve from the system. FFMPEG_PREFIX is read by build-deb.sh's bundler. +ENV PKG_CONFIG_PATH=/opt/ffmpeg/lib/pkgconfig \ + FFMPEG_PREFIX=/opt/ffmpeg + +# Toolchain shared across CI users (jobs may run as different uids). +ENV RUSTUP_HOME=/usr/local/rustup \ + CARGO_HOME=/usr/local/cargo \ + PATH=/usr/local/cargo/bin:$PATH +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --no-modify-path --profile minimal \ + --component rustfmt,clippy \ + && chmod -R a+w "$RUSTUP_HOME" "$CARGO_HOME" \ + && rustc --version && cargo clippy --version && cargo fmt --version diff --git a/packaging/debian/README.md b/packaging/debian/README.md index a66c4a7a..ce0f495f 100644 --- a/packaging/debian/README.md +++ b/packaging/debian/README.md @@ -18,11 +18,24 @@ here.) Package layout mirrors the Fedora RPM (`../rpm/punktfunk.spec`): the host binary, the `/dev/uinput` udev rule, the systemd **user** unit, headless session helpers, the example config, and the OpenAPI -doc. Runtime `Depends` are computed by `dpkg-shlibdeps` from the binary itself (built in the Ubuntu -26.04 rust-ci image, so the lib soname package names match the target). The NVIDIA driver +doc. Runtime `Depends` are computed by `dpkg-shlibdeps` from the binary itself. The NVIDIA driver (`libnvidia-encode` / `libEGL_nvidia` / `libcuda`) is **not** a dependency — it's installed out of band, like on the RPM side. +## Ubuntu 24.04 LTS (and why it needs a special build) + +`punktfunk-host` needs **FFmpeg 8** (libavcodec62), but Ubuntu 24.04 LTS ships FFmpeg 6.1 +(libavcodec60). So a host `.deb` built the obvious way — on the same Ubuntu 26.04 image as the +client (`ci/rust-ci.Dockerfile`) — declares `Depends: libavcodec62, …` and a glibc-2.41 floor that +24.04's apt can't satisfy ("the required packages are too recent"). To fix that, the host `.deb` is +instead built on an **Ubuntu 24.04 image** (`ci/rust-ci-noble.Dockerfile`) that carries a from-source +FFmpeg 8, and that FFmpeg is **bundled into the package** (`build-deb.sh BUNDLE_FFMPEG=1` → the +libav* land in `/usr/lib/punktfunk-host`, the binary's rpath points there, and the libav* sonames are +dropped from `Depends`). The result is **one** host `.deb` that installs on **Ubuntu 24.04 LTS through +26.04** (glibc floor 2.39; no distro-FFmpeg dependency). The client/web/scripting `.deb`s still build +on 26.04 (the native client needs SDL3 / GTK4 ≥ 4.20, absent on 24.04) — install the client on the box +you stream *to*, which is independent of the host's distro. + ## Install on a host (one-time) The registry is public, so no apt auth is needed — just trust the repo's signing key: @@ -144,5 +157,17 @@ VERSION=0.0.1 bash packaging/debian/build-deb.sh # -> dist/punktfunk-host_0.0. ``` Needs `dpkg-dev` (`dpkg-shlibdeps`, `dpkg-deb`). It builds the release binary first if missing. -Build it in the rust-ci image (or on an Ubuntu 26.04 box) so the resolved `Depends` match the -hosts; building on a GPU box is fine — the NVIDIA driver lib is filtered out either way. +Building on a GPU box is fine — the NVIDIA driver lib is filtered out either way. + +That plain invocation hard-depends on the build box's system FFmpeg, so it only installs on a box +with the same libav* soname. For the **universal** package CI ships (installs on 24.04 LTS → 26.04), +build it in the noble image with FFmpeg bundled: + +```sh +docker build -f ci/rust-ci-noble.Dockerfile -t pf-noble ci +docker run --rm -v "$PWD:/src" -w /src pf-noble \ + bash -lc 'VERSION=0.0.1 BUNDLE_FFMPEG=1 bash packaging/debian/build-deb.sh' +``` + +`BUNDLE_FFMPEG=1` needs `patchelf` and an FFmpeg install at `FFMPEG_PREFIX` (default `/opt/ffmpeg`, +which the noble image provides). diff --git a/packaging/debian/build-deb.sh b/packaging/debian/build-deb.sh index 67ac6ee3..3505683b 100755 --- a/packaging/debian/build-deb.sh +++ b/packaging/debian/build-deb.sh @@ -13,13 +13,23 @@ # FFI): on a GPU-less builder it resolves to no package, and we must never hard-depend on a # specific libnvidia-compute- anyway — NVENC/EGL come from the driver, out of band. # -# Usage: VERSION=0.0.1~ci42.gdeadbee [ARCH=amd64] bash packaging/debian/build-deb.sh +# BUNDLE_FFMPEG=1 (Ubuntu 24.04 LTS builds, ci/rust-ci-noble.Dockerfile): instead of hard-depending +# on the distro's libav* — which don't exist on 24.04 (it ships FFmpeg 6.1 / libavcodec60, the host +# needs 8 / libavcodec62) — copy a from-source FFmpeg into /usr/lib/punktfunk-host, repoint the +# binary's rpath there, and drop the libav*/libsw*/libpostproc sonames from the auto Depends. Set +# FFMPEG_PREFIX to that FFmpeg's install prefix (default /opt/ffmpeg, as the noble image sets it). +# See packaging/debian/README.md → "Ubuntu 24.04 LTS". +# +# Usage: VERSION=0.0.1~ci42.gdeadbee [ARCH=amd64] [BUNDLE_FFMPEG=1] bash packaging/debian/build-deb.sh # Output: dist/punktfunk-host__.deb set -euo pipefail VERSION="${VERSION:?set VERSION (e.g. 0.0.1 or 0.0.1~ci42.gdeadbee)}" ARCH="${ARCH:-amd64}" PKG="punktfunk-host" +BUNDLE_FFMPEG="${BUNDLE_FFMPEG:-0}" +FFMPEG_PREFIX="${FFMPEG_PREFIX:-/opt/ffmpeg}" +LIBDIR_REL="usr/lib/$PKG" # bundled FFmpeg lands here: /usr/lib/punktfunk-host ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)" cd "$ROOTDIR" @@ -119,8 +129,41 @@ printf '%s (%s) stable; urgency=medium\n\n * Automated build %s.\n\n -- unom /dev/null || echo 'Thu, 01 Jan 1970 00:00:00 +0000')" \ | gzip -9n > "$DOCDIR/changelog.Debian.gz" +# --- bundled FFmpeg (Ubuntu 24.04 LTS builds) -------------------------------- +# Copy the from-source libav*/libsw*/libpostproc .so's into /usr/lib/punktfunk-host and repoint the +# binary at them, so the package carries FFmpeg 8 instead of depending on a distro libavcodec62 that +# 24.04 doesn't have. BUNDLED_LIBS is fed to dpkg-shlibdeps below so the libs' OWN external deps +# (libva2, libdrm2, …, all present on 24.04) still become Depends. +BUNDLED_LIBS="" +if [ "$BUNDLE_FFMPEG" = "1" ]; then + command -v patchelf >/dev/null || { echo "BUNDLE_FFMPEG=1 needs patchelf" >&2; exit 1; } + [ -d "$FFMPEG_PREFIX/lib" ] || { echo "FFMPEG_PREFIX=$FFMPEG_PREFIX has no lib/ — build FFmpeg first" >&2; exit 1; } + DEST="$STAGE/$LIBDIR_REL" + install -d "$DEST" + # cp -a preserves the SONAME symlink chain (libavcodec.so -> .so.62 -> .so.62.x.x); the loader + # resolves the binary's DT_NEEDED (libavcodec.so.62) to the middle link. + shopt -s nullglob + for so in "$FFMPEG_PREFIX"/lib/lib{avcodec,avformat,avutil,avfilter,avdevice,swscale,swresample,postproc}.so*; do + cp -a "$so" "$DEST/" + done + shopt -u nullglob + ls "$DEST"/libavcodec.so.* >/dev/null 2>&1 || { echo "no libav* found under $FFMPEG_PREFIX/lib" >&2; exit 1; } + # Each bundled lib finds its siblings (libavcodec needs libavutil) via its own $ORIGIN RUNPATH; + # patch only the real versioned files, not the symlinks. The executable then finds the top-level + # libs via ../lib/$PKG, written as DT_RPATH (--force-rpath) so it's also searched transitively — + # belt-and-suspenders against DT_RUNPATH's non-transitivity. + for so in "$DEST"/*.so.*; do + [ -L "$so" ] && continue + patchelf --set-rpath '$ORIGIN' "$so" + done + patchelf --force-rpath --set-rpath "\$ORIGIN/../lib/$PKG" "$STAGE/usr/bin/$PKG" + BUNDLED_LIBS="$(printf '%s ' "$DEST"/*.so.*)" + echo "==> bundled FFmpeg from $FFMPEG_PREFIX into /$LIBDIR_REL" +fi + # --- dependencies ------------------------------------------------------------ -# Auto: the binary's directly-linked shared libs (libcuda ignored, see header). +# Auto: the binary's directly-linked shared libs (libcuda ignored, see header). In bundle mode the +# bundled .so's are appended so their external deps (libva2/libdrm2/…) are captured too. SHLIB_TMP="$(mktemp -d)" mkdir -p "$SHLIB_TMP/debian" cat > "$SHLIB_TMP/debian/control" </dev/null \ - | sed -n 's/^shlibs:Depends=//p')" +# In bundle mode the libav* live in FFMPEG_PREFIX/lib — not a standard loader path, and the +# target/release binary carries no rpath (only the staged copy does) — so dpkg-shlibdeps can't +# locate libavcodec.so.62 and exits 2. Point it there via LD_LIBRARY_PATH. Stderr is captured so a +# future resolution failure is visible instead of swallowed. +SHDEPS_RAW="$( + cd "$SHLIB_TMP" + if [ "$BUNDLE_FFMPEG" = "1" ]; then export LD_LIBRARY_PATH="$FFMPEG_PREFIX/lib"; fi + dpkg-shlibdeps -O --ignore-missing-info "$ROOTDIR/$BIN" $BUNDLED_LIBS 2>"$SHLIB_TMP/err" \ + | sed -n 's/^shlibs:Depends=//p' +)" || { echo "dpkg-shlibdeps failed (exit $?):" >&2; sed 's/^/ /' "$SHLIB_TMP/err" >&2; rm -rf "$SHLIB_TMP"; exit 1; } rm -rf "$SHLIB_TMP" [ -n "$SHDEPS_RAW" ] || { echo "dpkg-shlibdeps produced no deps — is dpkg-dev installed?" >&2; exit 1; } @@ -139,8 +190,12 @@ rm -rf "$SHLIB_TMP" # GPU-less builder (stub, no owning package), but on a box WITH the driver shlibdeps resolves # libcuda.so.1 -> libnvidia-compute- and would pin that exact driver build. NVENC/EGL are # provided by whatever driver the host runs, so this must never be a package dependency. +# In bundle mode also drop the FFmpeg sonames: they're shipped inside the package (/usr/lib/$PKG), +# not pulled from apt, so a `Depends: libavcodec62` would wrongly re-block install on 24.04. +FILTER='^(libnvidia-compute|libcuda)' +[ "$BUNDLE_FFMPEG" = "1" ] && FILTER='^(libnvidia-compute|libcuda|libav|libsw|libpostproc)' SHDEPS="$(printf '%s' "$SHDEPS_RAW" | tr ',' '\n' | sed 's/^ *//; s/ *$//' \ - | grep -ivE '^(libnvidia-compute|libcuda)' | awk 'NF' | paste -sd ',' - | sed 's/,/, /g')" + | grep -ivE "$FILTER" | awk 'NF' | paste -sd ',' - | sed 's/,/, /g')" [ -n "$SHDEPS" ] || { echo "no deps left after filtering — unexpected" >&2; exit 1; } # Manual additions shlibdeps can't see: