Initial RK1 media-center image project
This commit is contained in:
Executable
+226
@@ -0,0 +1,226 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
LOCK_FILE="${RKMEDIA_ROOT}/sources.lock.json"
|
||||
WORK_DIR="${RKMEDIA_ROOT}/out/work"
|
||||
SOURCE_DIR="${RKMEDIA_ROOT}/out/sources"
|
||||
OUTPUT_DIR="${RKMEDIA_ROOT}/out"
|
||||
JOBS="$(nproc)"
|
||||
FETCH=1
|
||||
PACKAGE_VERSION="$(tr -d '[:space:]' < "${RKMEDIA_ROOT}/VERSION")"
|
||||
RUNTIME_PREFIX=/opt/rkmedia
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: build-media.sh [options]
|
||||
|
||||
--source-dir DIR Locked source checkouts (default: out/sources)
|
||||
--work-dir DIR Build work directory (default: out/work)
|
||||
--output-dir DIR Package output directory (default: out)
|
||||
--jobs N Parallel jobs (default: nproc)
|
||||
--no-fetch Require sources to exist; never access the network
|
||||
--version VERSION Debian package version (default: VERSION file)
|
||||
EOF
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--source-dir) [[ $# -ge 2 ]] || die "--source-dir requires a directory"; SOURCE_DIR="$2"; shift 2 ;;
|
||||
--work-dir) [[ $# -ge 2 ]] || die "--work-dir requires a directory"; WORK_DIR="$2"; shift 2 ;;
|
||||
--output-dir) [[ $# -ge 2 ]] || die "--output-dir requires a directory"; OUTPUT_DIR="$2"; shift 2 ;;
|
||||
--jobs) [[ $# -ge 2 ]] || die "--jobs requires a number"; JOBS="$2"; shift 2 ;;
|
||||
--no-fetch) FETCH=0; shift ;;
|
||||
--version) [[ $# -ge 2 ]] || die "--version requires a value"; PACKAGE_VERSION="$2"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ "${JOBS}" =~ ^[1-9][0-9]*$ ]] || die "--jobs must be a positive integer"
|
||||
[[ "${PACKAGE_VERSION}" =~ ^[0-9A-Za-z.+:~-]+$ ]] || die "invalid Debian package version: ${PACKAGE_VERSION}"
|
||||
require_arm64_trixie
|
||||
for command_name in cmake git jq meson ninja pkg-config make patch readelf strip; do
|
||||
need_command "${command_name}"
|
||||
done
|
||||
|
||||
SOURCE_DIR="$(absolute_path "${SOURCE_DIR}")"
|
||||
WORK_DIR="$(absolute_path "${WORK_DIR}")"
|
||||
OUTPUT_DIR="$(absolute_path "${OUTPUT_DIR}")"
|
||||
mkdir -p -- "${SOURCE_DIR}" "${WORK_DIR}" "${OUTPUT_DIR}"
|
||||
|
||||
if ((FETCH)); then
|
||||
"${SCRIPT_DIR}/fetch-sources.sh" --lock "${LOCK_FILE}" --dest "${SOURCE_DIR}"
|
||||
fi
|
||||
|
||||
while IFS=$'\t' read -r name commit; do
|
||||
dir="${SOURCE_DIR}/${name}"
|
||||
[[ -d "${dir}/.git" ]] || die "missing locked source: ${dir}"
|
||||
[[ "$(git -C "${dir}" rev-parse HEAD)" == "${commit}" ]] || die "source commit mismatch: ${name}"
|
||||
done < <(jq -r '.sources[] | select(.build == true) | [.name, .commit] | @tsv' "${LOCK_FILE}")
|
||||
|
||||
BUILD_ROOT="${WORK_DIR}/build"
|
||||
BUILD_PREFIX="${WORK_DIR}/prefix"
|
||||
STAGE_DIR="${WORK_DIR}/stage"
|
||||
reset_child_dir "${WORK_DIR}" "${BUILD_ROOT}"
|
||||
reset_child_dir "${WORK_DIR}" "${BUILD_PREFIX}"
|
||||
reset_child_dir "${WORK_DIR}" "${STAGE_DIR}"
|
||||
|
||||
export SOURCE_DATE_EPOCH
|
||||
SOURCE_DATE_EPOCH="$(jq -er '.source_date_epoch' "${LOCK_FILE}")"
|
||||
export CFLAGS="${CFLAGS:--O2 -pipe -ffile-prefix-map=${WORK_DIR}=. -fdebug-prefix-map=${WORK_DIR}=.}"
|
||||
export CXXFLAGS="${CXXFLAGS:--O2 -pipe -ffile-prefix-map=${WORK_DIR}=. -fdebug-prefix-map=${WORK_DIR}=.}"
|
||||
export LDFLAGS="${LDFLAGS:-}"
|
||||
|
||||
log "building MPP"
|
||||
cmake -S "${SOURCE_DIR}/mpp" -B "${BUILD_ROOT}/mpp" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${BUILD_PREFIX}" \
|
||||
-DCMAKE_INSTALL_LIBDIR=lib \
|
||||
-DBUILD_SHARED_LIBS=ON \
|
||||
-DBUILD_TEST=OFF
|
||||
cmake --build "${BUILD_ROOT}/mpp" --parallel "${JOBS}"
|
||||
cmake --install "${BUILD_ROOT}/mpp"
|
||||
|
||||
log "building RGA"
|
||||
meson setup "${BUILD_ROOT}/rga" "${SOURCE_DIR}/rga" \
|
||||
--prefix="${BUILD_PREFIX}" \
|
||||
--libdir=lib \
|
||||
--buildtype=release \
|
||||
--default-library=shared \
|
||||
-Dcpp_args=-fpermissive \
|
||||
-Dlibdrm=false \
|
||||
-Dlibrga_demo=false
|
||||
meson compile -C "${BUILD_ROOT}/rga" -j "${JOBS}"
|
||||
meson install -C "${BUILD_ROOT}/rga"
|
||||
|
||||
log "building dav1d"
|
||||
meson setup "${BUILD_ROOT}/dav1d" "${SOURCE_DIR}/dav1d" \
|
||||
--prefix="${BUILD_PREFIX}" \
|
||||
--libdir=lib \
|
||||
--buildtype=release \
|
||||
--default-library=shared \
|
||||
-Denable_tools=false \
|
||||
-Denable_tests=false \
|
||||
-Denable_examples=false \
|
||||
-Denable_docs=false
|
||||
meson compile -C "${BUILD_ROOT}/dav1d" -j "${JOBS}"
|
||||
meson install -C "${BUILD_ROOT}/dav1d"
|
||||
|
||||
export PKG_CONFIG_PATH="${BUILD_PREFIX}/lib/pkgconfig${PKG_CONFIG_PATH:+:${PKG_CONFIG_PATH}}"
|
||||
export LD_LIBRARY_PATH="${BUILD_PREFIX}/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}"
|
||||
|
||||
log "building FFmpeg-Rockchip"
|
||||
mkdir -p -- "${BUILD_ROOT}/ffmpeg"
|
||||
pushd "${BUILD_ROOT}/ffmpeg" >/dev/null
|
||||
"${SOURCE_DIR}/ffmpeg-rockchip/configure" \
|
||||
--prefix="${BUILD_PREFIX}" \
|
||||
--libdir="${BUILD_PREFIX}/lib" \
|
||||
--enable-shared \
|
||||
--disable-static \
|
||||
--disable-debug \
|
||||
--disable-doc \
|
||||
--enable-pic \
|
||||
--enable-gpl \
|
||||
--enable-version3 \
|
||||
--enable-libdrm \
|
||||
--enable-rkmpp \
|
||||
--enable-rkrga \
|
||||
--enable-libdav1d \
|
||||
--extra-cflags="-I${BUILD_PREFIX}/include" \
|
||||
--extra-ldflags="-L${BUILD_PREFIX}/lib -Wl,-rpath,${RUNTIME_PREFIX}/lib"
|
||||
make -j "${JOBS}"
|
||||
make install
|
||||
popd >/dev/null
|
||||
|
||||
FFMPEG_BUILD="${BUILD_PREFIX}/bin/ffmpeg"
|
||||
for decoder in h264_rkmpp hevc_rkmpp vp9_rkmpp av1_rkmpp mjpeg_rkmpp; do
|
||||
"${FFMPEG_BUILD}" -hide_banner -decoders 2>/dev/null | awk '{print $2}' | grep -Fxq "${decoder}" \
|
||||
|| die "FFmpeg is missing decoder ${decoder}"
|
||||
done
|
||||
for encoder in h264_rkmpp hevc_rkmpp mjpeg_rkmpp; do
|
||||
"${FFMPEG_BUILD}" -hide_banner -encoders 2>/dev/null | awk '{print $2}' | grep -Fxq "${encoder}" \
|
||||
|| die "FFmpeg is missing encoder ${encoder}"
|
||||
done
|
||||
for filter in scale_rkrga vpp_rkrga overlay_rkrga; do
|
||||
"${FFMPEG_BUILD}" -hide_banner -filters 2>/dev/null | awk '{print $2}' | grep -Fxq "${filter}" \
|
||||
|| die "FFmpeg is missing filter ${filter}"
|
||||
done
|
||||
|
||||
log "building libdisplay-info"
|
||||
meson setup "${BUILD_ROOT}/libdisplay-info" "${SOURCE_DIR}/libdisplay-info" \
|
||||
--prefix="${BUILD_PREFIX}" \
|
||||
--libdir=lib \
|
||||
--buildtype=release \
|
||||
--default-library=shared
|
||||
meson compile -C "${BUILD_ROOT}/libdisplay-info" -j "${JOBS}"
|
||||
meson install -C "${BUILD_ROOT}/libdisplay-info"
|
||||
|
||||
log "building Kodi for GBM/GLES with external FFmpeg-Rockchip"
|
||||
cmake -S "${SOURCE_DIR}/kodi" -B "${BUILD_ROOT}/kodi" -G Ninja \
|
||||
-DCMAKE_BUILD_TYPE=Release \
|
||||
-DCMAKE_INSTALL_PREFIX="${RUNTIME_PREFIX}" \
|
||||
-DCMAKE_INSTALL_RPATH="${RUNTIME_PREFIX}/lib" \
|
||||
-DCMAKE_BUILD_RPATH="${BUILD_PREFIX}/lib" \
|
||||
-DCMAKE_PREFIX_PATH="${BUILD_PREFIX}" \
|
||||
-DCORE_PLATFORM_NAME=gbm \
|
||||
-DAPP_RENDER_SYSTEM=gles \
|
||||
-DENABLE_INTERNAL_FFMPEG=OFF \
|
||||
-DFFMPEG_PATH="${BUILD_PREFIX}" \
|
||||
-DENABLE_INTERNAL_FMT=ON \
|
||||
-DENABLE_INTERNAL_FLATBUFFERS=ON
|
||||
cmake --build "${BUILD_ROOT}/kodi" --parallel "${JOBS}"
|
||||
|
||||
mkdir -p -- "${STAGE_DIR}${RUNTIME_PREFIX}"
|
||||
cp -a -- "${BUILD_PREFIX}/." "${STAGE_DIR}${RUNTIME_PREFIX}/"
|
||||
DESTDIR="${STAGE_DIR}" cmake --install "${BUILD_ROOT}/kodi"
|
||||
|
||||
log "adding runtime integration and source provenance"
|
||||
cp -a -- "${RKMEDIA_ROOT}/packaging/rootfs/." "${STAGE_DIR}/"
|
||||
mkdir -p -- "${STAGE_DIR}/usr/bin" "${STAGE_DIR}/usr/share/doc/rk1-media-stack/licenses"
|
||||
ln -sfn /opt/rkmedia/bin/ffmpeg-rk "${STAGE_DIR}/usr/bin/ffmpeg-rk"
|
||||
ln -sfn /opt/rkmedia/bin/ffprobe-rk "${STAGE_DIR}/usr/bin/ffprobe-rk"
|
||||
ln -sfn /opt/rkmedia/bin/kodi-rk "${STAGE_DIR}/usr/bin/kodi-rk"
|
||||
install -m 0644 "${LOCK_FILE}" "${STAGE_DIR}/usr/share/doc/rk1-media-stack/sources.lock.json"
|
||||
|
||||
while IFS=$'\t' read -r name license_path; do
|
||||
source_license="${SOURCE_DIR}/${name}/${license_path}"
|
||||
destination="${STAGE_DIR}/usr/share/doc/rk1-media-stack/licenses/${name}"
|
||||
[[ -e "${source_license}" ]] || die "locked license path is missing: ${name}/${license_path}"
|
||||
mkdir -p -- "${destination}"
|
||||
cp -a -- "${source_license}" "${destination}/"
|
||||
done < <(jq -r '.sources[] | select(.build == true) as $source | $source.license_files[] | [$source.name, .] | @tsv' "${LOCK_FILE}")
|
||||
|
||||
rm -rf -- \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/include" \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/lib/pkgconfig" \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/share/pkgconfig"
|
||||
find "${STAGE_DIR}${RUNTIME_PREFIX}" -type f \( -name '*.a' -o -name '*.la' \) -delete
|
||||
|
||||
while IFS= read -r -d '' candidate; do
|
||||
if file --brief "${candidate}" | grep -q '^ELF '; then
|
||||
strip --strip-unneeded "${candidate}" || die "failed to strip ${candidate}"
|
||||
fi
|
||||
done < <(find "${STAGE_DIR}${RUNTIME_PREFIX}" -type f -print0)
|
||||
|
||||
for required in \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/bin/ffmpeg" \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/bin/ffprobe" \
|
||||
"${STAGE_DIR}${RUNTIME_PREFIX}/bin/kodi" \
|
||||
"${STAGE_DIR}/usr/lib/systemd/system/kodi-rk.service"; do
|
||||
[[ -e "${required}" ]] || die "staged runtime file is missing: ${required}"
|
||||
done
|
||||
|
||||
if find "${STAGE_DIR}${RUNTIME_PREFIX}" -type f -exec grep -IlF "${WORK_DIR}" {} + | grep -q .; then
|
||||
die "build path leaked into a staged text file"
|
||||
fi
|
||||
|
||||
"${SCRIPT_DIR}/package-media.sh" \
|
||||
--stage "${STAGE_DIR}" \
|
||||
--output-dir "${OUTPUT_DIR}" \
|
||||
--version "${PACKAGE_VERSION}"
|
||||
|
||||
log "media build complete"
|
||||
Executable
+85
@@ -0,0 +1,85 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
LOCK_FILE="${RKMEDIA_ROOT}/sources.lock.json"
|
||||
DEST_DIR="${RKMEDIA_ROOT}/out/sources"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: fetch-sources.sh [--lock FILE] [--dest DIRECTORY]
|
||||
|
||||
Fetch every build input at the exact commit recorded in the source lock.
|
||||
Existing repositories are accepted only when their origin and HEAD match.
|
||||
EOF
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--lock)
|
||||
[[ $# -ge 2 ]] || die "--lock requires a file"
|
||||
LOCK_FILE="$2"
|
||||
shift 2
|
||||
;;
|
||||
--dest)
|
||||
[[ $# -ge 2 ]] || die "--dest requires a directory"
|
||||
DEST_DIR="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
need_command git
|
||||
need_command jq
|
||||
LOCK_FILE="$(absolute_path "${LOCK_FILE}")"
|
||||
DEST_DIR="$(absolute_path "${DEST_DIR}")"
|
||||
[[ -f "${LOCK_FILE}" ]] || die "source lock not found: ${LOCK_FILE}"
|
||||
jq -e '.schema_version == 1 and (.sources | type == "array")' "${LOCK_FILE}" >/dev/null \
|
||||
|| die "invalid source lock: ${LOCK_FILE}"
|
||||
mkdir -p -- "${DEST_DIR}"
|
||||
|
||||
verify_checkout() {
|
||||
local dir="$1" expected_repo="$2" expected_commit="$3"
|
||||
local actual_repo actual_commit
|
||||
[[ -d "${dir}/.git" ]] || return 1
|
||||
actual_repo="$(git -C "${dir}" remote get-url origin 2>/dev/null || true)"
|
||||
actual_commit="$(git -C "${dir}" rev-parse HEAD 2>/dev/null || true)"
|
||||
[[ "${actual_repo}" == "${expected_repo}" && "${actual_commit}" == "${expected_commit}" ]]
|
||||
}
|
||||
|
||||
while IFS=$'\t' read -r name repository ref commit; do
|
||||
[[ "${name}" =~ ^[a-z0-9][a-z0-9._-]*$ ]] || die "unsafe source name in lock: ${name}"
|
||||
[[ "${commit}" =~ ^[0-9a-f]{40}$ ]] || die "invalid commit for ${name}: ${commit}"
|
||||
source_dir="${DEST_DIR}/${name}"
|
||||
|
||||
if verify_checkout "${source_dir}" "${repository}" "${commit}"; then
|
||||
log "source already verified: ${name} ${commit}"
|
||||
continue
|
||||
fi
|
||||
if [[ -e "${source_dir}" ]]; then
|
||||
die "${source_dir} exists but does not match the lock; move it aside and retry"
|
||||
fi
|
||||
|
||||
log "fetching ${name} at ${commit}"
|
||||
mkdir -p -- "${source_dir}"
|
||||
git -C "${source_dir}" init --quiet
|
||||
git -C "${source_dir}" remote add origin "${repository}"
|
||||
if ! git -C "${source_dir}" fetch --quiet --depth=1 origin "${commit}"; then
|
||||
log "direct commit fetch was unavailable; fetching history from ${ref}"
|
||||
git -C "${source_dir}" fetch --quiet --filter=blob:none origin "${ref}"
|
||||
fi
|
||||
git -C "${source_dir}" checkout --quiet --detach "${commit}"
|
||||
verify_checkout "${source_dir}" "${repository}" "${commit}" \
|
||||
|| die "checkout verification failed for ${name}"
|
||||
done < <(jq -r '.sources[] | select(.build == true) | [.name, .repository, .ref, .commit] | @tsv' "${LOCK_FILE}")
|
||||
|
||||
log "all locked sources are present in ${DEST_DIR}"
|
||||
|
||||
Executable
+63
@@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
MODE=check
|
||||
case "${1:---check}" in
|
||||
--check) MODE=check ;;
|
||||
--install) MODE=install ;;
|
||||
-h|--help)
|
||||
printf 'Usage: install-build-deps.sh [--check|--install]\n'
|
||||
exit 0
|
||||
;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
|
||||
require_arm64_trixie
|
||||
need_command dpkg-query
|
||||
|
||||
packages=(
|
||||
adduser autoconf automake autopoint autotools-dev build-essential ca-certificates
|
||||
cmake default-jre devscripts dpkg-dev file gawk gcc g++ gdc gettext git gperf
|
||||
hwdata jq libasound2-dev libass-dev libavahi-client-dev libavahi-common-dev
|
||||
libbluetooth-dev libbluray-dev libbz2-dev libcdio++-dev libcdio-dev libcec-dev
|
||||
libcrossguid-dev libcwiid-dev libcurl4-openssl-dev libdbus-1-dev libdrm-dev
|
||||
libegl1-mesa-dev libenca-dev libexiv2-dev libflac-dev libfmt-dev
|
||||
libfontconfig-dev libfreetype6-dev libfribidi-dev libfstrcmp-dev libgbm-dev
|
||||
libgcrypt20-dev libgif-dev libgl1-mesa-dev libgles2-mesa-dev libglu1-mesa-dev
|
||||
libgnutls28-dev libgpg-error-dev libgtest-dev libinput-dev libiso9660-dev
|
||||
libjpeg-dev liblcms2-dev liblirc-dev libltdl-dev liblzo2-dev libmariadb-dev
|
||||
libmicrohttpd-dev libnfs-dev libogg-dev libp8-platform-dev libpcre2-dev
|
||||
libplist-dev libpng-dev libpulse-dev libshairplay-dev libsmbclient-dev
|
||||
libspdlog-dev libsqlite3-dev libssl-dev libtag1-dev libtiff-dev
|
||||
libtinyxml2-dev libtinyxml-dev libtool libudev-dev libunistring-dev
|
||||
libvorbis-dev libxkbcommon-dev libxslt1-dev libxt-dev lsb-release meson nasm
|
||||
ninja-build nlohmann-json3-dev patch pkg-config python3-dev python3-pil
|
||||
python3-pip rapidjson-dev swig unzip uuid-dev zip zlib1g-dev
|
||||
)
|
||||
|
||||
missing=()
|
||||
for package in "${packages[@]}"; do
|
||||
if ! dpkg-query -W -f='${db:Status-Abbrev}' "${package}" 2>/dev/null | grep -q '^ii '; then
|
||||
missing+=("${package}")
|
||||
fi
|
||||
done
|
||||
|
||||
if ((${#missing[@]} == 0)); then
|
||||
log "all build dependencies are installed"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ "${MODE}" == "check" ]]; then
|
||||
printf 'Missing build packages:\n'
|
||||
printf ' %s\n' "${missing[@]}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
[[ "${EUID}" -eq 0 ]] || die "--install must run as root (use sudo)"
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y --no-install-recommends "${missing[@]}"
|
||||
Executable
+62
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
|
||||
RKMEDIA_SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
RKMEDIA_ROOT="$(cd -- "${RKMEDIA_SCRIPT_DIR}/.." && pwd -P)"
|
||||
|
||||
log() {
|
||||
printf '[rkmedia] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
printf '[rkmedia] ERROR: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
need_command() {
|
||||
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
|
||||
}
|
||||
|
||||
absolute_path() {
|
||||
realpath -m -- "$1"
|
||||
}
|
||||
|
||||
assert_child_path() {
|
||||
local parent child
|
||||
parent="$(absolute_path "$1")"
|
||||
child="$(absolute_path "$2")"
|
||||
case "${child}" in
|
||||
"${parent}"/*) ;;
|
||||
*) die "refusing operation outside ${parent}: ${child}" ;;
|
||||
esac
|
||||
[[ "${child}" != "${parent}" ]] || die "refusing operation on parent directory itself: ${parent}"
|
||||
}
|
||||
|
||||
reset_child_dir() {
|
||||
local parent="$1"
|
||||
local child="$2"
|
||||
assert_child_path "${parent}" "${child}"
|
||||
rm -rf -- "${child}"
|
||||
mkdir -p -- "${child}"
|
||||
}
|
||||
|
||||
require_arm64_trixie() {
|
||||
local arch codename
|
||||
arch="$(dpkg --print-architecture 2>/dev/null || true)"
|
||||
codename=""
|
||||
if [[ -r /etc/os-release ]]; then
|
||||
# shellcheck disable=SC1091
|
||||
. /etc/os-release
|
||||
codename="${VERSION_CODENAME:-}"
|
||||
fi
|
||||
|
||||
if [[ "${arch}" != "arm64" || "$(uname -m)" != "aarch64" || "${codename}" != "trixie" ]]; then
|
||||
if [[ "${RKMEDIA_ALLOW_UNSUPPORTED_HOST:-0}" == "1" ]]; then
|
||||
log "warning: unsupported build host (dpkg=${arch}, uname=$(uname -m), suite=${codename:-unknown})"
|
||||
return
|
||||
fi
|
||||
die "native Debian Trixie arm64 is required (set RKMEDIA_ALLOW_UNSUPPORTED_HOST=1 only for development)"
|
||||
fi
|
||||
}
|
||||
|
||||
Executable
+134
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
umask 022
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
STAGE_DIR=""
|
||||
OUTPUT_DIR="${RKMEDIA_ROOT}/out"
|
||||
PACKAGE_VERSION="$(tr -d '[:space:]' < "${RKMEDIA_ROOT}/VERSION")"
|
||||
DEPENDS_FROM_DEB=""
|
||||
|
||||
usage() {
|
||||
printf 'Usage: package-media.sh --stage DIRECTORY [--output-dir DIRECTORY] [--version VERSION] [--depends-from-deb FILE]\n'
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--stage) [[ $# -ge 2 ]] || die "--stage requires a directory"; STAGE_DIR="$2"; shift 2 ;;
|
||||
--output-dir) [[ $# -ge 2 ]] || die "--output-dir requires a directory"; OUTPUT_DIR="$2"; shift 2 ;;
|
||||
--version) [[ $# -ge 2 ]] || die "--version requires a value"; PACKAGE_VERSION="$2"; shift 2 ;;
|
||||
--depends-from-deb) [[ $# -ge 2 ]] || die "--depends-from-deb requires a file"; DEPENDS_FROM_DEB="$2"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n "${STAGE_DIR}" ]] || die "--stage is required"
|
||||
[[ "${PACKAGE_VERSION}" =~ ^[0-9A-Za-z.+:~-]+$ ]] || die "invalid Debian package version: ${PACKAGE_VERSION}"
|
||||
for command_name in dpkg-deb dpkg-shlibdeps file jq md5sum sed sha256sum; do
|
||||
need_command "${command_name}"
|
||||
done
|
||||
|
||||
STAGE_DIR="$(absolute_path "${STAGE_DIR}")"
|
||||
OUTPUT_DIR="$(absolute_path "${OUTPUT_DIR}")"
|
||||
[[ -x "${STAGE_DIR}/opt/rkmedia/bin/ffmpeg" ]] || die "stage does not contain the RKMedia runtime"
|
||||
mkdir -p -- "${OUTPUT_DIR}"
|
||||
|
||||
PACKAGE_ROOT="${OUTPUT_DIR}/package-root"
|
||||
reset_child_dir "${OUTPUT_DIR}" "${PACKAGE_ROOT}"
|
||||
cp -a -- "${STAGE_DIR}/." "${PACKAGE_ROOT}/"
|
||||
mkdir -p -- "${PACKAGE_ROOT}/DEBIAN" "${PACKAGE_ROOT}/usr/share/doc/rk1-media-stack"
|
||||
|
||||
cp -- "${RKMEDIA_ROOT}/packaging/postinst" "${PACKAGE_ROOT}/DEBIAN/postinst"
|
||||
cp -- "${RKMEDIA_ROOT}/packaging/prerm" "${PACKAGE_ROOT}/DEBIAN/prerm"
|
||||
cp -- "${RKMEDIA_ROOT}/packaging/postrm" "${PACKAGE_ROOT}/DEBIAN/postrm"
|
||||
chmod 0755 "${PACKAGE_ROOT}/DEBIAN/postinst" "${PACKAGE_ROOT}/DEBIAN/prerm" "${PACKAGE_ROOT}/DEBIAN/postrm"
|
||||
printf '/etc/rkmedia/kodi.env\n' > "${PACKAGE_ROOT}/DEBIAN/conffiles"
|
||||
|
||||
# A collaborative workspace or permissive host umask must never make core
|
||||
# target directories such as /etc, /usr, or /opt group-writable via dpkg.
|
||||
find "${PACKAGE_ROOT}" -type d -exec chmod 0755 {} +
|
||||
find "${PACKAGE_ROOT}" -type f ! -perm /0111 -exec chmod 0644 {} +
|
||||
find "${PACKAGE_ROOT}" -type f -perm /0111 -exec chmod 0755 {} +
|
||||
if find "${PACKAGE_ROOT}" \( -type d -o -type f \) -perm /0022 -print -quit \
|
||||
| grep -q .; then
|
||||
die "package contains a group/world-writable path"
|
||||
fi
|
||||
|
||||
if [[ -n "${DEPENDS_FROM_DEB}" ]]; then
|
||||
DEPENDS_FROM_DEB="$(absolute_path "${DEPENDS_FROM_DEB}")"
|
||||
[[ -f "${DEPENDS_FROM_DEB}" ]] || die "dependency source package not found: ${DEPENDS_FROM_DEB}"
|
||||
shlib_depends="$(dpkg-deb --field "${DEPENDS_FROM_DEB}" Depends)"
|
||||
[[ -n "${shlib_depends}" ]] || die "upstream package has an empty Depends field"
|
||||
else
|
||||
analysis_root="${OUTPUT_DIR}/shlibdeps"
|
||||
reset_child_dir "${OUTPUT_DIR}" "${analysis_root}"
|
||||
mkdir -p -- "${analysis_root}/debian"
|
||||
cat > "${analysis_root}/debian/control" <<'EOF'
|
||||
Source: rk1-media-stack
|
||||
Section: video
|
||||
Priority: optional
|
||||
|
||||
Package: rk1-media-stack
|
||||
Architecture: arm64
|
||||
Description: temporary metadata for dpkg-shlibdeps
|
||||
EOF
|
||||
|
||||
elf_files=()
|
||||
while IFS= read -r -d '' candidate; do
|
||||
if file --brief "${candidate}" | grep -q '^ELF '; then
|
||||
elf_files+=("${candidate}")
|
||||
fi
|
||||
done < <(find "${PACKAGE_ROOT}/opt/rkmedia" -type f -print0)
|
||||
((${#elf_files[@]} > 0)) || die "no ELF files found in media stage"
|
||||
|
||||
pushd "${analysis_root}" >/dev/null
|
||||
shlib_output="$(dpkg-shlibdeps --ignore-missing-info -O \
|
||||
-l"${PACKAGE_ROOT}/opt/rkmedia/lib" "${elf_files[@]}")"
|
||||
popd >/dev/null
|
||||
shlib_depends="${shlib_output#shlibs:Depends=}"
|
||||
[[ -n "${shlib_depends}" && "${shlib_depends}" != "${shlib_output}" ]] \
|
||||
|| die "dpkg-shlibdeps did not produce runtime dependencies"
|
||||
fi
|
||||
|
||||
depends="adduser, systemd, udev, ${shlib_depends}"
|
||||
installed_size="$(du -sk "${PACKAGE_ROOT}" | awk '{print $1}')"
|
||||
if [[ -f "${PACKAGE_ROOT}/usr/share/doc/rk1-media-stack/upstream-deb.lock.json" ]]; then
|
||||
provenance_file="${PACKAGE_ROOT}/usr/share/doc/rk1-media-stack/upstream-deb.lock.json"
|
||||
else
|
||||
provenance_file="${PACKAGE_ROOT}/usr/share/doc/rk1-media-stack/sources.lock.json"
|
||||
fi
|
||||
lock_sha256="$(sha256sum "${provenance_file}" | awk '{print $1}')"
|
||||
|
||||
sed \
|
||||
-e "s/@VERSION@/${PACKAGE_VERSION}/g" \
|
||||
-e "s/@INSTALLED_SIZE@/${installed_size}/g" \
|
||||
-e "s/@LOCK_SHA256@/${lock_sha256}/g" \
|
||||
-e "s/@DEPENDS@/${depends}/g" \
|
||||
"${RKMEDIA_ROOT}/packaging/control.in" > "${PACKAGE_ROOT}/DEBIAN/control"
|
||||
|
||||
pushd "${PACKAGE_ROOT}" >/dev/null
|
||||
find etc opt usr -type f -print0 \
|
||||
| sort -z \
|
||||
| xargs -0 md5sum > DEBIAN/md5sums
|
||||
popd >/dev/null
|
||||
|
||||
if find "${PACKAGE_ROOT}" \( -type d -o -type f \) -perm /0022 -print -quit \
|
||||
| grep -q .; then
|
||||
die "package contains a group/world-writable path after metadata generation"
|
||||
fi
|
||||
|
||||
output_deb="${OUTPUT_DIR}/rk1-media-stack_${PACKAGE_VERSION}_arm64.deb"
|
||||
rm -f -- "${output_deb}"
|
||||
SOURCE_DATE_EPOCH="$(jq -er '.source_date_epoch' "${RKMEDIA_ROOT}/sources.lock.json")" \
|
||||
dpkg-deb --root-owner-group --build "${PACKAGE_ROOT}" "${output_deb}"
|
||||
(
|
||||
cd "${OUTPUT_DIR}"
|
||||
sha256sum "$(basename -- "${output_deb}")" \
|
||||
>"$(basename -- "${output_deb}").sha256"
|
||||
)
|
||||
dpkg-deb --info "${output_deb}" >/dev/null
|
||||
log "created ${output_deb}"
|
||||
Executable
+144
@@ -0,0 +1,144 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -Eeuo pipefail
|
||||
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)"
|
||||
# shellcheck source=lib.sh
|
||||
. "${SCRIPT_DIR}/lib.sh"
|
||||
|
||||
LOCK_FILE="${RKMEDIA_ROOT}/upstream-deb.lock.json"
|
||||
WORK_DIR="${RKMEDIA_ROOT}/out/repack-work"
|
||||
OUTPUT_DIR="${RKMEDIA_ROOT}/out"
|
||||
CACHE_DIR="${RKMEDIA_ROOT}/out/downloads"
|
||||
INPUT_DEB=""
|
||||
PACKAGE_VERSION="20260721.1741+rk1.1"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: repack-upstream-deb.sh [options]
|
||||
|
||||
--deb FILE Use an existing upstream .deb instead of downloading
|
||||
--work-dir DIR Temporary extraction directory
|
||||
--output-dir DIR Repacked package output directory
|
||||
--cache-dir DIR Download cache directory
|
||||
--version VERSION Output Debian package version
|
||||
EOF
|
||||
}
|
||||
|
||||
while (($#)); do
|
||||
case "$1" in
|
||||
--deb) [[ $# -ge 2 ]] || die "--deb requires a file"; INPUT_DEB="$2"; shift 2 ;;
|
||||
--work-dir) [[ $# -ge 2 ]] || die "--work-dir requires a directory"; WORK_DIR="$2"; shift 2 ;;
|
||||
--output-dir) [[ $# -ge 2 ]] || die "--output-dir requires a directory"; OUTPUT_DIR="$2"; shift 2 ;;
|
||||
--cache-dir) [[ $# -ge 2 ]] || die "--cache-dir requires a directory"; CACHE_DIR="$2"; shift 2 ;;
|
||||
--version) [[ $# -ge 2 ]] || die "--version requires a value"; PACKAGE_VERSION="$2"; shift 2 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown argument: $1" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
for command_name in curl dpkg-deb file jq readelf sha256sum stat; do
|
||||
need_command "${command_name}"
|
||||
done
|
||||
[[ -f "${LOCK_FILE}" ]] || die "upstream asset lock is missing: ${LOCK_FILE}"
|
||||
jq -e '.schema_version == 1 and (.asset_sha256 | test("^[0-9a-f]{64}$"))' "${LOCK_FILE}" >/dev/null \
|
||||
|| die "invalid upstream asset lock"
|
||||
|
||||
WORK_DIR="$(absolute_path "${WORK_DIR}")"
|
||||
OUTPUT_DIR="$(absolute_path "${OUTPUT_DIR}")"
|
||||
CACHE_DIR="$(absolute_path "${CACHE_DIR}")"
|
||||
mkdir -p -- "${WORK_DIR}" "${OUTPUT_DIR}" "${CACHE_DIR}"
|
||||
|
||||
asset_name="$(jq -er '.asset_name' "${LOCK_FILE}")"
|
||||
asset_url="$(jq -er '.asset_url' "${LOCK_FILE}")"
|
||||
expected_size="$(jq -er '.asset_size' "${LOCK_FILE}")"
|
||||
expected_sha256="$(jq -er '.asset_sha256' "${LOCK_FILE}")"
|
||||
|
||||
if [[ -z "${INPUT_DEB}" ]]; then
|
||||
INPUT_DEB="${CACHE_DIR}/${asset_name}"
|
||||
if [[ ! -f "${INPUT_DEB}" ]]; then
|
||||
partial="${INPUT_DEB}.partial"
|
||||
rm -f -- "${partial}"
|
||||
log "downloading pinned upstream Kodi package"
|
||||
curl -L --fail --show-error --output "${partial}" "${asset_url}"
|
||||
mv -- "${partial}" "${INPUT_DEB}"
|
||||
fi
|
||||
fi
|
||||
INPUT_DEB="$(absolute_path "${INPUT_DEB}")"
|
||||
[[ -f "${INPUT_DEB}" ]] || die "upstream package not found: ${INPUT_DEB}"
|
||||
|
||||
actual_size="$(stat -c '%s' "${INPUT_DEB}")"
|
||||
actual_sha256="$(sha256sum "${INPUT_DEB}" | awk '{print $1}')"
|
||||
[[ "${actual_size}" == "${expected_size}" ]] || die "upstream package size mismatch"
|
||||
[[ "${actual_sha256}" == "${expected_sha256}" ]] || die "upstream package SHA-256 mismatch"
|
||||
[[ "$(dpkg-deb --field "${INPUT_DEB}" Package)" == "$(jq -er '.package' "${LOCK_FILE}")" ]] \
|
||||
|| die "upstream package name mismatch"
|
||||
[[ "$(dpkg-deb --field "${INPUT_DEB}" Version)" == "$(jq -er '.package_version' "${LOCK_FILE}")" ]] \
|
||||
|| die "upstream package version mismatch"
|
||||
[[ "$(dpkg-deb --field "${INPUT_DEB}" Architecture)" == "arm64" ]] \
|
||||
|| die "upstream package is not arm64"
|
||||
|
||||
STAGE_DIR="${WORK_DIR}/stage"
|
||||
reset_child_dir "${WORK_DIR}" "${STAGE_DIR}"
|
||||
dpkg-deb --extract "${INPUT_DEB}" "${STAGE_DIR}"
|
||||
|
||||
local_bin="${STAGE_DIR}/usr/local/bin"
|
||||
local_lib="${STAGE_DIR}/usr/local/lib"
|
||||
runtime_bin="${STAGE_DIR}/opt/rkmedia/bin"
|
||||
runtime_lib="${STAGE_DIR}/opt/rkmedia/lib"
|
||||
mkdir -p -- "${runtime_bin}" "${runtime_lib}"
|
||||
|
||||
for program in ffmpeg ffprobe; do
|
||||
[[ -x "${local_bin}/${program}" ]] || die "upstream package is missing ${program}"
|
||||
mv -- "${local_bin}/${program}" "${runtime_bin}/${program}"
|
||||
done
|
||||
if [[ -x "${local_bin}/di-edid-decode" ]]; then
|
||||
mv -- "${local_bin}/di-edid-decode" "${runtime_bin}/di-edid-decode"
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
runtime_libraries=(
|
||||
"${local_lib}"/libdav1d.so*
|
||||
"${local_lib}"/librga.so*
|
||||
"${local_lib}"/librockchip_mpp.so*
|
||||
"${local_lib}"/librockchip_vpu.so*
|
||||
"${local_lib}"/aarch64-linux-gnu/libdisplay-info.so*
|
||||
)
|
||||
((${#runtime_libraries[@]} > 0)) || die "no Rockchip runtime libraries found in upstream package"
|
||||
mv -- "${runtime_libraries[@]}" "${runtime_lib}/"
|
||||
shopt -u nullglob
|
||||
|
||||
find "${local_lib}" -type f \( -name '*.a' -o -name '*.la' \) -delete
|
||||
rm -rf -- "${local_lib}/pkgconfig" "${local_lib}/aarch64-linux-gnu/pkgconfig"
|
||||
rm -f -- \
|
||||
"${STAGE_DIR}/usr/lib/systemd/system/kodi.service" \
|
||||
"${STAGE_DIR}/usr/lib/systemd/system/kodi-pulse.service" \
|
||||
"${STAGE_DIR}/usr/lib/systemd/system/pulseaudio.service"
|
||||
|
||||
cp -a -- "${RKMEDIA_ROOT}/packaging/rootfs/." "${STAGE_DIR}/"
|
||||
mkdir -p -- "${STAGE_DIR}/usr/bin" "${STAGE_DIR}/usr/share/doc/rk1-media-stack"
|
||||
ln -sfn /opt/rkmedia/bin/ffmpeg-rk "${STAGE_DIR}/usr/bin/ffmpeg-rk"
|
||||
ln -sfn /opt/rkmedia/bin/ffprobe-rk "${STAGE_DIR}/usr/bin/ffprobe-rk"
|
||||
ln -sfn /opt/rkmedia/bin/kodi-rk "${STAGE_DIR}/usr/bin/kodi-rk"
|
||||
install -m 0644 "${LOCK_FILE}" "${STAGE_DIR}/usr/share/doc/rk1-media-stack/upstream-deb.lock.json"
|
||||
|
||||
[[ -x "${STAGE_DIR}/usr/local/bin/kodi" ]] || die "Kodi launcher was not retained at its compiled prefix"
|
||||
[[ -x "${STAGE_DIR}/usr/local/lib/kodi/kodi-gbm" ]] || die "Kodi GBM binary is missing"
|
||||
for soname in librga.so.2 libdav1d.so.7 librockchip_mpp.so.1 libdisplay-info.so.4; do
|
||||
[[ -e "${runtime_lib}/${soname}" ]] || die "relocated runtime is missing ${soname}"
|
||||
readelf -d "${STAGE_DIR}/usr/local/lib/kodi/kodi-gbm" | grep -Fq "Shared library: [${soname}]" \
|
||||
|| die "Kodi binary does not declare expected dependency ${soname}"
|
||||
done
|
||||
if find "${STAGE_DIR}" -type f -name '*.a' -print -quit | grep -q .; then
|
||||
die "static library remained after repack"
|
||||
fi
|
||||
if find "${STAGE_DIR}/usr/local/lib" -type d -name pkgconfig -print -quit | grep -q .; then
|
||||
die "pkg-config development directory remained after repack"
|
||||
fi
|
||||
|
||||
"${SCRIPT_DIR}/package-media.sh" \
|
||||
--stage "${STAGE_DIR}" \
|
||||
--output-dir "${OUTPUT_DIR}" \
|
||||
--version "${PACKAGE_VERSION}" \
|
||||
--depends-from-deb "${INPUT_DEB}"
|
||||
|
||||
log "repack complete; authoritative input SHA-256: ${actual_sha256}"
|
||||
Reference in New Issue
Block a user