Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -2794,6 +2794,16 @@ no replacement userspace build is needed.
`collect_declared_bsp_pkgs` and the pin file's names); `fetch_bsp` EXCLUDES exactly
this set from the Armbian fetch. An x86 family declares none. DRY_RUN logs the exact
URLs + hashes and downloads nothing.
- **Trixie `libv4l-0` compatibility is a generated package, not a patched upstream
asset.** `libv4l-0t64` provides the old name to apt but dpkg records only the t64
name, while the pinned `rockchip-multimedia-config` postinst runs
`dpkg -L libv4l-0 | grep libv4l2.so.0.0.0` and then plain `cp`. A fileless package
cannot satisfy that grep. `fetch_rk3588_userspace` therefore builds a real arm64
`libv4l-0` depending on `libv4l-0t64`, with exactly one payload: a collision-free
`/usr/share/libv4l-0-compat/libv4l2.so.0.0.0` symlink to the t64-owned library.
The platform layer explicitly installs it first in the SAME `mkosi-install`
transaction as the Rockchip package. Never move the payload onto either
`/usr/lib/aarch64-linux-gnu/libv4l2.so.0*` path; both belong to `libv4l-0t64`.
- **DO NOT** convert any of these into a `deb [signed-by=...] https://...` apt line —
that is a new live trust root, exactly what the pinned-URL + SHA-256 approach avoids.
**DO NOT** bump a pinned VERSION without re-proving HW encode (the versions are
Expand Down
30 changes: 26 additions & 4 deletions docs/trixie-package-resolution.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,32 @@ is still required before release — see "What this does not prove" at the end.

## Result

**68 names checked. 66 unchanged. 2 diverged.** No `t64` rename touched any name
in these lists — the transition renamed transitive dependencies only
(`libext2fs2t64`, `libcurl4t64`, `libglib2.0-0t64`, `libssl3t64` all appear in the
solved set, but none is named by us).
**68 names checked. 66 unchanged. 2 diverged.** At the time of that audit no `t64`
rename touched a name in the lists. A later real image build exposed one additional
compatibility boundary in the pinned third-party Rockchip package, described below.

### `libv4l-0` → `libv4l-0t64`: apt compatibility is not dpkg compatibility

Trixie installs the real library from `libv4l-0t64` at
`/usr/lib/aarch64-linux-gnu/libv4l2.so.0.0.0`. The package declares
`Provides: libv4l-0`, so apt can satisfy `rockchip-multimedia-config`'s old-name
dependency. Its postinst does not stop at dependency resolution, however:

```sh
libv4l_filename=$(dpkg -L libv4l-0 | grep libv4l2.so.0.0.0)
cp ${libv4l_filename} /usr/lib64/libv4l2.so
```

`dpkg` records the installed provider as `libv4l-0t64`, so the exact-name listing
fails. A fileless transitional package also fails because the grep needs a payload
path. The pipeline therefore builds an arm64 package named `libv4l-0` at fetch time.
It depends on `libv4l-0t64` and contains one file: the collision-free symlink
`/usr/share/libv4l-0-compat/libv4l2.so.0.0.0` pointing at the real t64 library.
Plain `cp` dereferences that link. The package is explicitly named first in the same
platform-layer `mkosi-install` transaction as `rockchip-multimedia-config`; it owns
neither `/usr/lib/aarch64-linux-gnu/libv4l2.so.0.0.0` nor the adjacent soname link.
This preserves the SHA-256-pinned third-party `.deb` unchanged while giving its
legacy maintainer script a real dpkg package record and readable path.

### The two divergences

Expand Down
77 changes: 77 additions & 0 deletions lib/fetch/userspace.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,71 @@
# Bodies moved VERBATIM from fetch-debs.sh; no behaviour change.
#
# shellcheck shell=bash

LIBV4L0_COMPAT_VERSION="1.30.1-1+ceralive1"
LIBV4L0_COMPAT_LINK="usr/share/libv4l-0-compat/libv4l2.so.0.0.0"
LIBV4L0_T64_LIBRARY="/usr/lib/aarch64-linux-gnu/libv4l2.so.0.0.0"

# build_libv4l0_compat_deb <debs-dir> — generate the real old-name package that
# rockchip-multimedia-config's pre-t64 postinst asks dpkg to list. Trixie's
# libv4l-0t64 Provides: libv4l-0 satisfies apt, but `dpkg -L libv4l-0` still has
# no package record. A fileless transitional package also fails because the
# postinst greps that listing for libv4l2.so.0.0.0 before copying it.
#
# The sole payload is therefore a collision-free symlink under /usr/share. Plain
# `cp` in the pinned postinst dereferences it to libv4l-0t64's real library; this
# package never owns either library path. The version sorts above 1.30.1-1 so it
# is not caught by libv4l-0t64's `Breaks: libv4l-0 (<< 1.30.1-1)` relation.
build_libv4l0_compat_deb() (
local debs="$1"
[[ "${ARCH}" == "arm64" ]] \
|| die "libv4l-0 compatibility package is arm64-only (got ARCH=${ARCH})"
if ! command -v ar >/dev/null 2>&1 || ! command -v tar >/dev/null 2>&1; then
die "building libv4l-0 compatibility package requires ar and tar"
fi

local epoch="${SOURCE_DATE_EPOCH:-0}"
[[ "${epoch}" =~ ^[0-9]+$ ]] \
|| die "SOURCE_DATE_EPOCH must be an integer (got '${epoch}')"
install -d -m 0755 "${debs}"

local work out tmp
work="$(mktemp -d "${debs}/.libv4l-0-compat.XXXXXX")"
trap 'rm -rf "${work}" "${tmp:-}"' EXIT
out="${debs}/libv4l-0_${LIBV4L0_COMPAT_VERSION}_arm64.deb"
tmp="${work}/libv4l-0.deb"

install -d -m 0755 "${work}/control" \
"${work}/data/$(dirname "${LIBV4L0_COMPAT_LINK}")"
cat >"${work}/control/control" <<EOF
Package: libv4l-0
Version: ${LIBV4L0_COMPAT_VERSION}
Architecture: arm64
Section: oldlibs
Priority: optional
Depends: libv4l-0t64
Maintainer: CeraLive <engineering@ceralive.tv>
Description: compatibility package for pre-t64 Rockchip multimedia configuration
This transitional package supplies the old dpkg package name and a collision-free
symlink to libv4l-0t64's libv4l2 library for legacy maintainer scripts.
EOF
ln -s "${LIBV4L0_T64_LIBRARY}" "${work}/data/${LIBV4L0_COMPAT_LINK}"
printf '2.0\n' >"${work}/debian-binary"

tar --sort=name --mtime="@${epoch}" --owner=0 --group=0 --numeric-owner \
-C "${work}/control" -czf "${work}/control.tar.gz" ./control
tar --sort=name --mtime="@${epoch}" --owner=0 --group=0 --numeric-owner \
-C "${work}/data" -czf "${work}/data.tar.gz" .
ar rcD "${tmp}" "${work}/debian-binary" "${work}/control.tar.gz" "${work}/data.tar.gz"
chmod 0644 "${tmp}"

if ! assert_deb_identity "${tmp}" libv4l-0 "${LIBV4L0_COMPAT_VERSION}" arm64; then
die "generated libv4l-0 compatibility package has invalid Debian control identity"
fi
mv -f "${tmp}" "${out}"
log_success "RK3588 userspace: built libv4l-0 compatibility package -> ${out}"
)

# rk3588_userspace_pkg_names — echo every pinned userspace package NAME (col 1)
# from the pin file. Empty (success) when the file is absent — a family that
# declares no RK3588 userspace package simply matches none of these.
Expand Down Expand Up @@ -128,6 +193,18 @@ fetch_rk3588_userspace() {
_run_bounded "${jobs}" _fetch_rk3588_userspace_one "${want[@]}" \
|| die "RK3588 userspace fetch failed: one or more pinned packages did not download/verify"

local needs_libv4l0_compat=0
for name in "${want[@]}"; do
[[ "${name}" == "rockchip-multimedia-config" ]] && needs_libv4l0_compat=1
done
if (( needs_libv4l0_compat )); then
if [[ -n "${DRY_RUN}" ]]; then
log_info "DRY-RUN would build: libv4l-0_${LIBV4L0_COMPAT_VERSION}_arm64.deb (Depends: libv4l-0t64; one compatibility symlink)"
else
build_libv4l0_compat_deb "${debs}"
fi
fi

if [[ -z "${DRY_RUN}" ]]; then
local pkg
local -a staged=()
Expand Down
2 changes: 1 addition & 1 deletion lib/stages/partition.sh
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ assert_staged_packages_unique() {
stage_partition() {
log_info "[3/9] partitioning staged .debs into BSP vs first-party by package name"
# The set of BSP package names (manifest-declared) is the partition key.
local bsp_names=" ${KERNEL_PACKAGES} ${DTB_PACKAGES} ${UBOOT_PACKAGES} ${FIRMWARE_PACKAGES} ${HW_ACCEL_GSTREAMER_PLUGINS:-} ${GSTREAMER_RUNTIME_PACKAGES:-} "
local bsp_names=" libv4l-0 ${KERNEL_PACKAGES} ${DTB_PACKAGES} ${UBOOT_PACKAGES} ${FIRMWARE_PACKAGES} ${HW_ACCEL_GSTREAMER_PLUGINS:-} ${GSTREAMER_RUNTIME_PACKAGES:-} "
# MUST stay a superset of fetch-debs.sh FIRST_PARTY_APT_PKGS: the 5 core packages
# + the 9-package ModemManager 1.24 closure and its Architecture: all support
# companion (modem-stack v1.3.0). The fetcher stages all 15 into debs/; a name missing here fails the build as
Expand Down
12 changes: 9 additions & 3 deletions mkosi/mkosi.images/platform/mkosi.postinst
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,12 @@ prune_irrelevant_rk3588_firmware() {
# Collect the always-on HW-accel GStreamer set (parity-relevant) from env.
read -r -a hw_gst <<<"${HW_ACCEL_GSTREAMER_PLUGINS:-}"
read -r -a gst_runtime <<<"${GSTREAMER_RUNTIME_PACKAGES:-}"
compat_pkgs=()
for pkg in "${gst_runtime[@]}"; do
if [[ "${pkg}" == "rockchip-multimedia-config" ]]; then
compat_pkgs+=(libv4l-0)
fi
done

# Boot BSP (kernel/DTB/U-Boot/firmware) — only when explicitly requested.
boot_bsp=()
Expand All @@ -390,7 +396,7 @@ if [[ "${INSTALL_BOOT_BSP}" == "1" ]]; then
boot_bsp+=("${_k[@]}" "${_d[@]}" "${_u[@]}" "${_f[@]}")
fi

bsp_pkgs=("${hw_gst[@]}" "${gst_runtime[@]}" "${boot_bsp[@]}")
bsp_pkgs=("${compat_pkgs[@]}" "${hw_gst[@]}" "${gst_runtime[@]}" "${boot_bsp[@]}")
if [[ ${#bsp_pkgs[@]} -eq 0 ]]; then
log "no BSP packages resolved from manifest — nothing to install."
exit 0
Expand All @@ -416,9 +422,9 @@ install_chroot_service_policy() {
}
install_chroot_service_policy

log "installing authenticated staged HW-accel GStreamer BSP: ${hw_gst[*]} ${gst_runtime[*]}"
log "installing authenticated staged HW-accel GStreamer BSP: ${compat_pkgs[*]} ${hw_gst[*]} ${gst_runtime[*]}"
if [[ ${#hw_gst[@]} -gt 0 || ${#gst_runtime[@]} -gt 0 ]]; then
mkosi-install -y --no-install-recommends "${hw_gst[@]}" "${gst_runtime[@]}"
mkosi-install -y --no-install-recommends "${compat_pkgs[@]}" "${hw_gst[@]}" "${gst_runtime[@]}"
fi

if [[ "${INSTALL_BOOT_BSP}" == "1" && ${#boot_bsp[@]} -gt 0 ]]; then
Expand Down
2 changes: 1 addition & 1 deletion tests/mkosi-image-contract.bats
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,7 @@ PY
[ -n "$policy_at" ] && [ -n "$apt_at" ] && [ "$policy_at" -lt "$apt_at" ]

policy_at="$(grep -n '^install_chroot_service_policy$' "$PIPELINE_DIR/mkosi/mkosi.images/platform/mkosi.postinst" | cut -d: -f1)"
apt_at="$(grep -n '^ mkosi-install -y --no-install-recommends "\${hw_gst\[@\]}"' "$PIPELINE_DIR/mkosi/mkosi.images/platform/mkosi.postinst" | cut -d: -f1)"
apt_at="$(grep -n '^ mkosi-install -y --no-install-recommends "\${compat_pkgs\[@\]}" "\${hw_gst\[@\]}"' "$PIPELINE_DIR/mkosi/mkosi.images/platform/mkosi.postinst" | cut -d: -f1)"
[ -n "$policy_at" ] && [ -n "$apt_at" ] && [ "$policy_at" -lt "$apt_at" ]
}

Expand Down
41 changes: 41 additions & 0 deletions tests/package-contract.bats
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,47 @@ PY
[ "$(sed -n 's/^v4l-utils[[:space:]]*//p' "$shared" | head -n 1 >/dev/null; grep -n '^v4l-utils[[:space:]]' "$shared" | cut -d: -f1)" -lt "$(grep -n '^libv4l-0[[:space:]]' "$shared" | cut -d: -f1)" ]
}

@test "RK3588 multimedia config: generated libv4l-0 compat package carries exactly the t64 library symlink" {
local debs="$BATS_TEST_TMPDIR/libv4l-compat-debs"
local root="$BATS_TEST_TMPDIR/libv4l-compat-root"
mkdir -p "$debs" "$root"

run env ARCH=arm64 SOURCE_DATE_EPOCH=1 bash -c \
'source "$1"; build_libv4l0_compat_deb "$2"' bash "$FETCH_DEBS" "$debs"
[ "$status" -eq 0 ]

local deb="$debs/libv4l-0_1.30.1-1+ceralive1_arm64.deb"
[ -f "$deb" ]
[ "$(bash -c 'source "$1"; deb_pkg_name "$2"' bash "$FETCH_DEBS" "$deb")" = "libv4l-0" ]
[ "$(bash -c 'source "$1"; deb_pkg_version "$2"' bash "$FETCH_DEBS" "$deb")" = "1.30.1-1+ceralive1" ]
[ "$(bash -c 'source "$1"; deb_pkg_arch "$2"' bash "$FETCH_DEBS" "$deb")" = "arm64" ]
[ "$(bash -c 'source "$1"; deb_control_field "$2" Depends' bash "$FETCH_DEBS" "$deb")" = "libv4l-0t64" ]

bash -c 'source "$1"; explode_deb "$2" "$3"' bash "$FETCH_DEBS" "$deb" "$root"
[ "$(find "$root" -type f | wc -l)" -eq 0 ]
[ "$(find "$root" -type l | wc -l)" -eq 1 ]
local link="$root/usr/share/libv4l-0-compat/libv4l2.so.0.0.0"
[ -L "$link" ]
[ "$(readlink "$link")" = "/usr/lib/aarch64-linux-gnu/libv4l2.so.0.0.0" ]

grep -Fq 'work="$(mktemp -d "${debs}/.libv4l-0-compat.XXXXXX")"' \
"$REPO_ROOT/lib/fetch/userspace.sh"
grep -Fq 'tmp="${work}/libv4l-0.deb"' \
"$REPO_ROOT/lib/fetch/userspace.sh"
run ! grep -Fq 'tmp="${out}.tmp"' "$REPO_ROOT/lib/fetch/userspace.sh"
}

@test "RK3588 multimedia config: libv4l-0 compat is staged and named first in the platform transaction" {
local userspace="$REPO_ROOT/lib/fetch/userspace.sh"
local partition="$REPO_ROOT/lib/stages/partition.sh"
local platform="$REPO_ROOT/mkosi/mkosi.images/platform/mkosi.postinst"

grep -Fq 'build_libv4l0_compat_deb "${debs}"' "$userspace"
grep -Eq 'bsp_names=.*libv4l-0' "$partition"
grep -Fq 'compat_pkgs+=(libv4l-0)' "$platform"
grep -Fq 'mkosi-install -y --no-install-recommends "${compat_pkgs[@]}" "${hw_gst[@]}" "${gst_runtime[@]}"' "$platform"
}

@test "wwan: the check asserts a .deb extractor (dpkg-deb or ar+tar) is available" {
# with a normal PATH the assertion passes (ar + tar are on the host)
run bash -c "source '$CHECK_WWAN'; wwan_assert_deb_tools"
Expand Down
Loading