ENABLE_RPMDB ENABLE_PUBKEY ENABLE_RPMMD ENABLE_RPMDB_BYRPMHEADER ENABLE_SUSEREPO ENABLE_COMPS
ENABLE_HELIXREPO ENABLE_MDKREPO ENABLE_ARCHREPO ENABLE_DEBIAN ENABLE_HAIKU
ENABLE_LZMA_COMPRESSION ENABLE_BZIP2_COMPRESSION ENABLE_PGPVRFY ENABLE_APPDATA
- ENABLE_LINKED_PKGS)
+ ENABLE_LINKED_PKGS ENABLE_COMPLEX_DEPS)
IF(${VAR})
ADD_DEFINITIONS (-D${VAR}=1)
SET (SWIG_FLAGS ${SWIG_FLAGS} -D${VAR})
bitmap.c poolarch.c poolvendor.c poolid.c strpool.c dirpool.c
solver.c solverdebug.c repo_solv.c repo_write.c evr.c pool.c
queue.c repo.c repodata.c repopage.c util.c policy.c solvable.c
- transaction.c rules.c problems.c linkedpkg.c
+ transaction.c rules.c problems.c linkedpkg.c cplxdeps.c
chksum.c md5.c sha1.c sha2.c solvversion.c selection.c)
SET (libsolv_HEADERS
--- /dev/null
+/*
+ * Copyright (c) 2014, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * cplxdeps.c
+ *
+ * normalize complex dependencies into CNF/DNF form
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <assert.h>
+
+#include "pool.h"
+#include "cplxdeps.h"
+
+#ifdef ENABLE_COMPLEX_DEPS
+
+#undef CPLXDEBUG
+
+int
+pool_is_complex_dep_rd(Pool *pool, Reldep *rd)
+{
+ for (;;)
+ {
+ if (rd->flags == REL_AND || rd->flags == REL_COND) /* those two are the complex ones */
+ return 1;
+ if (rd->flags != REL_OR)
+ return 0;
+ if (ISRELDEP(rd->name) && pool_is_complex_dep_rd(pool, GETRELDEP(pool, rd->name)))
+ return 1;
+ if (!ISRELDEP(rd->evr))
+ return 0;
+ rd = GETRELDEP(pool, rd->evr);
+ }
+}
+
+/* expand simple dependencies into package lists */
+static int
+expand_simpledeps(Pool *pool, Queue *bq, int start, int split)
+{
+ int end = bq->count;
+ int i, x;
+ int newsplit = 0;
+ for (i = start; i < end; i++)
+ {
+ if (i == split)
+ newsplit = bq->count - (end - start);
+ x = bq->elements[i];
+ if (x == pool->nsolvables)
+ {
+ Id *dp = pool->whatprovidesdata + bq->elements[++i];
+ for (; *dp; dp++)
+ queue_push(bq, *dp);
+ }
+ else
+ queue_push(bq, x);
+ }
+ if (i == split)
+ newsplit = bq->count - (end - start);
+ queue_deleten(bq, start, end - start);
+ return newsplit;
+}
+
+/* invert all literals in the blocks. note that this also turns DNF into CNF and vice versa */
+static void
+invert_depblocks(Pool *pool, Queue *bq, int start)
+{
+ int i, j, end;
+ expand_simpledeps(pool, bq, start, 0);
+ end = bq->count;
+ for (i = j = start; i < end; i++)
+ {
+ if (bq->elements[i])
+ {
+ bq->elements[i] = -bq->elements[i];
+ continue;
+ }
+ /* end of block reached, reorder */
+ if (i - 1 > j)
+ {
+ int k;
+ for (k = i - 1; j < k; j++, k--)
+ {
+ Id t = bq->elements[j];
+ bq->elements[j] = bq->elements[k];
+ bq->elements[k] = t;
+ }
+ }
+ j = i + 1;
+ }
+}
+
+/*
+ * returns:
+ * 0: no blocks
+ * 1: matches all
+ * -1: at least one block
+ */
+static int
+normalize_dep(Pool *pool, Id dep, Queue *bq, int todnf)
+{
+ int bqcnt = bq->count;
+ int bqcnt2;
+ Id dp;
+
+#ifdef CPLXDEBUG
+ printf("normalize_dep %s todnf:%d\n", pool_dep2str(pool, dep), todnf);
+#endif
+ if (pool_is_complex_dep(pool, dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags == REL_AND || rd->flags == REL_OR || rd->flags == REL_COND)
+ {
+ int flags = rd->flags;
+ int r, mode;
+
+ /* in inverted mode, COND means AND. otherwise it means OR NOT */
+ if (flags == REL_COND && todnf)
+ flags = REL_AND;
+ mode = flags == REL_AND ? 0 : 1;
+
+ /* get blocks of first argument */
+ r = normalize_dep(pool, rd->name, bq, todnf);
+ if (r == 0)
+ {
+ if (flags == REL_AND)
+ return 0;
+ if (flags == REL_COND)
+ {
+ r = normalize_dep(pool, rd->evr, bq, todnf ^ 1);
+ if (r == 0 || r == 1)
+ return r == 0 ? 1 : 0;
+ invert_depblocks(pool, bq, bqcnt); /* invert block for COND */
+ return r;
+ }
+ return normalize_dep(pool, rd->evr, bq, todnf);
+ }
+ if (r == 1)
+ {
+ if (flags != REL_AND)
+ return 1;
+ return normalize_dep(pool, rd->evr, bq, todnf);
+ }
+
+ /* get blocks of second argument */
+ bqcnt2 = bq->count;
+ /* COND is OR with NEG on evr block, so we invert the todnf flag in that case*/
+ r = normalize_dep(pool, rd->evr, bq, flags == REL_COND ? todnf ^ 1 : todnf);
+ if (r == 0)
+ {
+ if (flags == REL_OR)
+ return -1;
+ queue_truncate(bq, bqcnt);
+ return flags == REL_COND ? 1 : 0;
+ }
+ if (r == 1)
+ {
+ if (flags == REL_OR)
+ {
+ queue_truncate(bq, bqcnt);
+ return 1;
+ }
+ return -1;
+ }
+ if (flags == REL_COND)
+ invert_depblocks(pool, bq, bqcnt2); /* invert 2nd block */
+ if (mode == todnf)
+ {
+ /* simple case: just join em. nothing more to do here. */
+#ifdef CPLXDEBUG
+ printf("SIMPLE JOIN %d %d %d\n", bqcnt, bqcnt2, bq->count);
+#endif
+ return -1;
+ }
+ else
+ {
+ /* complex case: mix em */
+ int i, j, bqcnt3;
+#ifdef CPLXDEBUG
+ printf("COMPLEX JOIN %d %d %d\n", bqcnt, bqcnt2, bq->count);
+#endif
+ bqcnt2 = expand_simpledeps(pool, bq, bqcnt, bqcnt2);
+ bqcnt3 = bq->count;
+ for (i = bqcnt; i < bqcnt2; i++)
+ {
+ for (j = bqcnt2; j < bqcnt3; j++)
+ {
+ int a, b;
+ int bqcnt4 = bq->count;
+ int k = i;
+
+ /* mix i block with j block, both blocks are sorted */
+ while (bq->elements[k] && bq->elements[j])
+ {
+ if (bq->elements[k] < bq->elements[j])
+ queue_push(bq, bq->elements[k++]);
+ else
+ {
+ if (bq->elements[k] == bq->elements[j])
+ k++;
+ queue_push(bq, bq->elements[j++]);
+ }
+ }
+ while (bq->elements[j])
+ queue_push(bq, bq->elements[j++]);
+ while (bq->elements[k])
+ queue_push(bq, bq->elements[k++]);
+
+ /* block is finished, check for A + -A */
+ for (a = bqcnt4, b = bq->count - 1; a < b; )
+ {
+ if (-bq->elements[a] == bq->elements[b])
+ break;
+ if (-bq->elements[a] > bq->elements[b])
+ a++;
+ else
+ b--;
+ }
+ if (a < b)
+ queue_truncate(bq, bqcnt4); /* ignore this block */
+ else
+ queue_push(bq, 0); /* finish block */
+ }
+ /* advance to next block */
+ while (bq->elements[i])
+ i++;
+ }
+ i = -1;
+ if (bqcnt3 == bq->count) /* ignored all blocks? */
+ i = todnf ? 0 : 1;
+ queue_deleten(bq, bqcnt, bqcnt3 - bqcnt);
+ return i;
+ }
+ }
+ }
+
+ /* fallback case: just use package list */
+ dp = pool_whatprovides(pool, dep);
+ if (dp <= 2 || !pool->whatprovidesdata[dp])
+ return dp == 2 ? 1 : 0;
+ if (todnf)
+ {
+ for (; pool->whatprovidesdata[dp]; dp++)
+ queue_push2(bq, pool->whatprovidesdata[dp], 0);
+ }
+ else
+ {
+ queue_push2(bq, pool->nsolvables, dp); /* not yet expanded marker */
+ queue_push(bq, 0);
+ }
+ return -1;
+}
+
+int
+pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags)
+{
+ int i, bqcnt = bq->count;
+
+ i = normalize_dep(pool, dep, bq, (flags & CPLXDEPS_TODNF) ? 1 : 0);
+ if ((flags & CPLXDEPS_EXPAND) != 0)
+ {
+ if (i != 0 && i != 1)
+ expand_simpledeps(pool, bq, bqcnt, 0);
+ }
+ if ((flags & CPLXDEPS_INVERT) != 0)
+ {
+ if (i == 0 || i == 1)
+ i ^= 1;
+ else
+ invert_depblocks(pool, bq, bqcnt);
+ }
+#ifdef CPLXDEBUG
+ if (i == 0)
+ printf("NONE\n");
+ else if (i == 1)
+ printf("ALL\n");
+ else
+ {
+ for (i = bqcnt; i < bq->count; i++)
+ {
+ if (bq->elements[i] == pool->nsolvables)
+ {
+ Id *dp = pool->whatprovidesdata + bq->elements[++i];
+ printf(" (");
+ while (*dp)
+ printf(" %s", pool_solvid2str(pool, *dp++));
+ printf(" )");
+ }
+ else if (bq->elements[i] > 0)
+ printf(" %s", pool_solvid2str(pool, bq->elements[i]));
+ else if (bq->elements[i] < 0)
+ printf(" -%s", pool_solvid2str(pool, -bq->elements[i]));
+ else
+ printf(" ||");
+ }
+ printf("\n");
+ i = -1;
+ }
+#endif
+ return i;
+}
+
+#endif /* ENABLE_COMPLEX_DEPS */
+
--- /dev/null
+/*
+ * Copyright (c) 2014, Novell Inc.
+ *
+ * This program is licensed under the BSD license, read LICENSE.BSD
+ * for further information
+ */
+
+/*
+ * cplxdeps.h (internal)
+ */
+
+#ifndef LIBSOLV_CPLXDEPS_H
+#define LIBSOLV_CPLXDEPS_H
+
+extern int pool_is_complex_dep_rd(Pool *pool, Reldep *rd);
+
+static inline int
+pool_is_complex_dep(Pool *pool, Id dep)
+{
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags >= 8 && pool_is_complex_dep_rd(pool, rd))
+ return 1;
+ }
+ return 0;
+}
+
+extern int pool_normalize_complex_dep(Pool *pool, Id dep, Queue *bq, int flags);
+
+#define CPLXDEPS_TODNF (1 << 0)
+#define CPLXDEPS_EXPAND (1 << 1)
+#define CPLXDEPS_INVERT (1 << 2)
+
+#endif
+
#include "policy.h"
#include "solverdebug.h"
#include "linkedpkg.h"
+#include "cplxdeps.h"
#define RULES_BLOCK 63
#endif
+#ifdef ENABLE_COMPLEX_DEPS
+
+static void
+add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *workq, Map *m)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ int i, j;
+ Queue bq;
+
+ queue_init(&bq);
+
+ /* CNF expansion for requires, DNF + INVERT expansion for conflicts */
+ i = pool_normalize_complex_dep(pool, dep, &bq, type == SOLVER_RULE_RPM_PACKAGE_REQUIRES ? 0 : (CPLXDEPS_TODNF | CPLXDEPS_EXPAND | CPLXDEPS_INVERT));
+ /* handle special cases */
+ if (i == 0)
+ {
+ if (dontfix)
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken dependency %s of installed package %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, p));
+ }
+ else
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, p), p, pool_dep2str(pool, dep));
+ addrpmrule(solv, -p, 0, type == SOLVER_RULE_RPM_PACKAGE_REQUIRES ? SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP : type, dep);
+ }
+ queue_free(&bq);
+ return;
+ }
+ if (i == 1)
+ {
+ queue_free(&bq);
+ return;
+ }
+
+ /* go through all blocks and add a rule for each block */
+ for (i = 0; i < bq.count; i++)
+ {
+ if (!bq.elements[i])
+ continue; /* huh? */
+ if (bq.elements[i] == pool->nsolvables)
+ {
+ /* conventional requires (cannot be a conflicts as they have been expanded) */
+ Id *dp = pool->whatprovidesdata + bq.elements[i + 1];
+ i += 2;
+ if (dontfix)
+ {
+ for (j = 0; dp[j] != 0; j++)
+ if (pool->solvables[dp[j]].repo == installed)
+ break; /* provider was installed */
+ if (!dp[j])
+ {
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "ignoring broken requires %s of installed package %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, p));
+ continue;
+ }
+ }
+ if (!*dp)
+ {
+ /* nothing provides req! */
+ POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s [%d] is not installable (%s)\n", pool_solvid2str(pool, p), p, pool_dep2str(pool, dep));
+ addrpmrule(solv, -p, 0, SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP, dep);
+ continue;
+ }
+ addrpmrule(solv, -p, dp - pool->whatprovidesdata, SOLVER_RULE_RPM_PACKAGE_REQUIRES, dep);
+ /* push all non-visited providers on the work queue */
+ if (m)
+ for (; *dp; dp++)
+ if (!MAPTST(m, *dp))
+ queue_push(workq, *dp);
+ continue;
+ }
+ if (!bq.elements[i + 1])
+ {
+ Id p2 = bq.elements[i];
+ /* simple rule with just two literals */
+ if (dontfix && p2 < 0 && pool->solvables[-p2].repo == installed)
+ continue;
+ if (dontfix && p2 > 0 && pool->solvables[p2].repo != installed)
+ continue;
+ if (p == p2)
+ continue;
+ if (-p == p2)
+ {
+ if (type == SOLVER_RULE_RPM_PACKAGE_CONFLICT)
+ {
+ if (pool->forbidselfconflicts && !is_otherproviders_dep(pool, dep))
+ addrpmrule(solv, -p, 0, SOLVER_RULE_RPM_SELF_CONFLICT, dep);
+ continue;
+ }
+ addrpmrule(solv, -p, 0, type, dep);
+ continue;
+ }
+ if (p2 > 0)
+ addrpmrule(solv, p2, -p, type, dep); /* hack so that we don't need pool_queuetowhatprovides */
+ else
+ addrpmrule(solv, -p, p2, type, dep);
+ if (m && p2 > 0 && !MAPTST(m, p2))
+ queue_push(workq, p2);
+ }
+ else
+ {
+ Id *qele;
+ int qcnt;
+
+ qele = bq.elements + i;
+ qcnt = i;
+ while (bq.elements[i])
+ i++;
+ qcnt = i - qcnt;
+ if (dontfix)
+ {
+ for (j = 0; j < qcnt; j++)
+ {
+ if (qele[j] > 0 && pool->solvables[qele[j]].repo == installed)
+ break;
+ if (qele[j] < 0 && pool->solvables[-qele[j]].repo != installed)
+ break;
+ }
+ if (j == qcnt)
+ continue;
+ }
+ /* add -p to (ordered) rule (overwriting the trailing zero) */
+ for (j = 0; ; j++)
+ {
+ if (j == qcnt || qele[j] > -p)
+ {
+ if (j < qcnt)
+ memmove(qele + j + 1, qele + j, (qcnt - j) * sizeof(Id));
+ qele[j] = -p;
+ qcnt++;
+ break;
+ }
+ if (qele[j] == -p)
+ break;
+ }
+ /* check if the rule contains both p and -p */
+ for (j = 0; j < qcnt; j++)
+ if (qele[j] == p)
+ break;
+ if (j == qcnt)
+ {
+ /* hack: create fake queue 'q' so that we can call pool_queuetowhatprovides */
+ Queue q;
+ memset(&q, 0, sizeof(q));
+ q.count = qcnt - 1;
+ q.elements = qele + 1;
+ addrpmrule(solv, qele[0], pool_queuetowhatprovides(pool, &q), type, dep);
+ if (m)
+ for (j = 0; j < qcnt; j++)
+ if (qele[j] > 0 && !MAPTST(m, qele[j]))
+ queue_push(workq, qele[j]);
+ }
+ }
+ }
+ queue_free(&bq);
+}
+
+#endif
+
/*-------------------------------------------------------------------
*
* add (install) rules for solvable
if (req == SOLVABLE_PREREQMARKER) /* skip the marker */
continue;
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, req))
+ {
+ /* we have AND/COND deps, normalize */
+ add_complex_deprules(solv, n, req, SOLVER_RULE_RPM_PACKAGE_REQUIRES, dontfix, &workq, m);
+ continue;
+ }
+#endif
+
/* find list of solvables providing 'req' */
dp = pool_whatprovides_ptr(pool, req);
/* foreach conflicts of 's' */
while ((con = *conp++) != 0)
{
+#ifdef ENABLE_COMPLEX_DEPS
+ if (!ispatch && pool_is_complex_dep(pool, con))
+ {
+ /* we have AND/COND deps, normalize */
+ add_complex_deprules(solv, n, con, SOLVER_RULE_RPM_PACKAGE_CONFLICT, dontfix, &workq, m);
+ continue;
+ }
+#endif
/* foreach providers of a conflict of 's' */
FOR_PROVIDES(p, pp, con)
{
#include "policy.h"
#include "poolarch.h"
#include "solverdebug.h"
+#include "cplxdeps.h"
#define RULES_BLOCK 63
queue_truncate(q, j);
}
+#ifdef ENABLE_COMPLEX_DEPS
+
+static void
+add_complex_recommends(Solver *solv, Id rec, Queue *dq, Map *dqmap)
+{
+ Pool *pool = solv->pool;
+ int oldcnt = dq->count;
+ int cutcnt, blkcnt;
+ Id p;
+ int i, j;
+
+ printf("ADD_COMPLEX_RECOMMENDS %s\n", pool_dep2str(pool, rec));
+ i = pool_normalize_complex_dep(pool, rec, dq, CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ return;
+ cutcnt = dq->count;
+ for (i = oldcnt; i < cutcnt; i++)
+ {
+ blkcnt = dq->count;
+ for (; (p = dq->elements[i]) != 0; i++)
+ {
+ if (p < 0)
+ {
+ if (solv->decisionmap[-p] <= 0)
+ break;
+ continue;
+ }
+ if (solv->decisionmap[p] > 0)
+ {
+ queue_truncate(dq, blkcnt);
+ break;
+ }
+ if (dqmap)
+ {
+ if (!MAPTST(dqmap, p))
+ continue;
+ }
+ else
+ {
+ if (solv->decisionmap[p] < 0)
+ continue;
+ if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))))
+ continue;
+ }
+ queue_push(dq, p);
+ }
+ while (dq->elements[i])
+ i++;
+ }
+ queue_deleten(dq, oldcnt, cutcnt - oldcnt);
+ /* unify */
+ if (dq->count != oldcnt)
+ {
+ for (j = oldcnt; j < dq->count; j++)
+ {
+ p = dq->elements[j];
+ for (i = 0; i < j; i++)
+ if (dq->elements[i] == p)
+ {
+ dq->elements[j] = 0;
+ break;
+ }
+ }
+ for (i = j = oldcnt; j < dq->count; j++)
+ if (dq->elements[j])
+ dq->elements[i++] = dq->elements[j];
+ queue_truncate(dq, i);
+ }
+ printf("RETURN:\n");
+ for (i = oldcnt; i < dq->count; i++)
+ printf(" - %s\n", pool_solvid2str(pool, dq->elements[i]));
+}
+
+static void
+do_complex_recommendations(Solver *solv, Id rec, Map *m, int noselected)
+{
+ Pool *pool = solv->pool;
+ Queue dq;
+ Id p;
+ int i, blk;
+
+ printf("DO_COMPLEX_RECOMMENDATIONS %s\n", pool_dep2str(pool, rec));
+ queue_init(&dq);
+ i = pool_normalize_complex_dep(pool, rec, &dq, CPLXDEPS_EXPAND);
+ if (i == 0 || i == 1)
+ {
+ queue_free(&dq);
+ return;
+ }
+ for (i = 0; i < dq.count; i++)
+ {
+ blk = i;
+ for (; (p = dq.elements[i]) != 0; i++)
+ {
+ if (p < 0)
+ {
+ if (solv->decisionmap[-p] <= 0)
+ break;
+ continue;
+ }
+ if (solv->decisionmap[p] > 0)
+ {
+ if (noselected)
+ break;
+ MAPSET(m, p);
+ for (i++; (p = dq.elements[i]) != 0; i++)
+ if (p > 0 && solv->decisionmap[p] > 0)
+ MAPSET(m, p);
+ p = 1;
+ break;
+ }
+ }
+ if (!p)
+ {
+ for (i = blk; (p = dq.elements[i]) != 0; i++)
+ if (p > 0)
+ MAPSET(m, p);
+ }
+ while (dq.elements[i])
+ i++;
+ }
+ queue_free(&dq);
+}
+
+#endif
+
/*-------------------------------------------------------------------
*
* solver_run_sat
recp = s->repo->idarraydata + s->recommends;
while ((rec = *recp++) != 0)
{
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, rec))
+ {
+ add_complex_recommends(solv, rec, &dq, 0);
+ continue;
+ }
+#endif
qcount = dq.count;
FOR_PROVIDES(p, pp, rec)
{
while ((rec = *recp++) != 0)
{
queue_empty(&dq);
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, rec))
+ add_complex_recommends(solv, rec, &dq, &dqmap);
+ else
+#endif
FOR_PROVIDES(p, pp, rec)
{
if (solv->decisionmap[p] > 0)
break;
}
else if (solv->decisionmap[p] == 0 && MAPTST(&dqmap, p))
- queue_pushunique(&dq, p);
+ queue_push(&dq, p);
}
if (!dq.count)
continue;
recp = s->repo->idarraydata + s->recommends;
while ((rec = *recp++) != 0)
{
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, rec))
+ {
+ do_complex_recommendations(solv, rec, &solv->recommendsmap, noselected);
+ continue;
+ }
+#endif
FOR_PROVIDES(p, pp, rec)
if (solv->decisionmap[p] > 0)
break;
sugp = s->repo->idarraydata + s->suggests;
while ((sug = *sugp++) != 0)
{
+#ifdef ENABLE_COMPLEX_DEPS
+ if (pool_is_complex_dep(pool, sug))
+ {
+ do_complex_recommendations(solv, sug, &solv->suggestsmap, noselected);
+ continue;
+ }
+#endif
FOR_PROVIDES(p, pp, sug)
if (solv->decisionmap[p] > 0)
break;