[PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)

classic Classic list List threaded Threaded
75 messages Options
1234
Reply | Threaded
Open this post in threaded view
|

[PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)

Jon TURNEY
... solve some problems, perhaps add some new ones, I guess.  I'm not 100%
sure this is the right approach to take, but I wrote it, so here it is.

This replaces the current PackageVersion class with a similar one which stores
the information in a libsolv pool, and the current depsolver with the libsolv
solver (as used by zypper, dnf and others).

Immediately, this enables:
- use of a version relation in package dependencies
- an obsoletes: relation between packages

This also makes it much easier to support:
- version numbers with an epoch component (I think just how we handle ':' in
filenames needs auditing to make this work)
- other commonly-implemented package relations such as conflicts:, provides:
etc.

What remains to be done:
- I've dodged a lot of the UI issues: If the solver reports problems, all that
can be done is accept the default solution or cancel.  This possibly isn't a
big problem until we have a package set which can contain problems...
- We had a very poor UI for showing what will actually be done (combine in
your head the "Pending" view with packages listed in the text on the
PrereChecker page), and this removes part of that
- As implemented, selecting "Current" overrides "Keep".  This is wrong, and a
change from current behaviour, but is probably a symptom of some deeper
confusion in the picker UI I'm not sure how to address

libsolv needs to be lightly patched to build for Win32, see [1].  I will ITP
that, but it probably also needs an RPM to support cross-building.

[1] https://github.com/jon-turney/libsolv

Jon Turney (14):
  Opaque how PackageDepends is stored
  Factor out reading installed.db
  Hoist addScript() etc. up from packageversion to packagemeta
  Hoist pick() up to packagemeta
  Hoist uninstall up to Installer::uninstallOne()
  Hoist scan() up from packageversion to packagemeta
  Store package stability in class packageversion
  Change to using a libsolv pool for storing package information
  Remove cygpackage class
  Remove packageversion class
  Drop in SolvableVersion as a replacement for packageversion
  Use solver to check for problems and produce a list of package
    transactions
  Download/checksum/install/uninstall what transaction wants
  Add obsoletes: support

 IniDBBuilderPackage.cc  | 331 ++++++++---------------
 IniDBBuilderPackage.h   |  32 ++-
 Makefile.am             |   7 +-
 PackageSpecification.cc |  12 +
 PackageSpecification.h  |   7 +-
 PickPackageLine.cc      |  14 +-
 PickView.cc             |   9 +-
 bootstrap.sh            |   2 +-
 choose.cc               |   8 +-
 configure.ac            |   1 +
 cygpackage.cc           | 187 -------------
 cygpackage.h            |  88 -------
 desktop.cc              |   1 -
 download.cc             |  72 ++---
 ini.cc                  |   1 +
 inilex.ll               |   1 +
 iniparse.yy             |   3 +
 install.cc              | 176 ++++++++-----
 libsolv.cc              | 689 ++++++++++++++++++++++++++++++++++++++++++++++++
 libsolv.h               | 231 ++++++++++++++++
 package_db.cc           | 115 ++++++--
 package_db.h            |   8 +
 package_depends.cc      |  19 +-
 package_depends.h       |   2 +-
 package_meta.cc         | 294 ++++++++++++---------
 package_meta.h          |  30 ++-
 package_version.cc      | 414 -----------------------------
 package_version.h       | 190 +------------
 postinstall.cc          |   6 +-
 prereq.cc               | 206 ++++-----------
 prereq.h                |  22 +-
 res.rc                  |   4 +-
 32 files changed, 1606 insertions(+), 1576 deletions(-)
 delete mode 100644 cygpackage.cc
 delete mode 100644 cygpackage.h
 create mode 100644 libsolv.cc
 create mode 100644 libsolv.h
 delete mode 100644 package_version.cc

--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 01/14] Opaque how PackageDepends is stored

Jon TURNEY
We want to be more opaque about how the PackageDepends for a packageversion
is stored, so rather than exposing a pointer to a PackageDepends object
inside class packageversion, access it by value.

This also makes us be more explicit about set/get of package depends()

Fix some iterations to deal with depends() returning a value rather than a
pointer.

Also adjust dumpPackageDepends() appropriately
Also remove useless debug showing the dependency list before we build it.
---
 IniDBBuilderPackage.cc | 30 ++++++++++++------------------
 IniDBBuilderPackage.h  |  2 +-
 package_db.cc          | 10 ++++++----
 package_depends.cc     | 19 ++++++++-----------
 package_depends.h      |  2 +-
 package_meta.cc        | 11 ++++-------
 package_version.cc     | 10 +++++-----
 package_version.h      |  5 ++---
 prereq.cc              |  4 ++--
 9 files changed, 41 insertions(+), 52 deletions(-)

diff --git a/IniDBBuilderPackage.cc b/IniDBBuilderPackage.cc
index ad1cc88..0e1be78 100644
--- a/IniDBBuilderPackage.cc
+++ b/IniDBBuilderPackage.cc
@@ -34,7 +34,7 @@
 using namespace std;
 
 IniDBBuilderPackage::IniDBBuilderPackage (IniParseFeedback const &aFeedback) :
-cp (0), cbpv (), cspv (), currentSpec (0), currentNodeList (0), trust (0), _feedback (aFeedback){}
+cp (0), cbpv (), cspv (), currentSpec (0), trust (0), _feedback (aFeedback){}
 
 IniDBBuilderPackage::~IniDBBuilderPackage()
 {
@@ -92,7 +92,7 @@ IniDBBuilderPackage::buildPackage (const std::string& name)
   cbpv = cygpackage::createInstance (name, package_binary);
   cspv = packageversion ();
   currentSpec = NULL;
-  currentNodeList = NULL;
+  currentNodeList = PackageDepends();
   trust = TRUST_CURR;
 #if DEBUG
   Log (LOG_BABBLE) << "Created package " << name << endLog;
@@ -231,12 +231,10 @@ void
 IniDBBuilderPackage::buildBeginDepends ()
 {
 #if DEBUG
-  Log (LOG_BABBLE) << "Beginning of a depends statement for " << cp->name
-    << endLog;
-  dumpPackageDepends (currentNodeList, Log (LOG_BABBLE));
+  Log (LOG_BABBLE) << "Beginning of a depends statement " << endLog;
 #endif
   currentSpec = NULL;
-  currentNodeList = cbpv.depends();
+  currentNodeList = PackageDepends();
 }
 
 void
@@ -246,7 +244,7 @@ IniDBBuilderPackage::buildBeginBuildDepends ()
   Log (LOG_BABBLE) << "Beginning of a Build-Depends statement" << endLog;
 #endif
   currentSpec = NULL;
-  currentNodeList = NULL; /* there is currently nowhere to store Build-Depends information */
+  currentNodeList = PackageDepends(); /* there is currently nowhere to store Build-Depends information */
 }
 
 void
@@ -287,14 +285,11 @@ IniDBBuilderPackage::buildSourceNameVersion (const std::string& version)
 void
 IniDBBuilderPackage::buildPackageListNode (const std::string & name)
 {
-  if (currentNodeList)
-    {
 #if DEBUG
-      Log (LOG_BABBLE) << "New node '" << name << "' for package list" << endLog;
+  Log (LOG_BABBLE) << "New node '" << name << "' for package list" << endLog;
 #endif
-      currentSpec = new PackageSpecification (name);
-      currentNodeList->push_back (currentSpec);
-    }
+  currentSpec = new PackageSpecification (name);
+  currentNodeList.push_back (currentSpec);
 }
 
 void
@@ -337,8 +332,7 @@ IniDBBuilderPackage::buildPackageListOperatorVersion (const std::string& aVersio
 void
 IniDBBuilderPackage::add_correct_version()
 {
-  if (currentNodeList)
-    *cbpv.depends() = *currentNodeList;
+  cbpv.setDepends(currentNodeList);
 
   int merged = 0;
   for (set<packageversion>::iterator n = cp->versions.begin();
@@ -362,11 +356,11 @@ IniDBBuilderPackage::add_correct_version()
           ver.set_sdesc (cbpv.SDesc ());
         if (cbpv.LDesc ().size() && !n->LDesc ().size())
           ver.set_ldesc (cbpv.LDesc ());
- if (cbpv.depends()->size() && !ver.depends ()->size())
-  *ver.depends() = *cbpv.depends();
+ if (cbpv.depends().size() && !ver.depends().size())
+  ver.setDepends(cbpv.depends());
  /* TODO: other package lists */
  /* Prevent dangling references */
- currentNodeList = NULL;
+ currentNodeList = PackageDepends();
  currentSpec = NULL;
         cbpv = *n;
         merged = 1;
diff --git a/IniDBBuilderPackage.h b/IniDBBuilderPackage.h
index 766a5ef..dee65d4 100644
--- a/IniDBBuilderPackage.h
+++ b/IniDBBuilderPackage.h
@@ -79,7 +79,7 @@ private:
   packagemeta *csp;
   packageversion cspv;
   PackageSpecification *currentSpec;
-  PackageDepends *currentNodeList;
+  PackageDepends currentNodeList;
   int trust;
   IniParseFeedback const &_feedback;
 };
diff --git a/package_db.cc b/package_db.cc
index dbec17e..00395d3 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -312,8 +312,9 @@ ConnectedLoopFinder::visit(packagemeta *nodeToVisit)
   nodesInStronglyConnectedComponent.push(nodeToVisit);
 
   /* walk through each node */
-  PackageDepends::const_iterator dp = nodeToVisit->installed.depends()->begin();
-  while (dp != nodeToVisit->installed.depends()->end())
+  const PackageDepends deps = nodeToVisit->installed.depends();
+  PackageDepends::const_iterator dp = deps.begin();
+  while (dp != deps.end())
     {
       /* check for an installed match */
       if (checkForInstalled (*dp))
@@ -489,8 +490,9 @@ packagedb::guessUserPicked()
  continue;
 
       /* walk through each node */
-      std::vector <PackageSpecification *>::const_iterator dp = pkgm.installed.depends()->begin();
-      while (dp != pkgm.installed.depends()->end())
+      const PackageDepends deps = pkgm.installed.depends();
+      std::vector <PackageSpecification *>::const_iterator dp = deps.begin();
+      while (dp != deps.end())
  {
   /* check for an installed match */
           if (checkForInstalled(*dp))
diff --git a/package_depends.cc b/package_depends.cc
index e288c0b..a03f5a0 100644
--- a/package_depends.cc
+++ b/package_depends.cc
@@ -15,19 +15,16 @@
 #include <LogSingleton.h>
 
 void
-dumpPackageDepends (PackageDepends const *currentList,
+dumpPackageDepends (PackageDepends const &currentList,
                     std::ostream &logger)
 {
-  if (currentList)
+  Log (LOG_BABBLE) << "( ";
+  PackageDepends::const_iterator i = currentList.begin();
+  while (true)
     {
-      Log (LOG_BABBLE) << "( ";
-      PackageDepends::const_iterator i = currentList->begin();
-      while (true)
-        {
-          if (i == currentList->end()) break;
-          Log (LOG_BABBLE) << **i << " ";
-          ++i;
-        }
-      Log (LOG_BABBLE) << ")";
+      if (i == currentList.end()) break;
+      Log (LOG_BABBLE) << **i << " ";
+      ++i;
     }
+  Log (LOG_BABBLE) << ")";
 }
diff --git a/package_depends.h b/package_depends.h
index af3fa01..36f7f6f 100644
--- a/package_depends.h
+++ b/package_depends.h
@@ -19,6 +19,6 @@
 
 typedef std::vector <PackageSpecification *> PackageDepends;
 
-void dumpPackageDepends (PackageDepends const *currentList, std::ostream &);
+void dumpPackageDepends (PackageDepends const &currentList, std::ostream &);
 
 #endif // PACKAGE_DEPENDS_H
diff --git a/package_meta.cc b/package_meta.cc
index b35b554..1f548e2 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -560,13 +560,10 @@ packagemeta::logAllVersions () const
       {
  Log (LOG_BABBLE) << "    [" << trustLabel(*i) <<
   "] ver=" << i->Canonical_version() << endLog;
- if (i->depends()->size())
- {
-  std::ostream & logger = Log (LOG_BABBLE);
-  logger << "      depends=";
-  dumpPackageDepends(i->depends(), logger);
-  logger << endLog;
- }
+ std::ostream & logger = Log (LOG_BABBLE);
+ logger << "      depends=";
+ dumpPackageDepends(i->depends(), logger);
+ logger << endLog;
       }
 #if 0
     Log (LOG_BABBLE) << "      inst=" << i->
diff --git a/package_version.cc b/package_version.cc
index f022e70..7005850 100644
--- a/package_version.cc
+++ b/package_version.cc
@@ -229,16 +229,16 @@ packageversion::setSourcePackageSpecification (PackageSpecification const &spec)
   data->setSourcePackageSpecification(spec);
 }
 
-PackageDepends *
-packageversion::depends()
+void
+packageversion::setDepends(const PackageDepends _depends)
 {
-  return &data->depends;
+  data->depends = _depends;
 }
 
-const PackageDepends *
+const PackageDepends
 packageversion::depends() const
 {
-  return &data->depends;
+  return data->depends;
 }
 
 bool
diff --git a/package_version.h b/package_version.h
index d70eda0..c26593a 100644
--- a/package_version.h
+++ b/package_version.h
@@ -110,9 +110,8 @@ public:
   PackageSpecification & sourcePackageSpecification () const;
   void setSourcePackageSpecification (PackageSpecification const &);
 
-  /* invariant: these never return NULL */
-  PackageDepends *depends();
-  const PackageDepends *depends() const;
+  void setDepends(const PackageDepends);
+  const PackageDepends depends() const;
 
   bool picked() const;   /* true if this version is to be installed */
   void pick(bool, packagemeta *); /* trigger an install/reinsall */
diff --git a/prereq.cc b/prereq.cc
index eb8e21f..0e7fdf5 100644
--- a/prereq.cc
+++ b/prereq.cc
@@ -211,11 +211,11 @@ PrereqChecker::isMet ()
 
       // Fetch the dependencies of the package. This assumes that the
       // dependencies of all versions are all the same.
-      const PackageDepends *deps = pack->curr.depends ();
+      const PackageDepends deps = pack->curr.depends ();
 
       // go through the package's dependencies
       for (PackageDepends::const_iterator d =
-            deps->begin (); d != deps->end (); ++d)
+            deps.begin (); d != deps.end (); ++d)
         {
           PackageSpecification *dep_spec = *d;
           packagemeta *dep = db.findBinary (*dep_spec);
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 02/14] Factor out reading installed.db

Jon TURNEY
In reply to this post by Jon TURNEY
Rather that doing implicitly the first time a packagedb is constructed, do
it explicitly at a certain point in time that is early enough.
---
 ini.cc        | 4 +++-
 package_db.cc | 9 +++++++--
 package_db.h  | 1 +
 3 files changed, 11 insertions(+), 3 deletions(-)

diff --git a/ini.cc b/ini.cc
index 82990a2..1162b91 100644
--- a/ini.cc
+++ b/ini.cc
@@ -346,13 +346,15 @@ do_remote_ini (HWND owner)
 static bool
 do_ini_thread (HINSTANCE h, HWND owner)
 {
+  packagedb db;
+  db.read();
+
   size_t ini_count = 0;
   if (source == IDC_SOURCE_LOCALDIR)
     ini_count = do_local_ini (owner);
   else
     ini_count = do_remote_ini (owner);
 
-  packagedb db;
   db.upgrade();
 
   if (ini_count == 0)
diff --git a/package_db.cc b/package_db.cc
index 00395d3..3d6d0de 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -45,10 +45,15 @@ using namespace std;
 
 packagedb::packagedb ()
 {
-  io_stream *db = 0;
+}
+
+void
+packagedb::read ()
+{
   if (!installeddbread)
     {
-      /* no parameters. Read in the local installation database. */
+      /* Read in the local installation database. */
+      io_stream *db = 0;
       db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
       installeddbread = 1;
       if (!db)
diff --git a/package_db.h b/package_db.h
index d02dbc4..d26f8ad 100644
--- a/package_db.h
+++ b/package_db.h
@@ -63,6 +63,7 @@ class packagedb
 {
 public:
   packagedb ();
+  void read();
   /* 0 on success */
   int flush ();
   void upgrade ();
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 03/14] Hoist addScript() etc. up from packageversion to packagemeta

Jon TURNEY
In reply to this post by Jon TURNEY
We're only interesting in storing scripts and later running them from the
desired version as we install it, so despite being stored per
packageversion, this is only significant for the desired version.

Hoist it up from packageversion to packagemeta.
---
 install.cc         |  2 +-
 package_meta.cc    | 12 ++++++++++++
 package_meta.h     |  7 ++++++-
 package_version.cc | 28 ----------------------------
 package_version.h  |  8 --------
 postinstall.cc     |  6 +++---
 6 files changed, 22 insertions(+), 41 deletions(-)

diff --git a/install.cc b/install.cc
index 79ddd20..cd3128c 100644
--- a/install.cc
+++ b/install.cc
@@ -485,7 +485,7 @@ Installer::installOne (packagemeta &pkgm, const packageversion &ver,
           lst->write (tmp.c_str(), tmp.size());
         }
       if (Script::isAScript (fn))
-        pkgm.desired.addScript (Script (canonicalfn));
+        pkgm.addScript (Script (canonicalfn));
 
       int iteration = 0;
       archive::extract_results extres;
diff --git a/package_meta.cc b/package_meta.cc
index 1f548e2..f37340b 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -693,3 +693,15 @@ packagemeta::addToCategoryAll()
 {
   add_category ("All");
 }
+
+void
+packagemeta::addScript(Script const &aScript)
+{
+  scripts_.push_back(aScript);
+}
+
+std::vector <Script> &
+packagemeta::scripts()
+{
+  return scripts_;
+}
diff --git a/package_meta.h b/package_meta.h
index 506d0f2..640c253 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -19,11 +19,12 @@
 class packageversion;
 class packagemeta;
 
-/* Required to parse this completely */
 #include <set>
+#include <vector>
 #include "PackageTrust.h"
 #include "package_version.h"
 #include "package_message.h"
+#include "script.h"
 
 typedef std::pair<const std::string, std::vector<packagemeta *> > Category;
 
@@ -144,10 +145,14 @@ public:
   void logSelectionStatus() const;
   void logAllVersions() const;
 
+  void addScript(Script const &);
+  std::vector <Script> &scripts();
+
 protected:
   packagemeta &operator= (packagemeta const &);
 private:
   std::string trustLabel(packageversion const &) const;
+  std::vector <Script> scripts_;
 };
 
 #endif /* SETUP_PACKAGE_META_H */
diff --git a/package_version.cc b/package_version.cc
index 7005850..60aae06 100644
--- a/package_version.cc
+++ b/package_version.cc
@@ -54,11 +54,7 @@ public:
   void set_ldesc (const std::string& ) {}
   void uninstall (){}
   void pick(bool const &newValue){/* Ignore attempts to pick this!. Throw an exception here if you want to detect such attemtps instead */}
-  virtual void addScript(Script const &) {}
-  virtual std::vector <Script> &scripts() { scripts_.clear();  return scripts_;}
   virtual bool accessible () const {return false;}
-  private:
-    std::vector <Script> scripts_;
 };
 static _defaultversion defaultversion;
 
@@ -301,18 +297,6 @@ packageversion::scan (bool mirror_mode)
     }
 }
 
-void
-packageversion::addScript(Script const &aScript)
-{
-  return data->addScript (aScript);
-}
-
-std::vector <Script> &
-packageversion::scripts()
-{
-  return data->scripts();
-}
-
 int
 packageversion::compareVersions(const packageversion &a, const packageversion &b)
 {
@@ -400,15 +384,3 @@ _packageversion::accessible() const
   // otherwise, not accessible
   return false;
 }
-
-void
-_packageversion::addScript(Script const &aScript)
-{
-  scripts().push_back(aScript);
-}
-
-std::vector <Script> &
-_packageversion::scripts()
-{
-  return scripts_;
-}
diff --git a/package_version.h b/package_version.h
index c26593a..0f83fdc 100644
--- a/package_version.h
+++ b/package_version.h
@@ -44,8 +44,6 @@ class CategoryList;
 #include "PackageSpecification.h"
 #include "PackageTrust.h"
 #include "package_depends.h"
-#include "script.h"
-#include <vector>
 
 typedef enum
 {
@@ -127,9 +125,6 @@ public:
   /* ensure that the depends clause is satisfied */
   int set_requirements (trusts deftrust, size_t depth = 0);
 
-  void addScript(Script const &);
-  std::vector <Script> &scripts();
-
   /* utility function to compare package versions */
   static int compareVersions(const packageversion &a, const packageversion &b);
 
@@ -186,13 +181,10 @@ public:
      static package_meta * scan_package (io_stream *);
    */
   size_t references;
-  virtual void addScript(Script const &);
-  virtual std::vector <Script> &scripts();
 protected:
   /* only meaningful for binary packages */
   PackageSpecification _sourcePackage;
   packageversion sourceVersion;
-  std::vector <Script> scripts_;
 };
 
 #endif /* SETUP_PACKAGE_VERSION_H */
diff --git a/postinstall.cc b/postinstall.cc
index 2d8e73c..c871201 100644
--- a/postinstall.cc
+++ b/postinstall.cc
@@ -190,7 +190,7 @@ do_postinstall_thread (HINSTANCE h, HWND owner)
     {
       packagemeta & pkg = **i;
 
-      vector<Script> installed = pkg.installed.scripts();
+      vector<Script> installed = pkg.scripts();
       vector<Script> run;
       // extract non-perpetual scripts for the current stratum
       for (vector <Script>::iterator  j = installed.begin(); j != installed.end(); j++)
@@ -213,8 +213,8 @@ do_postinstall_thread (HINSTANCE h, HWND owner)
   for (vector <packagemeta *>::iterator i = packages.begin (); i != packages.end (); ++i)
     {
        packagemeta & pkg = **i;
-       for (std::vector<Script>::const_iterator j = pkg.installed.scripts().begin();
-            j != pkg.installed.scripts().end();
+       for (std::vector<Script>::const_iterator j = pkg.scripts().begin();
+            j != pkg.scripts().end();
             j++)
          {
            std::vector<Script>::iterator p = find(scripts.begin(), scripts.end(), *j);
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 04/14] Hoist pick() up to packagemeta

Jon TURNEY
In reply to this post by Jon TURNEY
We are always writing packagemeta.desired.pick(bool, packagemeta).  This
kind of suggests something not quite right.

The pick flag means install/reinstall, so despite being stored per
packageversion, is only significant to download/install for the desired
version.

There's a slight wrinkle in that we want to also set/clear this flag for the
source packageversion.  We can't change this to point to packagemeta rather
than packageversion, as that may not be the same for all versions, so
instead just track this flag separately as srcpicked.

Note that there is still a complicated mapping between the state of desired
and pick and the action represented in the UI:

desired == empty, installed == desired : skip
desired == empty, installed != desired : uninstall
desired == installed, pick == true     : reinstall
desired == installed, pick == false    : keep
desired != installed, pick == true     : upgrade
desired != installed, pick == false    : invalid
---
 PickPackageLine.cc | 13 +++++------
 PickView.cc        |  8 +++----
 download.cc        | 12 +++++-----
 install.cc         | 19 ++++++++--------
 package_db.cc      |  2 +-
 package_meta.cc    | 67 ++++++++++++++++++++++++++++++++++++++----------------
 package_meta.h     | 11 ++++++++-
 package_version.cc | 16 -------------
 package_version.h  |  7 ------
 prereq.cc          | 11 +++++----
 10 files changed, 90 insertions(+), 76 deletions(-)

diff --git a/PickPackageLine.cc b/PickPackageLine.cc
index 60ece7f..95c1557 100644
--- a/PickPackageLine.cc
+++ b/PickPackageLine.cc
@@ -44,7 +44,7 @@ PickPackageLine::paint (HDC hdc, HRGN unused, int x, int y, int col_num, int sho
           /* current version */ pkg.desired == pkg.installed ||
           /* no source */ !pkg.desired.accessible())
         theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkna);
-      else if (pkg.desired.picked())
+      else if (pkg.picked())
         theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkyes);
       else
         theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkno);
@@ -67,7 +67,7 @@ PickPackageLine::paint (HDC hdc, HRGN unused, int x, int y, int col_num, int sho
           /* when no source mirror available */
           !pkg.desired.sourcePackage().accessible())
         theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkna);
-      else if (pkg.desired.sourcePackage().picked())
+      else if (pkg.srcpicked())
         theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkyes);
       else
         theView.DrawIcon (hdc, x + HMARGIN/2, by, theView.bm_checkno);
