Bladeren bron

Merge branch 'feature/apt-dpkg-comm'

David Kalnischkies 10 jaren geleden
bovenliggende
commit
0a7370ca91
28 gewijzigde bestanden met toevoegingen van 703 en 246 verwijderingen
  1. 63 2
      apt-pkg/algorithms.cc
  2. 9 1
      apt-pkg/algorithms.h
  3. 441 76
      apt-pkg/deb/dpkgpm.cc
  4. 8 4
      apt-pkg/deb/dpkgpm.h
  5. 9 4
      apt-pkg/packagemanager.cc
  6. 1 1
      apt-pkg/packagemanager.h
  7. 2 1
      apt-pkg/statechanges.cc
  8. 7 77
      doc/apt.conf.5.xml
  9. 3 1
      doc/external-installation-planner-protocol.txt
  10. 4 4
      test/integration/test-allow-scores-for-all-dependency-types
  11. 9 1
      test/integration/test-apt-get-autoremove
  12. 1 1
      test/integration/test-apt-progress-fd-error
  13. 1 1
      test/integration/test-bug-673536-pre-depends-breaks-loop
  14. 51 35
      test/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch
  15. 3 3
      test/integration/test-bug-735967-lib32-to-i386-unavailable
  16. 8 8
      test/integration/test-bug-740843-versioned-up-down-breaks
  17. 2 2
      test/integration/test-bug-lp1562402-nomark-removals-as-keep
  18. 2 2
      test/integration/test-bug-multiarch-upgrade
  19. 49 0
      test/integration/test-crossgrades
  20. 1 1
      test/integration/test-dpkg-assert-multi-arch
  21. 3 0
      test/integration/test-essential-force-loopbreak
  22. 2 5
      test/integration/test-external-installation-planner-protocol
  23. 1 1
      test/integration/test-ignore-provides-if-versioned-breaks
  24. 1 1
      test/integration/test-ignore-provides-if-versioned-conflicts
  25. 6 2
      test/integration/test-no-fds-leaked-to-maintainer-scripts
  26. 4 4
      test/integration/test-prevent-markinstall-multiarch-same-versionscrew
  27. 4 0
      test/integration/test-ubuntu-bug-761175-remove-purge
  28. 8 8
      test/integration/test-very-tight-loop-configure-with-unpacking-new-packages

+ 63 - 2
apt-pkg/algorithms.cc

@@ -25,22 +25,29 @@
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/cacheiterators.h>
 #include <apt-pkg/cacheiterators.h>
 #include <apt-pkg/prettyprinters.h>
 #include <apt-pkg/prettyprinters.h>
+#include <apt-pkg/dpkgpm.h>
 
 
 #include <string.h>
 #include <string.h>
 #include <string>
 #include <string>
 #include <cstdlib>
 #include <cstdlib>
 #include <iostream>
 #include <iostream>
+#include <utility>
 
 
 #include <apti18n.h>
 #include <apti18n.h>
 									/*}}}*/
 									/*}}}*/
 using namespace std;
 using namespace std;
 
 
+class APT_HIDDEN pkgSimulatePrivate
+{
+public:
+   std::vector<pkgDPkgPM::Item> List;
+};
 // Simulate::Simulate - Constructor					/*{{{*/
 // Simulate::Simulate - Constructor					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* The legacy translations here of input Pkg iterators is obsolete, 
 /* The legacy translations here of input Pkg iterators is obsolete, 
    this is not necessary since the pkgCaches are fully shared now. */
    this is not necessary since the pkgCaches are fully shared now. */
 pkgSimulate::pkgSimulate(pkgDepCache *Cache) : pkgPackageManager(Cache),
 pkgSimulate::pkgSimulate(pkgDepCache *Cache) : pkgPackageManager(Cache),
-		            d(NULL), iPolicy(Cache),
+		            d(new pkgSimulatePrivate()), iPolicy(Cache),
 			    Sim(&Cache->GetCache(),&iPolicy),
 			    Sim(&Cache->GetCache(),&iPolicy),
 			    group(Sim)
 			    group(Sim)
 {
 {
@@ -58,6 +65,7 @@ pkgSimulate::pkgSimulate(pkgDepCache *Cache) : pkgPackageManager(Cache),
 pkgSimulate::~pkgSimulate()
 pkgSimulate::~pkgSimulate()
 {
 {
    delete[] Flags;
    delete[] Flags;
+   delete d;
 }
 }
 									/*}}}*/
 									/*}}}*/
 // Simulate::Describe - Describe a package				/*{{{*/
 // Simulate::Describe - Describe a package				/*{{{*/
@@ -90,7 +98,14 @@ void pkgSimulate::Describe(PkgIterator Pkg,ostream &out,bool Current,bool Candid
 // Simulate::Install - Simulate unpacking of a package			/*{{{*/
 // Simulate::Install - Simulate unpacking of a package			/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
-bool pkgSimulate::Install(PkgIterator iPkg,string /*File*/)
+bool pkgSimulate::Install(PkgIterator iPkg,string File)
+{
+   if (iPkg.end() || File.empty())
+      return false;
+   d->List.emplace_back(pkgDPkgPM::Item::Install, iPkg, File);
+   return true;
+}
+bool pkgSimulate::RealInstall(PkgIterator iPkg,string /*File*/)
 {
 {
    // Adapt the iterator
    // Adapt the iterator
    PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
    PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
@@ -137,6 +152,13 @@ bool pkgSimulate::Install(PkgIterator iPkg,string /*File*/)
    install the package.. For some investigations it may be necessary 
    install the package.. For some investigations it may be necessary 
    however. */
    however. */
 bool pkgSimulate::Configure(PkgIterator iPkg)
 bool pkgSimulate::Configure(PkgIterator iPkg)
+{
+   if (iPkg.end())
+      return false;
+   d->List.emplace_back(pkgDPkgPM::Item::Configure, iPkg);
+   return true;
+}
+bool pkgSimulate::RealConfigure(PkgIterator iPkg)
 {
 {
    // Adapt the iterator
    // Adapt the iterator
    PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
    PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
@@ -187,6 +209,13 @@ bool pkgSimulate::Configure(PkgIterator iPkg)
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* */
 /* */
 bool pkgSimulate::Remove(PkgIterator iPkg,bool Purge)
 bool pkgSimulate::Remove(PkgIterator iPkg,bool Purge)
+{
+   if (iPkg.end())
+      return false;
+   d->List.emplace_back(Purge ? pkgDPkgPM::Item::Purge : pkgDPkgPM::Item::Remove, iPkg);
+   return true;
+}
+bool pkgSimulate::RealRemove(PkgIterator iPkg,bool Purge)
 {
 {
    // Adapt the iterator
    // Adapt the iterator
    PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
    PkgIterator Pkg = Sim.FindPkg(iPkg.Name(), iPkg.Arch());
@@ -232,6 +261,38 @@ void pkgSimulate::ShortBreaks()
    cout << ']' << endl;
    cout << ']' << endl;
 }
 }
 									/*}}}*/
 									/*}}}*/
+bool pkgSimulate::Go2(APT::Progress::PackageManager *)			/*{{{*/
+{
+   if (pkgDPkgPM::ExpandPendingCalls(d->List, Cache) == false)
+      return false;
+   for (auto && I : d->List)
+      switch (I.Op)
+      {
+	 case pkgDPkgPM::Item::Install:
+	    if (RealInstall(I.Pkg, I.File) == false)
+	       return false;
+	    break;
+	 case pkgDPkgPM::Item::Configure:
+	    if (RealConfigure(I.Pkg) == false)
+	       return false;
+	    break;
+	 case pkgDPkgPM::Item::Remove:
+	    if (RealRemove(I.Pkg, false) == false)
+	       return false;
+	    break;
+	 case pkgDPkgPM::Item::Purge:
+	    if (RealRemove(I.Pkg, true) == false)
+	       return false;
+	    break;
+	 case pkgDPkgPM::Item::ConfigurePending:
+	 case pkgDPkgPM::Item::TriggersPending:
+	 case pkgDPkgPM::Item::RemovePending:
+	 case pkgDPkgPM::Item::PurgePending:
+	    return _error->Error("Internal error, simulation encountered unexpected pending item");
+      }
+   return true;
+}
+									/*}}}*/
 // ApplyStatus - Adjust for non-ok packages				/*{{{*/
 // ApplyStatus - Adjust for non-ok packages				/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
 /* We attempt to change the state of the all packages that have failed
 /* We attempt to change the state of the all packages that have failed

+ 9 - 1
apt-pkg/algorithms.h

@@ -52,9 +52,10 @@ using std::ostream;
 #endif
 #endif
 
 
 
 
+class pkgSimulatePrivate;
 class pkgSimulate : public pkgPackageManager				/*{{{*/
 class pkgSimulate : public pkgPackageManager				/*{{{*/
 {
 {
-   void * const d;
+   pkgSimulatePrivate * const d;
    protected:
    protected:
 
 
    class Policy : public pkgDepCache::Policy
    class Policy : public pkgDepCache::Policy
@@ -81,9 +82,16 @@ class pkgSimulate : public pkgPackageManager				/*{{{*/
    virtual bool Configure(PkgIterator Pkg) APT_OVERRIDE;
    virtual bool Configure(PkgIterator Pkg) APT_OVERRIDE;
    virtual bool Remove(PkgIterator Pkg,bool Purge) APT_OVERRIDE;
    virtual bool Remove(PkgIterator Pkg,bool Purge) APT_OVERRIDE;
 
 
+   // FIXME: trick to avoid ABI break for virtual reimplementation; fix on next ABI break
+public:
+   APT_HIDDEN bool Go2(APT::Progress::PackageManager * progress);
+
 private:
 private:
    APT_HIDDEN void ShortBreaks();
    APT_HIDDEN void ShortBreaks();
    APT_HIDDEN void Describe(PkgIterator iPkg,std::ostream &out,bool Current,bool Candidate);
    APT_HIDDEN void Describe(PkgIterator iPkg,std::ostream &out,bool Current,bool Candidate);
+   APT_HIDDEN bool RealInstall(PkgIterator Pkg,std::string File);
+   APT_HIDDEN bool RealConfigure(PkgIterator Pkg);
+   APT_HIDDEN bool RealRemove(PkgIterator Pkg,bool Purge);
 
 
    public:
    public:
 
 

+ 441 - 76
apt-pkg/deb/dpkgpm.cc

@@ -19,9 +19,11 @@
 #include <apt-pkg/install-progress.h>
 #include <apt-pkg/install-progress.h>
 #include <apt-pkg/packagemanager.h>
 #include <apt-pkg/packagemanager.h>
 #include <apt-pkg/strutl.h>
 #include <apt-pkg/strutl.h>
+#include <apt-pkg/statechanges.h>
 #include <apt-pkg/cacheiterators.h>
 #include <apt-pkg/cacheiterators.h>
 #include <apt-pkg/macros.h>
 #include <apt-pkg/macros.h>
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/pkgcache.h>
+#include <apt-pkg/version.h>
 
 
 #include <errno.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <fcntl.h>
@@ -36,6 +38,8 @@
 #include <sys/stat.h>
 #include <sys/stat.h>
 #include <sys/time.h>
 #include <sys/time.h>
 #include <sys/wait.h>
 #include <sys/wait.h>
+#include <sys/types.h>
+#include <dirent.h>
 #include <termios.h>
 #include <termios.h>
 #include <time.h>
 #include <time.h>
 #include <unistd.h>
 #include <unistd.h>
@@ -47,7 +51,9 @@
 #include <map>
 #include <map>
 #include <set>
 #include <set>
 #include <string>
 #include <string>
+#include <type_traits>
 #include <utility>
 #include <utility>
+#include <unordered_set>
 #include <vector>
 #include <vector>
 #include <sstream>
 #include <sstream>
 #include <numeric>
 #include <numeric>
@@ -205,6 +211,14 @@ pkgCache::VerIterator FindNowVersion(const pkgCache::PkgIterator &Pkg)
    return Ver;
    return Ver;
 }
 }
 									/*}}}*/
 									/*}}}*/
+static pkgCache::VerIterator FindToBeRemovedVersion(pkgCache::PkgIterator const &Pkg)/*{{{*/
+{
+   auto const PV = Pkg.CurrentVer();
+   if (PV.end() == false)
+      return PV;
+   return FindNowVersion(Pkg);
+}
+									/*}}}*/
 
 
 // DPkgPM::pkgDPkgPM - Constructor					/*{{{*/
 // DPkgPM::pkgDPkgPM - Constructor					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
@@ -656,27 +670,23 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
       // if there are multiple pkgs dpkg would send us a full pkgname:arch
       // if there are multiple pkgs dpkg would send us a full pkgname:arch
       pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname);
       pkgCache::GrpIterator Grp = Cache.FindGrp(pkgname);
       if (Grp.end() == false)
       if (Grp.end() == false)
-      {
-	 pkgCache::PkgIterator P = Grp.PackageList();
-	 for (; P.end() != true; P = Grp.NextPkg(P))
-	 {
+	 for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
 	    if(Cache[P].Keep() == false || Cache[P].ReInstall() == true)
 	    if(Cache[P].Keep() == false || Cache[P].ReInstall() == true)
 	    {
 	    {
-	       pkgname = P.FullName();
+	       auto fullname = P.FullName();
+	       if (Cache[P].Delete() && PackageOps[fullname].size() <= PackageOpsDone[fullname])
+		  continue;
+	       pkgname = std::move(fullname);
 	       break;
 	       break;
 	    }
 	    }
-	 }
-      }
    }
    }
 
 
-   const char* const pkg = pkgname.c_str();
-   std::string short_pkgname = StringSplit(pkgname, ":")[0];
    std::string arch = "";
    std::string arch = "";
    if (pkgname.find(":") != string::npos)
    if (pkgname.find(":") != string::npos)
       arch = StringSplit(pkgname, ":")[1];
       arch = StringSplit(pkgname, ":")[1];
    std::string i18n_pkgname = pkgname;
    std::string i18n_pkgname = pkgname;
    if (arch.size() != 0)
    if (arch.size() != 0)
-      strprintf(i18n_pkgname, "%s (%s)", short_pkgname.c_str(), arch.c_str());
+      strprintf(i18n_pkgname, "%s (%s)", StringSplit(pkgname, ":")[0].c_str(), arch.c_str());
 
 
    // 'processing' from dpkg looks like
    // 'processing' from dpkg looks like
    // 'processing: action: pkg'
    // 'processing: action: pkg'
@@ -706,21 +716,21 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
 
 
    if (prefix == "status")
    if (prefix == "status")
    {
    {
-      std::vector<struct DpkgState> &states = PackageOps[pkg];
+      std::vector<struct DpkgState> &states = PackageOps[pkgname];
       if (action == "triggers-pending")
       if (action == "triggers-pending")
       {
       {
 	 if (Debug == true)
 	 if (Debug == true)
-	    std::clog << "(parsed from dpkg) pkg: " << short_pkgname
+	    std::clog << "(parsed from dpkg) pkg: " << pkgname
 	       << " action: " << action << " (prefix 2 to "
 	       << " action: " << action << " (prefix 2 to "
-	       << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl;
+	       << PackageOpsDone[pkgname] << " of " << states.size() << ")" << endl;
 
 
 	 states.insert(states.begin(), {"installed", N_("Installed %s")});
 	 states.insert(states.begin(), {"installed", N_("Installed %s")});
 	 states.insert(states.begin(), {"half-configured", N_("Configuring %s")});
 	 states.insert(states.begin(), {"half-configured", N_("Configuring %s")});
 	 PackagesTotal += 2;
 	 PackagesTotal += 2;
       }
       }
-      else if(PackageOpsDone[pkg] < states.size())
+      else if(PackageOpsDone[pkgname] < states.size())
       {
       {
-	 char const * next_action = states[PackageOpsDone[pkg]].state;
+	 char const * next_action = states[PackageOpsDone[pkgname]].state;
 	 if (next_action)
 	 if (next_action)
 	 {
 	 {
 	    /*
 	    /*
@@ -737,24 +747,52 @@ void pkgDPkgPM::ProcessDpkgStatusLine(char *line)
 	    }
 	    }
 	    */
 	    */
 	    if (Debug == true)
 	    if (Debug == true)
-	       std::clog << "(parsed from dpkg) pkg: " << short_pkgname
+	       std::clog << "(parsed from dpkg) pkg: " << pkgname
 		  << " action: " << action << " (expected: '" << next_action << "' "
 		  << " action: " << action << " (expected: '" << next_action << "' "
-		  << PackageOpsDone[pkg] << " of " << states.size() << ")" << endl;
+		  << PackageOpsDone[pkgname] << " of " << states.size() << ")" << endl;
 
 
 	    // check if the package moved to the next dpkg state
 	    // check if the package moved to the next dpkg state
 	    if(action == next_action)
 	    if(action == next_action)
 	    {
 	    {
 	       // only read the translation if there is actually a next action
 	       // only read the translation if there is actually a next action
-	       char const * const translation = _(states[PackageOpsDone[pkg]].str);
+	       char const * const translation = _(states[PackageOpsDone[pkgname]].str);
 
 
 	       // we moved from one dpkg state to a new one, report that
 	       // we moved from one dpkg state to a new one, report that
-	       ++PackageOpsDone[pkg];
+	       ++PackageOpsDone[pkgname];
 	       ++PackagesDone;
 	       ++PackagesDone;
 
 
 	       std::string msg;
 	       std::string msg;
 	       strprintf(msg, translation, i18n_pkgname.c_str());
 	       strprintf(msg, translation, i18n_pkgname.c_str());
 	       d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
 	       d->progress->StatusChanged(pkgname, PackagesDone, PackagesTotal, msg);
 	    }
 	    }
+	    else if (action == "unpacked" && strcmp(next_action, "config-files") == 0)
+	    {
+	       // in a crossgrade what looked like a remove first is really an unpack over it
+	       ++PackageOpsDone[pkgname];
+	       ++PackagesDone;
+
+	       auto const Pkg = Cache.FindPkg(pkgname);
+	       if (likely(Pkg.end() == false))
+	       {
+		  auto const Grp = Pkg.Group();
+		  if (likely(Grp.end() == false))
+		  {
+		     for (auto P = Grp.PackageList(); P.end() != true; P = Grp.NextPkg(P))
+			if(Cache[P].Install())
+			{
+			   auto && Ops = PackageOps[P.FullName()];
+			   auto const unpackOp = std::find_if(Ops.cbegin(), Ops.cend(), [](DpkgState const &s) { return strcmp(s.state, "unpacked") == 0; });
+			   if (unpackOp != Ops.cend())
+			   {
+			      auto const skipped = std::distance(Ops.cbegin(), unpackOp);
+			      PackagesDone += skipped;
+			      PackageOpsDone[P.FullName()] += skipped;
+			      break;
+			   }
+			}
+		  }
+	       }
+	    }
 	 }
 	 }
       }
       }
    }
    }
@@ -1221,6 +1259,34 @@ void pkgDPkgPM::StopPtyMagic()						/*{{{*/
       d->master = -1;
       d->master = -1;
    }
    }
 }
 }
