summaryrefslogblamecommitdiff
path: root/checkapk.in
blob: 500e9c7387a280df64e1fea76072b8bd0de1bf82 (plain) (tree)
1
2
3
4
5
6
7
8
9
            
 





                                                            
                         



                                                   
              

                         
 






                                     
         


                                                                                  
 
                                                        
 
               

 
           


                           



                                                                            

                                                  


                        





















                                                                             
                

                                                            
 
                   








                                                           






                                                            


















                                                                        

                                                                                 




                                               



                     
 
                                                               

                                  

                          
                                                             

  



                                    



                                             
                                
                                               

                                  
                         

                                        

                              
 
                                                                                                      



                                           
                                                               
 
                                                                       
                                                                         
 
                                                                                               
                                                                                     

                        
                                                              


                                                            

                                                   
 
                                                   
                                                                 
                                                     






                                                                                  
                                      

                                                                      
 
                    
    
#!/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

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