@@ -100,7 +100,7 @@ PickPackageLine::paint (HDC hdc, HRGN unused, int x, int y, int col_num, int sho
       /* Include the size of the binary package, and if selected, the source
          package as well.  */
       sz += picked.source()->size;
-      if (picked.sourcePackage().picked())
+      if (pkg.srcpicked())
         sz += picked.sourcePackage().source()->size;
 
       /* If size still 0, size must be unknown.  */
@@ -133,20 +133,19 @@ PickPackageLine::click (int const myrow, int const ClickedRow, int const x)
       && x <= theView.headers[theView.bintick_col + 1].x - HMARGIN / 2)
     {
       if (pkg.desired.accessible ())
- pkg.desired.pick (!pkg.desired.picked (), &pkg);
+ pkg.pick (!pkg.picked ());
     }
   else if (x >= theView.headers[theView.srctick_col].x - HMARGIN / 2
    && x <= theView.headers[theView.srctick_col + 1].x - HMARGIN / 2)
     {
       if (pkg.desired.sourcePackage ().accessible ())
- pkg.desired.sourcePackage ().pick (
- !pkg.desired.sourcePackage ().picked (), NULL);
+ pkg.srcpick (!pkg.srcpicked ());
     }
   /* Unchecking binary while source is unchecked or vice versa is equivalent
      to uninstalling.  It's essential to set desired correctly, otherwise the
      package gets uninstalled without visual feedback to the user.  The package
      will not even show up in the "Pending" view! */
-  if (!pkg.desired.picked () && !pkg.desired.sourcePackage ().picked ())
+  if (!pkg.picked () && !pkg.srcpicked ())
     pkg.desired = packageversion ();
   return 0;
 }
diff --git a/PickView.cc b/PickView.cc
index 222bcb8..4c728f8 100644
--- a/PickView.cc
+++ b/PickView.cc
@@ -175,13 +175,13 @@ PickView::setViewMode (views mode)
               || (view_mode == PickView::views::PackagePending &&
                   ((!pkg.desired && pkg.installed) ||         // uninstall
                     (pkg.desired &&
-                      (pkg.desired.picked () ||               // install bin
-                       pkg.desired.sourcePackage ().picked ())))) // src
+                      (pkg.picked () ||               // install bin
+                       pkg.srcpicked ())))) // src
               
               // "Up to date" : installed packages that will not be changed
               || (view_mode == PickView::views::PackageKeeps &&
-                  (pkg.installed && pkg.desired && !pkg.desired.picked ()
-                    && !pkg.desired.sourcePackage ().picked ()))
+                  (pkg.installed && pkg.desired && !pkg.picked ()
+                    && !pkg.srcpicked ()))
 
               // "Not installed"
               || (view_mode == PickView::views::PackageSkips &&
diff --git a/download.cc b/download.cc
index 80615f3..a2237a7 100644
--- a/download.cc
+++ b/download.cc
@@ -208,18 +208,18 @@ do_download_thread (HINSTANCE h, HWND owner)
        i != db.packages.end (); ++i)
     {
       packagemeta & pkg = *(i->second);
-      if (pkg.desired.picked () || pkg.desired.sourcePackage ().picked ())
+      if (pkg.picked () || pkg.srcpicked ())
  {
   packageversion version = pkg.desired;
   packageversion sourceversion = version.sourcePackage();
   try
     {
-          if (version.picked())
+      if (pkg.picked())
  {
     if (!check_for_cached (*version.source()))
       total_download_bytes += version.source()->size;
  }
-          if (sourceversion.picked () || IncludeSource)
+      if (pkg.srcpicked () || IncludeSource)
  {
     if (!check_for_cached (*sourceversion.source()))
       total_download_bytes += sourceversion.source()->size;
@@ -243,16 +243,16 @@ do_download_thread (HINSTANCE h, HWND owner)
        i != db.packages.end (); ++i)
     {
       packagemeta & pkg = *(i->second);
-      if (pkg.desired.picked () || pkg.desired.sourcePackage ().picked ())
+      if (pkg.picked () || pkg.srcpicked ())
  {
   int e = 0;
   packageversion version = pkg.desired;
   packageversion sourceversion = version.sourcePackage();
-  if (version.picked())
+  if (pkg.picked())
     {
  e += download_one (*version.source(), owner);
     }
-  if (sourceversion && (sourceversion.picked() || IncludeSource))
+  if (sourceversion && (pkg.srcpicked() || IncludeSource))
     {
  e += download_one (*sourceversion.source (), owner);
     }
diff --git a/install.cc b/install.cc
index cd3128c..fee2e19 100644
--- a/install.cc
+++ b/install.cc
@@ -740,12 +740,12 @@ do_install_thread (HINSTANCE h, HWND owner)
   {
     packagemeta & pkg = *(i->second);
 
-    if (pkg.desired.picked())
+    if (pkg.picked())
     {
       md5sum_total_bytes += pkg.desired.source()->size;
     }
 
-    if (pkg.desired.sourcePackage ().picked() || IncludeSource)
+    if (pkg.srcpicked() || IncludeSource)
     {
       md5sum_total_bytes += pkg.desired.sourcePackage ().source()->size;
     }
@@ -759,7 +759,7 @@ do_install_thread (HINSTANCE h, HWND owner)
   {
     packagemeta & pkg = *(i->second);
 
-    if (pkg.desired.picked())
+    if (pkg.picked())
     {
       try
       {
@@ -768,9 +768,9 @@ do_install_thread (HINSTANCE h, HWND owner)
       catch (Exception *e)
       {
         if (yesno (owner, IDS_SKIP_PACKAGE, e->what()) == IDYES)
-          pkg.desired.pick (false, &pkg);
+          pkg.pick (false);
       }
-      if (pkg.desired.picked())
+      if (pkg.picked())
       {
         md5sum_total_bytes_sofar += pkg.desired.source()->size;
         total_bytes += pkg.desired.source()->size;
@@ -778,7 +778,7 @@ do_install_thread (HINSTANCE h, HWND owner)
       }
     }
 
-    if (pkg.desired.sourcePackage ().picked() || IncludeSource)
+    if (pkg.srcpicked() || IncludeSource)
     {
       bool skiprequested = false ;
       try
@@ -790,10 +790,10 @@ do_install_thread (HINSTANCE h, HWND owner)
         if (yesno (owner, IDS_SKIP_PACKAGE, e->what()) == IDYES)
  {
   skiprequested = true ; //(err occurred,) skip pkg desired
-          pkg.desired.sourcePackage ().pick (false, &pkg);
+          pkg.srcpick (false);
  }
       }
-      if (pkg.desired.sourcePackage().picked() || (IncludeSource && !skiprequested))
+      if (pkg.srcpicked() || (IncludeSource && !skiprequested))
       {
         md5sum_total_bytes_sofar += pkg.desired.sourcePackage ().source()->size;
         total_bytes += pkg.desired.sourcePackage ().source()->size;
@@ -801,8 +801,9 @@ do_install_thread (HINSTANCE h, HWND owner)
       }
     }
 
+    /* Upgrade or reinstall */
     if ((pkg.installed && pkg.desired != pkg.installed)
-        || pkg.installed.picked ())
+        || pkg.picked ())
     {
       uninstall_q.push_back (&pkg);
     }
diff --git a/package_db.cc b/package_db.cc
index 3d6d0de..4e22953 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -445,7 +445,7 @@ packagedb::defaultTrust (trusts trust)
         {
           pkg.desired = pkg.trustp (true, trust);
           if (pkg.desired)
-            pkg.desired.pick (pkg.desired.accessible() && pkg.desired != pkg.installed, &pkg);
+            pkg.pick (pkg.desired.accessible() && pkg.desired != pkg.installed);
         }
       else
         pkg.desired = packageversion ();
diff --git a/package_meta.cc b/package_meta.cc
index f37340b..55fe471 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -385,9 +385,9 @@ packagemeta::action_caption () const
     return "Uninstall";
   else if (!desired)
     return "Skip";
-  else if (desired == installed && desired.picked())
+  else if (desired == installed && picked())
     return packagedb::task == PackageDB_Install ? "Reinstall" : "Retrieve";
-  else if (desired == installed && desired.sourcePackage() && desired.sourcePackage().picked())
+  else if (desired == installed && desired.sourcePackage() && srcpicked())
     /* FIXME: Redo source should come up if the tarball is already present locally */
     return "Source";
   else if (desired == installed) /* and neither src nor bin */
@@ -405,15 +405,15 @@ packagemeta::set_action (trusts const trust)
   /* Keep the picked settings of the former desired version, if any, and make
      sure at least one of them is picked.  If both are unpicked, pick the
      binary version. */
-  bool source_picked = desired && desired.sourcePackage().picked ();
-  bool binary_picked = !desired || desired.picked () || !source_picked;
+  bool source_picked = desired && srcpicked ();
+  bool binary_picked = !desired || picked () || !source_picked;
 
   /* If we're on "Keep" on the installed version, and the version is available,
      switch to "Reinstall". */
-  if (desired && desired == installed && !desired.picked ()
+  if (desired && desired == installed && !picked ()
       && desired.accessible ())
     {
-      desired.pick (true, this);
+      pick (true);
       return;
     }
 
@@ -445,9 +445,8 @@ packagemeta::set_action (trusts const trust)
       /* If the next version is the installed version, unpick it.  This will
  have the desired effect to show the package in "Keep" mode.  See also
  above for the code switching to "Reinstall". */
-      desired.pick (desired != installed && binary_picked, this);
-      desired.sourcePackage ().pick (desired.sourcePackage().accessible ()
-     && source_picked, NULL);
+      pick (desired != installed && binary_picked);
+      srcpick (desired.sourcePackage().accessible () && source_picked);
     }
   else
     desired = packageversion ();
@@ -469,8 +468,8 @@ packagemeta::set_action (_actions action, packageversion const &default_version)
   desired = default_version;
   if (desired)
     {
-      desired.pick (desired != installed, this);
-      desired.sourcePackage ().pick (false, NULL);
+      pick (desired != installed);
+      srcpick (false);
     }
  }
       else
@@ -486,18 +485,18 @@ packagemeta::set_action (_actions action, packageversion const &default_version)
     if (desired.accessible ())
       {
  user_picked = true;
- desired.pick (true, this);
- desired.sourcePackage ().pick (false, NULL);
+ pick (true);
+ srcpick (false);
       }
     else
       {
- desired.pick (false, NULL);
- desired.sourcePackage ().pick (true, NULL);
+ pick (false);
+ srcpick (true);
       }
   else
     {
-      desired.pick (false, NULL);
-      desired.sourcePackage ().pick (false, NULL);
+      pick (false);
+      srcpick (false);
     }
  }
       return;
@@ -507,8 +506,8 @@ packagemeta::set_action (_actions action, packageversion const &default_version)
       desired = installed;
       if (desired)
  {
-  desired.pick (true, this);
-  desired.sourcePackage ().pick (false, NULL);
+  pick (true);
+  srcpick (false);
  }
     }
   else if (action == Uninstall_action)
@@ -518,6 +517,34 @@ packagemeta::set_action (_actions action, packageversion const &default_version)
 }
 
 bool
+packagemeta::picked () const
+{
+  return _picked;
+}
+
+void
+packagemeta::pick (bool picked)
+{
+  _picked = picked;
+
+  // side effect: display message when picked (if not already seen)
+  if (picked)
+    this->message.display ();
+}
+
+bool
+packagemeta::srcpicked () const
+{
+  return _srcpicked;
+}
+
+void
+packagemeta::srcpick (bool picked)
+{
+  _srcpicked = picked;
+}
+
+bool
 packagemeta::accessible () const
 {
   for (set<packageversion>::iterator i=versions.begin();
@@ -607,7 +634,7 @@ packagemeta::logSelectionStatus() const
   const std::string installed =
    pkg.installed ? pkg.installed.Canonical_version () : "none";
 
-  Log (LOG_BABBLE) << "[" << pkg.name << "] action=" << action << " trust=" << trust << " installed=" << installed << " src?=" << (pkg.desired && pkg.desired.sourcePackage().picked() ? "yes" : "no") << endLog;
+  Log (LOG_BABBLE) << "[" << pkg.name << "] action=" << action << " trust=" << trust << " installed=" << installed << " src?=" << (pkg.desired && srcpicked() ? "yes" : "no") << endLog;
   if (pkg.categories.size ())
     Log (LOG_BABBLE) << "     categories=" << for_each(pkg.categories.begin(), pkg.categories.end(), StringConcatenator(", ")).result << endLog;
 #if 0
diff --git a/package_meta.h b/package_meta.h
index 640c253..dbd8eb9 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -35,7 +35,8 @@ public:
   static void ScanDownloadedFiles (bool);
   packagemeta (packagemeta const &);
   packagemeta (const std::string& pkgname)
-  : name (pkgname), key(pkgname), user_picked (false)
+    : name (pkgname), key(pkgname), user_picked (false),
+    _picked(false), _srcpicked(false)
   {
   }
 
@@ -134,6 +135,12 @@ public:
   /* What version does the user want ? */
   packageversion desired;
 
+  bool picked() const;   /* true if desired version is to be (re-)installed */
+  void pick(bool); /* trigger an install/reinstall */
+
+  bool srcpicked() const;   /* true if source for desired version is to be installed */
+  void srcpick(bool);
+
   packagemessage message;
 
   /* can one or more versions be installed? */
@@ -153,6 +160,8 @@ protected:
 private:
   std::string trustLabel(packageversion const &) const;
   std::vector <Script> scripts_;
+  bool _picked; /* true if desired version is to be (re)installed */
+  bool _srcpicked;
 };
 
 #endif /* SETUP_PACKAGE_META_H */
diff --git a/package_version.cc b/package_version.cc
index 60aae06..bb68229 100644
--- a/package_version.cc
+++ b/package_version.cc
@@ -53,8 +53,6 @@ public:
   const std::string LDesc () {return std::string();}
   void set_ldesc (const std::string& ) {}
   void uninstall (){}
-  void pick(bool const &newValue){/* Ignore attempts to pick this!. Throw an exception here if you want to detect such attemtps instead */}
-  virtual bool accessible () const {return false;}
 };
 static _defaultversion defaultversion;
 
@@ -237,20 +235,6 @@ packageversion::depends() const
   return data->depends;
 }
 
-bool
-packageversion::picked () const
-{
-  return data->picked;
-}
-
-void
-packageversion::pick (bool aBool, packagemeta *pkg)
-{
-  data->pick(aBool);
-  if (pkg && aBool)
-    pkg->message.display ();
-}
-
 void
 packageversion::uninstall ()
 {
diff --git a/package_version.h b/package_version.h
index 0f83fdc..9351f26 100644
--- a/package_version.h
+++ b/package_version.h
@@ -111,9 +111,6 @@ public:
   void setDepends(const PackageDepends);
   const PackageDepends depends() const;
 
-  bool picked() const;   /* true if this version is to be installed */
-  void pick(bool, packagemeta *); /* trigger an install/reinsall */
-
   void uninstall ();
   /* invariant: never null */
   packagesource *source() const; /* where can we source the file from */
@@ -168,10 +165,6 @@ public:
 
   PackageDepends depends;
 
-  virtual void pick(bool const &newValue) { picked = newValue;}
-  bool picked; /* non zero if this version is to be installed */
- /* This will also trigger reinstalled if it is set */
-
   virtual void uninstall () = 0;
   packagesource source; /* where can we source the file from */
 
diff --git a/prereq.cc b/prereq.cc
index 0e7fdf5..16af7fa 100644
--- a/prereq.cc
+++ b/prereq.cc
@@ -302,20 +302,21 @@ PrereqChecker::selectMissing ()
   map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
   for (i = unmet.begin(); i != unmet.end(); i++)
     {
-      packageversion vers = i->first->trustp (false, theTrust);
-      i->first->desired = vers;
-      vers.sourcePackage ().pick (false, NULL);
+      packagemeta *pkg = i->first;
+      packageversion vers = pkg->trustp (false, theTrust);
+      pkg->desired = vers;
+      pkg->srcpick (false);
 
       if (vers == i->first->installed)
         {
-          vers.pick (false, NULL);
+          pkg->pick (false);
           Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
                ": Selecting already-installed version " <<
                i->first->installed.Canonical_version () << "." << endLog;
         }
       else
         {
-          vers.pick (vers.accessible (), i->first);
+          pkg->pick (vers.accessible ());
           Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
               ": Selecting version " << vers.Canonical_version () <<
               " for installation." << endLog;
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 05/14] Hoist uninstall up to Installer::uninstallOne()

Jon TURNEY
In reply to this post by Jon TURNEY
This calls packagemeta, which applies to packageversion (which delegates
through packageversion_ to cygpackage) to return lines from the .lst.gz file
(pretending that we know it contains a file list for that specific version)
and to remove the .lst.gz file when done.

Move this all up into Installer::uninstallOne(), where it's all in the same
place as the operation it is reversing, Installer::installOne().
---
 cygpackage.cc      | 46 +---------------------------------
 cygpackage.h       | 14 -----------
 install.cc         | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
 package_meta.cc    | 70 ---------------------------------------------------
 package_meta.h     |  1 -
 package_version.cc | 21 ----------------
 package_version.h  |  9 -------
 7 files changed, 73 insertions(+), 161 deletions(-)

diff --git a/cygpackage.cc b/cygpackage.cc
index 56c1da8..32b9403 100644
--- a/cygpackage.cc
+++ b/cygpackage.cc
@@ -36,12 +36,8 @@ packagev (),
 canonical (),
 sdesc (),
 ldesc (),
-type (package_binary),
-listdata (),
-listfile ()
+type (package_binary)
 {
-  memset( getfilenamebuffer, '\0', CYG_PATH_MAX);
-
   /* FIXME: query the install database for the currently installed
    * version details
    */
@@ -104,46 +100,6 @@ cygpackage::~cygpackage ()
 }
 
 const std::string
-cygpackage::getfirstfile ()
-{
-  if (listdata)
-    delete listdata;
-  listfile =
-    io_stream::open ("cygfile:///etc/setup/" + name + ".lst.gz", "rb", 0);
-  listdata = compress::decompress (listfile);
-  if (!listdata)
-    return std::string();
-  /* std::string(NULL) will crash, so be careful to test for that. */
-  const char *result = listdata->gets (getfilenamebuffer, sizeof (getfilenamebuffer));
-  if (result == NULL)
-    Log (LOG_PLAIN) << "Corrupt package listing for " << name << ", can't uninstall old files." << endLog;
-  return std::string (result ? result : "");
-}
-
-const std::string
-cygpackage::getnextfile ()
-{
-  if (listdata)
-  {
-    /* std::string(NULL) will crash, so be careful to test for that. */
-    const char *sz = listdata->gets (getfilenamebuffer,
-                                     sizeof (getfilenamebuffer));
-    if (sz)
-      return std::string(sz);
-  }
-  return std::string();
-}
-
-void
-cygpackage::uninstall ()
-{
-  if (listdata)
-    delete listdata;
-  listdata = 0;
-  io_stream::remove ("cygfile:///etc/setup/" + name + ".lst.gz");
-}
-
-const std::string
 cygpackage::Name ()
 {
   return name;
diff --git a/cygpackage.h b/cygpackage.h
index 4022472..c6d0657 100644
--- a/cygpackage.h
+++ b/cygpackage.h
@@ -20,9 +20,6 @@
  * arbitrate acceess to cygwin binary packages amd cygwin source packages
  */
 
-/* for MAX_PATH */
-#include "win32.h"
-
 #include "package_version.h"
 
 class io_stream;
@@ -48,19 +45,11 @@ public:
   {
     return ldesc;
   };
-  virtual void uninstall ();
-
 
   /* pass the name of the package when constructing */
   void setCanonicalVersion (const std::string& );
 
-
   virtual ~ cygpackage ();
-  /* TODO: we should probably return a metaclass - file name & path & size & type
-     - ie doc/script/binary
-   */
-  virtual const std::string getfirstfile ();
-  virtual const std::string getnextfile ();
 
   /* pass the name of the package when constructing */
   static packageversion createInstance (const std::string& pkgname,
@@ -77,12 +66,9 @@ private:
   std::string packagev;
   std::string canonical;
   std::string sdesc, ldesc;
-  char getfilenamebuffer[CYG_PATH_MAX];
 
 //  package_stability_t stability;
   package_type_t type;
-
-  io_stream *listdata, *listfile;
 };
 
 #endif /* SETUP_CYGPACKAGE_H */
diff --git a/install.cc b/install.cc
index fee2e19..9c4c01b 100644
--- a/install.cc
+++ b/install.cc
@@ -165,10 +165,81 @@ Installer::preremoveOne (packagemeta & pkg)
 void
 Installer::uninstallOne (packagemeta & pkg)
 {
+  if (!pkg.installed)
+    return;
+
   Progress.SetText1 ("Uninstalling...");
   Progress.SetText2 (pkg.name.c_str());
   Log (LOG_PLAIN) << "Uninstalling " << pkg.name << endLog;
-  pkg.uninstall ();
+
+  std::set<std::string> dirs;
+
+  io_stream *listfile = io_stream::open ("cygfile:///etc/setup/" + pkg.name + ".lst.gz", "rb", 0);
+  io_stream *listdata = compress::decompress (listfile);
+
+  while (listdata)
+    {
+      char getfilenamebuffer[CYG_PATH_MAX];
+      const char *sz = listdata->gets (getfilenamebuffer, sizeof (getfilenamebuffer));
+      if (sz == NULL)
+        break;
+
+      std::string line(sz);
+
+      /* Insert the paths of all parent directories of line into dirs. */
+      size_t idx = line.length();
+      while ((idx = line.find_last_of('/', idx-1)) != string::npos)
+      {
+        std::string dir_path = line.substr(0, idx);
+        bool was_new = dirs.insert(dir_path).second;
+        /* If the path was already present in dirs, then all parent paths
+         * must necessarily be present also, so don't do any further work.
+         * */
+        if (!was_new) break;
+      }
+
+      std::string d = cygpath ("/" + line);
+      WCHAR wname[d.size () + 11]; /* Prefix + ".lnk". */
+      mklongpath (wname, d.c_str (), d.size () + 11);
+      DWORD dw = GetFileAttributesW (wname);
+      if (dw != INVALID_FILE_ATTRIBUTES
+          && !(dw & FILE_ATTRIBUTE_DIRECTORY))
+        {
+          Log (LOG_BABBLE) << "unlink " << d << endLog;
+          SetFileAttributesW (wname, dw & ~FILE_ATTRIBUTE_READONLY);
+          DeleteFileW (wname);
+        }
+      /* Check for Windows shortcut of same name. */
+      d += ".lnk";
+      wcscat (wname, L".lnk");
+      dw = GetFileAttributesW (wname);
+      if (dw != INVALID_FILE_ATTRIBUTES
+          && !(dw & FILE_ATTRIBUTE_DIRECTORY))
+        {
+          Log (LOG_BABBLE) << "unlink " << d << endLog;
+          SetFileAttributesW (wname, dw & ~FILE_ATTRIBUTE_READONLY);
+          DeleteFileW (wname);
+        }
+    }
+
+  /* Remove the listing file */
+  delete listdata;
+  io_stream::remove ("cygfile:///etc/setup/" + pkg.name + ".lst.gz");
+
+  /* An STL set maintains itself in sorted order. Thus, iterating over it
+   * in reverse order will ensure we process directories depth-first. */
+  set<string>::const_iterator it = dirs.end();
+  while (it != dirs.begin())
+  {
+    it--;
+    std::string d = cygpath("/" + *it);
+    WCHAR wname[d.size () + 11];
+    mklongpath (wname, d.c_str (), d.size () + 11);
+    if (RemoveDirectoryW (wname))
+      Log (LOG_BABBLE) << "rmdir " << d << endLog;
+  }
+
+  pkg.installed = packageversion();
   num_uninstalls++;
 }
 
diff --git a/package_meta.cc b/package_meta.cc
index 55fe471..f4678f0 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -140,76 +140,6 @@ packagemeta::set_installed (packageversion & thepkg)
     installed = thepkg;
 }
 
-/* uninstall a package if it's installed */
-void
-packagemeta::uninstall ()
-{
-  if (installed)
-    {
-      /* this will need to be pushed down to the version, or even the source level
-       * to allow differences between formats to be seamlessly managed
-       * but for now: here is ok
-       */
-      set<string> dirs;
-      string line = installed.getfirstfile ();
-
-      while (line.size())
- {
-          /* Insert the paths of all parent directories of line into dirs. */
-          size_t idx = line.length();
-          while ((idx = line.find_last_of('/', idx-1)) != string::npos)
-          {
-            string dir_path = line.substr(0, idx);
-            bool was_new = dirs.insert(dir_path).second;
-            /* If the path was already present in dirs, then all parent paths
-             * must necessarily be present also, so don't do any further work.
-             * */
-            if (!was_new) break;
-          }
-
-  std::string d = cygpath ("/" + line);
-  WCHAR wname[d.size () + 11]; /* Prefix + ".lnk". */
-  mklongpath (wname, d.c_str (), d.size () + 11);
-  DWORD dw = GetFileAttributesW (wname);
-  if (dw != INVALID_FILE_ATTRIBUTES
-      && !(dw & FILE_ATTRIBUTE_DIRECTORY))
-    {
-      Log (LOG_BABBLE) << "unlink " << d << endLog;
-      SetFileAttributesW (wname, dw & ~FILE_ATTRIBUTE_READONLY);
-      DeleteFileW (wname);
-    }
-  /* Check for Windows shortcut of same name. */
-  d += ".lnk";
-  wcscat (wname, L".lnk");
-  dw = GetFileAttributesW (wname);
-  if (dw != INVALID_FILE_ATTRIBUTES
-      && !(dw & FILE_ATTRIBUTE_DIRECTORY))
-    {
-      Log (LOG_BABBLE) << "unlink " << d << endLog;
-      SetFileAttributesW (wname, dw & ~FILE_ATTRIBUTE_READONLY);
-      DeleteFileW (wname);
-    }
-  line = installed.getnextfile ();
- }
-      installed.uninstall ();
-
-      /* An STL set maintains itself in sorted order. Thus, iterating over it
-       * in reverse order will ensure we process directories depth-first. */
-      set<string>::const_iterator it = dirs.end();
-      while (it != dirs.begin())
-      {
-        it--;
-        std::string d = cygpath("/" + *it);
- WCHAR wname[d.size () + 11];
- mklongpath (wname, d.c_str (), d.size () + 11);
- if (RemoveDirectoryW (wname))
-  Log (LOG_BABBLE) << "rmdir " << d << endLog;
-      }
-    }
-  installed = packageversion();
-}
-
-
 void
 packagemeta::add_category (const std::string& cat)
 {
diff --git a/package_meta.h b/package_meta.h
index dbd8eb9..529b2a2 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -71,7 +71,6 @@ public:
   static const _actions Uninstall_action;
   void set_action (trusts const t);
   void set_action (_actions, packageversion const & default_version);
-  void uninstall ();
 
   void set_message (const std::string& message_id, const std::string& message_string)
   {
diff --git a/package_version.cc b/package_version.cc
index bb68229..3dcd7ea 100644
--- a/package_version.cc
+++ b/package_version.cc
@@ -46,13 +46,10 @@ public:
   const std::string Canonical_version() {return std::string();}
   void setCanonicalVersion (const std::string& ) {}
   package_type_t Type () {return package_binary;}
-  const std::string getfirstfile () {return std::string();}
-  const std::string getnextfile () {return std::string();}
   const std::string SDesc () {return std::string();}
   void set_sdesc (const std::string& ) {}
   const std::string LDesc () {return std::string();}
   void set_ldesc (const std::string& ) {}
-  void uninstall (){}
 };
 static _defaultversion defaultversion;
 
@@ -170,18 +167,6 @@ packageversion::Type () const
 }
 
 const std::string
-packageversion::getfirstfile ()
-{
-  return data->getfirstfile ();
-}
-
-const std::string
-packageversion::getnextfile ()
-{
-  return data->getnextfile ();
-}
-
-const std::string
 packageversion::SDesc () const
 {
   return data->SDesc ();
@@ -235,12 +220,6 @@ packageversion::depends() const
   return data->depends;
 }
 
-void
-packageversion::uninstall ()
-{
- data->uninstall ();
-}
-
 packagesource *
 packageversion::source () const
 {
diff --git a/package_version.h b/package_version.h
index 9351f26..fc8a084 100644
--- a/package_version.h
+++ b/package_version.h
@@ -98,8 +98,6 @@ public:
   const std::string Canonical_version () const;
   void setCanonicalVersion (const std::string& );
   package_type_t Type () const;
-  const std::string getfirstfile ();
-  const std::string getnextfile ();
   const std::string SDesc () const;
   void set_sdesc (const std::string& );
   const std::string LDesc () const;
@@ -111,7 +109,6 @@ public:
   void setDepends(const PackageDepends);
   const PackageDepends depends() const;
 
-  void uninstall ();
   /* invariant: never null */
   packagesource *source() const; /* where can we source the file from */
 
@@ -145,11 +142,6 @@ public:
   virtual void setCanonicalVersion (const std::string& ) = 0;
 //  virtual package_stability_t Stability () = 0;
   virtual package_type_t Type () = 0;
-  /* TODO: we should probably return a metaclass - file name & path & size & type
-     - ie doc/script/binary
-   */
-  virtual const std::string getfirstfile () = 0;
-  virtual const std::string getnextfile () = 0;
   virtual const std::string SDesc () = 0;
   virtual void set_sdesc (const std::string& ) = 0;
   virtual const std::string LDesc () = 0;
@@ -165,7 +157,6 @@ public:
 
   PackageDepends depends;
 
-  virtual void uninstall () = 0;
   packagesource source; /* where can we source the file from */
 
   virtual bool accessible () const;
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 06/14] Hoist scan() up from packageversion to packagemeta

Jon TURNEY
In reply to this post by Jon TURNEY
---
 package_meta.cc    | 37 +++++++++++++++++++++++++++++++++++--
 package_meta.h     |  2 ++
 package_version.cc | 29 -----------------------------
 package_version.h  |  2 --
 4 files changed, 37 insertions(+), 33 deletions(-)

diff --git a/package_meta.cc b/package_meta.cc
index f4678f0..425df59 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -44,6 +44,9 @@ using namespace std;
 
 #include <algorithm>
 #include "Generic.h"
+#include "download.h"
+#include "Exception.h"
+#include "resource.h"
 
 using namespace std;
 
@@ -582,6 +585,36 @@ packagemeta::logSelectionStatus() const
   pkg.logAllVersions();
 }
 
+/* scan for local copies of package */
+void
+packagemeta::scan (const packageversion &pkg, bool mirror_mode)
+{
+  /* Already have something */
+  if (!pkg)
+    return;
+
+  /* Remove mirror sites.
+   * FIXME: This is a bit of a hack.
+   */
+  try
+    {
+      if (!check_for_cached (*(pkg.source ()), mirror_mode)
+  && ::source == IDC_SOURCE_LOCALDIR)
+ pkg.source ()->sites.clear ();
+    }
+  catch (Exception * e)
+    {
+      // We can ignore these, since we're clearing the source list anyway
+      if (e->errNo () == APPERR_CORRUPT_PACKAGE)
+ {
+  pkg.source ()->sites.clear ();
+  return;
+ }
+      // Unexpected exception.
+      throw e;
+    }
+}
+
 void
 packagemeta::ScanDownloadedFiles (bool mirror_mode)
 {
@@ -601,10 +634,10 @@ packagemeta::ScanDownloadedFiles (bool mirror_mode)
    && (*i != pkg.installed
        || pkg.installed == pkg.curr
        || pkg.installed == pkg.exp);
-  const_cast<packageversion &>(*i).scan (lazy_scan);
+  scan (*i, lazy_scan);
   packageversion foo = *i;
   packageversion pkgsrcver = foo.sourcePackage ();
-  pkgsrcver.scan (lazy_scan);
+  scan (pkgsrcver, lazy_scan);
 
   /* For local installs, if there is no src and no bin, the version
    * is unavailable
diff --git a/package_meta.h b/package_meta.h
index 529b2a2..8041aa1 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -159,6 +159,8 @@ protected:
 private:
   std::string trustLabel(packageversion const &) const;
   std::vector <Script> scripts_;
+  static void scan (const packageversion &pkg, bool mirror_mode);
+
   bool _picked; /* true if desired version is to be (re)installed */
   bool _srcpicked;
 };
diff --git a/package_version.cc b/package_version.cc
index 3dcd7ea..6e8f692 100644
--- a/package_version.cc
+++ b/package_version.cc
@@ -23,8 +23,6 @@
 #include "state.h"
 #include "resource.h"
 #include <algorithm>
-#include "download.h"
-#include "Exception.h"
 #include "csu_util/version_compare.h"
 
 using namespace std;
@@ -232,33 +230,6 @@ packageversion::accessible() const
   return data->accessible();
 }
 
-void
-packageversion::scan (bool mirror_mode)
-{
-  if (!*this)
-    return;
-  /* Remove mirror sites.
-   * FIXME: This is a bit of a hack. a better way is to abstract
-   * the availability logic to the package
-   */
-  try
-    {
-      if (!check_for_cached (*(source ()), mirror_mode)
-  && ::source == IDC_SOURCE_LOCALDIR)
- source ()->sites.clear ();
-    }
-  catch (Exception * e)
-    {
-      // We can ignore these, since we're clearing the source list anyway
-      if (e->errNo () == APPERR_CORRUPT_PACKAGE)
- {
-  source ()->sites.clear ();
-  return;
- }
-      // Unexpected exception.
-      throw e;
-    }
-}
 
 int
 packageversion::compareVersions(const packageversion &a, const packageversion &b)
diff --git a/package_version.h b/package_version.h
index fc8a084..4c04d77 100644
--- a/package_version.h
+++ b/package_version.h
@@ -113,8 +113,6 @@ public:
   packagesource *source() const; /* where can we source the file from */
 
   bool accessible () const;
-  /* scan for local copies */
-  void scan (bool);
 
   /* ensure that the depends clause is satisfied */
   int set_requirements (trusts deftrust, size_t depth = 0);
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 07/14] Store package stability in class packageversion

