#!/usr/bin/env bash # # PRODUCER for the committed LGPL FFmpeg binaries. # # Run by a maintainer ON A VERSION BUMP (mac: native clang; win: mingw-w64 cross-compile). # It builds both binaries FROM SOURCE, minimal and dependency-free, validates them, writes them # to resources/ffmpeg// (which are COMMITTED), and records resources/ffmpeg/MANIFEST.json # (version + per-platform SHA-256 + config). Normal app builds do NOT run this — they package the # committed binaries; CI validates them (see the ffmpeg licence guard test). # # Secretary42 invokes ffmpeg only as a subprocess to make 16 kHz mono s16le WAV for whisper.cpp # (never links libav*), so an LGPL build needs only a notice + source offer, no relinking. # # Usage: scripts/build-ffmpeg-lgpl.sh [mac|win|all] (default: all) # set -euo pipefail REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" OUT_DIR="$REPO_ROOT/resources/ffmpeg" WORK_DIR="$(mktemp -d "${TMPDIR:-/tmp}/s42-ffmpeg-build.XXXXXX")" trap 'rm -rf "$WORK_DIR"' EXIT # --- pinned source + targets --- FFMPEG_VERSION="7.1.1" FFMPEG_URL="https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.xz" FFMPEG_SHA256="733984395e0dbbe5c046abda2dc49a5544e7e0e1e2366bba849222ae9e3a03b1" MIN_MACOS="13.0" # keep in sync with electron-builder mac.minimumSystemVersion + the native build scripts # Durable public LGPL source-offer base (per-version dir appended). The bundle for this URL is # produced by scripts/build-ffmpeg-source-offer.sh; keep in sync with resources/ffmpeg/README.md. SOURCE_OFFER_BASE="https://license.secretary42.insight42.com/licenses/ffmpeg" # Minimal LGPL configure — covers exactly the recorder pipeline (WebM/Matroska + Ogg + WAV demux, # native Opus/Vorbis/PCM decode, resample, WAV mux). --disable-autodetect ⇒ no external (and thus # no GPL/nonfree) library is linked; avdevice/swscale/postproc dropped (audio-only). COMMON_FLAGS=( --disable-gpl --disable-nonfree --disable-everything --disable-autodetect --disable-network --disable-x86asm --disable-avdevice --disable-swscale --disable-postproc --disable-doc --disable-htmlpages --disable-manpages --disable-podpages --disable-txtpages --disable-ffplay --disable-ffprobe --enable-small --enable-protocol=file,pipe --enable-demuxer=matroska,wav,w64,ogg,mov,mp3,flac,aac,aiff --enable-decoder=opus,vorbis,pcm_s16le,pcm_s24le,pcm_s32le,pcm_f32le,pcm_u8,pcm_mulaw,pcm_alaw,aac,mp3,flac,alac --enable-parser=opus,vorbis,aac,flac,mpegaudio --enable-filter=aresample,aformat,anull,atrim --enable-encoder=pcm_s16le --enable-muxer=wav --enable-swresample ) # All status logs go to STDERR so command substitution (src="$(fetch_source)") captures only paths. log() { printf '\n[build-ffmpeg-lgpl] %s\n' "$*" >&2; } die() { printf '[build-ffmpeg-lgpl] ERROR: %s\n' "$*" >&2; exit 1; } sha256() { shasum -a 256 "$1" | awk '{print $1}'; } # Strict, positive LGPL assertion on the embedded `configure` line. `strings` is REQUIRED # (a missing tool is a hard failure, not a warning — this is a legal gate). NB: capture the # `strings` output into a variable and bash-match it; piping a long stream into `grep -q` would # SIGPIPE `strings` and trip `set -o pipefail`. assert_lgpl_config() { local bin="$1" cfg command -v strings >/dev/null 2>&1 || die "'strings' is required for the LGPL provenance check" cfg="$(strings -a "$bin" || true)" [[ "$cfg" == *"--disable-gpl"* ]] || die "$bin: missing --disable-gpl in embedded config" [[ "$cfg" == *"--disable-nonfree"* ]] || die "$bin: missing --disable-nonfree in embedded config" # Manifest declares LGPL-2.1+; --enable-version3 would activate LGPLv3 terms (and GPL/nonfree are # GPL-only). Reject all three at the producer, matching the unit guard + source-offer generator. if [[ "$cfg" == *"--enable-gpl"* || "$cfg" == *"--enable-nonfree"* || "$cfg" == *"--enable-version3"* ]]; then die "$bin advertises --enable-gpl/--enable-nonfree/--enable-version3" fi } assert_mac() { local bin="$1" minos ver file "$bin" | grep -q 'arm64' || die "$bin is not arm64" if file "$bin" | grep -q 'x86_64'; then die "$bin is x86_64 (wrong arch under mac-arm64)"; fi minos="$(otool -l "$bin" | awk '/LC_BUILD_VERSION/{f=1} f&&/minos/{print $2; exit}')" [ "$minos" = "$MIN_MACOS" ] || die "$bin minos=$minos, expected $MIN_MACOS" ver="$("$bin" -hide_banner -version 2>/dev/null | head -1 || true)" [[ "$ver" == *"version ${FFMPEG_VERSION}"* ]] || die "$bin is not FFmpeg ${FFMPEG_VERSION}" assert_lgpl_config "$bin" } assert_win() { local bin="$1" s file "$bin" | grep -q 'PE32+' || die "$bin is not a PE32+ (win64) binary" file "$bin" | grep -q 'x86-64' || die "$bin is not x86-64" s="$(strings -a "$bin" || true)" [[ "$s" == *"${FFMPEG_VERSION}"* ]] || die "$bin: FFmpeg ${FFMPEG_VERSION} string not found" assert_lgpl_config "$bin" } fetch_source() { # fetch_source