Просмотр исходного кода

eipp: provide the internal planer as an external one

Testing the current implementation can benefit from being able to be
feed an EIPP request and produce a fully compliant response. It is also
a great test for EIPP in general.
David Kalnischkies лет назад: 10
Родитель
Сommit
f74d99c6a7

+ 140 - 14
apt-pkg/edsp.cc

@@ -12,6 +12,8 @@
 #include <apt-pkg/depcache.h>
 #include <apt-pkg/depcache.h>
 #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/packagemanager.h>
 #include <apt-pkg/progress.h>
 #include <apt-pkg/progress.h>
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/fileutl.h>
 #include <apt-pkg/edsp.h>
 #include <apt-pkg/edsp.h>
@@ -659,8 +661,12 @@ bool EDSP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progres
 			return false;
 			return false;
 		} else if (section.Exists("Autoremove") == true)
 		} else if (section.Exists("Autoremove") == true)
 			type = "Autoremove";
 			type = "Autoremove";
-		else
+		else {
+			char const *Start, *End;
+			section.GetSection(Start, End);
+			_error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str());
 			continue;
 			continue;
+		}
 
 
 		size_t const id = section.FindULL(type.c_str(), VersionCount);
 		size_t const id = section.FindULL(type.c_str(), VersionCount);
 		if (id == VersionCount) {
 		if (id == VersionCount) {
@@ -1065,7 +1071,7 @@ bool EDSP::ResolveExternal(const char* const solver, pkgDepCache &Cache,
 }
 }
 									/*}}}*/
 									/*}}}*/
 
 
-bool EIPP::OrderInstall(char const * const solver, pkgDepCache &Cache,	/*{{{*/
+bool EIPP::OrderInstall(char const * const solver, pkgPackageManager * const PM,	/*{{{*/
 			 unsigned int const flags, OpProgress * const Progress)
 			 unsigned int const flags, OpProgress * const Progress)
 {
 {
    int solver_in, solver_out;
    int solver_in, solver_out;
@@ -1080,15 +1086,15 @@ bool EIPP::OrderInstall(char const * const solver, pkgDepCache &Cache,	/*{{{*/
    bool Okay = output.Failed() == false;
    bool Okay = output.Failed() == false;
    if (Progress != NULL)
    if (Progress != NULL)
       Progress->OverallProgress(0, 100, 5, _("Execute external planer"));
       Progress->OverallProgress(0, 100, 5, _("Execute external planer"));
-   Okay &= EIPP::WriteRequest(Cache, output, flags, Progress);
+   Okay &= EIPP::WriteRequest(PM->Cache, output, flags, Progress);
    if (Progress != NULL)
    if (Progress != NULL)
       Progress->OverallProgress(5, 100, 20, _("Execute external planer"));
       Progress->OverallProgress(5, 100, 20, _("Execute external planer"));
-   Okay &= EIPP::WriteScenario(Cache, output, Progress);
+   Okay &= EIPP::WriteScenario(PM->Cache, output, Progress);
    output.Close();
    output.Close();
 
 
    if (Progress != NULL)
    if (Progress != NULL)
       Progress->OverallProgress(25, 100, 75, _("Execute external planer"));
       Progress->OverallProgress(25, 100, 75, _("Execute external planer"));
-   if (Okay && EIPP::ReadResponse(solver_out, Cache, Progress) == false)
+   if (Okay && EIPP::ReadResponse(solver_out, PM, Progress) == false)
       return false;
       return false;
 
 
    return ExecWait(solver_pid, solver);
    return ExecWait(solver_pid, solver);
@@ -1113,7 +1119,7 @@ bool EIPP::WriteRequest(pkgDepCache &Cache, FileFd &output,		/*{{{*/
 	 req = &purge;
 	 req = &purge;
       if (P.Delete() == true)
       if (P.Delete() == true)
 	 req = &del;
 	 req = &del;
-      else if (P.NewInstall() == true || P.Upgrade() == true)
+      else if (P.NewInstall() == true || P.Upgrade() == true || P.Downgrade() == true)
 	 req = &inst;
 	 req = &inst;
       else if (P.ReInstall() == true)
       else if (P.ReInstall() == true)
 	 req = &reinst;
 	 req = &reinst;
@@ -1228,27 +1234,25 @@ bool EIPP::WriteScenario(pkgDepCache &Cache, FileFd &output, OpProgress * const
 }
 }
 									/*}}}*/
 									/*}}}*/
 // EIPP::ReadResponse - from the given file descriptor			/*{{{*/
 // EIPP::ReadResponse - from the given file descriptor			/*{{{*/
-bool EIPP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progress) {
+bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM, OpProgress *Progress) {
    /* We build an map id to mmap offset here
    /* We build an map id to mmap offset here
       In theory we could use the offset as ID, but then VersionCount
       In theory we could use the offset as ID, but then VersionCount
       couldn't be used to create other versionmappings anymore and it
       couldn't be used to create other versionmappings anymore and it
       would be too easy for a (buggy) solver to segfault APT… */
       would be too easy for a (buggy) solver to segfault APT… */
-   /*
-   unsigned long long const VersionCount = Cache.Head().VersionCount;
+   unsigned long long const VersionCount = PM->Cache.Head().VersionCount;
    unsigned long VerIdx[VersionCount];
    unsigned long VerIdx[VersionCount];
-   for (pkgCache::PkgIterator P = Cache.PkgBegin(); P.end() == false; ++P) {
+   for (pkgCache::PkgIterator P = PM->Cache.PkgBegin(); P.end() == false; ++P) {
       for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
       for (pkgCache::VerIterator V = P.VersionList(); V.end() == false; ++V)
 	 VerIdx[V->ID] = V.Index();
 	 VerIdx[V->ID] = V.Index();
    }
    }
-   */
 
 
    FileFd in;
    FileFd in;
    in.OpenDescriptor(input, FileFd::ReadOnly);
    in.OpenDescriptor(input, FileFd::ReadOnly);
    pkgTagFile response(&in, 100);
    pkgTagFile response(&in, 100);
    pkgTagSection section;
    pkgTagSection section;
 
 
-   std::set<decltype(Cache.PkgBegin()->ID)> seenOnce;
    while (response.Step(section) == true) {
    while (response.Step(section) == true) {
+      char const * type = nullptr;
       if (section.Exists("Progress") == true) {
       if (section.Exists("Progress") == true) {
 	 if (Progress != NULL) {
 	 if (Progress != NULL) {
 	    string msg = section.FindS("Message");
 	    string msg = section.FindS("Message");
@@ -1270,8 +1274,130 @@ bool EIPP::ReadResponse(int const input, pkgDepCache &Cache, OpProgress *Progres
 	 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
 	 std::cerr << "The following information might help you to understand what is wrong:" << std::endl;
 	 std::cerr << msg << std::endl << std::endl;
 	 std::cerr << msg << std::endl << std::endl;
 	 return false;
 	 return false;
-      } else {
-	 _error->Warning("Encountered an unexpected section with %d fields", section.Count());
+      } else if (section.Exists("Install") == true)
+	 type = "Install";
+      else if (section.Exists("Configure") == true)
+	 type = "Configure";
+      else if (section.Exists("Remove") == true)
+	 type = "Remove";
+      else {
+	 char const *Start, *End;
+	 section.GetSection(Start, End);
+	 _error->Warning("Encountered an unexpected section with %d fields: %s", section.Count(), std::string(Start, End).c_str());
+	 continue;
+      }
+
+      if (type == nullptr)
+	 continue;
+      size_t const id = section.FindULL(type, VersionCount);
+      if (id == VersionCount) {
+	 _error->Warning("Unable to parse %s request with id value '%s'!", type, section.FindS(type).c_str());
+	 continue;
+      } else if (id > PM->Cache.Head().VersionCount) {
+	 _error->Warning("ID value '%s' in %s request stanza is to high to refer to a known version!", section.FindS(type).c_str(), type);
+	 continue;
+      }
+
+      pkgCache::VerIterator Ver(PM->Cache.GetCache(), PM->Cache.GetCache().VerP + VerIdx[id]);
+      auto const Pkg = Ver.ParentPkg();
+      if (strcmp(type, "Install") == 0)
+	 PM->Install(Pkg, PM->FileNames[Pkg->ID]);
+      else if (strcmp(type, "Configure") == 0)
+	 PM->Configure(Pkg);
+      else if (strcmp(type, "Remove") == 0)
+	 PM->Remove(Pkg, PM->Cache[Pkg].Purge());
+   }
+   return true;
+}
+									/*}}}*/
+bool EIPP::ReadRequest(int const input, std::list<std::pair<std::string,PKG_ACTION>> &actions,/*{{{*/
+      unsigned int &flags)
+{
+   actions.clear();
+   flags = 0;
+   std::string line;
+   while (ReadLine(input, line) == true)
+   {
+      // Skip empty lines before request
+      if (line.empty() == true)
+	 continue;
+      // The first Tag must be a request, so search for it
+      if (line.compare(0, 8, "Request:") != 0)
+	 continue;
+
+      while (ReadLine(input, line) == true)
+      {
+	 // empty lines are the end of the request
+	 if (line.empty() == true)
+	    return true;
+
+	 PKG_ACTION pkgact = PKG_ACTION::NOOP;
+	 if (LineStartsWithAndStrip(line, "Install:"))
+	    pkgact = PKG_ACTION::INSTALL;
+	 else if (LineStartsWithAndStrip(line, "ReInstall:"))
+	    pkgact = PKG_ACTION::REINSTALL;
+	 else if (LineStartsWithAndStrip(line, "Remove:"))
+	    pkgact = PKG_ACTION::REMOVE;
+	 else if (LineStartsWithAndStrip(line, "Architecture:"))
+	    _config->Set("APT::Architecture", line);
+	 else if (LineStartsWithAndStrip(line, "Architectures:"))
+	    _config->Set("APT::Architectures", SubstVar(line, " ", ","));
+	 else if (LineStartsWithAndStrip(line, "Planer:"))
+	    ; // purely informational line
+	 else
+	    _error->Warning("Unknown line in EIPP Request stanza: %s", line.c_str());
+
+	 if (pkgact == PKG_ACTION::NOOP)
+	    continue;
+	 for (auto && p: VectorizeString(line, ' '))
+	    actions.emplace_back(std::move(p), pkgact);
+      }
+   }
+   return false;
+
+
+   return false;
+}
+									/*}}}*/
+bool EIPP::ApplyRequest(std::list<std::pair<std::string,PKG_ACTION>> &actions,/*{{{*/
+	 pkgDepCache &Cache)
+{
+   for (auto Pkg = Cache.PkgBegin(); Pkg.end() == false; ++Pkg)
+   {
+      short versions = 0;
+      for (auto Ver = Pkg.VersionList(); Ver.end() == false; ++Ver)
+      {
+	 ++versions;
+	 if (Pkg.CurrentVer() == Ver)
+	    continue;
+	 Cache.SetCandidateVersion(Ver);
+      }
+      if (unlikely(versions > 2))
+	 _error->Warning("Package %s has %d versions, but should have at most 2!", Pkg.FullName().c_str(), versions);
+   }
+   for (auto && a: actions)
+   {
+      pkgCache::PkgIterator P = Cache.FindPkg(a.first);
+      if (P.end() == true)
+      {
+	 _error->Warning("Package %s is not known, so can't be acted on", a.first.c_str());
+	 continue;
+      }
+      switch (a.second)
+      {
+	 case PKG_ACTION::NOOP:
+	    _error->Warning("Package %s has NOOP as action?!?", a.first.c_str());
+	    break;
+	 case PKG_ACTION::INSTALL:
+	    Cache.MarkInstall(P, false);
+	    break;
+	 case PKG_ACTION::REINSTALL:
+	    Cache.MarkInstall(P, false);
+	    Cache.SetReInstall(P, true);
+	    break;
+	 case PKG_ACTION::REMOVE:
+	    Cache.MarkDelete(P);
+	    break;
       }
       }
    }
    }
    return true;
    return true;

+ 18 - 3
apt-pkg/edsp.h

@@ -236,16 +236,31 @@ namespace EDSP								/*{{{*/
 				    bool const autoRemove, OpProgress *Progress = NULL);
 				    bool const autoRemove, OpProgress *Progress = NULL);
 }
 }
 									/*}}}*/
 									/*}}}*/
+class pkgPackageManager;
 namespace EIPP								/*{{{*/
 namespace EIPP								/*{{{*/
 {
 {
-   APT_HIDDEN bool OrderInstall(char const * const solver, pkgDepCache &Cache,
-	 unsigned int const version, OpProgress * const Progress);
    APT_HIDDEN bool WriteRequest(pkgDepCache &Cache, FileFd &output,
    APT_HIDDEN bool WriteRequest(pkgDepCache &Cache, FileFd &output,
 	 unsigned int const version, OpProgress * const Progress);
 	 unsigned int const version, OpProgress * const Progress);
    APT_HIDDEN bool WriteScenario(pkgDepCache &Cache, FileFd &output,
    APT_HIDDEN bool WriteScenario(pkgDepCache &Cache, FileFd &output,
 	 OpProgress * const Progress);
 	 OpProgress * const Progress);
-   APT_HIDDEN bool ReadResponse(int const input, pkgDepCache &Cache,
+
+   APT_HIDDEN bool OrderInstall(char const * const planer, pkgPackageManager * const PM,
+	 unsigned int const version, OpProgress * const Progress);
+   APT_HIDDEN bool ReadResponse(int const input, pkgPackageManager * const PM,
 	 OpProgress * const Progress);
 	 OpProgress * const Progress);
+
+   enum class PKG_ACTION
+   {
+      NOOP,
+      INSTALL,
+      REINSTALL,
+      REMOVE
+   };
+   bool ReadRequest(int const input,
+	 std::list<std::pair<std::string,PKG_ACTION>> &actions,
+	 unsigned int &flags);
+   bool ApplyRequest(std::list<std::pair<std::string,PKG_ACTION>> &actions,
+	 pkgDepCache &Cache);
 }
 }
 									/*}}}*/
 									/*}}}*/
 #endif
 #endif

+ 36 - 1
apt-pkg/edsp/edspindexfile.cc

@@ -69,6 +69,25 @@ pkgCacheListParser * edspIndex::CreateListParser(FileFd &Pkg)
    return newError ? NULL : Parser;
    return newError ? NULL : Parser;
 }
 }
 									/*}}}*/
 									/*}}}*/
+// EIPP Index								/*{{{*/
+eippIndex::eippIndex(std::string const &File) : edspLikeIndex(File)
+{
+}
+std::string eippIndex::GetComponent() const
+{
+   return "eipp";
+}
+pkgCacheListParser * eippIndex::CreateListParser(FileFd &Pkg)
+{
+   if (Pkg.IsOpen() == false)
+      return NULL;
+   _error->PushToStack();
+   pkgCacheListParser * const Parser = new eippListParser(&Pkg);
+   bool const newError = _error->PendingError();
+   _error->MergeWithStack();
+   return newError ? NULL : Parser;
+}
+									/*}}}*/
 
 
 // Index File types for APT						/*{{{*/
 // Index File types for APT						/*{{{*/
 class APT_HIDDEN edspIFType: public pkgIndexFile::Type
 class APT_HIDDEN edspIFType: public pkgIndexFile::Type
@@ -82,12 +101,28 @@ class APT_HIDDEN edspIFType: public pkgIndexFile::Type
    edspIFType() {Label = "EDSP scenario file";};
    edspIFType() {Label = "EDSP scenario file";};
 };
 };
 APT_HIDDEN edspIFType _apt_Edsp;
 APT_HIDDEN edspIFType _apt_Edsp;
