[pacman-dev] [PATCH] alpm_checkdeps clean-up <- some notes

Xavier shiningxc at gmail.com
Sun Dec 2 13:20:51 EST 2007


On Sat, Dec 01, 2007 at 11:25:45PM +0100, Nagy Gabor wrote:
> > 4.
> > Hmm, how am I supposed to rebase your unneeded patch now? :)
> > Seems like the "causingpkg" is now hidden behind the alpm_list_find +
> > satisfycmp function.
> Well, then you must compute it...
> If we know a dependency that would be broken after -R, we just find a
> satisfier in the target list and keep that target.
> 

Well, I rebased your -Ru patch, and it seems to work fine (at least it didn't
break any pactest), but just in case, here is the important part of the rebased patch
(I will attach the whole patch as well) :

@@ -295,10 +296,17 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(pmdb_t *db, int reversedeps,
                        pmpkg_t *lp = i->data;
                        for(j = alpm_pkg_get_depends(lp); j; j = j->next) {
                                pmdepend_t *depend = j->data;
+                               int found = 0;
+                               pmpkg_t *causingpkg = NULL;
                                /* we won't break this depend, if it is already broken, we ignore it */
+                               for(k = modified; k && !found; k = k->next) {
+                                       causingpkg = k->data;
+                                       found = alpm_depcmp(causingpkg, depend);
+                               }
+
                                /* 1. check upgrade list for satisfiers */
                                /* 2. check dblist for satisfiers */
-                               if(alpm_list_find(modified, depend, satisfycmp) &&
+                               if(found &&
                                   !alpm_list_find(upgrade, depend, satisfycmp) &&
                                   !alpm_list_find(dblist, depend, satisfycmp)) {
                                        char *missdepstring = alpm_dep_get_string(depend);
@@ -306,7 +314,7 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(pmdb_t *db, int reversedeps,
                                                        missdepstring, alpm_pkg_get_name(lp));
                                        free(missdepstring);
                                        miss = _alpm_depmiss_new(lp->name, depend->mod,
-                                                       depend->name, depend->version);
+                                                       depend->name, depend->version, alpm_pkg_get_name(causingpkg));
                                        baddeps = alpm_list_add(baddeps, miss);
                                }
                        }
-------------- next part --------------
>From 6dc794a4f6f73feaca55a6c159f5fb78f785e54d Mon Sep 17 00:00:00 2001
From: Nagy Gabor <ngaba at bibl.u-szeged.hu>
Date: Sun, 18 Nov 2007 18:45:46 +0100
Subject: [PATCH] New remove option : -u / --unneeded (FS#6505).

With --unneeded option 'pacman -R' doesn't stop in case of dependency error;
it removes the needed-dependency targets from the target-list instead.  See
also: http://archlinux.org/pipermail/pacman-dev/2007-October/009653.html .

The patch also adds a new causingpkg field to pmdepmissing_t which indicates
the to-be-removed package which would cause a dependency break. This is
needed, because miss->depend.name may be a provision. miss->causingpkg will
be useful in -R dependency error messages too.

[Xavier: renamed inducer to causingpkg, removed the _alpm_pkgname_pkg_cmp
helper function as requested by Aaron. This might be added by a further
commit.  Other small cleanups, updated manpage and bash completion.]

Signed-off-by: Chantry Xavier <shiningxc at gmail.com>
---
 contrib/bash_completion    |    2 ++
 doc/pacman.8.txt           |    5 +++++
 lib/libalpm/alpm.h         |    4 +++-
 lib/libalpm/deps.c         |   28 +++++++++++++++++++++++-----
 lib/libalpm/deps.h         |    3 ++-
 lib/libalpm/remove.c       |   22 ++++++++++++++++++++++
 pactest/tests/remove049.py |   19 +++++++++++++++++++
 src/pacman/pacman.c        |    3 +++
 8 files changed, 79 insertions(+), 7 deletions(-)
 create mode 100644 pactest/tests/remove049.py

diff --git a/contrib/bash_completion b/contrib/bash_completion
index 844396d..4083659 100644
--- a/contrib/bash_completion
+++ b/contrib/bash_completion
@@ -187,6 +187,7 @@ _pacman ()
           dbonly) mod="${mod}k" ;;
           nosave) mod="${mod}n" ;;
           recursive) mod="${mod}s" ;;
