#!/bin/bash
# Builds the LGPL FFmpeg that ships inside VerseCut 3.0 for macOS.
#
#   Universal binary (Apple silicon + Intel), macOS 11 or later.
#   LGPL v2.1 or later. No GPL or non-free code: no x264, no x265, no fdk-aac.
#   H.264 and AAC are encoded only by Apple's VideoToolbox / AudioToolbox.
#   MP3 is encoded by LAME 3.100 (LGPL). Network protocols are disabled.
#
# Run once on a Mac with Apple's Command Line Tools installed (xcode-select --install):
#     bash build-ffmpeg-macos.sh            # about 15-25 minutes
# Output: ~/Downloads/VerseCut-3.0-build/ffmpeg-macos/  (ffmpeg, ffprobe, licences, sources, SHA256SUMS)
set -euo pipefail

FF_VER=9.0.2
FF_SHA=8c3850283eb25fa026482078a04051e0be17347b09ef81a0849bec15a96e002e
FF_URL="https://ffmpeg.org/releases/ffmpeg-${FF_VER}.tar.xz"
LAME_VER=3.100
LAME_SHA=ddfe36cab873794038ae2c1210557ad34857a4b6bdc515785d1da9e175b1da1e
LAME_URL="https://downloads.sourceforge.net/project/lame/lame/${LAME_VER}/lame-${LAME_VER}.tar.gz"
NASM_VER=3.02
NASM_SHA=87336eba53b4acfe917424ab5d500d2b0054d9f5148d35c2273ccf2cfb712f0d
NASM_URL="https://www.nasm.us/pub/nasm/releasebuilds/${NASM_VER}/nasm-${NASM_VER}.tar.xz"
MIN_MACOS=11.0

FINISH_ONLY=0; if [ "${1:-}" = "--finish" ]; then FINISH_ONLY=1; shift; fi   # --finish: re-run only the checks on an existing build
OUT="${1:-$HOME/Downloads/VerseCut-3.0-build/ffmpeg-macos}"
WORK="$(mktemp -d /tmp/versecut-ffmpeg.XXXXXX)"
LOG="$OUT/build.log"          # kept beside the output so it can be read afterwards
JOBS="$(sysctl -n hw.ncpu 2>/dev/null || echo 4)"
HOST="$(uname -m)"

say()  { printf '\n\033[33m== %s\033[0m\n' "$*"; }
fail() { printf '\n\033[31mBUILD FAILED: %s\033[0m\nLast lines of %s:\n' "$*" "$LOG"; tail -40 "$LOG"; exit 1; }
trap 'fail "unexpected error on line $LINENO"' ERR

[ "$(uname)" = "Darwin" ] || { echo "Run this on a Mac."; exit 1; }
xcode-select -p >/dev/null 2>&1 || { echo "Install Apple's Command Line Tools first:  xcode-select --install"; exit 1; }
command -v clang >/dev/null || { echo "clang not found - install the Command Line Tools."; exit 1; }

mkdir -p "$OUT" "$OUT/source" "$OUT/licences"
[ "$FINISH_ONLY" = 1 ] || : > "$LOG"
echo "Working in $WORK (log: $LOG)"

fetch() {  # url sha file
  local url="$1" sha="$2" file="$3"
  if [ ! -f "$OUT/source/$file" ] || ! echo "$sha  $OUT/source/$file" | shasum -a 256 -c - >/dev/null 2>&1; then
    curl -fL --retry 3 --connect-timeout 20 -o "$OUT/source/$file" "$url" >>"$LOG" 2>&1 || fail "download of $file"
  fi
  echo "$sha  $OUT/source/$file" | shasum -a 256 -c - >>"$LOG" 2>&1 || fail "checksum of $file does not match"
  echo "  verified $file"
}

if [ "$FINISH_ONLY" = 0 ]; then
say "Downloading and verifying sources"
fetch "$FF_URL"   "$FF_SHA"   "ffmpeg-${FF_VER}.tar.xz"
fetch "$LAME_URL" "$LAME_SHA" "lame-${LAME_VER}.tar.gz"
fetch "$NASM_URL" "$NASM_SHA" "nasm-${NASM_VER}.tar.xz"

say "Building NASM (assembler used only for the Intel slice)"
cd "$WORK" && tar xf "$OUT/source/nasm-${NASM_VER}.tar.xz"
cd "nasm-${NASM_VER}"
./configure --prefix="$WORK/tools" >>"$LOG" 2>&1
{ make -j"$JOBS" >>"$LOG" 2>&1 && make install >>"$LOG" 2>&1; } || fail "NASM build"
NASM="$WORK/tools/bin/nasm"

