add support for complex dependencies
authorMichael Schroeder <mls@suse.de>
Wed, 5 Mar 2014 14:33:56 +0000 (15:33 +0100)
committerMichael Schroeder <mls@suse.de>
Wed, 5 Mar 2014 14:33:56 +0000 (15:33 +0100)
CMakeLists.txt
src/CMakeLists.txt
src/cplxdeps.c [new file with mode: 0644]
src/cplxdeps.h [new file with mode: 0644]
src/rules.c
src/solver.c

index c1f9fc5..816c333 100644 (file)
@@ -206,7 +206,7 @@ FOREACH (VAR HAVE_STRCHRNUL HAVE_FOPENCOOKIE HAVE_FUNOPEN WORDS_BIGENDIAN
   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})
index c917c7e..f6542ce 100644 (file)
@@ -17,7 +17,7 @@ SET (libsolv_SRCS
     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
diff --git a/src/cplxdeps.c b/src/cplxdeps.c
new file mode 100644 (file)
index 0000000..edb7cf8
--- /dev/null
@@ -0,0 +1,311 @@
+/*
+ * 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 */
+
diff --git a/src/cplxdeps.h b/src/cplxdeps.h
new file mode 100644 (file)
index 0000000..101d7ad
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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
+
index dad682f..9139812 100644 (file)
@@ -27,6 +27,7 @@
 #include "policy.h"
 #include "solverdebug.h"
 #include "linkedpkg.h"
+#include "cplxdeps.h"
 
 #define RULES_BLOCK 63
 
@@ -532,6 +533,165 @@ add_package_link(Solver *solv, Solvable *s, Map *m, Queue *workq)
 
 #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
@@ -631,6 +791,15 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
              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);
 
@@ -704,6 +873,14 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
          /* 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)
                {
index 4ffdec4..8ca9efe 100644 (file)
@@ -25,6 +25,7 @@
 #include "policy.h"
 #include "poolarch.h"
 #include "solverdebug.h"
+#include "cplxdeps.h"
 
 #define RULES_BLOCK 63
 
@@ -1750,6 +1751,132 @@ prune_to_update_targets(Solver *solv, Id *cp, Queue *q)
   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
@@ -2205,6 +2332,13 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                      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)
                            {
@@ -2408,6 +2542,11 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                  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)
@@ -2416,7 +2555,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                              break;
                            }
                          else if (solv->decisionmap[p] == 0 && MAPTST(&dqmap, p))
-                           queue_pushunique(&dq, p);
+                           queue_push(&dq, p);
                        }
                      if (!dq.count)
                        continue;
@@ -3856,6 +3995,13 @@ void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *su
              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;
@@ -3921,6 +4067,13 @@ void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *su
              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;