blob: b9313d55d5d64fe17d39f5f12f2bd09cd5f29853 (
plain) (
tree)
|
|
#!/bin/sh -e
# checkapk - find ABI breakages in package upgrades
# Copyright (c) 2012 Natanael Copa <natanael.copa@gmail.com>
#
# 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
: ${APK:="/sbin/apk"}
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
# lrwxrwxrwx root/root 0 2019-09-13 06:01 usr/lib/libixion-0.15.so.0 -> libixion-0.15.so.0.0.0
# hrwxr-xr-x root/root 0 2019-04-22 10:26 usr/bin/python3.6 link to usr/bin/python3.6m
tar_fmt='{ printf "%s %s ", $1, $2 }
/^l/ { printf "%s -> ", $(NF-2) }
/^h/ { printf "%s -> ", $(NF-3) }
{ print $NF }';;
busybox)
# -rw-r--r-- root/root 737 2019-06-14 20:45:29 .PKGINFO
# lrwxrwxrwx root/root 0 2019-09-13 06:01:49 usr/lib/libixion-0.15.so.0 -> libixion-0.15.so.0.0.0
# -rwxr-xr-x root/root 0 2019-04-22 10:26:45 usr/bin/python3.6 -> usr/bin/python3.6m
tar_fmt='{ printf "%s %s ", $1, $2 }
/ -> / { printf "%s -> ", $(NF-2) }
{ print $NF }';;
libarchive)
# -rw-r--r-- 0 root root 737 Jun 14 15:45 .PKGINFO
# lrwxrwxrwx 0 root root 0 Sep 13 01:01 usr/lib/libixion-0.15.so.0 -> libixion-0.15.so.0.0.0
# hrwxr-xr-x 0 root root 0 Oct 1 22:40 usr/bin/python3.6m link to usr/bin/python3.6
tar_fmt='{ printf "%s %s/%s ", $1, $3, $4 }
/^l/ { printf "%s -> ", $(NF-2) }
/^h/ { printf "%s -> ", $(NF-3) }
{ print $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 --exact "$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 newapk in "$PKGDEST"/$pkgfile "$REPODEST"/$repo/$CARCH/$pkgfile "$startdir"/$pkgfile; do
if [ -f "$newapk" ]; then
break
fi
done
[ -f "$newapk" ] || 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 ! $APK fetch --stdout --repositories-file "$tmpdir/repositories" "$_pkgname" > old.apk; then
warning "could not download $_pkgname (network error or new package)"
continue
fi
[ -e "old.apk" ] || die "can't find old apk old.apk"
pkginfo "old.apk" "pkginfo-$_pkgname-old"
pkginfo "$newapk" "pkginfo-$_pkgname-new"
filelist "old.apk" "filelist-$_pkgname-old"
touch -r "pkginfo-$_pkgname-old" "filelist-$_pkgname-old"
filelist "$newapk" "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 "old.apk"
done
|