Jon TURNEY
In reply to this post by Jon TURNEY
---
 IniDBBuilderPackage.cc | 12 +++++++-----
 IniDBBuilderPackage.h  |  3 +--
 cygpackage.cc          |  7 -------
 cygpackage.h           | 10 +++++++++-
 package_version.cc     | 14 ++++++++++++++
 package_version.h      | 14 +++++---------
 6 files changed, 36 insertions(+), 24 deletions(-)

diff --git a/IniDBBuilderPackage.cc b/IniDBBuilderPackage.cc
index 0e1be78..fb200a8 100644
--- a/IniDBBuilderPackage.cc
+++ b/IniDBBuilderPackage.cc
@@ -34,7 +34,7 @@
 using namespace std;
 
 IniDBBuilderPackage::IniDBBuilderPackage (IniParseFeedback const &aFeedback) :
-cp (0), cbpv (), cspv (), currentSpec (0), trust (0), _feedback (aFeedback){}
+cp (0), cbpv (), cspv (), currentSpec (0), _feedback (aFeedback){}
 
 IniDBBuilderPackage::~IniDBBuilderPackage()
 {
@@ -90,10 +90,10 @@ IniDBBuilderPackage::buildPackage (const std::string& name)
       db.packages.insert (packagedb::packagecollection::value_type(cp->name,cp));
     }
   cbpv = cygpackage::createInstance (name, package_binary);
+  cbpv.SetStability(TRUST_CURR);
   cspv = packageversion ();
   currentSpec = NULL;
   currentNodeList = PackageDepends();
-  trust = TRUST_CURR;
 #if DEBUG
   Log (LOG_BABBLE) << "Created package " << name << endLog;
 #endif
@@ -211,12 +211,12 @@ IniDBBuilderPackage::buildPackageSource (const std::string& path,
 }
 
 void
-IniDBBuilderPackage::buildPackageTrust (int newtrust)
+IniDBBuilderPackage::buildPackageTrust (package_stability_t newtrust)
 {
-  trust = newtrust;
   if (newtrust != TRUST_UNKNOWN)
     {
       cbpv = cygpackage::createInstance (cp->name, package_binary);
+      cbpv.SetStability(newtrust);
       cspv = packageversion ();
     }
 }
@@ -383,7 +383,7 @@ IniDBBuilderPackage::add_correct_version()
     databases, we should pick the one with the highest version number.
   */
   packageversion *v = NULL;
-  switch (trust)
+  switch (cbpv.Stability())
   {
     case TRUST_CURR:
       v = &(cp->curr);
@@ -391,6 +391,8 @@ IniDBBuilderPackage::add_correct_version()
     case TRUST_TEST:
       v = &(cp->exp);
     break;
+    default:
+    break;
   }
 
   if (v)
diff --git a/IniDBBuilderPackage.h b/IniDBBuilderPackage.h
index dee65d4..8825add 100644
--- a/IniDBBuilderPackage.h
+++ b/IniDBBuilderPackage.h
@@ -48,7 +48,7 @@ public:
   void buildPackageSource (const std::string& path, const std::string& size)
   { buildPackageSource(path, size, NULL, hashType::none); }
 
-  void buildPackageTrust (int);
+  void buildPackageTrust (package_stability_t);
   void buildPackageCategory (const std::string& );
 
   void buildBeginDepends ();
@@ -80,7 +80,6 @@ private:
   packageversion cspv;
   PackageSpecification *currentSpec;
   PackageDepends currentNodeList;
-  int trust;
   IniParseFeedback const &_feedback;
 };
 
diff --git a/cygpackage.cc b/cygpackage.cc
index 32b9403..2724249 100644
--- a/cygpackage.cc
+++ b/cygpackage.cc
@@ -134,10 +134,3 @@ cygpackage::set_ldesc (const std::string& desc)
 {
   ldesc = desc;
 }
-
-#if 0
-package_stability_t cygpackage::Stability ()
-{
-  return stability;
-}
-#endif
diff --git a/cygpackage.h b/cygpackage.h
index c6d0657..720921d 100644
--- a/cygpackage.h
+++ b/cygpackage.h
@@ -31,6 +31,14 @@ public:
   virtual const std::string Vendor_version ();
   virtual const std::string Package_version ();
   virtual const std::string Canonical_version ();
+  virtual package_stability_t Stability ()
+  {
+    return stability;
+  }
+  virtual void SetStability (package_stability_t newstability)
+  {
+    stability = newstability;
+  }
   virtual package_type_t Type ()
   {
     return type;
@@ -67,7 +75,7 @@ private:
   std::string canonical;
   std::string sdesc, ldesc;
 
-//  package_stability_t stability;
+  package_stability_t stability;
   package_type_t type;
 };
 
diff --git a/package_version.cc b/package_version.cc
index 6e8f692..2d4416e 100644
--- a/package_version.cc
+++ b/package_version.cc
@@ -43,6 +43,8 @@ public:
   const std::string Package_version() {return std::string();}
   const std::string Canonical_version() {return std::string();}
   void setCanonicalVersion (const std::string& ) {}
+  package_stability_t Stability (){return TRUST_UNKNOWN;}
+  void SetStability (package_stability_t) {}
   package_type_t Type () {return package_binary;}
   const std::string SDesc () {return std::string();}
   void set_sdesc (const std::string& ) {}
@@ -158,6 +160,18 @@ packageversion::setCanonicalVersion (const std::string& ver)
   data->setCanonicalVersion (ver);
 }
 
