#!/usr/bin/env bash
# Build the punktfunk-host RPM from the committed tree, for the Gitea RPM registry (Bazzite).
#
# Counterpart to ../debian/build-deb.sh. The library Requires (libavcodec.so.NN, …) are
# auto-generated by rpmbuild from the binary it links — so build this in the Fedora 43 image
# (ci/fedora-rpm.Dockerfile) to match Bazzite's sonames. libcuda is excluded in the spec.
#
# Usage: PF_VERSION=0.0.1 [PF_RELEASE=0.ci42.gdeadbee] bash packaging/rpm/build-rpm.sh
# Output: dist/punktfunk-<version>-<release>.<arch>.rpm  (+ the -debuginfo/-debugsource subpkgs)
set -euo pipefail

PF_VERSION="${PF_VERSION:-0.5.0}"   # canary base; keep one minor ahead of the latest stable release
PF_RELEASE="${PF_RELEASE:-1}"
# PF_WITH_WEB=1 builds the punktfunk-web subpackage too (needs `bun` on PATH — present in the CI
# builder image, not in a plain mock chroot). Default off so a bare `rpmbuild`/COPR still works.
WEB_OPT=()
[ "${PF_WITH_WEB:-0}" = "1" ] && WEB_OPT=(--with web)
# PF_PREBUILT_WEB_OUTPUT: an already-built web/.output to package instead of building the console a
# second time (see the pf_prebuilt_web note in the spec's %build). CI restores one from the shared
# actions cache and points this at it; a plain build leaves it unset and nothing changes.
# Deliberately validated HERE rather than trusted: an empty or half-restored cache directory must
# fall back to building, not produce a console-less package. The spec re-asserts the Bun.serve
# marker on whatever it ends up with.
PREBUILT_WEB_OPT=()
if [ "${PF_WITH_WEB:-0}" = "1" ] && [ -n "${PF_PREBUILT_WEB_OUTPUT:-}" ]; then
  if [ -f "${PF_PREBUILT_WEB_OUTPUT}/server/index.mjs" ]; then
    # rpmbuild resolves the macro inside the extracted tarball dir, so it must be absolute.
    PREBUILT_WEB_ABS="$(cd "$PF_PREBUILT_WEB_OUTPUT" && pwd)"
    PREBUILT_WEB_OPT=(--define "pf_prebuilt_web ${PREBUILT_WEB_ABS}")
    echo "==> reusing prebuilt web console: $PREBUILT_WEB_ABS"
  else
    echo "==> PF_PREBUILT_WEB_OUTPUT=$PF_PREBUILT_WEB_OUTPUT has no server/index.mjs — building the console" >&2
  fi
fi
# PF_WITH_SCRIPTING=1 builds the punktfunk-scripting subpackage (the plugin/script runner). Same bun
# requirement as web; default off so a bare `rpmbuild`/COPR still works.
SCRIPTING_OPT=()
[ "${PF_WITH_SCRIPTING:-0}" = "1" ] && SCRIPTING_OPT=(--with scripting)
# PF_WITHOUT_HOST=1 drops the host binary, the tray and the main package, leaving only
# punktfunk-client. This is what an aarch64 build uses: the client is portable, the host's encode
# stack (NVENC/QSV/AMF) is x86. Harmless on x86_64 too, if you only want the client RPM.
HOST_OPT=()
[ "${PF_WITHOUT_HOST:-0}" = "1" ] && HOST_OPT=(--without host)
ROOTDIR="$(cd "$(dirname "$0")/../.." && pwd)"
cd "$ROOTDIR"

TOP="$(mktemp -d)"
trap 'rm -rf "$TOP"' EXIT
mkdir -p "$TOP"/{SOURCES,SPECS,BUILD,BUILDROOT,RPMS,SRPMS}

# Source tarball with the prefix %autosetup expects (punktfunk-<version>/). From HEAD so the
# build is reproducible from a commit (CI checks one out); the spec is read from the working
# tree directly, so spec edits apply without a re-commit.
git archive --format=tar.gz --prefix="punktfunk-${PF_VERSION}/" \
  -o "$TOP/SOURCES/punktfunk-${PF_VERSION}.tar.gz" HEAD

# libcuda link stub (self-maintaining). The zerocopy FFI links the NVIDIA driver lib (-lcuda), but
# the CI builder has no GPU and never RUNS CUDA. Synthesize a stub libcuda that DEFINES every cu*
# driver symbol the host source references, derived from the source HERE so a newly-added cu* call
# can't silently break the link. (ci/fedora-rpm.Dockerfile ships a frozen list that went stale —
# undefined cuStreamCreateWithPriority/cuMemcpy2DAsync_v2/…; this regen supersedes it.) Defining
# extra unused symbols is harmless; a missing one fails the link. Only when /usr/lib64 is writable
# (CI image runs as root) — COPR/mock provides the real cuda-cudart-devel stub instead.
if [ "${PF_WITHOUT_HOST:-0}" != "1" ] && [ "$(id -u)" = 0 ] && [ -d /usr/lib64 ]; then
  CU_SYMS="$(grep -rhoE '\bcu[A-Z][A-Za-z0-9_]*' crates/punktfunk-host/src/ | sort -u || true)"
  if [ -n "$CU_SYMS" ]; then
    STUB_C="$(mktemp --suffix=.c)"
    for s in $CU_SYMS; do printf 'int %s(void){return 0;}\n' "$s" >> "$STUB_C"; done
    gcc -shared -fPIC -Wl,-soname,libcuda.so.1 -o /usr/lib64/libcuda.so.1 "$STUB_C"
    ln -sf libcuda.so.1 /usr/lib64/libcuda.so
    rm -f "$STUB_C"; ldconfig 2>/dev/null || true
    echo "== libcuda stub regenerated from source: $(printf '%s\n' "$CU_SYMS" | wc -l) symbols =="
  fi
fi

# --nodeps: the spec's BuildRequires (cargo, rust, *-devel) are for COPR's mock chroot, which
# resolves them from RPMs. Our builder image provides the toolchain via rustup (so
# rust-toolchain.toml's pinned channel works) and the -devel libs via dnf, neither of which
# rpmbuild's RPM-level check sees — skip it; a genuinely missing dep fails the compile/link.
rpmbuild -bb --nodeps "${WEB_OPT[@]}" "${SCRIPTING_OPT[@]}" "${HOST_OPT[@]}" \
  "${PREBUILT_WEB_OPT[@]}" \
  --define "_topdir $TOP" \
  --define "pf_version ${PF_VERSION}" \
  --define "pf_release ${PF_RELEASE}" \
  --define "pf_channel $(case "${PF_RELEASE:-1}" in 0.ci*) echo canary ;; *) echo stable ;; esac)" \
  packaging/rpm/punktfunk.spec

mkdir -p dist
find "$TOP/RPMS" -name '*.rpm' -exec cp -v {} dist/ \;
# The libcuda leak check applies to the HOST package (the only thing that links the driver); a
# client-only build has no such package to inspect.
if [ "${PF_WITHOUT_HOST:-0}" = "1" ]; then
  echo "== client-only build: no host RPM, libcuda check not applicable =="
else
  echo "== Requires (must NOT contain libcuda) =="
  rpm -qp --requires dist/punktfunk-${PF_VERSION}-*.rpm 2>/dev/null | grep -iE 'cuda|nvidia' \
    && echo "  !! NVIDIA/CUDA leak !!" || echo "  clean"
fi
