173 lines
9.0 KiB
Bash
173 lines
9.0 KiB
Bash
#!/bin/bash
|
|
# Run on the ARM64 terminal. All sources, libraries and output stay in TASK_ROOT.
|
|
# No apt install, system library replacement, desktop restart or sudo is needed.
|
|
set -euo pipefail
|
|
TASK_ROOT="${VDI_RKMPP_BUILD_ROOT:-$HOME/vdi-build/rkmpp-experiment}"
|
|
SDK="$TASK_ROOT/deps/usr"
|
|
PREFIX="$TASK_ROOT/runtime"
|
|
JOBS="${VDI_BUILD_JOBS:-2}"
|
|
mkdir -p "$TASK_ROOT"/{src,debs,build} "$PREFIX"
|
|
|
|
clone_source() {
|
|
local name="$1" ref="$2" url="$3"
|
|
if [ ! -d "$TASK_ROOT/src/$name/.git" ]; then
|
|
git clone --depth=1 --branch "$ref" "$url" "$TASK_ROOT/src/$name"
|
|
fi
|
|
test "$(git -C "$TASK_ROOT/src/$name" rev-parse HEAD)" = "$4" || {
|
|
echo "Unexpected $name source revision; inspect before building" >&2; exit 1;
|
|
}
|
|
}
|
|
clone_source mpp develop https://github.com/rockchip-linux/mpp.git 7916c3cc000fefc0bdf96636b7d54fe174724f44
|
|
clone_source ffmpeg 6.1 https://github.com/nyanmisaka/ffmpeg-rockchip.git 797387de1c9caa9170100d7217ee093d1897f750
|
|
clone_source moonlight v6.2.0 https://github.com/moonlight-stream/moonlight-qt.git de2467e433821664cdd2224aad8c89a625be1ad9
|
|
git -C "$TASK_ROOT/src/moonlight" submodule update --init --recursive --depth=1
|
|
|
|
# Versioned VDI interaction patches. Explicit directory supports copying this
|
|
# builder to the box without the rest of the repository. Refuse unknown drift.
|
|
PATCH_DIR="${VDI_MOONLIGHT_PATCH_DIR:-$(cd "$(dirname "$0")/../terminal/moonlight/patches" && pwd)}"
|
|
apply_vdi_patch() {
|
|
local source="$1" patch="$2"
|
|
test -r "$patch" || { echo "Missing VDI patch: $patch" >&2; exit 1; }
|
|
# A Windows checkout can give patch files CRLF. Normalize patch transport
|
|
# only; source drift still has to pass the ordinary exact-context check.
|
|
if sed 's/\r$//' "$patch" | git -C "$source" apply --reverse --check - 2>/dev/null; then
|
|
return
|
|
fi
|
|
sed 's/\r$//' "$patch" | git -C "$source" apply --check -
|
|
sed 's/\r$//' "$patch" | git -C "$source" apply -
|
|
}
|
|
apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0001-vdi-input-and-latest-frame.patch"
|
|
apply_vdi_patch "$TASK_ROOT/src/moonlight/moonlight-common-c/moonlight-common-c" \
|
|
"$PATCH_DIR/0002-vdi-absolute-click-barrier.patch"
|
|
apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0003-vdi-input-priority.patch"
|
|
apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0004-vdi-power-key-policy.patch"
|
|
apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0005-vdi-native-linux-cursor.patch"
|
|
apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0006-vdi-release-stuck-mouse-buttons.patch"
|
|
apply_vdi_patch "$TASK_ROOT/src/moonlight" "$PATCH_DIR/0007-vdi-kiosk-launch-surface.patch"
|
|
|
|
# Moonlight 6.2.0 uses std::optional in drm.h but relies on a transitive
|
|
# include from Qt 6. Our Qt 5 build needs the standard header explicitly.
|
|
export VDI_MOONLIGHT_SOURCE="$TASK_ROOT/src/moonlight"
|
|
python3 - <<'PY'
|
|
import os
|
|
from pathlib import Path
|
|
header = Path(os.environ['VDI_MOONLIGHT_SOURCE']) / 'app/streaming/video/ffmpeg-renderers/drm.h'
|
|
text = header.read_text()
|
|
if '#include <optional>' not in text:
|
|
assert '#include <set>' in text
|
|
header.write_text(text.replace('#include <set>', '#include <optional>\n#include <set>', 1))
|
|
PY
|
|
|
|
# Download only the required headers and private runtime libraries from the
|
|
# configured distribution repository. apt does not install or upgrade anything.
|
|
cd "$TASK_ROOT/debs"
|
|
apt-get download libdrm-dev libopus-dev libsdl2-dev libsdl2-2.0-0 \
|
|
libsdl2-ttf-dev libsdl2-ttf-2.0-0 libudev-dev libxkbcommon-dev \
|
|
libegl-dev libgles-dev libgles1 libgbm-dev libwayland-dev libwayland-bin wayland-protocols \
|
|
qtdeclarative5-dev qtdeclarative5-dev-tools qt5-qmltooling-plugins \
|
|
qtquickcontrols2-5-dev libqt5quickcontrols2-5 libqt5quicktemplates2-5 \
|
|
libqt5svg5-dev libqt5svg5 libqt5qmlworkerscript5 libqt5quickparticles5 \
|
|
libqt5quickshapes5 libqt5quicktest5 libx264-dev \
|
|
qml-module-qtquick2 qml-module-qtquick-window2 qml-module-qtquick-layouts \
|
|
qml-module-qtqml-models2 qml-module-qtquick-controls2 qml-module-qtquick-templates2 \
|
|
libharfbuzz-dev libfreetype-dev libpng-dev zlib1g-dev libbrotli-dev \
|
|
libglib2.0-dev libgraphite2-dev libffi-dev
|
|
for package in ./*.deb; do dpkg-deb -x "$package" "$TASK_ROOT/deps"; done
|
|
|
|
# Relocate pkg-config prefixes, preserving the system /usr and its libraries.
|
|
export VDI_PRIVATE_SDK="$SDK"
|
|
python3 - <<'PY'
|
|
import os
|
|
from pathlib import Path
|
|
sdk = Path(os.environ['VDI_PRIVATE_SDK'])
|
|
for pc in sdk.rglob('*.pc'):
|
|
text = pc.read_text()
|
|
if str(sdk) not in text:
|
|
pc.write_text(text.replace('/usr', str(sdk)))
|
|
for pri in sdk.rglob('qt_lib_*.pri'):
|
|
pri.write_text(pri.read_text().replace('$$QT_MODULE_LIB_BASE', str(sdk / 'lib/aarch64-linux-gnu'))
|
|
.replace('$$QT_MODULE_INCLUDE_BASE', str(sdk / 'include/aarch64-linux-gnu/qt5'))
|
|
.replace('$$QT_MODULE_BIN_BASE', str(sdk / 'lib/qt5/bin')))
|
|
# A -dev symlink can target a different distro patch version than is installed.
|
|
# Link to the existing matching SONAME; never replace the system runtime.
|
|
for link in (sdk / 'lib/aarch64-linux-gnu').glob('*.so'):
|
|
if link.is_symlink() and not link.exists():
|
|
target = Path(os.readlink(link)).name
|
|
if '.so.' not in target:
|
|
continue
|
|
soname = target.split('.so.')[0] + '.so.' + target.split('.so.')[1].split('.')[0]
|
|
system_lib = Path('/lib/aarch64-linux-gnu') / soname
|
|
if system_lib.exists():
|
|
link.unlink()
|
|
link.symlink_to(system_lib)
|
|
PY
|
|
export PKG_CONFIG_PATH="$PREFIX/lib/pkgconfig:$SDK/lib/aarch64-linux-gnu/pkgconfig:$SDK/share/pkgconfig${PKG_CONFIG_PATH:+:$PKG_CONFIG_PATH}"
|
|
export LD_LIBRARY_PATH="$PREFIX/lib:$SDK/lib/aarch64-linux-gnu${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
|
|
export QMAKEPATH="$SDK/lib/aarch64-linux-gnu/qt5"
|
|
export QML2_IMPORT_PATH="$SDK/lib/aarch64-linux-gnu/qt5/qml"
|
|
export CPLUS_INCLUDE_PATH="$SDK/include/aarch64-linux-gnu:$SDK/include${CPLUS_INCLUDE_PATH:+:$CPLUS_INCLUDE_PATH}"
|
|
export C_INCLUDE_PATH="$SDK/include/aarch64-linux-gnu:$SDK/include${C_INCLUDE_PATH:+:$C_INCLUDE_PATH}"
|
|
|
|
if [ "${VDI_BUILD_STAGE:-all}" != moonlight ]; then
|
|
echo '=== build MPP ==='
|
|
cmake -S "$TASK_ROOT/src/mpp" -B "$TASK_ROOT/build/mpp" \
|
|
-DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="$PREFIX" \
|
|
-DRKPLATFORM=ON -DHAVE_DRM=ON -DBUILD_SHARED_LIBS=ON -DBUILD_TEST=OFF
|
|
cmake --build "$TASK_ROOT/build/mpp" -j "$JOBS"
|
|
cmake --install "$TASK_ROOT/build/mpp"
|
|
|
|
echo '=== build FFmpeg with RKMPP ==='
|
|
mkdir -p "$TASK_ROOT/build/ffmpeg"
|
|
cd "$TASK_ROOT/build/ffmpeg"
|
|
"$TASK_ROOT/src/ffmpeg/configure" --prefix="$PREFIX" \
|
|
--enable-gpl --enable-version3 --enable-shared --disable-static \
|
|
--enable-libdrm --enable-rkmpp --disable-rkrga --enable-libx264 \
|
|
--disable-doc --disable-debug --disable-autodetect --disable-network \
|
|
--disable-everything --enable-avcodec --enable-avformat --enable-avfilter \
|
|
--enable-swscale --enable-swresample --enable-ffmpeg --enable-ffprobe \
|
|
--enable-decoder=h264,hevc,h264_rkmpp,hevc_rkmpp,rawvideo,wrapped_avframe \
|
|
--enable-parser=h264,hevc --enable-demuxer=h264,hevc \
|
|
--enable-muxer=h264,null,rawvideo --enable-protocol=file \
|
|
--enable-encoder=libx264,wrapped_avframe,rawvideo --enable-indev=lavfi \
|
|
--enable-filter=testsrc2,null,format,hwdownload,scale \
|
|
--extra-cflags="-I$PREFIX/include -I$SDK/include" \
|
|
--extra-ldflags="-L$PREFIX/lib -L$SDK/lib/aarch64-linux-gnu -Wl,-rpath,$PREFIX/lib -Wl,-rpath,$SDK/lib/aarch64-linux-gnu"
|
|
make -j "$JOBS"
|
|
make install
|
|
"$PREFIX/bin/ffmpeg" -hide_banner -decoders | grep rkmpp
|
|
fi
|
|
|
|
echo '=== build Moonlight ==='
|
|
mkdir -p "$TASK_ROOT/build/moonlight"
|
|
cd "$TASK_ROOT/build/moonlight"
|
|
qmake "$TASK_ROOT/src/moonlight/moonlight-qt.pro" \
|
|
CONFIG+=release CONFIG+=recheck CONFIG+=disable-libva CONFIG+=disable-libvdpau \
|
|
CONFIG+=disable-libplacebo CONFIG+=disable-discord \
|
|
"QMAKE_LIBDIR+=$SDK/lib/aarch64-linux-gnu" \
|
|
"QMAKE_LIBDIR+=$PREFIX/lib" \
|
|
"QMAKE_RPATHDIR+=$SDK/lib/aarch64-linux-gnu" \
|
|
"QMAKE_RPATHDIR+=$PREFIX/lib" \
|
|
"QMAKE_INCDIR+=$SDK/include/aarch64-linux-gnu/qt5" \
|
|
"QMAKE_INCDIR+=$SDK/include/aarch64-linux-gnu" \
|
|
"QMAKE_INCDIR+=$SDK/include"
|
|
make qmake_all
|
|
if ! grep -q -- '-DHAVE_EGL' app/Makefile.Release; then
|
|
echo 'EGL renderer missing; refusing to build a client without the DRM PRIME display path' >&2
|
|
exit 1
|
|
fi
|
|
# qmake/make does not invalidate old objects when feature defines change.
|
|
# In particular a stale ffmpeg.o can silently omit EGL despite a successful
|
|
# configure check. Clean the app whenever its compiler/link flags change.
|
|
flags_hash="$(grep -E '^(DEFINES|INCPATH|LIBS|CXXFLAGS)[[:space:]]*=' app/Makefile.Release | sha256sum | cut -d ' ' -f 1)"
|
|
if [ "$(cat .vdi-build-flags.sha256 2>/dev/null || true)" != "$flags_hash" ]; then
|
|
make -C app -f Makefile.Release clean
|
|
printf '%s\n' "$flags_hash" > .vdi-build-flags.sha256
|
|
fi
|
|
make -j "$JOBS"
|
|
echo '=== complete; experimental binary only, production unchanged ==='
|
|
echo "$TASK_ROOT/build/moonlight/app/moonlight"
|
|
git -C "$TASK_ROOT/src/mpp" rev-parse HEAD
|
|
git -C "$TASK_ROOT/src/ffmpeg" rev-parse HEAD
|
|
git -C "$TASK_ROOT/src/moonlight" rev-parse HEAD
|
|
sha256sum "$TASK_ROOT"/debs/*.deb > "$TASK_ROOT/dependency-sha256.txt"
|