+package_stability_t
+packageversion::Stability () const
+{
+  return data->Stability ();
+}
+
+void
+packageversion::SetStability (package_stability_t stability)
+{
+  data->SetStability (stability);
+}
+
 package_type_t
 packageversion::Type () const
 {
diff --git a/package_version.h b/package_version.h
index 4c04d77..21d053e 100644
--- a/package_version.h
+++ b/package_version.h
@@ -45,14 +45,7 @@ class CategoryList;
 #include "PackageTrust.h"
 #include "package_depends.h"
 
-typedef enum
-{
-  package_invalid,
-  package_old,
-  package_current,
-  package_experimental
-}
-package_stability_t;
+typedef trusts package_stability_t;
 
 typedef enum
 {
@@ -97,6 +90,8 @@ public:
   const std::string Package_version () const;
   const std::string Canonical_version () const;
   void setCanonicalVersion (const std::string& );
+  package_stability_t Stability () const;
+  void SetStability (package_stability_t);
   package_type_t Type () const;
   const std::string SDesc () const;
   void set_sdesc (const std::string& );
@@ -138,7 +133,8 @@ public:
   virtual const std::string Package_version () = 0;
   virtual const std::string Canonical_version () = 0;
   virtual void setCanonicalVersion (const std::string& ) = 0;
-//  virtual package_stability_t Stability () = 0;
+  virtual package_stability_t Stability () = 0;
+  virtual void SetStability (package_stability_t) = 0;
   virtual package_type_t Type () = 0;
   virtual const std::string SDesc () = 0;
   virtual void set_sdesc (const std::string& ) = 0;
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 08/14] Change to using a libsolv pool for storing package information

Jon TURNEY
In reply to this post by Jon TURNEY
Add class SolverVersion, a wrapper around a Solvable Id. The interface is
similar to class packageversion, the name change is just to make sure I've
got everything.

Place test packages into separate repos.

Expressing that curr: packages are preferred to prev: ones when that is not
the version number ordering should be done with epoch numbers.

Wire up various bits of data in packageversion to Solvable attributes,
including sourcepackage, stability, archive (packagesource) and depends.

Store sourcePackage() by the id rather than name, for much faster lookup.

SolverVersions for the same package can be ordered and compared by evr.

Factor out packagedb:addBinary() and also use it in IniDBBuilder, rather
that inlining the process of adding a package there. Add an analagous
packagedb:addSource() to do the same thing for source packages.

Change to reading installed.db after setup.ini's have been read, so we can
supplement the installed.db packages with information from setup.ini.

Make packagemeta::add_version() check for successful insertion of version.
Record the version at a stability level. The last version wins in setting
curr/test.

Use a Solver object inside packagedb

XXX: All SolvableVersion methods need to check for null solvable?
---
 IniDBBuilderPackage.cc  | 304 ++++++++++-----------------------
 IniDBBuilderPackage.h   |  28 +--
 Makefile.am             |   4 +-
 PackageSpecification.cc |  12 ++
 PackageSpecification.h  |   3 +
 configure.ac            |   1 +
 ini.cc                  |   5 +-
 libsolv.cc              | 443 ++++++++++++++++++++++++++++++++++++++++++++++++
 libsolv.h               | 146 ++++++++++++++++
 package_db.cc           |  90 ++++++++--
 package_db.h            |   6 +
 package_meta.cc         | 101 ++++++++---
 package_meta.h          |   6 +-
 13 files changed, 887 insertions(+), 262 deletions(-)
 create mode 100644 libsolv.cc
 create mode 100644 libsolv.h

diff --git a/IniDBBuilderPackage.cc b/IniDBBuilderPackage.cc
index fb200a8..b929c7e 100644
--- a/IniDBBuilderPackage.cc
+++ b/IniDBBuilderPackage.cc
@@ -22,8 +22,6 @@
 #include "IniParseFeedback.h"
 #include "package_db.h"
 #include "package_meta.h"
-#include "package_version.h"
-#include "cygpackage.h"
 #include "ini.h"
 // for strtoul
 #include <string.h>
@@ -34,7 +32,7 @@
 using namespace std;
 
 IniDBBuilderPackage::IniDBBuilderPackage (IniParseFeedback const &aFeedback) :
-cp (0), cbpv (), cspv (), currentSpec (0), _feedback (aFeedback){}
+currentSpec (0), _feedback (aFeedback){}
 
 IniDBBuilderPackage::~IniDBBuilderPackage()
 {
@@ -67,31 +65,28 @@ IniDBBuilderPackage::buildVersion (const std::string& aVersion)
 }
 
 void
-IniDBBuilderPackage::buildPackage (const std::string& name)
+IniDBBuilderPackage::buildPackage (const std::string& _name)
 {
-#if DEBUG
-  if (cp)
-    {
-      Log (LOG_BABBLE) << "Finished with package " << cp->name << endLog;
-      if (cbpv)
- {
-  Log (LOG_BABBLE) << "Version " << cbpv.Canonical_version() << endLog;
-  Log (LOG_BABBLE) << "Depends:";
-  dumpPackageDepends (cbpv.depends(), Log (LOG_BABBLE));
-  Log (LOG_BABBLE) << endLog;
- }
-    }
-#endif
-  packagedb db;
-  cp = db.findBinary (PackageSpecification(name));
-  if (!cp)
-    {
-      cp = new packagemeta (name);
-      db.packages.insert (packagedb::packagecollection::value_type(cp->name,cp));
-    }
-  cbpv = cygpackage::createInstance (name, package_binary);
-  cbpv.SetStability(TRUST_CURR);
-  cspv = packageversion ();
+  process();
+
+  /* Reset for next package */
+  name = _name;
+  message_id = "";
+  message_string = "";
+  categories.clear();
+
+  cbpv.reponame = release;
+  cbpv.version = "";
+  cbpv.vendor = release;
+  cbpv.sdesc = "";
+  cbpv.ldesc = "";
+  cbpv.stability = TRUST_CURR;
+  cbpv.type = package_binary;
+  cbpv.spkg = PackageSpecification();
+  cbpv.spkg_id = packageversion();
+  cbpv.requires = NULL;
+  cbpv.archive = packagesource();
+
   currentSpec = NULL;
   currentNodeList = PackageDepends();
 #if DEBUG
@@ -102,20 +97,19 @@ IniDBBuilderPackage::buildPackage (const std::string& name)
 void
 IniDBBuilderPackage::buildPackageVersion (const std::string& version)
 {
-  cbpv.setCanonicalVersion (version);
-  add_correct_version();
+  cbpv.version = version;
 }
 
 void
 IniDBBuilderPackage::buildPackageSDesc (const std::string& theDesc)
 {
-  cbpv.set_sdesc(theDesc);
+  cbpv.sdesc = theDesc;
 }
 
 void
 IniDBBuilderPackage::buildPackageLDesc (const std::string& theDesc)
 {
-  cbpv.set_ldesc(theDesc);
+  cbpv.ldesc = theDesc;
 }
 
 void
@@ -124,21 +118,23 @@ IniDBBuilderPackage::buildPackageInstall (const std::string& path,
                                           char *hash,
                                           hashType type)
 {
-  process_src (*cbpv.source(), path);
-  setSourceSize (*cbpv.source(), size);
+  // set archive path, size, mirror, hash
+  cbpv.archive.set_canonical(path.c_str());
+  cbpv.archive.size = atoi(size.c_str());
+  cbpv.archive.sites.push_back(site(parse_mirror));
 
   switch (type) {
   case hashType::sha512:
-    if (hash && !cbpv.source()->sha512_isSet)
+    if (hash && !cbpv.archive.sha512_isSet)
       {
-        memcpy (cbpv.source()->sha512sum, hash, sizeof(cbpv.source()->sha512sum));
-        cbpv.source()->sha512_isSet = true;
+        memcpy (cbpv.archive.sha512sum, hash, sizeof(cbpv.archive.sha512sum));
+        cbpv.archive.sha512_isSet = true;
       }
     break;
 
   case hashType::md5:
-    if (hash && !cbpv.source()->md5.isSet())
-      cbpv.source()->md5.set((unsigned char *)hash);
+    if (hash && !cbpv.archive.md5.isSet())
+      cbpv.archive.md5.set((unsigned char *)hash);
     break;
 
   case hashType::none:
@@ -152,79 +148,57 @@ IniDBBuilderPackage::buildPackageSource (const std::string& path,
                                          char *hash,
                                          hashType type)
 {
-  packagedb db;
-  /* get an appropriate metadata */
-  csp = db.findSource (PackageSpecification (cbpv.Name()));
-  if (!csp)
-    {
-      /* Copy the existing meta data to a new source package */
-      csp = new packagemeta (*cp);
-      /* delete versions information */
-      csp->versions.clear();
-      csp->desired = packageversion();
-      csp->installed = packageversion();
-      csp->curr = packageversion();
-      csp->exp = packageversion();
-      db.sourcePackages.insert (packagedb::packagecollection::value_type(csp->name,csp));
-    }
-  /* create a source packageversion */
-  cspv = cygpackage::createInstance (cbpv.Name(), package_source);
-  cspv.setCanonicalVersion (cbpv.Canonical_version());
-  set<packageversion>::iterator i=find (csp->versions.begin(),
-    csp->versions.end(), cspv);
-  if (i == csp->versions.end())
-    {
-      csp->add_version (cspv);
-    }
-  else
-    cspv = *i;
+  /* When there is a source: line, we invent a package to contain the source,
+     and make it the source package for this package. */
 
-  if (!cspv.source()->Canonical())
-    cspv.source()->set_canonical (path.c_str());
-  cspv.source()->sites.push_back(site(parse_mirror));
+  /* create a source package version */
+  SolverPool::addPackageData cspv = cbpv;
+  cspv.type = package_source;
+  cspv.requires = NULL;
 
-  /* creates the relationship between binary and source packageversions */
-  cbpv.setSourcePackageSpecification (PackageSpecification (cspv.Name()));
-  PackageSpecification &spec = cbpv.sourcePackageSpecification();
-  spec.setOperator (PackageSpecification::Equals);
-  spec.setVersion (cbpv.Canonical_version());
-
-  setSourceSize (*cspv.source(), size);
+  /* set archive path, size, mirror, hash */
+  cspv.archive = packagesource();
+  cspv.archive.set_canonical(path.c_str());
+  cspv.archive.size = atoi(size.c_str());
+  cspv.archive.sites.push_back(site(parse_mirror));
 
   switch (type) {
   case hashType::sha512:
-    if (hash && !cspv.source()->sha512_isSet)
+    if (hash && !cspv.archive.sha512_isSet)
       {
-        memcpy (cspv.source()->sha512sum, hash, sizeof(cspv.source()->sha512sum));
-        cspv.source()->sha512_isSet = true;
+        memcpy (cspv.archive.sha512sum, hash, sizeof(cspv.archive.sha512sum));
+        cspv.archive.sha512_isSet = true;
       }
     break;
 
   case hashType::md5:
-    if (hash && !cspv.source()->md5.isSet())
-      cspv.source()->md5.set((unsigned char *)hash);
+    if (hash && !cspv.archive.md5.isSet())
+      cspv.archive.md5.set((unsigned char *)hash);
     break;
 
   case hashType::none:
     break;
   }
+
+  packagedb db;
+  packageversion spkg_id = db.addSource (name + "-src", cspv);
+
+  /* create relationship between binary and source packageversions */
+  cbpv.spkg = PackageSpecification(name + "-src");
+  cbpv.spkg_id = spkg_id;
 }
 
 void
-IniDBBuilderPackage::buildPackageTrust (package_stability_t newtrust)
+IniDBBuilderPackage::buildPackageTrust (trusts newtrust)
 {
-  if (newtrust != TRUST_UNKNOWN)
-    {
-      cbpv = cygpackage::createInstance (cp->name, package_binary);
-      cbpv.SetStability(newtrust);
-      cspv = packageversion ();
-    }
+  process();
+  cbpv.stability = newtrust;
 }
 
 void
 IniDBBuilderPackage::buildPackageCategory (const std::string& name)
 {
-  cp->add_category (name);
+  categories.insert(name);
 }
 
 void
@@ -235,6 +209,7 @@ IniDBBuilderPackage::buildBeginDepends ()
 #endif
   currentSpec = NULL;
   currentNodeList = PackageDepends();
+  cbpv.requires = &currentNodeList;
 }
 
 void
@@ -244,42 +219,25 @@ IniDBBuilderPackage::buildBeginBuildDepends ()
   Log (LOG_BABBLE) << "Beginning of a Build-Depends statement" << endLog;
 #endif
   currentSpec = NULL;
-  currentNodeList = PackageDepends(); /* there is currently nowhere to store Build-Depends information */
+  currentNodeList = PackageDepends();
+  /* there is currently nowhere to store Build-Depends information */
 }
 
 void
-IniDBBuilderPackage::buildSourceName (const std::string& name)
+IniDBBuilderPackage::buildSourceName (const std::string& _name)
 {
-  if (cbpv)
-    {
-      cbpv.setSourcePackageSpecification (PackageSpecification (name));
+  // When there is a Source: line, that names a real source package
+  cbpv.spkg = PackageSpecification(name);
+  // XXX: set cbpv.spkg_id
 #if DEBUG
-      Log (LOG_BABBLE) << "\"" << cbpv.sourcePackageSpecification() <<
- "\" is the source package for " << cp->name << "." << endLog;
+  Log (LOG_BABBLE) << "\"" << _name << "\" is the source package for " << name << "." << endLog;
 #endif
-    }
-  else
-      _feedback.warning ((std::string ("Attempt to set source for package")
-                          + std::string(cp->name)
-  + "before creation of a version.").c_str());
 }
 
 void
 IniDBBuilderPackage::buildSourceNameVersion (const std::string& version)
 {
-  if (cbpv)
-    {
-      cbpv.sourcePackageSpecification().setOperator (PackageSpecification::Equals);
-      cbpv.sourcePackageSpecification().setVersion (version);
-#if DEBUG
-      Log (LOG_BABBLE) << "The source version needed for " << cp->name <<
- " is " << version << "." << endLog;
-#endif
-    }
-  else
-      _feedback.warning ((std::string ("Attempt to set source version for package")
-                          + std::string(cp->name)
-  + "before creation of a version.").c_str());
+  // XXX: should be stored as sourceevr
 }
 
 void
@@ -303,13 +261,8 @@ IniDBBuilderPackage::buildPackageListOperator (PackageSpecification::_operators
  endLog;
 #endif
     }
-  else
-    _feedback.warning ((std::string ("Attempt to set an operator for package ")
-                        + std::string(cp->name)
-       + " with no current specification.").c_str());
 }
 
-
 void
 IniDBBuilderPackage::buildPackageListOperatorVersion (const std::string& aVersion)
 {
@@ -321,111 +274,40 @@ IniDBBuilderPackage::buildPackageListOperatorVersion (const std::string& aVersio
  endLog;
 #endif
     }
-  else
-      _feedback.warning ((std::string ("Attempt to set an operator version for package ")
-                          + std::string(cp->name)
-  + " with no current specification.").c_str());
 }
 
-/* privates */
-
 void
-IniDBBuilderPackage::add_correct_version()
+IniDBBuilderPackage::buildMessage (const std::string& _message_id, const std::string& _message_string)
 {
-  cbpv.setDepends(currentNodeList);
+  message_id = _message_id;
+  message_string = _message_string;
+}
 
-  int merged = 0;
-  for (set<packageversion>::iterator n = cp->versions.begin();
-       !merged && n != cp->versions.end(); ++n)
-    if (*n == cbpv )
-      {
- packageversion ver = *n;
-        /* ASSUMPTIONS:
-           categories and requires are consistent for the same version across
-           all mirrors
-           */
-        /*
-          XXX: if the versions are equal but the size/md5sum are different,
-          we should alert the user, as they may not be getting what they expect...
-        */
-        /* Copy the binary mirror across if this site claims to have an install */
-        if (cbpv.source()->sites.size() )
-          ver.source()->sites.push_back(site (cbpv.source()->sites.begin()->key));
-        /* Copy the descriptions across */
-        if (cbpv.SDesc ().size() && !n->SDesc ().size())
-          ver.set_sdesc (cbpv.SDesc ());
-        if (cbpv.LDesc ().size() && !n->LDesc ().size())
-          ver.set_ldesc (cbpv.LDesc ());
- if (cbpv.depends().size() && !ver.depends().size())
-  ver.setDepends(cbpv.depends());
- /* TODO: other package lists */
- /* Prevent dangling references */
- currentNodeList = PackageDepends();
- currentSpec = NULL;
-        cbpv = *n;
-        merged = 1;
-#if DEBUG
-        Log (LOG_BABBLE) << cp->name << " merged with an existing version " << cbpv.Canonical_version() << endLog;
-#endif
-      }
+/* privates */
+void
+IniDBBuilderPackage::process ()
+{
+  if (!name.size())
+    return;
 
-  if (!merged)
-    {
-      cp->add_version (cbpv);
 #if DEBUG
-      Log (LOG_BABBLE) << cp->name << " version " << cbpv.Canonical_version() << " added" << endLog;
+  Log (LOG_BABBLE) << "Finished with package " << name << endLog;
+  Log (LOG_BABBLE) << "Version " << cbpv.version << endLog;
 #endif
-    }
 
-  /*
-    Should this version be the one selected for this package at a given
-    stability/trust setting?  After merging potentially multiple package
-    databases, we should pick the one with the highest version number.
-  */
-  packageversion *v = NULL;
-  switch (cbpv.Stability())
-  {
-    case TRUST_CURR:
-      v = &(cp->curr);
-    break;
-    case TRUST_TEST:
-      v = &(cp->exp);
-    break;
-    default:
-    break;
-  }
+  /* Transfer the accumulated package information to packagedb */
+  packagedb db;
+  packagemeta *pkg = db.addBinary (name, cbpv);
 
-  if (v)
+  // For no good historical reason, some data lives in packagemeta rather than
+  // the packageversion
+  for (auto i = categories.begin(); i != categories.end(); i++)
     {
-      int comparison = packageversion::compareVersions(cbpv, *v);
-
-      if ((bool)(*v))
-        Log (LOG_BABBLE) << "package " << cp->name << " comparing versions " << cbpv.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog;
-
-      if (comparison > 0)
-        {
-          *v = cbpv;
-        }
+      pkg->add_category(*i);
     }
-}
-
-void
-IniDBBuilderPackage::process_src (packagesource &src, const std::string& path)
-{
-  if (!src.Canonical())
-    src.set_canonical (path.c_str());
-  src.sites.push_back(site(parse_mirror));
-}
+  pkg->set_message(message_id, message_string);
 
-void
-IniDBBuilderPackage::setSourceSize (packagesource &src, const std::string& size)
-{
-  if (!src.size)
-    src.size = atoi(size.c_str());
-}
-
-void
-IniDBBuilderPackage::buildMessage (const std::string& message_id, const std::string& message)
-{
-  cp->set_message (message_id, message);
+  // Reset for next version
+  cbpv.spkg = PackageSpecification();
+  cbpv.spkg_id = packageversion();
 }
diff --git a/IniDBBuilderPackage.h b/IniDBBuilderPackage.h
index 8825add..307d2b5 100644
--- a/IniDBBuilderPackage.h
+++ b/IniDBBuilderPackage.h
@@ -17,11 +17,15 @@
 #define SETUP_INIDBBUILDERPACKAGE_H
 
 #include <vector>
-#include "package_version.h"
+#include <set>
+
+#include "package_message.h"
+#include "PackageTrust.h"
+#include "String++.h"
+#include "libsolv.h"
 
 class IniParseFeedback;
 class packagesource;
-class packagemeta;
 
 enum class hashType { none, md5, sha512 };
 
@@ -48,7 +52,7 @@ public:
   void buildPackageSource (const std::string& path, const std::string& size)
   { buildPackageSource(path, size, NULL, hashType::none); }
 
-  void buildPackageTrust (package_stability_t);
+  void buildPackageTrust (trusts);
   void buildPackageCategory (const std::string& );
 
   void buildBeginDepends ();
@@ -63,6 +67,7 @@ public:
   void set_arch (const std::string& a) { arch = a; }
   void set_release (const std::string& rel) { release = rel; }
 
+  // setup.ini header data
   unsigned int timestamp;
   std::string arch;
   std::string release;
@@ -70,16 +75,17 @@ public:
   std::string parse_mirror;
 
 private:
-  void add_correct_version();
-  void process_src (packagesource &src, const std::string& );
-  void setSourceSize (packagesource &src, const std::string& );
-
-  packagemeta *cp;
-  packageversion cbpv;
-  packagemeta *csp;
-  packageversion cspv;
+  void process ();
+
+  // package data
+  std::string name;
+  std::set <std::string, casecompare_lt_op> categories;
+  std::string message_id;
+  std::string message_string;
   PackageSpecification *currentSpec;
   PackageDepends currentNodeList;
+  SolverPool::addPackageData cbpv;
+
   IniParseFeedback const &_feedback;
 };
 
diff --git a/Makefile.am b/Makefile.am
index d4c8472..a8bfe4b 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -100,7 +100,7 @@ inilint_SOURCES = \
  String++.h
 
 @SETUP@_LDADD = \
- libgetopt++/libgetopt++.la -lgcrypt -lgpg-error -llzma -lbz2 -lz \
+ libgetopt++/libgetopt++.la -lgcrypt -lgpg-error -llzma -lbz2 -lz -lsolv -lgnurx \
  -lshlwapi -lcomctl32 -lole32 -lws2_32 -lpsapi -luuid -lntdll -lwininet -lmingw32
 @SETUP@_LDFLAGS = -mwindows -Wc,-static -static-libtool-libs
 @SETUP@_SOURCES = \
@@ -173,6 +173,8 @@ inilint_SOURCES = \
  IOStreamProvider.h \
  KeysSetting.cc \
  KeysSetting.h \
+ libsolv.cc \
+ libsolv.h \
  localdir.cc \
  localdir.h \
  LogFile.cc \
diff --git a/PackageSpecification.cc b/PackageSpecification.cc
index 247f3da..a2a3e48 100644
--- a/PackageSpecification.cc
+++ b/PackageSpecification.cc
@@ -28,6 +28,18 @@ PackageSpecification::packageName () const
   return _packageName;
 }
 
+const PackageSpecification::_operators
+PackageSpecification::op() const
+{
+  return _operator;
+}
+
+const std::string&
+PackageSpecification::version() const
+{
+  return _version;
+}
+
 void
 PackageSpecification::setOperator (_operators anOperator)
 {
diff --git a/PackageSpecification.h b/PackageSpecification.h
index 4c3ed6d..ef992fa 100644
--- a/PackageSpecification.h
+++ b/PackageSpecification.h
@@ -39,6 +39,9 @@ public:
   };
 
   const std::string& packageName() const;
+  const _operators op() const;
+  const std::string& version() const;
+
   void setOperator (_operators);
   void setVersion (const std::string& );
 
diff --git a/configure.ac b/configure.ac
index ab548c0..421b9b4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -60,6 +60,7 @@ AC_CHECK_HEADER(zlib.h, , missing_deps="$missing_deps zlib")
 AC_CHECK_HEADER(bzlib.h, , missing_deps="$missing_deps libbz2")
 AC_CHECK_HEADER(lzma.h, , missing_deps="$missing_deps liblzma")
 AC_CHECK_HEADER(gcrypt.h, , missing_deps="$missing_deps libgcrypt")
+AC_CHECK_HEADER(solv/pool.h, , missing_deps="$missing_deps libsolv")
 
 if test -n "$missing_deps"; then
  AC_MSG_ERROR([missing prerequisites: $missing_deps])