+          unneeded) mod="${mod}u" ;;
         esac ;;
       *) toparse="${a}" ;;
     esac
@@ -242,6 +243,7 @@ _pacman ()
           -k --dbonly \
           -n --nosave \
           -s --recursive \
+          -u --unneeded \
           --config \
           --logfile \
           --noconfirm \
diff --git a/doc/pacman.8.txt b/doc/pacman.8.txt
index 887f912..95db0a1 100644
--- a/doc/pacman.8.txt
+++ b/doc/pacman.8.txt
@@ -208,6 +208,11 @@ Remove Options[[RO]]
 	explicitly installed by the user. This option is analogous to a
 	backwards '\--sync' operation.
 
+*-u, \--unneeded*::
+	Removes the targets that are not required by any other packages.
+	This is mostly useful when removing a group without using the '-c' option,
+	to avoid breaking any dependencies.
+
 
 Sync Options[[SO]]
 ------------------
diff --git a/lib/libalpm/alpm.h b/lib/libalpm/alpm.h
index 335ce39..a1ea88b 100644
--- a/lib/libalpm/alpm.h
+++ b/lib/libalpm/alpm.h
@@ -280,7 +280,8 @@ typedef enum _pmtransflag_t {
 	PM_TRANS_FLAG_DOWNLOADONLY = 0x200,
 	PM_TRANS_FLAG_NOSCRIPTLET = 0x400,
 	PM_TRANS_FLAG_NOCONFLICTS = 0x800,
-	PM_TRANS_FLAG_PRINTURIS = 0x1000
+	PM_TRANS_FLAG_PRINTURIS = 0x1000,
+	PM_TRANS_FLAG_UNNEEDED = 0x2000,
 } pmtransflag_t;
 
 /* Transaction Events */
@@ -375,6 +376,7 @@ alpm_list_t *alpm_checkdeps(pmdb_t *db, int reversedeps,
 
 const char *alpm_miss_get_target(const pmdepmissing_t *miss);
 pmdepend_t *alpm_miss_get_dep(pmdepmissing_t *miss);
+const char *alpm_miss_get_causingpkg(const pmdepmissing_t *miss);
 
 const char *alpm_conflict_get_package1(pmconflict_t *conflict);
 const char *alpm_conflict_get_package2(pmconflict_t *conflict);
diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c
index 350df6b..ff2c733 100644
--- a/lib/libalpm/deps.c
+++ b/lib/libalpm/deps.c
@@ -62,7 +62,7 @@ static void _alpm_graph_free(void *data)
 }
 
 pmdepmissing_t *_alpm_depmiss_new(const char *target, pmdepmod_t depmod,
-		const char *depname, const char *depversion)
+		const char *depname, const char *depversion, const char *causingpkg)
 {
 	pmdepmissing_t *miss;
 
@@ -78,6 +78,7 @@ pmdepmissing_t *_alpm_depmiss_new(const char *target, pmdepmod_t depmod,
 	} else {
 		miss->depend.version[0] = 0;
 	}
+	strncpy(miss->causingpkg, causingpkg, PKG_NAME_LEN);
 
 	return(miss);
 }
@@ -246,7 +247,7 @@ static int satisfycmp(const void *pkg, const void *depend)
 alpm_list_t SYMEXPORT *alpm_checkdeps(pmdb_t *db, int reversedeps,
 		alpm_list_t *remove, alpm_list_t *upgrade)
 {
-	alpm_list_t *i, *j;
+	alpm_list_t *i, *j, *k;
 	alpm_list_t *joined, *dblist;
 	alpm_list_t *baddeps = NULL;
 	pmdepmissing_t *miss = NULL;
@@ -279,7 +280,7 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(pmdb_t *db, int reversedeps,
 						missdepstring, alpm_pkg_get_name(tp));
 				free(missdepstring);
 				miss = _alpm_depmiss_new(alpm_pkg_get_name(tp), depend->mod,
-						depend->name, depend->version);
+						depend->name, depend->version, "");
 				baddeps = alpm_list_add(baddeps, miss);
 			}
 		}
