public inbox for cygwin-apps@cygwin.com
 help / color / mirror / Atom feed
* [PATCH setup 03/14] Hoist addScript() etc. up from packageversion to packagemeta
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
  2017-05-31 10:53 ` [PATCH setup 04/14] Hoist pick() up to packagemeta Jon Turney
  2017-05-31 10:53 ` [PATCH setup 05/14] Hoist uninstall up to Installer::uninstallOne() Jon Turney
@ 2017-05-31 10:53 ` Jon Turney
  2017-05-31 10:53 ` [PATCH setup 01/14] Opaque how PackageDepends is stored Jon Turney
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:53 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 05/14] Hoist uninstall up to Installer::uninstallOne()
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
  2017-05-31 10:53 ` [PATCH setup 04/14] Hoist pick() up to packagemeta Jon Turney
@ 2017-05-31 10:53 ` Jon Turney
  2017-05-31 10:53 ` [PATCH setup 03/14] Hoist addScript() etc. up from packageversion to packagemeta Jon Turney
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:53 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
@ 2017-05-31 10:53 Jon Turney
  2017-05-31 10:53 ` [PATCH setup 04/14] Hoist pick() up to packagemeta Jon Turney
                   ` (13 more replies)
  0 siblings, 14 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:53 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 04/14] Hoist pick() up to packagemeta
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
@ 2017-05-31 10:53 ` Jon Turney
  2017-05-31 10:53 ` [PATCH setup 05/14] Hoist uninstall up to Installer::uninstallOne() Jon Turney
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:53 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 01/14] Opaque how PackageDepends is stored
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (2 preceding siblings ...)
  2017-05-31 10:53 ` [PATCH setup 03/14] Hoist addScript() etc. up from packageversion to packagemeta Jon Turney
@ 2017-05-31 10:53 ` Jon Turney
  2017-05-31 10:53 ` [PATCH setup 02/14] Factor out reading installed.db Jon Turney
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:53 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 02/14] Factor out reading installed.db
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (3 preceding siblings ...)
  2017-05-31 10:53 ` [PATCH setup 01/14] Opaque how PackageDepends is stored Jon Turney
@ 2017-05-31 10:53 ` Jon Turney
  2017-05-31 10:57 ` [PATCH setup 08/14] Change to using a libsolv pool for storing package information Jon Turney
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:53 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 10/14] Remove packageversion class
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (6 preceding siblings ...)
  2017-05-31 10:57 ` [PATCH setup 09/14] Remove cygpackage class Jon Turney
@ 2017-05-31 10:57 ` Jon Turney
  2017-05-31 10:57 ` [PATCH setup 06/14] Hoist scan() up from packageversion to packagemeta Jon Turney
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:57 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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  <rbtcollins@hotmail.com>
- *
- */
-
-/* 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  <rbtcollins@hotmail.com>
- *
- */
-
-#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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 09/14] Remove cygpackage class
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (5 preceding siblings ...)
  2017-05-31 10:57 ` [PATCH setup 08/14] Change to using a libsolv pool for storing package information Jon Turney
@ 2017-05-31 10:57 ` Jon Turney
  2017-05-31 10:57 ` [PATCH setup 10/14] Remove packageversion class Jon Turney
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:57 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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  <rbtcollins@hotmail.com>
- *
- */
-
-/* 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  <rbtcollins@hotmail.com>
- *
- */
-
-#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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 07/14] Store package stability in class packageversion
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (8 preceding siblings ...)
  2017-05-31 10:57 ` [PATCH setup 06/14] Hoist scan() up from packageversion to packagemeta Jon Turney
@ 2017-05-31 10:57 ` Jon Turney
  2017-05-31 11:05 ` [PATCH setup 11/14] Drop in SolvableVersion as a replacement for packageversion Jon Turney
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:57 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 06/14] Hoist scan() up from packageversion to packagemeta
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (7 preceding siblings ...)
  2017-05-31 10:57 ` [PATCH setup 10/14] Remove packageversion class Jon Turney
@ 2017-05-31 10:57 ` Jon Turney
  2017-05-31 10:57 ` [PATCH setup 07/14] Store package stability in class packageversion Jon Turney
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:57 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 08/14] Change to using a libsolv pool for storing package information
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (4 preceding siblings ...)
  2017-05-31 10:53 ` [PATCH setup 02/14] Factor out reading installed.db Jon Turney
@ 2017-05-31 10:57 ` Jon Turney
  2017-05-31 10:57 ` [PATCH setup 09/14] Remove cygpackage class Jon Turney
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 10:57 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 12/14] Use solver to check for problems and produce a list of package transactions
  2017-05-31 11:05 ` [PATCH setup 11/14] Drop in SolvableVersion as a replacement for packageversion Jon Turney
@ 2017-05-31 11:05   ` Jon Turney
  2017-05-31 11:05   ` [PATCH setup 13/14] Download/checksum/install/uninstall what transaction wants Jon Turney
  2017-05-31 11:05   ` [PATCH setup 14/14] Add obsoletes: support Jon Turney
  2 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 11:05 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 13/14] Download/checksum/install/uninstall what transaction wants
  2017-05-31 11:05 ` [PATCH setup 11/14] Drop in SolvableVersion as a replacement for packageversion Jon Turney
  2017-05-31 11:05   ` [PATCH setup 12/14] Use solver to check for problems and produce a list of package transactions Jon Turney
