Jon Turney wrote:
On 02/08/2022 13:17, Christian Franke wrote:
In long standing cygwin installations, many no longer needed automatically installed packages (e.g. libicuNN) accumulate. This patch adds a new view which is possibly helpful to cleanup packages manually.

Some possible later enhancements:
- automatically refresh this view (a few seconds) after the user changed a package status as this may add or remove entries. - add a keyboard shortcut (^U) to the list view for "Uninstall this package and then select next package"


Thanks.  This looks good.

I think perhaps a better approach would be a view showing all packages which aren't user_picked, or a dependency of a user_picked package.

This would drop the ability to easily clean up user_picked packages without later conflicts. The attached new patch splits this into two views "Removable" (not "Uninstallable" due to possible ambiguity with "cannot be installed") and "Unneeded" (or "Stale" ?).



(If I've read the code correctly your implementation has the weakness that if e.g. appA -> libbB -> libC, which is then changed to appA -> libD -> libE, it will only show libC as unneeded, then libB on the next run?)

I'm not sure for this case. It may be correct again after the view is refreshed during the same run. In general, this ad-hoc algorithm does not handle all corner cases. It should be sufficient if installation cleanup is done in a separate run.




+// Scan installed or desired packages and collect the names of packages
+// which provide the dependencies of other packages or are member of
+// category "Base".
+static void FindNeededPackages (const packagedb & db, std::set<std::string> & needed)
+{
+  std::map<std::string, std::string> providedBy;
+  for (const auto & p : db.packages)
+    {
+      const packagemeta & pkg = *p.second;
+      if (!pkg.isBinary ())
+        continue;
+      if (!(pkg.desired && (pkg.installed || pkg.picked ())))
+        continue;

This seems redundant.  Why can't this be just !pkg.desired?

Yes, fixed. I originally wanted to handle the "install source package without the binary" case here. During development of "Ctrl+I/R/U" patch, I learned that this could not happen.


This should also update the tooltip for the view dropdown (IDS_VIEWBUTTON_TOOLTIP) to describe the new view.


Done.

Open issue: An easy way to refresh the views after Uninstall requests (Ctrl+L ?).

From b31674d809a71bf17bb621c74e5ba7b3df3cd80a Mon Sep 17 00:00:00 2001
From: Christian Franke <christian.fra...@t-online.de>
Date: Mon, 15 Aug 2022 14:21:36 +0200
Subject: [PATCH] Add view modes "Removable" and "Unneeded"

These views show user picked or automatically installed packages which
could be safely removed.
---
 PickView.cc   | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++-
 PickView.h    |  2 ++
 res/en/res.rc | 12 +++++++++++-
 res/fr/res.rc |  4 ++++
 resource.h    |  2 ++
 5 files changed, 71 insertions(+), 2 deletions(-)

diff --git a/PickView.cc b/PickView.cc
index c961b9f..7d60d8a 100644
--- a/PickView.cc
+++ b/PickView.cc
@@ -17,6 +17,7 @@
 #include "PickPackageLine.h"
 #include "PickCategoryLine.h"
 #include <algorithm>
+#include <set>
 #include <limits.h>
 #include <shlwapi.h>
 
@@ -28,6 +29,39 @@
 #include "LogSingleton.h"
 #include "Exception.h"
 
+// Scan desired packages and collect the names of packages which provide the
+// dependencies of other desired packages or are member of category "Base".
+static void FindNeededPackages (const packagedb & db, std::set<std::string> & 
needed)
+{
+  std::map<std::string, std::string> providedBy;
+  for (const auto & p : db.packages)
+    {
+      const packagemeta & pkg = *p.second;
+      if (!pkg.isBinary ())
+        continue;
+      if (!pkg.desired)
+        continue;
+      for (const PackageSpecification *s : pkg.desired.provides ())
+        providedBy.insert ({s->packageName (), pkg.name});
+    }
+  for (const auto & p : db.packages)
+    {
+      const packagemeta & pkg = *p.second;
+      if (!pkg.isBinary ())
+        continue;
+      if (pkg.categories.count ("Base"))
+        needed.insert (pkg.name);
+      if (!pkg.desired)
+        continue;
+      for (const PackageSpecification *s : pkg.desired.depends ()) {
+        const auto i = providedBy.find (s->packageName ());
+        if (i == providedBy.end ())
+          continue;
+        needed.insert (i->second);
+      }
+    }
+}
+
 void
 PickView::setViewMode (views mode)
 {
@@ -47,6 +81,11 @@ PickView::setViewMode (views mode)
     }
   else
     {
+      std::set<std::string> needed;
+      if (view_mode == PickView::views::PackageRemovable
+          || view_mode == PickView::views::PackageUnneeded)
+        FindNeededPackages (db, needed);
+
       // iterate through every package
       for (packagedb::packagecollection::iterator i = db.packages.begin ();
             i != db.packages.end (); ++i)
@@ -77,7 +116,15 @@ PickView::setViewMode (views mode)
 
               // "UserPick" : installed packages that were picked by user
               || (view_mode == PickView::views::PackageUserPicked &&
-                  (pkg.installed && pkg.user_picked)))
+                  (pkg.installed && pkg.user_picked))
+
+              // "Removable" : user picked packages that could be safely 
removed
+              || (view_mode == PickView::views::PackageRemovable &&
+                  (pkg.installed && pkg.user_picked && !needed.count 
(pkg.name)))
+
+              // "Unneeded" : auto installed packages that could be safely 
removed
+              || (view_mode == PickView::views::PackageUnneeded &&
+                  (pkg.installed && !pkg.user_picked && !needed.count 
(pkg.name))))
             {
               // Filter by package name
               if (packageFilterString.empty ()
@@ -111,6 +158,10 @@ PickView::mode_caption (views mode)
       return IDS_VIEW_NOTINSTALLED;
     case views::PackageUserPicked:
       return IDS_VIEW_PICKED;
+    case views::PackageRemovable:
+      return IDS_VIEW_REMOVABLE;
+    case views::PackageUnneeded:
+      return IDS_VIEW_UNNEEDED;
     case views::Category:
       return IDS_VIEW_CATEGORY;
     default:
diff --git a/PickView.h b/PickView.h
index 1e14a74..4833414 100644
--- a/PickView.h
+++ b/PickView.h
@@ -36,6 +36,8 @@ public:
     PackageKeeps,
     PackageSkips,
     PackageUserPicked,
+    PackageRemovable,
+    PackageUnneeded,
     Category,
   };
 
diff --git a/res/en/res.rc b/res/en/res.rc
index 644b252..fc61e59 100644
--- a/res/en/res.rc
+++ b/res/en/res.rc
@@ -537,7 +537,15 @@ BEGIN
        "and haven't been selected for installation.\n"
        "\n"
        "Picked: Show installed packages that were selected, not installed "
-       "as a dependency."
+       "as a dependency.\n"
+       "\n"
+       "Removable: Show installed packages that were selected and could be "
+       "safely removed.  No other installed or selected packages depend on "
+       "these packages.\n"
+       "\n"
+       "Unneeded: Show automatically installed packages that could now be "
+       "safely removed.  Other installed or selected packages no longer "
+       "depend on these packages."
     IDS_HIDEOBS_TOOLTIP     "If selected, setup will hide packages in 
categories "
        "with names that begin with '_'.  Such packages are usually empty "
        "placeholders for packages that have been removed or renamed, or are "
@@ -578,6 +586,8 @@ BEGIN
     IDS_VIEW_UPTODATE "Up To Date"
     IDS_VIEW_NOTINSTALLED "Not Installed"
     IDS_VIEW_PICKED "Picked"
+    IDS_VIEW_REMOVABLE "Removable"
+    IDS_VIEW_UNNEEDED "Unneeded"
     IDS_VIEW_CATEGORY "Category"
     IDS_COLUMN_PACKAGE "Package"
     IDS_COLUMN_CURRENT "Current"
diff --git a/res/fr/res.rc b/res/fr/res.rc
index a0a7909..681aea4 100644
--- a/res/fr/res.rc
+++ b/res/fr/res.rc
@@ -524,6 +524,8 @@ BEGIN
        "\n"
        "Choisi : montre les paquets installés qui ont été sélectionnés, et non 
installés "
        "pour résoudre une dépendance."
+       // "Removable: ... XXX: Missing translation"
+       // "Unneeded: ... XXX: Missing translation"
     IDS_HIDEOBS_TOOLTIP     "L'assistant cachera les paquets des catégories 
dont "
        "le nom commence par '_'.  Ces paquets sont vides et sont des 
emplacements "
        "pour des paquets éliminés ou renommés, ou encore des paquets "
@@ -564,6 +566,8 @@ BEGIN
     IDS_VIEW_UPTODATE "À jour"
     IDS_VIEW_NOTINSTALLED "Non installé"
     IDS_VIEW_PICKED "Sélectionné"
+    // IDS_VIEW_REMOVABLE "XXX: missing translation"
+    // IDS_VIEW_UNNEEDED "XXX: missing translation"
     IDS_VIEW_CATEGORY  "Catégorie"
     IDS_COLUMN_PACKAGE "Paquet"
     IDS_COLUMN_CURRENT "Actuel"
diff --git a/resource.h b/resource.h
index 2668dd9..cfe860b 100644
--- a/resource.h
+++ b/resource.h
@@ -105,6 +105,8 @@
 #define IDS_FILE_INUSE_MSG               1208
 #define IDS_DEPRECATED_WINDOWS_VERSION   1209
 #define IDS_DEPRECATED_WINDOWS_ARCH      1210
+#define IDS_VIEW_REMOVABLE               1211
+#define IDS_VIEW_UNNEEDED                1212
 
 #define IDS_HELPTEXT_COMPACTOS           1500
 #define IDS_HELPTEXT_PUBKEY              1501
-- 
2.37.1

Reply via email to