#!/bin/sh # # Copyright 2015 Johannes Schauer # # 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/DEBIAN/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/DEBIAN/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/DEBIAN/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/DEBIAN/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 $(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