@ 2017-05-31 11:05   ` Jon Turney
  2017-05-31 11:05   ` [PATCH setup 14/14] Add obsoletes: support Jon Turney
  2 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 11:05 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 11/14] Drop in SolvableVersion as a replacement for packageversion
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (9 preceding siblings ...)
  2017-05-31 10:57 ` [PATCH setup 07/14] Store package stability in class packageversion Jon Turney
@ 2017-05-31 11:05 ` Jon Turney
  2017-05-31 11:05   ` [PATCH setup 12/14] Use solver to check for problems and produce a list of package transactions Jon Turney
                     ` (2 more replies)
  2017-08-29 13:37 ` [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Ken Brown
                   ` (2 subsequent siblings)
  13 siblings, 3 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 11:05 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* [PATCH setup 14/14] Add obsoletes: support
  2017-05-31 11:05 ` [PATCH setup 11/14] Drop in SolvableVersion as a replacement for packageversion Jon Turney
  2017-05-31 11:05   ` [PATCH setup 12/14] Use solver to check for problems and produce a list of package transactions Jon Turney
  2017-05-31 11:05   ` [PATCH setup 13/14] Download/checksum/install/uninstall what transaction wants Jon Turney
@ 2017-05-31 11:05   ` Jon Turney
  2 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-05-31 11:05 UTC (permalink / raw)
  To: cygwin-apps; +Cc: 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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (10 preceding siblings ...)
  2017-05-31 11:05 ` [PATCH setup 11/14] Drop in SolvableVersion as a replacement for packageversion Jon Turney
@ 2017-08-29 13:37 ` Ken Brown
  2017-08-30 21:47   ` Ken Brown
  2017-09-01 15:01 ` Ken Brown
  2017-09-08 18:54 ` Ken Brown
  13 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-08-29 13:37 UTC (permalink / raw)
  To: cygwin-apps

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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-08-29 13:37 ` [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Ken Brown
@ 2017-08-30 21:47   ` Ken Brown
  0 siblings, 0 replies; 75+ messages in thread
From: Ken Brown @ 2017-08-30 21:47 UTC (permalink / raw)
  To: cygwin-apps

[-- Attachment #1: Type: text/plain, Size: 1405 bytes --]

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


[-- Attachment #2: 0001-Don-t-override-a-Keep-selection.patch --]
[-- Type: text/plain, Size: 3235 bytes --]

From 26231d3c83c392e6fa267bcea9135f2b5e5af1ca Mon Sep 17 00:00:00 2001
From: Ken Brown <kbrown@cornell.edu>
Date: Wed, 30 Aug 2017 17:36:13 -0400
Subject: [PATCH] Don't override a Keep selection

---
 libsolv.cc     |  3 +++
 libsolv.h      |  3 ++-
 package_db.cc  |  2 +-
 package_meta.h |  2 ++
 prereq.cc      | 12 ++++++++----
 5 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/libsolv.cc b/libsolv.cc
index f509617..67b99b2 100644
--- a/libsolv.cc
+++ b/libsolv.cc
@@ -526,6 +526,9 @@ SolverSolution::update(SolverTasks &tasks, bool update, bool use_test_packages,
           // we don't know how to ask solver for this, so we just add the erase
           // and install later
           break;
+	case SolverTasks::taskKeep:
+	  queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE, sv.id);
+	  break;
         default:
           Log (LOG_PLAIN) << "unknown task " << (*i).second << endLog;
         }
diff --git a/libsolv.h b/libsolv.h
index be518e9..7768128 100644
--- a/libsolv.h
+++ b/libsolv.h
@@ -165,7 +165,8 @@ class SolverTasks
   {
     taskInstall,
     taskUninstall,
-    taskReinstall
+    taskReinstall,
+    taskKeep,
   };
   void add(const SolvableVersion &v, task t)
   {
diff --git a/package_db.cc b/package_db.cc
index 9f9e0a6..d7ec043 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -522,7 +522,7 @@ packagedb::defaultTrust (trusts trust)
             || pkg.categories.find ("Base") != pkg.categories.end ()
             || pkg.categories.find ("Orphaned") != pkg.categories.end ())
         {
-          pkg.desired = pkg.trustp (true, trust);
+          pkg.desired = pkg.default_version = pkg.trustp (true, trust);
           if (pkg.desired)
             pkg.pick (pkg.desired.accessible() && pkg.desired != pkg.installed);
         }
diff --git a/package_meta.h b/package_meta.h
index b6faab8..d91f7c9 100644
--- a/package_meta.h
+++ b/package_meta.h
@@ -131,6 +131,8 @@ public:
   packageversion curr;
   /* ditto for "test" (experimental) */
   packageversion exp;
+  /* which one is the default according to the chooser global state */
+  packageversion default_version;
   /* Now for the user stuff :] */
   /* What version does the user want ? */
   packageversion desired;
diff --git a/prereq.cc b/prereq.cc
index 49fbd77..8922cc6 100644
--- a/prereq.cc
+++ b/prereq.cc
@@ -170,6 +170,7 @@ PrereqChecker::isMet ()
 
       // decode UI state to action
       // skip and keep don't change dependency solution
+      // except when we want to keep an old version
       if (pkg->installed != pkg->desired)
         {
           if (pkg->desired)
@@ -177,10 +178,13 @@ PrereqChecker::isMet ()
           else
             q.add(pkg->installed, SolverTasks::taskUninstall); // uninstall
         }
-      else
-        if (pkg->picked())
-          q.add(pkg->installed, SolverTasks::taskReinstall); // reinstall
-
+      else if (pkg->installed)
+	{
+	  if (pkg->picked())
+	    q.add(pkg->installed, SolverTasks::taskReinstall); // reinstall
+	  else if (upgrade && pkg->installed < pkg->default_version)
+	    q.add(pkg->installed, SolverTasks::taskKeep); // keep
+	}
       // only install action makes sense for source packages
       if (pkg->srcpicked())
         {
-- 
2.14.1


^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (11 preceding siblings ...)
  2017-08-29 13:37 ` [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Ken Brown
@ 2017-09-01 15:01 ` Ken Brown
  2017-09-02 16:57   ` Ken Brown
  2017-09-08 18:54 ` Ken Brown
  13 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-01 15:01 UTC (permalink / raw)
  To: cygwin-apps

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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-01 15:01 ` Ken Brown
@ 2017-09-02 16:57   ` Ken Brown
  2017-09-05 13:34     ` Jon Turney
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-02 16:57 UTC (permalink / raw)
  To: cygwin-apps

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

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-02 16:57   ` Ken Brown
@ 2017-09-05 13:34     ` Jon Turney
  2017-09-05 18:40       ` Achim Gratz
  0 siblings, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-09-05 13:34 UTC (permalink / raw)
  To: cygwin-apps; +Cc: Ken Brown

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.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-05 13:34     ` Jon Turney
@ 2017-09-05 18:40       ` Achim Gratz
  2017-09-06  2:52         ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Achim Gratz @ 2017-09-05 18:40 UTC (permalink / raw)
  To: cygwin-apps

Jon Turney writes:
> 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)

At least for zypper, which is based on the same library:

You can give each repo a priority and it will pick the package from the
one with the highest priority, even if another repo has a higher
version.  You can also tell the solver not to switch packages to another
repo automatically, so that once you've switched something to testing it
will stay there.


Regards,
Achim.
-- 
+<[Q+ Matrix-12 WAVE#46+305 Neuron microQkb Andromeda XTk Blofeld]>+

SD adaptation for Waldorf rackAttack V1.04R1:
http://Synth.Stromeko.net/Downloads.html#WaldorfSDada

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-05 18:40       ` Achim Gratz
@ 2017-09-06  2:52         ` Ken Brown
  2017-11-23 18:10           ` Jon Turney
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-06  2:52 UTC (permalink / raw)
  To: cygwin-apps

On 9/5/2017 2:40 PM, Achim Gratz wrote:
> Jon Turney writes:
>> 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)
> 
> At least for zypper, which is based on the same library:
> 
> You can give each repo a priority and it will pick the package from the
> one with the highest priority, even if another repo has a higher
> version.

Yes, that works.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
                   ` (12 preceding siblings ...)
  2017-09-01 15:01 ` Ken Brown
@ 2017-09-08 18:54 ` Ken Brown
  2017-09-11 20:40   ` Ken Brown
  2017-09-15 15:15   ` Jon Turney
  13 siblings, 2 replies; 75+ messages in thread
From: Ken Brown @ 2017-09-08 18:54 UTC (permalink / raw)
  To: cygwin-apps

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.

I've now fixed all the bugs I've noticed, and I think I've gotten the 
libsolv branch pretty close to the point where it is usable (barring 
further bugs).

> - 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.

I've changed this so that the Back button takes the user back to the 
chooser page, with all packages reset to show the solver's default 
solution.  The user can then see what will be done and, if desired, try 
to solve the problems in other ways.

> - 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

This is fixed by the above.

> - As implemented, selecting "Current" overrides "Keep".  This is wrong, and a
> change from current behaviour,

Fixed.

Here are some other things I've done, aside from fixing bugs:

- As discussed earlier in the thread, I've made it possible for the user 
to install test packages without clicking the Test button.

- I've artificially created a conflict if the user tries to uninstall a 
Base package.  There's probably a better way to deal with this.

- I've made the solver check dependencies of installed packages.  (It 
doesn't do this by default, which seems strange to me.)

There's still one UI issue that I haven't dealt with: If the solver 
finds missing dependencies, setup simply installs them silently without 
informing the user (except in the log).  In particular, the user can no 
longer refuse to install them.  I'm not sure how to best deal with this.

Finally, I have a question for you, Jon: You introduced 
PrereqChecker::upgrade, which is true if and only if the user selects 
Current or Test.  I don't see why this is needed.  I've disabled the use 
of it and haven't noticed any ill effects.  Am I missing something?

Ken

P.S. Anyone who wants to look at the code can find it at

   https://github.com/kbrow1i/cygwin-setup ;

checkout the libsolv branch.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-08 18:54 ` Ken Brown
@ 2017-09-11 20:40   ` Ken Brown
  2017-09-13 19:17     ` Achim Gratz
  2017-09-15 15:15   ` Jon Turney
  1 sibling, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-11 20:40 UTC (permalink / raw)
  To: cygwin-apps

On 9/8/2017 2:54 PM, 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.
> 
> I've now fixed all the bugs I've noticed, and I think I've gotten the 
> libsolv branch pretty close to the point where it is usable (barring 
> further bugs).
> 
>> - 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.
> 
> I've changed this so that the Back button takes the user back to the 
> chooser page, with all packages reset to show the solver's default 
> solution.  The user can then see what will be done and, if desired, try 
> to solve the problems in other ways.
> 
>> - 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
> 
> This is fixed by the above.
> 
>> - As implemented, selecting "Current" overrides "Keep".  This is 
>> wrong, and a
>> change from current behaviour,
> 
> Fixed.
> 
> Here are some other things I've done, aside from fixing bugs:
> 
> - As discussed earlier in the thread, I've made it possible for the user 
> to install test packages without clicking the Test button.
> 
> - I've artificially created a conflict if the user tries to uninstall a 
> Base package.  There's probably a better way to deal with this.
> 
> - I've made the solver check dependencies of installed packages.  (It 
> doesn't do this by default, which seems strange to me.)
> 
> There's still one UI issue that I haven't dealt with: If the solver 
> finds missing dependencies, setup simply installs them silently without 
> informing the user (except in the log).  In particular, the user can no 
> longer refuse to install them.  I'm not sure how to best deal with this.

I've changed this so that the user can now review the packages that will 
be installed.  But I still haven't found a good way to enable the user 
to refuse dependencies.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-11 20:40   ` Ken Brown
@ 2017-09-13 19:17     ` Achim Gratz
  2017-09-13 21:16       ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Achim Gratz @ 2017-09-13 19:17 UTC (permalink / raw)
  To: cygwin-apps

Ken Brown writes:
> I've changed this so that the user can now review the packages that
> will be installed.  But I still haven't found a good way to enable the
> user to refuse dependencies.

With zypper you'd create a package lock and there'd be no changes to
the state of the package (including from uninstalled to installed).


Regards,
Achim.
-- 
+<[Q+ Matrix-12 WAVE#46+305 Neuron microQkb Andromeda XTk Blofeld]>+

SD adaptation for Waldorf rackAttack V1.04R1:
http://Synth.Stromeko.net/Downloads.html#WaldorfSDada

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-13 19:17     ` Achim Gratz
@ 2017-09-13 21:16       ` Ken Brown
  2017-09-14 17:26         ` Achim Gratz
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-13 21:16 UTC (permalink / raw)
  To: cygwin-apps

On 9/13/2017 3:17 PM, Achim Gratz wrote:
> Ken Brown writes:
>> I've changed this so that the user can now review the packages that
>> will be installed.  But I still haven't found a good way to enable the
>> user to refuse dependencies.
> 
> With zypper you'd create a package lock and there'd be no changes to
> the state of the package (including from uninstalled to installed).

Yes, I think that would work.  I already use that to prevent upgrade 
when the user has selected "Keep" on a package that would otherwise be 
upgraded.

What I've been struggling with, however, is the UI.  But now that I 
think about it, maybe it isn't that hard.  It's just a matter of doing 
something reasonable if the user unchecks "Accept default problem 
solutions".  I'll see what I can come up with.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-13 21:16       ` Ken Brown
@ 2017-09-14 17:26         ` Achim Gratz
  2017-09-14 20:46           ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Achim Gratz @ 2017-09-14 17:26 UTC (permalink / raw)
  To: cygwin-apps

Ken Brown writes:
> What I've been struggling with, however, is the UI.  But now that I
> think about it, maybe it isn't that hard.  It's just a matter of doing
> something reasonable if the user unchecks "Accept default problem
> solutions".  I'll see what I can come up with.

Well, zypper pretty much just gives you a bunch of possible solutions
and asks you to select one if there is either more than one or the
otherwise preferred solution is blocked by a lock.  There is always one
"break <whatever> package by doing <stuff>" down that list.  You could
maybe offer something along those lines in the inevitable dialog box?


Regards,
Achim.
-- 
+<[Q+ Matrix-12 WAVE#46+305 Neuron microQkb Andromeda XTk Blofeld]>+

SD adaptations for Waldorf Q V3.00R3 and Q+ V3.54R2:
http://Synth.Stromeko.net/Downloads.html#WaldorfSDada

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-14 17:26         ` Achim Gratz
@ 2017-09-14 20:46           ` Ken Brown
  2017-09-15 19:24             ` Jon Turney
  2017-12-05 14:32             ` Jon Turney
  0 siblings, 2 replies; 75+ messages in thread
From: Ken Brown @ 2017-09-14 20:46 UTC (permalink / raw)
  To: cygwin-apps

On 9/14/2017 1:26 PM, Achim Gratz wrote:
> Ken Brown writes:
>> What I've been struggling with, however, is the UI.  But now that I
>> think about it, maybe it isn't that hard.  It's just a matter of doing
>> something reasonable if the user unchecks "Accept default problem
>> solutions".  I'll see what I can come up with.
> 
> Well, zypper pretty much just gives you a bunch of possible solutions
> and asks you to select one if there is either more than one or the
> otherwise preferred solution is blocked by a lock.  There is always one
> "break <whatever> package by doing <stuff>" down that list.  You could
> maybe offer something along those lines in the inevitable dialog box?

In the long run I think that's the way to go.  But implementing that is 
more work than I feel like doing at the moment.  For now I've gone with 
an approach that was easier to program, more like the current setup.exe. 
  If the solver finds problems (including missing dependencies), the 
user has four choices on the Prerequisite page:

1. Click Back to go back to the Chooser page, with the Pending view 
showing the solver's default solutions.

2. Click Next to accept the default solutions.

3. Uncheck the "Accept default solutions" box and click Next.  If the 
user dismisses the resulting warning, setup will go ahead and do what 
the user requested.

4. Cancel.

Once the inevitable remaining bugs are fixed, I think we'll have a 
setup.exe that's better than the current one, with possibilities for 
further UI improvements along the lines you suggested.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-08 18:54 ` Ken Brown
  2017-09-11 20:40   ` Ken Brown
@ 2017-09-15 15:15   ` Jon Turney
  2017-09-15 16:53     ` Ken Brown
  1 sibling, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-09-15 15:15 UTC (permalink / raw)
  To: cygwin-apps; +Cc: Ken Brown

On 08/09/2017 19:54, Ken Brown wrote:
> Finally, I have a question for you, Jon: You introduced 
> PrereqChecker::upgrade, which is true if and only if the user selects 
> Current or Test.  I don't see why this is needed.  I've disabled the use 
> of it and haven't noticed any ill effects.  Am I missing something?

This is supposed to be passed into SolverSolution::update() and used to 
determine if a SOLVER_UPDATE | SOLVER_SOLVABLE_ALL task is given to the 
solver, causing all packages to be updated (if possible) (i.e. so 'Keep' 
doesn't update anything)

I guess the UI could be improved to make the choice of 'Keep' or 
'Current' ('Update') orthogonal to 'Use test packages?'

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-15 15:15   ` Jon Turney
@ 2017-09-15 16:53     ` Ken Brown
  2017-09-15 20:56       ` cyg Simple
  2017-09-26 14:50       ` Jon Turney
  0 siblings, 2 replies; 75+ messages in thread
From: Ken Brown @ 2017-09-15 16:53 UTC (permalink / raw)
  To: Jon Turney, cygwin-apps

On 9/15/2017 11:15 AM, Jon Turney wrote:
> On 08/09/2017 19:54, Ken Brown wrote:
>> Finally, I have a question for you, Jon: You introduced 
>> PrereqChecker::upgrade, which is true if and only if the user selects 
>> Current or Test.  I don't see why this is needed.  I've disabled the 
>> use of it and haven't noticed any ill effects.  Am I missing something?
> 
> This is supposed to be passed into SolverSolution::update() and used to 
> determine if a SOLVER_UPDATE | SOLVER_SOLVABLE_ALL task is given to the 
> solver, causing all packages to be updated (if possible) (i.e. so 'Keep' 
> doesn't update anything)

I've already arranged (by using SOLVER_LOCK) that 'Keep' doesn't update 
anything.  So I don't think we need to worry about that case.  On the 
other hand, if 'Current' or 'Test' is selected, then we already upgrade 
as appropriate in the task list sent to the solver, so I don't think 
it's necessary to send SOLVER_UPDATE | SOLVER_SOLVABLE_ALL to the solver 
in that case either.

Anyway, as I said, I've already disabled the use of 
PrereqChecker::upgrade.  More precisely, I've changed 
SolverSolution::update so that it never sends SOLVER_UPDATE | 
SOLVER_SOLVABLE_ALL.  As a result, PrereqChecker::upgrade is simply 
ignored, and everything seems to be working OK.

> I guess the UI could be improved to make the choice of 'Keep' or 
> 'Current' ('Update') orthogonal to 'Use test packages?'

I'm not seeing a problem here, but maybe I'm misunderstanding you.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-14 20:46           ` Ken Brown
@ 2017-09-15 19:24             ` Jon Turney
  2017-09-16 16:21               ` Ken Brown
  2017-12-05 14:32             ` Jon Turney
  1 sibling, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-09-15 19:24 UTC (permalink / raw)
  To: cygwin-apps

On 14/09/2017 21:46, Ken Brown wrote:
> On 9/14/2017 1:26 PM, Achim Gratz wrote:
>> Ken Brown writes:
>>> What I've been struggling with, however, is the UI.  But now that I
>>> think about it, maybe it isn't that hard.  It's just a matter of doing
>>> something reasonable if the user unchecks "Accept default problem
>>> solutions".  I'll see what I can come up with.
>>
>> Well, zypper pretty much just gives you a bunch of possible solutions
>> and asks you to select one if there is either more than one or the
>> otherwise preferred solution is blocked by a lock.  There is always one
>> "break <whatever> package by doing <stuff>" down that list.  You could
>> maybe offer something along those lines in the inevitable dialog box?
> 
> In the long run I think that's the way to go.  But implementing that is 
> more work than I feel like doing at the moment.  For now I've gone with 

Yeah, a better interface to the solution list would be nice, but 
:effort: and it's unclear how much use it would get with the loose 
requirements our current package database contains.

I'm not sure there is huge value in the current "don't install 
dependencies" option.  I don't know why anyone would want to do that, 
and it's just going to give you a broken install sometimes...

> an approach that was easier to program, more like the current setup.exe. 
>   If the solver finds problems (including missing dependencies), the 
> user has four choices on the Prerequisite page:
> 
> 1. Click Back to go back to the Chooser page, with the Pending view 
> showing the solver's default solutions.
> 
> 2. Click Next to accept the default solutions.
> 
> 3. Uncheck the "Accept default solutions" box and click Next.  If the 
> user dismisses the resulting warning, setup will go ahead and do what 
> the user requested.
> 
> 4. Cancel.
> 
> Once the inevitable remaining bugs are fixed, I think we'll have a 
> setup.exe that's better than the current one, with possibilities for 
> further UI improvements along the lines you suggested.

Thanks again for your work on this.

Can you rebase your and my patches onto master, and push to sourceware 
in a topic/libsolv branch?

After that, I think it might be useful to make a binary available for 
wider testing.

Two things I have left to look at:

- Since the distributed setup is cross-built on Linux, I need to look 
into making a RPM for the cross-built libsolv.

- Wire up the "Source:" (note capital 'S') lines in setup.ini.  These 
work in current setup versions (although we don't use it, but I'd like 
to change that).  Since the the source package might appear after the 
package in setup.ini, this seems to conflict with the current approach 
of storing the sourcepackage solvable id, which I did because searching 
the solver repo for a package by name was slow. With hindsight, this 
seems wrong.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-15 16:53     ` Ken Brown
@ 2017-09-15 20:56       ` cyg Simple
  2017-09-17 16:02         ` Ken Brown
  2017-09-26 14:50       ` Jon Turney
  1 sibling, 1 reply; 75+ messages in thread
From: cyg Simple @ 2017-09-15 20:56 UTC (permalink / raw)
  To: cygwin-apps

On 9/15/2017 12:53 PM, Ken Brown wrote:
> On 9/15/2017 11:15 AM, Jon Turney wrote:
>> On 08/09/2017 19:54, Ken Brown wrote:
>>> Finally, I have a question for you, Jon: You introduced
>>> PrereqChecker::upgrade, which is true if and only if the user selects
>>> Current or Test.  I don't see why this is needed.  I've disabled the
>>> use of it and haven't noticed any ill effects.  Am I missing something?
>>
>> This is supposed to be passed into SolverSolution::update() and used
>> to determine if a SOLVER_UPDATE | SOLVER_SOLVABLE_ALL task is given to
>> the solver, causing all packages to be updated (if possible) (i.e. so
>> 'Keep' doesn't update anything)
> 
> I've already arranged (by using SOLVER_LOCK) that 'Keep' doesn't update
> anything.  So I don't think we need to worry about that case.  On the
> other hand, if 'Current' or 'Test' is selected, then we already upgrade
> as appropriate in the task list sent to the solver, so I don't think
> it's necessary to send SOLVER_UPDATE | SOLVER_SOLVABLE_ALL to the solver
> in that case either.
> 
> Anyway, as I said, I've already disabled the use of
> PrereqChecker::upgrade.  More precisely, I've changed
> SolverSolution::update so that it never sends SOLVER_UPDATE |
> SOLVER_SOLVABLE_ALL.  As a result, PrereqChecker::upgrade is simply
> ignored, and everything seems to be working OK.
> 

Since this discussion appears to resolve around "test" versus
"production" releases would it not be better served if there were two
differing base paths, one for production and one for test?  If that
occurred then there may be more testers as what is used for production
isn't borked by a test version of some package or even Cygwin itself.

So for example C:\cygwin64 serves the production path while
C:\cygwin64test serves the test path.  This would help segregate test
from a production environment without worrying the user with needing to
revert back if something fails.

-- 
cyg Simple

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-15 19:24             ` Jon Turney
@ 2017-09-16 16:21               ` Ken Brown
  2017-09-19 12:24                 ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-16 16:21 UTC (permalink / raw)
  To: cygwin-apps

On 9/15/2017 3:24 PM, Jon Turney wrote:
> Can you rebase your and my patches onto master, and push to sourceware 
> in a topic/libsolv branch?

I've done the first part, but I don't seem to have the right permissions 
for the second:

$ git push -u origin topic/libsolv
Counting objects: 221, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (221/221), done.
Writing objects: 100% (221/221), 49.10 KiB | 1.82 MiB/s, done.
Total 221 (delta 180), reused 0 (delta 0)
fatal: Unable to create temporary file: Permission denied
error: remote unpack failed: index-pack abnormal exit
To cygwin.com:/git/cygwin-setup.git
  ! [remote rejected] topic/libsolv -> topic/libsolv (n/a (unpacker error))
error: failed to push some refs to 'cygwin.com:/git/cygwin-setup.git'

I think I used to have commit access on sourceware, but it's possible 
that my ssh key has changed since I last tried.  I can't really tell 
from the error message whether that's the issue.

Can you or Yaakov help with this?  My recollection is that when Corinna 
first gave me access, she sent me an email with a link to click on to 
submit my ssh key.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-15 20:56       ` cyg Simple
@ 2017-09-17 16:02         ` Ken Brown
  0 siblings, 0 replies; 75+ messages in thread
From: Ken Brown @ 2017-09-17 16:02 UTC (permalink / raw)
  To: cygwin-apps

On 9/15/2017 4:56 PM, cyg Simple wrote:
> On 9/15/2017 12:53 PM, Ken Brown wrote:
>> On 9/15/2017 11:15 AM, Jon Turney wrote:
>>> On 08/09/2017 19:54, Ken Brown wrote:
>>>> Finally, I have a question for you, Jon: You introduced
>>>> PrereqChecker::upgrade, which is true if and only if the user selects
>>>> Current or Test.  I don't see why this is needed.  I've disabled the
>>>> use of it and haven't noticed any ill effects.  Am I missing something?
>>>
>>> This is supposed to be passed into SolverSolution::update() and used
>>> to determine if a SOLVER_UPDATE | SOLVER_SOLVABLE_ALL task is given to
>>> the solver, causing all packages to be updated (if possible) (i.e. so
>>> 'Keep' doesn't update anything)
>>
>> I've already arranged (by using SOLVER_LOCK) that 'Keep' doesn't update
>> anything.  So I don't think we need to worry about that case.  On the
>> other hand, if 'Current' or 'Test' is selected, then we already upgrade
>> as appropriate in the task list sent to the solver, so I don't think
>> it's necessary to send SOLVER_UPDATE | SOLVER_SOLVABLE_ALL to the solver
>> in that case either.
>>
>> Anyway, as I said, I've already disabled the use of
>> PrereqChecker::upgrade.  More precisely, I've changed
>> SolverSolution::update so that it never sends SOLVER_UPDATE |
>> SOLVER_SOLVABLE_ALL.  As a result, PrereqChecker::upgrade is simply
>> ignored, and everything seems to be working OK.
>>
> 
> Since this discussion appears to resolve around "test" versus
> "production" releases

No, it's simply a technical discussion about a new implementation of 
setup's dependency checker.

> would it not be better served if there were two
> differing base paths, one for production and one for test?  If that
> occurred then there may be more testers as what is used for production
> isn't borked by a test version of some package or even Cygwin itself.
> 
> So for example C:\cygwin64 serves the production path while
> C:\cygwin64test serves the test path.  This would help segregate test
> from a production environment without worrying the user with needing to
> revert back if something fails.

I'm not sure what you're suggesting.  Anyone is free to create two 
different Cygwin installations and use one of them for testing.  In 
fact, I do that all the time.  But I don't see that this is something 
for setup to manage.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-16 16:21               ` Ken Brown
@ 2017-09-19 12:24                 ` Ken Brown
  2017-09-19 16:46                   ` Jon Turney
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-19 12:24 UTC (permalink / raw)
  To: cygwin-apps

On 9/16/2017 12:21 PM, Ken Brown wrote:
> On 9/15/2017 3:24 PM, Jon Turney wrote:
>> Can you rebase your and my patches onto master, and push to sourceware 
>> in a topic/libsolv branch?
> 
> I've done the first part, but I don't seem to have the right permissions 
> for the second:
> 
> $ git push -u origin topic/libsolv
> Counting objects: 221, done.
> Delta compression using up to 4 threads.
> Compressing objects: 100% (221/221), done.
> Writing objects: 100% (221/221), 49.10 KiB | 1.82 MiB/s, done.
> Total 221 (delta 180), reused 0 (delta 0)
> fatal: Unable to create temporary file: Permission denied
> error: remote unpack failed: index-pack abnormal exit
> To cygwin.com:/git/cygwin-setup.git
>   ! [remote rejected] topic/libsolv -> topic/libsolv (n/a (unpacker error))
> error: failed to push some refs to 'cygwin.com:/git/cygwin-setup.git'
> 
> I think I used to have commit access on sourceware, but it's possible 
> that my ssh key has changed since I last tried.  I can't really tell 
> from the error message whether that's the issue.
> 
> Can you or Yaakov help with this?  My recollection is that when Corinna 
> first gave me access, she sent me an email with a link to click on to 
> submit my ssh key.

I just checked that I can still ssh into sourceware, so I don't think 
it's an issue with my ssh key.  Maybe someone just needs to give me 
permission to write to the cygwin-setup repo.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-19 12:24                 ` Ken Brown
@ 2017-09-19 16:46                   ` Jon Turney
  2017-09-19 16:58                     ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-09-19 16:46 UTC (permalink / raw)
  To: Ken Brown; +Cc: cygwin-apps

On 19/09/2017 13:24, Ken Brown wrote:
> On 9/16/2017 12:21 PM, Ken Brown wrote:
>> On 9/15/2017 3:24 PM, Jon Turney wrote:
>>> Can you rebase your and my patches onto master, and push to 
>>> sourceware in a topic/libsolv branch?
>>
>> I've done the first part, but I don't seem to have the right 
>> permissions for the second:
>>
>> $ git push -u origin topic/libsolv
>> Counting objects: 221, done.
>> Delta compression using up to 4 threads.
>> Compressing objects: 100% (221/221), done.
>> Writing objects: 100% (221/221), 49.10 KiB | 1.82 MiB/s, done.
>> Total 221 (delta 180), reused 0 (delta 0)
>> fatal: Unable to create temporary file: Permission denied
>> error: remote unpack failed: index-pack abnormal exit
>> To cygwin.com:/git/cygwin-setup.git
>>   ! [remote rejected] topic/libsolv -> topic/libsolv (n/a (unpacker 
>> error))
>> error: failed to push some refs to 'cygwin.com:/git/cygwin-setup.git'
>>
>> I think I used to have commit access on sourceware, but it's possible 
>> that my ssh key has changed since I last tried.  I can't really tell 
>> from the error message whether that's the issue.
>>
>> Can you or Yaakov help with this?  My recollection is that when 
>> Corinna first gave me access, she sent me an email with a link to 
>> click on to submit my ssh key.
> 
> I just checked that I can still ssh into sourceware, so I don't think 
> it's an issue with my ssh key.  Maybe someone just needs to give me 
> permission to write to the cygwin-setup repo.

Yes, additional permissions are required since it's under cygwin-apps.

You should be good now.




^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-19 16:46                   ` Jon Turney
@ 2017-09-19 16:58                     ` Ken Brown
  0 siblings, 0 replies; 75+ messages in thread
From: Ken Brown @ 2017-09-19 16:58 UTC (permalink / raw)
  To: Jon Turney; +Cc: cygwin-apps

On 9/19/2017 12:46 PM, Jon Turney wrote:
> On 19/09/2017 13:24, Ken Brown wrote:
>> On 9/16/2017 12:21 PM, Ken Brown wrote:
>>> On 9/15/2017 3:24 PM, Jon Turney wrote:
>>>> Can you rebase your and my patches onto master, and push to 
>>>> sourceware in a topic/libsolv branch?
>>>
>>> I've done the first part, but I don't seem to have the right 
>>> permissions for the second:
>>>
>>> $ git push -u origin topic/libsolv
>>> Counting objects: 221, done.
>>> Delta compression using up to 4 threads.
>>> Compressing objects: 100% (221/221), done.
>>> Writing objects: 100% (221/221), 49.10 KiB | 1.82 MiB/s, done.
>>> Total 221 (delta 180), reused 0 (delta 0)
>>> fatal: Unable to create temporary file: Permission denied
>>> error: remote unpack failed: index-pack abnormal exit
>>> To cygwin.com:/git/cygwin-setup.git
>>>   ! [remote rejected] topic/libsolv -> topic/libsolv (n/a (unpacker 
>>> error))
>>> error: failed to push some refs to 'cygwin.com:/git/cygwin-setup.git'
>>>
>>> I think I used to have commit access on sourceware, but it's possible 
>>> that my ssh key has changed since I last tried.  I can't really tell 
>>> from the error message whether that's the issue.
>>>
>>> Can you or Yaakov help with this?  My recollection is that when 
>>> Corinna first gave me access, she sent me an email with a link to 
>>> click on to submit my ssh key.
>>
>> I just checked that I can still ssh into sourceware, so I don't think 
>> it's an issue with my ssh key.  Maybe someone just needs to give me 
>> permission to write to the cygwin-setup repo.
> 
> Yes, additional permissions are required since it's under cygwin-apps.
> 
> You should be good now.

Thanks.  I've pushed the branch.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-15 16:53     ` Ken Brown
  2017-09-15 20:56       ` cyg Simple
@ 2017-09-26 14:50       ` Jon Turney
  2017-09-26 16:07         ` Ken Brown
  1 sibling, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-09-26 14:50 UTC (permalink / raw)
  To: Ken Brown, cygwin-apps

On 15/09/2017 17:53, Ken Brown wrote:
> On 9/15/2017 11:15 AM, Jon Turney wrote:
>> On 08/09/2017 19:54, Ken Brown wrote:
>>> Finally, I have a question for you, Jon: You introduced 
>>> PrereqChecker::upgrade, which is true if and only if the user selects 
>>> Current or Test.  I don't see why this is needed.  I've disabled the 
>>> use of it and haven't noticed any ill effects.  Am I missing something?
>>
>> This is supposed to be passed into SolverSolution::update() and used 
>> to determine if a SOLVER_UPDATE | SOLVER_SOLVABLE_ALL task is given to 
>> the solver, causing all packages to be updated (if possible) (i.e. so 
>> 'Keep' doesn't update anything)
> 
> I've already arranged (by using SOLVER_LOCK) that 'Keep' doesn't update 
> anything.  So I don't think we need to worry about that case.  On the 
> other hand, if 'Current' or 'Test' is selected, then we already upgrade 
> as appropriate in the task list sent to the solver, so I don't think 
> it's necessary to send SOLVER_UPDATE | SOLVER_SOLVABLE_ALL to the solver 
> in that case either.

Yeah, I see. Sigh.

If we select 'curr', then the latest version of all installed packages 
is selected by the picker and will be shown in the pending view, and 
gets fed into the solver.

I guess this is technically wrong: really we should ask the solver to do 
SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, which will come up with a solution 
which updates all installed packages to the latest possible version, 
subject to any other constraints which exist.

I suspect there's no difference between these two at the moment, though.

Not sure how to do this properly, though.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-26 14:50       ` Jon Turney
@ 2017-09-26 16:07         ` Ken Brown
  2017-09-27 19:14           ` Jon Turney
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-26 16:07 UTC (permalink / raw)
  To: Jon Turney, cygwin-apps

On 9/26/2017 10:50 AM, Jon Turney wrote:
> On 15/09/2017 17:53, Ken Brown wrote:
>> On 9/15/2017 11:15 AM, Jon Turney wrote:
>>> On 08/09/2017 19:54, Ken Brown wrote:
>>>> Finally, I have a question for you, Jon: You introduced 
>>>> PrereqChecker::upgrade, which is true if and only if the user 
>>>> selects Current or Test.  I don't see why this is needed.  I've 
>>>> disabled the use of it and haven't noticed any ill effects.  Am I 
>>>> missing something?
>>>
>>> This is supposed to be passed into SolverSolution::update() and used 
>>> to determine if a SOLVER_UPDATE | SOLVER_SOLVABLE_ALL task is given 
>>> to the solver, causing all packages to be updated (if possible) (i.e. 
>>> so 'Keep' doesn't update anything)
>>
>> I've already arranged (by using SOLVER_LOCK) that 'Keep' doesn't 
>> update anything.  So I don't think we need to worry about that case.  
>> On the other hand, if 'Current' or 'Test' is selected, then we already 
>> upgrade as appropriate in the task list sent to the solver, so I don't 
>> think it's necessary to send SOLVER_UPDATE | SOLVER_SOLVABLE_ALL to 
>> the solver in that case either.
> 
> Yeah, I see. Sigh.
> 
> If we select 'curr', then the latest version of all installed packages 
> is selected by the picker and will be shown in the pending view, and 
> gets fed into the solver.
> 
> I guess this is technically wrong: really we should ask the solver to do 
> SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, which will come up with a solution 
> which updates all installed packages to the latest possible version, 
> subject to any other constraints which exist.
> 
> I suspect there's no difference between these two at the moment, though.
> 
> Not sure how to do this properly, though.

One possibility is that we feed all our information to the solver before 
going to the chooser page.  We could then present the solver's initial 
solution in the pending view as the first thing the user sees.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-26 16:07         ` Ken Brown
@ 2017-09-27 19:14           ` Jon Turney
  2017-09-27 20:33             ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-09-27 19:14 UTC (permalink / raw)
  To: Ken Brown; +Cc: cygwin-apps

On 26/09/2017 17:06, Ken Brown wrote:
> On 9/26/2017 10:50 AM, Jon Turney wrote:
>> On 15/09/2017 17:53, Ken Brown wrote:
>>> On 9/15/2017 11:15 AM, Jon Turney wrote:
[...]
>> If we select 'curr', then the latest version of all installed packages 
>> is selected by the picker and will be shown in the pending view, and 
>> gets fed into the solver.
>>
>> I guess this is technically wrong: really we should ask the solver to 
>> do SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, which will come up with a 
>> solution which updates all installed packages to the latest possible 
>> version, subject to any other constraints which exist.
>>
>> I suspect there's no difference between these two at the moment, though.
>>
>> Not sure how to do this properly, though.
> 
> One possibility is that we feed all our information to the solver before 
> going to the chooser page.  We could then present the solver's initial 
> solution in the pending view as the first thing the user sees.

Yes, this sounds about right.

Thanks to your work we kind of have a bidirectional conversion between 
packagedb pick/desired state and a SolverTransactionList now, so this 
should be possible.

(Although it should be done when the state of the 'Keep' or 'Current' 
control changes - note that changing this setting clears any manual 
picks currently. [and there's a mechanism to apply the initial state of 
that control initially])

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-27 19:14           ` Jon Turney
@ 2017-09-27 20:33             ` Ken Brown
  2017-09-29 17:38               ` Jon Turney
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-09-27 20:33 UTC (permalink / raw)
  To: Jon Turney; +Cc: cygwin-apps

On 9/27/2017 3:14 PM, Jon Turney wrote:
> On 26/09/2017 17:06, Ken Brown wrote:
>> On 9/26/2017 10:50 AM, Jon Turney wrote:
>>> On 15/09/2017 17:53, Ken Brown wrote:
>>>> On 9/15/2017 11:15 AM, Jon Turney wrote:
> [...]
>>> If we select 'curr', then the latest version of all installed 
>>> packages is selected by the picker and will be shown in the pending 
>>> view, and gets fed into the solver.
>>>
>>> I guess this is technically wrong: really we should ask the solver to 
>>> do SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, which will come up with a 
>>> solution which updates all installed packages to the latest possible 
>>> version, subject to any other constraints which exist.
>>>
>>> I suspect there's no difference between these two at the moment, though.
>>>
>>> Not sure how to do this properly, though.
>>
>> One possibility is that we feed all our information to the solver 
>> before going to the chooser page.  We could then present the solver's 
>> initial solution in the pending view as the first thing the user sees.
> 
> Yes, this sounds about right.
> 
> Thanks to your work we kind of have a bidirectional conversion between 
> packagedb pick/desired state and a SolverTransactionList now, so this 
> should be possible.
> 
> (Although it should be done when the state of the 'Keep' or 'Current' 
> control changes - note that changing this setting clears any manual 
> picks currently. [and there's a mechanism to apply the initial state of 
> that control initially])

OK, I'll work on this.  I'm traveling at the moment, but I should be 
able to do it in a couple weeks.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-27 20:33             ` Ken Brown
@ 2017-09-29 17:38               ` Jon Turney
  2017-09-29 20:34                 ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-09-29 17:38 UTC (permalink / raw)
  To: Ken Brown; +Cc: cygwin-apps

On 27/09/2017 21:33, Ken Brown wrote:
> On 9/27/2017 3:14 PM, Jon Turney wrote:
>> On 26/09/2017 17:06, Ken Brown wrote:
>>> On 9/26/2017 10:50 AM, Jon Turney wrote:
>>>> On 15/09/2017 17:53, Ken Brown wrote:
>>>>> On 9/15/2017 11:15 AM, Jon Turney wrote:
>> [...]
>>>> If we select 'curr', then the latest version of all installed 
>>>> packages is selected by the picker and will be shown in the pending 
>>>> view, and gets fed into the solver.
>>>>
>>>> I guess this is technically wrong: really we should ask the solver 
>>>> to do SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, which will come up with a 
>>>> solution which updates all installed packages to the latest possible 
>>>> version, subject to any other constraints which exist.
>>>>
>>>> I suspect there's no difference between these two at the moment, 
>>>> though.
>>>>
>>>> Not sure how to do this properly, though.
>>>
>>> One possibility is that we feed all our information to the solver 
>>> before going to the chooser page.  We could then present the solver's 
>>> initial solution in the pending view as the first thing the user sees.
>>
>> Yes, this sounds about right.
>>
>> Thanks to your work we kind of have a bidirectional conversion between 
>> packagedb pick/desired state and a SolverTransactionList now, so this 
>> should be possible.
>>
>> (Although it should be done when the state of the 'Keep' or 'Current' 
>> control changes - note that changing this setting clears any manual 
>> picks currently. [and there's a mechanism to apply the initial state 
>> of that control initially])
> 
> OK, I'll work on this.  I'm traveling at the moment, but I should be 
> able to do it in a couple weeks.

Thanks, but that was more me thinking out loud.  Since it seems pretty 
straightforward, I've written this change.

I've rebased the topic/libsolv branch, with the following additional 
changes:

* Applied your unrelated change to .gitignore to master
* Dropped a couple patches of mine which weren't supposed to escape yet.
* Squashed your "fix typo" patches into the patch they fix (I'm not sure 
how I managed to make that typo in bootstrap.sh! :))
* Rather than add a parameter to SolvableVersion::depends() to make it 
return obsoletes, I've factored out the key-to-deplist code as a helper 
function, and added SolvableVersion::obsoletes(). (There may be other 
deplist attributes in future, so this seems the sane way to do it.)
* Added handling of Source: lines in setup.ini

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-29 17:38               ` Jon Turney
@ 2017-09-29 20:34                 ` Ken Brown
  2017-10-02 14:07                   ` Jon Turney
  2017-10-10 11:18                   ` Ken Brown
  0 siblings, 2 replies; 75+ messages in thread
From: Ken Brown @ 2017-09-29 20:34 UTC (permalink / raw)
  To: Jon Turney; +Cc: cygwin-apps

On 9/29/2017 1:38 PM, Jon Turney wrote:
> On 27/09/2017 21:33, Ken Brown wrote:
>> On 9/27/2017 3:14 PM, Jon Turney wrote:
>>> On 26/09/2017 17:06, Ken Brown wrote:
>>>> On 9/26/2017 10:50 AM, Jon Turney wrote:
>>>>> On 15/09/2017 17:53, Ken Brown wrote:
>>>>>> On 9/15/2017 11:15 AM, Jon Turney wrote:
>>> [...]
>>>>> If we select 'curr', then the latest version of all installed 
>>>>> packages is selected by the picker and will be shown in the pending 
>>>>> view, and gets fed into the solver.
>>>>>
>>>>> I guess this is technically wrong: really we should ask the solver 
>>>>> to do SOLVER_UPDATE | SOLVER_SOLVABLE_ALL, which will come up with 
>>>>> a solution which updates all installed packages to the latest 
>>>>> possible version, subject to any other constraints which exist.
>>>>>
>>>>> I suspect there's no difference between these two at the moment, 
>>>>> though.
>>>>>
>>>>> Not sure how to do this properly, though.
>>>>
>>>> One possibility is that we feed all our information to the solver 
>>>> before going to the chooser page.  We could then present the 
>>>> solver's initial solution in the pending view as the first thing the 
>>>> user sees.
>>>
>>> Yes, this sounds about right.
>>>
>>> Thanks to your work we kind of have a bidirectional conversion 
>>> between packagedb pick/desired state and a SolverTransactionList now, 
>>> so this should be possible.
>>>
>>> (Although it should be done when the state of the 'Keep' or 'Current' 
>>> control changes - note that changing this setting clears any manual 
>>> picks currently. [and there's a mechanism to apply the initial state 
>>> of that control initially])
>>
>> OK, I'll work on this.  I'm traveling at the moment, but I should be 
>> able to do it in a couple weeks.
> 
> Thanks, but that was more me thinking out loud.  Since it seems pretty 
> straightforward, I've written this change.

Even better!  I'll resume my testing after I return.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-29 20:34                 ` Ken Brown
@ 2017-10-02 14:07                   ` Jon Turney
  2017-10-02 15:17                     ` Marco Atzeri
  2017-10-10 11:18                   ` Ken Brown
  1 sibling, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-10-02 14:07 UTC (permalink / raw)
  To: cygwin-apps

On 29/09/2017 21:33, Ken Brown wrote:
> On 9/29/2017 1:38 PM, Jon Turney wrote:
>>
>> Thanks, but that was more me thinking out loud.  Since it seems pretty 
>> straightforward, I've written this change.
> 
> Even better!  I'll resume my testing after I return.

If anyone else would like to give this some testing, I built:

https://cygwin.com/setup/setup-2.881-38-g6a01c5.x86.exe
https://cygwin.com/setup/setup-2.881-38-g6a01c5.x86_64.exe

These are cross-built on Fedora, and require mingw{32,64}-libsolv-static 
in addition to the usual cross-packages, which can be built using the 
RPM spec at [1], or installed from this COPR [2]

[1] https://github.com/jon-turney/mingw-libsolv-rpm
[2] https://copr.fedorainfracloud.org/coprs/jturney/mingw-libsolv/

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-10-02 14:07                   ` Jon Turney
@ 2017-10-02 15:17                     ` Marco Atzeri
  2017-10-04 14:43                       ` Jon Turney
  0 siblings, 1 reply; 75+ messages in thread
From: Marco Atzeri @ 2017-10-02 15:17 UTC (permalink / raw)
  To: cygwin-apps

On 02/10/2017 16:07, Jon Turney wrote:

> https://cygwin.com/setup/setup-2.881-38-g6a01c5.x86_64.exe
> 

interesting question about precedence as it tries to install
cscope-15.8.0.1-2 and speex 1.2.0-2
that is right for speex but wrong for cscope following the setup.ini
and the age of the files

version: 15.8b-1
[prev]
version: 15.8.0.1-2

speex
version: 1.2.0-2
[prev]
version: 1.2rc1-1

It seems previous setup was doing the opposite precedence
that is wrong for speex and right for cscope

Regards
Marco

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-10-02 15:17                     ` Marco Atzeri
@ 2017-10-04 14:43                       ` Jon Turney
  0 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-10-04 14:43 UTC (permalink / raw)
  To: cygwin-apps

On 02/10/2017 16:17, Marco Atzeri wrote:
> On 02/10/2017 16:07, Jon Turney wrote:
> 
>> https://cygwin.com/setup/setup-2.881-38-g6a01c5.x86_64.exe

Thanks for testing.

> interesting question about precedence as it tries to install
> cscope-15.8.0.1-2 and speex 1.2.0-2

So, this is change in behaviour. When asked to upgrade, setup will now 
always install the version which libsolv thinks has the highest version.

These versions are higher, because numeric sequences are considered 
higher than alphabetic sequences (i.e. '0' > 'b', '0' > 'rc').

Trying to control which version which gets upgraded to by setting the 
curr: value in setup.ini isn't actually always working, currently 
(perhaps this will be the subject of a whole other email...)

> that is right for speex but wrong for cscope following the setup.ini
> and the age of the files
> 
> version: 15.8b-1
> [prev]
> version: 15.8.0.1-2

So, this is actually a bug in the version sorting which calm does

(it was comparing the alphabetic sequence 'b' against the separator '.', 
rather than against the numeric sequence '0')

> speex
> version: 1.2.0-2
> [prev]
> version: 1.2rc1-1

And this is the right way round currently, only because it was noticed 
it wasn't sorted correctly when uploaded, and an override.hint added.

> It seems previous setup was doing the opposite precedence
> that is wrong for speex and right for cscope

I'll just point out that these two packages were made by people with 
incompatible ideas about how versions order.

I'm not going down the path of trying to support that, as that way 
madness lies...

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-29 20:34                 ` Ken Brown
  2017-10-02 14:07                   ` Jon Turney
@ 2017-10-10 11:18                   ` Ken Brown
  2017-10-10 15:49                     ` Jon Turney
  2017-10-17 12:45                     ` Ken Brown
  1 sibling, 2 replies; 75+ messages in thread
From: Ken Brown @ 2017-10-10 11:18 UTC (permalink / raw)
  To: cygwin-apps

On 9/29/2017 4:33 PM, Ken Brown wrote:
> I'll resume my testing after I return.

I've just started testing (based on the current HEAD of topic/libsolv), 
and so far everything looks good.  My only suggestion is a cosmetic one. 
  Now that you've made "Test" a checkbox, independent of the radio 
buttons "Keep/Current/Sync", users might be confused by the fact that 
"Current" remains selected when "Test" is checked.  In fact, the 
"Current" button has a different meaning now than it used to have. 
Maybe the "Current" label should be changed to "Best", in keeping with 
its new meaning (and tooltip).

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-10-10 11:18                   ` Ken Brown
@ 2017-10-10 15:49                     ` Jon Turney
  2017-10-17 12:45                     ` Ken Brown
  1 sibling, 0 replies; 75+ messages in thread
From: Jon Turney @ 2017-10-10 15:49 UTC (permalink / raw)
  To: cygwin-apps

On 10/10/2017 12:18, Ken Brown wrote:
> I've just started testing (based on the current HEAD of topic/libsolv), 
> and so far everything looks good.  My only suggestion is a cosmetic one. 
>   Now that you've made "Test" a checkbox, independent of the radio 
> buttons "Keep/Current/Sync", users might be confused by the fact that 
> "Current" remains selected when "Test" is checked.

Initially, I didn't want to make any UI change here, but it made it a 
lot easier to think about how map the command line options to solver flags.

But yeah, I wasn't sure if I should change this or not.  I kept it the 
same just to keep the default the same, but that doesn't seem a very 
good reason.

"Test" should probably be something a little less terse, as well.

 > In fact, the
> "Current" button has a different meaning now than it used to have. Maybe 
> the "Current" label should be changed to "Best", in keeping with its new 
> meaning (and tooltip).

People seem to sometimes think "Current" means (something like) "Sync", 
when it fact it is (something like) "Best", so perhaps that's another 
reason why a change of name is a good idea.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-10-10 11:18                   ` Ken Brown
  2017-10-10 15:49                     ` Jon Turney
@ 2017-10-17 12:45                     ` Ken Brown
  2017-10-17 18:47                       ` Jon Turney
  1 sibling, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-10-17 12:45 UTC (permalink / raw)
  To: cygwin-apps

[-- Attachment #1: Type: text/plain, Size: 739 bytes --]

On 10/10/2017 7:18 AM, Ken Brown wrote:
> On 9/29/2017 4:33 PM, Ken Brown wrote:
>> I'll resume my testing after I return.
> 
> I've just started testing (based on the current HEAD of topic/libsolv), 
> and so far everything looks good.

I came across a situation where a SolvableVersion method was being 
called on a trivial object (with pool and id both 0).  This caused a 
crash when pool_id2solvable(pool, id) was called and pool was 
dereferenced.  There's probably a bug that led to this situation.  [It 
involved a local install in which a package was listed in two different 
setup.ini files, but the tarballs existed only in one.]  I plan to 
investigate this further.  But in any case, we shouldn't crash.  Patch 
attached.

Ken

[-- Attachment #2: 0001-Avoid-dereferencing-NULL-pointers.patch --]
[-- Type: text/plain, Size: 2684 bytes --]

From f3b3c60ed473a1ef4e5b1ae5fcd1bfc46a6210fb Mon Sep 17 00:00:00 2001
From: Ken Brown <kbrown@cornell.edu>
Date: Tue, 17 Oct 2017 08:12:48 -0400
Subject: [PATCH] Avoid dereferencing NULL pointers

The libsolv function pool_id2solvable unconditionally dereferences its
first argument ('pool').  Callers must check that this argument is
non-NULL to avoid crashes.
---
 libsolv.cc | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/libsolv.cc b/libsolv.cc
index 78e73a8..3a244d4 100644
--- a/libsolv.cc
+++ b/libsolv.cc
@@ -75,6 +75,8 @@ RelId2Operator(Id id)
 const std::string
 SolvableVersion::Name () const
 {
+  if (!pool)
+    return "";
   Solvable *solvable = pool_id2solvable(pool, id);
   return std::string(pool_id2str(pool, solvable->name));
 }
@@ -82,6 +84,8 @@ SolvableVersion::Name () const
 const std::string
 SolvableVersion::Canonical_version() const
 {
+  if (!pool)
+    return "";
   Solvable *solvable = pool_id2solvable(pool, id);
   return std::string(pool_id2str(pool, solvable->evr));
 }
@@ -89,6 +93,8 @@ SolvableVersion::Canonical_version() const
 package_type_t
 SolvableVersion::Type () const
 {
+  if (!pool)
+    return package_binary;
   Solvable *solvable = pool_id2solvable(pool, id);
   if (solvable->arch == ARCH_SRC)
     return package_source;
@@ -112,6 +118,9 @@ SolvableVersion::obsoletes() const
 const PackageDepends
 SolvableVersion::deplist(Id keyname) const
 {
+  static PackageDepends empty_package;
+  if (!pool)
+    return empty_package;
   Solvable *solvable = pool_id2solvable(pool, id);
 
   Queue q;
@@ -147,13 +156,14 @@ SolvableVersion::deplist(Id keyname) const
     }
 
   // otherwise, return an empty depends list
-  static PackageDepends empty_package;
   return empty_package;
 }
 
 const std::string
 SolvableVersion::SDesc () const
 {
+  if (!pool)
+    return "";
   Solvable *solvable = pool_id2solvable(pool, id);
   const char *sdesc = repo_lookup_str(solvable->repo, id, SOLVABLE_SUMMARY);
   return sdesc;
@@ -197,6 +207,8 @@ SolvableVersion::sourcePackage () const
 void
 SolvableVersion::fixup_spkg_id (SolvableVersion spkg_id) const
 {
+  if (!pool)
+    return;
   Solvable *solvable = pool_id2solvable(pool, id);
   Repodata *data = repo_last_repodata(solvable->repo);
   Id handle = id;
@@ -237,6 +249,8 @@ SolvableVersion::accessible () const
 package_stability_t
 SolvableVersion::Stability () const
 {
+  if (!pool)
+    return TRUST_UNKNOWN;
   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);
-- 
2.14.2


^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-10-17 12:45                     ` Ken Brown
@ 2017-10-17 18:47                       ` Jon Turney
  2017-10-18 15:28                         ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Jon Turney @ 2017-10-17 18:47 UTC (permalink / raw)
  To: cygwin-apps

On 17/10/2017 13:44, Ken Brown wrote:
> On 10/10/2017 7:18 AM, Ken Brown wrote:
>> On 9/29/2017 4:33 PM, Ken Brown wrote:
>>> I'll resume my testing after I return.
>>
>> I've just started testing (based on the current HEAD of 
>> topic/libsolv), and so far everything looks good.
> 
> I came across a situation where a SolvableVersion method was being 
> called on a trivial object (with pool and id both 0).  This caused a 
> crash when pool_id2solvable(pool, id) was called and pool was 
> dereferenced.  There's probably a bug that led to this situation.  [It 
> involved a local install in which a package was listed in two different 
> setup.ini files, but the tarballs existed only in one.]  I plan to 
> investigate this further.  But in any case, we shouldn't crash.  Patch 
> attached.

I thought about putting this in, but decided against it as it would 
probably catch some mistakes I had made...

But yeah, for production use, I think not crashing is probably a good 
idea :).  Although I guess we might want asserts or something, if we 
think these cases shouldn't be happening.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-10-17 18:47                       ` Jon Turney
@ 2017-10-18 15:28                         ` Ken Brown
  2017-10-18 15:57                           ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-10-18 15:28 UTC (permalink / raw)
  To: cygwin-apps

On 10/17/2017 2:46 PM, Jon Turney wrote:
> On 17/10/2017 13:44, Ken Brown wrote:
>> On 10/10/2017 7:18 AM, Ken Brown wrote:
>>> On 9/29/2017 4:33 PM, Ken Brown wrote:
>>>> I'll resume my testing after I return.
>>>
>>> I've just started testing (based on the current HEAD of 
>>> topic/libsolv), and so far everything looks good.
>>
>> I came across a situation where a SolvableVersion method was being 
>> called on a trivial object (with pool and id both 0).  This caused a 
>> crash when pool_id2solvable(pool, id) was called and pool was 
>> dereferenced.  There's probably a bug that led to this situation.  [It 
>> involved a local install in which a package was listed in two 
>> different setup.ini files, but the tarballs existed only in one.]  I 
>> plan to investigate this further.  But in any case, we shouldn't 
>> crash.  Patch attached.
> 
> I thought about putting this in, but decided against it as it would 
> probably catch some mistakes I had made...
> 
> But yeah, for production use, I think not crashing is probably a good 
> idea :).  Although I guess we might want asserts or something, if we 
> think these cases shouldn't be happening.

Here's the situation where I got the crash: Package A is installed and 
requires B.  setup tries to install B, but the install fails for some 
reason.  During the postinstall phase, we're scanning the dependencies 
of A and we call checkForInstalled to see if B is installed.  This ends 
up calling PackageSpecification::satisfies(aPackage), with aPackage 
being the empty package (pool == NULL, id == 0) because B is not 
installed.  A call to aPackage.Name() then causes the crash.

I think this sequence of calls is reasonable.  Name() is part of the 
public interface to SolvableVersion, so we should be able to call Name() 
on any package without a crash or assertion violation.  In the case of 
the empty package, the empty string is a reasonable return value.

Similar considerations apply to the other public member functions of 
SolvableVersion.  So my inclination is to go with something like my 
patch rather than changing all callers.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-10-18 15:28                         ` Ken Brown
@ 2017-10-18 15:57                           ` Ken Brown
  0 siblings, 0 replies; 75+ messages in thread
From: Ken Brown @ 2017-10-18 15:57 UTC (permalink / raw)
  To: cygwin-apps

[-- Attachment #1: Type: text/plain, Size: 452 bytes --]

On 10/18/2017 11:28 AM, Ken Brown wrote:
> Similar considerations apply to the other public member functions of 
> SolvableVersion.  So my inclination is to go with something like my 
> patch...

...with perhaps one tweak.  Maybe we should test 'id' rather than 
'pool', since id being 0 is what's used elsewhere to characterize the 
empty package.  And if id != 0 but pool == 0, there's a bug that we want 
to catch.

Revised patch attached.

Ken


[-- Attachment #2: 0001-Extend-the-SolvableVersion-member-functions-to-the-e.patch --]
[-- Type: text/plain, Size: 2715 bytes --]

From 948db09180765d89639b63e37a98d3806bf199d5 Mon Sep 17 00:00:00 2001
From: Ken Brown <kbrown@cornell.edu>
Date: Tue, 17 Oct 2017 08:12:48 -0400
Subject: [PATCH] Extend the SolvableVersion member functions to the empty
 package

Currently some of these functions cause crashes when the package is
empty because the libsolv function pool_id2solvable unconditionally
dereferences its first argument ('pool').
---
 libsolv.cc | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/libsolv.cc b/libsolv.cc
index 78e73a8..289f19c 100644
--- a/libsolv.cc
+++ b/libsolv.cc
@@ -75,6 +75,8 @@ RelId2Operator(Id id)
 const std::string
 SolvableVersion::Name () const
 {
+  if (!id)
+    return "";
   Solvable *solvable = pool_id2solvable(pool, id);
   return std::string(pool_id2str(pool, solvable->name));
 }
@@ -82,6 +84,8 @@ SolvableVersion::Name () const
 const std::string
 SolvableVersion::Canonical_version() const
 {
+  if (!id)
+    return "";
   Solvable *solvable = pool_id2solvable(pool, id);
   return std::string(pool_id2str(pool, solvable->evr));
 }
@@ -89,6 +93,8 @@ SolvableVersion::Canonical_version() const
 package_type_t
 SolvableVersion::Type () const
 {
+  if (!id)
+    return package_binary;
   Solvable *solvable = pool_id2solvable(pool, id);
   if (solvable->arch == ARCH_SRC)
     return package_source;
@@ -112,6 +118,9 @@ SolvableVersion::obsoletes() const
 const PackageDepends
 SolvableVersion::deplist(Id keyname) const
 {
+  static PackageDepends empty_package;
+  if (!id)
+    return empty_package;
   Solvable *solvable = pool_id2solvable(pool, id);
 
   Queue q;
@@ -147,13 +156,14 @@ SolvableVersion::deplist(Id keyname) const
     }
 
   // otherwise, return an empty depends list
-  static PackageDepends empty_package;
   return empty_package;
 }
 
 const std::string
 SolvableVersion::SDesc () const
 {
+  if (!id)
+    return "";
   Solvable *solvable = pool_id2solvable(pool, id);
   const char *sdesc = repo_lookup_str(solvable->repo, id, SOLVABLE_SUMMARY);
   return sdesc;
@@ -197,6 +207,8 @@ SolvableVersion::sourcePackage () const
 void
 SolvableVersion::fixup_spkg_id (SolvableVersion spkg_id) const
 {
+  if (!id)
+    return;
   Solvable *solvable = pool_id2solvable(pool, id);
   Repodata *data = repo_last_repodata(solvable->repo);
   Id handle = id;
@@ -237,6 +249,8 @@ SolvableVersion::accessible () const
 package_stability_t
 SolvableVersion::Stability () const
 {
+  if (!id)
+    return TRUST_UNKNOWN;
   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);
-- 
2.14.2


^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-06  2:52         ` Ken Brown
@ 2017-11-23 18:10           ` Jon Turney
  2017-11-23 20:32             ` Ken Brown
  2017-11-23 20:54             ` Achim Gratz
  0 siblings, 2 replies; 75+ messages in thread
From: Jon Turney @ 2017-11-23 18:10 UTC (permalink / raw)
  To: cygwin-apps

On 06/09/2017 03:52, Ken Brown wrote:
> On 9/5/2017 2:40 PM, Achim Gratz wrote:
>> Jon Turney writes:
>>> 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)
>>
>> At least for zypper, which is based on the same library:
>>
>> You can give each repo a priority and it will pick the package from the
>> one with the highest priority, even if another repo has a higher
>> version.
> 
> Yes, that works.

It seems that, when test versions are enabled, a test version is 
preferred over a non-test version with a higher version

I guess this is a consequence of:

> void
> SolverPool::use_test_packages(bool use_test_packages)
> {
>   // Give repos containing test packages higher priority than normal
>   // if wanted, otherwise lower priority.
>   SolvRepo::priority_t p = use_test_packages ? SolvRepo::priorityHigh : SolvRepo::priorityLow;

I wonder if we just want to give test repos priorityNormal when they are 
wanted?

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-11-23 18:10           ` Jon Turney
@ 2017-11-23 20:32             ` Ken Brown
  2017-11-23 20:54             ` Achim Gratz
  1 sibling, 0 replies; 75+ messages in thread
From: Ken Brown @ 2017-11-23 20:32 UTC (permalink / raw)
  To: cygwin-apps

On 11/23/2017 1:10 PM, Jon Turney wrote:
> On 06/09/2017 03:52, Ken Brown wrote:
>> On 9/5/2017 2:40 PM, Achim Gratz wrote:
>>> Jon Turney writes:
>>>> 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)
>>>
>>> At least for zypper, which is based on the same library:
>>>
>>> You can give each repo a priority and it will pick the package from the
>>> one with the highest priority, even if another repo has a higher
>>> version.
>>
>> Yes, that works.
> 
> It seems that, when test versions are enabled, a test version is 
> preferred over a non-test version with a higher version
> 
> I guess this is a consequence of:
> 
>> void
>> SolverPool::use_test_packages(bool use_test_packages)
>> {
>>   // Give repos containing test packages higher priority than normal
>>   // if wanted, otherwise lower priority.
>>   SolvRepo::priority_t p = use_test_packages ? SolvRepo::priorityHigh 
>> : SolvRepo::priorityLow;
> 
> I wonder if we just want to give test repos priorityNormal when they are 
> wanted?

Yes, that sounds right to me.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-11-23 18:10           ` Jon Turney
  2017-11-23 20:32             ` Ken Brown
@ 2017-11-23 20:54             ` Achim Gratz
  1 sibling, 0 replies; 75+ messages in thread
From: Achim Gratz @ 2017-11-23 20:54 UTC (permalink / raw)
  To: cygwin-apps

Jon Turney writes:
>>> You can give each repo a priority and it will pick the package from the
>>> one with the highest priority, even if another repo has a higher
>>> version.
>>
>> Yes, that works.
>
> It seems that, when test versions are enabled, a test version is
> preferred over a non-test version with a higher version
>
> I guess this is a consequence of:
>
>> void
>> SolverPool::use_test_packages(bool use_test_packages)
>> {
>>   // Give repos containing test packages higher priority than normal
>>   // if wanted, otherwise lower priority.
>>   SolvRepo::priority_t p = use_test_packages ? SolvRepo::priorityHigh : SolvRepo::priorityLow;
>
> I wonder if we just want to give test repos priorityNormal when they
> are wanted?

It really depends on what you are trying to achieve (and assuming you always
work on all enabled repositories instead of specifically selecting one).

1) To chose the highest version available among a set of repositories
(these need to be compatible in their versioning), then the priorities
of all those repos need to be the same.

2) To prefer one repo over the other regardless of versioning
(versioning need not be compatible), then raise the priority of the
preferred repo.

We currently can't use the difference between an update and
distribution upgrade in setup since we would have to record which repo
each package was originally installed from.  If we had that, an update
would keep the repository selection (even if another repo has a higher
version) and a distribution upgrade would select the highest version,
switching repositories as necessary.


Regards,
Achim.
-- 
+<[Q+ Matrix-12 WAVE#46+305 Neuron microQkb Andromeda XTk Blofeld]>+

Factory and User Sound Singles for Waldorf rackAttack:
http://Synth.Stromeko.net/Downloads.html#WaldorfSounds

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-09-14 20:46           ` Ken Brown
  2017-09-15 19:24             ` Jon Turney
@ 2017-12-05 14:32             ` Jon Turney
  2017-12-05 17:36               ` Ken Brown
  2017-12-13 17:31               ` Ken Brown
  1 sibling, 2 replies; 75+ messages in thread
From: Jon Turney @ 2017-12-05 14:32 UTC (permalink / raw)
  To: cygwin-apps

On 14/09/2017 21:46, Ken Brown wrote:
> On 9/14/2017 1:26 PM, Achim Gratz wrote:
>> Ken Brown writes:
>>> What I've been struggling with, however, is the UI.  But now that I
>>> think about it, maybe it isn't that hard.  It's just a matter of doing
>>> something reasonable if the user unchecks "Accept default problem
>>> solutions".  I'll see what I can come up with.
>>
>> Well, zypper pretty much just gives you a bunch of possible solutions
>> and asks you to select one if there is either more than one or the
>> otherwise preferred solution is blocked by a lock.  There is always one
>> "break <whatever> package by doing <stuff>" down that list.  You could
>> maybe offer something along those lines in the inevitable dialog box?
> 
> In the long run I think that's the way to go.  But implementing that is 
> more work than I feel like doing at the moment.  For now I've gone with 
> an approach that was easier to program, more like the current setup.exe. 
>   If the solver finds problems (including missing dependencies), the 
> user has four choices on the Prerequisite page:
> 
> 1. Click Back to go back to the Chooser page, with the Pending view 
> showing the solver's default solutions.
> 
> 2. Click Next to accept the default solutions.

Doing some testing of per-version requires, I've been looking at this 
page quite a bit.

It seems we're missing something to actually apply the default solution, 
so "accept default solutions" makes no changes, at the moment. (looks 
like we have to do this ourselves with solver_take_solution() ?)

Also, in the dependency problem report, we should identify which of the 
possible solutions is the default one, so it's clearer what "accept 
default solutions" is going to do.

> 3. Uncheck the "Accept default solutions" box and click Next.  If the 
> user dismisses the resulting warning, setup will go ahead and do what 
> the user requested.
> 
> 4. Cancel.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-12-05 14:32             ` Jon Turney
@ 2017-12-05 17:36               ` Ken Brown
  2017-12-13 17:31               ` Ken Brown
  1 sibling, 0 replies; 75+ messages in thread
From: Ken Brown @ 2017-12-05 17:36 UTC (permalink / raw)
  To: cygwin-apps

On 12/5/2017 9:32 AM, Jon Turney wrote:
> On 14/09/2017 21:46, Ken Brown wrote:
>> On 9/14/2017 1:26 PM, Achim Gratz wrote:
>>> Ken Brown writes:
>>>> What I've been struggling with, however, is the UI.  But now that I
>>>> think about it, maybe it isn't that hard.  It's just a matter of doing
>>>> something reasonable if the user unchecks "Accept default problem
>>>> solutions".  I'll see what I can come up with.
>>>
>>> Well, zypper pretty much just gives you a bunch of possible solutions
>>> and asks you to select one if there is either more than one or the
>>> otherwise preferred solution is blocked by a lock.  There is always one
>>> "break <whatever> package by doing <stuff>" down that list.  You could
>>> maybe offer something along those lines in the inevitable dialog box?
>>
>> In the long run I think that's the way to go.  But implementing that 
>> is more work than I feel like doing at the moment.  For now I've gone 
>> with an approach that was easier to program, more like the current 
>> setup.exe.   If the solver finds problems (including missing 
>> dependencies), the user has four choices on the Prerequisite page:
>>
>> 1. Click Back to go back to the Chooser page, with the Pending view 
>> showing the solver's default solutions.
>>
>> 2. Click Next to accept the default solutions.
> 
> Doing some testing of per-version requires, I've been looking at this 
> page quite a bit.
> 
> It seems we're missing something to actually apply the default solution, 
> so "accept default solutions" makes no changes, at the moment. (looks 
> like we have to do this ourselves with solver_take_solution() ?)

I'm not sure.  I thought at some point I saw "accept default solutions" 
do something, but there have been a lot of changes since then.

> Also, in the dependency problem report, we should identify which of the 
> possible solutions is the default one, so it's clearer what "accept 
> default solutions" is going to do.

Agreed.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-12-05 14:32             ` Jon Turney
  2017-12-05 17:36               ` Ken Brown
@ 2017-12-13 17:31               ` Ken Brown
  2017-12-13 18:06                 ` Achim Gratz
  1 sibling, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-12-13 17:31 UTC (permalink / raw)
  To: cygwin-apps

On 12/5/2017 9:32 AM, Jon Turney wrote:
> It seems we're missing something to actually apply the default solution, 
> so "accept default solutions" makes no changes, at the moment. (looks 
> like we have to do this ourselves with solver_take_solution() ?)

I've just looked at this again, and I'm not sure what's happening.

Here's what I tried:  I had two installed packages A and B, where A 
requires B.  I tried to uninstall B and got the expected problem report 
telling me that A requires B.  The solutions presented were

1. Uninstall A.
2. Don't uninstall B.

On the surface, it would seem that libsolv chose 2 by default, because 
it returned an empty transaction list.  This was reflected in the log 
and was also clear when I selected 'Back'.

On the other hand, maybe libsolv's default was to do nothing, and it's 
just a coincidence that this coincided with solution 2.  This could have 
tricked me into thinking that libsolv chose a default solution.

> Also, in the dependency problem report, we should identify which of the 
> possible solutions is the default one, so it's clearer what "accept 
> default solutions" is going to do.

Is there in fact a default solution?  I skimmed through problems.c in 
the libsolv sources, and I didn't see any mention of a default solution.

Maybe we have to deal with this situation ourselves.  Whenever a problem 
involves a missing dependency, we could choose as default solution the 
one that installs/keeps the dependent package, as is currently done.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-12-13 17:31               ` Ken Brown
@ 2017-12-13 18:06                 ` Achim Gratz
  2017-12-13 22:31                   ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Achim Gratz @ 2017-12-13 18:06 UTC (permalink / raw)
  To: cygwin-apps

Ken Brown writes:
> 1. Uninstall A.
> 2. Don't uninstall B.
>
> On the surface, it would seem that libsolv chose 2 by default, because
> it returned an empty transaction list.  This was reflected in the log
> and was also clear when I selected 'Back'.

I don't think there is a default in this case.  I also see in zypper
that the order of the proposed solutions (there can be way more than two
if the dependencies are more complicated) is not always the same, so
there is no preference implied by the order as well.

> Maybe we have to deal with this situation ourselves.  Whenever a
> problem involves a missing dependency, we could choose as default
> solution the one that installs/keeps the dependent package, as is
> currently done.

That solution unfortunately isn't always the one that causes the least
amount of transactions or even the least amount of breakage.


Regards,
Achim.
-- 
+<[Q+ Matrix-12 WAVE#46+305 Neuron microQkb Andromeda XTk Blofeld]>+

Factory and User Sound Singles for Waldorf Q+, Q and microQ:
http://Synth.Stromeko.net/Downloads.html#WaldorfSounds

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-12-13 18:06                 ` Achim Gratz
@ 2017-12-13 22:31                   ` Ken Brown
  2017-12-14 14:12                     ` Ken Brown
  2017-12-24 15:00                     ` Ken Brown
  0 siblings, 2 replies; 75+ messages in thread
From: Ken Brown @ 2017-12-13 22:31 UTC (permalink / raw)
  To: cygwin-apps

[-- Attachment #1: Type: text/plain, Size: 1194 bytes --]

On 12/13/2017 1:05 PM, Achim Gratz wrote:
> Ken Brown writes:
>> 1. Uninstall A.
>> 2. Don't uninstall B.
>>
>> On the surface, it would seem that libsolv chose 2 by default, because
>> it returned an empty transaction list.  This was reflected in the log
>> and was also clear when I selected 'Back'.
> 
> I don't think there is a default in this case.  I also see in zypper
> that the order of the proposed solutions (there can be way more than two
> if the dependencies are more complicated) is not always the same, so
> there is no preference implied by the order as well.
> 
>> Maybe we have to deal with this situation ourselves.  Whenever a
>> problem involves a missing dependency, we could choose as default
>> solution the one that installs/keeps the dependent package, as is
>> currently done.
> 
> That solution unfortunately isn't always the one that causes the least
> amount of transactions or even the least amount of breakage.

That may be true, but I still think it's a reasonable default.  The user 
doesn't have to accept it.  Also, it's consistent with what setup 
currently does, so it won't surprise anyone.

The attached patch attempts to implement my suggestion.

Ken


[-- Attachment #2: 0001-Implement-a-default-solution-for-SOLVER_RULE_PKG_REQ.patch --]
[-- Type: text/plain, Size: 2594 bytes --]

From 65cb5413e81794829cbddeebc6826d04115f2a49 Mon Sep 17 00:00:00 2001
From: Ken Brown <kbrown@cornell.edu>
Date: Wed, 13 Dec 2017 17:20:26 -0500
Subject: [PATCH] Implement a default solution for SOLVER_RULE_PKG_REQUIRES

If libsolv reports a SOLVER_RULE_PKG_REQUIRES problem, it means that
the user chose to uninstall or skip a required package.  Add an
appropriate transInstall transaction if necessary, to override this
choice.  The user will have to uncheck the "Accept default problem
solutions" box to insist on the original choice.
---
 libsolv.cc | 18 +++++++++++++++---
 libsolv.h  |  2 +-
 2 files changed, 16 insertions(+), 4 deletions(-)

diff --git a/libsolv.cc b/libsolv.cc
index 0fb39c7..9977926 100644
--- a/libsolv.cc
+++ b/libsolv.cc
@@ -868,7 +868,7 @@ SolverSolution::transactions() const
 
 // Construct a string reporting the problems and solutions
 std::string
-SolverSolution::report() const
+SolverSolution::report()
 {
   packagedb db;
   std::string r = "";
@@ -881,8 +881,20 @@ SolverSolution::report() const
       Id probr = solver_findproblemrule(solv, problem);
       Id dep, source, target;
       SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep);
-      if (source == db.basepkg.id)
-	r += "package " + std::string(pool_dep2str(pool.pool, dep)) + " is a Base package and is therefore required";
+      if (type == SOLVER_RULE_PKG_REQUIRES)
+	{
+	  packagemeta *pkg = db.findBinary(PackageSpecification(pool_dep2str(pool.pool, dep)));
+	  if (!pkg->desired && pkg->installed < pkg->default_version)
+	    // User chose to uninstall or skip a required package.
+	    trans.push_back(SolverTransaction(pkg->default_version,
+					      SolverTransaction::transInstall));
+	  if (source == db.basepkg.id)
+	    r += "package " + std::string(pool_dep2str(pool.pool, dep))
+	      + " is a Base package and is therefore required";
+	  else
+	    r += "package " + std::string(pool_dep2str(pool.pool, dep)) +
+	      " is  required by " + std::string(pool_solvid2str(pool.pool, source));
+	}
       else
 	r += solver_problemruleinfo2str(solv, type, source, target, dep);
       r += "\n";
diff --git a/libsolv.h b/libsolv.h
index cddf76f..391a96d 100644
--- a/libsolv.h
+++ b/libsolv.h
@@ -253,7 +253,7 @@ class SolverSolution
     updateForce, // distupdate: downgrade if necessary to best version in repo
   };
   bool update(SolverTasks &tasks, updateMode update, bool use_test_packages, bool include_source);
-  std::string report() const;
+  std::string report();
 
   const SolverTransactionList &transactions() const;
 
-- 
2.15.1


^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-12-13 22:31                   ` Ken Brown
@ 2017-12-14 14:12                     ` Ken Brown
  2017-12-24 15:00                     ` Ken Brown
  1 sibling, 0 replies; 75+ messages in thread
From: Ken Brown @ 2017-12-14 14:12 UTC (permalink / raw)
  To: cygwin-apps

On 12/13/2017 5:31 PM, Ken Brown wrote:
> On 12/13/2017 1:05 PM, Achim Gratz wrote:
>> Ken Brown writes:
>>> 1. Uninstall A.
>>> 2. Don't uninstall B.
>>>
>>> On the surface, it would seem that libsolv chose 2 by default, because
>>> it returned an empty transaction list.  This was reflected in the log
>>> and was also clear when I selected 'Back'.
>>
>> I don't think there is a default in this case.  I also see in zypper
>> that the order of the proposed solutions (there can be way more than two
>> if the dependencies are more complicated) is not always the same, so
>> there is no preference implied by the order as well.
>>
>>> Maybe we have to deal with this situation ourselves.  Whenever a
>>> problem involves a missing dependency, we could choose as default
>>> solution the one that installs/keeps the dependent package, as is
>>> currently done.
>>
>> That solution unfortunately isn't always the one that causes the least
>> amount of transactions or even the least amount of breakage.
> 
> That may be true, but I still think it's a reasonable default.  The user 
> doesn't have to accept it.  Also, it's consistent with what setup 
> currently does, so it won't surprise anyone.
> 
> The attached patch attempts to implement my suggestion.

I should add that this patch is only a band aid.  Eventually, as we 
start to use more of libsolv's capabilities, we will have to find a way 
for users to choose a solution.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-12-13 22:31                   ` Ken Brown
  2017-12-14 14:12                     ` Ken Brown
@ 2017-12-24 15:00                     ` Ken Brown
  2018-01-09 13:25                       ` Jon Turney
  1 sibling, 1 reply; 75+ messages in thread
From: Ken Brown @ 2017-12-24 15:00 UTC (permalink / raw)
  To: cygwin-apps

[-- Attachment #1: Type: text/plain, Size: 1857 bytes --]

On 12/13/2017 5:31 PM, Ken Brown wrote:
> On 12/13/2017 1:05 PM, Achim Gratz wrote:
>> Ken Brown writes:
>>> 1. Uninstall A.
>>> 2. Don't uninstall B.
>>>
>>> On the surface, it would seem that libsolv chose 2 by default, because
>>> it returned an empty transaction list.  This was reflected in the log
>>> and was also clear when I selected 'Back'.
>>
>> I don't think there is a default in this case.  I also see in zypper
>> that the order of the proposed solutions (there can be way more than two
>> if the dependencies are more complicated) is not always the same, so
>> there is no preference implied by the order as well.
>>
>>> Maybe we have to deal with this situation ourselves.  Whenever a
>>> problem involves a missing dependency, we could choose as default
>>> solution the one that installs/keeps the dependent package, as is
>>> currently done.
>>
>> That solution unfortunately isn't always the one that causes the least
>> amount of transactions or even the least amount of breakage.
> 
> That may be true, but I still think it's a reasonable default.  The user 
> doesn't have to accept it.  Also, it's consistent with what setup 
> currently does, so it won't surprise anyone.
> 
> The attached patch attempts to implement my suggestion.
> 
> Ken
> 
> 
> 0001-Implement-a-default-solution-for-SOLVER_RULE_PKG_REQ.patch
> 
> +      if (type == SOLVER_RULE_PKG_REQUIRES)
> +	{
> +	  packagemeta *pkg = db.findBinary(PackageSpecification(pool_dep2str(pool.pool, dep)));
> +	  if (!pkg->desired && pkg->installed < pkg->default_version)
> +	    // User chose to uninstall or skip a required package.
> +	    trans.push_back(SolverTransaction(pkg->default_version,
> +					      SolverTransaction::transInstall));

This isn't quite right.  We also need a transErase if the package is 
installed.  Revised patch attached.

Ken

[-- Attachment #2: 0001-Implement-a-default-solution-for-SOLVER_RULE_PKG_REQ.patch --]
[-- Type: text/plain, Size: 2908 bytes --]

From 1930460c9f5c8a4c1aea0837778d76d8322642fa Mon Sep 17 00:00:00 2001
From: Ken Brown <kbrown@cornell.edu>
Date: Wed, 13 Dec 2017 17:20:26 -0500
Subject: [PATCH setup libsolv v2] Implement a default solution for
 SOLVER_RULE_PKG_REQUIRES

If libsolv reports a SOLVER_RULE_PKG_REQUIRES problem, it means that
the user chose to uninstall or skip a required package.  Add
appropriate transactions, if necessary, to override this choice.  The
user will have to uncheck the "Accept default problem solutions" box
to insist on the original choice.
---
 libsolv.cc | 26 +++++++++++++++++++++++---
 libsolv.h  |  2 +-
 2 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/libsolv.cc b/libsolv.cc
index 0fb39c7..48c24c4 100644
--- a/libsolv.cc
+++ b/libsolv.cc
@@ -868,7 +868,7 @@ SolverSolution::transactions() const
 
 // Construct a string reporting the problems and solutions
 std::string
-SolverSolution::report() const
+SolverSolution::report()
 {
   packagedb db;
   std::string r = "";
@@ -881,8 +881,28 @@ SolverSolution::report() const
       Id probr = solver_findproblemrule(solv, problem);
       Id dep, source, target;
       SolverRuleinfo type = solver_ruleinfo(solv, probr, &source, &target, &dep);
-      if (source == db.basepkg.id)
-	r += "package " + std::string(pool_dep2str(pool.pool, dep)) + " is a Base package and is therefore required";
+      if (type == SOLVER_RULE_PKG_REQUIRES)
+	{
+	  // The user chose to uninstall or skip a required package.
+	  // libsolv will not create an erase transaction, but we
+	  // might need to create our own transactions to implement a
+	  // default solution to the problem.
+	  packagemeta *pkg = db.findBinary(PackageSpecification(pool_dep2str(pool.pool, dep)));
+	  if (!pkg->desired && pkg->installed < pkg->default_version)
+	    {
+	      trans.push_back(SolverTransaction(pkg->default_version,
+						SolverTransaction::transInstall));
+	      if (pkg->installed)
+		trans.push_back(SolverTransaction(pkg->installed,
+						  SolverTransaction::transErase));
+	    }
+	  if (source == db.basepkg.id)
+	    r += "package " + std::string(pool_dep2str(pool.pool, dep))
+	      + " is a Base package and is therefore required";
+	  else
+	    r += "package " + std::string(pool_dep2str(pool.pool, dep)) +
+	      " is  required by " + std::string(pool_solvid2str(pool.pool, source));
+	}
       else
 	r += solver_problemruleinfo2str(solv, type, source, target, dep);
       r += "\n";
diff --git a/libsolv.h b/libsolv.h
index cddf76f..391a96d 100644
--- a/libsolv.h
+++ b/libsolv.h
@@ -253,7 +253,7 @@ class SolverSolution
     updateForce, // distupdate: downgrade if necessary to best version in repo
   };
   bool update(SolverTasks &tasks, updateMode update, bool use_test_packages, bool include_source);
-  std::string report() const;
+  std::string report();
 
   const SolverTransactionList &transactions() const;
 
-- 
2.15.1


^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2017-12-24 15:00                     ` Ken Brown
@ 2018-01-09 13:25                       ` Jon Turney
  2018-01-09 15:37                         ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Jon Turney @ 2018-01-09 13:25 UTC (permalink / raw)
  To: cygwin-apps

[-- Attachment #1: Type: text/plain, Size: 1909 bytes --]

On 24/12/2017 15:00, Ken Brown wrote:
> On 12/13/2017 5:31 PM, Ken Brown wrote:
>> On 12/13/2017 1:05 PM, Achim Gratz wrote:
>>> Ken Brown writes:
>>>> 1. Uninstall A.
>>>> 2. Don't uninstall B.
>>>>
>>>> On the surface, it would seem that libsolv chose 2 by default, because
>>>> it returned an empty transaction list.  This was reflected in the log
>>>> and was also clear when I selected 'Back'.

Yeah, I think what is actually happening here is that the solver returns 
a partial solution, without the problematic transaction.

But yeah, there's no real concept of a default solution, so (lacking a 
UI to choose, which I think is a bit out of scope for the moment), it's 
up to us to define one.

>>> I don't think there is a default in this case.  I also see in zypper
>>> that the order of the proposed solutions (there can be way more than two
>>> if the dependencies are more complicated) is not always the same, so
>>> there is no preference implied by the order as well.
>>>
>>>> Maybe we have to deal with this situation ourselves.  Whenever a
>>>> problem involves a missing dependency, we could choose as default
>>>> solution the one that installs/keeps the dependent package, as is
>>>> currently done.
>>>
>>> That solution unfortunately isn't always the one that causes the least
>>> amount of transactions or even the least amount of breakage.
>>
>> That may be true, but I still think it's a reasonable default.  The 
>> user doesn't have to accept it.  Also, it's consistent with what setup 
>> currently does, so it won't surprise anyone.
>>
>> The attached patch attempts to implement my suggestion.

I came up with a slightly different solution of just picking the first 
solution as a default.

After solving problems we also need to consider the 'install source for 
everything I install' flag, which unfortunately requires quite a bit of 
refactoring.

See attached.

[-- Attachment #2: 0001-Apply-default-solution-s.patch --]
[-- Type: text/plain, Size: 11230 bytes --]

From a60bbd19aa5504e0e7da6722dc7f3b81ac3afd6b Mon Sep 17 00:00:00 2001
From: Jon Turney <jon.turney@dronecode.org.uk>
Date: Wed, 6 Dec 2017 17:52:45 +0000
Subject: [PATCH setup] Apply default solution(s)

Refactoring of SolverSolution::update() so we can apply the default
solution.

Also:

Break out logging of the task list, so we can show it in the "dependency
problems exists, but don't use the default solution, just do what I ask"
case.

Break out 'include-source' process, so it can have effect in the case where
dependency problems exist.
---
 libsolv.cc    | 83 ++++++++++++++++++++++++++++++++++++++++++++---------------
 libsolv.h     | 11 ++++++--
 package_db.cc |  2 +-
 prereq.cc     | 28 ++++++++++++++++----
 prereq.h      |  4 +++
 5 files changed, 99 insertions(+), 29 deletions(-)

diff --git a/libsolv.cc b/libsolv.cc
index 0fb39c7..34af50b 100644
--- a/libsolv.cc
+++ b/libsolv.cc
@@ -544,6 +544,7 @@ SolverTasks::setTasks()
 {
   // go through all packages, adding changed ones to the solver task list
   packagedb db;
+  tasks.clear();
 
   for (packagedb::packagecollection::iterator p = db.packages.begin ();
        p != db.packages.end (); ++p)
@@ -602,6 +603,11 @@ SolverPool::use_test_packages(bool use_test_packages)
 // A wrapper around the libsolv solver
 // ---------------------------------------------------------------------------
 
+SolverSolution::SolverSolution(SolverPool &_pool) : pool(_pool), solv(NULL)
+{
+  queue_init(&job);
+}
+
 SolverSolution::~SolverSolution()
 {
   clear();
@@ -615,6 +621,7 @@ SolverSolution::clear()
       solver_free(solv);
       solv = NULL;
     }
+  queue_free(&job);
 }
 
 void
@@ -713,18 +720,9 @@ std::ostream &operator<<(std::ostream &stream,
   return stream;
 }
 
-bool
-SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_packages, bool include_source)
+void
+SolverSolution::tasksToJobs(SolverTasks &tasks, updateMode update, Queue &job)
 {
-  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();
@@ -745,11 +743,11 @@ SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_pack
           // we don't know how to ask solver for this, so we just add the erase
           // and install later
           break;
-	case SolverTasks::taskKeep:
-	  queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE, sv.id);
-	  break;
-	case SolverTasks::taskSkip:
-	  queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE_NAME, sv.name_id());
+        case SolverTasks::taskKeep:
+          queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE, sv.id);
+          break;
+        case SolverTasks::taskSkip:
+          queue_push2(&job, SOLVER_LOCK | SOLVER_SOLVABLE_NAME, sv.name_id());
           break;
         default:
           Log (LOG_PLAIN) << "unknown task " << (*i).second << endLog;
@@ -776,6 +774,19 @@ SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_pack
 
   // Ask solver to check dependencies of installed packages.
   queue_push2(&job, SOLVER_VERIFY | SOLVER_SOLVABLE_ALL, 0);
+}
+
+bool
+SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_packages)
+{
+  Log (LOG_PLAIN) << "solving: " << tasks.tasks.size() << " tasks," <<
+    " update: " << (update ? "yes" : "no") << "," <<
+    " use test packages: " << (use_test_packages ? "yes" : "no") << "," << endLog;
+
+  pool.use_test_packages(use_test_packages);
+
+  queue_free(&job);
+  tasksToJobs(tasks, update, job);
 
   if (!solv)
     solv = solver_create(pool.pool);
@@ -785,11 +796,18 @@ SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_pack
   solver_set_flag(solv, SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE, 1);
   solver_set_flag(solv, SOLVER_FLAG_DUP_ALLOW_DOWNGRADE, 1);
   solver_solve(solv, &job);
-  queue_free(&job);
 
   int pcnt = solver_problem_count(solv);
   solver_printdecisions(solv);
 
+  solutionToTransactionList();
+
+  return (pcnt == 0);
+}
+
+void
+SolverSolution::solutionToTransactionList()
+{
   // get transactions for solution
   Transaction *t = solver_create_transaction(solv);
   transaction_order(t, 0);
@@ -797,7 +815,6 @@ SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_pack
 
   // massage into SolverTransactions form
   trans.clear();
-
   for (int i = 0; i < t->steps.count; i++)
     {
       Id id = t->steps.elements[i];
@@ -806,6 +823,14 @@ SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_pack
         trans.push_back(SolverTransaction(SolvableVersion(id, pool.pool), tt));
     }
 
+  transaction_free(t);
+
+  dumpTransactionList();
+}
+
+void
+SolverSolution::augmentTasks(SolverTasks &tasks)
+{
   // add install and remove tasks for anything marked as reinstall
   for (SolverTasks::taskList::const_iterator i = tasks.tasks.begin();
        i != tasks.tasks.end();
@@ -821,7 +846,11 @@ SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_pack
                                             SolverTransaction::transInstall));
         }
     }