+									/*}}}*/
+static void cleanUpTmpDir(char * const tmpdir)				/*{{{*/
+{
+   if (tmpdir == nullptr)
+      return;
+   DIR * const D = opendir(tmpdir);
+   if (D == nullptr)
+      _error->Errno("opendir", _("Unable to read %s"), tmpdir);
+   else
+   {
+      auto const dfd = dirfd(D);
+      for (struct dirent *Ent = readdir(D); Ent != nullptr; Ent = readdir(D))
+      {
+	 if (Ent->d_name[0] == '.')
+	    continue;
+#ifdef _DIRENT_HAVE_D_TYPE
+	 if (unlikely(Ent->d_type != DT_LNK && Ent->d_type != DT_UNKNOWN))
+	    continue;
+#endif
+	 if (unlikely(unlinkat(dfd, Ent->d_name, 0) != 0))
+	    break;
+      }
+      closedir(D);
+      rmdir(tmpdir);
+   }
+   free(tmpdir);
+}
+									/*}}}*/
 
 
 // DPkgPM::Go - Run the sequence					/*{{{*/
 // DPkgPM::Go - Run the sequence					/*{{{*/
 // ---------------------------------------------------------------------
 // ---------------------------------------------------------------------
@@ -1231,8 +1297,59 @@ void pkgDPkgPM::StopPtyMagic()						/*{{{*/
  * through to human readable (and i10n-able)
  * through to human readable (and i10n-able)
  * names and calculates a percentage for each step.
  * names and calculates a percentage for each step.
  */
  */
