mirror of
https://github.com/kirbylink/mpv-natives
synced 2026-10-12 05:40:26 +02:00
build: add macOS universal libmpv.dylib CI build
Vendor ci/build-libmpv-macos.sh, which builds every dependency statically for the native runner architecture and links them into a single self-contained libmpv dylib (coreaudio output, videotoolbox hardware decode, OpenGL render API; no vulkan/cocoa player). build-macos.yml runs it on macos-14 (x86_64) and macos-15 (arm64) in parallel and a merge job combines the two dylibs into one universal libmpv.dylib with lipo, matching the file name jlibmpv expects on macOS.
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
name: build-macos-arch
|
||||
|
||||
# Builds the self-contained libmpv dylib for one macOS architecture
|
||||
# (x86_64 on macos-14, arm64 on macos-15) using the vendored build script.
|
||||
# The merge job in build-macos.yml combines the per-architecture dylibs into
|
||||
# a universal binary.
|
||||
|
||||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
arch:
|
||||
description: Target architecture (x86_64 or arm64)
|
||||
required: true
|
||||
type: string
|
||||
runner:
|
||||
description: GitHub runner image providing that architecture
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
MPV_REF: v0.41.0
|
||||
ARCH: ${{ inputs.arch }}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ inputs.runner }}
|
||||
env:
|
||||
CCACHE_BASEDIR: ${{ github.workspace }}
|
||||
CCACHE_DIR: ${{ github.workspace }}/.ccache
|
||||
CCACHE_MAXSIZE: 500M
|
||||
steps:
|
||||
- name: Check out mpv-natives (build scripts)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Check out mpv (pinned tag)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: mpv-player/mpv
|
||||
ref: ${{ env.MPV_REF }}
|
||||
path: mpv
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Get time
|
||||
id: get_time
|
||||
run: echo "timestamp=$(date +%s)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Restore ccache
|
||||
uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: macos-${{ env.ARCH }}-${{ steps.get_time.outputs.timestamp }}
|
||||
restore-keys: macos-${{ env.ARCH }}-
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
brew install ccache nasm ninja
|
||||
python3 -m pip install --user meson
|
||||
export PATH="$HOME/Library/Python/3.12/bin:$PATH"
|
||||
# export so meson finds pip-installed tools
|
||||
echo "$HOME/Library/Python/3.12/bin" >> "$GITHUB_PATH"
|
||||
|
||||
# Run the vendored build script from inside the mpv source tree, so the
|
||||
# native build writes macos_prefix/ and the meson build next to mpv's
|
||||
# meson.build and produces the self-contained dylib in mpv/artifact/.
|
||||
- name: Build self-contained libmpv
|
||||
working-directory: mpv
|
||||
run: bash "${{ github.workspace }}/ci/build-libmpv-macos.sh"
|
||||
|
||||
- name: Print meson log on failure
|
||||
if: failure()
|
||||
run: cat mpv/macos_build/meson-logs/meson-log.txt
|
||||
|
||||
- name: Collect artifact
|
||||
run: |
|
||||
mkdir -p "artifact-macos-${{ env.ARCH }}"
|
||||
cp -pv mpv/artifact/libmpv-2.dylib "artifact-macos-${{ env.ARCH }}/"
|
||||
cp -pv mpv/artifact/*.h "artifact-macos-${{ env.ARCH }}/" 2>/dev/null || true
|
||||
echo "=== contents ==="
|
||||
ls -l "artifact-macos-${{ env.ARCH }}"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mpv-natives-macos-${{ env.ARCH }}-mpv-${{ env.MPV_REF }}
|
||||
path: artifact-macos-${{ env.ARCH }}
|
||||
|
||||
- name: Save ccache
|
||||
uses: actions/cache/save@v4
|
||||
if: always()
|
||||
with:
|
||||
path: ${{ env.CCACHE_DIR }}
|
||||
key: macos-${{ env.ARCH }}-${{ steps.get_time.outputs.timestamp }}
|
||||
@@ -0,0 +1,78 @@
|
||||
name: build-macos
|
||||
|
||||
# Builds a universal (arm64 + x86_64) self-contained libmpv.dylib for macOS
|
||||
# from mpv source pinned to a release tag. Two native builds run in parallel
|
||||
# (x86_64 on macos-14, arm64 on macos-15); a merge job combines the two dylibs
|
||||
# with lipo into one universal library and uploads it.
|
||||
#
|
||||
# Every dependency is built static and linked into the one dylib (same
|
||||
# approach as the Windows build). jlibmpv expects the file name
|
||||
# `libmpv.dylib` (no -2) and one universal binary for both architectures.
|
||||
#
|
||||
# Pinned to mpv v0.41.0 — the version whose client API (131077) the jlibmpv
|
||||
# JNA bindings are validated against. Bump the ref here for a libmpv update.
|
||||
# The build recipe lives in ci/build-libmpv-macos.sh in this repository.
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
workflow_dispatch:
|
||||
|
||||
env:
|
||||
MPV_REF: v0.41.0
|
||||
|
||||
jobs:
|
||||
build-x86_64:
|
||||
uses: ./.github/workflows/build-macos-arch.yml
|
||||
with:
|
||||
arch: x86_64
|
||||
runner: macos-14
|
||||
|
||||
build-arm64:
|
||||
uses: ./.github/workflows/build-macos-arch.yml
|
||||
with:
|
||||
arch: arm64
|
||||
runner: macos-15
|
||||
|
||||
merge:
|
||||
needs: [build-x86_64, build-arm64]
|
||||
runs-on: macos-15
|
||||
steps:
|
||||
- name: Check out mpv-natives (build scripts)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Check out mpv (pinned tag)
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: mpv-player/mpv
|
||||
ref: ${{ env.MPV_REF }}
|
||||
path: mpv
|
||||
fetch-depth: 1
|
||||
|
||||
- name: Download architecture artifacts
|
||||
uses: actions/download-artifact@v4
|
||||
with:
|
||||
pattern: mpv-natives-macos-${{ env.MPV_REF }}-*
|
||||
path: arch
|
||||
merge-multiple: true
|
||||
|
||||
- name: Create universal dylib
|
||||
run: |
|
||||
mkdir -p artifact-libmpv
|
||||
lipo -create \
|
||||
"arch/x86_64/libmpv-2.dylib" \
|
||||
"arch/arm64/libmpv-2.dylib" \
|
||||
-output artifact-libmpv/libmpv.dylib
|
||||
lipo -info artifact-libmpv/libmpv.dylib
|
||||
cp -pv mpv/mpvrender.h mpv/mpv/client.h mpv/ebml_types.h artifact-libmpv/ 2>/dev/null || true
|
||||
sha256sum artifact-libmpv/libmpv.dylib > artifact-libmpv/checksums.txt
|
||||
cat artifact-libmpv/checksums.txt
|
||||
ls -l artifact-libmpv
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: mpv-natives-macos-universal-mpv-${{ env.MPV_REF }}
|
||||
path: artifact-libmpv
|
||||
@@ -14,9 +14,9 @@ here, as versioned GitHub release assets.
|
||||
jlibmpv JNA bindings are validated against. A libmpv update means bumping
|
||||
`MPV_REF` in the workflows.
|
||||
- **Build source:** the mpv source tree is checked out at that tag by
|
||||
GitHub Actions and built with mpv's own CI scripts
|
||||
(`ci/build-mingw64.sh`, ...). No mpv source or third-party binaries are
|
||||
stored in this repository.
|
||||
GitHub Actions and built with the vendored scripts in `ci/`
|
||||
(`build-libmpv-mingw64.sh`, `build-libmpv-macos.sh`). No mpv source or
|
||||
third-party binaries are stored in this repository.
|
||||
|
||||
## Releases
|
||||
|
||||
@@ -26,7 +26,7 @@ single file) and a `checksums.txt` (SHA-256) for verification.
|
||||
| Asset | Contents |
|
||||
|---|---|
|
||||
| `mpv-natives-windows-x86_64-mpv-<ver>.zip` | `libmpv-2.dll` (all dependencies statically linked, mingw64 build) |
|
||||
| `mpv-natives-macos-<arch>-mpv-<ver>.zip` | `libmpv.dylib` (in preparation) |
|
||||
| `mpv-natives-macos-universal-mpv-<ver>.zip` | `libmpv.dylib` (universal arm64 + x86_64, all dependencies statically linked) |
|
||||
|
||||
The Windows DLL is fully self-contained: every dependency (ffmpeg, libass,
|
||||
libplacebo, harfbuzz, freetype, fribidi, …) is statically linked into
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
#!/bin/bash -e
|
||||
set -e
|
||||
# (The shebang -e is not honored when the workflow invokes us via `bash file`,
|
||||
# so set -e is repeated here to guarantee fail-fast regardless of caller.)
|
||||
#
|
||||
# Build a single self-contained libmpv dylib for macOS on the runner's own
|
||||
# architecture (x86_64 on macos-14, arm64 on macos-15). The workflow runs
|
||||
# this script on both runner kinds and merges the two dylibs into a
|
||||
# universal binary with lipo.
|
||||
#
|
||||
# Recipe: every dependency is built STATIC and linked into the one dylib
|
||||
# (same approach as ci/build-libmpv-mingw64.sh for Windows). macOS needs no
|
||||
# cross-compilation: the host Apple Clang builds natively. No iconv is built
|
||||
# (macOS ships one), no system frameworks are vendored (CoreAudio,
|
||||
# VideoToolbox, ... are always present on the host).
|
||||
#
|
||||
# Vulkan is intentionally NOT built: jlibmpv renders through an externally
|
||||
# supplied OpenGL context (JOGL + render API) and never uses the Vulkan video
|
||||
# backend. CoreAudio is the audio output (ao=coreaudio) and VideoToolbox
|
||||
# provides hardware decode.
|
||||
#
|
||||
# All third-party sources are pinned to the latest stable tag that existed on
|
||||
# the mpv v0.41.0 release date (2025-12-21), so a rebuild reproduces the same
|
||||
# binary instead of drifting to git HEAD.
|
||||
|
||||
# The mpv source tree is the current working directory (checked out by CI at
|
||||
# MPV_REF). Everything is built next to it under macos_prefix/.
|
||||
prefix_dir=$PWD/macos_prefix
|
||||
mkdir -p "$prefix_dir"
|
||||
|
||||
wget="wget -nc --progress=bar:force"
|
||||
|
||||
export CC=cc
|
||||
export CXX=c++
|
||||
export AR=ar
|
||||
export NM=nm
|
||||
export RANLIB=ranlib
|
||||
|
||||
export CFLAGS="-O2 -pipe -Wall"
|
||||
export LDFLAGS=""
|
||||
|
||||
# anything that uses pkg-config
|
||||
export PKG_CONFIG_SYSROOT_DIR="$prefix_dir"
|
||||
export PKG_CONFIG_LIBDIR="$PKG_CONFIG_SYSROOT_DIR/lib/pkgconfig"
|
||||
|
||||
# --- Pinned dependency versions -------------------------------------------
|
||||
# Latest stable tag on or before the mpv v0.41.0 release (2025-12-21).
|
||||
FFMPEG_VER=n8.0.1
|
||||
DAV1D_VER=1.5.2
|
||||
LIBPLACEBO_VER=v7.351.0
|
||||
LIBASS_VER=0.17.4
|
||||
LCMS2_VER=lcms2.17
|
||||
ZLIB_VER=1.3.1
|
||||
FREETYPE_VER=2.14.1
|
||||
FRIBIDI_VER=1.0.16
|
||||
HARFBUZZ_VER=12.2.0
|
||||
|
||||
# Static everywhere: every dependency becomes a .a archive that gets linked
|
||||
# into the single libmpv dylib. (mpv's own script ships shared libs instead.)
|
||||
commonflags="--disable-shared --enable-static"
|
||||
|
||||
function builddir {
|
||||
[ -d "$1/builddir" ] && rm -rf "$1/builddir"
|
||||
mkdir -p "$1/builddir"
|
||||
pushd "$1/builddir"
|
||||
}
|
||||
|
||||
function makeplusinstall {
|
||||
if [ -f build.ninja ]; then
|
||||
ninja
|
||||
DESTDIR="$prefix_dir" ninja install
|
||||
else
|
||||
make -j$(sysctl -n hw.ncpu)
|
||||
make DESTDIR="$prefix_dir" install
|
||||
fi
|
||||
}
|
||||
|
||||
function gettar {
|
||||
local name="${1##*/}"
|
||||
[ -d "${name%%.*}" ] && return 0
|
||||
$wget "$1"
|
||||
tar -xaf "$name"
|
||||
}
|
||||
|
||||
# Clone a git dependency at a pinned tag (shallow, tag-checked-out).
|
||||
# Submodules are fetched shallow too: libplacebo needs its bundled `glad`.
|
||||
function gitpin {
|
||||
local url=$1 dest=$2 tag=$3
|
||||
[ -d "$dest" ] || git clone --depth=1 --branch "$tag" \
|
||||
--recurse-submodules --shallow-submodules "$url" "$dest"
|
||||
pushd "$dest"
|
||||
git checkout --quiet "$tag"
|
||||
popd
|
||||
}
|
||||
|
||||
function build_if_missing {
|
||||
local name=${1//-/_}
|
||||
local mark_var=_${name}_mark
|
||||
local mark_file=$prefix_dir/${!mark_var}
|
||||
[ -e "$mark_file" ] && return 0
|
||||
echo "::group::Building $1"
|
||||
_$name
|
||||
echo "::endgroup::"
|
||||
if [ ! -e "$mark_file" ]; then
|
||||
echo "Error: Build of $1 completed but $mark_file was not created."
|
||||
return 2
|
||||
fi
|
||||
}
|
||||
|
||||
## mpv's dependencies (all static, native build)
|
||||
|
||||
_zlib () {
|
||||
gettar "https://zlib.net/fossils/zlib-${ZLIB_VER}.tar.gz"
|
||||
builddir zlib-${ZLIB_VER}
|
||||
CC=cc AR=ar RANLIB=ranlib make -fMakefile.lib \
|
||||
LIBSUFFIX= PREFIX=/ LIBPATH=$prefix_dir/lib install
|
||||
popd
|
||||
}
|
||||
_zlib_mark=lib/libz.a
|
||||
|
||||
_dav1d () {
|
||||
gitpin https://code.videolan.org/videolan/dav1d.git dav1d "$DAV1D_VER"
|
||||
builddir dav1d
|
||||
meson setup .. -Denable_{tools,tests}=false -Ddefault_library=static
|
||||
makeplusinstall
|
||||
popd
|
||||
}
|
||||
_dav1d_mark=lib/libdav1d.a
|
||||
|
||||
_lcms2 () {
|
||||
gitpin https://github.com/mm2/Little-CMS.git lcms2 "$LCMS2_VER"
|
||||
builddir lcms2
|
||||
meson setup .. -Dtests=disabled -Dutils=false -Ddefault_library=static
|
||||
makeplusinstall
|
||||
popd
|
||||
}
|
||||
_lcms2_mark=lib/liblcms2.a
|
||||
|
||||
_ffmpeg () {
|
||||
gitpin https://github.com/FFmpeg/FFmpeg.git ffmpeg "$FFMPEG_VER"
|
||||
builddir ffmpeg
|
||||
local args=(
|
||||
--pkg-config=pkg-config --target-os=macosx --enable-gpl
|
||||
--enable-cross-compile \
|
||||
--extra-cflags="-I$prefix_dir/include" \
|
||||
--extra-ldflags="-L$prefix_dir/lib" \
|
||||
$commonflags
|
||||
--disable-{doc,programs}
|
||||
--enable-muxer=spdif --enable-encoder=mjpeg,png --enable-libdav1d
|
||||
)
|
||||
../configure "${args[@]}"
|
||||
makeplusinstall
|
||||
popd
|
||||
}
|
||||
_ffmpeg_mark=lib/libavcodec.a
|
||||
|
||||
_freetype () {
|
||||
gettar "https://download.savannah.gnu.org/releases/freetype/freetype-${FREETYPE_VER}.tar.xz"
|
||||
builddir freetype-${FREETYPE_VER}
|
||||
meson setup .. -Ddefault_library=static
|
||||
makeplusinstall
|
||||
popd
|
||||
}
|
||||
_freetype_mark=lib/libfreetype.a
|
||||
|
||||
_fribidi () {
|
||||
gettar "https://github.com/fribidi/fribidi/releases/download/v${FRIBIDI_VER}/fribidi-${FRIBIDI_VER}.tar.xz"
|
||||
builddir fribidi-${FRIBIDI_VER}
|
||||
meson setup .. -D{tests,docs}=false -Ddefault_library=static
|
||||
makeplusinstall
|
||||
popd
|
||||
}
|
||||
_fribidi_mark=lib/libfribidi.a
|
||||
|
||||
_harfbuzz () {
|
||||
gettar "https://github.com/harfbuzz/harfbuzz/releases/download/${HARFBUZZ_VER}/harfbuzz-${HARFBUZZ_VER}.tar.xz"
|
||||
builddir harfbuzz-${HARFBUZZ_VER}
|
||||
meson setup .. -Dtests=disabled -Ddefault_library=static
|
||||
makeplusinstall
|
||||
popd
|
||||
}
|
||||
_harfbuzz_mark=lib/libharfbuzz.a
|
||||
|
||||
_libass () {
|
||||
gitpin https://github.com/libass/libass.git libass "$LIBASS_VER"
|
||||
builddir libass
|
||||
meson setup .. -Ddefault_library=static
|
||||
makeplusinstall
|
||||
popd
|
||||
}
|
||||
_libass_mark=lib/libass.a
|
||||
|
||||
for x in zlib dav1d lcms2; do
|
||||
build_if_missing $x
|
||||
done
|
||||
for x in ffmpeg freetype fribidi harfbuzz libass; do
|
||||
build_if_missing $x
|
||||
done
|
||||
_libplacebo () {
|
||||
gitpin https://code.videolan.org/videolan/libplacebo.git libplacebo "$LIBPLACEBO_VER"
|
||||
builddir libplacebo
|
||||
# opengl enabled (the only renderer jlibmpv uses); vulkan/d3d11 off
|
||||
meson setup .. -Ddemos=false -Dopengl=enabled -Dd3d11=disabled \
|
||||
-Dvulkan=disabled -Dlcms=disabled -Ddefault_library=static
|
||||
makeplusinstall
|
||||
popd
|
||||
}
|
||||
_libplacebo_mark=lib/libplacebo.a
|
||||
build_if_missing libplacebo
|
||||
|
||||
## mpv
|
||||
|
||||
build=macos_build
|
||||
rm -rf $build
|
||||
|
||||
# libmpv itself is the ONE shared object (default_library=shared), linked
|
||||
# against the static dependency archives built above. No CLI player, no
|
||||
# tests, no lua/javascript, no vulkan. OpenGL stays (render API via JOGL);
|
||||
# coreaudio is the audio output; videotoolbox gives hardware decode.
|
||||
meson setup $build \
|
||||
--buildtype release -Dstrip=true \
|
||||
-Ddefault_library=shared \
|
||||
-Dlibmpv=true \
|
||||
-Dcplayer=false \
|
||||
-Dtests=false \
|
||||
-Dlua=disabled \
|
||||
-Djavascript=disabled \
|
||||
-Dshaderc=disabled \
|
||||
-Dspirv-cross=disabled \
|
||||
-Dvulkan=disabled \
|
||||
-Dcocoa=disabled \
|
||||
-Dgl-cocoa=disabled \
|
||||
-Dcoreaudio=enabled \
|
||||
-Dvideotoolbox-pl=enabled
|
||||
meson compile -C $build
|
||||
|
||||
## Collect the self-contained dylib
|
||||
|
||||
mkdir -p artifact
|
||||
LIBMPV_DYLIB=$(find $build -maxdepth 1 -name 'libmpv-2.dylib' | head -n 1)
|
||||
[ -n "$LIBMPV_DYLIB" ] || { echo "ERROR: libmpv dylib not found in $build"; ls -l $build; exit 1; }
|
||||
cp -pv "$LIBMPV_DYLIB" artifact/
|
||||
cp -pv "$build"/*.h artifact/ 2>/dev/null || true
|
||||
|
||||
echo "=== artifact contents ==="
|
||||
ls -l artifact
|
||||
echo "=== libmpv-2.dylib load commands (should be system frameworks only) ==="
|
||||
otool -L "artifact/$(basename "$LIBMPV_DYLIB")"
|
||||
Reference in New Issue
Block a user