public inbox for cygwin-apps@cygwin.com
 help / color / mirror / Atom feed
From: Jon Turney <jon.turney@dronecode.org.uk>
To: cygwin-apps@cygwin.com
Cc: Jon Turney <jon.turney@dronecode.org.uk>
Subject: [PATCH setup 08/10] Track if a package was installed by user, or as a dependency
Date: Tue, 02 Aug 2016 15:31:00 -0000	[thread overview]
Message-ID: <20160802153037.125216-9-jon.turney@dronecode.org.uk> (raw)
In-Reply-To: <20160802153037.125216-1-jon.turney@dronecode.org.uk>

Update the installed.db file format to version 3:
- Write installed version as a version, rather than as a notional filename.
- Also write user_picked flag

This extends the semantics of user_pick somewhat: currently it is only used
for UI purposes, to record if a package was picked in the current session.

Now we also use it to record if an installed package has ever been picked
via the UI (otherwise it is only installed because it is a dependency).

So, we are careful not to set it when a currently installed package has it's
installed version adjusted via the GUI.

We also arrange for user_pick to be set when a package was selected for
installation via CLI.

Add a heuristic to initially populate user_pick when upgrading from older
installed.db formats: All non-base installed packages which aren't
dependenies are assumed to be user_pick-ed.

Note: other tools (e.g. cygcheck) which read the installed.db file will need
updating appropriately
---
 ini.cc          |   4 ++
 package_db.cc   | 148 +++++++++++++++++++++++++++++++++++++++++++++++++-------
 package_db.h    |   3 ++
 package_meta.cc |   4 +-
 4 files changed, 140 insertions(+), 19 deletions(-)

diff --git a/ini.cc b/ini.cc
index 456eb6e..f925bf5 100644
--- a/ini.cc
+++ b/ini.cc
@@ -51,6 +51,7 @@
 #include "compress.h"
 #include "Exception.h"
 #include "crypto.h"
+#include "package_db.h"
 
 extern ThreeBarProgressPage Progress;
 
@@ -351,6 +352,9 @@ do_ini_thread (HINSTANCE h, HWND owner)
   else
     ini_count = do_remote_ini (owner);
 
+  packagedb db;
+  db.upgrade();
+
   if (ini_count == 0)
     return false;
 
diff --git a/package_db.cc b/package_db.cc
index f437daf..18f4f37 100644
--- a/package_db.cc
+++ b/package_db.cc
@@ -42,6 +42,7 @@ static const char *cvsid =
 #include "package_meta.h"
 #include "Exception.h"
 #include "Generic.h"
+#include "LogSingleton.h"
 
 using namespace std;
 
@@ -55,23 +56,26 @@ packagedb::packagedb ()
       installeddbread = 1;
       if (!db)
 	return;
-      /* flush_local_db_package_data */
-      char line[1000], pkgname[1000], inst[1000];
-      int instsz;
+      char line[1000], pkgname[1000];
 
       if (db->gets (line, 1000))
 	{
+	  /* Look for header line (absent in version 1) */
+	  int instsz;
 	  int dbver;
 	  sscanf (line, "%s %d", pkgname, &instsz);
-	  if (!strcasecmp (pkgname, "INSTALLED.DB") && instsz == 2)
-	    dbver = 2;
+	  if (!strcasecmp (pkgname, "INSTALLED.DB") && instsz <= 3)
+	    dbver = instsz;
 	  else
 	    dbver = 1;
 	  delete db;
 	  db = 0;
+
 	  /* Later versions may not use installed.db other than to record the version. */
 	  if (dbver == 1 || dbver == 2)
 	    {
+	      char inst[1000];
+
 	      db =
 		io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
 	      if (dbver == 2)
@@ -106,19 +110,63 @@ packagedb::packagedb ()
 
 		  packageversion binary = 
 		    cygpackage::createInstance (pkgname, f.ver,
-	    					package_installed,
-	    					package_binary);
+						package_installed,
+						package_binary);
+
+		  pkg->add_version (binary);
+		  pkg->set_installed (binary);
+		  pkg->desired = pkg->installed;
+		}
+	      delete db;
+	      db = 0;
+	    }
+	  else if (dbver == 3)
+	    {
+	      char ver[1000];
+
+	      db = io_stream::open ("cygfile:///etc/setup/installed.db", "rt", 0);
+
+	      // skip over already-parsed header line
+	      db->gets (line, 1000);
+
+	      while (db->gets (line, 1000))
+		{
+		  pkgname[0] = '\0';
+		  ver[0] = '\0';
+		  int user_picked = 0;
+
+		  /*
+		     In an INSTALLED.DB 3, the lines consist of:
+		     packagename installed-version user-picked
+		   */
+		  int res = sscanf (line, "%s %s %d", pkgname, ver, &user_picked);
 
+		  if (res < 3 || pkgname[0] == '\0' || ver[0] == '\0')
+			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, ver, package_installed, package_binary);
 		  pkg->add_version (binary);
 		  pkg->set_installed (binary);
 		  pkg->desired = pkg->installed;
+		  pkg->user_picked = (user_picked != 0);
 		}
 	      delete db;
 	      db = 0;
 	    }
 	  else
