Browse Source

* apt-pkg/depcache.cc:
- add SetCandidateRelease() to set a candidate version and
the candidates of dependencies if needed to a specified
release (Closes: #572709)
- change pkg/release behavior to use the new SetCandidateRelease
so installing packages from experimental or backports is easier

David Kalnischkies 13 years ago
parent
commit
2c085486d3

+ 161 - 0
apt-pkg/depcache.cc

@@ -10,6 +10,7 @@
 // Include Files							/*{{{*/
 #include <apt-pkg/depcache.h>
 #include <apt-pkg/version.h>
+#include <apt-pkg/versionmatch.h>
 #include <apt-pkg/error.h>
 #include <apt-pkg/sptr.h>
 #include <apt-pkg/algorithms.h>
@@ -1554,6 +1555,166 @@ void pkgDepCache::SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo)
    }
 }
 									/*}}}*/
+// DepCache::SetCandidateRelease - Change the candidate version		/*{{{*/
+// ---------------------------------------------------------------------
+/* changes the candidate of a package and walks over all its dependencies
+   to check if it needs to change the candidate of the dependency, too,
+   to reach a installable versionstate */
+bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer,
+					std::string const &TargetRel)
+{
+   std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> > Changed;
+   return SetCandidateRelease(TargetVer, TargetRel, Changed);
+}
+bool pkgDepCache::SetCandidateRelease(pkgCache::VerIterator TargetVer,
+					std::string const &TargetRel,
+					std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> > &Changed)
+{
+   SetCandidateVersion(TargetVer);
+
+   if (TargetRel == "installed" || TargetRel == "candidate") // both doesn't make sense in this context
+      return true;
+
+   pkgVersionMatch Match(TargetRel, pkgVersionMatch::Release);
+   // save the position of the last element we will not undo - if we have to
+   std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::iterator newChanged = --(Changed.end());
+
+   for (pkgCache::DepIterator D = TargetVer.DependsList(); D.end() == false; ++D)
+   {
+      if (D->Type != pkgCache::Dep::PreDepends && D->Type != pkgCache::Dep::Depends &&
+	  ((D->Type != pkgCache::Dep::Recommends && D->Type != pkgCache::Dep::Suggests) ||
+	   IsImportantDep(D) == false))
+	 continue;
+
+      // walk over an or-group and check if we need to do anything
+      // for simpilicity no or-group is handled as a or-group including one dependency
+      pkgCache::DepIterator Start = D;
+      bool itsFine = false;
+      for (bool stillOr = true; stillOr == true; ++Start)
+      {
+	 stillOr = (Start->CompareOp & Dep::Or) == Dep::Or;
+	 pkgCache::PkgIterator const P = Start.TargetPkg();
+	 // virtual packages can't be a solution
+	 if (P.end() == true || (P->ProvidesList == 0 && P->VersionList == 0))
+	    continue;
+	 pkgCache::VerIterator const Cand = PkgState[P->ID].CandidateVerIter(*this);
+	 // no versioned dependency - but is it installable?
+	 if (Start.TargetVer() == 0 || Start.TargetVer()[0] == '\0')
+	 {
+	    // Check if one of the providers is installable
+	    if (P->ProvidesList != 0)
+	    {
+	       pkgCache::PrvIterator Prv = P.ProvidesList();
+	       for (; Prv.end() == false; ++Prv)
+	       {
+		  pkgCache::VerIterator const C = PkgState[Prv.OwnerPkg()->ID].CandidateVerIter(*this);
+		  if (C.end() == true || C != Prv.OwnerVer() ||
+		      (VersionState(C.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) != DepCandMin)
+		     continue;
+		  break;
+	       }
+	       if (Prv.end() == true)
+		  continue;
+	    }
+	    // no providers, so check if we have an installable candidate version
+	    else if (Cand.end() == true ||
+		(VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) != DepCandMin)
+	       continue;
+	    itsFine = true;
+	    break;
+	 }
+	 if (Cand.end() == true)
+	    continue;
+	 // check if the current candidate is enough for the versioned dependency - and installable?
+	 if (VS().CheckDep(P.CandVersion(), Start->CompareOp, Start.TargetVer()) == true &&
+	     (VersionState(Cand.DependsList(), DepInstall, DepCandMin, DepCandPolicy) & DepCandMin) == DepCandMin)
+	 {
+	    itsFine = true;
+	    break;
+	 }
+      }
+
+      if (itsFine == true) {
+	 // something in the or-group was fine, skip all other members
+	 for (; (D->CompareOp & Dep::Or) == Dep::Or; ++D);
+	 continue;
+      }
+
+      // walk again over the or-group and check each if a candidate switch would help
+      itsFine = false;
+      for (bool stillOr = true; stillOr == true; ++D)
+      {
+	 stillOr = (D->CompareOp & Dep::Or) == Dep::Or;
+	 // changing candidate will not help if the dependency is not versioned
+	 if (D.TargetVer() == 0 || D.TargetVer()[0] == '\0')
+	 {
+	    if (stillOr == true)
+	       continue;
+	    break;
+	 }
+
+	 pkgCache::VerIterator V;
+	 if (TargetRel == "newest")
+	    V = D.TargetPkg().VersionList();
+	 else
+	    V = Match.Find(D.TargetPkg());
+
+	 // check if the version from this release could satisfy the dependency
+	 if (V.end() == true || VS().CheckDep(V.VerStr(), D->CompareOp, D.TargetVer()) == false)
+	 {
+	    if (stillOr == true)
+	       continue;
+	    break;
+	 }
+
+	 pkgCache::VerIterator oldCand = PkgState[D.TargetPkg()->ID].CandidateVerIter(*this);
+	 if (V == oldCand)
+	 {
+	    // Do we already touched this Version? If so, their versioned dependencies are okay, no need to check again
+	    for (std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::const_iterator c = Changed.begin();
+		 c != Changed.end(); ++c)
+	    {
+	       if (c->first->ParentPkg != V->ParentPkg)
+		  continue;
+	       itsFine = true;
+	       break;
+	    }
+	 }
+
+	 if (itsFine == false)
+	 {
+	    // change the candidate
+	    Changed.push_back(make_pair(oldCand, TargetVer));
+	    if (SetCandidateRelease(V, TargetRel, Changed) == false)
+	    {
+	       if (stillOr == false)
+		  break;
+	       // undo the candidate changing
+	       SetCandidateVersion(oldCand);
+	       Changed.pop_back();
+	       continue;
+	    }
+	    itsFine = true;
+	 }
+
+	 // something in the or-group was fine, skip all other members
+	 for (; (D->CompareOp & Dep::Or) == Dep::Or; ++D);
+	 break;
+      }
+
+      if (itsFine == false && (D->Type == pkgCache::Dep::PreDepends || D->Type == pkgCache::Dep::Depends))
+      {
+	 // undo all changes which aren't lead to a solution
+	 for (std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::const_iterator c = ++newChanged;
+	      c != Changed.end(); ++c)
+	    SetCandidateVersion(c->first);
+	 Changed.erase(newChanged, Changed.end());
+	 return false;
+      }
+   }
+   return true;
+}
+									/*}}}*/
 // DepCache::MarkAuto - set the Auto flag for a package			/*{{{*/
 // ---------------------------------------------------------------------
 /* */

+ 19 - 0
apt-pkg/depcache.h

@@ -396,6 +396,25 @@ class pkgDepCache : protected pkgCache::Namespace
 
    void SetReInstall(PkgIterator const &Pkg,bool To);
    void SetCandidateVersion(VerIterator TargetVer, bool const &Pseudo = true);
+   bool SetCandidateRelease(pkgCache::VerIterator TargetVer,
+				std::string const &TargetRel);
+   /** Set the candidate version for dependencies too if needed.
+    *
+    *  Sets not only the candidate version as SetCandidateVersion does,
+    *  but walks also down the dependency tree and checks if it is required
+    *  to set the candidate of the dependency to a version from the given
+    *  release, too.
+    *
+    *  \param TargetVer new candidate version of the package
+    *  \param TargetRel try to switch to this release if needed
+    *  \param[out] Changed a list of pairs consisting of the \b old
+    *              version of the changed package and the version which
+    *              required the switch of this dependency
+    *  \return \b true if the switch was successful, \b false otherwise
+    */
+   bool SetCandidateRelease(pkgCache::VerIterator TargetVer,
+			    std::string const &TargetRel,
+			    std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> > &Changed);
 
    /** Set the "is automatically installed" flag of Pkg. */
    void MarkAuto(const PkgIterator &Pkg, bool Auto);

+ 33 - 3
cmdline/apt-get.cc

@@ -628,6 +628,8 @@ class CacheSetHelperAPTGet : public APT::CacheSetHelper {
 	APT::PackageSet virtualPkgs;
 
 public:
+	std::list<std::pair<pkgCache::VerIterator, std::string> > selectedByRelease;
+
 	CacheSetHelperAPTGet(std::ostream &out) : APT::CacheSetHelper(true), out(out) {
 		explicitlyNamed = true;
 	}
@@ -646,9 +648,9 @@ public:
 	}
 	virtual void showSelectedVersion(pkgCache::PkgIterator const &Pkg, pkgCache::VerIterator const Ver,
 				 string const &ver, bool const &verIsRel) {
-		if (ver != Ver.VerStr())
-			ioprintf(out, _("Selected version '%s' (%s) for '%s'\n"),
-				 Ver.VerStr(), Ver.RelStr().c_str(), Pkg.FullName(true).c_str());
+		if (ver == Ver.VerStr())
+			return;
+		selectedByRelease.push_back(make_pair(Ver, ver));
 	}
 
 	bool showVirtualPackageErrors(pkgCacheFile &Cache) {
@@ -829,6 +831,33 @@ struct TryToInstall {
       }
    }
 
+   bool propergateReleaseCandiateSwitching(std::list<std::pair<pkgCache::VerIterator, std::string> > start, std::ostream &out)
+   {
+      bool Success = true;
+      std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> > Changed;
+      for (std::list<std::pair<pkgCache::VerIterator, std::string> >::const_iterator s = start.begin();
+		s != start.end(); ++s)
+      {
+	 Changed.push_back(std::make_pair(s->first, pkgCache::VerIterator(*Cache)));
+	 // We continue here even if it failed to enhance the ShowBroken output
+	 Success &= Cache->GetDepCache()->SetCandidateRelease(s->first, s->second, Changed);
+      }
+      for (std::list<std::pair<pkgCache::VerIterator, pkgCache::VerIterator> >::const_iterator c = Changed.begin();
+	   c != Changed.end(); ++c)
+      {
+	 if (c->second.end() == true)
+	    ioprintf(out, _("Selected version '%s' (%s) for '%s'\n"),
+		     c->first.VerStr(), c->first.RelStr().c_str(), c->first.ParentPkg().FullName(true).c_str());
+	 else if (c->first.ParentPkg()->Group != c->second.ParentPkg()->Group)
+	 {
+	    pkgCache::VerIterator V = (*Cache)[c->first.ParentPkg()].CandidateVerIter(*Cache);
+	    ioprintf(out, _("Selected version '%s' (%s) for '%s' because of '%s'\n"), V.VerStr(),
+		     V.RelStr().c_str(), V.ParentPkg().FullName(true).c_str(), c->second.ParentPkg().FullName(true).c_str());
+	 }
+      }
+      return Success;
+   }
+
    void doAutoInstall() {
       for (APT::PackageSet::const_iterator P = doAutoInstallLater.begin();
 	   P != doAutoInstallLater.end(); ++P) {
@@ -1779,6 +1808,7 @@ bool DoInstall(CommandLine &CmdL)
       {
 	 if (order[i] == MOD_INSTALL) {
 	    InstallAction = std::for_each(verset[MOD_INSTALL].begin(), verset[MOD_INSTALL].end(), InstallAction);
+	    InstallAction.propergateReleaseCandiateSwitching(helper.selectedByRelease, c0out);
 	    InstallAction.doAutoInstall();
 	 }
 	 else if (order[i] == MOD_REMOVE)

+ 9 - 0
debian/NEWS

@@ -1,3 +1,12 @@
+apt (0.8.11+wheezy) UNRELEASED; urgency=low
+
+  * apt-get install pkg/experimental will now not only switch the
+    candidate of package pkg to the version from the release experimental
+    but also of all dependencies of pkg if the current candidate can't
+    satisfy a versioned dependency.
+
+ -- David Kalnischkies <kalnischkies@gmail.com>  Fri, 03 Dez 2010 14:09:12 +0100
+
 apt (0.7.26~exp3) experimental; urgency=low
 
   * apt-ftparchive now reads the standard configuration files in

+ 7 - 1
debian/changelog

@@ -1,5 +1,9 @@
 apt (0.8.11+wheezy) unstable; urgency=low
 
+  * apt-pkg/depcache.cc:
+    - add SetCandidateRelease() to set a candidate version and
+      the candidates of dependencies if needed to a specified
+      release (Closes: #572709)
   * cmdline/apt-get.cc:
     - if --print-uris is used don't setup downloader as we don't need
       progress, lock nor the directories it would create otherwise
@@ -7,10 +11,12 @@ apt (0.8.11+wheezy) unstable; urgency=low
       only if they cause the remove of this essential (Closes: #601961)
     - keep not installed garbage packages uninstalled instead of showing
       in the autoremove section and installing those (Closes: #604222)
+    - change pkg/release behavior to use the new SetCandidateRelease
+      so installing packages from experimental or backports is easier
   * debian/control:
     - add Vcs-Browser now that loggerhead works again (Closes: #511168)
 
- -- David Kalnischkies <kalnischkies@gmail.com>  Fri, 03 Dec 2010 14:00:18 +0100
+ -- David Kalnischkies <kalnischkies@gmail.com>  Fri, 03 Dec 2010 17:06:28 +0100
 
 apt (0.8.10) unstable; urgency=low
 

+ 6 - 4
test/integration/framework

@@ -134,6 +134,7 @@ setupenvironment() {
 	echo "DPKG::options:: \"--force-not-root\";" >> aptconfig.conf
 	echo "DPKG::options:: \"--force-bad-path\";" >> aptconfig.conf
 	echo "DPKG::options:: \"--log=${TMPWORKINGDIRECTORY}/rootdir/var/log/dpkg.log\";" >> aptconfig.conf
+	echo 'quiet::NoUpdate "true";' >> aptconfig.conf
 	export LC_ALL=C
 	msgdone "info"
 }
@@ -337,10 +338,11 @@ insertpackage() {
 		echo "Package: $NAME
 Priority: optional
 Section: other
-Installed-Size: 23356
+Installed-Size: 42
 Maintainer: Joe Sixpack <joe@example.org>
 Architecture: $ARCH
-Version: $VERSION" >> $FILE
+Version: $VERSION
+Filename: pool/main/${NAME}/${NAME}_${VERSION}_${ARCH}.deb" >> $FILE
 		test -z "$DEPENDENCIES" || echo "$DEPENDENCIES" >> $FILE
 		echo "Description: an autogenerated dummy ${NAME}=${VERSION}/${RELEASE}
  If you find such a package installed on your system,
@@ -380,8 +382,8 @@ generatereleasefiles() {
 	if [ -e aptarchive/dists ]; then
 		for dir in $(find ./aptarchive/dists -mindepth 1 -maxdepth 1 -type d); do
 			local CODENAME="$(echo "$dir" | cut -d'/' -f 4)"
-			aptftparchive -qq release $dir -o APT::FTPArchive::Release::Codename="${CODENAME}" | sed -e '/0 Release$/ d' > $dir/Release # remove the self reference
-			if [ "$CODENAME" = "experimental" ]; then
+			aptftparchive -qq release $dir -o APT::FTPArchive::Release::Suite="${CODENAME}" -o APT::FTPArchive::Release::Codename="${CODENAME}" | sed -e '/0 Release$/ d' > $dir/Release # remove the self reference
+			if [ "$CODENAME" = "experimental" -o "$CODENAME" = "experimental2" ]; then
 				sed -i '/^Date: / a\
 NotAutomatic: yes' $dir/Release
 			fi

+ 435 - 0
test/integration/test-release-candidate-switching

@@ -0,0 +1,435 @@
+#!/bin/sh
+set -e
+
+local TESTDIR=$(readlink -f $(dirname $0))
+. $TESTDIR/framework
+setupenvironment
+configarchitecture "i386"
+
+insertpackage 'unstable' 'libc6' 'i386' '2.11.2-7+sid'
+insertpackage 'unstable' 'phonon-backend-xine' 'i386' '4:4.6.0really4.4.2-1+sid' 'Provides: phonon-backend'
+insertpackage 'unstable' 'phonon-backend-xine2' 'i386' '4:4.6.0really4.4.2-1+sid'
+insertpackage 'unstable' 'phonon-backend-xine3' 'i386' '4:4.6.0really4.4.2-1+sid'
+insertpackage 'unstable' 'phonon-backend-xine4' 'i386' '4:4.6.0really4.4.2-1+sid'
+insertpackage 'unstable' 'phonon-backend-null' 'i386' '4:4.20.0+sid' 'Provides: phonon-backend'
+insertpackage 'unstable' 'intermediatepkg' 'all' '1.0'
+
+insertpackage 'unstable' 'amarok-common' 'all' '2.3.1-1+sid'
+insertpackage 'unstable' 'amarok-utils' 'i386' '2.3.1-1+sid'
+insertpackage 'unstable' 'libmtp8' 'i386' '0.3.1+sid'
+insertpackage 'unstable' 'amarok' 'i386' '2.3.1-1+sid' 'Depends: amarok-common (= 2.3.1-1+sid), amarok-utils (= 2.3.1-1+sid), phonon-backend-xine | phonon-backend, libmtp8 (>= 0.3.1), libc6'
+
+insertpackage 'experimental' 'amarok-common' 'all' '2.3.2-2+exp'
+insertpackage 'experimental' 'amarok-utils' 'i386' '2.3.2-2+exp'
+insertpackage 'experimental' 'libmtp8' 'i386'  '0.3.3+exp'
+insertpackage 'experimental' 'phonon-backend-xine' 'i386' '5:4.6.0+exp' 'Provides: phonon-backend'
+insertpackage 'experimental' 'phonon-backend-xine2' 'i386' '5:4.6.0+exp' 'Depends: uninstallablepkg
+Provides: phonon-backend-broken'
+insertpackage 'experimental' 'phonon-backend-xine3' 'i386' '5:4.6.0+exp' 'Depends: intermediatepkg (>= 1.5)'
+insertpackage 'experimental' 'phonon-backend-xine4' 'i386' '5:4.6.0+exp' 'Depends: intermediateuninstallablepkg (= 2.0)
+Provides: phonon-backend-broken'
+insertpackage 'experimental' 'intermediatepkg' 'all' '2.0' 'Depends: libc6'
+insertpackage 'experimental' 'intermediateuninstallablepkg' 'all' '2.0' 'Depends: uninstallablepkg'
+insertpackage 'experimental' 'phonon-backend-null' 'i386' '5:4.20.0+exp' 'Provides: phonon-backend'
+insertpackage 'experimental' 'amarok' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), amarok-utils (= 2.3.2-2+exp), phonon-backend-xine | phonon-backend, libmtp8 (>= 0.3.1), libc6'
+
+insertpackage 'experimental2' 'phonon-backend-xine' 'i386' '5:4.00.0+exp' 'Provides: phonon-backend'
+insertpackage 'experimental2' 'amarok-less' 'i386' '2.3.2-2+exp' 'Depends: amarok-common, phonon-backend-xine (>= 5:4.00.0+exp), libmtp8, libc6, amarok-utils'
+insertpackage 'experimental' 'amarok-higher' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine (>= 5:4.6.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+
+insertpackage 'experimental' 'amarok-null' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine (= 1:1.0-1) | phonon-backend, libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+insertpackage 'experimental' 'amarok-null2' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-null (= 1:1.0-1) | phonon-backend, libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+insertpackage 'experimental' 'amarok-null2' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-null (= 1:1.0-1) | phonon-backend, libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+insertpackage 'experimental' 'amarok-xine' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine (= 5:4.6.0+exp) | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+insertpackage 'experimental' 'amarok-xine2' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine2 (= 5:4.6.0+exp) | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+insertpackage 'experimental' 'amarok-xine3' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine3 (= 5:4.6.0+exp) | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+insertpackage 'experimental' 'amarok-xine4' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-xine4 (= 5:4.6.0+exp) | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+insertpackage 'experimental' 'amarok-broken' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp), phonon-backend-broken | phonon-backend-null (= 5:4.20.0+exp), libmtp8 (>= 0.3.1), libc6, amarok-utils (= 2.3.2-2+exp)'
+
+insertpackage 'experimental' 'amarok-recommends' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp)
+Recommends: amarok-utils (= 2.3.2-2+exp), phonon-backend-xine | phonon-backend, libmtp8 (>= 0.3.1), libc6'
+insertpackage 'experimental' 'amarok-recommends2' 'i386' '2.3.2-2+exp' 'Depends: amarok-common (= 2.3.2-2+exp)
+Recommends: amarok-utils (= 2.30.2-2+exp), phonon-backend-xine | phonon-backend, libmtp8 (>= 0.3.1), libc6'
+
+insertpackage 'experimental' 'uninstallablepkg' 'all' '1.0' 'Depends: libmtp8 (>= 10:0.20.1), amarok-utils (= 2.3.2-2+exp)'
+
+setupaptarchive
+
+testequal "Reading package lists...
+Building dependency tree...
+The following extra packages will be installed:
+   amarok-common (2.3.1-1+sid)
+   amarok-utils (2.3.1-1+sid)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+The following NEW packages will be installed:
+   amarok (2.3.1-1+sid)
+   amarok-common (2.3.1-1+sid)
+   amarok-utils (2.3.1-1+sid)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok --trivial-only -V -q=0
+
+testequal "Reading package lists...
+Building dependency tree...
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.3+exp)
+   phonon-backend-xine (4.6.0+exp)
+The following NEW packages will be installed:
+   amarok (2.3.2-2+exp)
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.3+exp)
+   phonon-backend-xine (4.6.0+exp)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok -t experimental --trivial-only -V -q=0
+
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok'
+The following extra packages will be installed:
+   amarok (2.3.2-2+exp)
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+The following NEW packages will be installed:
+   amarok (2.3.2-2+exp)
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok/experimental --trivial-only -V -q=0
+
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-null'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-null'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-null'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-null (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+sid)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-null (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+sid)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-null/experimental --trivial-only -V -q=0
+
+# do not select the same version multiple times
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-null'
+The following extra packages will be installed:
+   amarok (2.3.2-2+exp)
+   amarok-common (2.3.2-2+exp)
+   amarok-null (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+The following NEW packages will be installed:
+   amarok (2.3.2-2+exp)
+   amarok-common (2.3.2-2+exp)
+   amarok-null (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 301 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok/experimental amarok-null/experimental --trivial-only -V -q=0
+
+# … but thighten the version if needed
+# in theory, the second line is wrong, but printing the right version is too much of a hassle
+# (we have to check if later in the Changed list is another change and if so use this version
+#  instead of the current candidate) - and it wouldn't be (really) useful anyway…
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental2 [i386]) for 'amarok-less'
+Selected version '5:4.6.0+exp' (experimental [i386]) for 'phonon-backend-xine' because of 'amarok-less'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-higher'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-higher'
+Selected version '5:4.6.0+exp' (experimental [i386]) for 'phonon-backend-xine' because of 'amarok-higher'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-higher'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-higher (2.3.2-2+exp)
+   amarok-less (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0+exp)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-higher (2.3.2-2+exp)
+   amarok-less (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0+exp)
+0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 301 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-less/experimental2 amarok-higher/experimental --trivial-only -V -q=0
+
+# phonon-backend-null can't be used directly, but as it provides it is still fine…
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-null2'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-null2'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-null2'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-null2 (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+sid)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-null2 (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+sid)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-null2/experimental --trivial-only -V -q=0
+
+# if an or-group satisfier is already found, do not set others
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-xine'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-xine'
+Selected version '5:4.6.0+exp' (experimental [i386]) for 'phonon-backend-xine' because of 'amarok-xine'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-xine'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   amarok-xine (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0+exp)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   amarok-xine (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0+exp)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-xine/experimental --trivial-only -V -q=0
+
+# … but proceed testing if the first doesn't work out
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-xine2'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-xine2'
+Selected version '5:4.20.0+exp' (experimental [i386]) for 'phonon-backend-null' because of 'amarok-xine2'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-xine2'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   amarok-xine2 (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+exp)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   amarok-xine2 (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+exp)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-xine2/experimental --trivial-only -V -q=0
+
+# sometimes, the second level need to be corrected, too
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-xine3'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-xine3'
+Selected version '5:4.6.0+exp' (experimental [i386]) for 'phonon-backend-xine3' because of 'amarok-xine3'
+Selected version '2.0' (experimental [all]) for 'intermediatepkg' because of 'phonon-backend-xine3'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-xine3'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   amarok-xine3 (2.3.2-2+exp)
+   intermediatepkg (2.0)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine3 (4.6.0+exp)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   amarok-xine3 (2.3.2-2+exp)
+   intermediatepkg (2.0)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine3 (4.6.0+exp)
+0 upgraded, 7 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 301 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-xine3/experimental --trivial-only -V -q=0
+
+# … but proceed testing if the first doesn't work out even in second deep
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-xine4'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-xine4'
+Selected version '5:4.20.0+exp' (experimental [i386]) for 'phonon-backend-null' because of 'amarok-xine4'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-xine4'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   amarok-xine4 (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+exp)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   amarok-xine4 (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+exp)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-xine4/experimental --trivial-only -V -q=0
+
+# providers can be broken, too
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-broken'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-broken'
+Selected version '5:4.20.0+exp' (experimental [i386]) for 'phonon-backend-null' because of 'amarok-broken'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-broken'
+The following extra packages will be installed:
+   amarok-broken (2.3.2-2+exp)
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+exp)
+The following NEW packages will be installed:
+   amarok-broken (2.3.2-2+exp)
+   amarok-common (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-null (4.20.0+exp)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-broken/experimental --trivial-only -V -q=0
+
+# switch the candidate for recommends too if they should be installed
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-recommends'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-recommends'
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-utils' because of 'amarok-recommends'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-recommends (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-recommends (2.3.2-2+exp)
+   amarok-utils (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 258 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-recommends/experimental --trivial-only -V -q=0 -o APT::Install-Recommends=1
+
+# … or not if not
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-recommends'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-recommends'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-recommends (2.3.2-2+exp)
+Recommended packages:
+   amarok-utils (2.3.1-1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+   phonon-backend ()
+   libmtp8 (0.3.1+sid)
+   libc6 (2.11.2-7+sid)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-recommends (2.3.2-2+exp)
+0 upgraded, 2 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 86.0 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-recommends/experimental --trivial-only -V -q=0 -o APT::Install-Recommends=0
+
+# but broken recommends are not the end of the world
+# FIXME: the version output for recommend packages is a bit strange… but what would be better?
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '2.3.2-2+exp' (experimental [i386]) for 'amarok-recommends2'
+Selected version '2.3.2-2+exp' (experimental [all]) for 'amarok-common' because of 'amarok-recommends2'
+The following extra packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-recommends2 (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+Recommended packages:
+   amarok-utils (2.3.1-1+sid)
+The following NEW packages will be installed:
+   amarok-common (2.3.2-2+exp)
+   amarok-recommends2 (2.3.2-2+exp)
+   libc6 (2.11.2-7+sid)
+   libmtp8 (0.3.1+sid)
+   phonon-backend-xine (4.6.0really4.4.2-1+sid)
+0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
+After this operation, 215 kB of additional disk space will be used.
+E: Trivial Only specified but this is not a trivial operation." aptget install amarok-recommends2/experimental --trivial-only -V -q=0 -o APT::Install-Recommends=1
+
+# if one depends doesn't work, we don't need to look deeper…
+testequal "Reading package lists...
+Building dependency tree...
+Selected version '1.0' (experimental [all]) for 'uninstallablepkg'
+Some packages could not be installed. This may mean that you have
+requested an impossible situation or if you are using the unstable
+distribution that some required packages have not yet been created
+or been moved out of Incoming.
+The following information may help to resolve the situation:
+
+The following packages have unmet dependencies:
+ uninstallablepkg : Depends: libmtp8 (>= 10:0.20.1) but it is not going to be installed
+                    Depends: amarok-utils (= 2.3.2-2+exp) but 2.3.1-1+sid is to be installed
+E: Broken packages" aptget install uninstallablepkg/experimental --trivial-only -V -q=0