+}
 
+void
+SolverSolution::addSource(bool include_source)
+{
   // if include_source mode is on, also install source for everything we are
   // installing
   if (include_source)
@@ -840,11 +869,15 @@ SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_pack
             }
         }
     }
+}
 
+void
+SolverSolution::dumpTransactionList() const
+{
   if (trans.size())
     {
       Log (LOG_PLAIN) << "Augmented Transaction List:" << endLog;
-      for (SolverTransactionList::iterator i = trans.begin ();
+      for (SolverTransactionList::const_iterator i = trans.begin ();
            i != trans.end ();
            ++i)
         {
@@ -854,10 +887,17 @@ SolverSolution::update(SolverTasks &tasks, updateMode update, bool use_test_pack
                           << std::setw(20) << i->version.Canonical_version() << endLog;
         }
     }
+  else
+    Log (LOG_PLAIN) << "Augmented Transaction List: is empty" << endLog;
+}
 
-  transaction_free(t);
+void SolverSolution::applyDefaultProblemSolutions()
+{
+  int pcnt = solver_problem_count(solv);
+  for (Id problem = 1; problem <= pcnt; problem++)
+    solver_take_solution(solv, problem, 1, &job);
 
-  return (pcnt == 0);
+  solutionToTransactionList();
 }
 
 const SolverTransactionList &
@@ -891,6 +931,7 @@ SolverSolution::report() const
       for (Id solution = 1; solution <= scnt; solution++)
         {
           r += "Solution " + std::to_string(solution) + "/" + std::to_string(scnt);
+          if (solution == 1) r += " (default)";
           r += "\n";
 
           Id p, rp, element;
diff --git a/libsolv.h b/libsolv.h
index cddf76f..78b6881 100644
--- a/libsolv.h
+++ b/libsolv.h
@@ -236,7 +236,7 @@ typedef std::vector<SolverTransaction> SolverTransactionList;
 class SolverSolution
 {
  public:
-  SolverSolution(SolverPool &_pool) : pool(_pool), solv(NULL) {};
+  SolverSolution(SolverPool &_pool);
   ~SolverSolution();
   void clear();
 
@@ -252,16 +252,23 @@ class SolverSolution
     updateBest,  // update to best version
     updateForce, // distupdate: downgrade if necessary to best version in repo
   };
-  bool update(SolverTasks &tasks, updateMode update, bool use_test_packages, bool include_source);
+  bool update(SolverTasks &tasks, updateMode update, bool use_test_packages);
+  void augmentTasks(SolverTasks &tasks);
+  void addSource(bool include_source);
+  void applyDefaultProblemSolutions();
   std::string report() const;
 
   const SolverTransactionList &transactions() const;
+  void dumpTransactionList() const;
 
  private:
   static SolverTransaction::transType type(Transaction *trans, int pos);
+  void tasksToJobs(SolverTasks &tasks, updateMode update, Queue &job);
+  void solutionToTransactionList();
 
   SolverPool &pool;
   Solver *solv;
+  Queue job;
   SolverTransactionList trans;
 };
 
diff --git a/package_db.cc b/package_db.cc
index 92fe4f9..e2443bf 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -602,7 +602,7 @@ packagedb::fillMissingCategory ()
 void
 packagedb::defaultTrust (SolverTasks &q, SolverSolution::updateMode mode, bool test)
 {
-  solution.update(q, mode, test, FALSE);
+  solution.update(q, mode, test);
 
   // reflect that task list into packagedb
   solution.trans2db();
diff --git a/prereq.cc b/prereq.cc
index bf7661a..5b3e785 100644
--- a/prereq.cc
+++ b/prereq.cc
@@ -90,6 +90,7 @@ long
 PrereqPage::OnNext ()
 {
   HWND h = GetHWND ();
+  packagedb db;
 
   if (!IsDlgButtonChecked (h, IDC_PREREQ_CHECK))
     {
@@ -109,10 +110,16 @@ PrereqPage::OnNext ()
             "NOTE!  User refused the default solutions!  "
             "Expect some packages to give errors or not function at all." << endLog;
 	  // Change the solver's transaction list to reflect the user's choices.
-	  packagedb db;
 	  db.solution.db2trans();
 	}
     }
+  else
+    {
+      db.solution.applyDefaultProblemSolutions();
+    }
+
+  PrereqChecker p;
+  p.finalize();
 
   return whatNext();
 }
@@ -157,8 +164,9 @@ PrereqPage::OnUnattended ()
 // implements class PrereqChecker
 // ---------------------------------------------------------------------------
 
-// instantiate the static member
+// instantiate the static members
 bool PrereqChecker::use_test_packages;
+SolverTasks PrereqChecker::q;
 
 bool
 PrereqChecker::isMet ()
@@ -170,11 +178,19 @@ PrereqChecker::isMet ()
   Progress.SetText3 ("");
 
   // Create task list corresponding to current state of package database
-  SolverTasks q;
   q.setTasks();
 
-  // apply solver to those tasks and global state (use test, include source)
-  return db.solution.update(q, SolverSolution::keep, use_test_packages, IncludeSource);
+  // apply solver to those tasks and global state (use test or not)
+  return db.solution.update(q, SolverSolution::keep, use_test_packages);
+}
+
+void
+PrereqChecker::finalize ()
+{
+  packagedb db;
+  db.solution.augmentTasks(q);
+  db.solution.addSource(IncludeSource);
+  db.solution.dumpTransactionList();
 }
 
 /* Formats problems and solutions as a string for display to the user.  */
@@ -205,6 +221,8 @@ do_prereq_check_thread(HINSTANCE h, HWND owner)
 
   if (p.isMet ())
     {
+      p.finalize();
+
       if (source == IDC_SOURCE_LOCALDIR)
 	Progress.SetActivateTask (WM_APP_START_INSTALL);  // install
       else
diff --git a/prereq.h b/prereq.h
index 5ae9323..f1561fa 100644
--- a/prereq.h
+++ b/prereq.h
@@ -40,10 +40,14 @@ public:
   // formats 'unmet' as a string for display
   void getUnmetString (std::string &s);
 
+  // finialize the transaction list
+  void finalize ();
+
   static void setTestPackages (bool t) { use_test_packages = t; };
 
 private:
   static bool use_test_packages;
+  static SolverTasks q;
 };
 
 #endif /* SETUP_PREREQ_H */
-- 
2.15.1


^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-09 13:25                       ` Jon Turney
@ 2018-01-09 15:37                         ` Ken Brown
  2018-01-09 15:49                           ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2018-01-09 15:37 UTC (permalink / raw)
  To: cygwin-apps

On 1/9/2018 8:25 AM, Jon Turney wrote:
> On 24/12/2017 15:00, Ken Brown wrote:
>> On 12/13/2017 5:31 PM, Ken Brown wrote:
>>> On 12/13/2017 1:05 PM, Achim Gratz wrote:
>>>> Ken Brown writes:
>>>>> 1. Uninstall A.
>>>>> 2. Don't uninstall B.
>>>>>
>>>>> On the surface, it would seem that libsolv chose 2 by default, because
>>>>> it returned an empty transaction list.  This was reflected in the log
>>>>> and was also clear when I selected 'Back'.
> 
> Yeah, I think what is actually happening here is that the solver returns 
> a partial solution, without the problematic transaction.
> 
> But yeah, there's no real concept of a default solution, so (lacking a 
> UI to choose, which I think is a bit out of scope for the moment), it's 
> up to us to define one.
> 
>>>> I don't think there is a default in this case.  I also see in zypper
>>>> that the order of the proposed solutions (there can be way more than 
>>>> two
>>>> if the dependencies are more complicated) is not always the same, so
>>>> there is no preference implied by the order as well.
>>>>
>>>>> Maybe we have to deal with this situation ourselves.  Whenever a
>>>>> problem involves a missing dependency, we could choose as default
>>>>> solution the one that installs/keeps the dependent package, as is
>>>>> currently done.
>>>>
>>>> That solution unfortunately isn't always the one that causes the least
>>>> amount of transactions or even the least amount of breakage.
>>>
>>> That may be true, but I still think it's a reasonable default.  The 
>>> user doesn't have to accept it.  Also, it's consistent with what 
>>> setup currently does, so it won't surprise anyone.
>>>
>>> The attached patch attempts to implement my suggestion.
> 
> I came up with a slightly different solution of just picking the first 
> solution as a default.