diff --git a/ini.cc b/ini.cc
index 1162b91..b324e73 100644
--- a/ini.cc
+++ b/ini.cc
@@ -346,15 +346,14 @@ do_remote_ini (HWND owner)
 static bool
 do_ini_thread (HINSTANCE h, HWND owner)
 {
-  packagedb db;
-  db.read();
-
   size_t ini_count = 0;
   if (source == IDC_SOURCE_LOCALDIR)
     ini_count = do_local_ini (owner);
   else
     ini_count = do_remote_ini (owner);
 
+  packagedb db;
+  db.read();
   db.upgrade();
 
   if (ini_count == 0)
diff --git a/libsolv.cc b/libsolv.cc
new file mode 100644
index 0000000..67c68b5
--- /dev/null
+++ b/libsolv.cc
@@ -0,0 +1,443 @@
+/*
+ * Copyright (c) 2017 Jon Turney
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#include "libsolv.h"
+
+#include "solv/solver.h"
+#include "solv/solverdebug.h"
+#include "solv/evr.h"
+
+#include "LogSingleton.h"
+
+// ---------------------------------------------------------------------------
+// Utility functions for mapping between Operators and Relation Ids
+// ---------------------------------------------------------------------------
+
+static Id
+Operator2RelId(PackageSpecification::_operators op)
+{
+  switch (op)
+    {
+    case PackageSpecification::Equals:
+      return REL_EQ;
+    case PackageSpecification::LessThan:
+      return REL_LT;
+    case PackageSpecification::MoreThan:
+      return REL_GT;
+    case PackageSpecification::LessThanEquals:
+      return REL_LT | REL_EQ;
+    case PackageSpecification::MoreThanEquals:
+      return REL_GT | REL_EQ;
+    }
+
+  return 0;
+}
+
+static PackageSpecification::_operators
+RelId2Operator(Id id)
+{
+  switch (id)
+    {
+    case REL_EQ:
+      return PackageSpecification::Equals;
+    case REL_LT:
+      return PackageSpecification::LessThan;
+    case REL_GT:
+      return PackageSpecification::MoreThan;
+    case REL_LT | REL_EQ:
+      return PackageSpecification::LessThanEquals;
+    case REL_GT | REL_EQ:
+      return PackageSpecification::MoreThanEquals;
+    }
+
+  return PackageSpecification::Equals;
+}
+
+// ---------------------------------------------------------------------------
+// implements class SolvableVersion
+//
+// a wrapper around a libsolv Solvable
+// ---------------------------------------------------------------------------
+
+const std::string
+SolvableVersion::Name () const
+{
+  Solvable *solvable = pool_id2solvable(pool, id);
+  return std::string(pool_id2str(pool, solvable->name));
+}
+
+const std::string
+SolvableVersion::Canonical_version() const
+{
+  Solvable *solvable = pool_id2solvable(pool, id);
+  return std::string(pool_id2str(pool, solvable->evr));
+}
+
+package_type_t
+SolvableVersion::Type () const
+{
+  Solvable *solvable = pool_id2solvable(pool, id);
+  if (solvable->arch == ARCH_SRC)
+    return package_source;
+  else
+    return package_binary;
+}
+
+const PackageDepends
+SolvableVersion::depends() const
+{
+  Solvable *solvable = pool_id2solvable(pool, id);
+
+  Queue q;
+  queue_init(&q);
+
+  if (repo_lookup_idarray(solvable->repo, id, SOLVABLE_REQUIRES, &q))
+    {
+      // convert
+      PackageDepends dep;
+
+      for (int i = 0; i < q.count; i++)
+        {
+#ifdef DEBUG
+          Log (LOG_PLAIN) << "dep " << std::hex << q.elements[i] << ": " << pool_dep2str(pool, q.elements[i]) << endLog;
+#endif
+
+          const char *name = pool_id2str(pool, q.elements[i]);
+          PackageSpecification *spec = new PackageSpecification (name);
+
+          if (ISRELDEP(id))
+            {
+              Reldep *rd = GETRELDEP(pool, id);
+              spec->setOperator(RelId2Operator(rd->flags));
+              spec->setVersion(pool_id2str(pool, rd->evr));
+            }
+
+          dep.push_back (spec);
+        }
+
+      queue_empty(&q);
+
+      return dep;
+    }
+
+  // otherwise, return an empty depends list
+  static PackageDepends empty_package;
+  return empty_package;
+}
+
+const std::string
+SolvableVersion::SDesc () const
+{
+  Solvable *solvable = pool_id2solvable(pool, id);
+  const char *sdesc = repo_lookup_str(solvable->repo, id, SOLVABLE_SUMMARY);
+  return sdesc;
+}
+
+SolvableVersion
+SolvableVersion::sourcePackage () const
+{
+  if (!id)
+    return SolvableVersion();
+
+  // extract source package id
+  Solvable *solvable = pool_id2solvable(pool, id);
+  Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1);
+  Id spkg_id = repo_lookup_id(solvable->repo, id, spkg_attr);
+
+  // has no such attribute
+  if (!spkg_id)
+    return SolvableVersion();
+
+  return SolvableVersion(spkg_id, pool);
+}
+
+packagesource *
+SolvableVersion::source() const
+{
+  if (!id) {
+    static packagesource empty_source = packagesource();
+    return &empty_source;
+  }
+
+  Solvable *solvable = pool_id2solvable(pool, id);
+  Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1);
+  return (packagesource *)repo_lookup_num(solvable->repo, id, psrc_attr, 0);
+}
+
+bool
+SolvableVersion::accessible () const
+{
+  // XXX: accessible if archive is locally available, or we know a mirror
+  //
+  // (This seems utterly pointless.  Packages which aren't locally available are
+  // removed from the package list.  Packages we don't know a mirror for don't
+  // appear in the packagelist.)
+  return TRUE;
+}
+
+package_stability_t
+SolvableVersion::Stability () const
+{
+  Solvable *solvable = pool_id2solvable(pool, id);
+  Id stability_attr = pool_str2id(pool, "solvable:stability", 1);
+  return (package_stability_t)repo_lookup_num(solvable->repo, id, stability_attr, TRUST_UNKNOWN);
+}
+
+bool
+SolvableVersion::operator <(SolvableVersion const &rhs) const
+{
+  return (compareVersions(*this, rhs) < 0);
+}
+
+bool
+SolvableVersion::operator ==(SolvableVersion const &rhs) const
+{
+  return (compareVersions(*this, rhs) == 0);
+}
+
+bool
+SolvableVersion::operator !=(SolvableVersion const &rhs) const
+{
+  return (compareVersions(*this, rhs) != 0);
+}
+
+int
+SolvableVersion::compareVersions(const SolvableVersion &a,
+                                 const SolvableVersion &b)
+{
+  if (a.id == b.id)
+    return 0;
+
+  // if a and b are different, at least one of them has a pool
+  Pool *pool = a.pool ? a.pool : b.pool;
+
+  Solvable *sa = a.id ? pool_id2solvable(a.pool, a.id) : NULL;
+  Solvable *sb = b.id ? pool_id2solvable(b.pool, b.id) : NULL;
+
+  // empty versions compare as if their version is the empty string
+  Id evra = sa ? sa->evr : pool_str2id(pool, "", 1);
+  Id evrb = sb ? sb->evr : pool_str2id(pool, "", 1);
+
+  return pool_evrcmp(pool, evra, evrb, EVRCMP_COMPARE);
+}
+
+// ---------------------------------------------------------------------------
+// implements class SolverPool
+//
+// a simplified wrapper for libsolv
+// ---------------------------------------------------------------------------
+
+static
+void debug_callback(Pool *pool, void *data, int type, const char *str)
+{
+  if (type & (SOLV_FATAL|SOLV_ERROR))
+    LogPlainPrintf("libsolv: %s", str);
+  else
+    LogBabblePrintf("libsolv: %s", str);
+}
+
+SolverPool::SolverPool()
+{
+  /* create a pool */
+  pool = pool_create();
+
+  pool_setdebugcallback(pool, debug_callback, NULL);
+
+  int level = 1;
+#if DEBUG
+  level = 3;
+#endif
+  pool_setdebuglevel(pool, level);
+
+  /* create the repo to hold installed packages */
+  SolvRepo *installed = getRepo("_installed");
+  pool_set_installed(pool, installed->repo);
+}
+
+SolvRepo *
+SolverPool::getRepo(const std::string &name, bool test)
+{
+  RepoList::iterator i = repos.find(name);
+  if (i != repos.end())
+    return i->second;
+
+  /* create repo if not found */
+  SolvRepo *r = new(SolvRepo);
+  r->repo = repo_create(pool, name.c_str());
+
+  /* create attribute store, with no local pool */
+  r->data = repo_add_repodata(r->repo, 0);
+
+  /* remember if this is a test stability repo */
+  r->test = test;
+
+  repos[name] = r;
+
+  return r;
+}
+
+/*
+  Helper function to convert a PackageDepends list to libsolv dependencies.
+*/
+Id
+SolverPool::makedeps(Repo *repo, PackageDepends *requires)
+{
+  Id deps = 0;
+
+  for (PackageDepends::iterator i = requires->begin();
+       i != requires->end();
+       i++)
+    {
+      Id name = pool_str2id(pool, (*i)->packageName().c_str(), 1);
+
+      if ((*i)->version().size() == 0)
+        {
+          // no relation, so dependency is just on package name
+          deps = repo_addid_dep(repo, deps, name, 0);
+        }
+      else
+        {
+          // otherwise, dependency is on package name with a version condition
+          Id evr = pool_str2id(pool, (*i)->version().c_str(), 1);
+          int rel = pool_rel2id(pool, name, evr, Operator2RelId((*i)->op()), 1);
+
+          deps = repo_addid_dep(repo, deps, rel, 0);
+        }
+    }
+
+  return deps;
+}
+
+SolvableVersion
+SolverPool::addPackage(const std::string& pkgname, const addPackageData &pkgdata)
+{
+  std::string repoName = pkgdata.reponame;
+  bool test = false;
+
+  /* It's simplest to place test packages into a separate repo, and then
+     arrange for that repo to be disabled, if we don't want to consider
+     those packages */
+
+  if (pkgdata.stability == TRUST_TEST)
+    {
+      repoName = pkgdata.reponame + "_test_";
+      test = true;
+    }
+
+  SolvRepo *r = getRepo(repoName, test);
+  Repo *repo = r->repo;
+
+  /* create a solvable */
+  Id s = repo_add_solvable(repo);
+  Solvable *solvable = pool_id2solvable(pool, s);
+
+  /* initialize solvable for this packageo/version/etc.  */
+  solvable->name = pool_str2id(pool, pkgname.c_str(), 1);
+  solvable->arch = (pkgdata.type == package_binary) ? ARCH_ANY : ARCH_SRC;
+  solvable->evr = pool_str2id(repo->pool, pkgdata.version.c_str(), 1);
+  solvable->vendor = pool_str2id(repo->pool, pkgdata.vendor.c_str(), 1);
+  solvable->provides = repo_addid_dep(repo, solvable->provides, pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0);
+  if (pkgdata.requires)
+    solvable->requires = makedeps(repo, pkgdata.requires);
+
+  /* a solvable can also store arbitrary attributes not needed for dependency
+     resolution, if we need them */
+
+  Repodata *data = r->data;
+  Id handle = s;
+#if DEBUG
+  Log (LOG_PLAIN) << "solvable " << s << " name " << pkgname << endLog;
+#endif
+
+  /* store short description attribute */
+  repodata_set_str(data, handle, SOLVABLE_SUMMARY, pkgdata.sdesc.c_str());
+  /* store long description attribute */
+  repodata_set_str(data, handle, SOLVABLE_DESCRIPTION, pkgdata.ldesc.c_str());
+
+  /* store source-package attribute */
+  const std::string sname = pkgdata.spkg.packageName();
+  if (!sname.empty())
+    repodata_set_id(data, handle, SOLVABLE_SOURCENAME, pool_str2id(pool, sname.c_str(), 1));
+  else
+    repodata_set_void(data, handle, SOLVABLE_SOURCENAME);
+  /* solvable:sourceevr may also be available from spkg but assumed to be same
+     as evr for the moment */
+
+  /* store source-package id */
+  /* XXX: this assumes we create install package after source package and so can
+     know that id */
+  Id spkg_attr = pool_str2id(pool, "solvable:sourceid", 1);
+  repodata_set_id(data, handle, spkg_attr, pkgdata.spkg_id.id);
+
+  /* we could store packagesource information as attributes ...
+
+     e.g.
+       size     SOLVABLE_DOWNLOADSIZE
+       pathname SOLVABLE_MEDIAFILE
+       site     SOLVABLE_MEDIABASE
+       checksum SOLVABLE_CHECKSUM
+
+     ... but for the moment, we just store a pointer to a packagesource object
+  */
+  Id psrc_attr = pool_str2id(pool, "solvable:packagesource", 1);
+  packagesource *psrc = new packagesource(pkgdata.archive);
+  repodata_set_num(data, handle, psrc_attr, (intptr_t)psrc);
+
+  /* store stability level attribute */
+  Id stability_attr = pool_str2id(pool, "solvable:stability", 1);
+  repodata_set_num(data, handle, stability_attr, pkgdata.stability);
+
+#if 0
+  repodata_internalize(data);
+
+  /* debug: verify the attributes we've just set get retrieved correctly */
+  SolvableVersion sv = SolvableVersion(s, pool);
+  const std::string check_sdesc = sv.SDesc();
+  if (pkgdata.sdesc.compare(check_sdesc) != 0) {
+    Log (LOG_PLAIN) << pkgname << " has sdesc mismatch: '" << pkgdata.sdesc << "' and '"
+                    << check_sdesc << "'" << endLog;
+  }
+  if (!sname.empty()) {
+    SolvableVersion check_spkg = sv.sourcePackage();
+    Solvable *check_spkg_solvable = pool_id2solvable(pool, check_spkg.id);
+    std::string check_sname = pool_id2str(pool, check_spkg_solvable->name);
+    if (sname.compare(check_sname) != 0) {
+      Log (LOG_PLAIN) << pkgname << " has spkg mismatch: '" << pkgdata.spkg.packageName()
+                      << "' and '" << check_sname << "'" << endLog;
+    }
+  }
+  packagesource *check_archive = sv.source();
+  if (check_archive != psrc)
+    Log (LOG_PLAIN) << pkgname << " has archive mismatch: " << psrc
+                    << " and " << check_archive << endLog;
+  package_stability_t check_stability = sv.Stability();
+  if (check_stability != pkgdata.stability) {
+    Log (LOG_PLAIN) << pkgname << " has stability mismatch: " << pkgdata.stability
+                    << " and " << check_stability << endLog;
+  }
+#endif
+
+  return SolvableVersion(s, pool);
+}
+
+void
+SolverPool::internalize()
+{
+  /* Make attribute data available to queries */
+  for (RepoList::iterator i = repos.begin();
+       i != repos.end();
+       i++)
+    {
+      repodata_internalize(i->second->data);
+    }
+}
diff --git a/libsolv.h b/libsolv.h
new file mode 100644
index 0000000..490da78
--- /dev/null
+++ b/libsolv.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2017 Jon Turney
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#ifndef LIBSOLV_H
+#define LIBSOLV_H
+
+#include "solv/pool.h"
+#include "solv/repo.h"
+#include "PackageSpecification.h"
+#include "PackageTrust.h"
+#include "package_source.h"
+#include "package_depends.h"
+#include <map>
+#include <vector>
+
+typedef trusts package_stability_t;
+
+typedef enum
+{
+  package_binary,
+  package_source
+}
+package_type_t;
+
+// ---------------------------------------------------------------------------
+// interface to class SolverVersion
+//
+// a wrapper around a libsolv Solvable
+// ---------------------------------------------------------------------------
+
+class SolverPool;
+
+class SolvableVersion
+{
+ public:
+  SolvableVersion() : id(0), pool(0) {};
+  SolvableVersion(Id _id, Pool *_pool) : id(_id), pool(_pool) {};
+
+  // converted to a bool, this is true if this isn't the result of the default
+  // constructor (an 'empty' version, in some sense)
+  explicit operator bool () const { return (id != 0); }
+
+  const std::string Name () const;
+  const std::string SDesc () const;
+  // In setup-speak, 'Canonical' version means 'e:v-r', the non-decomposed version
+  const std::string Canonical_version () const;
+  const PackageDepends depends() const;
+  bool accessible () const;
+  package_type_t Type () const;
+  package_stability_t Stability () const;
+
+  // the associated source package, if this is a binary package
+  SolvableVersion sourcePackage () const;
+
+  // where this package archive can be obtained from
+  packagesource *source() const;
+
+  // utility function to compare package versions
+  static int compareVersions(const SolvableVersion &a, const SolvableVersion &b);
+
+  // comparison operators
+
+  // these are somewhat necessary as otherwise we are compared as bool values
+  bool operator == (SolvableVersion const &) const;
+  bool operator != (SolvableVersion const &) const;
+
+  // these are only well defined for versions of the same package
+  bool operator < (SolvableVersion const &) const;
+  bool operator <= (SolvableVersion const &) const;
+  bool operator > (SolvableVersion const &) const;
+  bool operator >= (SolvableVersion const &) const;
+
+ private:
+  Id id;
+  Pool *pool;
+
+  friend SolverPool;
+};
+
+// ---------------------------------------------------------------------------
+// Helper class SolvRepo
+//
+// ---------------------------------------------------------------------------
+
+class SolvRepo
+{
+public:
+  Repo *repo;
+  Repodata *data;
+  bool test;
+};
+
+// ---------------------------------------------------------------------------
+// interface to class SolverPool
+//
+// a simplified wrapper for libsolv
+// ---------------------------------------------------------------------------
+
+class SolverPool
+{
+public:
+  SolverPool();
+  SolvRepo *getRepo(const std::string &name, bool test = false);
+
+  // Utility class for passing arguments to addPackage()
+  class addPackageData
+  {
+  public:
+    std::string reponame;
+    std::string version;
+    std::string vendor;
+    std::string sdesc;
+    std::string ldesc;
+    package_stability_t stability;
+    package_type_t type;
+    packagesource archive;
+    PackageSpecification spkg;
+    SolvableVersion spkg_id;
+    PackageDepends *requires;
+  };
+
+  SolvableVersion addPackage(const std::string& pkgname,
+                             const addPackageData &pkgdata);
+
+  void internalize(void);
+
+private:
+  Id makedeps(Repo *repo, PackageDepends *requires);
+  Pool *pool;
+
+  typedef std::map<std::string, SolvRepo *> RepoList;
+  RepoList repos;
+};
+
+
+#endif // LIBSOLV_H
diff --git a/package_db.cc b/package_db.cc
index 4e22953..c561089 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -40,6 +40,7 @@
 #include "Generic.h"
 #include "LogSingleton.h"
 #include "resource.h"
+#include "libsolv.h"
 
 using namespace std;
 
@@ -52,6 +53,8 @@ packagedb::read ()
 {
   if (!installeddbread)
     {
+      solver.internalize();
+
       /* Read in the local installation database. */
       io_stream *db = 0;
       db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
@@ -103,23 +106,37 @@ packagedb::read ()
   if (!parseable)
     continue;
 
-  packagemeta *pkg = findBinary (PackageSpecification(pkgname));
-  if (!pkg)
-    {
-      pkg = new packagemeta (pkgname);
-      packages.insert (packagedb::packagecollection::value_type(pkgname, pkg));
-    }
-
-  packageversion binary =
-    cygpackage::createInstance (pkgname, f.ver,
- package_binary);
-
-  pkg->add_version (binary);
-  pkg->set_installed (binary);
-  pkg->desired = pkg->installed;
+                  SolverPool::addPackageData data;
+                  data.reponame = "_installed";
+                  data.version = f.ver;
+                  data.type = package_binary;
+
+                  // very limited information is available from installed.db, so
+                  // we put our best guesses here...
+                  data.vendor = "cygwin";
+                  data.requires = NULL;
+                  data.sdesc = "";
+                  data.ldesc = "";
+                  data.stability = TRUST_CURR; // XXX: would be nice to get this correct as it effects upgrade decisions...
+
+                  // supplement this with sdesc and source information from
+                  // setup.ini, if possible...
+                  packagemeta *pkgm = findBinary(PackageSpecification(pkgname));
+                  if (pkgm)
+                    {
+                      data.sdesc = pkgm->curr.SDesc();
+                      data.archive = *pkgm->curr.source();
+                      data.spkg = std::string(pkgname) + "-src";
+                      data.spkg_id = pkgm->curr.sourcePackage();
+                    }
+
+  packagemeta *pkg = packagedb::addBinary (pkgname, data);
+
+  pkg->set_installed_version (f.ver);
 
   if (dbver == 3)
     pkg->user_picked = (user_picked & 1);
+
  }
       delete db;
       db = 0;
@@ -132,6 +149,50 @@ packagedb::read ()
   installeddbver = dbver;
  }
     }
+  solver.internalize();
+}
+
+/* Add a package version to the packagedb */
+packagemeta *
+packagedb::addBinary (const std::string &pkgname,
+                      const SolverPool::addPackageData &pkgdata)
+{
+  /* If pkgname isn't already in packagedb, add a packagemeta */
+  packagemeta *pkg = findBinary (PackageSpecification(pkgname));
+  if (!pkg)
+    {
+      pkg = new packagemeta (pkgname);
+      packages.insert (packagedb::packagecollection::value_type(pkgname, pkg));
+    }
+
+  /* Create the SolvableVersion  */
+  SolvableVersion sv = solver.addPackage(pkgname, pkgdata);
+
+  /* Register it in packagemeta */
+  pkg->add_version (sv, pkgdata);
+
+  return pkg;
+}
+
+packageversion
+packagedb::addSource (const std::string &pkgname,
+                      const SolverPool::addPackageData &pkgdata)
+{
+  /* If pkgname isn't already in packagedb, add a packagemeta */
+  packagemeta *pkg = findSource (PackageSpecification(pkgname));
+  if (!pkg)
+    {
+      pkg = new packagemeta (pkgname);
+      sourcePackages.insert (packagedb::packagecollection::value_type(pkgname, pkg));
+    }
+
+  /* Create the SolvableVersion  */
+  SolvableVersion sv = solver.addPackage(pkgname, pkgdata);
+
+  /* Register it in packagemeta */
+  pkg->add_version (sv, pkgdata);
+
+  return sv;
 }
 
 int
@@ -230,6 +291,7 @@ packagedb::categoriesType packagedb::categories;
 packagedb::packagecollection packagedb::sourcePackages;
 PackageDBActions packagedb::task = PackageDB_Install;
 std::vector <packagemeta *> packagedb::dependencyOrderedPackages;
+SolverPool packagedb::solver;
 
 #include <stack>
 
diff --git a/package_db.h b/package_db.h
index d26f8ad..022358e 100644
--- a/package_db.h
+++ b/package_db.h
@@ -57,6 +57,7 @@ typedef std::vector <packagemeta *>::iterator PackageDBConnectedIterator;
 
 */
 
+#include "libsolv.h"
 #include <PackageTrust.h>
 
 class packagedb
@@ -69,6 +70,8 @@ public:
   void upgrade ();
   packagemeta * findBinary (PackageSpecification const &) const;
   packagemeta * findSource (PackageSpecification const &) const;
+  packagemeta * addBinary (const std::string &pkgname, const SolverPool::addPackageData &pkgdata);
+  packageversion addSource (const std::string &pkgname, const SolverPool::addPackageData &pkgdata);
   PackageDBConnectedIterator connectedBegin();
   PackageDBConnectedIterator connectedEnd();
   void fillMissingCategory();
@@ -83,6 +86,9 @@ public:
   typedef std::map <std::string, std::vector <packagemeta *>, casecompare_lt_op > categoriesType;
   static categoriesType categories;
   static PackageDBActions task;
+
+  static SolverPool solver;
+
 private:
   static int installeddbread; /* do we have to reread this */
   static int installeddbver;
diff --git a/package_meta.cc b/package_meta.cc
index 425df59..c19eddf 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -128,19 +128,84 @@ packagemeta::~packagemeta()
 }
 
 void
-packagemeta::add_version (packageversion & thepkg)
+packagemeta::add_version (packageversion & thepkg, const SolverPool::addPackageData &pkgdata)
 {
-  /* todo: check return value */
-  versions.insert (thepkg);
+  /*
+    If a packageversion for the same version number is already present,allow
+    this version to replace it.
+
+    There is a problem where multiple repos provide a package.  It's never been
+    clear which repo should win.  With this implementation, the last one added
+    will win.
+
+    We rely on this by adding packages from installed.db last.
+   */
+
+  set <packageversion>::iterator i = versions.find(thepkg);
+  if (i != versions.end())
+    {
+      versions.erase(i);
+    }
+
+  /* Add the version */
+  std::pair<std::set <packageversion>::iterator, bool> result = versions.insert (thepkg);
+
+  if (!result.second)
+    Log (LOG_PLAIN) << "Failed to add version " << thepkg.Canonical_version() << " in package " << name << endLog;
+#ifdef DEBUG
+  else
+    Log (LOG_PLAIN) << "Added version " << thepkg.Canonical_version() << " in package " << name << endLog;
+#endif
+
+  /* Record the highest version at a given stability level */
+  /* (This has to be written somewhat carefully as attributes aren't
+     internalized yet so we can't look at them) */
+  packageversion *v = NULL;
+  switch (pkgdata.stability)
+    {
+    case TRUST_CURR:
+      v = &(this->curr);
+      break;
+    case TRUST_TEST:
+      v = &(this->exp);
+      break;
+    default:
+      break;
+    }
+
+  if (v)
+    {
+      /* Any version is always greater than no version */
+      int comparison = 1;
+      if (*v)
+        comparison = SolvableVersion::compareVersions(thepkg, *v);
+
+#ifdef DEBUG
+      if ((bool)(*v))
+        Log (LOG_BABBLE) << "package " << thepkg.Name() << " comparing versions " << thepkg.Canonical_version() << " and " << v->Canonical_version() << ", result was " << comparison << endLog;
+#endif
+
+      if (comparison >= 0)
+        {
+          *v = thepkg;
+        }
+    }
 }
 
-/* assumption: package thepkg is already in the metadata list. */
 void
-packagemeta::set_installed (packageversion & thepkg)
+packagemeta::set_installed_version (const std::string &version)
 {
-  set<packageversion>::const_iterator temp = versions.find (thepkg);
-  if (temp != versions.end())
-    installed = thepkg;
+  set<packageversion>::iterator i;
+  for (i = versions.begin(); i != versions.end(); i++)
+    {
+      if (version.compare(i->Canonical_version()) == 0)
+        {
+          installed = *i;
+
+          /* and mark as Keep */
+          desired = installed;
+        }
+    }
 }
 
 void
@@ -174,12 +239,6 @@ packagemeta::getReadableCategoryList () const
               ).visitor.result;
 }
 
-static bool
-hasSDesc(packageversion const &pkg)
-{
-  return pkg.SDesc().size();
-}
-
 static void
 parseNames (std::set<string> &parsed, std::string &option)
 {
@@ -304,11 +363,15 @@ bool packagemeta::isManuallyDeleted() const
 const std::string
 packagemeta::SDesc () const
 {
-  set<packageversion>::iterator i = find_if (versions.begin(), versions.end(), hasSDesc);
-  if (i == versions.end())
-    return std::string();
-  return i->SDesc ();
-};
+  set<packageversion>::iterator i;
+  for (i = versions.begin(); i != versions.end(); i++)
+    {
+      if (i->SDesc().size())
+        return i->SDesc ();
+    }
+
+  return std::string();
+}
 
 /* Return an appropriate caption given the current action. */
 std::string
diff --git a/package_meta.h b/package_meta.h
index 8041aa1..421646a 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -28,7 +28,7 @@ class packagemeta;
 
 typedef std::pair<const std::string, std::vector<packagemeta *> > Category;
 
-/* NOTE: A packagemeta without 1 packageversion is invalid! */
+/* NOTE: A packagemeta without 1 version is invalid! */
 class packagemeta
 {
 public:
@@ -42,8 +42,8 @@ public:
 
   ~packagemeta ();
 
-  void add_version (packageversion &);
-  void set_installed (packageversion &);
+  void add_version (packageversion &, const SolverPool::addPackageData &);
+  void set_installed_version (const std::string &);
   void addToCategoryBase();
   bool hasNoCategories() const;
   void setDefaultCategories();
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 09/14] Remove cygpackage class

Jon TURNEY
In reply to this post by Jon TURNEY
---
 Makefile.am     |   2 -
 bootstrap.sh    |   2 +-
 cygpackage.cc   | 136 --------------------------------------------------------
 cygpackage.h    |  82 ----------------------------------
 package_db.cc   |   1 -
 package_meta.cc |   1 -
 6 files changed, 1 insertion(+), 223 deletions(-)
 delete mode 100644 cygpackage.cc
 delete mode 100644 cygpackage.h

diff --git a/Makefile.am b/Makefile.am
index a8bfe4b..27bb8f1 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -128,8 +128,6 @@ inilint_SOURCES = \
  crypto.cc \
  crypto.h \
  cyg-pubkey.h \
- cygpackage.cc \
- cygpackage.h \
  desktop.cc \
  desktop.h \
  dialog.cc \
diff --git a/bootstrap.sh b/bootstrap.sh
index f21206d..a676268 100755
--- a/bootstrap.sh
+++ b/bootstrap.sh
@@ -21,7 +21,7 @@ bootstrap() {
 cd "$srcdir"
 
 # Make sure we are running in the right directory
-if [ ! -f cygpackage.cc ]; then
+if [ ! -f bmain.cc ]; then
   echo "You must run this script from the directory containing it"
   exit 1
 fi
diff --git a/cygpackage.cc b/cygpackage.cc
deleted file mode 100644
index 2724249..0000000
--- a/cygpackage.cc
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * Copyright (c) 2001, Robert Collins.
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     A copy of the GNU General Public License can be found at
- *     http://www.gnu.org/
- *
- * Written by Robert Collins  <[hidden email]>
- *
- */
-
-/* this is the parent class for all package operations.
- */
-
-#include "cygpackage.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "io_stream.h"
-#include "compress.h"
-
-#include "package_version.h"
-#include "cygpackage.h"
-#include "LogSingleton.h"
-
-/* this constructor creates an invalid package - further details MUST be provided */
-cygpackage::cygpackage ():
-name (),
-vendor (),
-packagev (),
-canonical (),
-sdesc (),
-ldesc (),
-type (package_binary)
-{
-  /* FIXME: query the install database for the currently installed
-   * version details
-   */
-}
-
-packageversion
-cygpackage::createInstance (const std::string& pkgname,
-                            const package_type_t type)
-{
-  cygpackage *temp = new cygpackage;
-  temp->name = pkgname;
-  temp->type = type;
-  return packageversion(temp);
-}
-
-packageversion
-cygpackage::createInstance (const std::string& pkgname,
-                            const std::string& version,
-    package_type_t const newtype)
-{
-  cygpackage *temp = new cygpackage;
-  temp->name = pkgname;
-  temp->type = newtype;
-  temp->setCanonicalVersion (version);
-  return packageversion(temp);
-}
-
-/* tell the version */
-void
-cygpackage::setCanonicalVersion (const std::string& version)
-{
-  canonical = version;
-
-  const char *start = canonical.c_str();
-  const char *curr = strchr(start, '-');
-
-  if (curr)
-    {
-      const char *next;
-      while ((next = strchr (curr + 1, '-')))
- curr = next;
-
-      /* package version appears after the last '-' in the version string */
-      packagev = curr + 1;
-      /* vendor version is everything up to that last '-' */
-      vendor.assign(canonical.c_str(), (size_t)(curr - start));
-    }
-  else
-    {
-      // FIXME: What's up with the "0"? It's probably a mistake, and should be
-      // "". It used to be written as 0, and was subject to a bizarre implicit
-      // conversion by the unwise String(int) constructor.
-      packagev = "0";
-      vendor = version;
-    }
-}
-
-cygpackage::~cygpackage ()
-{
-}
-
-const std::string
-cygpackage::Name ()
-{
-  return name;
-}
-
-const std::string
-cygpackage::Vendor_version ()
-{
-  return vendor;
-}
-
-const std::string
-cygpackage::Package_version ()
-{
-  return packagev;
-}
-
-std::string  const
-cygpackage::Canonical_version ()
-{
-  return canonical;
-}
-
-void
-cygpackage::set_sdesc (const std::string& desc)
-{
-  sdesc = desc;
-}
-
-void
-cygpackage::set_ldesc (const std::string& desc)
-{
-  ldesc = desc;
-}
diff --git a/cygpackage.h b/cygpackage.h
deleted file mode 100644
index 720921d..0000000
--- a/cygpackage.h
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * Copyright (c) 2001, Robert Collins.
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     A copy of the GNU General Public License can be found at
- *     http://www.gnu.org/
- *
- * Written by Robert Collins  <[hidden email]>
- *
- */
-
-#ifndef SETUP_CYGPACKAGE_H
-#define SETUP_CYGPACKAGE_H
-
-/* This is a cygwin specific package class, that should be able to
- * arbitrate acceess to cygwin binary packages amd cygwin source packages
- */
-
-#include "package_version.h"
-
-class io_stream;
-
-class cygpackage:public _packageversion
-{
-public:
-  virtual const std::string Name ();
-  virtual const std::string Vendor_version ();
-  virtual const std::string Package_version ();
-  virtual const std::string Canonical_version ();
-  virtual package_stability_t Stability ()
-  {
-    return stability;
-  }
-  virtual void SetStability (package_stability_t newstability)
-  {
-    stability = newstability;
-  }
-  virtual package_type_t Type ()
-  {
-    return type;
-  };
-  virtual void set_sdesc (const std::string& );
-  virtual void set_ldesc (const std::string& );
-  virtual const std::string SDesc ()
-  {
-    return sdesc;
-  };
-  virtual const std::string LDesc ()
-  {
-    return ldesc;
-  };
-
-  /* pass the name of the package when constructing */
-  void setCanonicalVersion (const std::string& );
-
-  virtual ~ cygpackage ();
-
-  /* pass the name of the package when constructing */
-  static packageversion createInstance (const std::string& pkgname,
-                                        const package_type_t type);
-
-  static packageversion createInstance (const std::string& pkgname,
-                                        const std::string& version,
- package_type_t const);
-
-private:
-  cygpackage ();
-  std::string name;
-  std::string vendor;
-  std::string packagev;
-  std::string canonical;
-  std::string sdesc, ldesc;
-
-  package_stability_t stability;
-  package_type_t type;
-};
-
-#endif /* SETUP_CYGPACKAGE_H */
diff --git a/package_db.cc b/package_db.cc
index c561089..2d6d22c 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -33,7 +33,6 @@
 #include "filemanip.h"
 
 #include "package_version.h"
-#include "cygpackage.h"
 #include "package_db.h"
 #include "package_meta.h"
 #include "Exception.h"
diff --git a/package_meta.cc b/package_meta.cc
index c19eddf..3d1d666 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -39,7 +39,6 @@ using namespace std;
 #include "script.h"
 
 #include "package_version.h"
-#include "cygpackage.h"
 #include "package_db.h"
 
 #include <algorithm>
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 10/14] Remove packageversion class

