summaryrefslogtreecommitdiffstats
path: root/lib/libalpm/deps.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libalpm/deps.c')
-rw-r--r--lib/libalpm/deps.c858
1 files changed, 401 insertions, 457 deletions
diff --git a/lib/libalpm/deps.c b/lib/libalpm/deps.c
index 9295fabe..7529ec98 100644
--- a/lib/libalpm/deps.c
+++ b/lib/libalpm/deps.c
@@ -1,10 +1,10 @@
/*
* deps.c
- *
+ *
* Copyright (c) 2002-2006 by Judd Vinet <jvinet@zeroflux.org>
* Copyright (c) 2005 by Aurelien Foret <orelien@chez.com>
* Copyright (c) 2005, 2006 by Miklos Vajna <vmiklos@frugalware.org>
- *
+ *
* 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
@@ -17,7 +17,7 @@
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
* USA.
*/
@@ -26,11 +26,6 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
-#ifdef __sun__
-#include <strings.h>
-#endif
-#include <libintl.h>
-#include <math.h>
/* libalpm */
#include "deps.h"
@@ -41,32 +36,45 @@
#include "package.h"
#include "db.h"
#include "cache.h"
-#include "provide.h"
-#include "versioncmp.h"
#include "handle.h"
-extern pmhandle_t *handle;
+static pmgraph_t *_alpm_graph_new(void)
+{
+ pmgraph_t *graph = NULL;
+
+ MALLOC(graph, sizeof(pmgraph_t), RET_ERR(PM_ERR_MEMORY, NULL));
+
+ if(graph) {
+ graph->state = 0;
+ graph->data = NULL;
+ graph->parent = NULL;
+ graph->children = NULL;
+ graph->childptr = NULL;
+ }
+ return(graph);
+}
+
+static void _alpm_graph_free(void *data)
+{
+ pmgraph_t *graph = data;
+ alpm_list_free(graph->children);
+ free(graph);
+}
-pmdepmissing_t *_alpm_depmiss_new(const char *target, pmdeptype_t type,
- pmdepmod_t depmod, const char *depname,
- const char *depversion)
+pmdepmissing_t *_alpm_depmiss_new(const char *target, pmdepmod_t depmod,
+ const char *depname, const char *depversion)
{
pmdepmissing_t *miss;
ALPM_LOG_FUNC;
- miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t));
- if(miss == NULL) {
- _alpm_log(PM_LOG_ERROR, _("malloc failure: could not allocate %d bytes"), sizeof(pmdepmissing_t));
- RET_ERR(PM_ERR_MEMORY, NULL);
- }
+ MALLOC(miss, sizeof(pmdepmissing_t), RET_ERR(PM_ERR_MEMORY, NULL));
- STRNCPY(miss->target, target, PKG_NAME_LEN);
- miss->type = type;
+ strncpy(miss->target, target, PKG_NAME_LEN);
miss->depend.mod = depmod;
- STRNCPY(miss->depend.name, depname, PKG_NAME_LEN);
+ strncpy(miss->depend.name, depname, PKG_NAME_LEN);
if(depversion) {
- STRNCPY(miss->depend.version, depversion, PKG_VERSION_LEN);
+ strncpy(miss->depend.version, depversion, PKG_VERSION_LEN);
} else {
miss->depend.version[0] = 0;
}
@@ -74,21 +82,43 @@ pmdepmissing_t *_alpm_depmiss_new(const char *target, pmdeptype_t type,
return(miss);
}
-int _alpm_depmiss_isin(pmdepmissing_t *needle, alpm_list_t *haystack)
+/* Convert a list of pmpkg_t * to a graph structure,
+ * with a edge for each dependency.
+ * Returns a list of vertices (one vertex = one package)
+ * (used by alpm_sortbydeps)
+ */
+static alpm_list_t *_alpm_graph_init(alpm_list_t *targets)
{
- alpm_list_t *i;
-
- ALPM_LOG_FUNC;
+ alpm_list_t *i, *j, *k;
+ alpm_list_t *vertices = NULL;
+ /* We create the vertices */
+ for(i = targets; i; i = i->next) {
+ pmgraph_t *vertex = _alpm_graph_new();
+ vertex->data = (void *)i->data;
+ vertices = alpm_list_add(vertices, vertex);
+ }
- for(i = haystack; i; i = i->next) {
- pmdepmissing_t *miss = i->data;
- if(!memcmp(needle, miss, sizeof(pmdepmissing_t))
- && !memcmp(&needle->depend, &miss->depend, sizeof(pmdepend_t))) {
- return(1);
+ /* We compute the edges */
+ for(i = vertices; i; i = i->next) {
+ pmgraph_t *vertex_i = i->data;
+ pmpkg_t *p_i = vertex_i->data;
+ /* TODO this should be somehow combined with alpm_checkdeps */
+ for(j = vertices; j; j = j->next) {
+ pmgraph_t *vertex_j = j->data;
+ pmpkg_t *p_j = vertex_j->data;
+ int child = 0;
+ for(k = alpm_pkg_get_depends(p_i); k && !child; k = k->next) {
+ pmdepend_t *depend = k->data;
+ child = alpm_depcmp(p_j, depend);
+ }
+ if(child) {
+ vertex_i->children =
+ alpm_list_add(vertex_i->children, vertex_j);
+ }
}
+ vertex_i->childptr = vertex_i->children;
}
-
- return(0);
+ return(vertices);
}
/* Re-order a list of target packages with respect to their dependencies.
@@ -99,21 +129,19 @@ int _alpm_depmiss_isin(pmdepmissing_t *needle, alpm_list_t *haystack)
* Target order is A,B,C,D
*
* Should be re-ordered to C,A,B,D
- *
+ *
* mode should be either PM_TRANS_TYPE_ADD or PM_TRANS_TYPE_REMOVE. This
* affects the dependency order sortbydeps() will use.
*
* This function returns the new alpm_list_t* target list.
*
- */
+ */
alpm_list_t *_alpm_sortbydeps(alpm_list_t *targets, pmtranstype_t mode)
{
alpm_list_t *newtargs = NULL;
- alpm_list_t *i, *j, *k, *l;
- int change = 1;
- int numscans = 0;
- int numtargs = 0;
- int maxscans;
+ alpm_list_t *vertices = NULL;
+ alpm_list_t *vptr;
+ pmgraph_t *vertex;
ALPM_LOG_FUNC;
@@ -121,90 +149,86 @@ alpm_list_t *_alpm_sortbydeps(alpm_list_t *targets, pmtranstype_t mode)
return(NULL);
}
- for(i = targets; i; i = i->next) {
- newtargs = alpm_list_add(newtargs, i->data);
- numtargs++;
- }
+ _alpm_log(PM_LOG_DEBUG, "started sorting dependencies\n");
- maxscans = (int)sqrt(numtargs);
+ vertices = _alpm_graph_init(targets);
- _alpm_log(PM_LOG_DEBUG, _("started sorting dependencies"));
- while(change) {
- alpm_list_t *tmptargs = NULL;
- change = 0;
- if(numscans > maxscans) {
- _alpm_log(PM_LOG_DEBUG, _("possible dependency cycle detected"));
- continue;
- }
- numscans++;
- /* run thru targets, moving up packages as necessary */
- for(i = newtargs; i; i = i->next) {
- pmpkg_t *p = i->data;
- _alpm_log(PM_LOG_DEBUG, " sorting %s", alpm_pkg_get_name(p));
- for(j = alpm_pkg_get_depends(p); j; j = j->next) {
- pmdepend_t *depend = alpm_splitdep(j->data);
- pmpkg_t *q = NULL;
- if(depend == NULL) {
- continue;
- }
- /* look for depend->name -- if it's farther down in the list, then
- * move it up above p
- */
- for(k = i->next; k; k = k->next) {
- q = k->data;
- const char *qname = alpm_pkg_get_name(q);
- if(!strcmp(depend->name, qname)) {
- if(!_alpm_pkg_find(qname, tmptargs)) {
- change = 1;
- tmptargs = alpm_list_add(tmptargs, q);
- }
- break;
- }
- for(l = alpm_pkg_get_provides(q); l; l = l->next) {
- const char *provname = l->data;
- if(!strcmp(depend->name, provname)) {
- if(!_alpm_pkg_find(qname, tmptargs)) {
- change = 1;
- tmptargs = alpm_list_add(tmptargs, q);
- }
- break;
- }
- }
+ vptr = vertices;
+ vertex = vertices->data;
+ while(vptr) {
+ /* mark that we touched the vertex */
+ vertex->state = -1;
+ int found = 0;
+ while(vertex->childptr && !found) {
+ pmgraph_t *nextchild = (vertex->childptr)->data;
+ vertex->childptr = (vertex->childptr)->next;
+ if (nextchild->state == 0) {
+ found = 1;
+ nextchild->parent = vertex;
+ vertex = nextchild;
+ }
+ else if(nextchild->state == -1) {
+ pmpkg_t *vertexpkg = vertex->data;
+ pmpkg_t *childpkg = nextchild->data;
+ _alpm_log(PM_LOG_WARNING, _("dependency cycle detected:\n"));
+ if(mode == PM_TRANS_TYPE_REMOVE) {
+ _alpm_log(PM_LOG_WARNING, _("%s will be removed after its %s dependency\n"), vertexpkg->name, childpkg->name);
+ } else {
+ _alpm_log(PM_LOG_WARNING, _("%s will be installed before its %s dependency\n"), vertexpkg->name, childpkg->name);
}
- free(depend);
}
- if(!_alpm_pkg_find(alpm_pkg_get_name(p), tmptargs)) {
- tmptargs = alpm_list_add(tmptargs, p);
+ }
+ if(!found) {
+ newtargs = alpm_list_add(newtargs, vertex->data);
+ /* mark that we've left this vertex */
+ vertex->state = 1;
+ vertex = vertex->parent;
+ if(!vertex) {
+ vptr = vptr->next;
+ while(vptr) {
+ vertex = vptr->data;
+ if (vertex->state == 0) break;
+ vptr = vptr->next;
+ }
}
}
- FREELISTPTR(newtargs);
- newtargs = tmptargs;
}
- _alpm_log(PM_LOG_DEBUG, _("sorting dependencies finished"));
+
+ _alpm_log(PM_LOG_DEBUG, "sorting dependencies finished\n");
if(mode == PM_TRANS_TYPE_REMOVE) {
/* we're removing packages, so reverse the order */
alpm_list_t *tmptargs = alpm_list_reverse(newtargs);
/* free the old one */
- FREELISTPTR(newtargs);
+ alpm_list_free(newtargs);
newtargs = tmptargs;
}
+ alpm_list_free_inner(vertices, _alpm_graph_free);
+ alpm_list_free(vertices);
+
return(newtargs);
}
-/** Checks dependencies and returns missing ones in a list. Dependencies can include versions with depmod operators.
- * @param trans pointer to the transaction object
+/* Little helper function for alpm_list_find */
+static int satisfycmp(const void *pkg, const void *depend)
+{
+ return(!alpm_depcmp((pmpkg_t*) pkg, (pmdepend_t*) depend));
+}
+
+/** Checks dependencies and returns missing ones in a list.
+ * Dependencies can include versions with depmod operators.
* @param db pointer to the local package database
- * @param op transaction type
- * @param packages an alpm_list_t* of packages to be checked
- * @return an alpm_list_t* of missing_t pointers.
+ * @param reversedeps handles the backward dependencies
+ * @param remove an alpm_list_t* of packages to be removed
+ * @param upgrade an alpm_list_t* of packages to be upgraded (remove-then-upgrade)
+ * @return an alpm_list_t* of pmpkg_t* of missing_t pointers.
*/
-alpm_list_t *_alpm_checkdeps(pmtrans_t *trans, pmdb_t *db, pmtranstype_t op,
- alpm_list_t *packages)
+alpm_list_t SYMEXPORT *alpm_checkdeps(pmdb_t *db, int reversedeps,
+ alpm_list_t *remove, alpm_list_t *upgrade)
{
- alpm_list_t *i, *j, *k, *l;
- int found = 0;
+ alpm_list_t *i, *j;
+ alpm_list_t *joined, *dblist;
alpm_list_t *baddeps = NULL;
pmdepmissing_t *miss = NULL;
@@ -214,376 +238,271 @@ alpm_list_t *_alpm_checkdeps(pmtrans_t *trans, pmdb_t *db, pmtranstype_t op,
return(NULL);
}
- if(op == PM_TRANS_TYPE_UPGRADE) {
- /* PM_TRANS_TYPE_UPGRADE handles the backwards dependencies, ie, the packages
- * listed in the requiredby field.
- */
- for(i = packages; i; i = i->next) {
- pmpkg_t *newpkg = i->data;
- pmpkg_t *oldpkg;
- if(newpkg == NULL) {
- _alpm_log(PM_LOG_DEBUG, _("null package found in package list"));
- continue;
+ joined = alpm_list_join(alpm_list_copy(remove), alpm_list_copy(upgrade));
+ dblist = alpm_list_diff(_alpm_db_get_pkgcache(db), joined, _alpm_pkg_cmp);
+ alpm_list_free(joined);
+
+ /* look for unsatisfied dependencies of the upgrade list */
+ for(i = upgrade; i; i = i->next) {
+ pmpkg_t *tp = i->data;
+ _alpm_log(PM_LOG_DEBUG, "checkdeps: package %s-%s\n",
+ alpm_pkg_get_name(tp), alpm_pkg_get_version(tp));
+
+ for(j = alpm_pkg_get_depends(tp); j; j = j->next) {
+ pmdepend_t *depend = j->data;
+ /* 1. we check the upgrade list */
+ /* 2. we check database for untouched satisfying packages */
+ if(!alpm_list_find(upgrade, depend, satisfycmp) &&
+ !alpm_list_find(dblist, depend, satisfycmp)) {
+ /* Unsatisfied dependency in the upgrade list */
+ char *missdepstring = alpm_dep_get_string(depend);
+ _alpm_log(PM_LOG_DEBUG, "checkdeps: missing dependency '%s' for package '%s'\n",
+ missdepstring, alpm_pkg_get_name(tp));
+ free(missdepstring);
+ miss = _alpm_depmiss_new(alpm_pkg_get_name(tp), depend->mod,
+ depend->name, depend->version);
+ baddeps = alpm_list_add(baddeps, miss);
}
+ }
+ }
- if((oldpkg = _alpm_db_get_pkgfromcache(db, alpm_pkg_get_name(newpkg))) == NULL) {
- _alpm_log(PM_LOG_DEBUG, _("cannot find package installed '%s'"),
- alpm_pkg_get_name(newpkg));
- continue;
- }
- for(j = alpm_pkg_get_requiredby(oldpkg); j; j = j->next) {
- pmpkg_t *p;
- found = 0;
- if((p = _alpm_db_get_pkgfromcache(db, j->data)) == NULL) {
- /* hmmm... package isn't installed.. */
- continue;
- }
- if(_alpm_pkg_find(alpm_pkg_get_name(p), packages)) {
- /* this package also in the upgrade list, so don't worry about it */
- continue;
- }
- for(k = alpm_pkg_get_depends(p); k; k = k->next) {
- /* don't break any existing dependencies (possible provides) */
- pmdepend_t *depend = alpm_splitdep(k->data);
- if(depend == NULL) {
- continue;
- }
-
- /* if oldpkg satisfied this dep, and newpkg doesn't */
- if(alpm_depcmp(oldpkg, depend) && !alpm_depcmp(newpkg, depend)) {
- /* we've found a dep that was removed... see if any other package
- * still contains/provides the dep */
- int satisfied = 0;
- for(l = packages; l; l = l->next) {
- pmpkg_t *pkg = l->data;
-
- if(alpm_depcmp(pkg, depend)) {
- _alpm_log(PM_LOG_DEBUG, _("checkdeps: dependency '%s' has moved from '%s' to '%s'"),
- depend->name, alpm_pkg_get_name(oldpkg), alpm_pkg_get_name(pkg));
- satisfied = 1;
- break;
- }
- }
-
- if(!satisfied) {
- /* worst case... check installed packages to see if anything else
- * satisfies this... */
- for(l = _alpm_db_get_pkgcache(db); l; l = l->next) {
- pmpkg_t *pkg = l->data;
-
- if(strcmp(alpm_pkg_get_name(pkg), alpm_pkg_get_name(oldpkg)) == 0) {
- /* well, we know this one succeeds, but we're removing it... skip it */
- continue;
- }
-
- if(alpm_depcmp(pkg, depend)) {
- _alpm_log(PM_LOG_DEBUG, _("checkdeps: dependency '%s' satisfied by installed package '%s'"),
- depend->name, alpm_pkg_get_name(pkg));
- satisfied = 1;
- break;
- }
- }
- }
-
- if(!satisfied) {
- _alpm_log(PM_LOG_DEBUG, _("checkdeps: updated '%s' won't satisfy a dependency of '%s'"),
- alpm_pkg_get_name(oldpkg), alpm_pkg_get_name(p));
- miss = _alpm_depmiss_new(p->name, PM_DEP_TYPE_DEPEND, depend->mod,
- depend->name, depend->version);
- if(!_alpm_depmiss_isin(miss, baddeps)) {
- baddeps = alpm_list_add(baddeps, miss);
- } else {
- FREE(miss);
- }
- }
- }
- free(depend);
+ if(reversedeps) {
+ /* reversedeps handles the backwards dependencies, ie,
+ * the packages listed in the requiredby field. */
+
+ alpm_list_t *modified = alpm_list_diff(_alpm_db_get_pkgcache(db), dblist, _alpm_pkg_cmp);
+
+ for(i = dblist; i; i = i->next) {
+ pmpkg_t *lp = i->data;
+ for(j = alpm_pkg_get_depends(lp); j; j = j->next) {
+ pmdepend_t *depend = j->data;
+ /* we won't break this depend, if it is already broken, we ignore it */
+ /* 1. check upgrade list for satisfiers */
+ /* 2. check dblist for satisfiers */
+ if(alpm_list_find(modified, depend, satisfycmp) &&
+ !alpm_list_find(upgrade, depend, satisfycmp) &&
+ !alpm_list_find(dblist, depend, satisfycmp)) {
+ char *missdepstring = alpm_dep_get_string(depend);
+ _alpm_log(PM_LOG_DEBUG, "checkdeps: transaction would break '%s' dependency of '%s'\n",
+ missdepstring, alpm_pkg_get_name(lp));
+ free(missdepstring);
+ miss = _alpm_depmiss_new(lp->name, depend->mod,
+ depend->name, depend->version);
+ baddeps = alpm_list_add(baddeps, miss);
}
}
}
+ alpm_list_free(modified);
}
- if(op == PM_TRANS_TYPE_ADD || op == PM_TRANS_TYPE_UPGRADE) {
- /* DEPENDENCIES -- look for unsatisfied dependencies */
- for(i = packages; i; i = i->next) {
- pmpkg_t *tp = i->data;
- if(tp == NULL) {
- _alpm_log(PM_LOG_DEBUG, _("null package found in package list"));
- continue;
- }
+ alpm_list_free(dblist);
- for(j = alpm_pkg_get_depends(tp); j; j = j->next) {
- /* split into name/version pairs */
- pmdepend_t *depend = alpm_splitdep((char*)j->data);
- if(depend == NULL) {
- continue;
- }
-
- found = 0;
- /* check database for literal packages */
- for(k = _alpm_db_get_pkgcache(db); k && !found; k = k->next) {
- pmpkg_t *p = (pmpkg_t *)k->data;
- found = alpm_depcmp(p, depend);
- }
- /* check database for provides matches */
- if(!found) {
- alpm_list_t *m;
- for(m = _alpm_db_whatprovides(db, depend->name); m && !found; m = m->next) {
- /* look for a match that isn't one of the packages we're trying
- * to install. this way, if we match against a to-be-installed
- * package, we'll defer to the NEW one, not the one already
- * installed. */
- pmpkg_t *p = m->data;
- alpm_list_t *n;
- int skip = 0;
- for(n = packages; n && !skip; n = n->next) {
- pmpkg_t *ptp = n->data;
- if(strcmp(alpm_pkg_get_name(ptp), alpm_pkg_get_name(p)) == 0) {
- skip = 1;
- }
- }
- if(skip) {
- continue;
- }
-
- found = alpm_depcmp(p, depend);
- }
- FREELISTPTR(k);
- }
- /* check other targets */
- for(k = packages; k && !found; k = k->next) {
- pmpkg_t *p = k->data;
- found = alpm_depcmp(p, depend);
- }
- /* else if still not found... */
- if(!found) {
- _alpm_log(PM_LOG_DEBUG, _("missing dependency '%s' for package '%s'"),
- depend->name, alpm_pkg_get_name(tp));
- miss = _alpm_depmiss_new(alpm_pkg_get_name(tp), PM_DEP_TYPE_DEPEND, depend->mod,
- depend->name, depend->version);
- if(!_alpm_depmiss_isin(miss, baddeps)) {
- baddeps = alpm_list_add(baddeps, miss);
- } else {
- FREE(miss);
- }
- }
- free(depend);
- }
+ return(baddeps);
+}
+
+static int dep_vercmp(const char *version1, pmdepmod_t mod,
+ const char *version2)
+{
+ int equal = 0;
+
+ if(mod == PM_DEP_MOD_ANY) {
+ equal = 1;
+ } else {
+ int cmp = _alpm_versioncmp(version1, version2);
+ switch(mod) {
+ case PM_DEP_MOD_EQ: equal = (cmp == 0); break;
+ case PM_DEP_MOD_GE: equal = (cmp >= 0); break;
+ case PM_DEP_MOD_LE: equal = (cmp <= 0); break;
+ default: equal = 1; break;
}
- } else if(op == PM_TRANS_TYPE_REMOVE) {
- /* check requiredby fields */
- for(i = packages; i; i = i->next) {
- pmpkg_t *tp = i->data;
- if(tp == NULL) {
- continue;
- }
+ }
+ return(equal);
+}
- found=0;
- for(j = alpm_pkg_get_requiredby(tp); j; j = j->next) {
- /* Search for 'reqname' in packages for removal */
- char *reqname = j->data;
- alpm_list_t *x = NULL;
- for(x = packages; x; x = x->next) {
- pmpkg_t *xp = x->data;
- if(strcmp(reqname, alpm_pkg_get_name(xp)) == 0) {
- found = 1;
- break;
- }
- }
- if(!found) {
- /* check if a package in trans->packages provides this package */
- for(k = trans->packages; !found && k; k=k->next) {
- pmpkg_t *spkg = NULL;
- if(trans->type == PM_TRANS_TYPE_SYNC) {
- pmsyncpkg_t *sync = k->data;
- spkg = sync->pkg;
- } else {
- spkg = k->data;
- }
- if(spkg) {
- if(alpm_list_find_str(alpm_pkg_get_provides(spkg), tp->name)) {
- found = 1;
- }
- }
- }
- if(!found) {
- _alpm_log(PM_LOG_DEBUG, _("checkdeps: found %s as required by %s"),
- reqname, alpm_pkg_get_name(tp));
- miss = _alpm_depmiss_new(alpm_pkg_get_name(tp), PM_DEP_TYPE_DEPEND,
- PM_DEP_MOD_ANY, j->data, NULL);
- if(!_alpm_depmiss_isin(miss, baddeps)) {
- baddeps = alpm_list_add(baddeps, miss);
- } else {
- FREE(miss);
- }
- }
- }
- }
+int SYMEXPORT alpm_depcmp(pmpkg_t *pkg, pmdepend_t *dep)
+{
+ alpm_list_t *i;
+
+ ALPM_LOG_FUNC;
+
+ const char *pkgname = alpm_pkg_get_name(pkg);
+ const char *pkgversion = alpm_pkg_get_version(pkg);
+ int satisfy = 0;
+
+ /* check (pkg->name, pkg->version) */
+ satisfy = (strcmp(pkgname, dep->name) == 0
+ && dep_vercmp(pkgversion, dep->mod, dep->version));
+
+ /* check provisions, format : "name version" */
+ for(i = alpm_pkg_get_provides(pkg); i && !satisfy; i = i->next) {
+ char *provname = strdup(i->data);
+ char *provver = strchr(provname, ' ');
+
+ if(provver == NULL) { /* no provision version */
+ satisfy = (dep->mod == PM_DEP_MOD_ANY
+ && strcmp(provname, dep->name) == 0);
+ } else {
+ /* replace the space with a NULL byte, and advance ptr the version */
+ *provver = '\0';
+ provver += 1;
+ satisfy = (strcmp(provname, dep->name) == 0
+ && dep_vercmp(provver, dep->mod, dep->version));
}
+ free(provname);
}
- return(baddeps);
+ return(satisfy);
}
pmdepend_t SYMEXPORT *alpm_splitdep(const char *depstring)
{
pmdepend_t *depend;
char *ptr = NULL;
+ char *newstr = NULL;
if(depstring == NULL) {
return(NULL);
}
-
- depend = (pmdepend_t *)malloc(sizeof(pmdepend_t));
- if(depend == NULL) {
- _alpm_log(PM_LOG_ERROR, _("malloc failure: could not allocate %d bytes"), sizeof(pmdepend_t));
- return(NULL);
- }
+ newstr = strdup(depstring);
+
+ MALLOC(depend, sizeof(pmdepend_t), return(NULL));
/* Find a version comparator if one exists. If it does, set the type and
* increment the ptr accordingly so we can copy the right strings. */
- if((ptr = strstr(depstring, ">="))) {
+ if((ptr = strstr(newstr, ">="))) {
depend->mod = PM_DEP_MOD_GE;
*ptr = '\0';
ptr += 2;
- } else if((ptr = strstr(depstring, "<="))) {
+ } else if((ptr = strstr(newstr, "<="))) {
depend->mod = PM_DEP_MOD_LE;
*ptr = '\0';
ptr += 2;
- } else if((ptr = strstr(depstring, "="))) {
+ } else if((ptr = strstr(newstr, "="))) {
depend->mod = PM_DEP_MOD_EQ;
*ptr = '\0';
ptr += 1;
} else {
/* no version specified - copy in the name and return it */
depend->mod = PM_DEP_MOD_ANY;
- strncpy(depend->name, depstring, PKG_NAME_LEN);
+ strncpy(depend->name, newstr, PKG_NAME_LEN);
depend->version[0] = '\0';
+ free(newstr);
return(depend);
}
/* if we get here, we have a version comparator, copy the right parts
* to the right places */
- strncpy(depend->name, depstring, PKG_NAME_LEN);
+ strncpy(depend->name, newstr, PKG_NAME_LEN);
strncpy(depend->version, ptr, PKG_VERSION_LEN);
+ free(newstr);
return(depend);
}
-/* These parameters are messy. We check if this package, given a list of
- * targets (and a db), is safe to remove. We do NOT remove it if it is in the
- * target list */
-static int can_remove_package(pmdb_t *db, pmpkg_t *pkg, alpm_list_t *targets)
+/* These parameters are messy. We check if this package, given a list of
+ * targets and a db is safe to remove. We do NOT remove it if it is in the
+ * target list, or if if the package was explictly installed and
+ * include_explicit == 0 */
+static int can_remove_package(pmdb_t *db, pmpkg_t *pkg, alpm_list_t *targets,
+ int include_explicit)
{
- alpm_list_t *i;
+ alpm_list_t *i, *requiredby;
if(_alpm_pkg_find(alpm_pkg_get_name(pkg), targets)) {
return(0);
}
- /* see if it was explicitly installed */
- if(alpm_pkg_get_reason(pkg) == PM_PKG_REASON_EXPLICIT) {
- _alpm_log(PM_LOG_DEBUG, _("excluding %s -- explicitly installed"), alpm_pkg_get_name(pkg));
- return(0);
+ if(!include_explicit) {
+ /* see if it was explicitly installed */
+ if(alpm_pkg_get_reason(pkg) == PM_PKG_REASON_EXPLICIT) {
+ _alpm_log(PM_LOG_DEBUG, "excluding %s -- explicitly installed\n",
+ alpm_pkg_get_name(pkg));
+ return(0);
+ }
}
+ /* TODO: checkdeps could be used here, it handles multiple providers
+ * better, but that also makes it slower.
+ * Also this would require to first add the package to the targets list,
+ * then call checkdeps with it, then remove the package from the targets list
+ * if checkdeps detected it would break something */
+
/* see if other packages need it */
- for(i = alpm_pkg_get_requiredby(pkg); i; i = i->next) {
+ requiredby = alpm_pkg_compute_requiredby(pkg);
+ for(i = requiredby; i; i = i->next) {
pmpkg_t *reqpkg = _alpm_db_get_pkgfromcache(db, i->data);
if(reqpkg && !_alpm_pkg_find(alpm_pkg_get_name(reqpkg), targets)) {
+ FREELIST(requiredby);
return(0);
}
}
+ FREELIST(requiredby);
/* it's ok to remove */
return(1);
}
-/* return a new alpm_list_t target list containing all packages in the original
- * target list, as well as all their un-needed dependencies. By un-needed,
- * I mean dependencies that are *only* required for packages in the target
- * list, so they can be safely removed. This function is recursive.
+/**
+ * @brief Adds unneeded dependencies to an existing list of packages.
+ * By unneeded, we mean dependencies that are only required by packages in the
+ * target list, so they can be safely removed.
+ * If the input list was topo sorted, the output list will be topo sorted too.
+ *
+ * @param db package database to do dependency tracing in
+ * @param *targs pointer to a list of packages
+ * @param include_explicit if 0, explicitly installed packages are not included
*/
-alpm_list_t *_alpm_removedeps(pmdb_t *db, alpm_list_t *targs)
+void _alpm_recursedeps(pmdb_t *db, alpm_list_t *targs, int include_explicit)
{
alpm_list_t *i, *j, *k;
- alpm_list_t *newtargs = targs;
ALPM_LOG_FUNC;
- if(db == NULL) {
- return(newtargs);
+ if(db == NULL || targs == NULL) {
+ return;
}
for(i = targs; i; i = i->next) {
pmpkg_t *pkg = i->data;
for(j = alpm_pkg_get_depends(pkg); j; j = j->next) {
- pmdepend_t *depend = alpm_splitdep(j->data);
- pmpkg_t *deppkg;
- if(depend == NULL) {
- continue;
- }
-
- deppkg = _alpm_db_get_pkgfromcache(db, depend->name);
- if(deppkg == NULL) {
- /* package not found... look for a provision instead */
- alpm_list_t *provides = _alpm_db_whatprovides(db, depend->name);
- if(!provides) {
- /* Not found, that's fine, carry on */
- _alpm_log(PM_LOG_DEBUG, _("cannot find package \"%s\" or anything that provides it!"), depend->name);
- continue;
- }
- for(k = provides; k; k = k->next) {
- pmpkg_t *provpkg = k->data;
- if(can_remove_package(db, provpkg, newtargs)) {
- pmpkg_t *pkg = _alpm_pkg_dup(provpkg);
-
- _alpm_log(PM_LOG_DEBUG, _("adding '%s' to the targets"), alpm_pkg_get_name(pkg));
-
+ pmdepend_t *depend = j->data;
+
+ for(k = _alpm_db_get_pkgcache(db); k; k = k->next) {
+ pmpkg_t *deppkg = k->data;
+ if(alpm_depcmp(deppkg,depend)
+ && can_remove_package(db, deppkg, targs, include_explicit)) {
+ _alpm_log(PM_LOG_DEBUG, "adding '%s' to the targets\n",
+ alpm_pkg_get_name(deppkg));
/* add it to the target list */
- newtargs = alpm_list_add(newtargs, pkg);
- newtargs = _alpm_removedeps(db, newtargs);
- }
+ targs = alpm_list_add(targs, _alpm_pkg_dup(deppkg));
}
- FREELISTPTR(provides);
- } else if(can_remove_package(db, deppkg, newtargs)) {
- pmpkg_t *pkg = _alpm_pkg_dup(deppkg);
-
- _alpm_log(PM_LOG_DEBUG, _("adding '%s' to the targets"), alpm_pkg_get_name(pkg));
-
- /* add it to the target list */
- newtargs = alpm_list_add(newtargs, pkg);
- newtargs = _alpm_removedeps(db, newtargs);
}
- free(depend);
}
}
-
- return(newtargs);
}
/* populates *list with packages that need to be installed to satisfy all
* dependencies (recursive) for syncpkg
*
- * make sure *list and *trail are already initialized
+ * @param remove contains packages elected for removal
+ * make sure **list is already initialized
*/
int _alpm_resolvedeps(pmdb_t *local, alpm_list_t *dbs_sync, pmpkg_t *syncpkg,
- alpm_list_t *list, alpm_list_t *trail, pmtrans_t *trans,
- alpm_list_t **data)
+ alpm_list_t **list, alpm_list_t *remove, pmtrans_t *trans, alpm_list_t **data)
{
- alpm_list_t *i, *j;
+ alpm_list_t *i, *j, *k;
alpm_list_t *targ;
alpm_list_t *deps = NULL;
ALPM_LOG_FUNC;
- if(local == NULL || dbs_sync == NULL || syncpkg == NULL) {
+ if(local == NULL || dbs_sync == NULL || syncpkg == NULL || list == NULL) {
return(-1);
}
- _alpm_log(PM_LOG_DEBUG, _("started resolving dependencies"));
+ _alpm_log(PM_LOG_DEBUG, "started resolving dependencies\n");
targ = alpm_list_add(NULL, syncpkg);
- deps = _alpm_checkdeps(trans, local, PM_TRANS_TYPE_ADD, targ);
- FREELISTPTR(targ);
+ deps = alpm_checkdeps(local, 0, remove, targ);
+ alpm_list_free(targ);
if(deps == NULL) {
return(0);
@@ -592,14 +511,17 @@ int _alpm_resolvedeps(pmdb_t *local, alpm_list_t *dbs_sync, pmpkg_t *syncpkg,
for(i = deps; i; i = i->next) {
int found = 0;
pmdepmissing_t *miss = i->data;
+ pmdepend_t *missdep = &(miss->depend);
pmpkg_t *sync = NULL;
- /* check if one of the packages in *list already provides this dependency */
- for(j = list; j && !found; j = j->next) {
+ /* check if one of the packages in *list already satisfies this dependency */
+ for(j = *list; j && !found; j = j->next) {
pmpkg_t *sp = j->data;
- if(alpm_list_find_str(alpm_pkg_get_provides(sp), miss->depend.name)) {
- _alpm_log(PM_LOG_DEBUG, _("%s provides dependency %s -- skipping"),
- alpm_pkg_get_name(sp), miss->depend.name);
+ if(alpm_depcmp(sp, missdep)) {
+ char *missdepstring = alpm_dep_get_string(missdep);
+ _alpm_log(PM_LOG_DEBUG, "%s satisfies dependency %s -- skipping\n",
+ alpm_pkg_get_name(sp), missdepstring);
+ free(missdepstring);
found = 1;
}
}
@@ -609,31 +531,53 @@ int _alpm_resolvedeps(pmdb_t *local, alpm_list_t *dbs_sync, pmpkg_t *syncpkg,
/* find the package in one of the repositories */
/* check literals */
- for(j = dbs_sync; !sync && j; j = j->next) {
- sync = _alpm_db_get_pkgfromcache(j->data, miss->depend.name);
+ for(j = dbs_sync; j && !found; j = j->next) {
+ sync = _alpm_db_get_pkgfromcache(j->data, missdep->name);
+ if(!sync) {
+ continue;
+ }
+ found = alpm_depcmp(sync, missdep) && !_alpm_pkg_find(alpm_pkg_get_name(sync), remove);
+ if(!found) {
+ continue;
+ }
+ /* If package is in the ignorepkg list, ask before we pull it */
+ if(_alpm_pkg_should_ignore(sync)) {
+ pmpkg_t *dummypkg = _alpm_pkg_new(miss->target, NULL);
+ QUESTION(trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, dummypkg, sync, NULL, &found);
+ _alpm_pkg_free(dummypkg);
+ }
}
- /*TODO this autoresolves the first 'provides' package... we should fix this
+ /*TODO this autoresolves the first 'satisfier' package... we should fix this
* somehow */
/* check provides */
- if(!sync) {
- for(j = dbs_sync; !sync && j; j = j->next) {
- alpm_list_t *provides;
- provides = _alpm_db_whatprovides(j->data, miss->depend.name);
- if(provides) {
- sync = provides->data;
+ for(j = dbs_sync; j && !found; j = j->next) {
+ for(k = _alpm_db_get_pkgcache(j->data); k && !found; k = k->next) {
+ sync = k->data;
+ if(!sync) {
+ continue;
+ }
+ found = alpm_depcmp(sync, missdep) && !_alpm_pkg_find(alpm_pkg_get_name(sync), remove);
+ if(!found) {
+ continue;
+ }
+ if(_alpm_pkg_should_ignore(sync)) {
+ pmpkg_t *dummypkg = _alpm_pkg_new(miss->target, NULL);
+ QUESTION(trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, dummypkg, sync, NULL, &found);
+ _alpm_pkg_free(dummypkg);
}
- FREELISTPTR(provides);
}
}
- if(!sync) {
- _alpm_log(PM_LOG_ERROR, _("cannot resolve dependencies for \"%s\" (\"%s\" is not in the package set)"),
- miss->target, miss->depend.name);
+ if(!found) {
+ char *missdepstring = alpm_dep_get_string(missdep);
+ _alpm_log(PM_LOG_ERROR, _("cannot resolve \"%s\", a dependency of \"%s\"\n"),
+ missdepstring, miss->target);
+ free(missdepstring);
if(data) {
- if((miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t))) == NULL) {
- _alpm_log(PM_LOG_ERROR, _("malloc failure: could not allocate %d bytes"), sizeof(pmdepmissing_t));
- FREELIST(*data);
+ MALLOC(miss, sizeof(pmdepmissing_t),/*nothing*/);
+ if(!miss) {
pm_errno = PM_ERR_MEMORY;
+ FREELIST(*data);
goto error;
}
*miss = *(pmdepmissing_t *)i->data;
@@ -641,54 +585,17 @@ int _alpm_resolvedeps(pmdb_t *local, alpm_list_t *dbs_sync, pmpkg_t *syncpkg,
}
pm_errno = PM_ERR_UNSATISFIED_DEPS;
goto error;
- }
- if(_alpm_pkg_find(alpm_pkg_get_name(sync), list)) {
- /* this dep is already in the target list */
- _alpm_log(PM_LOG_DEBUG, _("dependency %s is already in the target list -- skipping"),
- alpm_pkg_get_name(sync));
- continue;
- }
-
- if(!_alpm_pkg_find(alpm_pkg_get_name(sync), trail)) {
- /* check pmo_ignorepkg and pmo_s_ignore to make sure we haven't pulled in
- * something we're not supposed to.
- */
- int usedep = 1;
- if(alpm_list_find_str(handle->ignorepkg, alpm_pkg_get_name(sync))) {
- pmpkg_t *dummypkg = _alpm_pkg_new(miss->target, NULL);
- QUESTION(trans, PM_TRANS_CONV_INSTALL_IGNOREPKG, dummypkg, sync, NULL, &usedep);
- FREEPKG(dummypkg);
- }
- if(usedep) {
- trail = alpm_list_add(trail, sync);
- if(_alpm_resolvedeps(local, dbs_sync, sync, list, trail, trans, data)) {
- goto error;
- }
- _alpm_log(PM_LOG_DEBUG, _("pulling dependency %s (needed by %s)"),
- alpm_pkg_get_name(sync), alpm_pkg_get_name(syncpkg));
- list = alpm_list_add(list, sync);
- } else {
- _alpm_log(PM_LOG_ERROR, _("cannot resolve dependencies for \"%s\""), miss->target);
- if(data) {
- if((miss = (pmdepmissing_t *)malloc(sizeof(pmdepmissing_t))) == NULL) {
- _alpm_log(PM_LOG_ERROR, _("malloc failure: could not allocate %d bytes"), sizeof(pmdepmissing_t));
- FREELIST(*data);
- pm_errno = PM_ERR_MEMORY;
- goto error;
- }
- *miss = *(pmdepmissing_t *)i->data;
- *data = alpm_list_add(*data, miss);
- }
- pm_errno = PM_ERR_UNSATISFIED_DEPS;
+ } else {
+ _alpm_log(PM_LOG_DEBUG, "pulling dependency %s (needed by %s)\n",
+ alpm_pkg_get_name(sync), alpm_pkg_get_name(syncpkg));
+ *list = alpm_list_add(*list, sync);
+ if(_alpm_resolvedeps(local, dbs_sync, sync, list, remove, trans, data)) {
goto error;
}
- } else {
- /* cycle detected -- skip it */
- _alpm_log(PM_LOG_DEBUG, _("dependency cycle detected: %s"), sync->name);
}
}
-
- _alpm_log(PM_LOG_DEBUG, _("finished resolving dependencies"));
+
+ _alpm_log(PM_LOG_DEBUG, "finished resolving dependencies\n");
FREELIST(deps);
@@ -699,58 +606,95 @@ error:
return(-1);
}
-const char SYMEXPORT *alpm_dep_get_target(pmdepmissing_t *miss)
+const char SYMEXPORT *alpm_miss_get_target(const pmdepmissing_t *miss)
{
ALPM_LOG_FUNC;
/* Sanity checks */
- ASSERT(handle != NULL, return(NULL));
ASSERT(miss != NULL, return(NULL));
return miss->target;
}
-pmdeptype_t SYMEXPORT alpm_dep_get_type(pmdepmissing_t *miss)
+pmdepend_t SYMEXPORT *alpm_miss_get_dep(pmdepmissing_t *miss)
{
ALPM_LOG_FUNC;
/* Sanity checks */
- ASSERT(handle != NULL, return(-1));
- ASSERT(miss != NULL, return(-1));
+ ASSERT(miss != NULL, return(NULL));
- return miss->type;
+ return &(miss->depend);
}
-pmdepmod_t SYMEXPORT alpm_dep_get_mod(pmdepmissing_t *miss)
+pmdepmod_t SYMEXPORT alpm_dep_get_mod(const pmdepend_t *dep)
{
ALPM_LOG_FUNC;
/* Sanity checks */
- ASSERT(handle != NULL, return(-1));
- ASSERT(miss != NULL, return(-1));
+ ASSERT(dep != NULL, return(-1));
- return miss->depend.mod;
+ return dep->mod;
}
-const char SYMEXPORT *alpm_dep_get_name(pmdepmissing_t *miss)
+const char SYMEXPORT *alpm_dep_get_name(const pmdepend_t *dep)
{
ALPM_LOG_FUNC;
/* Sanity checks */
- ASSERT(handle != NULL, return(NULL));
- ASSERT(miss != NULL, return(NULL));
+ ASSERT(dep != NULL, return(NULL));
- return miss->depend.name;
+ return dep->name;
}
-const char SYMEXPORT *alpm_dep_get_version(pmdepmissing_t *miss)
+const char SYMEXPORT *alpm_dep_get_version(const pmdepend_t *dep)
{
ALPM_LOG_FUNC;
/* Sanity checks */
- ASSERT(handle != NULL, return(NULL));
- ASSERT(miss != NULL, return(NULL));
+ ASSERT(dep != NULL, return(NULL));
+
+ return dep->version;
+}
+
+/** Reverse of splitdep; make a dep string from a pmdepend_t struct.
+ * The string must be freed!
+ * @param dep the depend to turn into a string
+ * @return a string-formatted dependency with operator if necessary
+ */
+char SYMEXPORT *alpm_dep_get_string(const pmdepend_t *dep)
+{
+ char *opr, *str = NULL;
+ size_t len;
+
+ ALPM_LOG_FUNC;
+
+ /* Sanity checks */
+ ASSERT(dep != NULL, return(NULL));
+
+ switch(dep->mod) {
+ case PM_DEP_MOD_ANY:
+ opr = "";
+ break;
+ case PM_DEP_MOD_GE:
+ opr = ">=";
+ break;
+ case PM_DEP_MOD_LE:
+ opr = "<=";
+ break;
+ case PM_DEP_MOD_EQ:
+ opr = "=";
+ break;
+ default:
+ opr = "";
+ break;
+ }
+
+ /* we can always compute len and print the string like this because opr
+ * and ver will be empty when PM_DEP_MOD_ANY is the depend type */
+ len = strlen(dep->name) + strlen(opr) + strlen(dep->version) + 1;
+ MALLOC(str, len, RET_ERR(PM_ERR_MEMORY, NULL));
+ snprintf(str, len, "%s%s%s", dep->name, opr, dep->version);
- return miss->depend.version;
+ return(str);
}
/* vim: set ts=2 sw=2 noet: */