-	    // unknown dbversion
-	    exit (1);
+	    {
+	      Log (LOG_PLAIN) << "unknown INSTALLED.DB version " << dbver << endLog;
+	      exit (1);
+	    }
+
+	  installeddbver = dbver;
 	}
     }
 }
@@ -138,21 +186,17 @@ packagedb::flush ()
   if (!ndb)
     return errno ? errno : 1;
 
-  ndb->write ("INSTALLED.DB 2\n", strlen ("INSTALLED.DB 2\n"));
+  ndb->write ("INSTALLED.DB 3\n", strlen ("INSTALLED.DB 3\n"));
   for (packagedb::packagecollection::iterator i = packages.begin ();
        i != packages.end (); ++i)
     {
       packagemeta & pkgm = *(i->second);
       if (pkgm.installed)
 	{
-	  /* size here is irrelevant - as we can assume that this install source
-	   * no longer exists, and it does not correlate to used disk space
-	   * also note that we are writing a fictional install source 
-	   * to keep cygcheck happy.               
-	   */
 	  std::string line;
-	  line = pkgm.name + " " + pkgm.name + "-" + 
-	    std::string(pkgm.installed.Canonical_version()) + ".tar.bz2 0\n";
+	  line = pkgm.name + " " +
+	    std::string(pkgm.installed.Canonical_version()) + " " +
+	    (pkgm.user_picked ? "1" : "0") + "\n";
 	  ndb->write (line.c_str(), line.size());
 	}
     }
@@ -166,6 +210,18 @@ packagedb::flush ()
   return 0;
 }
 
+void
+packagedb::upgrade()
+{
+  if (installeddbver < 3)
+    {
+      /* Guess which packages were user_picked.  This has to take place after
+         setup.ini has been parsed as it needs dependency information. */
+      guessUserPicked();
+      installeddbver = 3;
+    }
+}
+
 packagemeta *
 packagedb::findBinary (PackageSpecification const &spec) const
 {
@@ -199,13 +255,13 @@ packagedb::findSource (PackageSpecification const &spec) const
 /* static members */
 
 int packagedb::installeddbread = 0;
+int packagedb::installeddbver = 0;
 packagedb::packagecollection packagedb::packages;
 packagedb::categoriesType packagedb::categories;
 packagedb::packagecollection packagedb::sourcePackages;
 PackageDBActions packagedb::task = PackageDB_Install;
 std::vector <packagemeta *> packagedb::dependencyOrderedPackages;
 
-#include "LogSingleton.h"
 #include <stack>
 
 class
@@ -449,3 +505,59 @@ packagedb::defaultTrust (trusts trust)
         packagedb::categories.erase (n++);
       }
 }
+
+void
+packagedb::guessUserPicked()
+{
+  /*
+    Assume that any non-base installed package which is a dependency of an
+    installed package wasn't user_picked
+
+    i.e. only installed packages which aren't in the base category, and aren't
+    a dependency of any installed package are user_picked
+  */
+
+  /* First mark all installed non-base packages */
+  for (packagedb::packagecollection::iterator i = packages.begin ();
+       i != packages.end (); ++i)
+    {
+      packagemeta & pkgm = *(i->second);
+
+      if (pkgm.categories.find ("Base") != pkgm.categories.end ())
+	continue;
+
+      if (pkgm.installed)
+	pkgm.user_picked = TRUE;
+    }
+
+  /* Then clear the mark for all dependencies of all installed packages */
+  for (packagedb::packagecollection::iterator i = packages.begin ();
+       i != packages.end (); ++i)
+    {
+      packagemeta & pkgm = *(i->second);
+
+      if (!pkgm.installed)
+	continue;
+
+      /* walk through each and clause */
+      vector <vector <PackageSpecification *> *>::const_iterator dp = pkgm.installed.depends()->begin();
+      while (dp != pkgm.installed.depends()->end())
+	{
+	  /* check each or clause for an installed match */
+	  vector <PackageSpecification *>::const_iterator i = find_if ((*dp)->begin(), (*dp)->end(), checkForInstalled);
+	  if (i != (*dp)->end())
+	    {
+	      const packagedb::packagecollection::iterator n = packages.find((*i)->packageName());
+	      if (n != packages.end())
+		{
+		  packagemeta *pkgm2 = n->second;
+		  pkgm2->user_picked = FALSE;
+		}
+	      /* skip to next and clause */
+	      ++dp;
+	      continue;
+	    }
+	  ++dp;
+	}
+    }
+}
diff --git a/package_db.h b/package_db.h
index bc828a1..6a99398 100644
--- a/package_db.h
+++ b/package_db.h
@@ -65,6 +65,7 @@ public:
   packagedb ();
   /* 0 on success */
   int flush ();