Jon TURNEY
In reply to this post by Jon TURNEY
Remove packageversion, _packageversion, defaultversion classes
---
 Makefile.am        |   2 -
 PickPackageLine.cc |   1 -
 PickView.cc        |   1 -
 choose.cc          |   1 -
 desktop.cc         |   1 -
 download.cc        |   1 -
 package_db.cc      |   2 -
 package_meta.cc    |   3 -
 package_version.cc | 334 -----------------------------------------------------
 package_version.h  | 168 ---------------------------
 10 files changed, 514 deletions(-)
 delete mode 100644 package_version.cc
 delete mode 100644 package_version.h

diff --git a/Makefile.am b/Makefile.am
index 27bb8f1..7ea9d89 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -208,8 +208,6 @@ inilint_SOURCES = \
  package_meta.h \
  package_source.cc \
  package_source.h \
- package_version.cc \
- package_version.h \
  PackageSpecification.cc \
  PackageSpecification.h \
  PackageTrust.h \
diff --git a/PickPackageLine.cc b/PickPackageLine.cc
index 95c1557..6f44192 100644
--- a/PickPackageLine.cc
+++ b/PickPackageLine.cc
@@ -16,7 +16,6 @@
 #include "PickPackageLine.h"
 #include "PickView.h"
 #include "package_db.h"
-#include "package_version.h"
 
 void
 PickPackageLine::paint (HDC hdc, HRGN unused, int x, int y, int col_num, int show_cat)
diff --git a/PickView.cc b/PickView.cc
index 4c728f8..5bc7504 100644
--- a/PickView.cc
+++ b/PickView.cc
@@ -21,7 +21,6 @@
 #include "PickPackageLine.h"
 #include "PickCategoryLine.h"
 #include "package_db.h"
-#include "package_version.h"
 #include "dialog.h"
 #include "resource.h"
 /* For 'source' */
diff --git a/choose.cc b/choose.cc
index 1bc4c0b..abfc14a 100644
--- a/choose.cc
+++ b/choose.cc
@@ -48,7 +48,6 @@
 
 #include "package_db.h"
 #include "package_meta.h"
-#include "package_version.h"
 
 #include "threebar.h"
 #include "Generic.h"
diff --git a/desktop.cc b/desktop.cc
index 24908f8..927c02f 100644
--- a/desktop.cc
+++ b/desktop.cc
@@ -35,7 +35,6 @@
 #include "mklink2.h"
 #include "package_db.h"
 #include "package_meta.h"
-#include "package_version.h"
 #include "filemanip.h"
 #include "io_stream.h"
 #include "getopt++/BoolOption.h"
diff --git a/download.cc b/download.cc
index a2237a7..fda175f 100644
--- a/download.cc
+++ b/download.cc
@@ -38,7 +38,6 @@
 
 #include "package_db.h"
 #include "package_meta.h"
-#include "package_version.h"
 #include "package_source.h"
 
 #include "threebar.h"
diff --git a/package_db.cc b/package_db.cc
index 2d6d22c..ae71900 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -31,8 +31,6 @@
 #include "compress.h"
 
 #include "filemanip.h"
-
-#include "package_version.h"
 #include "package_db.h"
 #include "package_meta.h"
 #include "Exception.h"
diff --git a/package_meta.cc b/package_meta.cc
index 3d1d666..ab358b9 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -35,10 +35,7 @@ using namespace std;
 /* this goes at the same time */
 #include "win32.h"
 
-
 #include "script.h"
-
-#include "package_version.h"
 #include "package_db.h"
 
 #include <algorithm>
diff --git a/package_version.cc b/package_version.cc
deleted file mode 100644
index 2d4416e..0000000
--- a/package_version.cc
+++ /dev/null
@@ -1,334 +0,0 @@
-/*
- * Copyright (c) 2001, 2003 Robert Collins.
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     A copy of the GNU General Public License can be found at
- *     http://www.gnu.org/
- *
- * Written by Robert Collins  <[hidden email]>
- *
- */
-
-/* this is the parent class for all package operations.
- */
-
-#include "package_version.h"
-#include "package_db.h"
-#include "package_meta.h"
-#include "LogSingleton.h"
-#include "state.h"
-#include "resource.h"
-#include <algorithm>
-#include "csu_util/version_compare.h"
-
-using namespace std;
-
-/* a default class to avoid special casing empty packageversions */
-  
-/* TODO place into the class header */
-class _defaultversion : public _packageversion
-{
-public:
-  _defaultversion()
-    {
-      // never try to free me!
-      ++references;
-    }
-  const std::string Name(){return std::string();}
-  const std::string Vendor_version() {return std::string();}
-  const std::string Package_version() {return std::string();}
-  const std::string Canonical_version() {return std::string();}
-  void setCanonicalVersion (const std::string& ) {}
-  package_stability_t Stability (){return TRUST_UNKNOWN;}
-  void SetStability (package_stability_t) {}
-  package_type_t Type () {return package_binary;}
-  const std::string SDesc () {return std::string();}
-  void set_sdesc (const std::string& ) {}
-  const std::string LDesc () {return std::string();}
-  void set_ldesc (const std::string& ) {}
-};
-static _defaultversion defaultversion;
-
-/* the wrapper class */
-packageversion::packageversion() : data (&defaultversion)
-{
-  ++data->references;
-}
-
-/* Create from an actual package */
-packageversion::packageversion (_packageversion *pkg)
-{
-  if (pkg)
-    data = pkg;
-  else
-    data = &defaultversion;
-  ++data->references;
-}
-
-packageversion::packageversion (packageversion const &existing) :
-data(existing.data)
-{
-  ++data->references;
-}
-
-packageversion::~packageversion()
-{
-  if (--data->references == 0)
-    delete data;
-}
-
-packageversion &
-packageversion::operator= (packageversion const &rhs)
-{
-  ++rhs.data->references;
-  if (--data->references == 0)
-    delete data;
-  data = rhs.data;
-  return *this;
-}
-
-bool
-packageversion::operator ! () const
-{
-  return !data->Name().size();
-}
-
-packageversion::operator bool () const
-{
-  return data->Name().size();
-}
-
-bool
-packageversion::operator == (packageversion const &rhs) const
-{
-  if (this == &rhs || data == rhs.data)
-    return true;
-  else
-    return data->Name () == rhs.data->Name() && data->Canonical_version () == rhs.data->Canonical_version();
-}
-
-bool
-packageversion::operator != (packageversion const &rhs) const
-{
-  return ! (*this == rhs);
-}
-
-bool
-packageversion::operator < (packageversion const &rhs) const
-{
-  int t = casecompare(data->Name(), rhs.data->Name());
-  if (t < 0)
-    return true;
-  else if (t > 0)
-    return false;
-  else if (casecompare (data->Canonical_version(), rhs.data->Canonical_version()) < 0)
-    return true;
-  return false;
-}
-
-const std::string
-packageversion::Name () const
-{
-  return data->Name ();
-}
-
-const std::string
-packageversion::Vendor_version() const
-{
-  return data->Vendor_version();
-}
-
-const std::string
-packageversion::Package_version() const
-{
-  return data->Package_version();
-}
-
-const std::string
-packageversion::Canonical_version() const
-{
-  return data->Canonical_version();
-}
-
-void
-packageversion::setCanonicalVersion (const std::string& ver)
-{
-  data->setCanonicalVersion (ver);
-}
-
-package_stability_t
-packageversion::Stability () const
-{
-  return data->Stability ();
-}
-
-void
-packageversion::SetStability (package_stability_t stability)
-{
-  data->SetStability (stability);
-}
-
-package_type_t
-packageversion::Type () const
-{
-  return data->Type ();
-}
-
-const std::string
-packageversion::SDesc () const
-{
-  return data->SDesc ();
-}
-
-void
-packageversion::set_sdesc (const std::string& sdesc)
-{
-  data->set_sdesc (sdesc);
-}
-
-const std::string
-packageversion::LDesc () const
-{
-  return data->LDesc ();
-}
-
-void
-packageversion::set_ldesc (const std::string& ldesc)
-{
-  data->set_ldesc (ldesc);
-}
-
-packageversion
-packageversion::sourcePackage() const
-{
-  return data->sourcePackage();
-}
-
-PackageSpecification &
-packageversion::sourcePackageSpecification () const
-{
-  return data->sourcePackageSpecification ();
-}
-
-void
-packageversion::setSourcePackageSpecification (PackageSpecification const &spec)
-{
-  data->setSourcePackageSpecification(spec);
-}
-
-void
-packageversion::setDepends(const PackageDepends _depends)
-{
-  data->depends = _depends;
-}
-
-const PackageDepends
-packageversion::depends() const
-{
-  return data->depends;
-}
-
-packagesource *
-packageversion::source () const
-{
-  return &data->source;
-}
-
-bool
-packageversion::accessible() const
-{
-  return data->accessible();
-}
-
-
-int
-packageversion::compareVersions(const packageversion &a, const packageversion &b)
-{
-  /* Compare Vendor_version */
-  int comparison = version_compare(a.Vendor_version(), b.Vendor_version());
-
-#if DEBUG
-  Log (LOG_BABBLE) << "vendor version comparison " << a.Vendor_version() << " and " << b.Vendor_version() << ", result was " << comparison << endLog;
-#endif
-
-  if (comparison != 0)
-    {
-      return comparison;
-    }
-
-  /* Vendor_version are tied, compare Package_version */
-#if DEBUG
-  Log (LOG_BABBLE) <<  "package version comparison " << a.Package_version() << " and " << b.Package_version() << ", result was " << comparison << endLog;
-#endif
-
-  comparison = version_compare(a.Package_version(), b.Package_version());
-  return comparison;
-}
-
-/* the parent data class */
-  
-_packageversion::_packageversion ():picked (false), references (0)
-{
-}
-
-_packageversion::~_packageversion ()
-{
-}
-
-PackageSpecification &
-_packageversion::sourcePackageSpecification ()
-{
-  return _sourcePackage;
-}
-
-void
-_packageversion::setSourcePackageSpecification (PackageSpecification const &spec)
-{
-  _sourcePackage = spec;
-}
-
-packageversion
-_packageversion::sourcePackage ()
-{
-  if (!sourceVersion)
-    {
-      packagedb db;
-      packagemeta * pkg;
-      pkg = db.findSource (_sourcePackage);
-      /* no valid source meta available, just return the default
- (blank) package version
- */
-      if (!pkg)
- return sourceVersion;
-      set<packageversion>::iterator i=pkg->versions.begin();
-      while (i != pkg->versions.end())
- {
-  packageversion const & ver = * i;
-          if (_sourcePackage.satisfies (ver))
-    sourceVersion = ver;
-          ++i;
- }
-    }
-  return sourceVersion;
-}
-
-// is archive accessible
-bool
-_packageversion::accessible() const
-{
-  // cached ?
-  if (source.Cached ())
-    return true;
-  // net access allowed?
-  if (::source == IDC_SOURCE_LOCALDIR)
-    return false;
-  // retrievable ?
-  if (source.sites.size() || source.Cached ())
-    return true;
-  // otherwise, not accessible
-  return false;
-}
diff --git a/package_version.h b/package_version.h
deleted file mode 100644
index 21d053e..0000000
--- a/package_version.h
+++ /dev/null
@@ -1,168 +0,0 @@
-/*
- * Copyright (c) 2001, 2003 Robert Collins.
- *
- *     This program is free software; you can redistribute it and/or modify
- *     it under the terms of the GNU General Public License as published by
- *     the Free Software Foundation; either version 2 of the License, or
- *     (at your option) any later version.
- *
- *     A copy of the GNU General Public License can be found at
- *     http://www.gnu.org/
- *
- * Written by Robert Collins  <[hidden email]>
- *
- */
-
-#ifndef SETUP_PACKAGE_VERSION_H
-#define SETUP_PACKAGE_VERSION_H
-
-/* This is a package version abstrct class, that should be able to
- * arbitrate acceess to cygwin binary packages, cygwin source package,
- * and the rpm and deb equivalents of the same.
- */
-
-/* standard binary package metadata:
- * Name (ie mutt
- * Vendor Version (ie 2.5.1)
- * Package Version (ie 16)
- * Stability
- * Files
- */
-
-/* For non installed files, this class can be populated via information about
- * what is available on the net, or by parsing a specific package file.
- * for installed packages, this class should represent what is currently installed,
- * - updated by what net metadata has about it.
- * i.e. the stability of this version will change simply because the net mirrors
- * now consider it old.
- */
-
-class CategoryList;
-
-/*Required for parsing */
-#include "package_source.h"
-#include "PackageSpecification.h"
-#include "PackageTrust.h"
-#include "package_depends.h"
-
-typedef trusts package_stability_t;
-
-typedef enum
-{
-  package_binary,
-  package_source
-}
-package_type_t;
-
-/* A wrapper class to be copied by value that
-   references the same package.
-   Nothing is virtual, because the wrapper cannot be inherited.
-   However, as all the methods are implemented in the referenced
-   _packageversion, that class allows virtual overriding.
-   */
-
-class _packageversion;
-class packagemeta;
-
-/* This class has pointer semantics
-   Specifically: a=b does not alter the value of *a.
-   */
-class packageversion
-{
-public:
-  packageversion (); /* creates an empty packageversion */
-  packageversion (_packageversion *); /* used when creating an instance */
-  packageversion (packageversion const &);
-  ~packageversion ();
-  packageversion &operator= (packageversion const &);
-  bool operator ! () const; /* true if the package is invalid. (i.e.
-       uninitialised */
-  operator bool () const; /* returns ! !() */
-  bool operator == (packageversion const &) const; /* equality */
-  bool operator != (packageversion const &) const;
-  bool operator < (packageversion const &) const;
-  bool operator <= (packageversion const &) const;
-  bool operator > (packageversion const &) const;
-  bool operator >= (packageversion const &) const;
-
-  const std::string Name () const;
-  const std::string Vendor_version () const;
-  const std::string Package_version () const;
-  const std::string Canonical_version () const;
-  void setCanonicalVersion (const std::string& );
-  package_stability_t Stability () const;
-  void SetStability (package_stability_t);
-  package_type_t Type () const;
-  const std::string SDesc () const;
-  void set_sdesc (const std::string& );
-  const std::string LDesc () const;
-  void set_ldesc (const std::string& );
-  packageversion sourcePackage () const;
-  PackageSpecification & sourcePackageSpecification () const;
-  void setSourcePackageSpecification (PackageSpecification const &);
-
-  void setDepends(const PackageDepends);
-  const PackageDepends depends() const;
-
-  /* invariant: never null */
-  packagesource *source() const; /* where can we source the file from */
-
-  bool accessible () const;
-
-  /* ensure that the depends clause is satisfied */
-  int set_requirements (trusts deftrust, size_t depth = 0);
-
-  /* utility function to compare package versions */
-  static int compareVersions(const packageversion &a, const packageversion &b);
-
-private:
-  _packageversion *data; /* Invariant: * data is always valid */
-};
-
-class _packageversion
-{
-public:
-  _packageversion();
-  virtual ~_packageversion();
-  /* for list inserts/mgmt. */
-  std::string key;
-  /* name is needed here, because if we are querying a file, the data may be embedded in
-     the file */
-  virtual const std::string Name () = 0;
-  virtual const std::string Vendor_version () = 0;
-  virtual const std::string Package_version () = 0;
-  virtual const std::string Canonical_version () = 0;
-  virtual void setCanonicalVersion (const std::string& ) = 0;
-  virtual package_stability_t Stability () = 0;
-  virtual void SetStability (package_stability_t) = 0;
-  virtual package_type_t Type () = 0;
-  virtual const std::string SDesc () = 0;
-  virtual void set_sdesc (const std::string& ) = 0;
-  virtual const std::string LDesc () = 0;
-  virtual void set_ldesc (const std::string& ) = 0;
-  /* only semantically meaningful for binary packages */
-  /* direct link to the source package for this binary */
-  /* if multiple versions exist and the source doesn't discriminate
-     then the most recent is used
-     */
-  virtual packageversion sourcePackage ();
-  virtual PackageSpecification & sourcePackageSpecification ();
-  virtual void setSourcePackageSpecification (PackageSpecification const &);
-
-  PackageDepends depends;
-
-  packagesource source; /* where can we source the file from */
-
-  virtual bool accessible () const;
-
-  /* TODO: Implement me:
-     static package_meta * scan_package (io_stream *);
-   */
-  size_t references;
-protected:
-  /* only meaningful for binary packages */
-  PackageSpecification _sourcePackage;
-  packageversion sourceVersion;
-};
-
-#endif /* SETUP_PACKAGE_VERSION_H */
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 11/14] Drop in SolvableVersion as a replacement for packageversion

Jon TURNEY
In reply to this post by Jon TURNEY
---
 Makefile.am            |  1 +
 PackageSpecification.h |  4 +++-
 package_db.cc          |  1 +
 package_meta.h         |  3 ++-
 package_version.h      | 21 +++++++++++++++++++++
 5 files changed, 28 insertions(+), 2 deletions(-)
 create mode 100644 package_version.h

diff --git a/Makefile.am b/Makefile.am
index 7ea9d89..7fe4b2f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -208,6 +208,7 @@ inilint_SOURCES = \
  package_meta.h \
  package_source.cc \
  package_source.h \
+ package_version.h \
  PackageSpecification.cc \
  PackageSpecification.h \
  PackageTrust.h \
diff --git a/PackageSpecification.h b/PackageSpecification.h
index ef992fa..0706ec8 100644
--- a/PackageSpecification.h
+++ b/PackageSpecification.h
@@ -18,7 +18,9 @@
 
 #include <iosfwd>
 #include "String++.h"
-class packageversion;
+
+class SolvableVersion;
+typedef SolvableVersion packageversion;
 
 /* Describe a package - i.e. we need version 5 of apt */
 
diff --git a/package_db.cc b/package_db.cc
index ae71900..123e4f3 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -31,6 +31,7 @@
 #include "compress.h"
 
 #include "filemanip.h"
+#include "package_version.h"
 #include "package_db.h"
 #include "package_meta.h"
 #include "Exception.h"
diff --git a/package_meta.h b/package_meta.h
index 421646a..b6faab8 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -16,7 +16,8 @@
 #ifndef SETUP_PACKAGE_META_H
 #define SETUP_PACKAGE_META_H
 
-class packageversion;
+class SolvableVersion;
+typedef SolvableVersion packageversion;
 class packagemeta;
 
 #include <set>
diff --git a/package_version.h b/package_version.h
new file mode 100644
index 0000000..43cf146
--- /dev/null
+++ b/package_version.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2017 Jon Turney
+ *
+ *     This program is free software; you can redistribute it and/or modify
+ *     it under the terms of the GNU General Public License as published by
+ *     the Free Software Foundation; either version 2 of the License, or
+ *     (at your option) any later version.
+ *
+ *     A copy of the GNU General Public License can be found at
+ *     http://www.gnu.org/
+ *
+ */
+
+#ifndef PACKAGE_VERSION_H
+#define PACKAGE_VERSION_H
+
+#include "libsolv.h"
+
+typedef SolvableVersion packageversion;
+
+#endif // PACKAGE_VERSION_H
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 12/14] Use solver to check for problems and produce a list of package transactions

Jon TURNEY
Convert chooser UI selections into a SolverTaskList

Apply SolverSolution to that task list (with choice of keep, upgrade,
upgrade with test, IncludeSource) to produce a vector of SolverTransactions.
Store a solution object in packagedb

The transaction list returned by the solver is postprocessed to add
reinstall and IncludeSource actions

Very crudely present solver problems in the PrereqChecker page UI, as text.
Change tickbox to say "accept default solutions" and don't allow to preceed
unless those solutions are accepted (ideally we would have a UI to choose
solutions).  Remove warning about missing dependencies.

Also pass initial trust state to PrereqChecker
---
 choose.cc     |   7 +-
 libsolv.cc    | 244 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 libsolv.h     |  84 ++++++++++++++++++++
 package_db.cc |   1 +
 package_db.h  |   1 +
 prereq.cc     | 207 +++++++++++--------------------------------------
 prereq.h      |  22 ++----
 res.rc        |   4 +-
 8 files changed, 393 insertions(+), 177 deletions(-)

diff --git a/choose.cc b/choose.cc
index abfc14a..db8f561 100644
--- a/choose.cc
+++ b/choose.cc
@@ -153,6 +153,8 @@ ChooserPage::createListview ()
   /* FIXME: do we need to init the desired fields ? */
   static int ta[] = { IDC_CHOOSE_KEEP, IDC_CHOOSE_CURR, IDC_CHOOSE_EXP, 0 };
   rbset (GetHWND (), ta, IDC_CHOOSE_CURR);
+  changeTrust (TRUST_CURR);
+
   ClearBusy ();
 }
 
@@ -364,6 +366,8 @@ ChooserPage::keepClicked()
       pkg.desired = pkg.installed;
     }
   chooser->refresh();
+
+  PrereqChecker::setUpgrade(false);
 }
 
 void
@@ -372,7 +376,8 @@ ChooserPage::changeTrust(trusts aTrust)
   SetBusy ();
   chooser->defaultTrust (aTrust);
   chooser->refresh();
-  PrereqChecker::setTrust (aTrust);
+  PrereqChecker::setUpgrade(true);
+  PrereqChecker::setTestPackages(aTrust == TRUST_TEST);
   ClearBusy ();
 }
 