@@ -295,10 +296,17 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(pmdb_t *db, int reversedeps,
 			pmpkg_t *lp = i->data;
 			for(j = alpm_pkg_get_depends(lp); j; j = j->next) {
 				pmdepend_t *depend = j->data;
+				int found = 0;
+				pmpkg_t *causingpkg = NULL;
 				/* we won't break this depend, if it is already broken, we ignore it */
+				for(k = modified; k && !found; k = k->next) {
+					causingpkg = k->data;
+					found = alpm_depcmp(causingpkg, depend);
+				}
+
 				/* 1. check upgrade list for satisfiers */
 				/* 2. check dblist for satisfiers */
-				if(alpm_list_find(modified, depend, satisfycmp) &&
+				if(found &&
 				   !alpm_list_find(upgrade, depend, satisfycmp) &&
 				   !alpm_list_find(dblist, depend, satisfycmp)) {
 					char *missdepstring = alpm_dep_get_string(depend);
@@ -306,7 +314,7 @@ alpm_list_t SYMEXPORT *alpm_checkdeps(pmdb_t *db, int reversedeps,
 							missdepstring, alpm_pkg_get_name(lp));
 					free(missdepstring);
 					miss = _alpm_depmiss_new(lp->name, depend->mod,
-							depend->name, depend->version);
+							depend->name, depend->version, alpm_pkg_get_name(causingpkg));
 					baddeps = alpm_list_add(baddeps, miss);
 				}
 			}
@@ -635,6 +643,16 @@ const char SYMEXPORT *alpm_miss_get_target(const pmdepmissing_t *miss)
 	return miss->target;
 }
 
+const char SYMEXPORT *alpm_miss_get_causingpkg(const pmdepmissing_t *miss)
+{
+	ALPM_LOG_FUNC;
+
+	/* Sanity checks */
+	ASSERT(miss != NULL, return(NULL));
+
+	return miss->causingpkg;
+}
+
 pmdepend_t SYMEXPORT *alpm_miss_get_dep(pmdepmissing_t *miss)
 {
 	ALPM_LOG_FUNC;
diff --git a/lib/libalpm/deps.h b/lib/libalpm/deps.h
index e4bba8b..d04f3f2 100644
--- a/lib/libalpm/deps.h
+++ b/lib/libalpm/deps.h
@@ -39,6 +39,7 @@ struct __pmdepend_t {
 struct __pmdepmissing_t {
 	char target[PKG_NAME_LEN];
 	pmdepend_t depend;
+	char causingpkg[PKG_NAME_LEN]; /* this is used in case of remove dependency error only */
 };
 
 /* Graphs */
@@ -51,7 +52,7 @@ struct __pmgraph_t {
 };
 
 pmdepmissing_t *_alpm_depmiss_new(const char *target, pmdepmod_t depmod,
-		const char *depname, const char *depversion);
+		const char *depname, const char *depversion, const char *causingpkg);
 int _alpm_depmiss_isin(pmdepmissing_t *needle, alpm_list_t *haystack);
 alpm_list_t *_alpm_sortbydeps(alpm_list_t *targets, pmtranstype_t mode);
 void _alpm_recursedeps(pmdb_t *db, alpm_list_t *targs, int include_explicit);
diff --git a/lib/libalpm/remove.c b/lib/libalpm/remove.c
index 349ff10..a49d166 100644
--- a/lib/libalpm/remove.c
+++ b/lib/libalpm/remove.c
@@ -125,6 +125,28 @@ int _alpm_remove_prepare(pmtrans_t *trans, pmdb_t *db, alpm_list_t **data)
 					FREELIST(lp);
 					lp = alpm_checkdeps(db, 1, trans->packages, NULL);
 				}