-
 const pkgIndexFile::Type *edspIndex::GetType() const
 const pkgIndexFile::Type *edspIndex::GetType() const
 {
 {
    return &_apt_Edsp;
    return &_apt_Edsp;
 }
 }
+
+class APT_HIDDEN eippIFType: public pkgIndexFile::Type
+{
+   public:
+   virtual pkgRecords::Parser *CreatePkgParser(pkgCache::PkgFileIterator const &) const APT_OVERRIDE
+   {
+      // we don't have a record parser for this type as the file is not presistent
+      return NULL;
+   };
+   eippIFType() {Label = "EIPP scenario file";};
+};
+APT_HIDDEN eippIFType _apt_Eipp;
+const pkgIndexFile::Type *eippIndex::GetType() const
+{
+   return &_apt_Eipp;
+}
 									/*}}}*/
 									/*}}}*/
 
 
 edspLikeIndex::~edspLikeIndex() {}
 edspLikeIndex::~edspLikeIndex() {}
 edspIndex::~edspIndex() {}
 edspIndex::~edspIndex() {}
+eippIndex::~eippIndex() {}

+ 13 - 0
apt-pkg/edsp/edspindexfile.h

@@ -46,4 +46,17 @@ public:
    virtual ~edspIndex();
    virtual ~edspIndex();
 };
 };
 
 