diff --git a/libsolv.cc b/libsolv.cc
index 67c68b5..df0af3d 100644
--- a/libsolv.cc
+++ b/libsolv.cc
@@ -18,6 +18,7 @@
 #include "solv/evr.h"
 
 #include "LogSingleton.h"
+#include  <iomanip>
 
 // ---------------------------------------------------------------------------
 // Utility functions for mapping between Operators and Relation Ids
@@ -441,3 +442,246 @@ SolverPool::internalize()
       repodata_internalize(i->second->data);
     }
 }
+
+
+void
+SolverPool::use_test_packages(bool use_test_packages)
+{
+  // Only enable repos containing test packages if wanted
+  for (RepoList::iterator i = repos.begin();
+       i != repos.end();
+       i++)
+    {
+      if (i->second->test)
+        {
+          i->second->repo->disabled = !use_test_packages;
+        }
+    }
+}
+
+// ---------------------------------------------------------------------------
+// implements class SolverSolution
+//
+// A wrapper around the libsolv solver
+// ---------------------------------------------------------------------------
+
+SolverSolution::~SolverSolution()
+{
+  if (solv)
+    {
+      solver_free(solv);
+      solv = NULL;
+    }
+}
+
+static
+std::ostream &operator<<(std::ostream &stream,
+                         SolverTransaction::transType type)
+{
+  switch (type)
+    {
+    case SolverTransaction::transInstall:
+      stream << "install";
+      break;
+    case SolverTransaction::transErase:
+      stream << "erase";
+      break;
+    default:
+      stream << "unknown";
+    }
+  return stream;
+}
+
+bool
+SolverSolution::update(SolverTasks &tasks, bool update, bool use_test_packages, bool include_source)
+{
+  Log (LOG_PLAIN) << "solving: " << tasks.tasks.size() << " tasks," <<
+    " update: " << (update ? "yes" : "no") << "," <<
+    " use test packages: " << (use_test_packages ? "yes" : "no") << "," <<
+    " include_source: " << (include_source ? "yes" : "no") << endLog;
+
+  pool.use_test_packages(use_test_packages);
+
+  Queue job;
+  queue_init(&job);
+  // solver accepts a queue containing pairs of (cmd, id) tasks
+  // cmd is job and selection flags ORed together
+  for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
+       i != tasks.tasks.end();
+       i++)
+    {
+      const SolvableVersion &sv = (*i).first;
+
+      switch ((*i).second)
+        {
+        case SolverTasks::taskInstall:
+          queue_push2(&job, SOLVER_INSTALL | SOLVER_SOLVABLE, sv.id);
+          break;
+        case SolverTasks::taskUninstall:
+          queue_push2(&job, SOLVER_ERASE | SOLVER_SOLVABLE, sv.id);
+          break;
+        case SolverTasks::taskReinstall:
+          // we don't know how to ask solver for this, so we just add the erase
+          // and install later
+          break;
+        default:
+          Log (LOG_PLAIN) << "unknown task " << (*i).second << endLog;
+        }
+    }
+
+  if (update)
+    queue_push2(&job, SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, 0);
+
+  if (!solv)
+    solv = solver_create(pool.pool);
+
+  solver_set_flag(solv, SOLVER_FLAG_ALLOW_VENDORCHANGE, 1);
+  solver_set_flag(solv, SOLVER_FLAG_ALLOW_DOWNGRADE, 0);
+  solver_solve(solv, &job);
+  queue_free(&job);
+
+  int pcnt = solver_problem_count(solv);
+  solver_printdecisions(solv);
+
+  // get transactions for solution
+  Transaction *t = solver_create_transaction(solv);
+  transaction_order(t, 0);
+  transaction_print(t);
+
+  // massage into SolverTransactions form
+  trans.clear();
+
+  for (int i = 0; i < t->steps.count; i++)
+    {
+      Id id = t->steps.elements[i];
+      SolverTransaction::transType tt = type(t, i);
+      trans.push_back(SolverTransaction(SolvableVersion(id, pool.pool), tt));
+    }
+
+  // add install and remove tasks for anything marked as reinstall
+  for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
+       i != tasks.tasks.end();
+       i++)
+    {
+      const SolvableVersion &sv = (*i).first;
+
+      if (((*i).second) == SolverTasks::taskReinstall)
+        {
+          trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool),
+                                            SolverTransaction::transErase));
+          trans.push_back(SolverTransaction(SolvableVersion(sv.id, pool.pool),
+                                            SolverTransaction::transInstall));
+        }
+    }
+
+  // if include_source mode is on, also install source for everything we are
+  // installing
+  if (include_source)
+    {
+      // (this uses indicies into the vector, as iterators might become
+      // invalidated by doing push_back)
+      size_t n = trans.size();
+      for (size_t i = 0; i < n; i++)
+        {
+          if (trans[i].type == SolverTransaction::transInstall)
+            {
+              SolvableVersion src_version = trans[i].version.sourcePackage();
+              if (src_version)
+                trans.push_back(SolverTransaction(src_version,
+                                                  SolverTransaction::transInstall));
+            }
+        }
+    }
+
+  if (trans.size())
+    {
+      Log (LOG_PLAIN) << "Augmented Transaction List:" << endLog;
+      for (SolverTransactionList::iterator i = trans.begin ();
+           i != trans.end ();
+           ++i)
+        {
+          Log (LOG_PLAIN) << std::setw(4) << std::distance(trans.begin(), i)
+                          << std::setw(8) << i->type
+                          << std::setw(48) << i->version.Name()
+                          << std::setw(20) << i->version.Canonical_version() << endLog;
+        }
+    }
+
+  transaction_free(t);
+
+  return (pcnt == 0);
+}
+
+const SolverTransactionList &
+SolverSolution::transactions() const
+{
+  return trans;
+}
+
+// Construct a string reporting the problems and solutions
+std::string
+SolverSolution::report() const
+{
+  std::string r = "";
+  int pcnt = solver_problem_count(solv);
+  for (Id problem = 1; problem <= pcnt; problem++)
+    {
+      r += "Problem " + std::to_string(problem) + "/" + std::to_string(pcnt);
+      r += "\n";
+
+      Id probr = solver_findproblemrule(solv, problem);
+      Id dep, source, target;
+      SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep);
+      r += solver_problemruleinfo2str(solv, type, source, target, dep);
+      r += "\n";
+
+      int scnt = solver_solution_count(solv, problem);
+      for (Id solution = 1; solution <= scnt; solution++)
+        {
+          r += "Solution " + std::to_string(solution) + "/" + std::to_string(scnt);
+          r += "\n";
+
+          Id p, rp, element;
+          element = 0;
+          while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0)
+            {
+              r += "  - ";
+              r += solver_solutionelement2str(solv, p, rp);
+              r += "\n";
+            }
+        }
+      r += "\n";
+    }
+  return r;
+}
+
+// helper function to map transaction type
+SolverTransaction::transType
+SolverSolution::type(Transaction *trans, int pos)
+{
+  Id tt = transaction_type(trans, trans->steps.elements[pos],
+                           SOLVER_TRANSACTION_SHOW_ACTIVE);
+
+  // if active side of transaction is nothing, ask again for passive side of
+  // transaction
+  if (tt == SOLVER_TRANSACTION_IGNORE)
+    tt = transaction_type(trans, trans->steps.elements[pos], 0);
+
+  switch(tt)
+    {
+    case SOLVER_TRANSACTION_INSTALL:
+    case SOLVER_TRANSACTION_REINSTALL:
+    case SOLVER_TRANSACTION_UPGRADE:
+    case SOLVER_TRANSACTION_DOWNGRADE:
+      return SolverTransaction::transInstall;
+    case SOLVER_TRANSACTION_ERASE:
+    case SOLVER_TRANSACTION_REINSTALLED:
+    case SOLVER_TRANSACTION_UPGRADED:
+    case SOLVER_TRANSACTION_DOWNGRADED:
+      return SolverTransaction::transErase;
+    default:
+      Log (LOG_PLAIN) << "unknown transaction type " << std::hex << tt << endLog;
+    case SOLVER_TRANSACTION_IGNORE:
+      return SolverTransaction::transIgnore;
+    }
+};
diff --git a/libsolv.h b/libsolv.h
index 490da78..43b37d2 100644
--- a/libsolv.h
+++ b/libsolv.h
@@ -16,6 +16,7 @@
 
 #include "solv/pool.h"
 #include "solv/repo.h"
+#include "solv/solver.h"
 #include "PackageSpecification.h"
 #include "PackageTrust.h"
 #include "package_source.h"
@@ -39,6 +40,7 @@ package_type_t;
 // ---------------------------------------------------------------------------
 
 class SolverPool;
+class SolverSolution;
 
 class SolvableVersion
 {
@@ -85,6 +87,7 @@ class SolvableVersion
   Pool *pool;
 
   friend SolverPool;
+  friend SolverSolution;
 };
 
 // ---------------------------------------------------------------------------
@@ -133,6 +136,8 @@ public:
                              const addPackageData &pkgdata);
 
   void internalize(void);
+  void use_test_packages(bool use_test_packages);
+
 
 private:
   Id makedeps(Repo *repo, PackageDepends *requires);
@@ -140,7 +145,86 @@ private:
 
   typedef std::map<std::string, SolvRepo *> RepoList;
   RepoList repos;
+
+  friend SolverSolution;
 };
 
 
+// ---------------------------------------------------------------------------
+// interface to class SolverTaskQueue
+//
+// This is used to contain a set of package install/uninstall tasks selected via
+// the UI to be passed to solver
+// ---------------------------------------------------------------------------
+
+class SolverTasks
+{
+ public:
+  enum task
+  {
+    taskInstall,
+    taskUninstall,
+    taskReinstall
+  };
+  void add(const SolvableVersion &v, task t)
+  {
+    tasks.push_back(taskList::value_type(v, t));
+  };
+ private:
+  typedef std::vector<std::pair<const SolvableVersion &, task>> taskList;
+  taskList tasks;
+
+  friend SolverSolution;
+};
+
+// ---------------------------------------------------------------------------
+// SolverTransactionList
+//
+// a list of transactions output by the solver
+// ---------------------------------------------------------------------------
+
+class SolverTransaction
+{
+ public:
+  typedef enum
+  {
+    transIgnore,
+    transInstall,
+    transErase,
+  } transType;
+
+  SolverTransaction(SolvableVersion version_, transType type_) :
+  version(version_), type(type_) {};
+
+  SolvableVersion version;
+  transType type;
+};
+
+typedef std::vector<SolverTransaction> SolverTransactionList;
+
+// ---------------------------------------------------------------------------
+// interface to class SolverSolution
+//
+// A wrapper around the libsolv solver
+// ---------------------------------------------------------------------------
+
+class SolverSolution
+{
+ public:
+  SolverSolution(SolverPool &_pool) : pool(_pool), solv(NULL) {};
+  ~SolverSolution();
+
+  bool update(SolverTasks &tasks, bool update, bool use_test_packages, bool include_source);
+  std::string report() const;
+
+  const SolverTransactionList &transactions() const;
+
+ private:
+  static SolverTransaction::transType type(Transaction *trans, int pos);
+
+  SolverPool &pool;
+  Solver *solv;
+  SolverTransactionList trans;
+};
+
 #endif // LIBSOLV_H
diff --git a/package_db.cc b/package_db.cc
index 123e4f3..f12afda 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -290,6 +290,7 @@ packagedb::packagecollection packagedb::sourcePackages;
 PackageDBActions packagedb::task = PackageDB_Install;
 std::vector <packagemeta *> packagedb::dependencyOrderedPackages;
 SolverPool packagedb::solver;
+SolverSolution packagedb::solution(packagedb::solver);
 
 #include <stack>
 
diff --git a/package_db.h b/package_db.h
index 022358e..d49f738 100644
--- a/package_db.h
+++ b/package_db.h
@@ -88,6 +88,7 @@ public:
   static PackageDBActions task;
 
   static SolverPool solver;
+  static SolverSolution solution;
 
 private:
   static int installeddbread; /* do we have to reread this */
diff --git a/prereq.cc b/prereq.cc
index 16af7fa..49fbd77 100644
--- a/prereq.cc
+++ b/prereq.cc
@@ -34,6 +34,7 @@
 #include "package_meta.h"
 #include "msg.h"
 #include "Exception.h"
+#include "getopt++/BoolOption.h"
 
 // Sizing information.
 static ControlAdjuster::ControlInfo PrereqControlsInfo[] = {
@@ -43,6 +44,7 @@ static ControlAdjuster::ControlInfo PrereqControlsInfo[] = {
 };
 
 extern ThreeBarProgressPage Progress;
+BoolOption IncludeSource (false, 'I', "include-source", "Automatically include source download");
 
 // ---------------------------------------------------------------------------
 // implements class PrereqPage
@@ -91,26 +93,7 @@ PrereqPage::OnNext ()
 
   if (!IsDlgButtonChecked (h, IDC_PREREQ_CHECK))
     {
-      // breakage imminent!  danger, danger
-      int res = MessageBox (h,
-          "The listed packages are required for packages depending on them to "
-          "work.  We strongly recommend that you allow Setup to select them."
-          "\r\n\r\n"
-          "Are you sure you want to proceed without these packages?",
-          "WARNING - Required Packages Not Selected",
-          MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2);
-      if (res == IDNO)
-        return -1;
-      else
-        Log (LOG_PLAIN) <<
-            "NOTE!  User refused suggested missing dependencies!  "
-            "Expect some packages to give errors or not function at all." << endLog;
-    }
-  else
-    {
-      // add the missing requirements
-      PrereqChecker p;
-      p.selectMissing ();
+      return -1;
     }
 
   return whatNext();
@@ -145,182 +128,86 @@ PrereqPage::OnUnattended ()
   if (unattended_mode == chooseronly)
     return -1;
 
-  // in unattended mode, add the missing requirements, then carry on to download/install
-  PrereqChecker p;
-  p.selectMissing ();
-
   return whatNext();
 }
 
+bool
+PrereqPage::OnMessageCmd (int id, HWND hwndctl, UINT code)
+{
+  if ((code == BN_CLICKED) && (id == IDC_PREREQ_CHECK))
+    {
+      GetOwner ()->SetButtons (PSWIZB_BACK | (IsButtonChecked (id) ? PSWIZB_NEXT : 0));
+      return true;
+    }
+
+  return false;
+}
+
 // ---------------------------------------------------------------------------
 // implements class PrereqChecker
 // ---------------------------------------------------------------------------
 
 // instantiate the static members
-map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> PrereqChecker::unmet;
-map <std::string, vector <packagemeta *> > PrereqChecker::notfound;
-trusts PrereqChecker::theTrust = TRUST_CURR;
+bool PrereqChecker::upgrade;
+bool PrereqChecker::use_test_packages;
 
-/* This function builds a list of unmet dependencies to present to the user on
-   the PrereqPage propsheet.
-
-   The data is stored in two associative maps:
-     unmet[package] = vector of packages that depend on package.
-     notfound[package-name] = vector of packages that depend on package.
-*/
 bool
 PrereqChecker::isMet ()
 {
   packagedb db;
 
-  Progress.SetText1 ("Checking prerequisites...");
+  Progress.SetText1 ("Solving dependencies...");
   Progress.SetText2 ("");
   Progress.SetText3 ("");
 
-  // clear static data each time this is called
-  unmet.clear ();
-  notfound.clear ();
-
-  // packages that need to be checked for dependencies
-  queue <packagemeta *> todo;
+  // go through all packages, adding changed ones to the solver task list
+  SolverTasks q;
 
-  // go through all packages, adding desired ones to the initial work list
   for (packagedb::packagecollection::iterator p = db.packages.begin ();
         p != db.packages.end (); ++p)
     {
-      if (p->second->desired)
-        todo.push (p->second);
-    }
-
-  int max = todo.size();
-  int pos = 0;
+      packagemeta *pkg = p->second;
 
-  // churn through the work list
-  while (!todo.empty ())
-    {
-      // get the first package off the work list
-      packagemeta *pack = todo.front ();
-      todo.pop ();
-
-      pos++;
-      Progress.SetText2 (pack->name.c_str());
-      static char buf[100];
-      sprintf(buf, "%d %%  (%d/%d)", pos * 100 / max, pos, max);
-      Progress.SetText3(buf);
-      Progress.SetBar1(pos, max);
-
-      // Fetch the dependencies of the package. This assumes that the
-      // dependencies of all versions are all the same.
-      const PackageDepends deps = pack->curr.depends ();
-
-      // go through the package's dependencies
-      for (PackageDepends::const_iterator d =
-            deps.begin (); d != deps.end (); ++d)
+      // decode UI state to action
+      // skip and keep don't change dependency solution
+      if (pkg->installed != pkg->desired)
         {
-          PackageSpecification *dep_spec = *d;
-          packagemeta *dep = db.findBinary (*dep_spec);
-
-          if (dep)
-            {
-              if (!(dep->desired && dep_spec->satisfies (dep->desired)))
-                {
-                  // we've got an unmet dependency
-                  if (unmet.find (dep) == unmet.end ())
-                    {
-                      // newly found dependency: add to worklist
-                      todo.push (dep);
-                      max++;
-                    }
-                  unmet[dep].push_back (pack);
-                }
-            }
+          if (pkg->desired)
+            q.add(pkg->desired, SolverTasks::taskInstall); // install/upgrade
           else
-            {
-              // dependency on a package which doesn't have any binary versions
-              // (i.e. it is source only or doesn't exist)
-              Log (LOG_PLAIN) << "package " << pack->name << " has dependency "
-                              << dep_spec->packageName() << " we can't find" << endLog;
-              notfound[dep_spec->packageName()].push_back (pack);
-            }
+            q.add(pkg->installed, SolverTasks::taskUninstall); // uninstall
         }
-    }
-
-  return unmet.empty () && notfound.empty ();
-}
-
-/* Formats 'unmet' as a string for display to the user.  */
-void
-PrereqChecker::getUnmetString (std::string &s)
-{
-  s = "";
-
-  {
-    map <std::string, vector <packagemeta *> >::iterator i;
-    for (i = notfound.begin(); i != notfound.end(); i++)
-      {
-        s = s + i->first
-          + "\t(not found)"
-          + "\r\n\tRequired by: ";
-        for (unsigned int j = 0; j < i->second.size(); j++)
-          {
-            s += i->second[j]->name;
-            if (j != i->second.size() - 1)
-              s += ", ";
-          }
-        s += "\r\n\r\n";
-      }
-  }
+      else
+        if (pkg->picked())
+          q.add(pkg->installed, SolverTasks::taskReinstall); // reinstall
 
-  map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
-  for (i = unmet.begin(); i != unmet.end(); i++)
-    {
-      s = s + i->first->name
-    + "\t(" + i->first->trustp (false, theTrust).Canonical_version ()
-    + ")\r\n\t" + i->first->SDesc ()
-    + "\r\n\tRequired by: ";
-      for (unsigned int j = 0; j < i->second.size(); j++)
+      // only install action makes sense for source packages
+      if (pkg->srcpicked())
         {
-          s += i->second[j]->name;
-          if (j != i->second.size() - 1)
-            s += ", ";
+          if (pkg->desired)
+            q.add(pkg->desired.sourcePackage(), SolverTasks::taskInstall);
+          else
+            q.add(pkg->installed.sourcePackage(), SolverTasks::taskInstall);
         }
-      s += "\r\n\r\n";
     }
+
+  // apply solver to those tasks and the chooser global state (keep, curr, test)
+  return db.solution.update(q, upgrade, use_test_packages, IncludeSource);
 }
 
-/* Takes the keys of 'unmet' and selects them, using the current trust.  */
+/* Formats problems and solutions as a string for display to the user.  */
 void
-PrereqChecker::selectMissing ()
+PrereqChecker::getUnmetString (std::string &s)
 {
   packagedb db;
+  s = db.solution.report();
 
-  // provide a default, even though this should have been set for us
-  if (!theTrust)
-    theTrust = TRUST_CURR;
-
-  // get each of the keys of 'unmet'
-  map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp>::iterator i;
-  for (i = unmet.begin(); i != unmet.end(); i++)
+  //
+  size_t pos = 0;
+  while ((pos = s.find("\n", pos)) != std::string::npos)
     {
-      packagemeta *pkg = i->first;
-      packageversion vers = pkg->trustp (false, theTrust);
-      pkg->desired = vers;
-      pkg->srcpick (false);
-
-      if (vers == i->first->installed)
-        {
-          pkg->pick (false);
-          Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
-               ": Selecting already-installed version " <<
-               i->first->installed.Canonical_version () << "." << endLog;
-        }
-      else
-        {
-          pkg->pick (vers.accessible ());
-          Log (LOG_PLAIN) << "Adding required dependency " << i->first->name <<
-              ": Selecting version " << vers.Canonical_version () <<
-              " for installation." << endLog;
-        }
+      s.replace(pos, 1, "\r\n");
+      pos += 2;
     }
 }
 
diff --git a/prereq.h b/prereq.h
index 163af6e..d5530d6 100644
--- a/prereq.h
+++ b/prereq.h
@@ -27,6 +27,7 @@ public:
   virtual long OnNext ();
   virtual long OnBack ();
   virtual long OnUnattended ();
+  virtual bool OnMessageCmd (int id, HWND hwndctl, UINT code);
 private:
   long whatNext ();
 };
@@ -34,25 +35,18 @@ private:
 class PrereqChecker
 {
 public:
-  // checks all dependecies, populates 'unmet'
-  // returns true if unsatisfied dependencies exist
+  // returns true if dependency problems exist
   bool isMet ();
-  
+
   // formats 'unmet' as a string for display
   void getUnmetString (std::string &s);
-  
-  // selects/picks the needed packages that were missing
-  void selectMissing ();
-  
-  // notes the current trust (for use in selectMissing)
-  static void setTrust (trusts t) { theTrust = t; };
+
+  static void setUpgrade (bool u) { upgrade = u; };
+  static void setTestPackages (bool t) { use_test_packages = t; };
 
 private:
-  
-  // this is the actual hash_map that does all the work
-  static map <packagemeta *, vector <packagemeta *>, packagemeta_ltcomp> unmet;
-  static map <std::string, vector <packagemeta *> > notfound;
-  static trusts theTrust;
+  static bool upgrade;
+  static bool use_test_packages;
 };
 
 #endif /* SETUP_PREREQ_H */
diff --git a/res.rc b/res.rc
index 96dcf00..5e13b0c 100644
--- a/res.rc
+++ b/res.rc
@@ -376,9 +376,9 @@ BEGIN
     ICON            IDI_CYGWIN,IDC_HEADICON,SETUP_HEADICON_X,0,21,20
     LTEXT           "Resolving Dependencies",IDC_STATIC_HEADER_TITLE
                     ,7,0,258,8,NOT WS_GROUP
-    LTEXT           "The following packages are required to satisfy "
+    LTEXT           "The following problems occured trying to satisfy "
                     "dependencies.",IDC_STATIC,21,9,239,16,NOT WS_GROUP
-    CONTROL         "&Select required packages (RECOMMENDED)"
+    CONTROL         "&Accept default problem solutions"
                     ,IDC_PREREQ_CHECK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,
                     7,167,225,14
     EDITTEXT        IDC_PREREQ_EDIT,7,41,303,124,WS_VSCROLL | WS_HSCROLL |
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 13/14] Download/checksum/install/uninstall what transaction wants

Jon TURNEY
In reply to this post by Jon TURNEY
Some of this goes rather around the houses to avoid lots of churm: In lots
of cases, we're looking up packagemeta for a given packageversion just so we
can use the pacakgemeta to access the name, which we could do via
packageversion just as easily.

We do actually need packagmeta for a couple of things: To note the package
as installed/uninstalled, and to note postinstalls scripts.

If IncludeSource is on source packages installs will have been added to the
task list in post-processing, so we don't need to handle that specially
anymore.

Source packages to be installed are kept in a separate queue as they are
installed differently to binary packages (root is /usr/src, install isn't
recorded, etc.)
---
 download.cc |  71 ++++++++++++++----------------------------
 install.cc  | 102 ++++++++++++++++++++++++------------------------------------
 2 files changed, 64 insertions(+), 109 deletions(-)

