diff options
| author | Anton <anton@picapica.im> | 2026-09-15 03:56:29 +0400 |
|---|---|---|
| committer | Anton <anton@picapica.im> | 2026-09-15 03:56:29 +0400 |
| commit | 30ee9355a97ea3939514b6fcb08ea7da5c6df8f7 (patch) | |
| tree | c4900c04f1fa2700f542eac81e5f4d0c67272e1b /deb_packaging | |
| parent | 4f9ebf0ebf61d95d9e3495b6596c954568e68c9d (diff) | |
| download | pica-pica-30ee9355a97ea3939514b6fcb08ea7da5c6df8f7.tar.gz pica-pica-30ee9355a97ea3939514b6fcb08ea7da5c6df8f7.zip | |
build scripts for building *.deb packages and repositories for supported distros
Diffstat (limited to 'deb_packaging')
| -rw-r--r-- | deb_packaging/README.md | 138 | ||||
| -rwxr-xr-x | deb_packaging/build-pica-packages.sh | 391 | ||||
| -rwxr-xr-x | deb_packaging/build-webrtc-audio-processing.sh | 342 | ||||
| -rwxr-xr-x | deb_packaging/container-build.sh | 136 |
4 files changed, 1007 insertions, 0 deletions
diff --git a/deb_packaging/README.md b/deb_packaging/README.md new file mode 100644 index 0000000..c87fd15 --- /dev/null +++ b/deb_packaging/README.md @@ -0,0 +1,138 @@ +# Debian and Ubuntu packaging + +Builds the pica-pica packages for every distribution we support, each in its +own podman container, and assembles the results into a signed apt repository. + +## Scripts + +| | | +|---|---| +| `build-pica-packages.sh` | The one to run. Installs podman, builds every target, assembles and signs the repository. | +| `build-webrtc-audio-processing.sh` | Backports Debian's webrtc-audio-processing 1.3 to a distribution that has no 1.x of its own. Usable on its own; the orchestrator calls it per container. | +| `container-build.sh` | The in-container worker. Not run by hand. | + +## Usage + +Make a source tarball the usual way, then hand it over: + +```sh +cd /path/to/pica-pica && ./configure && make dist +cd deb_packaging +./build-pica-packages.sh --gpg-key packaging@picapica.im \ + ../pica-pica-0.9.0dev.tar.gz +``` + +That leaves a repository in `repo/` and the raw `.deb` files in `out/`. + +Useful while iterating: + +```sh +./build-pica-packages.sh --only 'noble/*' --no-sign ../pica-pica-*.tar.gz +./build-pica-packages.sh --skip-build -k KEYID # re-sign what is in out/ +``` + +A `make dist` tarball is self-contained: it already carries the generated +`configure` and `Makefile.in`, so the containers have nothing to bootstrap. +They will fall back to `autoreconf -i -f` if handed a tarball made some other +way, and `autoconf`/`automake` are installed regardless — if the tarball's +timestamps leave `Makefile.am` looking newer than `Makefile.in`, automake's +own rebuild rules fire during the build and want them. + +Whatever the tarball is called and however it is compressed, it is bind +mounted into each container under one fixed name; `tar` works the compression +out from the content. + +## Build matrix + +| Distribution | Codename | amd64 | i386 | webrtc-audio-processing | +|---|---|---|---|---| +| Ubuntu 24.04 LTS | noble | yes | **no** | backported by us (archive has 0.3.1) | +| Ubuntu 26.04 LTS | resolute | yes | **no** | from the archive (1.3-3build2) | +| Debian 13 | trixie | yes | yes | from the archive (1.3-3) | + +### Why there is no Ubuntu i386 + +Ubuntu dropped i386 as an architecture after 18.04. What is left is a small +multiarch compatibility subset with no installable base system: there is no +i386 Ubuntu container image (`docker.io/i386/ubuntu` stops at `18.04`) and no +i386 Qt to build against. Debian still carries i386 as a full architecture, +including `qtbase5-dev`, so the 32 bit build is done there. + +## The webrtc-audio-processing backport + +pica-client needs AEC3, which arrived in webrtc-audio-processing 1.x. Ubuntu +24.04 ships 0.3.1, which predates it and has an entirely different API, so +noble needs a backport of Debian's 1.3-3. Nothing else does — the check is +made inside each container, and the script exits without doing anything where +the archive is already good enough. + +Debian's packaging is taken unmodified except for two things: + +1. **The `-dev` package is renamed** to `libwebrtc-audio-processing-1-dev`, + which is the name Debian itself used for 1.3-1. Debian renamed it to the + unversioned `libwebrtc-audio-processing-dev` in 1.3-2, after dropping 0.3 + from the archive — but noble still ships 0.3.1 under exactly that name, and + taking it over would replace a package other things build against. + + Nothing else collides. Upstream already versions everything that matters: + + | | 0.3.1 (noble) | 1.3 (ours) | + |---|---|---| + | headers | `/usr/include/webrtc_audio_processing/` | `/usr/include/webrtc-audio-processing-1/` | + | library | `libwebrtc_audio_processing.so.1` | `libwebrtc-audio-processing-1.so.3` | + | pkg-config | `webrtc-audio-processing` | `webrtc-audio-processing-1` | + | runtime deb | `libwebrtc-audio-processing1` | `libwebrtc-audio-processing-1-3` | + + So the four packages we produce — `libwebrtc-audio-processing-1-dev`, + `libwebrtc-audio-processing-1-3`, `libwebrtc-audio-coding-dev`, + `libwebrtc-audio-coding-1-3` — are all collision-free on noble. + +2. **Debian's rename-era `Breaks`/`Replaces` are dropped.** With the old name + back they say nothing, and the pair on `libwebrtc-audio-coding-dev` would + otherwise declare `Replaces: libwebrtc-audio-processing-dev (<< 1.3-2~)`, + which on noble matches the distribution's own 0.3 package — the exact thing + this is trying not to disturb. The unrelated relationship between the two + runtime packages is kept. + +The version is `1.3-3~noble1`. The `~` sorts it *below* plain `1.3-3`, so if +Ubuntu ever ships 1.3 itself, apt upgrades away from the backport rather than +being held back by it. + +## Signing + +The GPG key never enters a container. Containers produce `.deb` files; +the repository is assembled and signed on the host. Both `InRelease` +(what modern apt fetches) and `Release.gpg` are produced, and the public key +is exported to `repo/pica-pica-archive-keyring.asc`. + +Packages themselves are not signed with `debsigs` — apt does not check that; +repository trust comes from the signed `Release`. + +## Repository layout + +One pool per codename rather than a shared one. Indices are per suite, and a +shared pool would let noble's `Packages` list trixie's binaries — apt would +offer them and they would not install. + +``` +repo/ +├── dists/<codename>/ +│ ├── Release, Release.gpg, InRelease +│ └── main/binary-<arch>/{Packages,Packages.gz,Release} +├── pool/<codename>/main/<letter>/<source>/*.deb +└── pica-pica-archive-keyring.asc +``` + +## Client setup + +```sh +sudo install -d /usr/share/keyrings +sudo curl -fsSL https://YOUR.HOST/pica-pica-archive-keyring.asc \ + -o /usr/share/keyrings/pica-pica-archive-keyring.asc + +echo "deb [signed-by=/usr/share/keyrings/pica-pica-archive-keyring.asc] \ +https://YOUR.HOST $(lsb_release -cs) main" \ + | sudo tee /etc/apt/sources.list.d/pica-pica.list + +sudo apt update && sudo apt install pica-client +``` diff --git a/deb_packaging/build-pica-packages.sh b/deb_packaging/build-pica-packages.sh new file mode 100755 index 0000000..4371c2e --- /dev/null +++ b/deb_packaging/build-pica-packages.sh @@ -0,0 +1,391 @@ +#!/bin/bash +# +# Builds the pica-pica Debian packages for every distribution we support, each +# in its own podman container, and assembles the results into a signed apt +# repository. +# +# Takes a source tarball as produced by "make dist" - a self contained +# autotools distribution, which already carries the generated configure, so +# the containers have nothing to bootstrap. +# +# Meant to be run on an Ubuntu 24.04 host, but the only thing that is host +# specific is installing podman; give it podman by hand and it will run +# anywhere. +# +# The build matrix is at the top of the script. Note what is NOT in it: +# Ubuntu i386. Ubuntu has had no i386 architecture since 19.10 - what remains +# is a small multiarch compatibility subset with no installable base system, +# so there is no i386 Ubuntu container image to build in (docker.io/i386/ubuntu +# stops at 18.04) and no i386 Qt to build against. Debian still has i386 as a +# full architecture, so the 32 bit build happens there. +# +# The GPG key never enters a container. Containers produce .deb files; the +# repository is assembled and signed here on the host. +# +# (c) Copyright 2012 - 2026 Anton Sviridenko +# https://picapica.im +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. + +set -euo pipefail + +# codename | container image | dpkg arch | podman platform +TARGETS=( + "noble|docker.io/library/ubuntu:24.04|amd64|linux/amd64" + "resolute|docker.io/library/ubuntu:26.04|amd64|linux/amd64" + "trixie|docker.io/library/debian:trixie|amd64|linux/amd64" + "trixie|docker.io/i386/debian:trixie|i386|linux/386" +) + +SCRIPTDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +TARBALL="" +OUTDIR="$SCRIPTDIR/out" +REPODIR="$SCRIPTDIR/repo" +GPGKEY="" +SIGN=1 +ONLY="" +SKIP_BUILD=0 + +ORIGIN="Pica Pica" +LABEL="Pica Pica" + +progname=$(basename "$0") + +usage() +{ + cat <<EOF +Usage: $progname [OPTIONS] TARBALL + +Build the pica-pica packages in containers and assemble a signed apt +repository from the results. + +TARBALL is a source tarball as produced by "make dist" in the pica-pica tree, +e.g. pica-pica-0.9.0dev.tar.gz. Not needed with --skip-build. + +Options: + -k, --gpg-key KEYID Key to sign the repository's Release files with. Any + form gpg accepts - key id, fingerprint, or the email + address on the uid. Required unless --no-sign. + --no-sign Build the repository but leave it unsigned. apt will + refuse it without extra configuration; for testing. + -o, --output DIR Where the containers leave their .deb files. + Default: $OUTDIR + -r, --repo DIR Where to assemble the apt repository. + Default: $REPODIR + --only PATTERN Only build targets whose "codename/arch" matches this + shell pattern, e.g. --only 'noble/*' or --only '*/i386'. + --skip-build Do not build anything; just assemble and sign the + repository from whatever is already in the output + directory. + -h, --help This message. + +Targets: +EOF + + local t cn img arch plat + for t in "${TARGETS[@]}"; do + IFS='|' read -r cn img arch plat <<<"$t" + printf ' %-16s %-34s %s\n' "$cn/$arch" "$img" "$plat" + done + + cat <<'EOF' + +There is no Ubuntu i386 target and there cannot be: Ubuntu dropped i386 as an +architecture after 18.04, so there is no i386 Ubuntu base image and no i386 Qt +to build against. The 32 bit build is done on Debian, which still carries i386 +as a full architecture. +EOF +} + +die() +{ + echo "$progname: error: $*" >&2 + exit 1 +} + +note() +{ + echo + echo "=== $*" +} + +while [ $# -gt 0 ]; do + case "$1" in + -k|--gpg-key) GPGKEY="${2:?--gpg-key needs a value}"; shift 2 ;; + --no-sign) SIGN=0; shift ;; + -o|--output) OUTDIR="${2:?--output needs a value}"; shift 2 ;; + -r|--repo) REPODIR="${2:?--repo needs a value}"; shift 2 ;; + --only) ONLY="${2:?--only needs a value}"; shift 2 ;; + --skip-build) SKIP_BUILD=1; shift ;; + -h|--help) usage; exit 0 ;; + -*) die "unknown option $1 (try --help)" ;; + *) break ;; + esac +done + +if [ $# -gt 1 ]; then + die "expected at most one tarball, got: $*" +elif [ $# -eq 1 ]; then + TARBALL=$(readlink -f "$1") +fi + +if [ "$SIGN" -eq 1 ] && [ -z "$GPGKEY" ]; then + die "no signing key given. Pass --gpg-key KEYID, or --no-sign to skip signing" +fi + +if [ "$SKIP_BUILD" -eq 0 ]; then + [ -n "$TARBALL" ] || + die "no source tarball given. Run \"make dist\" in the pica-pica tree and pass the result (try --help)" + [ -f "$TARBALL" ] || + die "$TARBALL does not exist" + tar -tf "$TARBALL" >/dev/null 2>&1 || + die "$TARBALL is not a readable tar archive" +fi + +if [ "$(id -u)" -eq 0 ]; then + SUDO="" +else + SUDO="sudo" + command -v sudo >/dev/null || + die "not root and no sudo - install podman yourself and re-run" +fi + +# --- podman --------------------------------------------------------------- + +if ! command -v podman >/dev/null; then + note "installing podman" + $SUDO apt-get update + $SUDO apt-get install -y podman +else + note "podman is already installed: $(podman --version)" +fi + +# apt-ftparchive assembles the repository indices; gpg signs them. +for tool in apt-ftparchive gpg; do + command -v "$tool" >/dev/null && continue + note "installing $tool" + case "$tool" in + apt-ftparchive) $SUDO apt-get install -y apt-utils ;; + gpg) $SUDO apt-get install -y gnupg ;; + esac +done + +if [ "$SIGN" -eq 1 ]; then + gpg --list-keys "$GPGKEY" >/dev/null 2>&1 || + die "gpg does not know a key called '$GPGKEY'" + gpg --list-secret-keys "$GPGKEY" >/dev/null 2>&1 || + die "no SECRET key for '$GPGKEY' - the repository cannot be signed with it" +fi + +# --- work area ------------------------------------------------------------ + +WORKDIR=$(mktemp -d /tmp/pica-packaging.XXXXXX) +trap 'rm -rf "$WORKDIR"' EXIT + +if [ "$SKIP_BUILD" -eq 0 ]; then + note "source tarball" + echo " $TARBALL" + echo " $(du -h "$TARBALL" | cut -f1), top level: $(tar -tf "$TARBALL" | head -1)" +fi + +# --- build ---------------------------------------------------------------- + +mkdir -p "$OUTDIR" + +BUILT=() +FAILED=() + +if [ "$SKIP_BUILD" -eq 0 ]; then + for target in "${TARGETS[@]}"; do + IFS='|' read -r codename image arch platform <<<"$target" + + if [ -n "$ONLY" ]; then + # shellcheck disable=SC2254 + case "$codename/$arch" in + $ONLY) ;; + *) echo " skipping $codename/$arch (does not match --only '$ONLY')"; continue ;; + esac + fi + + note "$codename/$arch ($image)" + + if ! podman pull --platform "$platform" "$image"; then + FAILED+=("$codename/$arch: could not pull $image") + continue + fi + + # --userns=keep-id is deliberately not used: the build wants to be + # root inside the container to install packages, and in rootless + # podman that still maps back to this user on the way out. + # The tarball is bind mounted as a single file under a fixed name, so + # container-build.sh does not have to be told what it is called. tar + # works out the compression from the content, so the missing .gz or + # .xz on that name costs nothing. + if podman run --rm \ + --platform "$platform" \ + -v "$TARBALL:/src/pica-source.tar:ro" \ + -v "$SCRIPTDIR:/scripts:ro" \ + -v "$OUTDIR:/out" \ + -e PICA_DISTRO="$codename" \ + -e PICA_ARCH="$arch" \ + -w / \ + "$image" \ + /bin/bash /scripts/container-build.sh + then + BUILT+=("$codename/$arch") + else + FAILED+=("$codename/$arch: build failed") + fi + done + + note "build summary" + for b in "${BUILT[@]:-}"; do [ -n "$b" ] && echo " ok $b"; done + for f in "${FAILED[@]:-}"; do [ -n "$f" ] && echo " FAILED $f"; done + + [ ${#BUILT[@]} -gt 0 ] || die "nothing built - not assembling a repository" +fi + +# --- repository ----------------------------------------------------------- +# +# Laid out with a pool per codename rather than one shared pool. The indices +# are per suite, and a shared pool would let noble's Packages list trixie's +# binaries - apt would happily offer them and they would not install. + +note "assembling the repository in $REPODIR" + +rm -rf "$REPODIR" +mkdir -p "$REPODIR" + +CODENAMES=() + +for distdir in "$OUTDIR"/*/; do + [ -d "$distdir" ] || continue + codename=$(basename "$distdir") + + arches=() + + for archdir in "$distdir"*/; do + [ -d "$archdir" ] || continue + arch=$(basename "$archdir") + + compgen -G "$archdir*.deb" >/dev/null || continue + arches+=("$arch") + + for deb in "$archdir"*.deb; do + # Pool path by source package, the way Debian lays it out, so + # that the binaries of one source sit together. + src=$(dpkg-deb -f "$deb" Source 2>/dev/null | awk '{print $1}') + [ -n "$src" ] || src=$(dpkg-deb -f "$deb" Package) + case "$src" in + lib*) letter="${src:0:4}" ;; + *) letter="${src:0:1}" ;; + esac + + pool="$REPODIR/pool/$codename/main/$letter/$src" + mkdir -p "$pool" + cp "$deb" "$pool/" + done + done + + [ ${#arches[@]} -gt 0 ] || continue + CODENAMES+=("$codename") + + # Deduplicate: a codename built for two architectures appears twice above. + mapfile -t arches < <(printf '%s\n' "${arches[@]}" | sort -u) + + echo " $codename: ${arches[*]}" + + for arch in "${arches[@]}"; do + bindir="$REPODIR/dists/$codename/main/binary-$arch" + mkdir -p "$bindir" + + # Run from the repository root so that the Filename fields come out + # relative to it, which is what apt expects. + ( cd "$REPODIR" && + apt-ftparchive --arch "$arch" packages "pool/$codename" \ + > "dists/$codename/main/binary-$arch/Packages" ) + + gzip -9 -k -f "$bindir/Packages" + + cat > "$bindir/Release" <<EOF +Archive: $codename +Suite: $codename +Component: main +Origin: $ORIGIN +Label: $LABEL +Architecture: $arch +EOF + done + + # The suite Release, carrying the checksums of everything above. Written + # to a temporary file first - apt-ftparchive walks the directory it is + # indexing, and would otherwise hash a half-written Release. + ( cd "$REPODIR" && + apt-ftparchive \ + -o APT::FTPArchive::Release::Origin="$ORIGIN" \ + -o APT::FTPArchive::Release::Label="$LABEL" \ + -o APT::FTPArchive::Release::Suite="$codename" \ + -o APT::FTPArchive::Release::Codename="$codename" \ + -o APT::FTPArchive::Release::Components="main" \ + -o APT::FTPArchive::Release::Architectures="${arches[*]}" \ + -o APT::FTPArchive::Release::Description="Pica Pica Messenger packages" \ + release "dists/$codename" > "$WORKDIR/Release.$codename" ) + + mv "$WORKDIR/Release.$codename" "$REPODIR/dists/$codename/Release" + + if [ "$SIGN" -eq 1 ]; then + echo " signing $codename with $GPGKEY" + rm -f "$REPODIR/dists/$codename/Release.gpg" \ + "$REPODIR/dists/$codename/InRelease" + + # Both forms: InRelease is what modern apt fetches, Release.gpg is + # the detached signature older clients look for. + gpg --batch --yes --default-key "$GPGKEY" \ + --armor --detach-sign \ + -o "$REPODIR/dists/$codename/Release.gpg" \ + "$REPODIR/dists/$codename/Release" + + gpg --batch --yes --default-key "$GPGKEY" \ + --clearsign \ + -o "$REPODIR/dists/$codename/InRelease" \ + "$REPODIR/dists/$codename/Release" + fi +done + +[ ${#CODENAMES[@]} -gt 0 ] || die "no packages found under $OUTDIR" + +if [ "$SIGN" -eq 1 ]; then + gpg --armor --export "$GPGKEY" > "$REPODIR/pica-pica-archive-keyring.asc" +fi + +# --- how to use it -------------------------------------------------------- + +note "done" +cat <<EOF + +Repository: $REPODIR +Suites: ${CODENAMES[*]} + +Serve that directory over HTTP, then on a client: + + sudo install -d /usr/share/keyrings + sudo curl -fsSL https://YOUR.HOST/pica-pica-archive-keyring.asc \\ + -o /usr/share/keyrings/pica-pica-archive-keyring.asc + + echo "deb [signed-by=/usr/share/keyrings/pica-pica-archive-keyring.asc] \\ +https://YOUR.HOST \$(lsb_release -cs) main" \\ + | sudo tee /etc/apt/sources.list.d/pica-pica.list + + sudo apt update && sudo apt install pica-client +EOF + +if [ "$SIGN" -eq 0 ]; then + cat <<'EOF' + +The repository is NOT signed. apt will reject it unless clients add +[trusted=yes] to the sources.list entry, which disables authentication - +do that for local testing only. +EOF +fi diff --git a/deb_packaging/build-webrtc-audio-processing.sh b/deb_packaging/build-webrtc-audio-processing.sh new file mode 100755 index 0000000..6108e04 --- /dev/null +++ b/deb_packaging/build-webrtc-audio-processing.sh @@ -0,0 +1,342 @@ +#!/bin/bash +# +# Build a backported webrtc-audio-processing 1.3 for a distribution whose +# archive does not carry it. +# +# pica-client needs AEC3, which arrived in the 1.x series. Ubuntu 24.04 still +# ships 0.3.1, which predates it entirely and has a different API. Debian 13 +# and Ubuntu 25.10 or newer already have 1.3, so on those this script does +# nothing unless forced. +# +# Debian's own packaging is taken as-is and given a two-part delta: +# +# - The -dev binary package is renamed back to the name Debian itself used +# for 1.3-1, libwebrtc-audio-processing-1-dev. Debian renamed it to the +# unversioned libwebrtc-audio-processing-dev in 1.3-2, after dropping 0.3 +# from the archive - but on a distribution that still ships 0.3 under +# that unversioned name, taking it over would replace a package other +# things build against. Nothing else collides: the runtime package +# (libwebrtc-audio-processing-1-3), the SONAME, the include directory and +# the pkg-config name are all versioned by upstream already. +# +# - The Breaks/Replaces relationships that record Debian's own rename are +# dropped. They are meaningless once the package carries the old name +# again, and on the audio-coding package they would otherwise declare a +# Replaces on the distribution's 0.3 -dev package, which is the exact +# thing this is trying not to disturb. +# +# Run this inside the target distribution - a container from +# build-pica-packages.sh, an sbuild chroot, or a machine running it. It uses +# apt, so it has to be somewhere that apt describes the target archive. +# +# (c) Copyright 2012 - 2026 Anton Sviridenko +# https://picapica.im +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. + +set -euo pipefail + +UPSTREAM_VERSION=1.3 +DEBIAN_REVISION=3 +DEBIAN_MIRROR=https://deb.debian.org/debian +POOL="$DEBIAN_MIRROR/pool/main/w/webrtc-audio-processing" + +# The version below which the distribution's own package is no good to us. +# 0.3 has no AEC3; see the note at the top. +MINIMUM_USABLE_VERSION=1.3 + +ARCH="" +OUTDIR="$PWD/out" +FORCE=0 +RENAME_DEV=1 +KEEP_BUILD=0 + +progname=$(basename "$0") + +usage() +{ + cat <<EOF +Usage: $progname [OPTIONS] DISTRO + +Build webrtc-audio-processing $UPSTREAM_VERSION-$DEBIAN_REVISION as a backport for DISTRO. + +DISTRO is the target distribution codename - noble, resolute, trixie. It is +used in the package version (${UPSTREAM_VERSION}-${DEBIAN_REVISION}~DISTRO1) and the changelog entry, so that +the distribution's own package supersedes this one if it ever gains a newer +version. + +Options: + -a, --arch ARCH Architecture to build for. Accepts either dpkg names + (amd64, arm64, i386) or uname names (x86_64, aarch64, + i686), which are normalised. Defaults to the + architecture this is running on. Anything else is a + cross build and needs crossbuild-essential-ARCH, which + this will install. + -o, --output DIR Where to put the resulting .deb files. + Default: $OUTDIR + -f, --force Build even when the distribution already has a good + enough webrtc-audio-processing. + --no-rename-dev Keep Debian's unversioned libwebrtc-audio-processing-dev + name. Only correct where the distribution has no 0.3 of + its own to collide with. + --keep-build Leave the build tree behind for inspection. + -h, --help This message. + +Exit status: + 0 packages built, or the distribution already has a good enough one + 1 something went wrong +EOF +} + +die() +{ + echo "$progname: error: $*" >&2 + exit 1 +} + +note() +{ + echo "==> $*" +} + +# dpkg and uname disagree about architecture names, and people reach for +# whichever they saw last. Take both. +normalise_arch() +{ + case "$1" in + x86_64|amd64) echo amd64 ;; + aarch64|arm64) echo arm64 ;; + i386|i486|i586|i686) echo i386 ;; + armv7l|armhf) echo armhf ;; + ppc64le|ppc64el) echo ppc64el ;; + *) echo "$1" ;; + esac +} + +while [ $# -gt 0 ]; do + case "$1" in + -a|--arch) ARCH=$(normalise_arch "${2:?--arch needs a value}"); shift 2 ;; + -o|--output) OUTDIR="${2:?--output needs a value}"; shift 2 ;; + -f|--force) FORCE=1; shift ;; + --no-rename-dev) RENAME_DEV=0; shift ;; + --keep-build) KEEP_BUILD=1; shift ;; + -h|--help) usage; exit 0 ;; + -*) die "unknown option $1 (try --help)" ;; + *) break ;; + esac +done + +[ $# -eq 1 ] || { usage >&2; exit 1; } +DISTRO="$1" + +[ "$(id -u)" -eq 0 ] || die "this installs build dependencies, so it needs to run as root" + +command -v apt-get >/dev/null || die "no apt-get - this has to run inside the target distribution" + +HOST_ARCH=$(dpkg --print-architecture) +BUILD_ARCH="${ARCH:-$HOST_ARCH}" +CROSS=0 +[ "$BUILD_ARCH" = "$HOST_ARCH" ] || CROSS=1 + +VERSION="${UPSTREAM_VERSION}-${DEBIAN_REVISION}~${DISTRO}1" + +export DEBIAN_FRONTEND=noninteractive + +# Is it needed at all? +# +# apt-cache policy reports the candidate the target archive would install. +# Anything from 1.x on is fine and there is no reason to ship our own. +if [ "$FORCE" -eq 0 ]; then + apt-get update -qq + + candidate=$(apt-cache policy libwebrtc-audio-processing-dev 2>/dev/null | + sed -n 's/^ Candidate: //p') + + if [ -n "$candidate" ] && [ "$candidate" != "(none)" ] && + dpkg --compare-versions "$candidate" ge "$MINIMUM_USABLE_VERSION"; then + note "$DISTRO already has libwebrtc-audio-processing-dev $candidate - nothing to build" + note "(pass --force to build the backport anyway)" + exit 0 + fi + + if [ -n "$candidate" ] && [ "$candidate" != "(none)" ]; then + note "$DISTRO has libwebrtc-audio-processing-dev $candidate, which predates AEC3 - backporting $VERSION" + else + note "$DISTRO has no libwebrtc-audio-processing-dev at all - backporting $VERSION" + fi +fi + +note "building webrtc-audio-processing $VERSION for $BUILD_ARCH" + +# Just enough to fetch and unpack a Debian source package. The build +# dependencies proper come from the extracted debian/control further down, +# via apt-get build-dep, so that this does not have to be kept in step with +# Debian's packaging by hand. +# +# devscripts is deliberately not installed: the changelog entry below is +# written directly, so this works without DEBEMAIL or DEBFULLNAME being set. +note "installing the tools needed to fetch the source" +apt-get install -qq -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + dpkg-dev \ + xz-utils + +if [ "$CROSS" -eq 1 ]; then + note "cross building for $BUILD_ARCH from $HOST_ARCH" + dpkg --add-architecture "$BUILD_ARCH" + apt-get update -qq + apt-get install -qq -y --no-install-recommends \ + "crossbuild-essential-$BUILD_ARCH" +fi + +BUILDROOT=$(mktemp -d /tmp/webrtc-backport.XXXXXX) +cleanup() +{ + [ "$KEEP_BUILD" -eq 1 ] && { note "build tree left at $BUILDROOT"; return; } + rm -rf "$BUILDROOT" +} +trap cleanup EXIT + +cd "$BUILDROOT" + +DSC="webrtc-audio-processing_${UPSTREAM_VERSION}-${DEBIAN_REVISION}.dsc" + +note "fetching Debian's source package" +for f in "$DSC" \ + "webrtc-audio-processing_${UPSTREAM_VERSION}.orig.tar.gz" \ + "webrtc-audio-processing_${UPSTREAM_VERSION}-${DEBIAN_REVISION}.debian.tar.xz"; do + curl -fsSL --retry 3 -o "$f" "$POOL/$f" || die "could not fetch $f from $POOL" +done + +# dpkg-source checks the tarballs against the checksums in the .dsc as it +# extracts, so a corrupted or substituted tarball fails here. It does not +# check the .dsc's own OpenPGP signature; dscverify from devscripts does that +# and needs the Debian keyring, so it is done only when both are present. +if command -v dscverify >/dev/null && [ -d /usr/share/keyrings ]; then + if dscverify --keyring /usr/share/keyrings/debian-keyring.gpg "$DSC" >/dev/null 2>&1; then + note "the .dsc signature verifies against the Debian keyring" + else + note "WARNING: could not verify the .dsc signature (keyring missing or untrusted signer)" + fi +fi + +note "extracting" +dpkg-source -x "$DSC" src >/dev/null +cd src + +# Straight out of the extracted debian/control, so Debian's own list is the +# source of truth rather than a copy of it kept here. +note "installing Debian's build dependencies for it" +if [ "$CROSS" -eq 1 ]; then + apt-get build-dep -qq -y -a "$BUILD_ARCH" ./ || + die "could not satisfy the build dependencies for $BUILD_ARCH" +else + apt-get build-dep -qq -y ./ || + die "could not satisfy the build dependencies from debian/control" +fi + +# --- the delta ------------------------------------------------------------ + +if [ "$RENAME_DEV" -eq 1 ]; then + note "renaming libwebrtc-audio-processing-dev to libwebrtc-audio-processing-1-dev" + + # Only the -dev package is renamed. The runtime package keeps Debian's + # name (libwebrtc-audio-processing-1-3), which does not collide with + # 0.3's libwebrtc-audio-processing1 and is what pica-client will depend + # on through shlibs. + sed -i 's/^Package: libwebrtc-audio-processing-dev$/Package: libwebrtc-audio-processing-1-dev/' \ + debian/control + + grep -q '^Package: libwebrtc-audio-processing-1-dev$' debian/control || + die "the -dev package rename did not apply - has Debian's control changed?" +fi + +# Drop the Breaks/Replaces that record Debian's 1.3-1 -> 1.3-2 rename. +# +# With the -dev package carrying its old name again these say nothing useful, +# and the pair on libwebrtc-audio-coding-dev is actively wrong here: it +# declares Breaks/Replaces on libwebrtc-audio-processing-dev (<< 1.3-2~), +# which on a distribution still shipping 0.3 under that name matches the +# distribution's own package. +note "dropping Debian's rename-era Breaks/Replaces" +# perl rather than python3: a minimal Debian or Ubuntu container has no +# python3, but debhelper depends on perl so it is always here. +# +# A deb822 field runs from its name to the next line starting in column zero, +# continuation lines being those that start with whitespace - hence the +# (?:\n[ \t][^\n]*)* . Only the relationships naming the -dev packages are +# dropped; anything else Debian declares is left alone. +perl -0777 -i -pe ' + s{^((?:Breaks|Replaces):[^\n]*(?:\n[ \t][^\n]*)*\n)}{ + $1 =~ /libwebrtc-audio-(?:processing|coding)(?:-1)?-dev/ ? q{} : $1 + }gme; +' debian/control + +# A changelog entry written directly rather than through dch, so that this +# does not depend on DEBEMAIL/DEBFULLNAME being set in the container. +# +# The ~DISTRO1 suffix sorts BELOW the plain Debian version, so if the +# distribution ever ships 1.3-3 itself, apt upgrades away from this backport +# rather than being held back by it. +note "adding the changelog entry for $VERSION" +{ + cat <<EOF +webrtc-audio-processing ($VERSION) $DISTRO; urgency=medium + + * Rebuild of Debian's ${UPSTREAM_VERSION}-${DEBIAN_REVISION} for $DISTRO, which has no + webrtc-audio-processing 1.x of its own. pica-client needs it for AEC3. +EOF + + if [ "$RENAME_DEV" -eq 1 ]; then + cat <<'EOF' + * Keep the versioned libwebrtc-audio-processing-1-dev name that Debian used + for 1.3-1, so that this coexists with the 0.3 that the distribution ships + as libwebrtc-audio-processing-dev instead of replacing it + * Drop the Breaks/Replaces recording Debian's rename of that package; with + the old name back they say nothing, and on libwebrtc-audio-coding-dev the + pair would match the distribution's own 0.3 -dev package +EOF + fi + + cat <<EOF + + -- Pica Pica packaging <anton@picapica.im> $(date -R) + +EOF + cat debian/changelog +} > debian/changelog.new +mv debian/changelog.new debian/changelog + +note "the delta applied to Debian's packaging:" +sed -n '1,20p' debian/changelog | sed 's/^/ /' + +# --- build ---------------------------------------------------------------- + +note "building" +if [ "$CROSS" -eq 1 ]; then + dpkg-buildpackage --host-arch "$BUILD_ARCH" -b -us -uc +else + dpkg-buildpackage -b -us -uc +fi + +cd "$BUILDROOT" + +mkdir -p "$OUTDIR" +found=0 +for deb in *.deb; do + [ -e "$deb" ] || continue + cp -v "$deb" "$OUTDIR/" + found=1 +done +for changes in *.changes; do + [ -e "$changes" ] || continue + cp "$changes" "$OUTDIR/" +done + +[ "$found" -eq 1 ] || die "the build produced no .deb files" + +note "done - packages are in $OUTDIR" diff --git a/deb_packaging/container-build.sh b/deb_packaging/container-build.sh new file mode 100755 index 0000000..f8ef801 --- /dev/null +++ b/deb_packaging/container-build.sh @@ -0,0 +1,136 @@ +#!/bin/bash +# +# Builds the pica-pica packages inside one container. Not meant to be run by +# hand - build-pica-packages.sh starts a container per target and runs this +# as its entry point. +# +# What it expects to find, all put there by the orchestrator: +# +# /src/pica-source.tar the pica-pica source tarball, as produced by +# "make dist" - bind mounted under that fixed name +# whatever it is really called, and whatever it is +# compressed with +# /scripts/ this directory, so that the backport script is +# reachable +# /out/ writable; artifacts are left in +# /out/<codename>/<arch>/ +# +# and in the environment: PICA_DISTRO (codename) and PICA_ARCH (dpkg name). +# +# (c) Copyright 2012 - 2026 Anton Sviridenko +# https://picapica.im +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, version 3. + +set -euo pipefail + +DISTRO="${PICA_DISTRO:?PICA_DISTRO is not set}" +ARCH="${PICA_ARCH:-$(dpkg --print-architecture)}" + +export DEBIAN_FRONTEND=noninteractive + +note() +{ + echo "==> [$DISTRO/$ARCH] $*" +} + +die() +{ + echo "[$DISTRO/$ARCH] error: $*" >&2 + exit 1 +} + +note "starting, $(. /etc/os-release && echo "$PRETTY_NAME"), dpkg arch $(dpkg --print-architecture)" + +note "refreshing the package lists" +apt-get update -qq + +# autoconf/automake because the pica-pica tree does not carry a generated +# configure - it is not in git, so the tree has to be bootstrapped here. +note "installing the base build tooling" +apt-get install -qq -y --no-install-recommends \ + build-essential \ + ca-certificates \ + curl \ + dpkg-dev \ + debhelper \ + autoconf \ + automake \ + pkgconf + +# webrtc-audio-processing, if this distribution has nothing new enough. +# The script decides that for itself and exits without doing anything when +# the archive already has 1.x, so it is safe to call unconditionally. +note "checking whether webrtc-audio-processing needs backporting" +WEBRTC_OUT=/tmp/webrtc-backport-out +mkdir -p "$WEBRTC_OUT" + +bash /scripts/build-webrtc-audio-processing.sh --output "$WEBRTC_OUT" "$DISTRO" + +if compgen -G "$WEBRTC_OUT/*.deb" >/dev/null; then + note "installing the backported webrtc-audio-processing" + apt-get install -qq -y --no-install-recommends "$WEBRTC_OUT"/*.deb + + # These go into the repository too - without them the pica-client + # package would be uninstallable on this distribution. + mkdir -p "/out/$DISTRO/$ARCH" + cp -v "$WEBRTC_OUT"/*.deb "/out/$DISTRO/$ARCH/" +else + note "the archive's own webrtc-audio-processing is good enough" +fi + +# --- pica-pica ------------------------------------------------------------ + +note "unpacking the source" +rm -rf /build +mkdir -p /build +tar -xf /src/pica-source.tar -C /build + +# "make dist" produces exactly one top level directory, pica-pica-<version>. +SRCDIR=$(find /build -maxdepth 1 -mindepth 1 -type d | head -1) +[ -n "$SRCDIR" ] || die "the source tarball did not contain a directory" +cd "$SRCDIR" +note "source is $(basename "$SRCDIR")" + +[ -f debian/control ] || + die "no debian/control in the tarball - was it made by \"make dist\" in a tree that has debian/?" + +note "installing pica-pica's build dependencies" +# Reading them straight out of debian/control means the alternative +# "libwebrtc-audio-processing-dev (>= 1.3) | libwebrtc-audio-processing-1-dev" +# is resolved by apt rather than second-guessed here. +apt-get build-dep -qq -y ./ || + die "could not satisfy the build dependencies from debian/control" + +# A "make dist" tarball already carries the generated configure, so there is +# normally nothing to do here. The fallback is for a tarball made some other +# way; autoconf and automake are installed above either way, because if the +# tarball's timestamps make Makefile.am look newer than Makefile.in, automake's +# own rebuild rules fire during the build and want them. +if [ -x configure ]; then + note "the tarball carries a generated configure - not bootstrapping" +else + note "no configure in the tarball - bootstrapping the build system" + autoreconf -i -f +fi + +note "building the packages" +# Binary-only: dpkg-source is not involved, so this does not need an .orig +# tarball alongside a 3.0 (quilt) source format. +dpkg-buildpackage -b -us -uc + +note "collecting artifacts" +mkdir -p "/out/$DISTRO/$ARCH" +cd /build +found=0 +for f in *.deb *.changes *.buildinfo; do + [ -e "$f" ] || continue + cp -v "$f" "/out/$DISTRO/$ARCH/" + case "$f" in *.deb) found=1 ;; esac +done + +[ "$found" -eq 1 ] || die "the build produced no .deb files" + +note "done" |