+class APT_HIDDEN eippIndex : public edspLikeIndex
+{
+protected:
+   APT_HIDDEN virtual pkgCacheListParser * CreateListParser(FileFd &Pkg) APT_OVERRIDE;
+   virtual std::string GetComponent() const APT_OVERRIDE;
+
+public:
+   virtual const Type *GetType() const APT_OVERRIDE APT_CONST;
+
+   eippIndex(std::string const &File);
+   virtual ~eippIndex();
+};
+
 #endif
 #endif

+ 54 - 0
apt-pkg/edsp/edsplistparser.cc

@@ -125,5 +125,59 @@ bool edspListParser::ParseStatus(pkgCache::PkgIterator &Pkg,
 }
 }
 									/*}}}*/
 									/*}}}*/
 
 
+// ListParser::eippListParser - Constructor				/*{{{*/
+eippListParser::eippListParser(FileFd *File) : edspLikeListParser(File)
+{
+}
+									/*}}}*/
+// ListParser::ParseStatus - Parse the status field			/*{{{*/
+// ---------------------------------------------------------------------
+/* The Status: line here is not a normal dpkg one but just one which tells
+   use if the package is installed or not, where missing means not. */
+bool eippListParser::ParseStatus(pkgCache::PkgIterator &Pkg,
+				pkgCache::VerIterator &Ver)
+{
+   // Process the flag field
+   static std::array<WordList, 8> const statusvalues = {{
+      {"not-installed",pkgCache::State::NotInstalled},
+      {"config-files",pkgCache::State::ConfigFiles},
+      {"half-installed",pkgCache::State::HalfInstalled},
+      {"unpacked",pkgCache::State::UnPacked},
+      {"half-configured",pkgCache::State::HalfConfigured},
+      {"triggers-awaited",pkgCache::State::TriggersAwaited},
+      {"triggers-pending",pkgCache::State::TriggersPending},
+      {"installed",pkgCache::State::Installed},
+   }};
+   auto const status = Section.Find("Status");
+   if (status.empty() == false)
+   {
+      for (auto && sv: statusvalues)
+      {
+	 if (status != sv.Str)
+	    continue;
+	 Pkg->CurrentState = sv.Val;
+	 switch (Pkg->CurrentState)
+	 {
+	    case pkgCache::State::NotInstalled:
+	    case pkgCache::State::ConfigFiles:
+	       break;
+	    case pkgCache::State::HalfInstalled:
+	    case pkgCache::State::UnPacked:
+	    case pkgCache::State::HalfConfigured:
+	    case pkgCache::State::TriggersAwaited:
+	    case pkgCache::State::TriggersPending:
+	    case pkgCache::State::Installed:
+	       Pkg->CurrentVer = Ver.Index();
+	       break;
+	 }
+	 break;
+      }
+   }
+
+   return true;
+}
+									/*}}}*/
+
 edspLikeListParser::~edspLikeListParser() {}
 edspLikeListParser::~edspLikeListParser() {}
 edspListParser::~edspListParser() {}
 edspListParser::~edspListParser() {}
