#!/bin/sh -e # checkapk - find ABI breakages in package upgrades # Copyright (c) 2012 Natanael Copa # # Distributed under GPL-2 # program_version=@VERSION@ datadir=@datadir@ if ! [ -f "$datadir/functions.sh" ]; then echo "$datadir/functions.sh: not found" >&2 exit 1 fi . "$datadir/functions.sh" case "$(tar --version)" in *GNU*) tar_flavor=GNU;; *libarchive*) tar_flavor=libarchive;; *busybox*) tar_flavor=busybox;; *) die 'unknown tar flavor';; esac usage() { cat >&2 <<-__EOF__ $program $program_version - find ABI breakages in package upgrades Usage: $program Run in the directory of a built package. __EOF__ } pkginfo() { local apk="$1" local filename="$2" tar -zxf "$apk" .PKGINFO mv .PKGINFO .PKGINFO.orig grep -E \ '^(depend|provides|provider|replaces|triggers|install_if)' \ .PKGINFO.orig | sort > "$filename" touch -r .PKGINFO.orig "$filename" rm .PKGINFO.orig } filelist() { local apk="$1" local filename="$2" local tar_fmt case "$tar_flavor" in GNU) # -rw-r--r-- root/root 737 2019-06-14 20:45 .PKGINFO tar_fmt='{ print $1, $2, $NF }';; libarchive) # -rw-r--r-- 0 root root 737 Jun 14 15:45 .PKGINFO tar_fmt='{ printf "%s %s/%s %s\n", $1, $3, $4, $NF }';; busybox) # -rw-r--r-- root/root 737 2019-06-14 20:45:29 .PKGINFO tar_fmt='{ print $1, $2, $NF }';; esac tar -ztvf "$apk" | grep -ve ' \.SIGN\.' \ | awk "$tar_fmt" \ | sort -k 3,3 > "$filename" } check_soname() { local soname sover_old basename soname_new sover_new local rdeps real_rdeps i self j soname="$1" sover_old="${soname#*=}" soname="${soname%=*}" basename="${soname%%.so*}.so" soname_new="$(grep -F "provides = $basename" \ "pkginfo-$_pkgname-new" | cut -d ' ' -f 3)" sover_new="${soname_new#*=}" soname_new="${soname_new%=*}" if [ -z "$soname_new" ]; then warning "SONAME moved or deleted: $soname" elif [ "$soname" != "$soname_new" ]; then warning "SONAME changed: $soname=$sover_old" warning2 "-> $soname_new=$sover_new" elif [ "$sover_old" != "$sover_new" ]; then warning "SOVER changed: $soname=$sover_old" warning2 "-> $soname_new=$sover_new" fi rdeps="$(apk search --repositories-file "$tmpdir/repositories" \ --rdepends --quiet "$soname" | sort -u)" real_rdeps= for i in $rdeps; do self=0 for j in $pkgname $subpackages; do [ "$i" = "$j" ] || continue self=1 break done [ "$self" -eq 0 ] && real_rdeps="$real_rdeps $i" done if [ -n "$real_rdeps" ]; then warning "dependents on $soname:" # (warning2) >>> WARNING: printf '%s\n' "$real_rdeps" | sed '/^$/d; s/^/ /' >&2 else msg "No dependents on $soname." fi } if [ $# -gt 0 ]; then usage exit 2 fi if ! [ -f "$ABUILD_CONF" ] && ! [ -f "$ABUILD_USERCONF" ]; then die "no abuild.conf found" fi if ! [ -f APKBUILD ]; then die 'must be run in the directory of a built package' fi if ! [ -n "$CARCH" ]; then die "failed to detect CARCH" fi . ./APKBUILD startdir="$PWD" tmpdir=$(mktemp -d -t checkpkg-script.XXXXXX) trap "rm -rf '$tmpdir'" INT EXIT cd "$tmpdir" || die "failed to create temp dir" for i in $pkgname $subpackages; do _pkgname=${i%%:*} pkg=${_pkgname}-$pkgver-r$pkgrel pkgfile=${pkg}.apk repodir=${startdir%/*} repo=${repodir##*/} for filepath in "$PKGDEST"/$pkgfile "$REPODEST"/$repo/$CARCH/$pkgfile "$startdir"/$pkgfile; do if [ -f "$filepath" ]; then break fi done [ -f "$filepath" ] || die "can't find new apk $pkgfile" # generate a temp repositories file with only the http(s) repos grep -E "^https?:" /etc/apk/repositories > "$tmpdir/repositories" if ! oldapk="$(apk fetch --repositories-file "$tmpdir/repositories" "$_pkgname")"; then warning "could not download $_pkgname (network error or new package)" continue fi [ -n "$oldapk" ] || die "can't determine new apk name" oldapk="${oldapk##Downloading }.apk" [ -e "$oldapk" ] || die "can't find old apk $oldapk" pkginfo "$oldapk" "pkginfo-$_pkgname-old" pkginfo "$filepath" "pkginfo-$_pkgname-new" filelist "$oldapk" "filelist-$_pkgname-old" touch -r "pkginfo-$_pkgname-old" "filelist-$_pkgname-old" filelist "$filepath" "filelist-$_pkgname-new" touch -r "pkginfo-$_pkgname-new" "filelist-$_pkgname-new" diff -u "filelist-$_pkgname-old" "filelist-$_pkgname-new" || true diff -u "pkginfo-$_pkgname-old" "pkginfo-$_pkgname-new" | tee pkginfo-diff soname= for soname in $(awk '/^-provides = so:/ { print $3 }' pkginfo-diff); do check_soname "$soname" done [ -n "$soname" ] || msg "No soname differences for $_pkgname." rm "$oldapk" done