build_arch() {
  local arch="$1" prefix="$WORK/$1" host ffarch cross=() build
  if [ "$HOST" = arm64 ]; then build=aarch64-apple-darwin; else build=x86_64-apple-darwin; fi
  if [ "$arch" = arm64 ]; then host=aarch64-apple-darwin; ffarch=aarch64; else host=x86_64-apple-darwin; ffarch=x86_64; fi
  [ "$arch" = "$HOST" ] || cross=(--enable-cross-compile)
  local cc="clang -arch $arch -mmacosx-version-min=$MIN_MACOS"

  say "[$arch] LAME $LAME_VER"
  rm -rf "$WORK/lame-$arch" && mkdir -p "$WORK/lame-$arch" && cd "$WORK/lame-$arch"
  tar xf "$OUT/source/lame-${LAME_VER}.tar.gz" --strip-components=1
  sed -i '' '/lame_init_old/d' include/libmp3lame.sym 2>/dev/null || true
  ac_cv_prog_cc_c23=no CC="$cc" CFLAGS="-O2 -Wno-implicit-function-declaration" \
    ./configure --host="$host" --build="$build" --prefix="$prefix" --disable-shared --enable-static \
      --disable-frontend --disable-dependency-tracking --disable-debug >>"$LOG" 2>&1
  { make -j"$JOBS" >>"$LOG" 2>&1 && make install >>"$LOG" 2>&1; } || fail "LAME build ($arch)"

  say "[$arch] FFmpeg $FF_VER (LGPL)"
  rm -rf "$WORK/ffmpeg-$arch" && mkdir -p "$WORK/ffmpeg-$arch" && cd "$WORK/ffmpeg-$arch"
  tar xf "$OUT/source/ffmpeg-${FF_VER}.tar.xz" --strip-components=1
  ./configure \
    --prefix="$prefix" --arch="$ffarch" --target-os=darwin ${cross[@]+"${cross[@]}"} \
    --cc="$cc" --cxx="clang++ -arch $arch -mmacosx-version-min=$MIN_MACOS" --x86asmexe="$NASM" \
    --extra-cflags="-I$prefix/include" --extra-ldflags="-L$prefix/lib -mmacosx-version-min=$MIN_MACOS" \
    --disable-autodetect --disable-gpl --disable-nonfree \
    --enable-videotoolbox --enable-audiotoolbox --enable-zlib --enable-bzlib \
    --enable-libmp3lame --enable-pthreads \
    --disable-network --disable-ffplay --disable-doc --disable-debug \
    --disable-shared --enable-static \
    --extra-version=versecut >>"$LOG" 2>&1 || fail "FFmpeg configure ($arch) - see ffbuild/config.log"
  { make -j"$JOBS" >>"$LOG" 2>&1 && make install >>"$LOG" 2>&1; } || fail "FFmpeg build ($arch)"
  cp "$WORK/ffmpeg-$arch/COPYING.LGPLv2.1" "$OUT/licences/FFmpeg-COPYING.LGPLv2.1"
  cp "$WORK/ffmpeg-$arch/LICENSE.md" "$OUT/licences/FFmpeg-LICENSE.md"
  cp "$WORK/lame-$arch/COPYING" "$OUT/licences/LAME-COPYING"
}

build_arch arm64
build_arch x86_64
fi

if [ "$FINISH_ONLY" = 0 ]; then
say "Making universal binaries"
for t in ffmpeg ffprobe; do
  lipo -create "$WORK/arm64/bin/$t" "$WORK/x86_64/bin/$t" -output "$OUT/$t"
  codesign --force --sign - "$OUT/$t" >>"$LOG" 2>&1
  chmod 755 "$OUT/$t"
done
fi

say "Checking the result"
BC="$("$OUT/ffmpeg" -hide_banner -buildconf)"
echo "$BC" > "$OUT/BUILDCONF.txt"
if echo "$BC" | grep -q -- "--enable-gpl\|--enable-nonfree\|libx264\|libx265\|fdk"; then fail "GPL or non-free option found"; fi
"$OUT/ffmpeg" -hide_banner -L 2>&1 | head -5 > "$OUT/LICENSE-STATEMENT.txt"
LIC_TEXT="$(tr -s "\n " "  " < "$OUT/LICENSE-STATEMENT.txt")"
case "$LIC_TEXT" in *"Lesser General Public License"*) echo "  licence: LGPL" ;; *) fail "ffmpeg -L does not report LGPL" ;; esac
ENC="$("$OUT/ffmpeg" -hide_banner -encoders)"
for e in h264_videotoolbox aac_at libmp3lame; do echo "$ENC" | grep -q " $e " || fail "encoder $e missing"; done
"$OUT/ffmpeg" -hide_banner -v error -f lavfi -i testsrc2=s=640x360:d=1 -f lavfi -i sine=d=1 \
   -c:v h264_videotoolbox -b:v 2M -c:a aac_at -b:a 128k -shortest -y "$WORK/test.mp4" || fail "VideoToolbox/AAC test encode"
"$OUT/ffmpeg" -hide_banner -v error -f lavfi -i sine=d=1 -c:a libmp3lame -y "$WORK/test.mp3" || fail "MP3 test encode"
lipo -archs "$OUT/ffmpeg" | tee "$OUT/ARCHS.txt"
if [ "$HOST" = arm64 ] && arch -x86_64 /usr/bin/true 2>/dev/null; then
  arch -x86_64 "$OUT/ffmpeg" -hide_banner -version | head -1 || fail "Intel slice does not run under Rosetta"
fi

cat > "$OUT/BUILDINFO.txt" <<INFO
VerseCut 3.0 video engine for macOS
FFmpeg ${FF_VER}  ${FF_URL}  sha256 ${FF_SHA}
LAME ${LAME_VER}  ${LAME_URL}  sha256 ${LAME_SHA}
NASM ${NASM_VER} (build tool only, not shipped)  ${NASM_URL}  sha256 ${NASM_SHA}
Built $(date -u +%Y-%m-%dT%H:%MZ) on macOS $(sw_vers -productVersion) $(uname -m), $(clang --version | head -1)
Architectures: $(lipo -archs "$OUT/ffmpeg")
Licence: LGPL version 2.1 or later (FFmpeg and LAME). Configure line: see BUILDCONF.txt
To rebuild: bash build-ffmpeg-macos.sh
INFO
cp "$0" "$OUT/source/build-ffmpeg-macos.sh"
( cd "$OUT" && shasum -a 256 ffmpeg ffprobe > SHA256SUMS )
trap - ERR
printf '\n\033[32mDone. Everything is in %s\033[0m\n' "$OUT"
cat "$OUT/SHA256SUMS"