+  void upgrade ();
   packagemeta * findBinary (PackageSpecification const &) const;
   packagemeta * findSource (PackageSpecification const &) const;
   PackageDBConnectedIterator connectedBegin();
@@ -84,8 +85,10 @@ public:
   static PackageDBActions task;
 private:
   static int installeddbread;	/* do we have to reread this */
+  static int installeddbver;
   friend class ConnectedLoopFinder;
   static std::vector <packagemeta *> dependencyOrderedPackages;
+  void guessUserPicked(void);
 };
 
 #endif /* SETUP_PACKAGE_DB_H */
diff --git a/package_meta.cc b/package_meta.cc
index 21b21ef..3923b13 100644
--- a/package_meta.cc
+++ b/package_meta.cc
@@ -458,7 +458,8 @@ packagemeta::set_action (trusts const trust)
   else
     desired = packageversion ();
   /* Memorize the fact that the user picked at least once. */
-  user_picked = true;
+  if (!installed)
+    user_picked = true;
 }
 
 int
@@ -510,6 +511,7 @@ packagemeta::set_action (_actions action, packageversion const &default_version)
 	  if (desired != installed)
 	    if (desired.accessible ())
 	      {
+		user_picked = true;
 		desired.pick (true, this);
 		desired.sourcePackage ().pick (false, NULL);
 	      }
-- 
2.8.3

  parent reply	other threads:[~2016-08-02 15:31 UTC|newest]

Thread overview: 24+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2016-08-02 15:31 [PATCH setup 00/10] Various setup patches Jon Turney
2016-08-02 15:31 ` [PATCH setup 06/10] Remove obsolete installed_from member from packagemeta Jon Turney
2016-08-02 15:31 ` [PATCH setup 04/10] Downgrade "Running preremove script" logging to debug Jon Turney
2016-08-02 15:31 ` [PATCH setup 03/10] Add lex and yacc generated files to .gitignore Jon Turney
2016-08-02 15:31 ` Jon Turney [this message]
2016-08-03 10:49   ` [PATCH setup 08/10] Track if a package was installed by user, or as a dependency Jon Turney
2016-08-02 15:31 ` [PATCH setup 07/10] Remove unused fn member from cygpackage Jon Turney
2016-08-02 15:31 ` [PATCH setup 09/10] Add an additional filter view, showing packages which were user picked Jon Turney
2016-08-02 15:31 ` [PATCH setup 01/10] Remove stray execute permissions Jon Turney
2016-08-02 15:31 ` [PATCH setup 05/10] Properly report progress in PrereqChecker::isMet Jon Turney
2016-08-02 15:31 ` [PATCH setup 10/10] Reserve paths starting "." for package metadata Jon Turney
2016-08-02 15:31 ` [PATCH setup 02/10] Prevent libtool warning that a getopt++ shared library cannot be built Jon Turney
2016-08-03  7:10 ` [PATCH setup 00/10] Various setup patches Achim Gratz
2016-08-03  8:35   ` Corinna Vinschen
2016-08-03  9:52     ` Achim Gratz
2016-08-03 17:40       ` Corinna Vinschen
2016-08-03 18:28         ` Achim Gratz
2016-08-03 18:43           ` Corinna Vinschen
2016-08-03 19:52             ` Achim Gratz
2016-08-04 11:40               ` Corinna Vinschen
2016-08-04 17:57                 ` Achim Gratz
2016-08-04 18:00                   ` Corinna Vinschen
2016-08-04 19:26                     ` Achim Gratz
2016-08-03 17:30 ` Corinna Vinschen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20160802153037.125216-9-jon.turney@dronecode.org.uk \
    --to=jon.turney@dronecode.org.uk \
    --cc=cygwin-apps@cygwin.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).