From 47cacd4d06cfbea5d2b010b251870a9b49a11fc0 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta Date: Thu, 8 Oct 2026 15:08:14 +0530 Subject: [PATCH 1/2] Lower Linux FFmpeg runtime requirements --- .github/workflows/release.yml | 45 +++++++++++++++++++++++++++++------ scripts/build-desktop-cli.sh | 22 ++++++++++++++++- scripts/verify-desktop-cli.sh | 38 +++++++++++++---------------- versions.env | 7 +++++- 4 files changed, 82 insertions(+), 30 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c0df134..06abe09 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,6 +10,10 @@ on: description: Mark the GitHub release as a prerelease required: true default: "true" + publish: + description: Publish the release after building and testing + type: boolean + default: true permissions: contents: write @@ -122,23 +126,37 @@ jobs: fail-fast: false matrix: include: - - { arch: x64, runner: ubuntu-22.04 } - - { arch: arm64, runner: ubuntu-22.04-arm } + - arch: x64 + runner: ubuntu-22.04 + image: quay.io/pypa/manylinux_2_28_x86_64@sha256:39df0042d5cc900b085aa25a0659368b42a0006c54c474299b785b44c1b4ff82 + - arch: arm64 + runner: ubuntu-22.04-arm + image: quay.io/pypa/manylinux_2_28_aarch64@sha256:f5f03dedf61b47d69d0742d36047db78adeee3bdb7a9910ff52304750ecf2514 runs-on: ${{ matrix.runner }} timeout-minutes: 120 steps: - uses: actions/checkout@v4 - - name: Install host tools and libraries + - name: Install verification tools run: | sudo apt-get update - sudo apt-get install -y --no-install-recommends \ - autoconf automake binutils curl g++ git libtool make nasm pkg-config ripgrep \ - meson ninja-build python3 xz-utils zlib1g-dev + sudo apt-get install -y --no-install-recommends binutils python3 ripgrep - name: Build desktop CLI - run: scripts/build-desktop-cli.sh desktop-linux-${{ matrix.arch }} + run: | + mkdir -p "$FFMPEG_WORKSPACE" + docker run --rm \ + -v "$PWD:/src:ro" -v "$FFMPEG_WORKSPACE:/build" \ + -w /src -e FFMPEG_WORKSPACE=/build \ + "${{ matrix.image }}" bash -ec ' + dnf upgrade -y --security + dnf install -y meson ninja-build nasm ripgrep xz-devel + export PATH="/opt/python/cp312-cp312/bin:$PATH" + git config --global --add safe.directory /src + scripts/build-desktop-cli.sh desktop-linux-${{ matrix.arch }} + ' + sudo chown -R "$(id -u):$(id -g)" "$FFMPEG_WORKSPACE" - name: Verify desktop CLI run: | @@ -146,6 +164,18 @@ jobs: tar -xzf "$FFMPEG_WORKSPACE/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz" -C artifact scripts/verify-desktop-cli.sh "$PWD/artifact" + - name: Verify on glibc 2.28 + run: | + docker run --rm -v "$PWD:/src:ro" \ + almalinux:8@sha256:8b469a3a78515e8a18ea8fc727e6a3679e1d0c5ba6f58d5b30be3d0d9b86cffe \ + bash -ec ' + dnf upgrade -y --security + dnf install -y python3.12 libstdc++ zlib xz-libs + python3.12 /src/tests/desktop_cli/verify_media.py \ + --ffmpeg /src/artifact/ffmpeg --ffprobe /src/artifact/ffprobe \ + --work-dir /tmp/verify --target linux-${{ matrix.arch }} + ' + - name: Upload desktop assets uses: actions/upload-artifact@v4 with: @@ -366,6 +396,7 @@ jobs: publish: name: Publish GitHub release + if: inputs.publish runs-on: ubuntu-24.04 needs: - wasm diff --git a/scripts/build-desktop-cli.sh b/scripts/build-desktop-cli.sh index 2203a03..a90f6ce 100755 --- a/scripts/build-desktop-cli.sh +++ b/scripts/build-desktop-cli.sh @@ -78,6 +78,18 @@ build_one_desktop() { local extra_cflags="" local extra_ldflags="" local ffmpeg_cross_flags=() + if [ "$DESKTOP_OS" = "linux" ]; then + local bzip2_src="$target_root/bzip2-$BZIP2_VERSION" + local bzip2_archive="$DOWNLOADS_ROOT/bzip2-$BZIP2_VERSION.tar.gz" + curl -fL "https://sourceware.org/pub/bzip2/bzip2-$BZIP2_VERSION.tar.gz" -o "$bzip2_archive" + verify_sha256 "$bzip2_archive" "$BZIP2_SHA256" + tar -xf "$bzip2_archive" -C "$target_root" + extra_cflags="-fstack-protector-strong -D_FORTIFY_SOURCE=2 -I$bzip2_src" + extra_ldflags="-L$bzip2_src -pie -Wl,-z,relro,-z,now" + make -C "$bzip2_src" -j"$JOBS" libbz2.a \ + CC="$cc" AR="$ar" RANLIB="$ranlib" CFLAGS="-O2 -fPIC $extra_cflags" + ffmpeg_cross_flags+=(--enable-pic --enable-bzlib --enable-lzma --disable-xlib) + fi if [ "$DESKTOP_OS" = "darwin" ]; then export MACOSX_DEPLOYMENT_TARGET="$DESKTOP_MACOS_MIN_VERSION" extra_cflags="-arch $DESKTOP_ARCH" @@ -263,10 +275,18 @@ case "$target" in ;; *) archive="$out/ffmpeg.tar.gz" - tar -C "$out" -czf "$archive" ffmpeg ffprobe dav1d-COPYING + files=(ffmpeg ffprobe dav1d-COPYING) + if [[ "$target" == desktop-linux-* ]]; then + cp "$BUILD_ROOT/$target/bzip2-$BZIP2_VERSION/LICENSE" "$out/bzip2-LICENSE" + files+=(bzip2-LICENSE) + fi + tar -C "$out" -czf "$archive" "${files[@]}" ;; esac sha256_file "$archive" > "$archive.sha256" write_manifest "$archive.manifest.env" "TARGET=$target" "ARTIFACT=$archive" \ "DAV1D_VERSION=$DAV1D_VERSION" "DAV1D_SHA256=$DAV1D_SHA256" +if [[ "$target" == desktop-linux-* ]]; then + printf 'BZIP2_VERSION=%s\nBZIP2_SHA256=%s\n' "$BZIP2_VERSION" "$BZIP2_SHA256" >> "$archive.manifest.env" +fi log "built desktop artifact $archive" diff --git a/scripts/verify-desktop-cli.sh b/scripts/verify-desktop-cli.sh index ed32e08..5c7df4c 100755 --- a/scripts/verify-desktop-cli.sh +++ b/scripts/verify-desktop-cli.sh @@ -60,32 +60,28 @@ if command -v ldd >/dev/null 2>&1 && [ "$(uname -s)" = "Linux" ]; then fi fi -if command -v objdump >/dev/null 2>&1 && [ "$(uname -s)" = "Linux" ]; then +if [ "$(uname -s)" = "Linux" ]; then + require_cmd readelf for binary in "$ffmpeg" "$ffprobe"; do - glibc_report="$WORK_ROOT/$(basename "$binary")-glibc-symbols.txt" - objdump -T "$binary" | rg -o 'GLIBC_[0-9]+\.[0-9]+' | sort -Vu > "$glibc_report" || true - python3 - "$binary" "$glibc_report" "$DESKTOP_LINUX_MAX_GLIBC" <<'PY' + report="$WORK_ROOT/$(basename "$binary")-elf.txt" + LC_ALL=C readelf --wide --version-info "$binary" > "$report" + python3 - "$binary" "$report" "$DESKTOP_LINUX_MAX_GLIBC" \ + "$DESKTOP_LINUX_MAX_GLIBCXX" "$DESKTOP_LINUX_MAX_CXXABI" <<'PYTHON' +import re import sys +from pathlib import Path -binary, report, max_allowed = sys.argv[1:] +binary, report, *limits = sys.argv[1:] +text = Path(report).read_text(encoding="utf-8") -def parse(version): - major, minor = version.split(".", 1) - return int(major), int(minor) +def numeric(value): + return tuple(map(int, value.split("."))) -with open(report, encoding="utf-8") as handle: - versions = sorted( - {line.strip().removeprefix("GLIBC_") for line in handle if line.strip()}, - key=parse, - ) -if not versions: - raise SystemExit(0) -highest = versions[-1] -if parse(highest) > parse(max_allowed): - raise SystemExit( - f"{binary} requires GLIBC_{highest}; max allowed is GLIBC_{max_allowed}" - ) -PY +for namespace, limit in zip(("GLIBC", "GLIBCXX", "CXXABI"), limits): + versions = re.findall(rf"\b{namespace}_([0-9.]+)", text) + if not versions or max(map(numeric, versions)) > numeric(limit): + raise SystemExit(f"{binary}: missing or unsupported {namespace} versions; maximum is {limit}") +PYTHON done fi diff --git a/versions.env b/versions.env index d9273c5..4d68a86 100644 --- a/versions.env +++ b/versions.env @@ -15,11 +15,16 @@ DAV1D_VERSION=1.5.4 DAV1D_URL=https://downloads.videolan.org/pub/videolan/dav1d/1.5.4/dav1d-1.5.4.tar.xz DAV1D_SHA256=686616b7c69eb88d44459391ab25cac13b6647a3b288835c5784e71c1514a5c5 +BZIP2_VERSION=1.0.8 +BZIP2_SHA256=ab5a03176ee106d3f0fa90e381da478ddae405918153cca248e682cd0c4a2269 + ANDROID_API=26 ANDROID_NDK_VERSION=28.2.13676358 IOS_MIN_VERSION=14.0 XCODE_VERSION=26.2 -DESKTOP_LINUX_MAX_GLIBC=2.35 +DESKTOP_LINUX_MAX_GLIBC=2.28 +DESKTOP_LINUX_MAX_GLIBCXX=3.4.25 +DESKTOP_LINUX_MAX_CXXABI=1.3.11 DESKTOP_MACOS_MIN_VERSION=13.3 MOBILE_TARGETS="android-arm64 android-armv7 ios-device-arm64 ios-sim-arm64" From 058ab0b4e58848332ba3f78e5231b6b462227402 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta Date: Thu, 8 Oct 2026 16:39:43 +0530 Subject: [PATCH 2/2] Simplify Linux builds and fix verification --- .github/workflows/desktop-linux.yml | 88 +++++++++++++++++++++++++++++ .github/workflows/release.yml | 71 +---------------------- scripts/build-desktop-cli.sh | 4 +- scripts/verify-desktop-cli.sh | 16 +++--- versions.env | 2 - 5 files changed, 99 insertions(+), 82 deletions(-) create mode 100644 .github/workflows/desktop-linux.yml diff --git a/.github/workflows/desktop-linux.yml b/.github/workflows/desktop-linux.yml new file mode 100644 index 0000000..4f026d4 --- /dev/null +++ b/.github/workflows/desktop-linux.yml @@ -0,0 +1,88 @@ +name: Desktop Linux + +on: + workflow_dispatch: + workflow_call: + pull_request: + paths: + - ".github/workflows/desktop-linux.yml" + - ".github/workflows/release.yml" + - "versions.env" + - "scripts/build-desktop-cli.sh" + - "scripts/common.sh" + - "scripts/fetch-sources.sh" + - "scripts/verify-desktop-cli.sh" + - "patches/x264/**" + - "tests/desktop_cli/**" + +permissions: + contents: read + +env: + FFMPEG_WORKSPACE: ${{ github.workspace }}-build + +jobs: + build: + name: Build and verify ${{ matrix.arch }} + strategy: + fail-fast: false + matrix: + include: + - arch: x64 + runner: ubuntu-22.04 + image: quay.io/pypa/manylinux_2_28_x86_64@sha256:39df0042d5cc900b085aa25a0659368b42a0006c54c474299b785b44c1b4ff82 + - arch: arm64 + runner: ubuntu-22.04-arm + image: quay.io/pypa/manylinux_2_28_aarch64@sha256:f5f03dedf61b47d69d0742d36047db78adeee3bdb7a9910ff52304750ecf2514 + runs-on: ${{ matrix.runner }} + timeout-minutes: 120 + + steps: + - uses: actions/checkout@v4 + + - name: Install verification tools + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends binutils python3 ripgrep + + - name: Build desktop CLI + run: | + mkdir -p "$FFMPEG_WORKSPACE" + docker run --rm \ + -v "$PWD:/src:ro" -v "$FFMPEG_WORKSPACE:/build" \ + -w /src -e FFMPEG_WORKSPACE=/build \ + "${{ matrix.image }}" bash -ec ' + dnf upgrade -y --security + dnf install -y meson ninja-build nasm ripgrep xz-devel + export PATH="/opt/python/cp312-cp312/bin:$PATH" + git config --global --add safe.directory /src + scripts/build-desktop-cli.sh desktop-linux-${{ matrix.arch }} + ' + + - name: Verify on Ubuntu + run: | + mkdir -p artifact + tar -xzf "$FFMPEG_WORKSPACE/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz" -C artifact + WORK_ROOT="$RUNNER_TEMP/ffmpeg-verify" scripts/verify-desktop-cli.sh "$PWD/artifact" + + - name: Verify on glibc 2.28 + run: | + docker run --rm -v "$PWD:/src:ro" \ + almalinux:8@sha256:8b469a3a78515e8a18ea8fc727e6a3679e1d0c5ba6f58d5b30be3d0d9b86cffe \ + bash -ec ' + dnf upgrade -y --security + dnf install -y python3.12 libstdc++ zlib xz-libs + python3.12 /src/tests/desktop_cli/verify_media.py \ + --ffmpeg /src/artifact/ffmpeg --ffprobe /src/artifact/ffprobe \ + --work-dir /tmp/verify --target linux-${{ matrix.arch }} + ' + + - name: Upload desktop assets + uses: actions/upload-artifact@v4 + with: + name: release-desktop-linux-${{ matrix.arch }} + path: | + ${{ env.FFMPEG_WORKSPACE }}/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz + ${{ env.FFMPEG_WORKSPACE }}/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz.sha256 + ${{ env.FFMPEG_WORKSPACE }}/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz.manifest.env + if-no-files-found: error diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 06abe09..d294f0f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,10 +10,6 @@ on: description: Mark the GitHub release as a prerelease required: true default: "true" - publish: - description: Publish the release after building and testing - type: boolean - default: true permissions: contents: write @@ -121,70 +117,8 @@ jobs: if-no-files-found: error desktop-linux: - name: Desktop Linux ${{ matrix.arch }} - strategy: - fail-fast: false - matrix: - include: - - arch: x64 - runner: ubuntu-22.04 - image: quay.io/pypa/manylinux_2_28_x86_64@sha256:39df0042d5cc900b085aa25a0659368b42a0006c54c474299b785b44c1b4ff82 - - arch: arm64 - runner: ubuntu-22.04-arm - image: quay.io/pypa/manylinux_2_28_aarch64@sha256:f5f03dedf61b47d69d0742d36047db78adeee3bdb7a9910ff52304750ecf2514 - runs-on: ${{ matrix.runner }} - timeout-minutes: 120 - - steps: - - uses: actions/checkout@v4 - - - name: Install verification tools - run: | - sudo apt-get update - sudo apt-get install -y --no-install-recommends binutils python3 ripgrep - - - name: Build desktop CLI - run: | - mkdir -p "$FFMPEG_WORKSPACE" - docker run --rm \ - -v "$PWD:/src:ro" -v "$FFMPEG_WORKSPACE:/build" \ - -w /src -e FFMPEG_WORKSPACE=/build \ - "${{ matrix.image }}" bash -ec ' - dnf upgrade -y --security - dnf install -y meson ninja-build nasm ripgrep xz-devel - export PATH="/opt/python/cp312-cp312/bin:$PATH" - git config --global --add safe.directory /src - scripts/build-desktop-cli.sh desktop-linux-${{ matrix.arch }} - ' - sudo chown -R "$(id -u):$(id -g)" "$FFMPEG_WORKSPACE" - - - name: Verify desktop CLI - run: | - mkdir -p artifact - tar -xzf "$FFMPEG_WORKSPACE/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz" -C artifact - scripts/verify-desktop-cli.sh "$PWD/artifact" - - - name: Verify on glibc 2.28 - run: | - docker run --rm -v "$PWD:/src:ro" \ - almalinux:8@sha256:8b469a3a78515e8a18ea8fc727e6a3679e1d0c5ba6f58d5b30be3d0d9b86cffe \ - bash -ec ' - dnf upgrade -y --security - dnf install -y python3.12 libstdc++ zlib xz-libs - python3.12 /src/tests/desktop_cli/verify_media.py \ - --ffmpeg /src/artifact/ffmpeg --ffprobe /src/artifact/ffprobe \ - --work-dir /tmp/verify --target linux-${{ matrix.arch }} - ' - - - name: Upload desktop assets - uses: actions/upload-artifact@v4 - with: - name: release-desktop-linux-${{ matrix.arch }} - path: | - ${{ env.FFMPEG_WORKSPACE }}/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz - ${{ env.FFMPEG_WORKSPACE }}/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz.sha256 - ${{ env.FFMPEG_WORKSPACE }}/dist/desktop/linux-${{ matrix.arch }}/ffmpeg.tar.gz.manifest.env - if-no-files-found: error + name: Desktop Linux + uses: ./.github/workflows/desktop-linux.yml desktop-macos-universal: name: Desktop macOS universal @@ -396,7 +330,6 @@ jobs: publish: name: Publish GitHub release - if: inputs.publish runs-on: ubuntu-24.04 needs: - wasm diff --git a/scripts/build-desktop-cli.sh b/scripts/build-desktop-cli.sh index a90f6ce..45281b5 100755 --- a/scripts/build-desktop-cli.sh +++ b/scripts/build-desktop-cli.sh @@ -85,10 +85,10 @@ build_one_desktop() { verify_sha256 "$bzip2_archive" "$BZIP2_SHA256" tar -xf "$bzip2_archive" -C "$target_root" extra_cflags="-fstack-protector-strong -D_FORTIFY_SOURCE=2 -I$bzip2_src" - extra_ldflags="-L$bzip2_src -pie -Wl,-z,relro,-z,now" + extra_ldflags="-L$bzip2_src -Wl,-z,relro,-z,now" make -C "$bzip2_src" -j"$JOBS" libbz2.a \ CC="$cc" AR="$ar" RANLIB="$ranlib" CFLAGS="-O2 -fPIC $extra_cflags" - ffmpeg_cross_flags+=(--enable-pic --enable-bzlib --enable-lzma --disable-xlib) + ffmpeg_cross_flags+=(--enable-pic --extra-ldexeflags=-pie --enable-bzlib --enable-lzma --disable-xlib) fi if [ "$DESKTOP_OS" = "darwin" ]; then export MACOSX_DEPLOYMENT_TARGET="$DESKTOP_MACOS_MIN_VERSION" diff --git a/scripts/verify-desktop-cli.sh b/scripts/verify-desktop-cli.sh index 5c7df4c..1979e30 100755 --- a/scripts/verify-desktop-cli.sh +++ b/scripts/verify-desktop-cli.sh @@ -55,8 +55,8 @@ fi if command -v ldd >/dev/null 2>&1 && [ "$(uname -s)" = "Linux" ]; then ldd "$ffmpeg" > "$WORK_ROOT/desktop-ffmpeg-ldd.txt" - if rg -q 'lib(x264|zimg|dav1d)' "$WORK_ROOT/desktop-ffmpeg-ldd.txt"; then - die "desktop ffmpeg must link pinned x264/zimg/dav1d statically" + if rg -q 'lib(x264|zimg|dav1d|bz2)' "$WORK_ROOT/desktop-ffmpeg-ldd.txt"; then + die "desktop ffmpeg must link pinned x264/zimg/dav1d/bzip2 statically" fi fi @@ -65,22 +65,20 @@ if [ "$(uname -s)" = "Linux" ]; then for binary in "$ffmpeg" "$ffprobe"; do report="$WORK_ROOT/$(basename "$binary")-elf.txt" LC_ALL=C readelf --wide --version-info "$binary" > "$report" - python3 - "$binary" "$report" "$DESKTOP_LINUX_MAX_GLIBC" \ - "$DESKTOP_LINUX_MAX_GLIBCXX" "$DESKTOP_LINUX_MAX_CXXABI" <<'PYTHON' + python3 - "$binary" "$report" "$DESKTOP_LINUX_MAX_GLIBC" <<'PYTHON' import re import sys from pathlib import Path -binary, report, *limits = sys.argv[1:] +binary, report, maximum = sys.argv[1:] text = Path(report).read_text(encoding="utf-8") def numeric(value): return tuple(map(int, value.split("."))) -for namespace, limit in zip(("GLIBC", "GLIBCXX", "CXXABI"), limits): - versions = re.findall(rf"\b{namespace}_([0-9.]+)", text) - if not versions or max(map(numeric, versions)) > numeric(limit): - raise SystemExit(f"{binary}: missing or unsupported {namespace} versions; maximum is {limit}") +versions = re.findall(r"\bGLIBC_([0-9.]+)", text) +if not versions or max(map(numeric, versions)) > numeric(maximum): + raise SystemExit(f"{binary}: missing or unsupported GLIBC versions; maximum is {maximum}") PYTHON done fi diff --git a/versions.env b/versions.env index 4d68a86..8c86d07 100644 --- a/versions.env +++ b/versions.env @@ -23,8 +23,6 @@ ANDROID_NDK_VERSION=28.2.13676358 IOS_MIN_VERSION=14.0 XCODE_VERSION=26.2 DESKTOP_LINUX_MAX_GLIBC=2.28 -DESKTOP_LINUX_MAX_GLIBCXX=3.4.25 -DESKTOP_LINUX_MAX_CXXABI=1.3.11 DESKTOP_MACOS_MIN_VERSION=13.3 MOBILE_TARGETS="android-arm64 android-armv7 ios-device-arm64 ios-sim-arm64"