mirror of
https://github.com/narrrl/proton-stream.git
synced 2026-11-04 00:47:00 +01:00
Every Android run since August failed in :app:buildRustHost: the host build of pstr-android reaches libdbus-sys through keyring's Secret Service store, and the runner had no libdbus-1-dev. With no successful run, nothing was ever cached, so each run rebuilt from nothing. - libdbus-1-dev in both jobs - one shared, save-on-failure Rust cache for both jobs - the staged libmpv tree cached on the build script's hash; the script stamps its output with the revision and its own hash and skips the libmpv build when the stamp matches PLAN.md records the Android parity and UI work that follows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
210 lines
8.1 KiB
Bash
210 lines
8.1 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
|
repo_root="$(cd -- "$script_dir/.." && pwd)"
|
|
source_dir="${PSTR_MPV_SOURCE_DIR:-$repo_root/.build/mpv-android}"
|
|
revision=20a3fa526fac6d3fe267aee0d4c349893fee65a3
|
|
destination="$repo_root/android/app/build/generated/mpv"
|
|
|
|
# The staged tree depends only on the pinned revision and on this script, which
|
|
# also pins the NDK. A tree stamped with both is reused as is: that is what lets
|
|
# CI restore it from a cache instead of rebuilding FFmpeg and every other
|
|
# dependency from source on each run.
|
|
stamp="$revision $(sha256sum "${BASH_SOURCE[0]}" | cut -d' ' -f1)"
|
|
if [[ "$(cat "$destination/STAMP" 2>/dev/null)" == "$stamp" &&
|
|
-f "$destination/jniLibs/arm64-v8a/libmpv.so" &&
|
|
-f "$destination/jniLibs/x86_64/libmpv.so" &&
|
|
-f "$destination/mpv-android-source-$revision.tar.gz" ]]; then
|
|
echo "libmpv $revision is already staged in $destination"
|
|
exit 0
|
|
fi
|
|
rm -f -- "$destination/STAMP"
|
|
|
|
if [[ ! -d "$source_dir/.git" ]]; then
|
|
git clone https://github.com/mpv-android/mpv-android.git "$source_dir"
|
|
fi
|
|
git -C "$source_dir" fetch --depth=1 origin "$revision"
|
|
git -C "$source_dir" checkout --detach "$revision"
|
|
git -C "$source_dir" submodule sync --recursive
|
|
git -C "$source_dir" submodule update --init --recursive
|
|
|
|
if [[ -n "${ANDROID_NDK_HOME:-}" ]]; then
|
|
ndk_dir="$ANDROID_NDK_HOME"
|
|
elif [[ -n "${ANDROID_HOME:-}" ]]; then
|
|
ndk_dir="$ANDROID_HOME/ndk/29.0.14206865"
|
|
else
|
|
echo "error: set ANDROID_NDK_HOME or ANDROID_HOME to an Android NDK r29 installation" >&2
|
|
exit 1
|
|
fi
|
|
if [[ ! -f "$ndk_dir/source.properties" ]] ||
|
|
! grep -q '^Pkg.Revision = 29\.0\.14206865$' "$ndk_dir/source.properties"; then
|
|
echo "error: libmpv requires Android NDK 29.0.14206865: $ndk_dir" >&2
|
|
exit 1
|
|
fi
|
|
ndk_dir="$(cd -- "$ndk_dir" && pwd -P)"
|
|
mpv_sdk_dir="$source_dir/buildscripts/sdk"
|
|
mpv_ndk_link="$mpv_sdk_dir/android-ndk-r29"
|
|
mkdir -p "$mpv_sdk_dir"
|
|
if [[ -e "$mpv_ndk_link" && ! -L "$mpv_ndk_link" ]]; then
|
|
echo "error: refusing to replace non-symlink NDK path: $mpv_ndk_link" >&2
|
|
exit 1
|
|
fi
|
|
ln -sfn "$ndk_dir" "$mpv_ndk_link"
|
|
|
|
ensure_lua_source() {
|
|
local lua_dir="$source_dir/buildscripts/deps/lua"
|
|
if [[ -f "$lua_dir/Makefile" ]]; then
|
|
return
|
|
fi
|
|
|
|
local lua_url="https://launchpad.net/ubuntu/+archive/primary/+sourcefiles/lua5.2/5.2.4-3build2/lua5.2_5.2.4.orig.tar.gz"
|
|
local lua_sha256="86fb7e23cbbddfcd92684e5f8017ff41c9112251d1656dbece415a97fad171c0"
|
|
local lua_archive
|
|
local lua_staging
|
|
lua_archive="$(mktemp)"
|
|
lua_staging="$(mktemp -d)"
|
|
if ! curl --fail --location --retry 3 --output "$lua_archive" "$lua_url"; then
|
|
rm -f -- "$lua_archive"
|
|
rm -rf -- "$lua_staging"
|
|
echo "error: failed to download the pinned Lua 5.2.4 source" >&2
|
|
exit 1
|
|
fi
|
|
if ! printf '%s %s\n' "$lua_sha256" "$lua_archive" | sha256sum --check --status; then
|
|
rm -f -- "$lua_archive"
|
|
rm -rf -- "$lua_staging"
|
|
echo "error: Lua 5.2.4 source checksum mismatch" >&2
|
|
exit 1
|
|
fi
|
|
mkdir -p "$source_dir/buildscripts/deps"
|
|
tar -xzf "$lua_archive" -C "$lua_staging" --strip-components=1
|
|
rm -f -- "$lua_archive"
|
|
if [[ -e "$lua_dir" || -L "$lua_dir" ]]; then
|
|
# This path is fixed beneath the generated dependency directory, and the
|
|
# valid-source early return above prevents replacing a completed checkout.
|
|
rm -rf -- "$lua_dir"
|
|
fi
|
|
mv "$lua_staging" "$lua_dir"
|
|
}
|
|
|
|
# This pinned mpv-android revision stores its dependency recipes in-tree and
|
|
# materializes their exact source revisions under buildscripts/deps. Some of
|
|
# those dependencies contain submodules of their own. The downloader is
|
|
# idempotent and must run before buildall can resolve the `mpv` target.
|
|
ensure_lua_source
|
|
(cd "$source_dir/buildscripts" && ./include/download-deps.sh)
|
|
ensure_lua_source
|
|
|
|
for arch in arm64 x86_64; do
|
|
(cd "$source_dir/buildscripts" && ./buildall.sh --arch "$arch" mpv)
|
|
done
|
|
|
|
llvm_readelf="$(find "$ndk_dir/toolchains/llvm/prebuilt" -name llvm-readelf -print -quit)"
|
|
if [[ -z "$llvm_readelf" ]]; then
|
|
echo "error: llvm-readelf is missing from Android NDK: $ndk_dir" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# These libraries are supplied by Android itself. Every other DT_NEEDED entry
|
|
# must be packaged in the APK for each ABI. Keep this list explicit: silently
|
|
# treating an unknown library as a platform library produces installable APKs
|
|
# that fail as soon as the dynamic linker loads pstr_mpv/libmpv.
|
|
is_android_system_library() {
|
|
case "$1" in
|
|
libc.so | libdl.so | liblog.so | libm.so | libz.so | libandroid.so | \
|
|
libmediandk.so | libnativewindow.so | libOpenSLES.so | libaaudio.so | \
|
|
libEGL.so | libGLESv1_CM.so | libGLESv2.so | libGLESv3.so | \
|
|
libjnigraphics.so | libvulkan.so)
|
|
return 0
|
|
;;
|
|
*)
|
|
return 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
stage_shared_dependency_closure() {
|
|
local arch="$1"
|
|
local abi="$2"
|
|
local ndk_triple="$3"
|
|
local prefix_lib="$source_dir/buildscripts/prefix/$arch/lib"
|
|
local ndk_lib="$ndk_dir/toolchains/llvm/prebuilt/$(basename "$(dirname "$(dirname "$llvm_readelf")")")/sysroot/usr/lib/$ndk_triple"
|
|
local output_dir="$destination/jniLibs/$abi"
|
|
local current needed source soname
|
|
local -a pending=(libmpv.so)
|
|
local -A staged=()
|
|
|
|
# Remove stale libraries from an earlier build before constructing the exact
|
|
# closure for this pinned revision.
|
|
mkdir -p "$output_dir"
|
|
find "$output_dir" -maxdepth 1 -type f -name '*.so' -delete
|
|
|
|
while ((${#pending[@]})); do
|
|
current="${pending[0]}"
|
|
pending=("${pending[@]:1}")
|
|
[[ -n "${staged[$current]:-}" ]] && continue
|
|
|
|
if [[ -f "$prefix_lib/$current" ]]; then
|
|
source="$prefix_lib/$current"
|
|
elif [[ "$current" == libc++_shared.so && -f "$ndk_lib/$current" ]]; then
|
|
source="$ndk_lib/$current"
|
|
else
|
|
echo "error: unresolved non-system dependency for $abi: $current" >&2
|
|
exit 1
|
|
fi
|
|
|
|
soname="$($llvm_readelf -d "$source" | sed -n 's/.*SONAME.*\[\(.*\)\]/\1/p')"
|
|
if [[ "$soname" != "$current" ]]; then
|
|
echo "error: $source has SONAME '$soname', expected '$current'" >&2
|
|
exit 1
|
|
fi
|
|
cp -- "$source" "$output_dir/$current"
|
|
staged[$current]=1
|
|
|
|
while IFS= read -r needed; do
|
|
if ! is_android_system_library "$needed" && [[ -z "${staged[$needed]:-}" ]]; then
|
|
pending+=("$needed")
|
|
fi
|
|
done < <("$llvm_readelf" -d "$source" | sed -n 's/.*Shared library: \[\(.*\)\]/\1/p')
|
|
done
|
|
|
|
# Re-read only the packaged copies, so verification also catches a bad or
|
|
# versioned staging filename rather than trusting the source tree.
|
|
for current in "${!staged[@]}"; do
|
|
while IFS= read -r needed; do
|
|
if ! is_android_system_library "$needed" && [[ ! -f "$output_dir/$needed" ]]; then
|
|
echo "error: $abi/$current needs unstaged library $needed" >&2
|
|
exit 1
|
|
fi
|
|
done < <("$llvm_readelf" -d "$output_dir/$current" | sed -n 's/.*Shared library: \[\(.*\)\]/\1/p')
|
|
done
|
|
|
|
printf 'staged %s shared libraries for %s:\n' "${#staged[@]}" "$abi"
|
|
printf ' %s\n' "${!staged[@]}" | LC_ALL=C sort
|
|
}
|
|
|
|
mkdir -p "$destination/include/mpv"
|
|
cp "$source_dir/buildscripts/prefix/arm64/include/mpv/"*.h "$destination/include/mpv/"
|
|
# FFmpeg's JNI entry points are not part of the mpv API, but registering the
|
|
# JavaVM MediaCodec needs is our job now that mpv-android's JNI layer is gone.
|
|
mkdir -p "$destination/include/libavcodec"
|
|
cp "$source_dir/buildscripts/prefix/arm64/include/libavcodec/jni.h" "$destination/include/libavcodec/"
|
|
stage_shared_dependency_closure arm64 arm64-v8a aarch64-linux-android
|
|
stage_shared_dependency_closure x86_64 x86_64 x86_64-linux-android
|
|
|
|
# `git archive` omits dependency working trees. Archive the pinned checkout and
|
|
# downloaded sources together, excluding only generated build/install output
|
|
# and Git administration data, so distributed binaries have corresponding
|
|
# source for mpv and every statically linked dependency.
|
|
tar \
|
|
--exclude='.git' \
|
|
--exclude='buildscripts/prefix' \
|
|
--exclude='buildscripts/sdk' \
|
|
--exclude='buildscripts/deps/*/_build_*' \
|
|
-C "$source_dir" \
|
|
-czf "$destination/mpv-android-source-$revision.tar.gz" \
|
|
.
|
|
printf '%s\n' "$revision" > "$destination/REVISION"
|
|
# Last, so an interrupted run never leaves a stamp over a partial tree.
|
|
printf '%s\n' "$stamp" > "$destination/STAMP"
|