That's certainly easier than what I was trying to do.  If we do that, we 
should probably change the UI to remove the implication that the default 
solution is recommended.  For example, if A requires B and the user 
tries to uninstall B, the first solution seems to usually (always?) be 
"uninstall A".  This may or may not be what the user wants.

Maybe we should say something like, "By default, the first proposed 
solution will be selected for each problem.  If this is not what you 
want, press Back...."

> After solving problems we also need to consider the 'install source for 
> everything I install' flag, which unfortunately requires quite a bit of 
> refactoring.
> 
> See attached.

I just did a quick test, trying to uninstall B in the situation above, 
and it didn't work as expected.  Even though "Uninstall A" was the first 
solution, A didn't get uninstalled.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-09 15:37                         ` Ken Brown
@ 2018-01-09 15:49                           ` Ken Brown
  2018-01-13 14:14                             ` Jon Turney
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2018-01-09 15:49 UTC (permalink / raw)
  To: cygwin-apps

On 1/9/2018 10:37 AM, Ken Brown wrote:
> On 1/9/2018 8:25 AM, Jon Turney wrote:
>> On 24/12/2017 15:00, Ken Brown wrote:
>>> On 12/13/2017 5:31 PM, Ken Brown wrote:
>>>> On 12/13/2017 1:05 PM, Achim Gratz wrote:
>>>>> Ken Brown writes:
>>>>>> 1. Uninstall A.
>>>>>> 2. Don't uninstall B.
>>>>>>
>>>>>> On the surface, it would seem that libsolv chose 2 by default, 
>>>>>> because
>>>>>> it returned an empty transaction list.  This was reflected in the log
>>>>>> and was also clear when I selected 'Back'.
>>
>> Yeah, I think what is actually happening here is that the solver returns
>> a partial solution, without the problematic transaction.
>>
>> But yeah, there's no real concept of a default solution, so (lacking a
>> UI to choose, which I think is a bit out of scope for the moment), it's
>> up to us to define one.
>>
>>>>> I don't think there is a default in this case.  I also see in zypper
>>>>> that the order of the proposed solutions (there can be way more than
>>>>> two
>>>>> if the dependencies are more complicated) is not always the same, so
>>>>> there is no preference implied by the order as well.
>>>>>
>>>>>> Maybe we have to deal with this situation ourselves.  Whenever a
>>>>>> problem involves a missing dependency, we could choose as default
>>>>>> solution the one that installs/keeps the dependent package, as is
>>>>>> currently done.
>>>>>
>>>>> That solution unfortunately isn't always the one that causes the least
>>>>> amount of transactions or even the least amount of breakage.
>>>>
>>>> That may be true, but I still think it's a reasonable default.  The
>>>> user doesn't have to accept it.  Also, it's consistent with what
>>>> setup currently does, so it won't surprise anyone.
>>>>
>>>> The attached patch attempts to implement my suggestion.
>>
>> I came up with a slightly different solution of just picking the first
>> solution as a default.
> 
> That's certainly easier than what I was trying to do.  If we do that, we
> should probably change the UI to remove the implication that the default
> solution is recommended.  For example, if A requires B and the user
> tries to uninstall B, the first solution seems to usually (always?) be
> "uninstall A".  This may or may not be what the user wants.
> 
> Maybe we should say something like, "By default, the first proposed
> solution will be selected for each problem.  If this is not what you
> want, press Back...."
> 
>> After solving problems we also need to consider the 'install source for
>> everything I install' flag, which unfortunately requires quite a bit of
>> refactoring.
>>
>> See attached.
> 
> I just did a quick test, trying to uninstall B in the situation above,
> and it didn't work as expected.  Even though "Uninstall A" was the first
> solution, A didn't get uninstalled.

