diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 2c728f8..eb5a948 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -69,13 +69,30 @@ jobs: - name: Install dependencies (macOS) if: runner.os == 'macOS' - run: brew install nasm pkg-config x264 x265 + run: brew install nasm pkg-config x265 - name: Install dependencies (Linux) if: runner.os == 'Linux' run: | sudo apt-get update - sudo apt-get install -y --no-install-recommends nasm pkg-config clang libx264-dev libx265-dev + sudo apt-get install -y --no-install-recommends nasm pkg-config clang libx265-dev + + # x264 puts its ABI version in its symbol names (x264_encoder_open_165), so an FFmpeg + # compiled against the runner's x264 doesn't link with the x264 of another system. The + # archive carries its own static x264 instead, which the linker finds before the system's. + - name: Compile x264 + if: contains(matrix.features, 'x264') + env: + X264_REV: b35605ace3ddf7c1a5d67a2eb553f034aef41d55 # stable, 0.165.3222 + run: | + git init -q x264 + git -C x264 fetch -q --depth 1 https://code.videolan.org/videolan/x264.git "$X264_REV" + git -C x264 checkout -q FETCH_HEAD + cd x264 + ./configure --prefix="$GITHUB_WORKSPACE/external" --enable-static --enable-pic --disable-cli --disable-opencl + make -j"$(getconf _NPROCESSORS_ONLN)" install-lib-static + echo "PKG_CONFIG_PATH=$GITHUB_WORKSPACE/external/lib/pkgconfig" >> "$GITHUB_ENV" + echo "FFMPEG_BINARIES_BUNDLE_LIBS=$GITHUB_WORKSPACE/external/lib/libx264.a" >> "$GITHUB_ENV" - name: Compile FFmpeg run: cargo build --release --target ${{ matrix.target.triple }} --features "build,static,build-portable,${{ matrix.features }}" diff --git a/README.md b/README.md index 876aed7..b6008bc 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,16 @@ build script first downloads a static FFmpeg matching the target and the enabled the [`binaries-` release](https://github.com/dmtrKovalenko/rust-ffmpeg-sys/releases) and only compiles FFmpeg from source when there is none. Archives are built by `.github/workflows/binaries.yml` for macOS (arm64, x86_64) and Linux (x86_64, arm64) with the -codec sets fframes uses. External codec libraries (x264, x265) still come from the system. +codec sets fframes uses. They carry a static x264, because x264 puts its ABI version in its +symbol names and an FFmpeg compiled against one x264 doesn't link with another. x265 still +comes from the system. - `FFMPEG_FORCE_BUILD=1` always compiles from source (so do `FFMPEG_MARCH`/`FFMPEG_MTUNE`, the archives are compiled with `build-portable`). - `FFMPEG_BINARIES_URL` overrides the download URL template (`{tag}`, `{key}`; `file://` works). - `FFMPEG_BINARIES_CACHE` is where downloads are kept (default: the user cache directory). - `FFMPEG_BINARIES_EXPORT_DIR` makes a source build write its archive there. +- `FFMPEG_BINARIES_BUNDLE_LIBS` lists static libraries an exported archive carries in `lib/`. ## Feature flags diff --git a/build_prebuilt.rs b/build_prebuilt.rs index cf01147..1dcb2a2 100644 --- a/build_prebuilt.rs +++ b/build_prebuilt.rs @@ -17,6 +17,10 @@ //! directory), so that every project on the machine downloads them once. //! - `FFMPEG_BINARIES_EXPORT_DIR`: after compiling from source, write the archive and its key to //! this directory (used by CI to produce the binaries). +//! - `FFMPEG_BINARIES_BUNDLE_LIBS`: static libraries (a path list, like `PATH`) that are copied +//! into the archive's `lib/` on export. CI bundles x264 this way: its ABI version is part of +//! its symbol names (`x264_encoder_open_165`), so an FFmpeg compiled against one x264 doesn't +//! link with another, and the system's x264 differs from machine to machine. //! //! Archives are compiled with the `build-portable` flags, so setting `FFMPEG_MARCH` or //! `FFMPEG_MTUNE` compiles from source as well. @@ -39,6 +43,7 @@ pub fn rerun_if_env_changed() { "FFMPEG_FORCE_BUILD", "FFMPEG_BINARIES_CACHE", "FFMPEG_BINARIES_EXPORT_DIR", + "FFMPEG_BINARIES_BUNDLE_LIBS", "FFMPEG_MARCH", "FFMPEG_MTUNE", ] { @@ -203,6 +208,15 @@ pub fn export(dist: &Path) -> io::Result<()> { return Ok(()); }; fs::create_dir_all(&export_dir)?; + // Next to FFmpeg's own libraries, the linker finds them before the system's. + if let Some(libs) = env::var_os("FFMPEG_BINARIES_BUNDLE_LIBS") { + for lib in env::split_paths(&libs) { + let name = lib + .file_name() + .ok_or_else(|| io::Error::other(format!("{} is not a library", lib.display())))?; + fs::copy(&lib, dist.join("lib").join(name))?; + } + } let key = key(); run(Command::new("tar") // pkg-config files hold absolute paths of the build machine