+static bool ItemIsEssential(pkgDPkgPM::Item const &I)
+{
+   static auto const cachegen = _config->Find("pkgCacheGen::Essential");
+   if (cachegen == "none" || cachegen == "native")
+      return true;
+   if (unlikely(I.Pkg.end()))
+      return true;
+   return (I.Pkg->Flags & pkgCache::Flag::Essential) != 0;
+}
+bool pkgDPkgPM::ExpandPendingCalls(std::vector<Item> &List, pkgDepCache &Cache)
+{
+   {
+      std::unordered_set<decltype(pkgCache::Package::ID)> alreadyRemoved;
+      for (auto && I : List)
+	 if (I.Op == Item::Remove || I.Op == Item::Purge)
+	    alreadyRemoved.insert(I.Pkg->ID);
+      std::remove_reference<decltype(List)>::type AppendList;
+      for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
+	 if (Cache[Pkg].Delete() && alreadyRemoved.insert(Pkg->ID).second == true)
+	    AppendList.emplace_back(Cache[Pkg].Purge() ? Item::Purge : Item::Remove, Pkg);
+      std::move(AppendList.begin(), AppendList.end(), std::back_inserter(List));
+   }
+   {
+      std::unordered_set<decltype(pkgCache::Package::ID)> alreadyConfigured;
+      for (auto && I : List)
+	 if (I.Op == Item::Configure)
+	    alreadyConfigured.insert(I.Pkg->ID);
+      std::remove_reference<decltype(List)>::type AppendList;
+      for (auto && I : List)
+	 if (I.Op == Item::Install && alreadyConfigured.insert(I.Pkg->ID).second == true)
+	    AppendList.emplace_back(Item::Configure, I.Pkg);
+      for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
+	 if (Pkg.State() == pkgCache::PkgIterator::NeedsConfigure && alreadyConfigured.insert(Pkg->ID).second == true)
+	    AppendList.emplace_back(Item::Configure, Pkg);
+      std::move(AppendList.begin(), AppendList.end(), std::back_inserter(List));
+   }
+   return true;
+}
 bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 {
 {
+   // explicitely remove&configure everything for hookscripts and progress building
+   // we need them only temporarily through, so keep the length and erase afterwards
+   decltype(List)::const_iterator::difference_type explicitIdx =
+      std::distance(List.cbegin(), List.cend());
+   ExpandPendingCalls(List, Cache);
+
+   auto const StripAlreadyDoneFromPending = [&](APT::VersionVector & Pending) {
+      Pending.erase(std::remove_if(Pending.begin(), Pending.end(), [&](pkgCache::VerIterator const &Ver) {
+	    auto const PN = Ver.ParentPkg().FullName();
+	    return PackageOps[PN].size() <= PackageOpsDone[PN];
+	 }), Pending.end());
+   };
+
    pkgPackageManager::SigINTStop = false;
    pkgPackageManager::SigINTStop = false;
    d->progress = progress;
    d->progress = progress;
 
 
@@ -1254,7 +1371,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
       OSArgMax = 32*1024;
       OSArgMax = 32*1024;
    OSArgMax -= EnvironmentSize() - 2*1024;
    OSArgMax -= EnvironmentSize() - 2*1024;
    unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes", OSArgMax);
    unsigned int const MaxArgBytes = _config->FindI("Dpkg::MaxArgBytes", OSArgMax);
-   bool const NoTriggers = _config->FindB("DPkg::NoTriggers", false);
+   bool const NoTriggers = _config->FindB("DPkg::NoTriggers", true);
 
 
    if (RunScripts("DPkg::Pre-Invoke") == false)
    if (RunScripts("DPkg::Pre-Invoke") == false)
       return false;
       return false;
@@ -1267,28 +1384,173 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
    if (noopDPkgInvocation == false)
    if (noopDPkgInvocation == false)
       Cache.writeStateFile(NULL);
       Cache.writeStateFile(NULL);
 
 
-   decltype(List)::const_iterator::difference_type const notconfidx =
-      _config->FindB("Dpkg::ExplicitLastConfigure", false) ? std::numeric_limits<decltype(notconfidx)>::max() :
-      std::distance(List.cbegin(), std::find_if_not(List.crbegin(), List.crend(), [](Item const &i) { return i.Op == Item::Configure; }).base());
-
-   // support subpressing of triggers processing for special
-   // cases like d-i that runs the triggers handling manually
-   bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false);
-   bool const ConfigurePending = _config->FindB("DPkg::ConfigurePending", true);
-   if (ConfigurePending)
-      List.push_back(Item(Item::ConfigurePending, PkgIterator()));
+   bool dpkg_recursive_install = _config->FindB("dpkg::install::recursive", false);
+   if (_config->FindB("dpkg::install::recursive::force", false) == false)
+   {
+      // dpkg uses a sorted treewalk since that version which enables the workaround to work
+      auto const dpkgpkg = Cache.FindPkg("dpkg");
+      if (likely(dpkgpkg.end() == false && dpkgpkg->CurrentVer != 0))
+	 dpkg_recursive_install = Cache.VS().CmpVersion("1.18.5", dpkgpkg.CurrentVer().VerStr()) <= 0;
+   }
+   // no point in doing this dance for a handful of packages only
+   unsigned int const dpkg_recursive_install_min = _config->FindB("dpkg::install::recursive::minimum", 5);
+   // FIXME: workaround for dpkg bug, see our ./test-bug-740843-versioned-up-down-breaks test
+   bool const dpkg_recursive_install_numbered = _config->FindB("dpkg::install::recursive::numbered", true);
 
 
    // for the progress
    // for the progress
    BuildPackagesProgressMap();
    BuildPackagesProgressMap();
 
 
-   if (notconfidx != std::numeric_limits<decltype(notconfidx)>::max())
+   APT::StateChanges approvedStates;
+   if (_config->FindB("dpkg::selection::remove::approved", true))
    {
    {
-      if (ConfigurePending)
-	 List.erase(std::next(List.begin(), notconfidx), std::prev(List.end()));
-      else
-	 List.erase(std::next(List.begin(), notconfidx), List.end());
+      for (auto && I : List)
+	 if (I.Op == Item::Purge)
+	    approvedStates.Purge(FindToBeRemovedVersion(I.Pkg));
+	 else if (I.Op == Item::Remove)
+	    approvedStates.Remove(FindToBeRemovedVersion(I.Pkg));
+   }
+
+   // Skip removes if we install another architecture of this package soon (crossgrade)
+   // We can't just skip them all the time as it could be an ordering requirement [of another package]
+   if ((approvedStates.Remove().empty() == false || approvedStates.Purge().empty() == false) &&
+	 _config->FindB("dpkg::remove::crossgrade::implicit", true) == true)
+   {
+      std::unordered_set<decltype(pkgCache::Package::ID)> crossgraded;
+      std::vector<std::pair<Item*, std::string>> toCrossgrade;
+      auto const PlanedEnd = std::next(List.begin(), explicitIdx);
+      for (auto I = List.begin(); I != PlanedEnd; ++I)
+      {
+	 if (I->Op != Item::Remove && I->Op != Item::Purge)
+	    continue;
+
+	 auto const Grp = I->Pkg.Group();
+	 size_t installedInstances = 0;
+	 for (auto Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
+	    if (Pkg->CurrentVer != 0 || Cache[Pkg].Install())
+	       ++installedInstances;
+	 if (installedInstances == 2)
+	 {
+	    auto const FirstInstall = std::find_if_not(I, List.end(),
+		  [](Item const &i) { return i.Op == Item::Remove || i.Op == Item::Purge; });
+	    auto const LastInstall = std::find_if_not(FirstInstall, List.end(),
+		  [](Item const &i) { return i.Op == Item::Install; });
+	    auto const crosser = std::find_if(FirstInstall, LastInstall,
+		  [&I](Item const &i) { return i.Pkg->Group == I->Pkg->Group; });
+	    if (crosser != LastInstall)
+	    {
+	       crossgraded.insert(I->Pkg->ID);
+	       toCrossgrade.emplace_back(&(*I), crosser->Pkg.FullName());
+	    }
+	 }
+      }
+      for (auto I = PlanedEnd; I != List.end(); ++I)
+      {
+	 if (I->Op != Item::Remove && I->Op != Item::Purge)
+	    continue;
+
+	 auto const Grp = I->Pkg.Group();
+	 for (auto Pkg = Grp.PackageList(); Pkg.end() == false; Pkg = Grp.NextPkg(Pkg))
+	 {
+	    if (Pkg == I->Pkg || Cache[Pkg].Install() == false)
+	       continue;
+	    toCrossgrade.emplace_back(&(*I), Pkg.FullName());
+	    break;
+	 }
+      }
+      for (auto C : toCrossgrade)
+      {
+	 // we never do purges on packages which are crossgraded, even if "requested"
+	 if (C.first->Op == Item::Purge)
+	 {
+	    C.first->Op = Item::Remove; // crossgrades should never be purged
+	    auto && Purges = approvedStates.Purge();
+	    auto const Ver = std::find_if(
+#if __GNUC__ >= 5 || (__GNUC_MINOR__ >= 9 && __GNUC__ >= 4)
+	       Purges.cbegin(), Purges.cend(),
+#else
+	       Purges.begin(), Purges.end(),
+#endif
+	       [&C](pkgCache::VerIterator const &V) { return V.ParentPkg() == C.first->Pkg; });
+	    approvedStates.Remove(*Ver);
+	    Purges.erase(Ver);
+	    auto && RemOp = PackageOps[C.first->Pkg.FullName()];
+	    if (RemOp.size() == 5)
+	    {
+	       RemOp.erase(std::next(RemOp.begin(), 3), RemOp.end());
+	       PackagesTotal -= 2;
+	    }
+	    else
+	    _error->Warning("Unexpected amount of planned ops for package %s: %lu", C.first->Pkg.FullName().c_str(), RemOp.size());
+	 }
+      }
+      if (crossgraded.empty() == false)
+      {
+	 auto const oldsize = List.size();
+	 List.erase(std::remove_if(List.begin(), PlanedEnd,
+	       [&crossgraded](Item const &i){
+		  return (i.Op == Item::Remove || i.Op == Item::Purge) &&
+		     crossgraded.find(i.Pkg->ID) != crossgraded.end();
+	       }), PlanedEnd);
+	 explicitIdx -= (oldsize - List.size());
+      }
    }
    }
 
 
+   APT::StateChanges currentStates;
+   if (_config->FindB("dpkg::selection::current::saveandrestore", true))
+   {
+      for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
+	 if (Pkg->CurrentVer == 0)
+	    continue;
+	 else if (Pkg->SelectedState == pkgCache::State::Purge)
+	    currentStates.Purge(FindToBeRemovedVersion(Pkg));
+	 else if (Pkg->SelectedState == pkgCache::State::DeInstall)
+	    currentStates.Remove(FindToBeRemovedVersion(Pkg));
+      if (currentStates.empty() == false)
+      {
+	 APT::StateChanges cleanStates;
+	 for (auto && P: currentStates.Remove())
+	    cleanStates.Install(P);
+	 for (auto && P: currentStates.Purge())
+	    cleanStates.Install(P);
+	 if (cleanStates.Save(false) == false)
+	    return _error->Error("Couldn't clean the currently selected dpkg states");
+      }
+   }
+
+   if (_config->FindB("dpkg::selection::remove::approved", true))
+   {
+      if (approvedStates.Save(false) == false)
+      {
+	 _error->Error("Couldn't record the approved state changes as dpkg selection states");
+	 if (currentStates.Save(false) == false)
+	    _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
+	 return false;
+      }
+
+      List.erase(std::next(List.begin(), explicitIdx), List.end());
+
+      std::vector<bool> toBeRemoved(Cache.Head().PackageCount, false);
+      for (auto && I: approvedStates.Remove())
+	 toBeRemoved[I.ParentPkg()->ID] = true;
+      for (auto && I: approvedStates.Purge())
+	 toBeRemoved[I.ParentPkg()->ID] = true;
+
+      for (auto && I: List)
+	 if (I.Op == Item::Remove || I.Op == Item::Purge)
+	    toBeRemoved[I.Pkg->ID] = false;
+
+      if (std::find(toBeRemoved.begin(), toBeRemoved.end(), true) != toBeRemoved.end())
+	 List.emplace_back(Item::RemovePending, pkgCache::PkgIterator());
+      if (approvedStates.Purge().empty() == false)
+	 List.emplace_back(Item::PurgePending, pkgCache::PkgIterator());
+
+      // support subpressing of triggers processing for special
+      // cases like d-i that runs the triggers handling manually
+      if (_config->FindB("DPkg::ConfigurePending", true))
+	 List.emplace_back(Item::ConfigurePending, pkgCache::PkgIterator());
+   }
+   bool const TriggersPending = _config->FindB("DPkg::TriggersPending", false);
+
    d->stdin_is_dev_null = false;
    d->stdin_is_dev_null = false;
 
 
    // create log
    // create log
@@ -1303,7 +1565,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
    d->progress->Start(d->master);
    d->progress->Start(d->master);
 
 
    // this loop is runs once per dpkg operation
    // this loop is runs once per dpkg operation
-   vector<Item>::const_iterator I = List.begin();
+   vector<Item>::const_iterator I = List.cbegin();
    while (I != List.end())
    while (I != List.end())
    {
    {
       // Do all actions with the same Op in one run
       // Do all actions with the same Op in one run
@@ -1320,9 +1582,10 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 	       continue;
 	       continue;
 	    break;
 	    break;
 	 }
 	 }
+      else if (J->Op == Item::Remove || J->Op == Item::Purge)
+	 J = std::find_if(J, List.cend(), [](Item const &I) { return I.Op != Item::Remove && I.Op != Item::Purge; });
       else
       else
-	 for (; J != List.end() && J->Op == I->Op; ++J)
-	    /* nothing */;
+	 J = std::find_if(J, List.cend(), [&J](Item const &I) { return I.Op != J->Op; });
 
 
       auto const size = (J - I) + 10;
       auto const size = (J - I) + 10;
 
 
@@ -1346,18 +1609,20 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
       ADDARG(status_fd_buf);
       ADDARG(status_fd_buf);
       unsigned long const Op = I->Op;
       unsigned long const Op = I->Op;
 
 
+      if (NoTriggers == true && I->Op != Item::TriggersPending &&
+	  I->Op != Item::ConfigurePending)
+      {
+	 ADDARGC("--no-triggers");
+      }
+
       switch (I->Op)
       switch (I->Op)
       {
       {
 	 case Item::Remove:
 	 case Item::Remove:
-	 ADDARGC("--force-depends");
-	 ADDARGC("--force-remove-essential");
-	 ADDARGC("--remove");
-	 break;
-
 	 case Item::Purge:
 	 case Item::Purge:
 	 ADDARGC("--force-depends");
 	 ADDARGC("--force-depends");
-	 ADDARGC("--force-remove-essential");
-	 ADDARGC("--purge");
+	 if (std::any_of(I, J, ItemIsEssential))
+	    ADDARGC("--force-remove-essential");
+	 ADDARGC("--remove");
 	 break;
 	 break;
 
 
 	 case Item::Configure:
 	 case Item::Configure:
@@ -1374,34 +1639,96 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 	 ADDARGC("--pending");
 	 ADDARGC("--pending");
 	 break;
 	 break;
 
 
+	 case Item::RemovePending:
+	 ADDARGC("--remove");
+	 ADDARGC("--pending");
+	 break;
+
+	 case Item::PurgePending:
+	 ADDARGC("--purge");
+	 ADDARGC("--pending");
+	 break;
+
 	 case Item::Install:
 	 case Item::Install:
 	 ADDARGC("--unpack");
 	 ADDARGC("--unpack");
 	 ADDARGC("--auto-deconfigure");
 	 ADDARGC("--auto-deconfigure");
 	 break;
 	 break;
       }
       }
 
 
-      if (NoTriggers == true && I->Op != Item::TriggersPending &&
-	  I->Op != Item::ConfigurePending)
-      {
-	 ADDARGC("--no-triggers");
-      }
-#undef ADDARGC
+      char * tmpdir_to_free = nullptr;
 
 
       // Write in the file or package names
       // Write in the file or package names
       if (I->Op == Item::Install)
       if (I->Op == Item::Install)
       {
       {
-	 for (;I != J && Size < MaxArgBytes; ++I)
+	 auto const installsToDo = J - I;
+	 if (dpkg_recursive_install == true && dpkg_recursive_install_min < installsToDo)
 	 {
 	 {
-	    if (I->File[0] != '/')
-	       return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
-	    Args.push_back(I->File.c_str());
-	    Size += I->File.length();
+	    std::string tmpdir;
+	    strprintf(tmpdir, "%s/apt-dpkg-install-XXXXXX", GetTempDir().c_str());
+	    tmpdir_to_free = strndup(tmpdir.data(), tmpdir.length());
+	    if (mkdtemp(tmpdir_to_free) == nullptr)
+	       return _error->Errno("DPkg::Go", "mkdtemp of %s failed in preparation of calling dpkg unpack", tmpdir_to_free);
+
+	    char p = 1;
+	    for (auto c = installsToDo - 1; (c = c/10) != 0; ++p);
+	    for (unsigned long n = 0; I != J; ++n, ++I)
+	    {
+	       if (I->File[0] != '/')
+		  return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
+	       auto const file = flNotDir(I->File);
+	       std::string linkpath;
+	       if (dpkg_recursive_install_numbered)
+		  strprintf(linkpath, "%s/%.*lu-%s", tmpdir_to_free, p, n, file.c_str());
+	       else
+		  strprintf(linkpath, "%s/%s", tmpdir_to_free, file.c_str());
+	       if (symlink(I->File.c_str(), linkpath.c_str()) != 0)
+		  return _error->Errno("DPkg::Go", "Symlinking %s to %s failed!", I->File.c_str(), linkpath.c_str());
+	    }
+	    ADDARGC("--recursive");
+	    ADDARG(tmpdir_to_free);
+	 }
+	 else
+	 {
+	    for (;I != J && Size < MaxArgBytes; ++I)
+	    {
+	       if (I->File[0] != '/')
+		  return _error->Error("Internal Error, Pathname to install is not absolute '%s'",I->File.c_str());
+	       Args.push_back(I->File.c_str());
+	       Size += I->File.length();
+	    }
+	 }
+      }
+      else if (I->Op == Item::RemovePending)
+      {
+	 ++I;
+	 StripAlreadyDoneFromPending(approvedStates.Remove());
+	 if (approvedStates.Remove().empty())
+	    continue;
+      }
+      else if (I->Op == Item::PurgePending)
+      {
+	 ++I;
+	 // explicit removes of packages without conffiles passthrough the purge states instantly, too.
+	 // Setting these non-installed packages up for purging generates 'unknown pkg' warnings from dpkg
+	 StripAlreadyDoneFromPending(approvedStates.Purge());
+	 if (approvedStates.Purge().empty())
+	    continue;
+	 std::remove_reference<decltype(approvedStates.Remove())>::type approvedRemoves;
+	 std::swap(approvedRemoves, approvedStates.Remove());
+	 // we apply it again here as an explicit remove in the ordering will have cleared the purge state
+	 if (approvedStates.Save(false) == false)
+	 {
+	    _error->Error("Couldn't record the approved purges as dpkg selection states");
+	    if (currentStates.Save(false) == false)
+	       _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
+	    return false;
 	 }
 	 }
+	 std::swap(approvedRemoves, approvedStates.Remove());
       }
       }
       else
       else
       {
       {
 	 string const nativeArch = _config->Find("APT::Architecture");
 	 string const nativeArch = _config->Find("APT::Architecture");
-	 unsigned long const oldSize = I->Op == Item::Configure ? Size : 0;
+	 unsigned long const oldSize = I->Pkg.end() == false ? Size : 0;
 	 for (;I != J && Size < MaxArgBytes; ++I)
 	 for (;I != J && Size < MaxArgBytes; ++I)
 	 {
 	 {
 	    if((*I).Pkg.end() == true)
 	    if((*I).Pkg.end() == true)
@@ -1420,12 +1747,15 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 	    {
 	    {
 	       pkgCache::VerIterator PkgVer;
 	       pkgCache::VerIterator PkgVer;
 	       std::string name = I->Pkg.Name();
 	       std::string name = I->Pkg.Name();
-	       if (Op == Item::Remove || Op == Item::Purge) 
-               {
+	       if (Op == Item::Remove)
+		  PkgVer = I->Pkg.CurrentVer();
+	       else if (Op == Item::Purge)
+	       {
+		  // we purge later with --purge --pending, so if it isn't installed (aka rc-only), skip it here
 		  PkgVer = I->Pkg.CurrentVer();
 		  PkgVer = I->Pkg.CurrentVer();
-                  if(PkgVer.end() == true)
-                     PkgVer = FindNowVersion(I->Pkg);
-               }
+		  if (PkgVer.end() == true)
+		     continue;
+	       }
 	       else
 	       else
 		  PkgVer = Cache[I->Pkg].InstVerIter(Cache);
 		  PkgVer = Cache[I->Pkg].InstVerIter(Cache);
 	       if (strcmp(I->Pkg.Arch(), "none") == 0)
 	       if (strcmp(I->Pkg.Arch(), "none") == 0)
@@ -1443,6 +1773,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 	 if (oldSize == Size)
 	 if (oldSize == Size)
 	    continue;
 	    continue;
       }
       }
+#undef ADDARGC
 #undef ADDARG
 #undef ADDARG
 
 
       J = I;
       J = I;
@@ -1459,6 +1790,7 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
 	 Packages.clear();
 	 Packages.clear();
 	 close(fd[0]);
 	 close(fd[0]);
 	 close(fd[1]);
 	 close(fd[1]);
+	 cleanUpTmpDir(tmpdir_to_free);
 	 continue;
 	 continue;
       }
       }
       Args.push_back(NULL);
       Args.push_back(NULL);
@@ -1594,6 +1926,8 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
       signal(SIGINT,old_SIGINT);
       signal(SIGINT,old_SIGINT);
       signal(SIGHUP,old_SIGHUP);
       signal(SIGHUP,old_SIGHUP);
 
 
+      cleanUpTmpDir(tmpdir_to_free);
+
       if (waitpid_failure == true)
       if (waitpid_failure == true)
       {
       {
 	 strprintf(d->dpkg_error, "Sub-process %s couldn't be waited for.",Args[0]);
 	 strprintf(d->dpkg_error, "Sub-process %s couldn't be waited for.",Args[0]);
@@ -1625,6 +1959,23 @@ bool pkgDPkgPM::Go(APT::Progress::PackageManager *progress)
    StopPtyMagic();
    StopPtyMagic();
    CloseLog();
    CloseLog();
 
 
+   if (d->dpkg_error.empty() == false)
+   {
+      // no point in reseting packages we already completed removal for
+      StripAlreadyDoneFromPending(approvedStates.Remove());
+      StripAlreadyDoneFromPending(approvedStates.Purge());
+      APT::StateChanges undo;
+      auto && undoRem = approvedStates.Remove();
+      std::move(undoRem.begin(), undoRem.end(), std::back_inserter(undo.Install()));
+      auto && undoPur = approvedStates.Purge();
+      std::move(undoPur.begin(), undoPur.end(), std::back_inserter(undo.Install()));
+      approvedStates.clear();
+      if (undo.Save(false) == false)
+	 _error->Error("Couldn't revert dpkg selection for approved remove/purge after an error was encountered!");
+   }
+   if (currentStates.Save(false) == false)
+      _error->Error("Couldn't restore dpkg selection states which were present before this interaction!");
+
    if (pkgPackageManager::SigINTStop)
    if (pkgPackageManager::SigINTStop)
        _error->Warning(_("Operation was interrupted before it could finish"));
        _error->Warning(_("Operation was interrupted before it could finish"));
 
 
@@ -1764,7 +2115,17 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
    // find the package version and source package name
    // find the package version and source package name
    pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
    pkgCache::PkgIterator Pkg = Cache.FindPkg(pkgname);
    if (Pkg.end() == true)
    if (Pkg.end() == true)
-      return;
+   {
+      if (pos == std::string::npos || _config->FindB("dpkg::install::recursive::numbered", true) == false)
+	 return;
+      auto const dash = pkgname.find_first_not_of("0123456789");
+      if (dash == std::string::npos || pkgname[dash] != '-')
+	 return;
+      pkgname.erase(0, dash + 1);
+      Pkg = Cache.FindPkg(pkgname);
+      if (Pkg.end() == true)
+	 return;
+   }
    pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg);
    pkgCache::VerIterator Ver = Cache.GetCandidateVersion(Pkg);
    if (Ver.end() == true)
    if (Ver.end() == true)
       return;
       return;
@@ -1862,20 +2223,24 @@ void pkgDPkgPM::WriteApportReport(const char *pkgpath, const char *errormsg)
    }
    }
 
 
    // log the ordering, see dpkgpm.h and the "Ops" enum there
    // log the ordering, see dpkgpm.h and the "Ops" enum there
-   const char *ops_str[] = {
-      "Install",
-      "Configure",
-      "Remove",
-      "Purge",
-      "ConfigurePending",
-      "TriggersPending",
-   };
    fprintf(report, "AptOrdering:\n");
    fprintf(report, "AptOrdering:\n");
-   for (vector<Item>::iterator I = List.begin(); I != List.end(); ++I)
-      if ((*I).Pkg != NULL)
-         fprintf(report, " %s: %s\n", (*I).Pkg.Name(), ops_str[(*I).Op]);
-      else
-         fprintf(report, " %s: %s\n", "NULL", ops_str[(*I).Op]);
+   for (auto && I : List)
+   {
+      char const * opstr = nullptr;
+      switch (I.Op)
+      {
+	 case Item::Install: opstr = "Install"; break;
+	 case Item::Configure: opstr = "Configure"; break;
+	 case Item::Remove: opstr = "Remove"; break;
+	 case Item::Purge: opstr = "Purge"; break;
+	 case Item::ConfigurePending: opstr = "ConfigurePending"; break;
+	 case Item::TriggersPending: opstr = "TriggersPending"; break;
+	 case Item::RemovePending: opstr = "RemovePending"; break;
+	 case Item::PurgePending: opstr = "PurgePending"; break;
+      }
+      auto const pkgname = I.Pkg.end() ? "NULL" : I.Pkg.FullName();
+      fprintf(report, " %s: %s\n", pkgname.c_str(), opstr);
+   }
 
 
    // attach dmesg log (to learn about segfaults)
    // attach dmesg log (to learn about segfaults)
    if (FileExists("/bin/dmesg"))
    if (FileExists("/bin/dmesg"))

+ 8 - 4
apt-pkg/deb/dpkgpm.h

@@ -76,17 +76,19 @@ class pkgDPkgPM : public pkgPackageManager
    // progress reporting
    // progress reporting
    unsigned int PackagesDone;
    unsigned int PackagesDone;
    unsigned int PackagesTotal;
    unsigned int PackagesTotal;
-  
+
+   public:
    struct Item
    struct Item
    {
    {
-      enum Ops {Install, Configure, Remove, Purge, ConfigurePending, TriggersPending} Op;
+      enum Ops {Install, Configure, Remove, Purge, ConfigurePending, TriggersPending,
+         RemovePending, PurgePending } Op;
       std::string File;
       std::string File;
       PkgIterator Pkg;
       PkgIterator Pkg;
       Item(Ops Op,PkgIterator Pkg,std::string File = "") : Op(Op),
       Item(Ops Op,PkgIterator Pkg,std::string File = "") : Op(Op),
             File(File), Pkg(Pkg) {};
             File(File), Pkg(Pkg) {};
       Item() {};
       Item() {};
-      
    };
    };
+   protected:
    std::vector<Item> List;
    std::vector<Item> List;
 
 
    // Helpers
    // Helpers
@@ -125,7 +127,7 @@ class pkgDPkgPM : public pkgPackageManager
    virtual bool Remove(PkgIterator Pkg,bool Purge = false) APT_OVERRIDE;
    virtual bool Remove(PkgIterator Pkg,bool Purge = false) APT_OVERRIDE;
 
 
    virtual bool Go(APT::Progress::PackageManager *progress) APT_OVERRIDE;
    virtual bool Go(APT::Progress::PackageManager *progress) APT_OVERRIDE;
-   virtual bool Go(int StatusFd=-1) APT_OVERRIDE;
+   APT_DEPRECATED_MSG("Use overload with explicit progress manager") virtual bool Go(int StatusFd=-1) APT_OVERRIDE;
 
 
    virtual void Reset() APT_OVERRIDE;
    virtual void Reset() APT_OVERRIDE;
    
    
@@ -133,6 +135,8 @@ class pkgDPkgPM : public pkgPackageManager
 
 
    pkgDPkgPM(pkgDepCache *Cache);
    pkgDPkgPM(pkgDepCache *Cache);
    virtual ~pkgDPkgPM();
    virtual ~pkgDPkgPM();
+
+   APT_HIDDEN static bool ExpandPendingCalls(std::vector<Item> &List, pkgDepCache &Cache);
 };
 };
 
 
 void SigINT(int sig);
 void SigINT(int sig);

+ 9 - 4
apt-pkg/packagemanager.cc

@@ -315,8 +315,8 @@ bool pkgPackageManager::ConfigureAll()
    if (OList.OrderConfigure() == false)
    if (OList.OrderConfigure() == false)
       return false;
       return false;
 
 
-   std::string const conf = _config->Find("PackageManager::Configure","all");
-   bool const ConfigurePkgs = (conf == "all");
+   std::string const conf = _config->Find("PackageManager::Configure", "smart");
+   bool const ConfigurePkgs = (ImmConfigureAll || conf == "all");
 
 
    // Perform the configuring
    // Perform the configuring
    for (pkgOrderList::iterator I = OList.begin(); I != OList.end(); ++I)
    for (pkgOrderList::iterator I = OList.begin(); I != OList.end(); ++I)
@@ -600,7 +600,7 @@ bool pkgPackageManager::SmartConfigure(PkgIterator Pkg, int const Depth)
 
 
    if (PkgLoop) return true;
    if (PkgLoop) return true;
 
 
-   static std::string const conf = _config->Find("PackageManager::Configure","all");
+   static std::string const conf = _config->Find("PackageManager::Configure", "smart");
    static bool const ConfigurePkgs = (conf == "all" || conf == "smart");
    static bool const ConfigurePkgs = (conf == "all" || conf == "smart");
 
 
    if (List->IsFlag(Pkg,pkgOrderList::Configured))
    if (List->IsFlag(Pkg,pkgOrderList::Configured))
@@ -1148,7 +1148,12 @@ pkgPackageManager::DoInstallPostFork(int statusFd)
 pkgPackageManager::OrderResult 
 pkgPackageManager::OrderResult 
 pkgPackageManager::DoInstallPostFork(APT::Progress::PackageManager *progress)
 pkgPackageManager::DoInstallPostFork(APT::Progress::PackageManager *progress)
 {
 {
-   bool goResult = Go(progress);
+   bool goResult;
+   auto simulation = dynamic_cast<pkgSimulate*>(this);
+   if (simulation == nullptr)
+      goResult = Go(progress);
+   else
+      goResult = simulation->Go2(progress);
    if(goResult == false) 
    if(goResult == false) 
       return Failed;
       return Failed;
    
    

+ 1 - 1
apt-pkg/packagemanager.h

@@ -99,7 +99,7 @@ class pkgPackageManager : protected pkgCache::Namespace
    virtual bool Configure(PkgIterator /*Pkg*/) {return false;};
    virtual bool Configure(PkgIterator /*Pkg*/) {return false;};
    virtual bool Remove(PkgIterator /*Pkg*/,bool /*Purge*/=false) {return false;};
    virtual bool Remove(PkgIterator /*Pkg*/,bool /*Purge*/=false) {return false;};
    virtual bool Go(APT::Progress::PackageManager * /*progress*/) {return true;};
    virtual bool Go(APT::Progress::PackageManager * /*progress*/) {return true;};
-   virtual bool Go(int /*statusFd*/=-1) {return true;};
+   APT_DEPRECATED_MSG("Use overload with explicit progress manager") virtual bool Go(int /*statusFd*/=-1) {return true;};
 
 
    virtual void Reset() {};
    virtual void Reset() {};
 
 

+ 2 - 1
apt-pkg/statechanges.cc

@@ -25,7 +25,8 @@ public:
 #define APT_GETTERSETTER(Name, Container) \
 #define APT_GETTERSETTER(Name, Container) \
 void StateChanges::Name(pkgCache::VerIterator const &Ver) \
 void StateChanges::Name(pkgCache::VerIterator const &Ver) \
 { \
 { \
-   Container.push_back(Ver); \
+   if (Ver.end() == false) \
+      Container.push_back(Ver); \
 }\
 }\
 APT::VersionVector& StateChanges::Name() \
 APT::VersionVector& StateChanges::Name() \
 { \
 { \

+ 7 - 77
doc/apt.conf.5.xml

@@ -904,84 +904,14 @@ APT::Compressor::rev {
      <listitem><para>These options are passed to &dpkg-buildpackage; when compiling packages;
      <listitem><para>These options are passed to &dpkg-buildpackage; when compiling packages;
      the default is to disable signing and produce all binaries.</para></listitem>
      the default is to disable signing and produce all binaries.</para></listitem>
      </varlistentry>
      </varlistentry>
-   </variablelist>
 
 
-   <refsect2><title>dpkg trigger usage (and related options)</title>
-     <para>APT can call &dpkg; in such a way as to let it make aggressive use of triggers over
-     multiple calls of &dpkg;. Without further options &dpkg; will use triggers once each time it runs.
-     Activating these options can therefore decrease the time needed to perform the
-     install or upgrade. Note that it is intended to activate these options per default in the
-     future, but as it drastically changes the way APT calls &dpkg; it needs a lot more testing.
-     <emphasis>These options are therefore currently experimental and should not be used in
-     production environments.</emphasis> It also breaks progress reporting such that all front-ends will
-     currently stay around half (or more) of the time in the 100% state while it actually configures
-     all packages.</para>
-     <para>Note that it is not guaranteed that APT will support these options or that these options will
-     not cause (big) trouble in the future. If you have understand the current risks and problems with
-     these options, but are brave enough to help testing them, create a new configuration file and test a
-     combination of options. Please report any bugs, problems and improvements you encounter and make sure
-     to note which options you have used in your reports. Asking &dpkg; for help could also be useful for
-     debugging proposes, see e.g. <command>dpkg --audit</command>. A defensive option combination would be
-<literallayout>DPkg::NoTriggers "true";
-PackageManager::Configure "smart";
-DPkg::ConfigurePending "true";
-DPkg::TriggersPending "true";</literallayout></para>
-
-     <variablelist>
-       <varlistentry><term><option>DPkg::NoTriggers</option></term>
-       <listitem><para>Add the no triggers flag to all &dpkg; calls (except the ConfigurePending call).
-       See &dpkg; if you are interested in what this actually means. In short: &dpkg; will not run the
-       triggers when this flag is present unless it is explicitly called to do so in an extra call.
-       Note that this option exists (undocumented) also in older APT versions with a slightly different
-       meaning: Previously these option only append --no-triggers to the configure calls to &dpkg; -
-       now APT will also add this flag to the unpack and remove calls.</para></listitem>
-       </varlistentry>
-       <varlistentry><term><option>PackageManager::Configure</option></term>
-       <listitem><para>Valid values are "<literal>all</literal>",
-       "<literal>smart</literal>" and "<literal>no</literal>".
-       The default value is "<literal>all</literal>", which causes APT to
-       configure all packages. The "<literal>smart</literal>" way is to
-       configure only packages which need to be configured before another
-       package can be unpacked (Pre-Depends), and let the rest be configured
-       by &dpkg; with a call generated by the ConfigurePending option (see
-       below). On the other hand, "<literal>no</literal>" will not configure
-       anything, and totally relies on &dpkg; for configuration (which at the
-       moment will fail if a Pre-Depends is encountered). Setting this option
-       to any value other than <literal>all</literal> will implicitly also
-       activate the next option by default, as otherwise the system could end
-       in an unconfigured and potentially unbootable state.</para></listitem>
-       </varlistentry>
-       <varlistentry><term><option>DPkg::ConfigurePending</option></term>
-       <listitem><para>If this option is set APT will call <command>dpkg --configure --pending</command>
-       to let &dpkg; handle all required configurations and triggers. This option is activated automatically
-       per default if the previous option is not set to <literal>all</literal>, but deactivating it could be useful
-       if you want to run APT multiple times in a row - e.g. in an installer. In these sceneries you could
-       deactivate this option in all but the last run.</para></listitem>
-       </varlistentry>
-       <varlistentry><term><option>DPkg::TriggersPending</option></term>
-       <listitem><para>Useful for the <literal>smart</literal> configuration as a package which has pending
-       triggers is not considered as <literal>installed</literal>, and &dpkg; treats them as <literal>unpacked</literal>
-       currently which is a showstopper for Pre-Dependencies (see debbugs #526774). Note that this will
-       process all triggers, not only the triggers needed to configure this package.</para></listitem>
-       </varlistentry>
-       <varlistentry><term><option>OrderList::Score::Immediate</option></term>
-       <listitem><para>Essential packages (and their dependencies) should be configured immediately
-       after unpacking. It is a good idea to do this quite early in the upgrade process as these
-       configure calls also currently require <literal>DPkg::TriggersPending</literal> which
-       will run quite a few triggers (which may not be needed). Essentials get per default a high score
-       but the immediate flag is relatively low (a package which has a Pre-Depends is rated higher).
-       These option and the others in the same group can be used to change the scoring. The following
-       example shows the settings with their default values.
-       <literallayout>OrderList::Score {
-	Delete 500;
-	Essential 200;
-	Immediate 10;
-	PreDepends 50;
-};</literallayout>
-       </para></listitem>
-       </varlistentry>
-     </variablelist>
-   </refsect2>
+     <varlistentry><term><option>DPkg::ConfigurePending</option></term>
+     <listitem><para>If this option is set APT will call <command>dpkg --configure --pending</command>
+     to let &dpkg; handle all required configurations and triggers. This option is activated by default,
+     but deactivating it could be useful if you want to run APT multiple times in a row - e.g. in an installer.
+     In this scenario you could deactivate this option in all but the last run.</para></listitem>
+     </varlistentry>
+   </variablelist>
  </refsect1>
  </refsect1>
 
 
  <refsect1>
  <refsect1>

+ 3 - 1
doc/external-installation-planner-protocol.txt

@@ -248,7 +248,9 @@ before it was unpacked, dependency relations must be satisfied, …), but
 they don't need to be complete: A planner can and should expect that any
 they don't need to be complete: A planner can and should expect that any
 package which wasn't explicitly configured will be configured at the end
 package which wasn't explicitly configured will be configured at the end
 automatically. That also means through that a planner is not allowed to
 automatically. That also means through that a planner is not allowed to
-produce a solution in which a package remains unconfigured.
+produce a solution in which a package remains unconfigured. Also,
+packages which are requested to be removed will be automatically removed
+at the end if not marked for removal explicitly earlier.
 
 
 In terms of expressivity, all stanzas can carry one single field each, as
 In terms of expressivity, all stanzas can carry one single field each, as
 APT-IDs are enough to pinpoint packages to be installed/removed.
 APT-IDs are enough to pinpoint packages to be installed/removed.

+ 4 - 4
test/integration/test-allow-scores-for-all-dependency-types

@@ -55,8 +55,8 @@ The following packages will be upgraded:
 Remv libdb5.1-dev [5.1.29-7] [libdb-dev:amd64 ]
 Remv libdb5.1-dev [5.1.29-7] [libdb-dev:amd64 ]
 Inst libdb-dev [5.1.7] (5.3.0 unversioned [amd64]) []
 Inst libdb-dev [5.1.7] (5.3.0 unversioned [amd64]) []
 Inst libdb5.3-dev (5.3.28-3 unversioned [amd64])
 Inst libdb5.3-dev (5.3.28-3 unversioned [amd64])
-Conf libdb5.3-dev (5.3.28-3 unversioned [amd64])
-Conf libdb-dev (5.3.0 unversioned [amd64])' aptget dist-upgrade -st unversioned
+Conf libdb-dev (5.3.0 unversioned [amd64])
+Conf libdb5.3-dev (5.3.28-3 unversioned [amd64])' aptget dist-upgrade -st unversioned
 testsuccessequal 'Reading package lists...
 testsuccessequal 'Reading package lists...
 Building dependency tree...
 Building dependency tree...
 Calculating upgrade...
 Calculating upgrade...
@@ -70,8 +70,8 @@ The following packages will be upgraded:
 Remv libdb5.1-dev [5.1.29-7] [libdb-dev:amd64 ]
 Remv libdb5.1-dev [5.1.29-7] [libdb-dev:amd64 ]
 Inst libdb-dev [5.1.7] (5.3.0 versioned [amd64]) []
 Inst libdb-dev [5.1.7] (5.3.0 versioned [amd64]) []
 Inst libdb5.3-dev (5.3.28-3 versioned [amd64])
 Inst libdb5.3-dev (5.3.28-3 versioned [amd64])
-Conf libdb5.3-dev (5.3.28-3 versioned [amd64])
-Conf libdb-dev (5.3.0 versioned [amd64])' aptget dist-upgrade -st versioned
+Conf libdb-dev (5.3.0 versioned [amd64])
+Conf libdb5.3-dev (5.3.28-3 versioned [amd64])' aptget dist-upgrade -st versioned
 
 
 cp -f rootdir/var/lib/dpkg/status-backup rootdir/var/lib/dpkg/status
 cp -f rootdir/var/lib/dpkg/status-backup rootdir/var/lib/dpkg/status
 insertinstalledpackage 'foo' 'amd64' '1'
 insertinstalledpackage 'foo' 'amd64' '1'

+ 9 - 1
test/integration/test-apt-get-autoremove

@@ -18,6 +18,8 @@ testmarkedauto 'po-debconf'
 testsuccess aptget remove debhelper -y
 testsuccess aptget remove debhelper -y
 testdpkgnotinstalled 'debhelper'
 testdpkgnotinstalled 'debhelper'
 testdpkginstalled 'po-debconf' 'unrelated'
 testdpkginstalled 'po-debconf' 'unrelated'
+echo 'unrelated purge' | dpkg --set-selections
+testdpkgstatus 'pi' '1' 'unrelated'
 
 
 AUTOREMOVE='apt autoremove'
 AUTOREMOVE='apt autoremove'
 if [ -n "$SUDO_USER" ]; then
 if [ -n "$SUDO_USER" ]; then
@@ -49,14 +51,17 @@ testdpkginstalled 'po-debconf'
 echo 'APT::NeverAutoRemove { "^po-debconf$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove
 echo 'APT::NeverAutoRemove { "^po-debconf$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove
 testsuccess aptget autoremove -y
 testsuccess aptget autoremove -y
 testdpkginstalled 'po-debconf'
 testdpkginstalled 'po-debconf'
+testdpkgstatus 'pi' '1' 'unrelated'
 
 
 echo 'APT::NeverAutoRemove { "^po-.*$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove
 echo 'APT::NeverAutoRemove { "^po-.*$"; };' > rootdir/etc/apt/apt.conf.d/00autoremove
 testsuccess aptget autoremove -y
 testsuccess aptget autoremove -y
 testdpkginstalled "po-debconf"
 testdpkginstalled "po-debconf"
+testdpkgstatus 'pi' '1' 'unrelated'
 
 
 rm rootdir/etc/apt/apt.conf.d/00autoremove
 rm rootdir/etc/apt/apt.conf.d/00autoremove
 testsuccess aptget autoremove -y
 testsuccess aptget autoremove -y
 testdpkgnotinstalled 'po-debconf'
 testdpkgnotinstalled 'po-debconf'
+testdpkgstatus 'pi' '1' 'unrelated'
 testmarkedauto
 testmarkedauto
 
 
 sed rootdir/var/log/apt/history.log -e '/^Commandline: / d' \
 sed rootdir/var/log/apt/history.log -e '/^Commandline: / d' \
@@ -71,7 +76,8 @@ Remove: debhelper:i386 (8.0.0)
 Remove: po-debconf:i386 (1.0.16)'
 Remove: po-debconf:i386 (1.0.16)'
 
 
 testsuccess aptget install debhelper -y
 testsuccess aptget install debhelper -y
-testdpkginstalled 'unrelated' 'debhelper' 'po-debconf'
+testdpkgstatus 'pi' '1' 'unrelated'
+testdpkginstalled 'debhelper' 'po-debconf'
 testsuccess aptmark auto debhelper
 testsuccess aptmark auto debhelper
 
 
 testmarkedauto 'debhelper' 'po-debconf'
 testmarkedauto 'debhelper' 'po-debconf'
@@ -105,9 +111,11 @@ Reading state information...
 
 
 testsuccess aptget autoremove debhelper -y --allow-change-held-packages
 testsuccess aptget autoremove debhelper -y --allow-change-held-packages
 testdpkgnotinstalled 'po-debconf' 'debhelper'
 testdpkgnotinstalled 'po-debconf' 'debhelper'
+testdpkgstatus 'pi' '1' 'unrelated'
 testmarkedauto
 testmarkedauto
 testsuccess aptget install debhelper --solver apt -y -o Debug::pkgDepCache::Marker=1
 testsuccess aptget install debhelper --solver apt -y -o Debug::pkgDepCache::Marker=1
 testmarkedauto 'po-debconf'
 testmarkedauto 'po-debconf'
+testdpkgstatus 'pi' '1' 'unrelated'
 
 
 insertinstalledpackage 'bar' 'all' '1' 'Depends: foo-provider'
 insertinstalledpackage 'bar' 'all' '1' 'Depends: foo-provider'
 insertinstalledpackage 'foo-multi1-1' 'all' '1' 'Provides: foo-provider
 insertinstalledpackage 'foo-multi1-1' 'all' '1' 'Provides: foo-provider

+ 1 - 1
test/integration/test-apt-progress-fd-error

@@ -19,7 +19,7 @@ setupaptarchive
 exec 3> apt-progress.log
 exec 3> apt-progress.log
 testfailure aptget install foo1 foo2 -y -o APT::Status-Fd=3
 testfailure aptget install foo1 foo2 -y -o APT::Status-Fd=3
 msgtest 'Ensure correct error message'
 msgtest 'Ensure correct error message'
-testsuccess --nomsg grep "aptarchive/pool/foo2_0.8.15_[^.]\+.deb:36.3636:trying to overwrite '/usr/bin/file-conflict', which is also in package foo1 0.8.15" apt-progress.log
+testsuccess --nomsg grep "foo2_0.8.15_[^.]\+.deb:36.3636:trying to overwrite '/usr/bin/file-conflict', which is also in package foo1 0.8.15" apt-progress.log
 
 
 testsuccess test -s rootdir/var/crash/foo2.0.crash
 testsuccess test -s rootdir/var/crash/foo2.0.crash
 testsuccess grep '^Package: foo2 0.8.15$' rootdir/var/crash/foo2.0.crash
 testsuccess grep '^Package: foo2 0.8.15$' rootdir/var/crash/foo2.0.crash

+ 1 - 1
test/integration/test-bug-673536-pre-depends-breaks-loop

@@ -11,7 +11,7 @@ buildsimplenativepackage 'advanced' 'native' '2' 'unstable' 'Pre-Depends: basic'
 buildsimplenativepackage 'basic' 'native' '2' 'unstable' 'Pre-Depends: common'
 buildsimplenativepackage 'basic' 'native' '2' 'unstable' 'Pre-Depends: common'
 
 
 buildsimplenativepackage 'common' 'native' '2~conflict' 'unstable-conflict' 'Conflicts: advanced (<= 1)'
 buildsimplenativepackage 'common' 'native' '2~conflict' 'unstable-conflict' 'Conflicts: advanced (<= 1)'
-buildsimplenativepackage 'common' 'native' '2~break' 'unstable-break' 'Conflicts: advanced (<= 1)'
+buildsimplenativepackage 'common' 'native' '2~break' 'unstable-break' 'Breaks: advanced (<= 1)'
 
 
 setupaptarchive
 setupaptarchive
 
 

+ 51 - 35
test/integration/test-bug-712116-dpkg-pre-install-pkgs-hook-multiarch

@@ -48,33 +48,42 @@ DPkg::Tools::options::\"./${hook}-v${1}.sh\"::Version \"$1\";" > rootdir/etc/apt
 observehook() {
 observehook() {
 	rm -f ${hook}-v2.list ${hook}-v3.list
 	rm -f ${hook}-v2.list ${hook}-v3.list
 	msgtest 'Observe hooks while' "$*"
 	msgtest 'Observe hooks while' "$*"
-	testsuccess --nomsg aptget "$@" -y --allow-downgrades
+	testsuccess --nomsg aptget "$@" -y --allow-downgrades --planner $planner
+	# different planners have different orders – we don't care in this test here
+	if [ -e ${hook}-v2.list ]; then
+		sort < ${hook}-v2.list > ${hook}-v2.list.new
+		mv ${hook}-v2.list.new ${hook}-v2.list
+	fi
+	if [ -e ${hook}-v3.list ]; then
+		sort < ${hook}-v3.list > ${hook}-v3.list.new
+		mv ${hook}-v3.list.new ${hook}-v3.list
+	fi
 }
 }
 
 
 testrun() {
 testrun() {
 	observehook install stuff -t stable
 	observehook install stuff -t stable
 	testfileequal "${hook}-v2.list" 'libsame - < 1 **CONFIGURE**
 	testfileequal "${hook}-v2.list" 'libsame - < 1 **CONFIGURE**
-toolkit - < 1 **CONFIGURE**
-stuff - < 1 **CONFIGURE**'
+stuff - < 1 **CONFIGURE**
+toolkit - < 1 **CONFIGURE**'
 	testfileequal "${hook}-v3.list" 'libsame - - none < 1 amd64 same **CONFIGURE**
 	testfileequal "${hook}-v3.list" 'libsame - - none < 1 amd64 same **CONFIGURE**
-toolkit - - none < 1 all foreign **CONFIGURE**
-stuff - - none < 1 amd64 none **CONFIGURE**'
+stuff - - none < 1 amd64 none **CONFIGURE**
+toolkit - - none < 1 all foreign **CONFIGURE**'
 
 
 	observehook install stuff -t unstable
 	observehook install stuff -t unstable
 	testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE**
 	testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE**
-toolkit 1 < 2 **CONFIGURE**
-stuff 1 < 2 **CONFIGURE**'
+stuff 1 < 2 **CONFIGURE**
+toolkit 1 < 2 **CONFIGURE**'
 	testfileequal "${hook}-v3.list" 'libsame 1 amd64 same < 2 amd64 same **CONFIGURE**
 	testfileequal "${hook}-v3.list" 'libsame 1 amd64 same < 2 amd64 same **CONFIGURE**
-toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**
-stuff 1 amd64 none < 2 amd64 none **CONFIGURE**'
+stuff 1 amd64 none < 2 amd64 none **CONFIGURE**
+toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**'
 
 
 	observehook install stuff:i386 -t unstable
 	observehook install stuff:i386 -t unstable
-	testfileequal "${hook}-v2.list" 'stuff 2 > - **REMOVE**
-libsame - < 2 **CONFIGURE**
-stuff - < 2 **CONFIGURE**'
-	testfileequal "${hook}-v3.list" 'stuff 2 amd64 none > - - none **REMOVE**
-libsame - - none < 2 i386 same **CONFIGURE**
-stuff - - none < 2 i386 none **CONFIGURE**'
+	testfileequal "${hook}-v2.list" 'libsame - < 2 **CONFIGURE**
+stuff - < 2 **CONFIGURE**
+stuff 2 > - **REMOVE**'
+	testfileequal "${hook}-v3.list" 'libsame - - none < 2 i386 same **CONFIGURE**
+stuff - - none < 2 i386 none **CONFIGURE**
+stuff 2 amd64 none > - - none **REMOVE**'
 
 
 	observehook remove libsame
 	observehook remove libsame
 	testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**'
 	testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**'
@@ -82,30 +91,30 @@ stuff - - none < 2 i386 none **CONFIGURE**'
 
 
 	observehook install stuff:i386/stable libsame:i386/stable toolkit/stable
 	observehook install stuff:i386/stable libsame:i386/stable toolkit/stable
 	testfileequal "${hook}-v2.list" 'libsame 2 > 1 **CONFIGURE**
 	testfileequal "${hook}-v2.list" 'libsame 2 > 1 **CONFIGURE**
-toolkit 2 > 1 **CONFIGURE**
-stuff 2 > 1 **CONFIGURE**'
+stuff 2 > 1 **CONFIGURE**
+toolkit 2 > 1 **CONFIGURE**'
 	testfileequal "${hook}-v3.list" 'libsame 2 i386 same > 1 i386 same **CONFIGURE**
 	testfileequal "${hook}-v3.list" 'libsame 2 i386 same > 1 i386 same **CONFIGURE**
-toolkit 2 amd64 foreign > 1 all foreign **CONFIGURE**
-stuff 2 i386 none > 1 i386 none **CONFIGURE**'
+stuff 2 i386 none > 1 i386 none **CONFIGURE**
+toolkit 2 amd64 foreign > 1 all foreign **CONFIGURE**'
 
 
 	observehook install 'libsame:*'
 	observehook install 'libsame:*'
-	testfileequal "${hook}-v2.list" 'libsame 1 < 2 **CONFIGURE**
-libsame - < 2 **CONFIGURE**
-toolkit 1 < 2 **CONFIGURE**
-stuff 1 < 2 **CONFIGURE**'
-	testfileequal "${hook}-v3.list" 'libsame 1 i386 same < 2 i386 same **CONFIGURE**
-libsame - - none < 2 amd64 same **CONFIGURE**
-toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**
-stuff 1 i386 none < 2 i386 none **CONFIGURE**'
+	testfileequal "${hook}-v2.list" 'libsame - < 2 **CONFIGURE**
+libsame 1 < 2 **CONFIGURE**
+stuff 1 < 2 **CONFIGURE**
+toolkit 1 < 2 **CONFIGURE**'
+	testfileequal "${hook}-v3.list" 'libsame - - none < 2 amd64 same **CONFIGURE**
+libsame 1 i386 same < 2 i386 same **CONFIGURE**
+stuff 1 i386 none < 2 i386 none **CONFIGURE**
+toolkit 1 all foreign < 2 amd64 foreign **CONFIGURE**'
 
 
 	observehook purge stuff:i386 'libsame:*' toolkit
 	observehook purge stuff:i386 'libsame:*' toolkit
 	testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**
 	testfileequal "${hook}-v2.list" 'libsame 2 > - **REMOVE**
-stuff 2 > - **REMOVE**
 libsame 2 > - **REMOVE**
 libsame 2 > - **REMOVE**
+stuff 2 > - **REMOVE**
 toolkit 2 > - **REMOVE**'
 toolkit 2 > - **REMOVE**'
 	testfileequal "${hook}-v3.list" 'libsame 2 amd64 same > - - none **REMOVE**
 	testfileequal "${hook}-v3.list" 'libsame 2 amd64 same > - - none **REMOVE**
-stuff 2 i386 none > - - none **REMOVE**
 libsame 2 i386 same > - - none **REMOVE**
 libsame 2 i386 same > - - none **REMOVE**
+stuff 2 i386 none > - - none **REMOVE**
 toolkit 2 amd64 foreign > - - none **REMOVE**'
 toolkit 2 amd64 foreign > - - none **REMOVE**'
 
 
 	observehook install confpkg
 	observehook install confpkg
@@ -127,10 +136,17 @@ toolkit 2 amd64 foreign > - - none **REMOVE**'
 	dpkg -l confpkg 2>/dev/null | grep -q '^rc' && msgfail || msgpass
 	dpkg -l confpkg 2>/dev/null | grep -q '^rc' && msgfail || msgpass
 }
 }
 
 
-enablehookversion 2
-enablehookversion 3
-testrun
+runwithplanner()
+{
+	msgmsg 'Running with planner' "$1"
+	planner="$1"
+	enablehookversion 2
+	enablehookversion 3
+	testrun
+
+	enablehookversion 2 13
+	enablehookversion 3 13
+	testrun
+}
 
 
-enablehookversion 2 13
-enablehookversion 3 13
-testrun
+runwithplanner 'apt'

+ 3 - 3
test/integration/test-bug-735967-lib32-to-i386-unavailable

@@ -71,10 +71,10 @@ Inst lib32nss-mdns [0.9-1] (0.10-6 unstable [amd64]) []
 Inst libnss-mdns [0.9-1] (0.10-6 unstable [amd64]) []
 Inst libnss-mdns [0.9-1] (0.10-6 unstable [amd64]) []
 Inst libnss-mdns:i386 (0.10-6 unstable [i386]) []
 Inst libnss-mdns:i386 (0.10-6 unstable [i386]) []
 Inst libnss-mdns-i386:i386 (0.10-6 unstable [i386])
 Inst libnss-mdns-i386:i386 (0.10-6 unstable [i386])
-Conf libnss-mdns:i386 (0.10-6 unstable [i386])
+Conf lib32nss-mdns (0.10-6 unstable [amd64])
 Conf libnss-mdns (0.10-6 unstable [amd64])
 Conf libnss-mdns (0.10-6 unstable [amd64])
-Conf libnss-mdns-i386:i386 (0.10-6 unstable [i386])
-Conf lib32nss-mdns (0.10-6 unstable [amd64])' aptget dist-upgrade -s
+Conf libnss-mdns:i386 (0.10-6 unstable [i386])
+Conf libnss-mdns-i386:i386 (0.10-6 unstable [i386])' aptget dist-upgrade -s
 
 
 testsuccessequal 'Reading package lists...
 testsuccessequal 'Reading package lists...
 Building dependency tree...
 Building dependency tree...

+ 8 - 8
test/integration/test-bug-740843-versioned-up-down-breaks

@@ -33,11 +33,11 @@ Inst libgl1-foo-glx:i386 [1] (2 stable [i386]) [foo-driver:amd64 on libgl1-foo-g
 Inst foo-driver [1] (2 stable [amd64]) []
 Inst foo-driver [1] (2 stable [amd64]) []
 Inst libfoo:i386 [1] (2 stable [i386]) [libfoo:amd64 on libfoo:i386] [libfoo:i386 on libfoo:amd64] [libfoo:amd64 ]
 Inst libfoo:i386 [1] (2 stable [i386]) [libfoo:amd64 on libfoo:i386] [libfoo:i386 on libfoo:amd64] [libfoo:amd64 ]
 Inst libfoo [1] (2 stable [amd64])
 Inst libfoo [1] (2 stable [amd64])
-Conf libfoo:i386 (2 stable [i386])
-Conf libfoo (2 stable [amd64])
-Conf libgl1-foo-glx:i386 (2 stable [i386])
 Conf libgl1-foo-glx (2 stable [amd64])
 Conf libgl1-foo-glx (2 stable [amd64])
-Conf foo-driver (2 stable [amd64])' 'Reading package lists...
+Conf libgl1-foo-glx:i386 (2 stable [i386])
+Conf foo-driver (2 stable [amd64])
+Conf libfoo:i386 (2 stable [i386])
+Conf libfoo (2 stable [amd64])' 'Reading package lists...
 Building dependency tree...
 Building dependency tree...
 Calculating upgrade...
 Calculating upgrade...
 The following packages will be upgraded:
 The following packages will be upgraded:
@@ -48,10 +48,10 @@ Inst libgl1-foo-glx:i386 [1] (2 stable [i386]) [foo-driver:amd64 on libgl1-foo-g
 Inst foo-driver [1] (2 stable [amd64]) []
 Inst foo-driver [1] (2 stable [amd64]) []
 Inst libfoo:i386 [1] (2 stable [i386]) [libfoo:amd64 on libfoo:i386] [libfoo:i386 on libfoo:amd64] [libfoo:amd64 ]
 Inst libfoo:i386 [1] (2 stable [i386]) [libfoo:amd64 on libfoo:i386] [libfoo:i386 on libfoo:amd64] [libfoo:amd64 ]
 Inst libfoo [1] (2 stable [amd64])
 Inst libfoo [1] (2 stable [amd64])
-Conf libfoo:i386 (2 stable [i386])
-Conf libfoo (2 stable [amd64])
-Conf libgl1-foo-glx:i386 (2 stable [i386])
 Conf libgl1-foo-glx (2 stable [amd64])
 Conf libgl1-foo-glx (2 stable [amd64])
-Conf foo-driver (2 stable [amd64])' aptget dist-upgrade -s
+Conf libgl1-foo-glx:i386 (2 stable [i386])
+Conf foo-driver (2 stable [amd64])
+Conf libfoo:i386 (2 stable [i386])
+Conf libfoo (2 stable [amd64])' aptget dist-upgrade -s
 
 
 testsuccess aptget dist-upgrade -y -o Debug::pkgPackageManager=1 -o Debug::pkgOrderList=1
 testsuccess aptget dist-upgrade -y -o Debug::pkgPackageManager=1 -o Debug::pkgOrderList=1

+ 2 - 2
test/integration/test-bug-lp1562402-nomark-removals-as-keep

@@ -33,7 +33,7 @@ Inst maas-region-controller [2.0.0~alpha3+bzr4810-0ubuntu1] (2.0.0~alpha4+bzr484
 Remv maas-region-controller-min [2.0.0~alpha3+bzr4810-0ubuntu1] []
 Remv maas-region-controller-min [2.0.0~alpha3+bzr4810-0ubuntu1] []
 Inst maas-common [2.0.0~alpha3+bzr4810-0ubuntu1] (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all]) []
 Inst maas-common [2.0.0~alpha3+bzr4810-0ubuntu1] (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all]) []
 Inst maas-region-api (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [amd64])
 Inst maas-region-api (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [amd64])
+Conf maas-region-controller (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all])
 Conf maas-common (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all])
 Conf maas-common (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all])
-Conf maas-region-api (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [amd64])
-Conf maas-region-controller (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [all])' \
+Conf maas-region-api (2.0.0~alpha4+bzr4843-0ubuntu1~xenial2 unstable [amd64])' \
 aptget dist-upgrade -s
 aptget dist-upgrade -s

+ 2 - 2
test/integration/test-bug-multiarch-upgrade

@@ -25,5 +25,5 @@ The following packages will be upgraded:
 2 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
 2 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.
 Inst libcups2 [1] (2 unstable [amd64]) [libcups2:amd64 on libcups2:i386] [libcups2:i386 on libcups2:amd64] [libcups2:i386 ]
 Inst libcups2 [1] (2 unstable [amd64]) [libcups2:amd64 on libcups2:i386] [libcups2:i386 on libcups2:amd64] [libcups2:i386 ]
 Inst libcups2:i386 [1] (2 unstable [i386])
 Inst libcups2:i386 [1] (2 unstable [i386])
-Conf libcups2:i386 (2 unstable [i386])
-Conf libcups2 (2 unstable [amd64])' aptget install -s libcups2:i386
+Conf libcups2 (2 unstable [amd64])
+Conf libcups2:i386 (2 unstable [i386])' aptget install -s libcups2:i386

+ 49 - 0
test/integration/test-crossgrades

@@ -0,0 +1,49 @@
+#!/bin/sh
+set -e
+
+TESTDIR="$(readlink -f "$(dirname "$0")")"
+. "$TESTDIR/framework"
+
+setupenvironment
+configarchitecture 'i386' 'amd64' 'armel'
+configdpkgnoopchroot
+
+buildsimplenativepackage 'unrelated' 'amd64' '1' 'stable'
+buildsimplenativepackage 'crosser' 'i386,armel' '1' 'stable' 'Multi-Arch: same'
+buildsimplenativepackage 'crosser' 'amd64' '2' 'unstable'
+setupaptarchive
+
+singleinstance() {
+	testsuccess apt install crosser:i386=1 unrelated:amd64 -y --planner $1
+	testdpkginstalled 'crosser:i386' 'unrelated'
+
+	testsuccess apt install crosser:amd64 -y -o Debug::pkgDpkgPm=1 -o Dpkg::Use-Pty=0 --purge --planner $1
+	cp -a rootdir/tmp/testsuccess.output crosser.output
+	testfailure grep -- '--remove.*crosser.*' crosser.output
+	testfailure grep -- '--purge' crosser.output
+	testsuccess apt install crosser:amd64 unrelated:amd64- -y -o Dpkg::Use-Pty=0 --purge -o Debug::pkgDPkgProgressReporting=1 --planner $1
+	testdpkgnotinstalled 'crosser:i386' 'unrelated'
+	testdpkginstalled 'crosser:amd64'
+
+	testsuccess apt purge crosser:amd64 -y --planner $1
+	testdpkgnotinstalled 'crosser:amd64'
+}
+singleinstance 'internal'
+singleinstance 'apt'
+
+multiinstance() {
+	testsuccess apt install crosser:i386=1 crosser:armel=1 unrelated:amd64 -y --planner $1
+	testdpkginstalled 'crosser:i386' 'crosser:armel' 'unrelated'
+
+	testsuccess apt install crosser:amd64 -y -o Debug::pkgDpkgPm=1 -o Dpkg::Use-Pty=0 --purge --planner $1
+	cp -a rootdir/tmp/testsuccess.output crosser.output
+	testsuccess grep -- '--remove.*crosser.*' crosser.output
+	testsuccess grep -- '--purge' crosser.output
+	testsuccess apt install crosser:amd64 unrelated:amd64- -y -o Dpkg::Use-Pty=0 --purge -o Debug::pkgDPkgProgressReporting=1 --planner $1
+	testdpkgnotinstalled 'crosser:i386' 'crosser:armel' 'unrelated'
+	testdpkginstalled 'crosser:amd64'
+
+	testsuccess apt purge crosser:amd64 -y --planner $1
+	testdpkgnotinstalled 'crosser:amd64'
+}
+multiinstance 'internal'

+ 1 - 1
test/integration/test-dpkg-assert-multi-arch

@@ -17,7 +17,7 @@ setupaptarchive
 testqualifier() {
 testqualifier() {
 	msgtest 'Test with' $1 'for correct qualifier mode' $2
 	msgtest 'Test with' $1 'for correct qualifier mode' $2
 	#aptget install $1 -qq -o Debug::pkgDPkgPM=1 || true
 	#aptget install $1 -qq -o Debug::pkgDPkgPM=1 || true
-	aptget install $1 -qq -o Debug::pkgDPkgPM=1 -o Dpkg::ExplicitLastConfigure=1 > testqualifier.output 2>&1 || true
+	aptget install $1 -qq -o Debug::pkgDPkgPM=1 -o Dpkg::ExplicitLastConfigure=1 -o PackageManager::Configure=all > testqualifier.output 2>&1 || true
 	GIVEN="$(grep -v -- '--unpack' testqualifier.output | sed -ne 's/^.*--[rpc][^ ]* \([^ ]*\).*$/\1/p')"
 	GIVEN="$(grep -v -- '--unpack' testqualifier.output | sed -ne 's/^.*--[rpc][^ ]* \([^ ]*\).*$/\1/p')"
 	if [ "$GIVEN" = "$2" ]; then
 	if [ "$GIVEN" = "$2" ]; then
 		msgpass
 		msgpass

+ 3 - 0
test/integration/test-essential-force-loopbreak

@@ -43,6 +43,9 @@ E: Internal Error, Could not early remove sysvinit:$(dpkg --print-architecture)
 
 
 	# with enough force however …
 	# with enough force however …
 	cp -a dpkg.status.backup rootdir/var/lib/dpkg/status
 	cp -a dpkg.status.backup rootdir/var/lib/dpkg/status
+	testsuccess aptget install systemd-sysv -y -t "$1" -o APT::Force-LoopBreak=1 -o Debug::pkgDpkgPm=1
+	cp rootdir/tmp/testsuccess.output apt.output
+	testsuccess grep -- '--force-remove-essential --remove sysvinit' apt.output
 	testsuccess aptget install systemd-sysv -y -t "$1" -o APT::Force-LoopBreak=1
 	testsuccess aptget install systemd-sysv -y -t "$1" -o APT::Force-LoopBreak=1
 	testdpkginstalled 'sysvinit' 'systemd-sysv'
 	testdpkginstalled 'sysvinit' 'systemd-sysv'
 }
 }

+ 2 - 5
test/integration/test-external-installation-planner-protocol

@@ -45,9 +45,7 @@ Planner: internal' head -n 6 "$EIPPLOG"
 aptinternalplanner < "$EIPPLOG" > planner.log || true
 aptinternalplanner < "$EIPPLOG" > planner.log || true
 testsuccessequal 'Remove: 6
 testsuccessequal 'Remove: 6
 Unpack: 2
 Unpack: 2
-Unpack: 4
-Configure: 2
-Configure: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log
+Unpack: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log
 
 
 rm -f "$EIPPLOG"
 rm -f "$EIPPLOG"
 testsuccess aptget install foo -s --reinstall
 testsuccess aptget install foo -s --reinstall
@@ -59,8 +57,7 @@ Architectures: amd64
 ReInstall: foo:amd64
 ReInstall: foo:amd64
 Planner: internal' head -n 5 "$EIPPLOG"
 Planner: internal' head -n 5 "$EIPPLOG"
 aptinternalplanner < "$EIPPLOG" > planner.log || true
 aptinternalplanner < "$EIPPLOG" > planner.log || true
-testsuccessequal 'Unpack: 4
-Configure: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log
+testsuccessequal 'Unpack: 4' grep -e '^Unpack:' -e '^Install:' -e '^Configure:' -e '^Remove:' planner.log
 
 
 rm -f "$EIPPLOG"
 rm -f "$EIPPLOG"
 testsuccess aptget purge foo -s
 testsuccess aptget purge foo -s

+ 1 - 1
test/integration/test-ignore-provides-if-versioned-breaks

@@ -144,7 +144,7 @@ Inst foo-same [2.0] (4.0 unstable [i386]) [foo-same:i386 on foo-same:amd64] [foo
 Inst foo-same:amd64 [2.0] (4.0 unstable [amd64])
 Inst foo-same:amd64 [2.0] (4.0 unstable [amd64])
 Inst foo-same-breaker-3 (1.0 unstable [i386])
 Inst foo-same-breaker-3 (1.0 unstable [i386])
 Inst foo-same-provider (1.0 unstable [i386])
 Inst foo-same-provider (1.0 unstable [i386])
-Conf foo-same:amd64 (4.0 unstable [amd64])
 Conf foo-same (4.0 unstable [i386])
 Conf foo-same (4.0 unstable [i386])
+Conf foo-same:amd64 (4.0 unstable [amd64])
 Conf foo-same-breaker-3 (1.0 unstable [i386])
 Conf foo-same-breaker-3 (1.0 unstable [i386])
 Conf foo-same-provider (1.0 unstable [i386])' aptget install foo-same-provider foo-same-breaker-3 -s
 Conf foo-same-provider (1.0 unstable [i386])' aptget install foo-same-provider foo-same-breaker-3 -s

+ 1 - 1
test/integration/test-ignore-provides-if-versioned-conflicts

@@ -144,7 +144,7 @@ Inst foo-same [2.0] (4.0 unstable [i386]) [foo-same:i386 on foo-same:amd64] [foo
 Inst foo-same:amd64 [2.0] (4.0 unstable [amd64])
 Inst foo-same:amd64 [2.0] (4.0 unstable [amd64])
 Inst foo-same-breaker-3 (1.0 unstable [i386])
 Inst foo-same-breaker-3 (1.0 unstable [i386])
 Inst foo-same-provider (1.0 unstable [i386])
 Inst foo-same-provider (1.0 unstable [i386])
-Conf foo-same:amd64 (4.0 unstable [amd64])
 Conf foo-same (4.0 unstable [i386])
 Conf foo-same (4.0 unstable [i386])
+Conf foo-same:amd64 (4.0 unstable [amd64])
 Conf foo-same-breaker-3 (1.0 unstable [i386])
 Conf foo-same-breaker-3 (1.0 unstable [i386])
 Conf foo-same-provider (1.0 unstable [i386])' aptget install foo-same-provider foo-same-breaker-3 -s
 Conf foo-same-provider (1.0 unstable [i386])' aptget install foo-same-provider foo-same-breaker-3 -s

+ 6 - 2
test/integration/test-no-fds-leaked-to-maintainer-scripts

@@ -34,7 +34,7 @@ rm -f rootdir/var/log/dpkg.log rootdir/var/log/apt/term.log
 testsuccess aptget install -y fdleaks -qq < /dev/null
 testsuccess aptget install -y fdleaks -qq < /dev/null
 
 
 checkfdleak() {
 checkfdleak() {
-	msgtest 'Check if fds were not' 'leaked'
+	msgtest 'Check if fds were not' "leaked: expect $1"
 	if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = "$1" ]; then
 	if [ "$(grep 'root root' rootdir/tmp/testsuccess.output | wc -l)" = "$1" ]; then
 		msgpass
 		msgpass
 	else
 	else
@@ -72,12 +72,15 @@ checkpurge() {
 	tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log
 	tail -n +3 rootdir/var/log/apt/term.log | head -n -1 > terminal.log
 	testfileequal 'terminal.log' "$(cat terminal.output)"
 	testfileequal 'terminal.log' "$(cat terminal.output)"
 
 
-	testequal "startup packages purge
+	testequal "startup packages remove
 status installed $PKGNAME 1.0
 status installed $PKGNAME 1.0
 remove $PKGNAME 1.0 <none>
 remove $PKGNAME 1.0 <none>
 status half-configured $PKGNAME 1.0
 status half-configured $PKGNAME 1.0
 status half-installed $PKGNAME 1.0
 status half-installed $PKGNAME 1.0
 status config-files $PKGNAME 1.0
 status config-files $PKGNAME 1.0
+status config-files $PKGNAME 1.0
+startup packages purge
+remove $PKGNAME 1.0 <none>
 purge $PKGNAME 1.0 <none>
 purge $PKGNAME 1.0 <none>
 status config-files $PKGNAME 1.0
 status config-files $PKGNAME 1.0
 status config-files $PKGNAME 1.0
 status config-files $PKGNAME 1.0
@@ -86,6 +89,7 @@ status config-files $PKGNAME 1.0
 status config-files $PKGNAME 1.0
 status config-files $PKGNAME 1.0
 status not-installed $PKGNAME <none>
 status not-installed $PKGNAME <none>
 startup packages configure" cut -f 3- -d' ' rootdir/var/log/dpkg.log
 startup packages configure" cut -f 3- -d' ' rootdir/var/log/dpkg.log
+	testequalor2 "dpkg-query: no packages found matching ${PKGNAME}" "No packages found matching ${PKGNAME}." dpkg -l "$PKGNAME"
 }
 }
 checkpurge
 checkpurge
 
 

+ 4 - 4
test/integration/test-prevent-markinstall-multiarch-same-versionscrew

@@ -59,10 +59,10 @@ Inst fine-installed [1] (2 unstable [amd64]) [fine-installed:amd64 on fine-insta
 Inst fine-installed:i386 [1] (2 unstable [i386])
 Inst fine-installed:i386 [1] (2 unstable [i386])
 Inst out-of-sync-gone-foreign [1] (2 unstable [amd64])
 Inst out-of-sync-gone-foreign [1] (2 unstable [amd64])
 Inst out-of-sync-gone-native:i386 [1] (2 unstable [i386])
 Inst out-of-sync-gone-native:i386 [1] (2 unstable [i386])
-Conf fine:i386 (2 unstable [i386])
 Conf fine (2 unstable [amd64])
 Conf fine (2 unstable [amd64])
-Conf fine-installed:i386 (2 unstable [i386])
+Conf fine:i386 (2 unstable [i386])
 Conf fine-installed (2 unstable [amd64])
 Conf fine-installed (2 unstable [amd64])
+Conf fine-installed:i386 (2 unstable [i386])
 Conf out-of-sync-gone-foreign (2 unstable [amd64])
 Conf out-of-sync-gone-foreign (2 unstable [amd64])
 Conf out-of-sync-gone-native:i386 (2 unstable [i386])' aptget dist-upgrade -s #-o Debug::pkgDepCache::Marker=1
 Conf out-of-sync-gone-native:i386 (2 unstable [i386])' aptget dist-upgrade -s #-o Debug::pkgDepCache::Marker=1
 
 
@@ -89,10 +89,10 @@ Inst fine-installed [1] (3 experimental [amd64]) [fine-installed:amd64 on fine-i
 Inst fine-installed:i386 [1] (3 experimental [i386])
 Inst fine-installed:i386 [1] (3 experimental [i386])
 Inst out-of-sync-gone-foreign [1] (2 unstable [amd64])
 Inst out-of-sync-gone-foreign [1] (2 unstable [amd64])
 Inst out-of-sync-gone-native:i386 [1] (2 unstable [i386])
 Inst out-of-sync-gone-native:i386 [1] (2 unstable [i386])
-Conf fine:i386 (3 experimental [i386])
 Conf fine (3 experimental [amd64])
 Conf fine (3 experimental [amd64])
-Conf fine-installed:i386 (3 experimental [i386])
+Conf fine:i386 (3 experimental [i386])
 Conf fine-installed (3 experimental [amd64])
 Conf fine-installed (3 experimental [amd64])
+Conf fine-installed:i386 (3 experimental [i386])
 Conf out-of-sync-gone-foreign (2 unstable [amd64])
 Conf out-of-sync-gone-foreign (2 unstable [amd64])
 Conf out-of-sync-gone-native:i386 (2 unstable [i386])' aptget dist-upgrade -s #-o Debug::pkgDepCache::Marker=1
 Conf out-of-sync-gone-native:i386 (2 unstable [i386])' aptget dist-upgrade -s #-o Debug::pkgDepCache::Marker=1
 
 

+ 4 - 0
test/integration/test-ubuntu-bug-761175-remove-purge

@@ -30,6 +30,8 @@ runtests() {
 	testsuccess aptget install compiz-core-${PKG} -t "${RELEASE}" "$@"
 	testsuccess aptget install compiz-core-${PKG} -t "${RELEASE}" "$@"
 	testdpkginstalled compiz-core-${PKG}
 	testdpkginstalled compiz-core-${PKG}
 
 
+	testsuccess aptget remove compiz-core-${PKG} -y "$@" -o Debug::pkgDpkgPm=1
+	testfailure grep -- '--force-remove-essential' rootdir/tmp/testsuccess.output
 	testsuccess aptget remove compiz-core-${PKG} -y "$@"
 	testsuccess aptget remove compiz-core-${PKG} -y "$@"
 	testdpkgnotinstalled compiz-core-${PKG}
 	testdpkgnotinstalled compiz-core-${PKG}
 	testdpkgstatus 'rc' '1' "compiz-core-${PKG}"
 	testdpkgstatus 'rc' '1' "compiz-core-${PKG}"
@@ -61,6 +63,8 @@ The following packages will be REMOVED:
 0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
 0 upgraded, 0 newly installed, 1 to remove and 0 not upgraded.
 Purg compiz-core-${PKG}" aptget purge compiz-core-${PKG} -s "$@"
 Purg compiz-core-${PKG}" aptget purge compiz-core-${PKG} -s "$@"
 	fi
 	fi
+	testsuccess aptget purge compiz-core-${PKG} -y "$@" -o Debug::pkgDpkgPm=1
+	testfailure grep -- '--force-remove-essential' rootdir/tmp/testsuccess.output
 	testsuccess aptget purge compiz-core-${PKG} -y "$@"
 	testsuccess aptget purge compiz-core-${PKG} -y "$@"
 	echo -n '' > rootdir/var/lib/dpkg/available # dpkg -l < 1.16.2 reads the available file by default, where the package can be found
 	echo -n '' > rootdir/var/lib/dpkg/available # dpkg -l < 1.16.2 reads the available file by default, where the package can be found
 	testequalor2 "dpkg-query: no packages found matching compiz-core-${PKG}" "No packages found matching compiz-core-${PKG}." dpkg -l compiz-core-${PKG}
 	testequalor2 "dpkg-query: no packages found matching compiz-core-${PKG}" "No packages found matching compiz-core-${PKG}." dpkg -l compiz-core-${PKG}

+ 8 - 8
test/integration/test-very-tight-loop-configure-with-unpacking-new-packages

@@ -39,11 +39,11 @@ Inst libreoffice-style-galaxy [3] (4 sid [amd64]) [libreoffice-common:amd64 on l
 Inst libreoffice-core [3] (4 sid [amd64]) [libreoffice-core:amd64 on libreoffice-common:amd64] [libreoffice-common:amd64 on libreoffice-core:amd64] [libreoffice-common:amd64 on libreoffice-style-galaxy:amd64] [libreoffice-common:amd64 ]
 Inst libreoffice-core [3] (4 sid [amd64]) [libreoffice-core:amd64 on libreoffice-common:amd64] [libreoffice-common:amd64 on libreoffice-core:amd64] [libreoffice-common:amd64 on libreoffice-style-galaxy:amd64] [libreoffice-common:amd64 ]
 Inst libreoffice-common [3] (4 sid [all]) []
 Inst libreoffice-common [3] (4 sid [all]) []
 Inst ure (4 sid [amd64])
 Inst ure (4 sid [amd64])
-Conf ure (4 sid [amd64])
-Conf libreoffice-common (4 sid [all])
-Conf libreoffice-core (4 sid [amd64])
+Conf libreoffice (4 sid [amd64])
 Conf libreoffice-style-galaxy (4 sid [amd64])
 Conf libreoffice-style-galaxy (4 sid [amd64])
-Conf libreoffice (4 sid [amd64])' 'Reading package lists...
+Conf libreoffice-core (4 sid [amd64])
+Conf libreoffice-common (4 sid [all])
+Conf ure (4 sid [amd64])' 'Reading package lists...
 Building dependency tree...
 Building dependency tree...
 Calculating upgrade...
 Calculating upgrade...
 The following NEW packages will be installed:
 The following NEW packages will be installed:
@@ -56,8 +56,8 @@ Inst libreoffice-style-galaxy [3] (4 sid [amd64]) [libreoffice-common:amd64 on l
 Inst libreoffice-core [3] (4 sid [amd64]) [libreoffice-common:amd64 on libreoffice-core:amd64] [libreoffice-common:amd64 on libreoffice-style-galaxy:amd64] [libreoffice-core:amd64 on libreoffice-common:amd64] [libreoffice-common:amd64 ]
 Inst libreoffice-core [3] (4 sid [amd64]) [libreoffice-common:amd64 on libreoffice-core:amd64] [libreoffice-common:amd64 on libreoffice-style-galaxy:amd64] [libreoffice-core:amd64 on libreoffice-common:amd64] [libreoffice-common:amd64 ]
 Inst libreoffice-common [3] (4 sid [all]) []
 Inst libreoffice-common [3] (4 sid [all]) []
 Inst ure (4 sid [amd64])
 Inst ure (4 sid [amd64])
-Conf ure (4 sid [amd64])
-Conf libreoffice-common (4 sid [all])
-Conf libreoffice-core (4 sid [amd64])
+Conf libreoffice (4 sid [amd64])
 Conf libreoffice-style-galaxy (4 sid [amd64])
 Conf libreoffice-style-galaxy (4 sid [amd64])
-Conf libreoffice (4 sid [amd64])' aptget dist-upgrade -s
+Conf libreoffice-core (4 sid [amd64])
+Conf libreoffice-common (4 sid [all])
+Conf ure (4 sid [amd64])' aptget dist-upgrade -s