+			} else if (trans->flags & PM_TRANS_FLAG_UNNEEDED) {
+				/* Remove needed packages (which break dependencies) from the target list */
+				while(lp != NULL) {
+					alpm_list_t *i;
+					for(i = lp; i; i = i->next) {
+						pmdepmissing_t *miss = (pmdepmissing_t *)i->data;
+						void *vpkg;
+						pmpkg_t *pkg;
+						pmpkg_t *dummy = _alpm_pkg_new(miss->causingpkg, NULL);
+						trans->packages = alpm_list_remove(trans->packages, dummy,
+								_alpm_pkg_cmp, &vpkg);
+						_alpm_pkg_free(dummy);
+						pkg = vpkg;
+						if(pkg) {
+							_alpm_log(PM_LOG_WARNING, "removing %s from the target-list\n",
+									alpm_pkg_get_name(pkg));
+							_alpm_pkg_free(pkg);
+						}
+					}
+					FREELIST(lp);
+					lp = alpm_checkdeps(db, 1, trans->packages, NULL);
+				}
 			} else {
 				if(data) {
 					*data = lp;
diff --git a/pactest/tests/remove049.py b/pactest/tests/remove049.py
new file mode 100644
index 0000000..724f8da
--- /dev/null
+++ b/pactest/tests/remove049.py
@@ -0,0 +1,19 @@
+self.description = "-Ru test"
+
+lp1 = pmpkg("pkg1")
+lp1.requiredby = [ "pkg3" ]
+self.addpkg2db("local", lp1)
+
+lp2 = pmpkg("pkg2")
+self.addpkg2db("local", lp2)
+
+lp3 = pmpkg("pkg3")
+lp3.depends = [ "pkg1" ]
+self.addpkg2db("local", lp3)
+
+self.args = "-Ru pkg1 pkg2"
+
+self.addrule("PACMAN_RETCODE=0")
+self.addrule("PKG_EXIST=pkg1")
+self.addrule("!PKG_EXIST=pkg2")
+self.addrule("PKG_EXIST=pkg3")
\ No newline at end of file
diff --git a/src/pacman/pacman.c b/src/pacman/pacman.c
index 3f4af22..49b4424 100644
--- a/src/pacman/pacman.c
+++ b/src/pacman/pacman.c
@@ -91,6 +91,7 @@ static void usage(int op, const char * const myname)
 			printf(_("  -k, --dbonly         only remove database entry, do not remove files\n"));
 			printf(_("  -n, --nosave         remove configuration files as well\n"));
 			printf(_("  -s, --recursive      remove dependencies also (that won't break packages)\n"));
+			printf(_("  -u, --unneeded       remove unneeded packages (that won't break packages)\n"));
 		} else if(op == PM_OP_UPGRADE) {
 			printf("%s:  %s {-U --upgrade} [%s] <%s>\n", str_usg, myname, str_opt, str_file);
 			printf("%s:\n", str_opt);
@@ -273,6 +274,7 @@ static int parseargs(int argc, char *argv[])
 		{"orphans",    no_argument,       0, 't'},
 		{"upgrades",   no_argument,       0, 'u'},
 		{"sysupgrade", no_argument,       0, 'u'},
+		{"unneeded",   no_argument,       0, 'u'},
 		{"verbose",    no_argument,       0, 'v'},
 		{"downloadonly", no_argument,     0, 'w'},
 		{"refresh",    no_argument,       0, 'y'},
@@ -421,6 +423,7 @@ static int parseargs(int argc, char *argv[])
 			case 'u':
 				config->op_s_upgrade = 1;
 				config->op_q_upgrade = 1;
+				config->flags |= PM_TRANS_FLAG_UNNEEDED;
 				break;
 			case 'v': (config->verbose)++; break;
 			case 'w':
-- 
1.5.3.6



More information about the pacman-dev mailing list