+eippListParser::~eippListParser() {}

+ 10 - 0
apt-pkg/edsp/edsplistparser.h

@@ -51,4 +51,14 @@ public:
    edspListParser(FileFd *File);
    edspListParser(FileFd *File);
    virtual ~edspListParser();
    virtual ~edspListParser();
 };
 };
+
+class APT_HIDDEN eippListParser : public edspLikeListParser
+{
+protected:
+   virtual bool ParseStatus(pkgCache::PkgIterator &Pkg,pkgCache::VerIterator &Ver) APT_OVERRIDE;
+
+public:
+   eippListParser(FileFd *File);
+   virtual ~eippListParser();
+};
 #endif
 #endif

+ 18 - 0
apt-pkg/edsp/edspsystem.cc

@@ -34,6 +34,9 @@ edspLikeSystem::edspLikeSystem(char const * const Label) : pkgSystem(Label, &deb
 }
 }
 edspSystem::edspSystem() : edspLikeSystem("Debian APT solver interface")
 edspSystem::edspSystem() : edspLikeSystem("Debian APT solver interface")
 {
 {
+}
+eippSystem::eippSystem() : edspLikeSystem("Debian APT planer interface")
+{
 }
 }
 									/*}}}*/
 									/*}}}*/
 // System::Lock - Get the lock						/*{{{*/
 // System::Lock - Get the lock						/*{{{*/
@@ -134,6 +137,19 @@ bool edspSystem::AddStatusFiles(std::vector<pkgIndexFile *> &List)	/*{{{*/
    return true;
    return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
+bool eippSystem::AddStatusFiles(std::vector<pkgIndexFile *> &List)	/*{{{*/
+{
+   if (StatusFile == nullptr)
+   {
+      if (_config->Find("eipp::scenario", "") == "/nonexistent/stdin")
+	 StatusFile.reset(new eippIndex("/nonexistent/stdin"));
+      else
+	 StatusFile.reset(new eippIndex(_config->FindFile("eipp::scenario")));
+   }
+   List.push_back(StatusFile.get());
+   return true;
+}
+									/*}}}*/
 
 
 edspLikeSystem::~edspLikeSystem() {}
 edspLikeSystem::~edspLikeSystem() {}
 edspSystem::~edspSystem()
 edspSystem::~edspSystem()
@@ -145,5 +161,7 @@ edspSystem::~edspSystem()
    RemoveFile("~edspSystem", tempPrefsFile);
    RemoveFile("~edspSystem", tempPrefsFile);
    rmdir(tempDir.c_str());
    rmdir(tempDir.c_str());
 }
 }
+eippSystem::~eippSystem() {}
 
 
 APT_HIDDEN edspSystem edspSys;
 APT_HIDDEN edspSystem edspSys;
+APT_HIDDEN eippSystem eippSys;

+ 9 - 0
apt-pkg/edsp/edspsystem.h

@@ -57,4 +57,13 @@ public:
    virtual ~edspSystem();
    virtual ~edspSystem();
 };
 };
 
 
+class APT_HIDDEN eippSystem : public edspLikeSystem
+{
+   public:
+   virtual bool AddStatusFiles(std::vector<pkgIndexFile *> &List) APT_OVERRIDE;
+
+   eippSystem();
+   virtual ~eippSystem();
+};
+
 #endif
 #endif

+ 5 - 1
apt-pkg/packagemanager.cc

@@ -1039,8 +1039,12 @@ pkgPackageManager::OrderResult pkgPackageManager::OrderInstall()
 
 
    std::string const planer = _config->Find("APT::Planer", "internal");
    std::string const planer = _config->Find("APT::Planer", "internal");
    if (planer != "internal")
    if (planer != "internal")
-      if (EIPP::OrderInstall(planer.c_str(), Cache, 0, nullptr) == false)
+   {
+      if (EIPP::OrderInstall(planer.c_str(), this, 0, nullptr))
+	 return Completed;
+      else
 	 return Failed;
 	 return Failed;
+   }
 
 
    bool const ordering =
    bool const ordering =
 	_config->FindB("PackageManager::UnpackAll",true) ?
 	_config->FindB("PackageManager::UnpackAll",true) ?

+ 8 - 1
apt-pkg/packagemanager.h

@@ -24,6 +24,7 @@
 
 
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/init.h>
 #include <apt-pkg/init.h>
+#include <apt-pkg/edsp.h>
 #include <apt-pkg/macros.h>
 #include <apt-pkg/macros.h>
 
 
 #include <string>
 #include <string>
@@ -43,13 +44,14 @@ class pkgDepCache;
 class pkgSourceList;
 class pkgSourceList;
 class pkgOrderList;
 class pkgOrderList;
 class pkgRecords;
 class pkgRecords;
+class OpProgress;
+class pkgPackageManager;
 namespace APT {
 namespace APT {
    namespace Progress {
    namespace Progress {
       class PackageManager;
       class PackageManager;
    }
    }
 }
 }
 
 