In case you want to replicate this, what I actually tried was 
uninstalling ImageMagick, which is required, directly or indirectly, by 
asymptote, dblatex, and xmlto.  Solution 1 was to uninstall these three 
but it didn't happen.

Ken


^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-09 15:49                           ` Ken Brown
@ 2018-01-13 14:14                             ` Jon Turney
  2018-01-13 19:56                               ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Jon Turney @ 2018-01-13 14:14 UTC (permalink / raw)
  To: cygwin-apps

On 09/01/2018 15:49, Ken Brown wrote:
> On 1/9/2018 10:37 AM, Ken Brown wrote:
>>
>> I just did a quick test, trying to uninstall B in the situation above,
>> and it didn't work as expected.  Even though "Uninstall A" was the first
>> solution, A didn't get uninstalled.
> 
> In case you want to replicate this, what I actually tried was 
> uninstalling ImageMagick, which is required, directly or indirectly, by 
> asymptote, dblatex, and xmlto.  Solution 1 was to uninstall these three 
> but it didn't happen.

Doh.

solver_take_solution() modifies the task list (and I went to the trouble 
of refactoring things to make it available to it), but I didn't then 
re-solve with the modified task list.

Also, looking at this again, it looks like the solver places solutions 
which remove a task at the end of of the solution list, so this should 
be the default

