mirror of
https://github.com/moparisthebest/pacman
synced 2024-11-02 08:35:06 -04:00
544bcbe664
Based on the "depth first search" algorithm, for more infos visit: http://en.wikipedia.org/wiki/Topological_sorting The previous algorithm used by sortbydeps was too slow, and to work around it the number of steps needed to get correct result was reduced greatly. So it produced wrong results in several cases : 1) smoke001.py 2) http://bugs.archlinux.org/task/7229 More here: http://archlinux.org/pipermail/pacman-dev/2007-April/008057.html Signed-off-by: Chantry Xavier <shiningxc@gmail.com> Signed-off-by: Dan McGee <dan@archlinux.org>
767 lines
21 KiB
C
767 lines
21 KiB
C
/*
|
|
* 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
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* 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,
|
|
* USA.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#ifdef __sun__
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
/* libalpm */
|
|
#include "deps.h"
|
|
#include "alpm_list.h"
|
|
#include "util.h"
|
|
#include "log.h"
|
|
#include "error.h"
|
|
#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;
|
|
|
|
graph = (pmgraph_t *)malloc(sizeof(pmgraph_t));
|
|
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 *miss;
|
|
|
|
ALPM_LOG_FUNC;
|
|
|
|
miss = 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);
|
|
}
|
|
|
|
strncpy(miss->target, target, PKG_NAME_LEN);
|
|
miss->type = type;
|
|
miss->depend.mod = depmod;
|
|
strncpy(miss->depend.name, depname, PKG_NAME_LEN);
|
|
if(depversion) {
|
|
strncpy(miss->depend.version, depversion, PKG_VERSION_LEN);
|
|
} else {
|
|
miss->depend.version[0] = 0;
|
|
}
|
|
|
|
return(miss);
|
|
}
|
|
|
|
int _alpm_depmiss_isin(pmdepmissing_t *needle, alpm_list_t *haystack)
|
|
{
|
|
alpm_list_t *i;
|
|
|
|
ALPM_LOG_FUNC;
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/* Re-order a list of target packages with respect to their dependencies.
|
|
*
|
|
* Example (PM_TRANS_TYPE_ADD):
|
|
* A depends on C
|
|
* B depends on A
|
|
* 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;
|
|
alpm_list_t *vertices = NULL;
|
|
alpm_list_t *vptr;
|
|
pmgraph_t *vertex;
|
|
|
|
ALPM_LOG_FUNC;
|
|
|
|
if(targets == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
_alpm_log(PM_LOG_DEBUG, _("started sorting dependencies"));
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* 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 = alpm_splitdep(k->data);
|
|
child = alpm_depcmp(p_j, depend);
|
|
free(depend);
|
|
}
|
|
if(child) vertex_i->children =
|
|
alpm_list_add(vertex_i->children, vertex_j);
|
|
}
|
|
vertex_i->childptr = vertex_i->children;
|
|
}
|
|
|
|
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) {
|
|
_alpm_log(PM_LOG_WARNING, _("dependency cycle detected\n"));
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
_alpm_log(PM_LOG_DEBUG, _("sorting dependencies finished"));
|
|
|
|
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 */
|
|
alpm_list_free(newtargs);
|
|
newtargs = tmptargs;
|
|
}
|
|
|
|
alpm_list_free_inner(vertices, _alpm_graph_free);
|
|
|
|
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
|
|
* @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.
|
|
*/
|
|
alpm_list_t *_alpm_checkdeps(pmtrans_t *trans, pmdb_t *db, pmtranstype_t op,
|
|
alpm_list_t *packages)
|
|
{
|
|
alpm_list_t *i, *j, *k, *l;
|
|
int found = 0;
|
|
alpm_list_t *baddeps = NULL;
|
|
pmdepmissing_t *miss = NULL;
|
|
|
|
ALPM_LOG_FUNC;
|
|
|
|
if(db == NULL) {
|
|
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;
|
|
}
|
|
|
|
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(_alpm_pkg_find(j->data, packages)) {
|
|
/* this package also in the upgrade list, so don't worry about it */
|
|
continue;
|
|
}
|
|
if((p = _alpm_db_get_pkgfromcache(db, j->data)) == NULL) {
|
|
/* hmmm... package isn't installed.. */
|
|
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(alpm_depcmp(pkg, depend) && !_alpm_pkg_find(alpm_pkg_get_name(pkg), packages)) {
|
|
/* we ignore packages that will be updated because we know
|
|
* that the updated ones don't satisfy 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(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;
|
|
}
|
|
|
|
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 other targets */
|
|
for(k = packages; k && !found; k = k->next) {
|
|
pmpkg_t *p = k->data;
|
|
found = alpm_depcmp(p, depend);
|
|
}
|
|
|
|
/* check database for satisfying packages */
|
|
/* we can ignore packages being updated, they were checked above */
|
|
for(k = _alpm_db_get_pkgcache(db); k && !found; k = k->next) {
|
|
pmpkg_t *p = k->data;
|
|
found = alpm_depcmp(p, depend)
|
|
&& !_alpm_pkg_find(alpm_pkg_get_name(p), packages);
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
} else if(op == PM_TRANS_TYPE_REMOVE) {
|
|
/* check requiredby fields */
|
|
for(i = packages; i; i = i->next) {
|
|
pmpkg_t *rmpkg = alpm_list_getdata(i);
|
|
|
|
if(rmpkg == NULL) {
|
|
_alpm_log(PM_LOG_DEBUG, _("null package found in package list"));
|
|
continue;
|
|
}
|
|
for(j = alpm_pkg_get_requiredby(rmpkg); j; j = j->next) {
|
|
pmpkg_t *p;
|
|
found = 0;
|
|
if(_alpm_pkg_find(j->data, packages)) {
|
|
/* package also in the remove list, so don't worry about it */
|
|
continue;
|
|
}
|
|
|
|
if((p = _alpm_db_get_pkgfromcache(db, j->data)) == NULL) {
|
|
/* hmmm... package isn't installed... */
|
|
continue;
|
|
}
|
|
for(k = alpm_pkg_get_depends(p); k; k = k->next) {
|
|
pmdepend_t *depend = alpm_splitdep(k->data);
|
|
if(depend == NULL) {
|
|
continue;
|
|
}
|
|
/* if rmpkg satisfied this dep, try to find an other satisfier
|
|
* (which won't be removed)*/
|
|
if(alpm_depcmp(rmpkg, depend)) {
|
|
int satisfied = 0;
|
|
for(l = _alpm_db_get_pkgcache(db); l; l = l->next) {
|
|
pmpkg_t *pkg = l->data;
|
|
if(alpm_depcmp(pkg, depend) && !_alpm_pkg_find(alpm_pkg_get_name(pkg), packages)) {
|
|
_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: found %s which requires %s"),
|
|
alpm_pkg_get_name(p), alpm_pkg_get_name(rmpkg));
|
|
miss = _alpm_depmiss_new(alpm_pkg_get_name(p),
|
|
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);
|
|
}
|
|
|
|
pmdepend_t SYMEXPORT *alpm_splitdep(const char *depstring)
|
|
{
|
|
pmdepend_t *depend;
|
|
char *ptr = NULL;
|
|
|
|
if(depstring == NULL) {
|
|
return(NULL);
|
|
}
|
|
|
|
depend = malloc(sizeof(pmdepend_t));
|
|
if(depend == NULL) {
|
|
_alpm_log(PM_LOG_ERROR, _("malloc failure: could not allocate %d bytes"), 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, ">="))) {
|
|
depend->mod = PM_DEP_MOD_GE;
|
|
*ptr = '\0';
|
|
ptr += 2;
|
|
} else if((ptr = strstr(depstring, "<="))) {
|
|
depend->mod = PM_DEP_MOD_LE;
|
|
*ptr = '\0';
|
|
ptr += 2;
|
|
} else if((ptr = strstr(depstring, "="))) {
|
|
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);
|
|
depend->version[0] = '\0';
|
|
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->version, ptr, PKG_VERSION_LEN);
|
|
|
|
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)
|
|
{
|
|
alpm_list_t *i;
|
|
|
|
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);
|
|
}
|
|
|
|
/* see if other packages need it */
|
|
for(i = alpm_pkg_get_requiredby(pkg); 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)) {
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
/* 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.
|
|
*/
|
|
alpm_list_t *_alpm_removedeps(pmdb_t *db, alpm_list_t *targs)
|
|
{
|
|
alpm_list_t *i, *j, *k;
|
|
alpm_list_t *newtargs = targs;
|
|
|
|
ALPM_LOG_FUNC;
|
|
|
|
if(db == NULL) {
|
|
return(newtargs);
|
|
}
|
|
|
|
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));
|
|
|
|
/* add it to the target list */
|
|
newtargs = alpm_list_add(newtargs, pkg);
|
|
newtargs = _alpm_removedeps(db, newtargs);
|
|
}
|
|
}
|
|
alpm_list_free(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
|
|
*/
|
|
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 *i, *j;
|
|
alpm_list_t *targ;
|
|
alpm_list_t *deps = NULL;
|
|
|
|
ALPM_LOG_FUNC;
|
|
|
|
if(local == NULL || dbs_sync == NULL || syncpkg == NULL) {
|
|
return(-1);
|
|
}
|
|
|
|
_alpm_log(PM_LOG_DEBUG, _("started resolving dependencies"));
|
|
targ = alpm_list_add(NULL, syncpkg);
|
|
deps = _alpm_checkdeps(trans, local, PM_TRANS_TYPE_ADD, targ);
|
|
alpm_list_free(targ);
|
|
|
|
if(deps == NULL) {
|
|
return(0);
|
|
}
|
|
|
|
for(i = deps; i; i = i->next) {
|
|
int found = 0;
|
|
pmdepmissing_t *miss = i->data;
|
|
pmpkg_t *sync = NULL;
|
|
|
|
/* check if one of the packages in *list already provides 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);
|
|
found = 1;
|
|
}
|
|
}
|
|
if(found) {
|
|
continue;
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
/*TODO this autoresolves the first 'provides' 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;
|
|
}
|
|
alpm_list_free(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(data) {
|
|
if((miss = 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;
|
|
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);
|
|
_alpm_pkg_free(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 = 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;
|
|
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"));
|
|
|
|
FREELIST(deps);
|
|
|
|
return(0);
|
|
|
|
error:
|
|
FREELIST(deps);
|
|
return(-1);
|
|
}
|
|
|
|
const char SYMEXPORT *alpm_dep_get_target(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)
|
|
{
|
|
ALPM_LOG_FUNC;
|
|
|
|
/* Sanity checks */
|
|
ASSERT(handle != NULL, return(-1));
|
|
ASSERT(miss != NULL, return(-1));
|
|
|
|
return miss->type;
|
|
}
|
|
|
|
pmdepmod_t SYMEXPORT alpm_dep_get_mod(pmdepmissing_t *miss)
|
|
{
|
|
ALPM_LOG_FUNC;
|
|
|
|
/* Sanity checks */
|
|
ASSERT(handle != NULL, return(-1));
|
|
ASSERT(miss != NULL, return(-1));
|
|
|
|
return miss->depend.mod;
|
|
}
|
|
|
|
const char SYMEXPORT *alpm_dep_get_name(pmdepmissing_t *miss)
|
|
{
|
|
ALPM_LOG_FUNC;
|
|
|
|
/* Sanity checks */
|
|
ASSERT(handle != NULL, return(NULL));
|
|
ASSERT(miss != NULL, return(NULL));
|
|
|
|
return miss->depend.name;
|
|
}
|
|
|
|
const char SYMEXPORT *alpm_dep_get_version(pmdepmissing_t *miss)
|
|
{
|
|
ALPM_LOG_FUNC;
|
|
|
|
/* Sanity checks */
|
|
ASSERT(handle != NULL, return(NULL));
|
|
ASSERT(miss != NULL, return(NULL));
|
|
|
|
return miss->depend.version;
|
|
}
|
|
/* vim: set ts=2 sw=2 noet: */
|