diff --git a/download.cc b/download.cc
index fda175f..4b2fb6f 100644
--- a/download.cc
+++ b/download.cc
@@ -44,14 +44,10 @@
 
 #include "Exception.h"
 
-#include "getopt++/BoolOption.h"
-
 using namespace std;
 
 extern ThreeBarProgressPage Progress;
 
-BoolOption IncludeSource (false, 'I', "include-source", "Automatically include source download");
-
 static bool
 validateCachedPackage (const std::string& fullname, packagesource & pkgsource)
 {
@@ -202,59 +198,38 @@ do_download_thread (HINSTANCE h, HWND owner)
   Progress.SetText3 ("");
 
   packagedb db;
-  /* calculate the amount needed */
-  for (packagedb::packagecollection::iterator i = db.packages.begin ();
-       i != db.packages.end (); ++i)
+  const SolverTransactionList &t = db.solution.transactions();
+
+  /* calculate the total size of the download */
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
     {
-      packagemeta & pkg = *(i->second);
-      if (pkg.picked () || pkg.srcpicked ())
- {
-  packageversion version = pkg.desired;
-  packageversion sourceversion = version.sourcePackage();
-  try
-    {
-      if (pkg.picked())
- {
-    if (!check_for_cached (*version.source()))
-      total_download_bytes += version.source()->size;
- }
-      if (pkg.srcpicked () || IncludeSource)
- {
-    if (!check_for_cached (*sourceversion.source()))
-      total_download_bytes += sourceversion.source()->size;
- }
-    }
-  catch (Exception * e)
-    {
-      // We know what to do with these..
-      if (e->errNo() == APPERR_CORRUPT_PACKAGE)
- fatal (owner, IDS_CORRUPT_PACKAGE, pkg.name.c_str());
-      // Unexpected exception.
-      throw e;
-    }
- }
+      packageversion version = i->version;
+
+      try
+        {
+          if (!check_for_cached (*version.source()))
+            total_download_bytes += version.source()->size;
+        }
+      catch (Exception * e)
+        {
+          // We know what to do with these..
+          if (e->errNo() == APPERR_CORRUPT_PACKAGE)
+            fatal (owner, IDS_CORRUPT_PACKAGE, version.Name().c_str());
+          // Unexpected exception.
+          throw e;
+        }
     }
 
   /* and do the download. FIXME: This here we assign a new name for the cached version
    * and check that above.
    */
-  for (packagedb::packagecollection::iterator i = db.packages.begin ();
-       i != db.packages.end (); ++i)
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
     {
-      packagemeta & pkg = *(i->second);
-      if (pkg.picked () || pkg.srcpicked ())
+      packageversion version = i->version;
+
  {
   int e = 0;
-  packageversion version = pkg.desired;
-  packageversion sourceversion = version.sourcePackage();
-  if (pkg.picked())
-    {
- e += download_one (*version.source(), owner);
-    }
-  if (sourceversion && (pkg.srcpicked() || IncludeSource))
-    {
- e += download_one (*sourceversion.source (), owner);
-    }
+  e += download_one (*version.source(), owner);
   errors += e;
 #if 0
   if (e)
diff --git a/install.cc b/install.cc
index 9c4c01b..94c2b16 100644
--- a/install.cc
+++ b/install.cc
@@ -69,7 +69,6 @@ static long long int total_bytes = 0;
 static long long int total_bytes_sofar = 0;
 static int package_bytes = 0;
 
-extern BoolOption IncludeSource;
 static BoolOption NoReplaceOnReboot (false, 'r', "no-replaceonreboot",
      "Disable replacing in-use files on next "
      "reboot.");
@@ -799,84 +798,61 @@ do_install_thread (HINSTANCE h, HWND owner)
   /* Writes Cygwin/setup/rootdir registry value */
   create_install_root ();
 
-  vector <packagemeta *> install_q, uninstall_q, sourceinstall_q;
+  vector <packageversion> install_q, uninstall_q, sourceinstall_q;
 
   packagedb db;
+  const SolverTransactionList &t = db.solution.transactions();
 
   /* Calculate the amount of data to md5sum */
   Progress.SetText1("Calculating...");
   long long int md5sum_total_bytes = 0;
-  for (packagedb::packagecollection::iterator i = db.packages.begin ();
-       i != db.packages.end (); ++i)
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
   {
-    packagemeta & pkg = *(i->second);
+    packageversion version = i->version;
 
-    if (pkg.picked())
+    if (i->type == SolverTransaction::transInstall)
     {
-      md5sum_total_bytes += pkg.desired.source()->size;
-    }
-
-    if (pkg.srcpicked() || IncludeSource)
-    {
-      md5sum_total_bytes += pkg.desired.sourcePackage ().source()->size;
+      md5sum_total_bytes += version.source()->size;
     }
   }
 
   /* md5sum the packages, build lists of packages to install and uninstall
      and calculate the total amount of data to install */
   long long int md5sum_total_bytes_sofar = 0;
-  for (packagedb::packagecollection::iterator i = db.packages.begin ();
-       i != db.packages.end (); ++i)
+  for (SolverTransactionList::const_iterator i = t.begin (); i != t.end (); ++i)
   {
-    packagemeta & pkg = *(i->second);
+    packageversion version = i->version;
+    packagemeta *pkgm = db.findBinary (PackageSpecification(version.Name()));
+    if (!pkgm)
+      pkgm = db.findSource (PackageSpecification(version.Name()));
 
-    if (pkg.picked())
+    if (i->type == SolverTransaction::transInstall)
     {
       try
       {
-        chksum_one (pkg, *pkg.desired.source ());
+        chksum_one (*pkgm, *version.source ());
       }
       catch (Exception *e)
       {
-        if (yesno (owner, IDS_SKIP_PACKAGE, e->what()) == IDYES)
-          pkg.pick (false);
+        yesno (owner, IDS_SKIP_PACKAGE, e->what());
       }
-      if (pkg.picked())
       {
-        md5sum_total_bytes_sofar += pkg.desired.source()->size;
-        total_bytes += pkg.desired.source()->size;
-        install_q.push_back (&pkg);
+        md5sum_total_bytes_sofar += version.source()->size;
+        total_bytes += version.source()->size;
+
+        // source packages are kept in a separate queue as they are installed
+        // differently: root is /usr/src, install isn't recorded, etc.
+        if (version.Type() == package_source)
+          sourceinstall_q.push_back (version);
+        else
+          install_q.push_back (version);
       }
     }
 
-    if (pkg.srcpicked() || IncludeSource)
+    /* Uninstall, upgrade or reinstall */
+    if (i->type == SolverTransaction::transErase)
     {
-      bool skiprequested = false ;
-      try
-      {
-        chksum_one (pkg, *pkg.desired.sourcePackage ().source ());
-      }
-      catch (Exception *e)
-      {
-        if (yesno (owner, IDS_SKIP_PACKAGE, e->what()) == IDYES)
- {
-  skiprequested = true ; //(err occurred,) skip pkg desired
-          pkg.srcpick (false);
- }
-      }
-      if (pkg.srcpicked() || (IncludeSource && !skiprequested))
-      {
-        md5sum_total_bytes_sofar += pkg.desired.sourcePackage ().source()->size;
-        total_bytes += pkg.desired.sourcePackage ().source()->size;
-        sourceinstall_q.push_back (&pkg);
-      }
-    }
-
-    /* Upgrade or reinstall */
-    if ((pkg.installed && pkg.desired != pkg.installed)
-        || pkg.picked ())
-    {
-      uninstall_q.push_back (&pkg);
+      uninstall_q.push_back (version);
     }
 
     if (md5sum_total_bytes > 0)
@@ -885,27 +861,31 @@ do_install_thread (HINSTANCE h, HWND owner)
 
   /* start with uninstalls - remove files that new packages may replace */
   Progress.SetBar2(0);
-  for (vector <packagemeta *>::iterator i = uninstall_q.begin ();
+  for (vector <packageversion>::iterator i = uninstall_q.begin ();
        i != uninstall_q.end (); ++i)
   {
-    myInstaller.preremoveOne (**i);
+    packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
+    myInstaller.preremoveOne (*pkgm);
     Progress.SetBar2(std::distance(uninstall_q.begin(), i) + 1, uninstall_q.size());
   }
 
   Progress.SetBar2(0);
-  for (vector <packagemeta *>::iterator i = uninstall_q.begin ();
+  for (vector <packageversion>::iterator i = uninstall_q.begin ();
        i != uninstall_q.end (); ++i)
   {
-    myInstaller.uninstallOne (**i);
+    packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
+    myInstaller.uninstallOne (*pkgm);
     Progress.SetBar2(std::distance(uninstall_q.begin(), i) + 1, uninstall_q.size());
   }
 
-  for (vector <packagemeta *>::iterator i = install_q.begin ();
+  for (vector <packageversion>::iterator i = install_q.begin ();
        i != install_q.end (); ++i)
   {
-    packagemeta & pkg = **i;
+    packageversion & pkg = *i;
+    packagemeta *pkgm = db.findBinary (PackageSpecification(i->Name()));
+
     try {
-      myInstaller.installOne (pkg, pkg.desired, *pkg.desired.source(),
+      myInstaller.installOne (*pkgm, pkg, *pkg.source(),
                               "cygfile://", "/", owner);
     }
     catch (exception *e)
@@ -920,12 +900,12 @@ do_install_thread (HINSTANCE h, HWND owner)
     }
   }
 
-  for (vector <packagemeta *>::iterator i = sourceinstall_q.begin ();
+  for (vector <packageversion>::iterator i = sourceinstall_q.begin ();
        i != sourceinstall_q.end (); ++i)
   {
-    packagemeta & pkg = **i;
-    myInstaller.installOne (pkg, pkg.desired.sourcePackage(),
-                            *pkg.desired.sourcePackage().source(),
+    packagemeta *pkgm = db.findSource (PackageSpecification(i->Name()));
+    packageversion & pkg = *i;
+    myInstaller.installOne (*pkgm, pkg, *pkg.source(),
                             "cygfile://", "/usr/src/", owner);
   }
 
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

[PATCH setup 14/14] Add obsoletes: support

Jon TURNEY
In reply to this post by Jon TURNEY
Note that we need separate depends and obsoletes nodelists
---
 IniDBBuilderPackage.cc | 23 ++++++++++++++++++-----
 IniDBBuilderPackage.h  |  5 ++++-
 inilex.ll              |  1 +
 iniparse.yy            |  3 +++
 libsolv.cc             |  2 ++
 libsolv.h              |  1 +
 package_db.cc          |  1 +
 7 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/IniDBBuilderPackage.cc b/IniDBBuilderPackage.cc
index b929c7e..4e92961 100644
--- a/IniDBBuilderPackage.cc
+++ b/IniDBBuilderPackage.cc
@@ -88,7 +88,9 @@ IniDBBuilderPackage::buildPackage (const std::string& _name)
   cbpv.archive = packagesource();
 
   currentSpec = NULL;
-  currentNodeList = PackageDepends();
+  currentNodeList = NULL;
+  dependsNodeList = PackageDepends();
+  obsoletesNodeList = PackageDepends();
 #if DEBUG
   Log (LOG_BABBLE) << "Created package " << name << endLog;
 #endif
@@ -208,8 +210,9 @@ IniDBBuilderPackage::buildBeginDepends ()
   Log (LOG_BABBLE) << "Beginning of a depends statement " << endLog;
 #endif
   currentSpec = NULL;
-  currentNodeList = PackageDepends();
-  cbpv.requires = &currentNodeList;
+  dependsNodeList= PackageDepends();
+  currentNodeList = &dependsNodeList;
+  cbpv.requires = &dependsNodeList;
 }
 
 void
@@ -219,11 +222,20 @@ IniDBBuilderPackage::buildBeginBuildDepends ()
   Log (LOG_BABBLE) << "Beginning of a Build-Depends statement" << endLog;
 #endif
   currentSpec = NULL;
-  currentNodeList = PackageDepends();
+  currentNodeList = NULL;
   /* there is currently nowhere to store Build-Depends information */
 }
 
 void
+IniDBBuilderPackage::buildBeginObsoletes ()
+{
+  currentSpec = NULL;
+  obsoletesNodeList =   PackageDepends();
+  currentNodeList = &obsoletesNodeList;
+  cbpv.obsoletes = &obsoletesNodeList;
+}
+
+void
 IniDBBuilderPackage::buildSourceName (const std::string& _name)
 {
   // When there is a Source: line, that names a real source package
@@ -247,7 +259,8 @@ IniDBBuilderPackage::buildPackageListNode (const std::string & name)
   Log (LOG_BABBLE) << "New node '" << name << "' for package list" << endLog;
 #endif
   currentSpec = new PackageSpecification (name);
-  currentNodeList.push_back (currentSpec);
+  if (currentNodeList)
+    currentNodeList->push_back (currentSpec);
 }
 
 void
diff --git a/IniDBBuilderPackage.h b/IniDBBuilderPackage.h
index 307d2b5..4e34680 100644
--- a/IniDBBuilderPackage.h
+++ b/IniDBBuilderPackage.h
@@ -57,6 +57,7 @@ public:
 
   void buildBeginDepends ();
   void buildBeginBuildDepends ();
+  void buildBeginObsoletes ();
   void buildMessage (const std::string&, const std::string&);
   void buildSourceName (const std::string& );
   void buildSourceNameVersion (const std::string& );
@@ -83,7 +84,9 @@ private:
   std::string message_id;
   std::string message_string;
   PackageSpecification *currentSpec;
-  PackageDepends currentNodeList;
+  PackageDepends *currentNodeList;
+  PackageDepends dependsNodeList;
+  PackageDepends obsoletesNodeList;
   SolverPool::addPackageData cbpv;
 
   IniParseFeedback const &_feedback;
diff --git a/inilex.ll b/inilex.ll
index 13422b1..698775c 100644
--- a/inilex.ll
+++ b/inilex.ll
@@ -122,6 +122,7 @@ B64 [a-zA-Z0-9_-]
 "category:"|"Section:" return CATEGORY;
 "requires:" return REQUIRES;
 [dD]"epends:" return DEPENDS;
+[oO]"bsoletes:" return OBSOLETES;
 
 ^{STR}":" ignore_line ();
 
diff --git a/iniparse.yy b/iniparse.yy
index 18ebe2a..3b74e36 100644
--- a/iniparse.yy
+++ b/iniparse.yy
@@ -45,6 +45,7 @@ extern int yylineno;
 %token COMMA NL AT
 %token OPENBRACE CLOSEBRACE EQUAL GT LT GTEQUAL LTEQUAL
 %token BUILDDEPENDS
+%token OBSOLETES
 %token MESSAGE
 %token ARCH RELEASE
 
@@ -103,6 +104,8 @@ singleitem /* non-empty */
  | DEPENDS { iniBuilder->buildBeginDepends(); } versionedpackagelist NL
  | REQUIRES { iniBuilder->buildBeginDepends(); } versionedpackagelistsp NL
  | BUILDDEPENDS { iniBuilder->buildBeginBuildDepends(); } versionedpackagelist NL
+ | OBSOLETES { iniBuilder->buildBeginObsoletes(); } versionedpackagelist NL
+
  | MESSAGE STRING STRING NL { iniBuilder->buildMessage ($2, $3); }
  | error NL { yyerror (std::string("unrecognized line ")
   + stringify(yylineno)
diff --git a/libsolv.cc b/libsolv.cc
index df0af3d..f509617 100644
--- a/libsolv.cc
+++ b/libsolv.cc
@@ -350,6 +350,8 @@ SolverPool::addPackage(const std::string& pkgname, const addPackageData &pkgdata
   solvable->provides = repo_addid_dep(repo, solvable->provides, pool_rel2id(pool, solvable->name, solvable->evr, REL_EQ, 1), 0);
   if (pkgdata.requires)
     solvable->requires = makedeps(repo, pkgdata.requires);
+  if (pkgdata.obsoletes)
+    solvable->obsoletes = makedeps(repo, pkgdata.obsoletes);
 
   /* a solvable can also store arbitrary attributes not needed for dependency
      resolution, if we need them */
diff --git a/libsolv.h b/libsolv.h
index 43b37d2..be518e9 100644
--- a/libsolv.h
+++ b/libsolv.h
@@ -130,6 +130,7 @@ public:
     PackageSpecification spkg;
     SolvableVersion spkg_id;
     PackageDepends *requires;
+    PackageDepends *obsoletes;
   };
 
   SolvableVersion addPackage(const std::string& pkgname,
diff --git a/package_db.cc b/package_db.cc
index f12afda..673e11a 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -113,6 +113,7 @@ packagedb::read ()
                   // we put our best guesses here...
                   data.vendor = "cygwin";
                   data.requires = NULL;
+                  data.obsoletes = NULL;
                   data.sdesc = "";
                   data.ldesc = "";
                   data.stability = TRUST_CURR; // XXX: would be nice to get this correct as it effects upgrade decisions...
--
2.12.3

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)

Ken Brown-6
In reply to this post by Jon TURNEY
On 5/31/2017 6:50 AM, Jon Turney wrote:
> ... solve some problems, perhaps add some new ones, I guess.  I'm not 100%
> sure this is the right approach to take, but I wrote it, so here it is.
[...]
> - As implemented, selecting "Current" overrides "Keep".  This is wrong, and a
> change from current behaviour, but is probably a symptom of some deeper
> confusion in the picker UI I'm not sure how to address

I think the problem might be the following lines in the definition of
SolverSolution::update:

   if (update)
     queue_push2(&job, SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, 0);

When the prerequisite checker calls SolverSolution::update, doesn't this
cause the upgrading of old versions that we want to keep (assuming
"Current" has been selected)?  As a quick test, I commented out those
lines and found that setup.exe let me keep an old version of a package.

Maybe you need to add a DISABLE_UPDATE command to the solver task list
to implement "Keep" for packages that would otherwise be updated.

(Disclaimer: I have only looked briefly at the libsolv sources, so what
I'm saying might be complete nonsense.)

Ken
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)

Ken Brown-6
On 8/29/2017 9:37 AM, Ken Brown wrote:

> On 5/31/2017 6:50 AM, Jon Turney wrote:
>> ... solve some problems, perhaps add some new ones, I guess.  I'm not
>> 100%
>> sure this is the right approach to take, but I wrote it, so here it is.
> [...]
>> - As implemented, selecting "Current" overrides "Keep".  This is
>> wrong, and a
>> change from current behaviour, but is probably a symptom of some deeper
>> confusion in the picker UI I'm not sure how to address
>
> I think the problem might be the following lines in the definition of
> SolverSolution::update:
>
>    if (update)
>      queue_push2(&job, SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, 0);
>
> When the prerequisite checker calls SolverSolution::update, doesn't this
> cause the upgrading of old versions that we want to keep (assuming
> "Current" has been selected)?  As a quick test, I commented out those
> lines and found that setup.exe let me keep an old version of a package.
>
> Maybe you need to add a DISABLE_UPDATE command to the solver task list
> to implement "Keep" for packages that would otherwise be updated.
DISABLE_UPDATE is not a command.  But SOLVER_LOCK seems to do the job.
Jon, I'm attaching a patch that should apply to the libsolv branch of
your github cygwin-setup repo.  So far I've only tested it very lightly,
enough to verify that it lets me keep an old version of a package.

Ken


0001-Don-t-override-a-Keep-selection.patch (4K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)

Ken Brown-6
In reply to this post by Jon TURNEY
On 5/31/2017 6:50 AM, Jon Turney wrote:

> What remains to be done:
> - I've dodged a lot of the UI issues: If the solver reports problems, all that
> can be done is accept the default solution or cancel.  This possibly isn't a
> big problem until we have a package set which can contain problems...
> - We had a very poor UI for showing what will actually be done (combine in
> your head the "Pending" view with packages listed in the text on the
> PrereChecker page), and this removes part of that
> - As implemented, selecting "Current" overrides "Keep".  This is wrong, and a
> change from current behaviour, but is probably a symptom of some deeper
> confusion in the picker UI I'm not sure how to address

There are also some issues involving the treatment of test releases:

- It's not possible to install a test release of a package without
clicking the global Test button.  But then you have to manually choose
Keep for all the package where you don't want the test release.

- Once a test release is installed, selecting Reinstall will downgrade
the package if the global Test button is not pressed.  Presumably this
is because the test release has become invisible, since its repo has
been disabled.  But I haven't checked carefully to see exactly what's
going on.

- I found some glitches involving SHA512 sums of test releases.  It's
probably not worth pursuing this until the handling of test releases is
redone.

Ken

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)

Ken Brown-6
On 9/1/2017 11:00 AM, Ken Brown wrote:

> On 5/31/2017 6:50 AM, Jon Turney wrote:
>> What remains to be done:
>> - I've dodged a lot of the UI issues: If the solver reports problems,
>> all that
>> can be done is accept the default solution or cancel.  This possibly
>> isn't a
>> big problem until we have a package set which can contain problems...
>> - We had a very poor UI for showing what will actually be done
>> (combine in
>> your head the "Pending" view with packages listed in the text on the
>> PrereChecker page), and this removes part of that
>> - As implemented, selecting "Current" overrides "Keep".  This is
>> wrong, and a
>> change from current behaviour, but is probably a symptom of some deeper
>> confusion in the picker UI I'm not sure how to address
>
> There are also some issues involving the treatment of test releases:
>
> - It's not possible to install a test release of a package without
> clicking the global Test button.  But then you have to manually choose
> Keep for all the package where you don't want the test release.
>
> - Once a test release is installed, selecting Reinstall will downgrade
> the package if the global Test button is not pressed.  Presumably this
> is because the test release has become invisible, since its repo has
> been disabled.  But I haven't checked carefully to see exactly what's
> going on.
>
> - I found some glitches involving SHA512 sums of test releases.  It's
> probably not worth pursuing this until the handling of test releases is
> redone.

I fixed the last two problems, which turned out to have nothing to do
with the way test releases are handle.  So all that's left of my three
issues is the first one, which is just a UI annoyance.

I don't think it makes sense for me to send further patches here.
Instead, I'll fork Jon's GitHub repo and continue to try to make
improvements.

Ken

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)

Jon TURNEY
On 02/09/2017 17:57, Ken Brown wrote:

> On 9/1/2017 11:00 AM, Ken Brown wrote:
>> On 5/31/2017 6:50 AM, Jon Turney wrote:
>>> What remains to be done:
>>> - I've dodged a lot of the UI issues: If the solver reports problems,
>>> all that
>>> can be done is accept the default solution or cancel.  This possibly
>>> isn't a
>>> big problem until we have a package set which can contain problems...
>>> - We had a very poor UI for showing what will actually be done
>>> (combine in
>>> your head the "Pending" view with packages listed in the text on the
>>> PrereChecker page), and this removes part of that
>>> - As implemented, selecting "Current" overrides "Keep".  This is
>>> wrong, and a
>>> change from current behaviour, but is probably a symptom of some deeper
>>> confusion in the picker UI I'm not sure how to address

Thanks very much for taking the time to look at these changes and test them.

It was my intention to come back and take another look at this, but that
hasn't happened yet :S

>> There are also some issues involving the treatment of test releases:
>>
>> - It's not possible to install a test release of a package without
>> clicking the global Test button.  But then you have to manually choose
>> Keep for all the package where you don't want the test release.

Yeah, I'm not sure if putting the test packages into a separate repo
which is disabled unless explicitly enabled is the right approach.

(Instead, perhaps it is possible to tell the solver that certain
repositories are disfavoured)

>> - Once a test release is installed, selecting Reinstall will downgrade
>> the package if the global Test button is not pressed.  Presumably this
>> is because the test release has become invisible, since its repo has
>> been disabled.  But I haven't checked carefully to see exactly what's
>> going on.

>> - I found some glitches involving SHA512 sums of test releases.  It's
>> probably not worth pursuing this until the handling of test releases
>> is redone.
>
> I fixed the last two problems, which turned out to have nothing to do
> with the way test releases are handle.  So all that's left of my three
> issues is the first one, which is just a UI annoyance.
>
> I don't think it makes sense for me to send further patches here.
> Instead, I'll fork Jon's GitHub repo and continue to try to make
> improvements.

Awesome.

1234