(This matches the previous behaviour, where the default is to accept 
dependencies i.e. if you try to remove a package required by other 
packages, the default solution should be to cancel the removal)

Maybe we need more complex criteria to identify the default, but this 
seems to work in my limited testing.

I've rebased and updated the topic/libsolv branch.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-13 14:14                             ` Jon Turney
@ 2018-01-13 19:56                               ` Ken Brown
  2018-01-13 21:29                                 ` Brian Inglis
  2018-01-15 19:02                                 ` Jon Turney
  0 siblings, 2 replies; 75+ messages in thread
From: Ken Brown @ 2018-01-13 19:56 UTC (permalink / raw)
  To: cygwin-apps

On 1/13/2018 9:14 AM, Jon Turney wrote:
> On 09/01/2018 15:49, Ken Brown wrote:
>> On 1/9/2018 10:37 AM, Ken Brown wrote:
>>>
>>> I just did a quick test, trying to uninstall B in the situation above,
>>> and it didn't work as expected.  Even though "Uninstall A" was the first
>>> solution, A didn't get uninstalled.
>>
>> In case you want to replicate this, what I actually tried was 
>> uninstalling ImageMagick, which is required, directly or indirectly, 
>> by asymptote, dblatex, and xmlto.  Solution 1 was to uninstall these 
>> three but it didn't happen.
> 
> Doh.
> 
> solver_take_solution() modifies the task list (and I went to the trouble 
> of refactoring things to make it available to it), but I didn't then 
> re-solve with the modified task list.
> 
> Also, looking at this again, it looks like the solver places solutions 
> which remove a task at the end of of the solution list, so this should 
> be the default
> 
> (This matches the previous behaviour, where the default is to accept 
> dependencies i.e. if you try to remove a package required by other 
> packages, the default solution should be to cancel the removal)
> 
> Maybe we need more complex criteria to identify the default, but this 
> seems to work in my limited testing.

I noticed two things:

1. I think the code you added to PrereqPage::OnNext() also needs to be 
added to PrereqPage::OnBack().

2. We should probably remove, or at least reword, the dire warning about 
accepting the default solutions.  I'm not sure we want to "strongly 
recommend" the default solution over the other solution(s).  I guess 
what we really want to say is that we strongly recommend resolving the 
problems before continuing.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-13 19:56                               ` Ken Brown
@ 2018-01-13 21:29                                 ` Brian Inglis
  2018-01-13 22:55                                   ` Ken Brown
  2018-01-15 19:02                                 ` Jon Turney
  1 sibling, 1 reply; 75+ messages in thread
From: Brian Inglis @ 2018-01-13 21:29 UTC (permalink / raw)
  To: cygwin-apps

On 2018-01-13 12:56, Ken Brown wrote:
> On 1/13/2018 9:14 AM, Jon Turney wrote:
>> On 09/01/2018 15:49, Ken Brown wrote:
>>> On 1/9/2018 10:37 AM, Ken Brown wrote:
>>>>
>>>> I just did a quick test, trying to uninstall B in the situation above,
>>>> and it didn't work as expected.  Even though "Uninstall A" was the first
>>>> solution, A didn't get uninstalled.
>>>
>>> In case you want to replicate this, what I actually tried was uninstalling
>>> ImageMagick, which is required, directly or indirectly, by asymptote,
>>> dblatex, and xmlto.  Solution 1 was to uninstall these three but it didn't
>>> happen.
>>
>> Doh.
>>
>> solver_take_solution() modifies the task list (and I went to the trouble of
>> refactoring things to make it available to it), but I didn't then re-solve
>> with the modified task list.
>>
>> Also, looking at this again, it looks like the solver places solutions which
>> remove a task at the end of of the solution list, so this should be the default
>>
>> (This matches the previous behaviour, where the default is to accept
>> dependencies i.e. if you try to remove a package required by other packages,
>> the default solution should be to cancel the removal)
>>
>> Maybe we need more complex criteria to identify the default, but this seems to
>> work in my limited testing.
> 
> I noticed two things:
> 
> 1. I think the code you added to PrereqPage::OnNext() also needs to be added to
> PrereqPage::OnBack().
> 
> 2. We should probably remove, or at least reword, the dire warning about
> accepting the default solutions.  I'm not sure we want to "strongly recommend"
> the default solution over the other solution(s).  I guess what we really want to
> say is that we strongly recommend resolving the problems before continuing.

For users who only run setup and use programs, a dire warning and strong
recommendations are appropriate.

Alternatives are to also remove all packages dependent on the package to be
removed, or lastly, to remove only the requested package, leaving the
installation inconsistent. Those alternatives would have to be presented to the
user for selection, then executed.

Anything else requiring the user to resolve would require a FAQ entry explaining
what that meant, what diagnosis and actions would be required, and that would
probably generate emails from users asking what they should do.

Better to allow the solver to resolve issues and just let users choose
straightforward alternatives, with the view of trying to keep the installation
consistent, unless explicitly overridden, such as to test an alternative
implementation of a dependency installed outside of setup.

-- 
Take care. Thanks, Brian Inglis, Calgary, Alberta, Canada

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-13 21:29                                 ` Brian Inglis
@ 2018-01-13 22:55                                   ` Ken Brown
  2018-01-14  0:00                                     ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2018-01-13 22:55 UTC (permalink / raw)
  To: cygwin-apps

On 1/13/2018 4:29 PM, Brian Inglis wrote:
> On 2018-01-13 12:56, Ken Brown wrote:
>> 2. We should probably remove, or at least reword, the dire warning about
>> accepting the default solutions.  I'm not sure we want to "strongly recommend"
>> the default solution over the other solution(s).  I guess what we really want to
>> say is that we strongly recommend resolving the problems before continuing.
> 
> For users who only run setup and use programs, a dire warning and strong
> recommendations are appropriate.
> 
> Alternatives are to also remove all packages dependent on the package to be
> removed, or lastly, to remove only the requested package, leaving the
> installation inconsistent. Those alternatives would have to be presented to the
> user for selection, then executed.
> 
> Anything else requiring the user to resolve would require a FAQ entry explaining
> what that meant, what diagnosis and actions would be required, and that would
> probably generate emails from users asking what they should do.
> 
> Better to allow the solver to resolve issues and just let users choose
> straightforward alternatives, with the view of trying to keep the installation
> consistent, unless explicitly overridden, such as to test an alternative
> implementation of a dependency installed outside of setup.

The current situation on the topic/libsolv branch is the following. 
Suppose A requires B and the user asks to uninstall B.  They will get a 
problem report showing two possible solutions:

1. Uninstall A.
2. (default) Don't uninstall B.

If they uncheck 'Accept default solutions' and select 'Next', they'll 
get a warning that says "We strongly recommend that you accept the 
default solutions.  Some packages may not work properly if you don't. 
Are you sure you want to proceed?"

This is misleading insofar as it implies that something bad will happen 
if the user prefers to solve the problem by uninstalling A.  What is 
true is that some packages may not work properly if the user answers 'Yes'.

I think we should be able to find wording that is accurate while still 
making it clear that we recommend going back and fixing the problem.  I 
don't yet have a good candidate for that wording.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-13 22:55                                   ` Ken Brown
@ 2018-01-14  0:00                                     ` Ken Brown
  2018-01-14  1:52                                       ` Brian Inglis
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2018-01-14  0:00 UTC (permalink / raw)
  To: cygwin-apps

