#!/bin/sh
#
# Copyright 2015 Johannes Schauer <josch@mister-muffin.de>
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
set -eu
if [ $# -eq 0 ] ; then
pkgArel = "depends conflicts"
pkgAvpkg = "pkgb pkgb:amd64 pkgb:i386 pkgb:any pkgc pkgc:amd64 pkgc:i386 pkgc:any"
pkgAsrcvpkg = "pkgb:native pkgc:native"
pkgAtype = "binary source"
pkgBprovides = "none pkgc pkgc:amd64 pkgc:i386"
binpkgarch = "amd64 i386 all"
# possible Multi-Arch values
maprop = "no foreign allowed same"
# list for Arch:all packages without "same"
allmaprop = "no foreign allowed"
# all testcases are a 8-tuple with the following content:
#
# 1. pkgAtype
# 2. pkgBprovides
# 3. pkgAarch (if pkgA is a binary package)
# 4. pkgBarch
# 5. pkgAmaprop (if pkgA is a binary package)
# 6. pkgBmaprop
# 7. relationship (depends or conflicts)
# 8. vpkg value
#
# then, in case pkgAtype is "binary" both packages are tried to be installed
# at the same time
# in case pkgAtype is "source", the source package is checked for its
# build depends/conflict satisfiability with the second package installed
#
# the :any qualifier will also be used to depend on packages which are not
# m-a:allowed. This is to test situations in which either:
# 1. :any was added wrongly or
# 2. a m-a:allowed package was changed to something else without changing
# all reverse dependencies or
# 3. different package repositories are mixed where a package which
# is m-a:allowed in one repository and depended upon with :any is not
# m-a:allowed in another repository but of higher version or otherwise
# preferred
for t1 in $pkgAtype ; do
for t2 in $pkgBprovides ; do
if [ " $t1 " = "binary" ] ; then
pkgAarches = " $binpkgarch "
else
pkgAarches = "none"
fi
for t3 in $pkgAarches ; do
for t4 in $binpkgarch ; do
if [ " $t1 " = "binary" ] ; then
if [ " $t3 " = "all" ] ; then
pkgAma = " $allmaprop "
else
pkgAma = " $maprop "
fi
else
pkgAma = "none"
fi
for t5 in $pkgAma ; do
if [ " $t4 " = "all" ] ; then
pkgBma = " $allmaprop "
else
pkgBma = " $maprop "
fi
for t6 in $pkgBma ; do
for t7 in $pkgArel ; do
if [ " $t1 " = "binary" ] ; then
props = " $pkgAvpkg "
else
props = " $pkgAvpkg $pkgAsrcvpkg "
fi
for t8 in $props ; do
echo " $t1 $t2 $t3 $t4 $t5 $t6 $t7 $t8 "
done
done
done
done
done
done
done
done
exit 0
fi
if [ "amd64" != " $( dpkg --print-architecture) " ] ; then
echo "This script must be run on amd64." >& 2
exit 1
fi
if [ $# -ne 8 ] ; then
echo " usage: $0 [pkgatype pkgbprovides pkgaarch pkgbarch pkgama pkgbma debrel debvpkg] " >& 2
echo "" >& 2
echo " * pkgatype is the type of pkga and can be one of binary or source" >& 2
echo " * pkgbprovides is the provides field of pkgb and can be anything like none (no provides), pkgc, pkgc:amd64 or pkgc:i386" >& 2
echo " * pkgaarch is the architecture of pkga and can be any valid architecture or none in case pkga is a source package" >& 2
echo " * pkgbarch is the architecture of pkgb and can be any valid architecture" >& 2
echo " * pkgama is the multi-arch value of pkga and can be any valid multi-arch value or none in case pkga is a source package" >& 2
echo " * pkgbma is the multi-arch value of pkgb and can be any valid multi-arch value" >& 2
echo " * debrel is the relationship of pkga to pkgb and can be one of depends or conflicts" >& 2
echo " * debvpkg is the dependency relationship value and can be any anything like pkgb, pkgb:amd64, pkgb:i386, pkgb:any, pkgc, pkgc:amd64, pkgc:i386, pkgc:any, pkgb:native or pkgc:native" >& 2
echo "" >& 2
echo "If no arguments are given, then all legal permutations are printed to standard output, each on one line." >& 2
echo "" >& 2
echo "This allows one to test all situations like this:" >& 2
echo "" >& 2
echo " $ ./check.sh | xargs --max-procs=8 --max-lines=1 ./check.sh 2>/dev/null " >& 2
exit 1
fi
cleanup( ) {
# don't do anything if workdir doesn't exist
if [ ! -e " $workdir " ] ; then
return
fi
# the only way to clean /var/lib/apt/lists using apt is to set
# Dir::Etc::Sourcelist and Dir::Etc::Sourceparts to /dev/null and then
# update and clean
if [ -e " $workdir /aptroot/etc/apt/apt.conf " -a \( -d " $workdir /aptroot/var/lib/apt/lists " -o -d " $workdir /aptroot/var/cache/apt " \) ] ; then
APTOPTS = "-o Dir::Etc::Sourcelist=/dev/null -o Dir::Etc::Sourceparts=/dev/null"
APT_CONFIG = " $workdir /aptroot/etc/apt/apt.conf " apt-get update $APTOPTS >& 2
APT_CONFIG = " $workdir /aptroot/etc/apt/apt.conf " apt-get clean $APTOPTS >& 2
fi
rm -f " $workdir /aptroot/var/lib/apt/lists/lock "
rm -f " $workdir /aptroot/var/cache/apt/archives/lock "
rm -fd " $workdir /aptroot/var/cache/apt/archives/partial "
rm -fd " $workdir /aptroot/var/cache/apt/archives "
rm -fd " $workdir /aptroot/var/cache/apt "
rm -fd " $workdir /aptroot/var/cache "
rm -fd " $workdir /aptroot/var/lib/apt/lists/partial "
rm -fd " $workdir /aptroot/var/lib/apt/lists "
rm -fd " $workdir /aptroot/var/lib/apt "
rm -f " $workdir /aptroot/var/lib/dpkg/status "
rm -f " $workdir /aptroot/var/lib/dpkg/lock "
rm -fd " $workdir /aptroot/var/lib/dpkg "
rm -fd " $workdir /aptroot/var/lib "
rm -fd " $workdir /aptroot/var "
rm -fd " $workdir /aptroot/etc/apt/preferences.d "
rm -fd " $workdir /aptroot/etc/apt/apt.conf.d "
rm -fd " $workdir /aptroot/etc/apt/trusted.gpg.d "
rm -fd " $workdir /aptroot/etc/apt/sources.list.d "
rm -f " $workdir /aptroot/etc/apt/sources.list "
rm -f " $workdir /aptroot/etc/apt/apt.conf "
rm -fd " $workdir /aptroot/etc/apt "
rm -fd " $workdir /aptroot/etc "
rm -fd " $workdir /aptroot "
rm -f " $workdir /repo/pkga.deb "
rm -f " $workdir /repo/pkgb.deb "
rm -f " $workdir /repo/pkga/DEBIAN/control "
rm -f " $workdir /repo/pkgb/DEBIAN/control "
rm -fd " $workdir /repo/pkga/DEBIAN "
rm -f " $workdir /repo/pkga/debian/changelog "
rm -f " $workdir /repo/pkga/debian/control "
rm -fd " $workdir /repo/pkga/debian "
rm -fd " $workdir /repo/pkga "
rm -fd " $workdir /repo/pkgb/DEBIAN "
rm -fd " $workdir /repo/pkgb "
rm -f " $workdir /repo/Packages "
rm -f " $workdir /repo/Sources "
rm -f " $workdir /repo/Release "
rm -f " $workdir /repo/pkga_1.tar.gz "
rm -f " $workdir /repo/pkga_1.dsc "
rm -f " $workdir /repo/crossbuild-essential-i386/DEBIAN/control "
rm -fd " $workdir /repo/crossbuild-essential-i386/DEBIAN "
rm -fd " $workdir /repo/crossbuild-essential-i386 "
rm -f " $workdir /repo/build-essential.deb "
rm -f " $workdir /repo/build-essential/DEBIAN/control "
rm -fd " $workdir /repo/build-essential/DEBIAN "
rm -fd " $workdir /repo/build-essential "
rm -f " $workdir /repo/crossbuild-essential-i386.deb "
rm -f " $workdir /dpkgroot/info/format "
rm -f " $workdir /dpkgroot/info/pkga.list "
rm -f " $workdir /dpkgroot/info/pkgb.list "
rm -f " $workdir /dpkgroot/info/pkga.md5sums "
rm -f " $workdir /dpkgroot/info/pkgb.md5sums "
rm -f " $workdir /dpkgroot/info/pkgb:amd64.md5sums "
rm -f " $workdir /dpkgroot/info/pkgb:amd64.list "
rm -f " $workdir /dpkgroot/info/pkgb:i386.md5sums "
rm -f " $workdir /dpkgroot/info/pkgb:i386.list "
rm -f " $workdir /dpkgroot/info/pkga:amd64.md5sums "
rm -f " $workdir /dpkgroot/info/pkga:amd64.list "
rm -f " $workdir /dpkgroot/info/pkga:i386.md5sums "
rm -f " $workdir /dpkgroot/info/pkga:i386.list "
rm -f " $workdir /dpkgroot/info/crossbuild-essential-i386.md5sums "
rm -f " $workdir /dpkgroot/info/crossbuild-essential-i386.list "
rm -f " $workdir /dpkgroot/info/build-essential.md5sums "
rm -f " $workdir /dpkgroot/info/build-essential.list "
rm -fd " $workdir /dpkgroot/info "
rm -f " $workdir /dpkgroot/triggers/Lock "
rm -f " $workdir /dpkgroot/triggers/Unincorp "
rm -fd " $workdir /dpkgroot/triggers "
rm -f " $workdir /dpkgroot/updates/0001 "
rm -f " $workdir /dpkgroot/updates/0000 "
rm -f " $workdir /dpkgroot/updates/0002 "
rm -f " $workdir /dpkgroot/updates/tmp.i "
rm -fd " $workdir /dpkgroot/updates "
rm -f " $workdir /dpkgroot/available "
rm -f " $workdir /dpkgroot/diversions "
rm -f " $workdir /dpkgroot/lock "
rm -f " $workdir /dpkgroot/statoverride "
rm -f " $workdir /dpkgroot/status "
rm -f " $workdir /dpkgroot/status-old "
rm -f " $workdir /dpkgroot/triggers/Lock "
rm -f " $workdir /dpkgroot/triggers/Unincorp "
rm -f " $workdir /dpkgroot/arch "
rm -fd " $workdir /dpkgroot "
rm -fd " $workdir /aptroot "
rm -fd " $workdir /repo "
rm -fd " $workdir "
}
# make sure that the working directory is removed upon exit
trap cleanup EXIT HUP INT TERM
workdir = $( mktemp --directory --suffix= .deb-m-a-dep-check)
pkgtype = $1
provides = $2
pkgaarch = $3
pkgbarch = $4
pkgama = $5
pkgbma = $6
debrel = $7
debvpkg = $8
echo " testcase: $pkgtype $provides $pkgaarch $pkgbarch $pkgama $pkgbma $debrel $debvpkg " >& 2
# apt needs a package repository
# dose needs Packages/Sources files
# dpkg needs a .deb or an unpacked source tree
#
# Thus, we start with creating a Packages (and optionally a Sources
# file) and test dose3 with it.
#
# Then we turn that into a repository, let apt update from it and apt
# simulate a package installation
#
# Finally we use dpkg to simulate an installation
mkdir " $workdir /repo "
mkdir -p " $workdir /repo/pkgb/DEBIAN "
# write package b (has to be binary)
cat << END > "$workdir/repo/pkgb/DE BIAN/control"
Package: pkgb
Version: 1
Architecture: $pkgbarch
Multi-arch: $pkgbma
Description: pkgb
Maintainer: pkgb maintainers
END
if [ " $provides " != "none" ] ; then
echo " Provides: $provides " >> " $workdir /repo/pkgb/DEBIAN/control "
fi
dpkg-deb --build " $workdir /repo/pkgb " " $workdir /repo/pkgb.deb " >& 2
# write package a (can be source or binary)
if [ " $pkgtype " = binary ] ; then
mkdir -p " $workdir /repo/pkga/DEBIAN "
cat << END >> "$workdir/repo/pkga/DE BIAN/control"
Package: pkga
Version: 1
Architecture: $pkgaarch
Multi-Arch: $pkgama
Description: pkga
Maintainer: pkga maintainers
END
case $debrel in
depends)
echo " Depends: $debvpkg " >> " $workdir /repo/pkga/DEBIAN/control "
; ;
conflicts)
echo " Conflicts: $debvpkg " >>" $workdir /repo/pkga/DEBIAN/control "
; ;
*)
echo " unexpected value for t7 $debrel "
exit 1
esac
dpkg-deb --build " $workdir /repo/pkga " " $workdir /repo/pkga.deb " >& 2
else
mkdir -p " $workdir /repo/build-essential/DEBIAN "
cat << END > "$workdir/repo/build-essential/DE BIAN/control"
Package: build-essential
Version: 1
Architecture: amd64
Description: build-essential dummy
Maintainer: build-essential dummy maintainers
END
dpkg-deb --build " $workdir /repo/build-essential " " $workdir /repo/build-essential.deb " >& 2
mkdir -p " $workdir /repo/crossbuild-essential-i386/DEBIAN "
cat << END > "$workdir/repo/crossbuild-essential-i386/DE BIAN/control"
Package: crossbuild-essential-i386
Version: 1
Architecture: amd64
Description: crossbuild-essential-i386 dummy
Maintainer: crossbuild-essential-i386 dummy maintainers
END
dpkg-deb --build " $workdir /repo/crossbuild-essential-i386 " " $workdir /repo/crossbuild-essential-i386.deb " >& 2
mkdir -p " $workdir /repo/pkga/debian "
cat << END > " $workdir /repo/pkga/debian/control "
Source: pkga
END
case $debrel in
depends)
echo " Build-Depends: $debvpkg " >> " $workdir /repo/pkga/debian/control "
; ;
conflicts)
echo " Build-Conflicts: $debvpkg " >> " $workdir /repo/pkga/debian/control "
; ;
*)
echo " unexpected value for t7 $debrel "
exit 1
esac
cat << END >> " $workdir /repo/pkga/debian/control "
Package: dummy
Architecture: all
END
cat << END >> " $workdir /repo/pkga/debian/changelog "
pkga ( 1) UNRELEASED; urgency = low
* Entry. Closes: #12345
-- pkga maintainers <foo@bar.com> $( date -R)
END
( cd " $workdir /repo " && dpkg-source -b ./pkga >& 2 )
fi
dpkg-scanpackages " $workdir /repo " /dev/null ./ > " $workdir /repo/Packages "
if [ " $pkgtype " != binary ] ; then
dpkg-scansources " $workdir /repo " /dev/null ./ > " $workdir /repo/Sources "
fi
if [ " $pkgtype " = binary ] ; then
tail -n 999999 " $workdir /repo/Packages " >& 2
else
tail -n 999999 " $workdir /repo/Packages " " $workdir /repo/Sources " >& 2
fi
# write a Release file
cat << END > " $workdir /repo/Release "
Date: $( date -R)
SHA512:
$( sha512sum " $workdir /repo/Packages " ) $( stat -c %s " $workdir /repo/Packages " ) ./Packages
END
if [ " $pkgtype " != binary ] ; then
echo " $( sha512sum " $workdir /repo/Sources " ) $( stat -c %s " $workdir /repo/Sources " ) ./Sources " >> " $workdir /repo/Release "
fi
# set up apt
for dir in /etc/apt /etc/apt/apt.conf.d /etc/apt/preferences.d \
/etc/apt/trusted.gpg.d /etc/apt/sources.list.d \
/var/lib/apt/lists/partial /var/cache/apt/archives/partial \
/var/lib/dpkg; do
mkdir -p " $workdir /aptroot/ $dir "
done
cat << END > " $workdir /aptroot/etc/apt/sources.list "
deb [ trusted = yes] file://$workdir /repo ./
END
if [ " $pkgtype " != binary ] ; then
echo " deb-src [trusted=yes] file:// $workdir /repo ./ " >> " $workdir /aptroot/etc/apt/sources.list "
fi
touch " $workdir /aptroot/var/lib/dpkg/status "
# We create an apt.conf and pass it to apt via the APT_CONFIG
# environment variable instead of passing all options via the command
# line because otherwise apt will read the system's config first and
# might get unwanted configuration options from there. See apt.conf(5)
# for the order in which configuration options are read.
#
# While we are at it, we also set all other options through our custom
# apt.conf.
#
# Apt::Architecture has to be set because otherwise apt will default to
# the architecture apt was compiled for.
#
# Apt::Architectures has to be set or otherwise apt will use dpkg to
# find all foreign architectures of the system running apt.
#
# Dir::State::status has to be set even though Dir is set because
# Dir::State is set to var/lib/apt, so Dir::State::status would be
# below that but really isn't and without an absolute path,
# Dir::State::status would be constructed from Dir + Dir::State +
# Dir::State::status. This has been fixed in apt commit
# 475f75506db48a7fa90711fce4ed129f6a14cc9a.
#
# Acquire::Check-Valid-Until has to be set to false because the
# snapshot timestamps might be too far in the past to still be valid.
#
# Acquire::Languages has to be set to prevent downloading of
# translations from the mirrors.
#
# Binary::apt-get::Acquire::AllowInsecureRepositories has to be set to
# false so that apt-get update fails if repositories cannot be
# authenticated. The default value of this option will change to true
# with apt from Debian Buster.
cat << END > " $workdir /aptroot/etc/apt/apt.conf "
Apt {
Architecture "amd64" ;
Architectures { "amd64" ; "i386" } ;
} ;
Dir " $workdir /aptroot " ;
Dir::State::status " $workdir /aptroot/var/lib/dpkg/status " ;
Acquire::Check-Valid-Until "false" ;
Acquire::Languages "none" ;
Binary::apt-get::Acquire::AllowInsecureRepositories "false" ;
END
APT_CONFIG = " $workdir /aptroot/etc/apt/apt.conf " apt-get update >& 2
# set up dpkg
mkdir " $workdir /dpkgroot "
mkdir " $workdir /dpkgroot/info "
mkdir " $workdir /dpkgroot/triggers "
mkdir " $workdir /dpkgroot/updates "
touch " $workdir /dpkgroot/available "
touch " $workdir /dpkgroot/diversions "
touch " $workdir /dpkgroot/lock "
touch " $workdir /dpkgroot/statoverride "
touch " $workdir /dpkgroot/status "
touch " $workdir /dpkgroot/status-old "
touch " $workdir /dpkgroot/triggers/Lock "
touch " $workdir /dpkgroot/triggers/Unincorp "
dpkg --admindir= " $workdir /dpkgroot " --add-architecture amd64
dpkg --admindir= " $workdir /dpkgroot " --add-architecture i386
# test dose3
dosestatus = 0
if [ " $pkgtype " = binary ] ; then
dose-deb-coinstall --deb-native-arch= amd64 --deb-foreign-archs= i386 " $workdir /repo/Packages " >/dev/null || dosestatus = 1
else
dose-builddebcheck --deb-native-arch= amd64 --deb-foreign-archs= i386 --checkonly= pkga " $workdir /repo/Packages " " $workdir /repo/Sources " >/dev/null || dosestatus = 1
fi
echo " dosestatus: $dosestatus " >& 2
# test apt
aptstatus = 0
if [ " $pkgtype " = binary ] ; then
APT_CONFIG = " $workdir /aptroot/etc/apt/apt.conf " apt-get install --simulate " pkga: $pkgaarch " " pkgb: $pkgbarch " >& 2 || aptstatus = 1
else
APT_CONFIG = " $workdir /aptroot/etc/apt/apt.conf " apt-get build-dep --simulate pkga >& 2 || aptstatus = 1
fi
echo " aptstatus: $aptstatus " >& 2
# test dpkg
dpkgstatus = 0
if [ " $pkgtype " = binary ] ; then
PATH = $PATH :/sbin dpkg --log= /dev/null --force-unsafe-io --force-not-root --admindir= " $workdir /dpkgroot " -i " $workdir /repo/pkga.deb " " $workdir /repo/pkgb.deb " >& 2 || dpkgstatus = 1
else
PATH = $PATH :/sbin dpkg --log= /dev/null --force-unsafe-io --force-not-root --admindir= " $workdir /dpkgroot " -i " $workdir /repo/pkgb.deb " " $workdir /repo/build-essential.deb " " $workdir /repo/crossbuild-essential-i386.deb " >& 2
dpkg-checkbuilddeps --admindir= " $workdir /dpkgroot " " $workdir /repo/pkga/debian/control " >& 2 || dpkgstatus = 1
fi
echo " dpkgstatus: $dpkgstatus " >& 2
echo " $pkgtype $provides $pkgaarch $pkgbarch $pkgama $pkgbma $debrel $debvpkg $dosestatus $aptstatus $dpkgstatus "
cleanup