-
 class pkgPackageManager : protected pkgCache::Namespace
 class pkgPackageManager : protected pkgCache::Namespace
 {
 {
    public:
    public:
@@ -115,6 +117,11 @@ class pkgPackageManager : protected pkgCache::Namespace
    // compat
    // compat
    APT_DEPRECATED_MSG("Use APT::Progress::PackageManager subclass instead of fd") OrderResult DoInstall(int statusFd=-1);
    APT_DEPRECATED_MSG("Use APT::Progress::PackageManager subclass instead of fd") OrderResult DoInstall(int statusFd=-1);
 
 
+   friend bool EIPP::OrderInstall(char const * const planer, pkgPackageManager * const PM,
+	 unsigned int const version, OpProgress * const Progress);
+   friend bool EIPP::ReadResponse(int const input, pkgPackageManager * const PM,
+	 OpProgress * const Progress);
+
    // stuff that needs to be done before the fork() of a library that
    // stuff that needs to be done before the fork() of a library that
    // uses apt
    // uses apt
    OrderResult DoInstallPreFork() {
    OrderResult DoInstallPreFork() {

+ 9 - 1
apt-private/private-cmndline.cc

@@ -160,6 +160,11 @@ static bool addArgumentsAPTFTPArchive(std::vector<CommandLine::Args> &Args, char
    return true;
    return true;
 }
 }
 									/*}}}*/
 									/*}}}*/
+static bool addArgumentsAPTInternalPlaner(std::vector<CommandLine::Args> &, char const * const)/*{{{*/
+{
+   return true;
+}
+									/*}}}*/
 static bool addArgumentsAPTInternalSolver(std::vector<CommandLine::Args> &, char const * const)/*{{{*/
 static bool addArgumentsAPTInternalSolver(std::vector<CommandLine::Args> &, char const * const)/*{{{*/
 {
 {
    return true;
    return true;
@@ -356,6 +361,7 @@ std::vector<CommandLine::Args> getCommandArgs(APT_CMD const Program, char const
 	 case APT_CMD::APT_EXTRACTTEMPLATES: addArgumentsAPTExtractTemplates(Args, Cmd); break;
 	 case APT_CMD::APT_EXTRACTTEMPLATES: addArgumentsAPTExtractTemplates(Args, Cmd); break;
 	 case APT_CMD::APT_FTPARCHIVE: addArgumentsAPTFTPArchive(Args, Cmd); break;
 	 case APT_CMD::APT_FTPARCHIVE: addArgumentsAPTFTPArchive(Args, Cmd); break;
 	 case APT_CMD::APT_HELPER: addArgumentsAPTHelper(Args, Cmd); break;
 	 case APT_CMD::APT_HELPER: addArgumentsAPTHelper(Args, Cmd); break;
+	 case APT_CMD::APT_INTERNAL_PLANER: addArgumentsAPTInternalPlaner(Args, Cmd); break;
 	 case APT_CMD::APT_INTERNAL_SOLVER: addArgumentsAPTInternalSolver(Args, Cmd); break;
 	 case APT_CMD::APT_INTERNAL_SOLVER: addArgumentsAPTInternalSolver(Args, Cmd); break;
 	 case APT_CMD::APT_MARK: addArgumentsAPTMark(Args, Cmd); break;
 	 case APT_CMD::APT_MARK: addArgumentsAPTMark(Args, Cmd); break;
 	 case APT_CMD::APT_SORTPKG: addArgumentsAPTSortPkgs(Args, Cmd); break;
 	 case APT_CMD::APT_SORTPKG: addArgumentsAPTSortPkgs(Args, Cmd); break;
@@ -412,13 +418,15 @@ static bool ShowCommonHelp(APT_CMD const Binary, CommandLine &CmdL, std::vector<
       case APT_CMD::APT_FTPARCHIVE: cmd = "apt-ftparchive(1)"; break;
       case APT_CMD::APT_FTPARCHIVE: cmd = "apt-ftparchive(1)"; break;
       case APT_CMD::APT_GET: cmd = "apt-get(8)"; break;
       case APT_CMD::APT_GET: cmd = "apt-get(8)"; break;
       case APT_CMD::APT_HELPER: cmd = nullptr; break;
       case APT_CMD::APT_HELPER: cmd = nullptr; break;
+      case APT_CMD::APT_INTERNAL_PLANER: cmd = nullptr; break;
       case APT_CMD::APT_INTERNAL_SOLVER: cmd = nullptr; break;
       case APT_CMD::APT_INTERNAL_SOLVER: cmd = nullptr; break;
       case APT_CMD::APT_MARK: cmd = "apt-mark(8)"; break;
       case APT_CMD::APT_MARK: cmd = "apt-mark(8)"; break;
       case APT_CMD::APT_SORTPKG: cmd = "apt-sortpkgs(1)"; break;
       case APT_CMD::APT_SORTPKG: cmd = "apt-sortpkgs(1)"; break;
    }
    }
    if (cmd != nullptr)
    if (cmd != nullptr)
       ioprintf(std::cout, _("See %s for more information about the available commands."), cmd);
       ioprintf(std::cout, _("See %s for more information about the available commands."), cmd);
-   if (Binary != APT_CMD::APT_DUMP_SOLVER && Binary != APT_CMD::APT_INTERNAL_SOLVER)
+   if (Binary != APT_CMD::APT_DUMP_SOLVER && Binary != APT_CMD::APT_INTERNAL_SOLVER &&
+	 Binary != APT_CMD::APT_INTERNAL_PLANER)
       std::cout << std::endl <<
       std::cout << std::endl <<
 	 _("Configuration options and syntax is detailed in apt.conf(5).\n"
 	 _("Configuration options and syntax is detailed in apt.conf(5).\n"
 	       "Information about how to configure sources can be found in sources.list(5).\n"
 	       "Information about how to configure sources can be found in sources.list(5).\n"

+ 1 - 0
apt-private/private-cmndline.h

@@ -22,6 +22,7 @@ enum class APT_CMD {
    APT_MARK,
    APT_MARK,
    APT_SORTPKG,
    APT_SORTPKG,
    APT_DUMP_SOLVER,
    APT_DUMP_SOLVER,
+   APT_INTERNAL_PLANER,
 };
 };
 struct aptDispatchWithHelp
 struct aptDispatchWithHelp
 {
 {

+ 1 - 0
apt-private/private-main.cc

@@ -38,6 +38,7 @@ void InitLocale(APT_CMD const binary)				/*{{{*/
 	 break;
 	 break;
       case APT_CMD::APT_EXTRACTTEMPLATES:
       case APT_CMD::APT_EXTRACTTEMPLATES:
       case APT_CMD::APT_FTPARCHIVE:
       case APT_CMD::APT_FTPARCHIVE:
+      case APT_CMD::APT_INTERNAL_PLANER:
       case APT_CMD::APT_INTERNAL_SOLVER:
       case APT_CMD::APT_INTERNAL_SOLVER:
       case APT_CMD::APT_SORTPKG:
       case APT_CMD::APT_SORTPKG:
 	 textdomain("apt-utils");
 	 textdomain("apt-utils");

+ 187 - 0
cmdline/apt-internal-planer.cc

@@ -0,0 +1,187 @@
+// -*- mode: cpp; mode: fold -*-
+// Description								/*{{{*/
+/* #####################################################################
+
+   cover around the internal solver to be able to run it like an external
+
+   ##################################################################### */
+									/*}}}*/
+// Include Files							/*{{{*/
+#include <config.h>
+
+#include <apt-pkg/error.h>
+#include <apt-pkg/cmndline.h>
+#include <apt-pkg/init.h>
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cacheset.h>
+#include <apt-pkg/strutl.h>
+#include <apt-pkg/edsp.h>
+#include <apt-pkg/fileutl.h>
+#include <apt-pkg/pkgsystem.h>
+#include <apt-pkg/configuration.h>
+#include <apt-pkg/packagemanager.h>
+#include <apt-pkg/prettyprinters.h>
+#include <apt-pkg/depcache.h>
+#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/cacheiterators.h>
+
+#include <apt-private/private-output.h>
+#include <apt-private/private-cmndline.h>
+#include <apt-private/private-main.h>
+
+#include <string.h>
+#include <iostream>
+#include <sstream>
+#include <list>
+#include <string>
+#include <unistd.h>
+#include <cstdio>
+#include <stdlib.h>
+
+#include <apti18n.h>
+									/*}}}*/
+
+static bool ShowHelp(CommandLine &)					/*{{{*/
+{
+	std::cout <<
+		_("Usage: apt-internal-planer\n"
+		"\n"
+		"apt-internal-planer is an interface to use the current internal\n"
+		"installation planer for the APT family like an external one,\n"
+		"for debugging or the like.\n");
+	return true;
+}
+									/*}}}*/
+APT_NORETURN static void DIE(std::string const &message) {		/*{{{*/
+	std::cerr << "ERROR: " << message << std::endl;
+	_error->DumpErrors(std::cerr);
+	exit(EXIT_FAILURE);
+}
+									/*}}}*/
+static std::vector<aptDispatchWithHelp> GetCommands()			/*{{{*/
+{
+   return {};
+}
+									/*}}}*/
+class PMOutput: public pkgPackageManager				/*{{{*/
+{
+   FileFd &output;
+   bool const Debug;
+
+protected:
+   virtual bool Install(PkgIterator Pkg,std::string) APT_OVERRIDE
+   {
+      //std::cerr << "INSTALL: " << APT::PrettyPkg(&Cache, Pkg) << std::endl;
+      return EDSP::WriteSolutionStanza(output, "Install", Cache[Pkg].InstVerIter(Cache));
+   }
+   virtual bool Configure(PkgIterator Pkg) APT_OVERRIDE
+   {
+      //std::cerr << "CONFIGURE: " << APT::PrettyPkg(&Cache, Pkg) << " " << std::endl;
+      return EDSP::WriteSolutionStanza(output, "Configure", Cache[Pkg].InstVerIter(Cache));
+   }
+   virtual bool Remove(PkgIterator Pkg,bool) APT_OVERRIDE
+   {
+      //std::cerr << "REMOVE: " << APT::PrettyPkg(&Cache, Pkg) << " " << std::endl;
+      return EDSP::WriteSolutionStanza(output, "Remove", Pkg.CurrentVer());
+   }
+public:
+   PMOutput(pkgDepCache *Cache, FileFd &file) : pkgPackageManager(Cache), output(file),
+      Debug(_config->FindB("Debug::EDSP::WriteSolution", false))
+   {}
+
+   bool ApplyRequest(std::list<std::pair<std::string,EIPP::PKG_ACTION>> const &actions)
+   {
+      for (auto && a: actions)
+      {
+	 auto const Pkg = Cache.FindPkg(a.first);
+	 if (unlikely(Pkg.end() == true))
+	    continue;
+	 switch (a.second)
+	 {
+	    case EIPP::PKG_ACTION::NOOP:
+	       break;
+	    case EIPP::PKG_ACTION::INSTALL:
+	    case EIPP::PKG_ACTION::REINSTALL:
+	       FileNames[Pkg->ID] = "EIPP";
+	       break;
+	    case EIPP::PKG_ACTION::REMOVE:
+	       break;
+	 }
+      }
+      return true;
+   }
+};
+									/*}}}*/
+int main(int argc,const char *argv[])					/*{{{*/
+{
+	// we really don't need anything
+	DropPrivileges();
+
+	CommandLine CmdL;
+	ParseCommandLine(CmdL, APT_CMD::APT_INTERNAL_PLANER, &_config, NULL, argc, argv, &ShowHelp, &GetCommands);
+
+	// Deal with stdout not being a tty
+	if (!isatty(STDOUT_FILENO) && _config->FindI("quiet", -1) == -1)
+		_config->Set("quiet","1");
+
+	if (_config->FindI("quiet", 0) < 1)
+		_config->Set("Debug::EIPP::WriteSolution", true);
+
+	_config->Set("APT::System", "Debian APT planer interface");
+	_config->Set("APT::Planer", "internal");
+	_config->Set("eipp::scenario", "/nonexistent/stdin");
+	FileFd output;
+	if (output.OpenDescriptor(STDOUT_FILENO, FileFd::WriteOnly | FileFd::BufferedWrite, true) == false)
+	   DIE("stdout couldn't be opened");
+	int const input = STDIN_FILENO;
+	SetNonBlock(input, false);
+
+	EDSP::WriteProgress(0, "Start up planer…", output);
+
+	if (pkgInitSystem(*_config,_system) == false)
+		DIE("System could not be initialized!");
+
+	EDSP::WriteProgress(1, "Read request…", output);
+
+	if (WaitFd(input, false, 5) == false)
+		DIE("WAIT timed out in the planer");
+
+	std::list<std::pair<std::string,EIPP::PKG_ACTION>> actions;
+	unsigned int flags;
+	if (EIPP::ReadRequest(input, actions, flags) == false)
+		DIE("Parsing the request failed!");
+
+	EDSP::WriteProgress(5, "Read scenario…", output);
+
+	pkgCacheFile CacheFile;
+	if (CacheFile.Open(NULL, false) == false)
+		DIE("Failed to open CacheFile!");
+
+	EDSP::WriteProgress(50, "Apply request on scenario…", output);
+
+	if (EIPP::ApplyRequest(actions, CacheFile) == false)
+		DIE("Failed to apply request to depcache!");
+
+	EDSP::WriteProgress(60, "Call orderinstall on current scenario…", output);
+
+	//_config->Set("Debug::pkgOrderList", true);
+	//_config->Set("Debug::pkgPackageManager", true);
+	PMOutput PM(CacheFile, output);
+	if (PM.ApplyRequest(actions) == false)
+		DIE("Failed to apply request to packagemanager!");
+	pkgPackageManager::OrderResult const Res = PM.DoInstallPreFork();
+	switch (Res)
+	{
+	   case pkgPackageManager::Completed:
+	      EDSP::WriteProgress(100, "Done", output);
+	      break;
+	   case pkgPackageManager::Incomplete:
+	      EDSP::WriteError("pm-incomplete", "Planer could only plan Incompletely", output);
+	   case pkgPackageManager::Failed:
+	      EDSP::WriteError("pm-failed", "Planer failed to find an order", output);
+	      break;
+	}
+
+	return DispatchCommandLine(CmdL, {});
+}
+									/*}}}*/

+ 7 - 1
cmdline/makefile

@@ -79,13 +79,19 @@ LIB_MAKES = apt-pkg/makefile apt-inst/makefile apt-private/makefile
 SOURCE = apt-extracttemplates.cc 
 SOURCE = apt-extracttemplates.cc 
 include $(PROGRAM_H)
 include $(PROGRAM_H)
 
 
-# The internal solver acting as an external
+# The internal solver/planer acting as an external
 PROGRAM=apt-internal-solver
 PROGRAM=apt-internal-solver
 SLIBS = -lapt-pkg -lapt-private $(INTLLIBS)
 SLIBS = -lapt-pkg -lapt-private $(INTLLIBS)
 LIB_MAKES = apt-pkg/makefile apt-private/makefile
 LIB_MAKES = apt-pkg/makefile apt-private/makefile
 SOURCE = apt-internal-solver.cc
 SOURCE = apt-internal-solver.cc
 include $(PROGRAM_H)
 include $(PROGRAM_H)
 
 
+PROGRAM=apt-internal-planer
+SLIBS = -lapt-pkg -lapt-private $(INTLLIBS)
+LIB_MAKES = apt-pkg/makefile apt-private/makefile
+SOURCE = apt-internal-planer.cc
+include $(PROGRAM_H)
+
 # This just dumps out the state
 # This just dumps out the state
 PROGRAM=apt-dump-solver
 PROGRAM=apt-dump-solver
 SLIBS = -lapt-pkg -lapt-private $(INTLLIBS)
 SLIBS = -lapt-pkg -lapt-private $(INTLLIBS)

+ 1 - 0
debian/apt-utils.dirs

@@ -1,2 +1,3 @@
 usr/lib/apt/solvers
 usr/lib/apt/solvers
+usr/lib/apt/planers
 usr/bin
 usr/bin

+ 2 - 1
debian/apt.dirs

@@ -1,6 +1,7 @@
 usr/bin
 usr/bin
 usr/lib/apt/methods
 usr/lib/apt/methods
 usr/lib/apt/solvers
 usr/lib/apt/solvers
+usr/lib/apt/planers
 usr/lib/dpkg/methods/apt
 usr/lib/dpkg/methods/apt
 etc/apt
 etc/apt
 etc/apt/apt.conf.d
 etc/apt/apt.conf.d
@@ -16,4 +17,4 @@ var/lib/apt/periodic
 var/log/apt
 var/log/apt
 usr/share/bug/apt
 usr/share/bug/apt
 usr/share/bash-completion/completions/
 usr/share/bash-completion/completions/
-lib/systemd/system/
+lib/systemd/system/

+ 0 - 2
debian/apt.lintian-overrides

@@ -1,4 +1,2 @@
 # the private library is for internal sharing only
 # the private library is for internal sharing only
 apt: package-name-doesnt-match-sonames
 apt: package-name-doesnt-match-sonames
-# external solvers are shipped in here:
-apt: package-contains-empty-directory

+ 3 - 1
debian/rules

@@ -189,8 +189,9 @@ apt: build-binary build-manpages debian/apt.install
 	dh_install -p$@ --sourcedir=$(BLD)
 	dh_install -p$@ --sourcedir=$(BLD)
 
 
 	# Remove the bits that are in apt-utils
 	# Remove the bits that are in apt-utils
-	rm $(addprefix debian/$@/usr/bin/apt-,$(APT_UTILS) dump-solver internal-solver)
+	rm $(addprefix debian/$@/usr/bin/apt-,$(APT_UTILS) dump-solver internal-solver internal-planer)
 	cp $(BLD)/bin/apt-dump-solver debian/$@/usr/lib/apt/solvers/dump
 	cp $(BLD)/bin/apt-dump-solver debian/$@/usr/lib/apt/solvers/dump
+	ln -s ../solvers/dump debian/$@/usr/lib/apt/planers/dump
 
 
 	# https has its own package
 	# https has its own package
 	rm debian/$@/usr/lib/apt/methods/https
 	rm debian/$@/usr/lib/apt/methods/https
@@ -252,6 +253,7 @@ apt-utils: build-binary build-manpages
 
 
 	cp $(addprefix $(BLD)/bin/apt-,$(APT_UTILS)) debian/$@/usr/bin/
 	cp $(addprefix $(BLD)/bin/apt-,$(APT_UTILS)) debian/$@/usr/bin/
 	cp $(BLD)/bin/apt-internal-solver debian/$@/usr/lib/apt/solvers/apt
 	cp $(BLD)/bin/apt-internal-solver debian/$@/usr/lib/apt/solvers/apt
+	cp $(BLD)/bin/apt-internal-planer debian/$@/usr/lib/apt/planers/apt
 
 
 	dh_install -p$@ --sourcedir=$(BLD)
 	dh_install -p$@ --sourcedir=$(BLD)
 	dh_link -p$@
 	dh_link -p$@

+ 1 - 0
debian/tests/run-tests

@@ -17,6 +17,7 @@ APT_INTEGRATION_TESTS_METHODS_DIR=/usr/lib/apt/methods \
 APT_INTEGRATION_TESTS_LIBEXEC_DIR=/usr/lib/apt/ \
 APT_INTEGRATION_TESTS_LIBEXEC_DIR=/usr/lib/apt/ \
 APT_INTEGRATION_TESTS_INTERNAL_SOLVER=/usr/lib/apt/solvers/apt \
 APT_INTEGRATION_TESTS_INTERNAL_SOLVER=/usr/lib/apt/solvers/apt \
 APT_INTEGRATION_TESTS_DUMP_SOLVER=/usr/lib/apt/solvers/dump \
 APT_INTEGRATION_TESTS_DUMP_SOLVER=/usr/lib/apt/solvers/dump \
+APT_INTEGRATION_TESTS_INTERNAL_PLANER=/usr/lib/apt/planers/apt \
 APT_INTEGRATION_TESTS_BUILD_DIR=/usr/bin \
 APT_INTEGRATION_TESTS_BUILD_DIR=/usr/bin \
 APT_INTEGRATION_TESTS_LIBRARY_PATH=/dev/null/does/not/exist \
 APT_INTEGRATION_TESTS_LIBRARY_PATH=/dev/null/does/not/exist \
 ./test/integration/run-tests -q
 ./test/integration/run-tests -q

+ 3 - 0
test/integration/framework

@@ -191,6 +191,7 @@ aptitude() { runapt aptitude "$@"; }
 aptextracttemplates() { runapt apt-extracttemplates "$@"; }
 aptextracttemplates() { runapt apt-extracttemplates "$@"; }
 aptinternalsolver() { runapt "${APTINTERNALSOLVER}" "$@"; }
 aptinternalsolver() { runapt "${APTINTERNALSOLVER}" "$@"; }
 aptdumpsolver() { runapt "${APTDUMPSOLVER}" "$@"; }
 aptdumpsolver() { runapt "${APTDUMPSOLVER}" "$@"; }
+aptinternalplaner() { runapt "${APTINTERNALPLANER}" "$@"; }
 
 
 dpkg() {
 dpkg() {
 	"${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" "$@"
 	"${TMPWORKINGDIRECTORY}/rootdir/usr/bin/dpkg" "$@"
@@ -289,6 +290,7 @@ setupenvironment() {
         APTWEBSERVERBINDIR="${APT_INTEGRATION_TESTS_WEBSERVER_BIN_DIR:-"${BUILDDIRECTORY}"}"
         APTWEBSERVERBINDIR="${APT_INTEGRATION_TESTS_WEBSERVER_BIN_DIR:-"${BUILDDIRECTORY}"}"
         APTINTERNALSOLVER="${APT_INTEGRATION_TESTS_INTERNAL_SOLVER:-"${BUILDDIRECTORY}/apt-internal-solver"}"
         APTINTERNALSOLVER="${APT_INTEGRATION_TESTS_INTERNAL_SOLVER:-"${BUILDDIRECTORY}/apt-internal-solver"}"
 	APTDUMPSOLVER="${APT_INTEGRATION_TESTS_DUMP_SOLVER:-"${BUILDDIRECTORY}/apt-dump-solver"}"
 	APTDUMPSOLVER="${APT_INTEGRATION_TESTS_DUMP_SOLVER:-"${BUILDDIRECTORY}/apt-dump-solver"}"
+	APTINTERNALPLANER="${APT_INTEGRATION_TESTS_INTERNAL_PLANER:-"${BUILDDIRECTORY}/apt-internal-planer"}"
 	test -x "${BUILDDIRECTORY}/apt-get" || msgdie "You need to build tree first"
 	test -x "${BUILDDIRECTORY}/apt-get" || msgdie "You need to build tree first"
         # -----
         # -----
 
 
@@ -306,6 +308,7 @@ setupenvironment() {
 		ln -s "${BUILDDIRECTORY}/apt-dump-solver" usr/lib/apt/solvers/dump
 		ln -s "${BUILDDIRECTORY}/apt-dump-solver" usr/lib/apt/solvers/dump
 		ln -s "${BUILDDIRECTORY}/apt-dump-solver" usr/lib/apt/planers/dump
 		ln -s "${BUILDDIRECTORY}/apt-dump-solver" usr/lib/apt/planers/dump
 		ln -s "${BUILDDIRECTORY}/apt-internal-solver" usr/lib/apt/solvers/apt
 		ln -s "${BUILDDIRECTORY}/apt-internal-solver" usr/lib/apt/solvers/apt
+		ln -s "${BUILDDIRECTORY}/apt-internal-planer" usr/lib/apt/planers/apt
 		echo "Dir::Bin::Solvers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/solvers\";" > etc/apt/apt.conf.d/externalsolver.conf
 		echo "Dir::Bin::Solvers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/solvers\";" > etc/apt/apt.conf.d/externalsolver.conf
 		echo "Dir::Bin::Planers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/planers\";" > etc/apt/apt.conf.d/externalplaner.conf
 		echo "Dir::Bin::Planers \"${TMPWORKINGDIRECTORY}/rootdir/usr/lib/apt/planers\";" > etc/apt/apt.conf.d/externalplaner.conf
 	fi
 	fi

+ 1 - 1
test/integration/test-00-commands-have-help

@@ -49,7 +49,7 @@ for CMD in 'apt-cache' 'apt-cdrom' 'apt-config' \
 	checkoptions "$cmd"
 	checkoptions "$cmd"
 done
 done
 
 
-for CMD in 'apt-dump-solver'  'apt-internal-solver'; do
+for CMD in 'apt-dump-solver'  'apt-internal-solver' 'apt-internal-planer'; do
 	checkoptions "$(echo "$CMD" | tr -d '-')"
 	checkoptions "$(echo "$CMD" | tr -d '-')"
 done
 done
 
 

+ 3 - 0
test/integration/test-external-installation-planer-protocol

@@ -26,3 +26,6 @@ testfailure aptget install foo --planer dump -y
 testfailure grep 'unrelated-2' "$APT_EDSP_DUMP_FILENAME"
 testfailure grep 'unrelated-2' "$APT_EDSP_DUMP_FILENAME"
 testsuccessequal '2' grep -c '^Package: foo$' "$APT_EDSP_DUMP_FILENAME"
 testsuccessequal '2' grep -c '^Package: foo$' "$APT_EDSP_DUMP_FILENAME"
 testsuccessequal '1' grep -c '^Package: libfoo$' "$APT_EDSP_DUMP_FILENAME"
 testsuccessequal '1' grep -c '^Package: libfoo$' "$APT_EDSP_DUMP_FILENAME"
+#less "$APT_EDSP_DUMP_FILENAME"
+
+aptget install foo -ys #--planer apt