[-- Attachment #1: Type: text/plain, Size: 2407 bytes --]

On 1/13/2018 5:55 PM, Ken Brown wrote:
> On 1/13/2018 4:29 PM, Brian Inglis wrote:
>> On 2018-01-13 12:56, Ken Brown wrote:
>>> 2. We should probably remove, or at least reword, the dire warning about
>>> accepting the default solutions.  I'm not sure we want to "strongly 
>>> recommend"
>>> the default solution over the other solution(s).  I guess what we 
>>> really want to
>>> say is that we strongly recommend resolving the problems before 
>>> continuing.
>>
>> For users who only run setup and use programs, a dire warning and strong
>> recommendations are appropriate.
>>
>> Alternatives are to also remove all packages dependent on the package 
>> to be
>> removed, or lastly, to remove only the requested package, leaving the
>> installation inconsistent. Those alternatives would have to be 
>> presented to the
>> user for selection, then executed.
>>
>> Anything else requiring the user to resolve would require a FAQ entry 
>> explaining
>> what that meant, what diagnosis and actions would be required, and 
>> that would
>> probably generate emails from users asking what they should do.
>>
>> Better to allow the solver to resolve issues and just let users choose
>> straightforward alternatives, with the view of trying to keep the 
>> installation
>> consistent, unless explicitly overridden, such as to test an alternative
>> implementation of a dependency installed outside of setup.
> 
> The current situation on the topic/libsolv branch is the following. 
> Suppose A requires B and the user asks to uninstall B.  They will get a 
> problem report showing two possible solutions:
> 
> 1. Uninstall A.
> 2. (default) Don't uninstall B.
> 
> If they uncheck 'Accept default solutions' and select 'Next', they'll 
> get a warning that says "We strongly recommend that you accept the 
> default solutions.  Some packages may not work properly if you don't. 
> Are you sure you want to proceed?"
> 
> This is misleading insofar as it implies that something bad will happen 
> if the user prefers to solve the problem by uninstalling A.  What is 
> true is that some packages may not work properly if the user answers 'Yes'.
> 
> I think we should be able to find wording that is accurate while still 
> making it clear that we recommend going back and fixing the problem.  I 
> don't yet have a good candidate for that wording.

Something like the attached might do the job.

Ken


[-- Attachment #2: 0001-Clarify-the-unsolved-problems-warning.patch --]
[-- Type: text/plain, Size: 1550 bytes --]

From d15d18dfa4db91416155385034bccf31be88ece3 Mon Sep 17 00:00:00 2001
From: Ken Brown <kbrown@cornell.edu>
Date: Sat, 13 Jan 2018 18:50:14 -0500
Subject: [PATCH] Clarify the unsolved-problems warning

If the user unchecks the 'Accept default solutions' box and selects
'Next', don't imply that choosing a non-default solution would break
their system.
---
 prereq.cc | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/prereq.cc b/prereq.cc
index a03e79b..4926c65 100644
--- a/prereq.cc
+++ b/prereq.cc
@@ -96,10 +96,9 @@ PrereqPage::OnNext ()
     {
       // breakage imminent!  danger, danger
       int res = MessageBox (h,
-          "We strongly recommend that you accept the default solutions. "
-          "Some packages may not work properly if you don't."
+          "Some packages may not work properly if you continue."
           "\r\n\r\n"
-          "Are you sure you want to proceed?",
+          "Are you sure you want to proceed (NOT RECOMMENDED)?",
           "WARNING - Unsolved Problems",
           MB_YESNO | MB_ICONEXCLAMATION | MB_DEFBUTTON2);
       if (res == IDNO)
@@ -107,7 +106,7 @@ PrereqPage::OnNext ()
       else
         {
           Log (LOG_PLAIN) <<
-            "NOTE!  User refused the default solutions!  "
+            "NOTE!  User continued with unsolved problems!  "
             "Expect some packages to give errors or not function at all." << endLog;
           // Change the solver's transaction list to reflect the user's choices.
           db.solution.db2trans();
-- 
2.15.1


^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-14  0:00                                     ` Ken Brown
@ 2018-01-14  1:52                                       ` Brian Inglis
  2018-01-14  2:37                                         ` Ken Brown
  0 siblings, 1 reply; 75+ messages in thread
From: Brian Inglis @ 2018-01-14  1:52 UTC (permalink / raw)
  To: cygwin-apps

On 2018-01-13 17:00, Ken Brown wrote:
> On 1/13/2018 5:55 PM, Ken Brown wrote:
>> On 1/13/2018 4:29 PM, Brian Inglis wrote:
>>> On 2018-01-13 12:56, Ken Brown wrote:
>>>> 2. We should probably remove, or at least reword, the dire warning about
>>>> accepting the default solutions.  I'm not sure we want to "strongly recommend"
>>>> the default solution over the other solution(s).  I guess what we really
>>>> want to
>>>> say is that we strongly recommend resolving the problems before continuing.
>>>
>>> For users who only run setup and use programs, a dire warning and strong
>>> recommendations are appropriate.
>>>
>>> Alternatives are to also remove all packages dependent on the package to be
>>> removed, or lastly, to remove only the requested package, leaving the
>>> installation inconsistent. Those alternatives would have to be presented to the
>>> user for selection, then executed.
>>>
>>> Anything else requiring the user to resolve would require a FAQ entry explaining
>>> what that meant, what diagnosis and actions would be required, and that would
>>> probably generate emails from users asking what they should do.
>>>
>>> Better to allow the solver to resolve issues and just let users choose
>>> straightforward alternatives, with the view of trying to keep the installation
>>> consistent, unless explicitly overridden, such as to test an alternative
>>> implementation of a dependency installed outside of setup.
>>
>> The current situation on the topic/libsolv branch is the following. Suppose A
>> requires B and the user asks to uninstall B.  They will get a problem report
>> showing two possible solutions:
>>
>> 1. Uninstall A.
>> 2. (default) Don't uninstall B.
>>
>> If they uncheck 'Accept default solutions' and select 'Next', they'll get a
>> warning that says "We strongly recommend that you accept the default
>> solutions.  Some packages may not work properly if you don't. Are you sure you
>> want to proceed?"
>>
>> This is misleading insofar as it implies that something bad will happen if the
>> user prefers to solve the problem by uninstalling A.  What is true is that
>> some packages may not work properly if the user answers 'Yes'.
>>
>> I think we should be able to find wording that is accurate while still making
>> it clear that we recommend going back and fixing the problem.  I don't yet
>> have a good candidate for that wording.
> 
> Something like the attached might do the job.

Just saying "Unsolved problems" does not tell the user what they did and what
impact it will have, and invites a FAQ entry for Setup - Unsolved problems.

Could we please be more explicit in the UI and the logs about the action being
taken and the impact: instead of "Unsolved problems" and "Some packages" maybe
something more like "uninstalling package U will break packages P1...", and
instead of "default solutions" maybe something more like "recommended actions"?

That shows and records the action and impact for diagnosis and remediation when
users do something unintended and need to undo it, changing the messaging from
the implementation model to the user's model, and giving users and maintainers
enough information to diagnose what will or did happen, and how to undo that.

-- 
Take care. Thanks, Brian Inglis, Calgary, Alberta, Canada

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-14  1:52                                       ` Brian Inglis
@ 2018-01-14  2:37                                         ` Ken Brown
  0 siblings, 0 replies; 75+ messages in thread
From: Ken Brown @ 2018-01-14  2:37 UTC (permalink / raw)
  To: cygwin-apps

On 1/13/2018 8:52 PM, Brian Inglis wrote:
> On 2018-01-13 17:00, Ken Brown wrote:
>> On 1/13/2018 5:55 PM, Ken Brown wrote:
>>> On 1/13/2018 4:29 PM, Brian Inglis wrote:
>>>> On 2018-01-13 12:56, Ken Brown wrote:
>>>>> 2. We should probably remove, or at least reword, the dire warning about
>>>>> accepting the default solutions.  I'm not sure we want to "strongly recommend"
>>>>> the default solution over the other solution(s).  I guess what we really
>>>>> want to
>>>>> say is that we strongly recommend resolving the problems before continuing.
>>>>
>>>> For users who only run setup and use programs, a dire warning and strong
>>>> recommendations are appropriate.
>>>>
>>>> Alternatives are to also remove all packages dependent on the package to be
>>>> removed, or lastly, to remove only the requested package, leaving the
>>>> installation inconsistent. Those alternatives would have to be presented to the
>>>> user for selection, then executed.
>>>>
>>>> Anything else requiring the user to resolve would require a FAQ entry explaining
>>>> what that meant, what diagnosis and actions would be required, and that would
>>>> probably generate emails from users asking what they should do.
>>>>
>>>> Better to allow the solver to resolve issues and just let users choose
>>>> straightforward alternatives, with the view of trying to keep the installation
>>>> consistent, unless explicitly overridden, such as to test an alternative
>>>> implementation of a dependency installed outside of setup.
>>>
>>> The current situation on the topic/libsolv branch is the following. Suppose A
>>> requires B and the user asks to uninstall B.  They will get a problem report
>>> showing two possible solutions:
>>>
>>> 1. Uninstall A.
>>> 2. (default) Don't uninstall B.
>>>
>>> If they uncheck 'Accept default solutions' and select 'Next', they'll get a
>>> warning that says "We strongly recommend that you accept the default
>>> solutions.  Some packages may not work properly if you don't. Are you sure you
>>> want to proceed?"
>>>
>>> This is misleading insofar as it implies that something bad will happen if the
>>> user prefers to solve the problem by uninstalling A.  What is true is that
>>> some packages may not work properly if the user answers 'Yes'.
>>>
>>> I think we should be able to find wording that is accurate while still making
>>> it clear that we recommend going back and fixing the problem.  I don't yet
>>> have a good candidate for that wording.
>>
>> Something like the attached might do the job.
> 
> Just saying "Unsolved problems" does not tell the user what they did and what
> impact it will have, and invites a FAQ entry for Setup - Unsolved problems.

"WARNING - Unsolved Problems" is simply the caption on the message box.

> Could we please be more explicit in the UI and the logs about the action being
> taken and the impact: instead of "Unsolved problems" and "Some packages" maybe
> something more like "uninstalling package U will break packages P1...", and

We were already explicit when we displayed the prerequisites page with a 
problem report.  There's no need to repeat it.  What we're discussing 
right now is what warning we should give if the user, *after seeing the 
problem report*, unchecks the 'Accept default solutions' box and selects 
'Next'.

> instead of "default solutions" maybe something more like "recommended actions"?

I've already explained why I think it would be misleading to say 
"recommended" rather than "default".  In the scenario above with 
packages A and B, the user may be trying to pare down their installation 
by uninstalling some packages.  They try to uninstall B, we warn them 
that this will break A, and we offer them two possible solutions.

It's perfectly reasonable for us to present "Keep B" as the default 
solution, especially since, as Jon pointed out, that's what we currently 
do.  But I don't think it's reasonable for us to *recommend* keeping B 
without knowing what the user wanted to accomplish.  Maybe uninstalling 
A better fits their needs.

> That shows and records the action and impact for diagnosis and remediation when
> users do something unintended and need to undo it, changing the messaging from
> the implementation model to the user's model, and giving users and maintainers
> enough information to diagnose what will or did happen, and how to undo that.

The current code in the topic/libsolv branch already does this.  Maybe 
it could do it better.  It would be great if you would test it and 
suggest improvements.

Ken

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-13 19:56                               ` Ken Brown
  2018-01-13 21:29                                 ` Brian Inglis
@ 2018-01-15 19:02                                 ` Jon Turney
  2018-01-15 21:50                                   ` Ken Brown
  1 sibling, 1 reply; 75+ messages in thread
From: Jon Turney @ 2018-01-15 19:02 UTC (permalink / raw)
  To: cygwin-apps

On 13/01/2018 19:56, Ken Brown wrote:
> On 1/13/2018 9:14 AM, Jon Turney wrote:
>> On 09/01/2018 15:49, Ken Brown wrote:
>>> On 1/9/2018 10:37 AM, Ken Brown wrote:
>>>>
>>>> I just did a quick test, trying to uninstall B in the situation above,
>>>> and it didn't work as expected.  Even though "Uninstall A" was the 
>>>> first
>>>> solution, A didn't get uninstalled.
>>>
>>> In case you want to replicate this, what I actually tried was 
>>> uninstalling ImageMagick, which is required, directly or indirectly, 
>>> by asymptote, dblatex, and xmlto.  Solution 1 was to uninstall these 
>>> three but it didn't happen.
>>
>> Doh.
>>
>> solver_take_solution() modifies the task list (and I went to the 
>> trouble of refactoring things to make it available to it), but I 
>> didn't then re-solve with the modified task list.
>>
>> Also, looking at this again, it looks like the solver places solutions 
>> which remove a task at the end of of the solution list, so this should 
>> be the default
>>
>> (This matches the previous behaviour, where the default is to accept 
>> dependencies i.e. if you try to remove a package required by other 
>> packages, the default solution should be to cancel the removal)
>>
>> Maybe we need more complex criteria to identify the default, but this 
>> seems to work in my limited testing.
> 
> I noticed two things:
> 
> 1. I think the code you added to PrereqPage::OnNext() also needs to be 
> added to PrereqPage::OnBack().

Hmm... not sure.

I don't think we want to applyDefaultProblemSolutions(), because the 
user should solve the problems their way.

As to finalize():

Not doing augmentTasks() means that a reinstall would disappear when we 
go back, so I guess we need that.

I don't think we want to addSource() as the consequences of that are 
never shown in the chooser, currently.

> 2. We should probably remove, or at least reword, the dire warning about 
> accepting the default solutions.  I'm not sure we want to "strongly 
> recommend" the default solution over the other solution(s).  I guess 
> what we really want to say is that we strongly recommend resolving the 
> problems before continuing.

Yes, good point.  I added your patch.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-15 19:02                                 ` Jon Turney
@ 2018-01-15 21:50                                   ` Ken Brown
  2018-01-18 19:14                                     ` Jon Turney
  0 siblings, 1 reply; 75+ messages in thread
From: Ken Brown @ 2018-01-15 21:50 UTC (permalink / raw)
  To: cygwin-apps

On 1/15/2018 2:01 PM, Jon Turney wrote:
> On 13/01/2018 19:56, Ken Brown wrote:
>> On 1/13/2018 9:14 AM, Jon Turney wrote:
>>> On 09/01/2018 15:49, Ken Brown wrote:
>>>> On 1/9/2018 10:37 AM, Ken Brown wrote:
>>>>>
>>>>> I just did a quick test, trying to uninstall B in the situation above,
>>>>> and it didn't work as expected.  Even though "Uninstall A" was the 
>>>>> first
>>>>> solution, A didn't get uninstalled.
>>>>
>>>> In case you want to replicate this, what I actually tried was 
>>>> uninstalling ImageMagick, which is required, directly or indirectly, 
>>>> by asymptote, dblatex, and xmlto.  Solution 1 was to uninstall these 
>>>> three but it didn't happen.
>>>
>>> Doh.
>>>
>>> solver_take_solution() modifies the task list (and I went to the 
>>> trouble of refactoring things to make it available to it), but I 
>>> didn't then re-solve with the modified task list.
>>>
>>> Also, looking at this again, it looks like the solver places 
>>> solutions which remove a task at the end of of the solution list, so 
>>> this should be the default
>>>
>>> (This matches the previous behaviour, where the default is to accept 
>>> dependencies i.e. if you try to remove a package required by other 
>>> packages, the default solution should be to cancel the removal)
>>>
>>> Maybe we need more complex criteria to identify the default, but this 
>>> seems to work in my limited testing.
>>
>> I noticed two things:
>>
>> 1. I think the code you added to PrereqPage::OnNext() also needs to be 
>> added to PrereqPage::OnBack().
> 
> Hmm... not sure.
> 
> I don't think we want to applyDefaultProblemSolutions(), because the 
> user should solve the problems their way.

Right.  I was forgetting that the user no longer has to select 'Back' to 
see the default solutions.  So we should probably delete the addendum to 
the problem report that tells them to do that.

> As to finalize():
> 
> Not doing augmentTasks() means that a reinstall would disappear when we 
> go back, so I guess we need that.
> 
> I don't think we want to addSource() as the consequences of that are 
> never shown in the chooser, currently.
> 
>> 2. We should probably remove, or at least reword, the dire warning 
>> about accepting the default solutions.  I'm not sure we want to 
>> "strongly recommend" the default solution over the other solution(s).  
>> I guess what we really want to say is that we strongly recommend 
>> resolving the problems before continuing.
> 
> Yes, good point.  I added your patch.

^ permalink raw reply	[flat|nested] 75+ messages in thread

* Re: [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP)
  2018-01-15 21:50                                   ` Ken Brown
@ 2018-01-18 19:14                                     ` Jon Turney
  0 siblings, 0 replies; 75+ messages in thread
From: Jon Turney @ 2018-01-18 19:14 UTC (permalink / raw)
  To: cygwin-apps

On 15/01/2018 21:48, Ken Brown wrote:
> On 1/15/2018 2:01 PM, Jon Turney wrote:
>> On 13/01/2018 19:56, Ken Brown wrote:
[...]>>> 1. I think the code you added to PrereqPage::OnNext() also 
needs to
>>> be added to PrereqPage::OnBack().
>>
>> Hmm... not sure.
>>
>> I don't think we want to applyDefaultProblemSolutions(), because the 
>> user should solve the problems their way.
> 
> Right.  I was forgetting that the user no longer has to select 'Back' to 
> see the default solutions.  So we should probably delete the addendum to 
> the problem report that tells them to do that.

Good point.

^ permalink raw reply	[flat|nested] 75+ messages in thread

end of thread, other threads:[~2018-01-18 19:14 UTC | newest]

Thread overview: 75+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-31 10:53 [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Jon Turney
2017-05-31 10:53 ` [PATCH setup 04/14] Hoist pick() up to packagemeta Jon Turney
2017-05-31 10:53 ` [PATCH setup 05/14] Hoist uninstall up to Installer::uninstallOne() Jon Turney
2017-05-31 10:53 ` [PATCH setup 03/14] Hoist addScript() etc. up from packageversion to packagemeta Jon Turney
2017-05-31 10:53 ` [PATCH setup 01/14] Opaque how PackageDepends is stored Jon Turney
2017-05-31 10:53 ` [PATCH setup 02/14] Factor out reading installed.db Jon Turney
2017-05-31 10:57 ` [PATCH setup 08/14] Change to using a libsolv pool for storing package information Jon Turney
2017-05-31 10:57 ` [PATCH setup 09/14] Remove cygpackage class Jon Turney
2017-05-31 10:57 ` [PATCH setup 10/14] Remove packageversion class Jon Turney
2017-05-31 10:57 ` [PATCH setup 06/14] Hoist scan() up from packageversion to packagemeta Jon Turney
2017-05-31 10:57 ` [PATCH setup 07/14] Store package stability in class packageversion Jon Turney
2017-05-31 11:05 ` [PATCH setup 11/14] Drop in SolvableVersion as a replacement for packageversion Jon Turney
2017-05-31 11:05   ` [PATCH setup 12/14] Use solver to check for problems and produce a list of package transactions Jon Turney
2017-05-31 11:05   ` [PATCH setup 13/14] Download/checksum/install/uninstall what transaction wants Jon Turney
2017-05-31 11:05   ` [PATCH setup 14/14] Add obsoletes: support Jon Turney
2017-08-29 13:37 ` [PATCH setup 00/14] Use libsolv, solve all our problems... (WIP) Ken Brown
2017-08-30 21:47   ` Ken Brown
2017-09-01 15:01 ` Ken Brown
2017-09-02 16:57   ` Ken Brown
2017-09-05 13:34     ` Jon Turney
2017-09-05 18:40       ` Achim Gratz
2017-09-06  2:52         ` Ken Brown
2017-11-23 18:10           ` Jon Turney
2017-11-23 20:32             ` Ken Brown
2017-11-23 20:54             ` Achim Gratz
2017-09-08 18:54 ` Ken Brown
2017-09-11 20:40   ` Ken Brown
2017-09-13 19:17     ` Achim Gratz
2017-09-13 21:16       ` Ken Brown
2017-09-14 17:26         ` Achim Gratz
2017-09-14 20:46           ` Ken Brown
2017-09-15 19:24             ` Jon Turney
2017-09-16 16:21               ` Ken Brown
2017-09-19 12:24                 ` Ken Brown
2017-09-19 16:46                   ` Jon Turney
2017-09-19 16:58                     ` Ken Brown
2017-12-05 14:32             ` Jon Turney
2017-12-05 17:36               ` Ken Brown
2017-12-13 17:31               ` Ken Brown
2017-12-13 18:06                 ` Achim Gratz
2017-12-13 22:31                   ` Ken Brown
2017-12-14 14:12                     ` Ken Brown
2017-12-24 15:00                     ` Ken Brown
2018-01-09 13:25                       ` Jon Turney
2018-01-09 15:37                         ` Ken Brown
2018-01-09 15:49                           ` Ken Brown
2018-01-13 14:14                             ` Jon Turney
2018-01-13 19:56                               ` Ken Brown
2018-01-13 21:29                                 ` Brian Inglis
2018-01-13 22:55                                   ` Ken Brown
2018-01-14  0:00                                     ` Ken Brown
2018-01-14  1:52                                       ` Brian Inglis
2018-01-14  2:37                                         ` Ken Brown
2018-01-15 19:02                                 ` Jon Turney
2018-01-15 21:50                                   ` Ken Brown
2018-01-18 19:14                                     ` Jon Turney
2017-09-15 15:15   ` Jon Turney
2017-09-15 16:53     ` Ken Brown
2017-09-15 20:56       ` cyg Simple
2017-09-17 16:02         ` Ken Brown
2017-09-26 14:50       ` Jon Turney
2017-09-26 16:07         ` Ken Brown
2017-09-27 19:14           ` Jon Turney
2017-09-27 20:33             ` Ken Brown
2017-09-29 17:38               ` Jon Turney
2017-09-29 20:34                 ` Ken Brown
2017-10-02 14:07                   ` Jon Turney
2017-10-02 15:17                     ` Marco Atzeri
2017-10-04 14:43                       ` Jon Turney
2017-10-10 11:18                   ` Ken Brown
2017-10-10 15:49                     ` Jon Turney
2017-10-17 12:45                     ` Ken Brown
2017-10-17 18:47                       ` Jon Turney
2017-10-18 15:28                         ` Ken Brown
2017-10-18 15:57                           ` Ken Brown

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).