apply the same code as https://build.opensuse.org/package/show/OBS:Server:2.10/perl... 34/306434/4 devel devel-py2 master
authorwangbiao <biao716.wang@samsung.com>
Wed, 21 Feb 2024 09:35:39 +0000 (18:35 +0900)
committerwangbiao <biao716.wang@samsung.com>
Mon, 26 Feb 2024 03:18:55 +0000 (12:18 +0900)
Enable zstd compress and supoort complex(rich) dependency in Ubuntu
Change-Id: Ic77f31278e11a1e632002011ac12970fca3200a6
Signed-off-by: wangbiao <biao716.wang@samsung.com>
BSSolv.xs
debian/control
debian/rules

index 68814e0..ffd992c 100644 (file)
--- a/BSSolv.xs
+++ b/BSSolv.xs
@@ -1,3 +1,10 @@
+/*
+ * Copyright (c) 2009 - 2017 SUSE Linux Products GmbH
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the same terms as Perl itself.
+ *
+ */
 #ifndef _GNU_SOURCE
 #define _GNU_SOURCE
 #endif
 
 #define MULTI_SEMANTICS
 
+#include "solvversion.h"
+#if LIBSOLV_VERSION < 623
+#define LIBSOLVEXT_FEATURE_DEBIAN
+#define LIBSOLVEXT_FEATURE_ARCHREPO
+#endif
+
 #include "pool.h"
 #include "repo.h"
 #include "util.h"
 #include "evr.h"
 #include "hash.h"
 #include "chksum.h"
+#include "testcase.h"
 #include "repo_solv.h"
 #include "repo_write.h"
 #include "repo_rpmdb.h"
+#if defined(LIBSOLVEXT_FEATURE_DEBIAN)
 #include "repo_deb.h"
-#if 1
+#endif
+#if defined(LIBSOLVEXT_FEATURE_ARCHREPO)
 #include "repo_arch.h"
 #endif
+#if defined(LIBSOLV_FEATURE_COMPLEX_DEPS)
+#include "pool_parserpmrichdep.h"
+#endif
+
+#ifndef REL_ERROR
+# define REL_ERROR 27          /* for old libsolv versions */
+#endif
+#ifndef REL_UNLESS
+# define REL_UNLESS 29         /* for old libsolv versions */
+#endif
+
+#define EXPANDER_DEBUG_ALL             (1 << 0)
+#define EXPANDER_DEBUG_STDOUT          (1 << 1)
+#define EXPANDER_DEBUG_STR             (1 << 2)
+
+#define EXPANDER_OPTION_IGNOREIGNORE                   (1 << 0)
+#define EXPANDER_OPTION_IGNORECONFLICTS                        (1 << 1)
+#define EXPANDER_OPTION_DORECOMMENDS                   (1 << 2)
+#define EXPANDER_OPTION_DOSUPPLEMENTS                  (1 << 3)
+#define EXPANDER_OPTION_USERECOMMENDSFORCHOICES                (1 << 4)
+#define EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES       (1 << 5)
 
 typedef struct _Expander {
   Pool *pool;
@@ -38,17 +75,54 @@ typedef struct _Expander {
   Queue conflictsq;
   Map conflicts;
 
-  int debug;
   int havefileprovides;
-  int ignoreconflicts;
-  int ignoreignore;
 
+  /* debug support */
+  int debug;
   char *debugstr;
   int debugstrl;
   int debugstrf;
+
+  /* options */
+  int ignoreconflicts;
+  int ignoreignore;
   int userecommendsforchoices;
+  int usesupplementsforchoices;
+  int dorecommends;
+  int dosupplements;
 } Expander;
 
+typedef struct _ExpanderCtx {
+  Pool *pool;
+  Expander *xp;
+  Queue *out;                  /* the result */
+  Map installed;               /* installed packages */
+  Map conflicts;               /* conflicts from installed packages */
+  Queue conflictsinfo;         /* source info for the above */
+  int cidone;                  /* conflictsinfo done position */
+  Queue todo;                  /* requires todo list */
+  Queue errors;                        /* expansion errors */
+  Queue cplxq;                 /* complex dep work queue */
+  Queue cplxblks;              /* complex dep block data, add only */
+  Queue todo_cond;             /* delayed requires/conflicts */
+  Queue pruneq;                        /* multi purpose queue for pruning packages */
+  Map todo_condmap;            /* all neg packages in todo_cond blocks */
+  Map recommended;             /* recommended packages */
+  int recdone;                 /* recommended done position */
+
+  /* options */
+  int ignoreconflicts;
+  int ignoreignore;
+  int userecommendsforchoices;
+  int usesupplementsforchoices;
+  int dorecommends;
+  int dosupplements;
+
+  /* hacks */
+  Solvable *ignore_s;          /* small hack: ignore requires of this solvable */
+} ExpanderCtx;
+
+
 typedef Pool *BSSolv__pool;
 typedef Repo *BSSolv__repo;
 typedef Expander *BSSolv__expander;
@@ -57,9 +131,14 @@ static Id buildservice_id;
 static Id buildservice_repocookie;
 static Id buildservice_external;
 static Id buildservice_dodurl;
-static Id expander_directdepsend;
 static Id buildservice_dodcookie;
+static Id buildservice_dodresources;
+static Id buildservice_annotation;
+static Id buildservice_modules;
+static Id expander_directdepsend;
+
 static int genmetaalgo;
+static int depsortsccs;
 
 /* make sure bit n is usable */
 #define MAPEXP(m, n) ((m)->size < (((n) + 8) >> 3) ? map_grow(m, n + 256) : 0)
@@ -150,7 +229,7 @@ id2name(Pool *pool, Id id)
 }
 
 static Id
-dep2id(Pool *pool, char *s)
+dep2id_rec(Pool *pool, char *s)
 {
   char *n;
   Id id;
@@ -158,17 +237,26 @@ dep2id(Pool *pool, char *s)
 
   if ((n = strchr(s, '|')) != 0)
     {
-      id = dep2id(pool, n + 1);
+      id = dep2id_rec(pool, n + 1);
       *n = 0;
-      id = pool_rel2id(pool, dep2id(pool, s), id, REL_OR, 1);
+      id = pool_rel2id(pool, dep2id_rec(pool, s), id, REL_OR, 1);
       *n = '|';
       return id;
     }
   while (*s == ' ' || *s == '\t')
     s++;
   n = s;
-  while (*s && *s != ' ' && *s != '\t' && *s != '<' && *s != '=' && *s != '>')
-    s++;
+  if (pool->disttype == DISTTYPE_RPM)
+    {
+      /* rpm delimits the name by whitespace only */
+      while (*s && *s != ' ' && *s != '\t')
+        s++;
+    }
+  else
+    {
+      while (*s && *s != ' ' && *s != '\t' && *s != '<' && *s != '=' && *s != '>')
+        s++;
+    }
 #ifdef REL_MULTIARCH
   if (s - n > 4 && s[-4] == ':' && !strncmp(s - 4, ":any", 4))
     {
@@ -186,13 +274,13 @@ dep2id(Pool *pool, char *s)
   for (;;s++)
     {
       if (*s == '<')
-       flags |= REL_LT;
+       flags |= REL_LT;
       else if (*s == '=')
-       flags |= REL_EQ;
+       flags |= REL_EQ;
       else if (*s == '>')
-       flags |= REL_GT;
+       flags |= REL_GT;
       else
-       break;
+       break;
     }
   if (!flags)
     return id;
@@ -204,11 +292,38 @@ dep2id(Pool *pool, char *s)
   return pool_rel2id(pool, id, pool_strn2id(pool, n, s - n, 1), flags, 1);
 }
 
-static inline Offset
+static Id
+parsedep_error(Pool *pool, const char *s)
+{
+  Id id;
+  id = pool_str2id(pool, s, 1);
+  return pool_rel2id(pool, pool_str2id(pool, "dependency parse error", 1), id, REL_ERROR, 1);
+}
+
+static Id
+dep2id(Pool *pool, char *s)
+{
+  Id id;
+  if (pool->disttype == DISTTYPE_RPM && *s == '(')
+    {
+#if defined(LIBSOLV_FEATURE_COMPLEX_DEPS)
+      id = pool_parserpmrichdep(pool, s);
+#else
+      id = 0;
+#endif
+    }
+  else
+    id = dep2id_rec(pool, s);
+  if (!id)
+    id = parsedep_error(pool, s);
+  return id;
+}
+
+static Offset
 importdeps(HV *hv, const char *key, int keyl, Repo *repo)
 {
   Pool *pool = repo->pool;
-  int i;
+  SSize_t i;
   AV *av = hvlookupav(hv, key, keyl);
   Offset off = 0;
   if (av)
@@ -217,13 +332,18 @@ importdeps(HV *hv, const char *key, int keyl, Repo *repo)
        {
          char *str = avlookupstr(av, i);
          if (str)
-           off = repo_addid_dep(repo, off, dep2id(pool, str), 0);
+           {
+             Id id = testcase_str2dep(pool, str);
+             if (!id)
+               id = parsedep_error(pool, str);
+             off = repo_addid_dep(repo, off, id, 0);
+           }
        }
     }
   return off;
 }
 
-void
+static void
 exportdeps(HV *hv, const char *key, int keyl, Repo *repo, Offset off, Id skey)
 {
   Pool *pool = repo->pool;
@@ -239,64 +359,7 @@ exportdeps(HV *hv, const char *key, int keyl, Repo *repo, Offset off, Id skey)
     {
       if (id == SOLVABLE_FILEMARKER)
        break;
-      str = pool_dep2str(pool, id);
-      if (ISRELDEP(id))
-       {
-         Reldep *rd = GETRELDEP(pool, id);
-         if (skey == SOLVABLE_CONFLICTS && rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_OTHERPROVIDERS)
-           {
-           if (!strncmp(str, "namespace:", 10))
-             str += 10;
-           }
-         if (skey == SOLVABLE_SUPPLEMENTS)
-           {
-             if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_FILESYSTEM)
-               {
-                 if (!strncmp(str, "namespace:", 10))
-                   str += 10;
-               }
-             else if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_MODALIAS)
-               {
-                 if (!strncmp(str, "namespace:", 10))
-                   str += 10;
-               }
-             else if (rd->flags == REL_AND)
-               {
-                 /* either packageand chain or modalias */
-                 str = 0;
-                 if (ISRELDEP(rd->evr))
-                   {
-                     Reldep *mrd = GETRELDEP(pool, rd->evr);
-                     if (mrd->flags == REL_NAMESPACE && mrd->name == NAMESPACE_MODALIAS)
-                       {
-                         str = pool_tmpjoin(pool, "modalias(", pool_dep2str(pool, rd->name), ":");
-                         str = pool_tmpappend(pool, str, pool_dep2str(pool, mrd->evr), ")");
-                       }
-                     else if (mrd->flags >= 8)
-                       continue;
-                   }
-                 if (!str)
-                   {
-                     /* must be and chain */
-                     str = pool_dep2str(pool, rd->evr);
-                     for (;;)
-                       {
-                         id = rd->name;
-                         if (!ISRELDEP(id))
-                           break;
-                         rd = GETRELDEP(pool, id);
-                         if (rd->flags != REL_AND)
-                           break;
-                         str = pool_tmpjoin(pool, pool_dep2str(pool, rd->evr), ":", str);
-                       }
-                     str = pool_tmpjoin(pool, pool_dep2str(pool, id), ":", str);
-                     str = pool_tmpjoin(pool, "packageand(", str, ")");
-                   }
-               }
-             else if (rd->flags >= 8)
-               continue;
-           }
-       }
+      str = testcase_dep2str(pool, id);
       if (skey == SOLVABLE_REQUIRES)
        {
          if (id == SOLVABLE_PREREQMARKER)
@@ -312,104 +375,179 @@ exportdeps(HV *hv, const char *key, int keyl, Repo *repo, Offset off, Id skey)
     (void)hv_store(hv, key, keyl, newRV_noinc((SV*)av), 0);
 }
 
-void
-data2solvables(Repo *repo, Repodata *data, HV *rhv)
+static int
+data2pkg(Repo *repo, Repodata *data, HV *hv, int isdod)
 {
   Pool *pool = repo->pool;
-  SV *sv;
-  HV *hv;
-  char *str, *key;
-  I32 keyl;
+  char *str;
   Id p;
   Solvable *s;
+  AV *av;
 
-  hv_iterinit(rhv);
-  while ((sv = hv_iternextsv(rhv, &key, &keyl)) != 0)
+  str = hvlookupstr(hv, "name", 4);
+  if (!str)
+    return 0;  /* need to have a name */
+  p = repo_add_solvable(repo);
+  s = pool_id2solvable(pool, p);
+  s->name = pool_str2id(pool, str, 1);
+  str = hvlookupstr(hv, "arch", 4);
+  if (!str)
+    str = "";  /* dummy, need to have arch */
+  s->arch = pool_str2id(pool, str, 1);
+  s->evr = makeevr(pool, hvlookupstr(hv, "epoch", 5), hvlookupstr(hv, "version", 7), hvlookupstr(hv, "release", 7));
+  str = hvlookupstr(hv, "path", 4);
+  if (str)
     {
-      if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV)
-       continue;
-      hv = (HV *)SvRV(sv);
-      str = hvlookupstr(hv, "name", 4);
-      if (!str)
-       continue;       /* need to have a name */
-      p = repo_add_solvable(repo);
-      s = pool_id2solvable(pool, p);
-      s->name = pool_str2id(pool, str, 1);
-      str = hvlookupstr(hv, "arch", 4);
-      if (!str)
-       str = "";       /* dummy, need to have arch */
-      s->arch = pool_str2id(pool, str, 1);
-      s->evr = makeevr(pool, hvlookupstr(hv, "epoch", 5), hvlookupstr(hv, "version", 7), hvlookupstr(hv, "release", 7));
-      str = hvlookupstr(hv, "path", 4);
-      if (str)
+      char *ss = strrchr(str, '/');
+      if (ss)
        {
-         char *ss = strrchr(str, '/');
-         if (ss)
-           {
-             *ss = 0;
-             repodata_set_str(data, p, SOLVABLE_MEDIADIR, str);
-             *ss++ = '/';
-           }
-         else
-           ss = str;
-         repodata_set_str(data, p, SOLVABLE_MEDIAFILE, ss);
+         *ss = 0;
+         repodata_set_str(data, p, SOLVABLE_MEDIADIR, str);
+         *ss++ = '/';
        }
+      else
+       ss = str;
+      repodata_set_str(data, p, SOLVABLE_MEDIAFILE, ss);
+    }
+  if (isdod)
+    repodata_set_str(data, p, buildservice_id, "dod");
+  else
+    {
       str = hvlookupstr(hv, "id", 2);
       if (str)
        repodata_set_str(data, p, buildservice_id, str);
-      str = hvlookupstr(hv, "source", 6);
-      if (str)
-       repodata_set_poolstr(data, p, SOLVABLE_SOURCENAME, str);
+    }
+  str = hvlookupstr(hv, "source", 6);
+  if (str)
+    repodata_set_poolstr(data, p, SOLVABLE_SOURCENAME, str);
+  if (isdod)
+    {
+      static unsigned char dod_pkgid[16] = { 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0, 0xd0 };
+      repodata_set_bin_checksum(data, p, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, dod_pkgid);
+    }
+  else
+    {
       str = hvlookupstr(hv, "hdrmd5", 6);
       if (str && strlen(str) == 32)
        repodata_set_checksum(data, p, SOLVABLE_PKGID, REPOKEY_TYPE_MD5, str);
-      s->provides    = importdeps(hv, "provides", 8, repo);
-      s->obsoletes   = importdeps(hv, "obsoletes", 9, repo);
-      s->conflicts   = importdeps(hv, "conflicts", 9, repo);
-      s->requires    = importdeps(hv, "requires", 8, repo);
-      s->recommends  = importdeps(hv, "recommends", 10, repo);
-      s->suggests    = importdeps(hv, "suggests", 8, repo);
-      s->supplements = importdeps(hv, "supplements", 11, repo);
-      s->enhances    = importdeps(hv, "enhances", 8, repo);
-      if (!s->evr && s->provides)
-       {
-         /* look for self provides */
-         Id pro, *prop = s->repo->idarraydata + s->provides;
-         while ((pro = *prop++) != 0)
-           {
-             Reldep *rd;
-             if (!ISRELDEP(pro))
-               continue;
-             rd = GETRELDEP(pool, pro);
-             if (rd->name == s->name && rd->flags == REL_EQ)
-               s->evr = rd->evr;
-           }
+    }
+  s->provides    = importdeps(hv, "provides", 8, repo);
+  s->obsoletes   = importdeps(hv, "obsoletes", 9, repo);
+  s->conflicts   = importdeps(hv, "conflicts", 9, repo);
+  s->requires    = importdeps(hv, "requires", 8, repo);
+  s->recommends  = importdeps(hv, "recommends", 10, repo);
+  s->suggests    = importdeps(hv, "suggests", 8, repo);
+  s->supplements = importdeps(hv, "supplements", 11, repo);
+  s->enhances    = importdeps(hv, "enhances", 8, repo);
+  if (!s->evr && s->provides)
+    {
+      /* look for self provides */
+      Id pro, *prop = s->repo->idarraydata + s->provides;
+      while ((pro = *prop++) != 0)
+       {
+         Reldep *rd;
+         if (!ISRELDEP(pro))
+           continue;
+         rd = GETRELDEP(pool, pro);
+         if (rd->name == s->name && rd->flags == REL_EQ)
+           s->evr = rd->evr;
+       }
+    }
+  if (s->evr && s->arch != ARCH_SRC && s->arch != ARCH_NOSRC)
+    s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
+  str = hvlookupstr(hv, "checksum", 8);
+  if (str)
+    {
+      char *cp, typebuf[8];
+      Id ctype;
+      if (*str != ':' && (cp = strchr(str, ':')) != 0 && cp - str < sizeof(typebuf))
+       {
+         strncpy(typebuf, str, cp - str);
+         typebuf[cp - str] = 0;
+         ctype = solv_chksum_str2type(typebuf);
+         if (ctype)
+           repodata_set_checksum(data, p, SOLVABLE_CHECKSUM, ctype, cp + 1);
+       }
+    }
+  str = hvlookupstr(hv, "annotation", 10);
+  if (str && strlen(str) < 100000)
+    repodata_set_str(data, p, buildservice_annotation, str);
+  av = hvlookupav(hv, "modules", 7);
+  if (av)
+    {
+      SSize_t i;
+      for (i = 0; i <= av_len(av); i++)
+       {
+         char *str = avlookupstr(av, i);
+         repodata_add_idarray(data, p, buildservice_modules, pool_str2id(pool, str, 1));
+       }
+    }
+  return p;
+}
+
+static void
+data2solvables(Repo *repo, Repodata *data, SV *rsv, int isdod)
+{
+  AV *rav = 0;
+  SSize_t ravi = 0;
+  HV *rhv = 0;
+  SV *sv;
+  char *key;
+  I32 keyl;
+
+  if (SvTYPE(rsv) == SVt_PVAV)
+    rav = (AV *)rsv;
+  else
+    rhv = (HV *)rsv;
+
+  if (rhv)
+    hv_iterinit(rhv);
+  for (;;)
+    {
+      if (rhv)
+       {
+         sv = hv_iternextsv(rhv, &key, &keyl);
+         if (!sv)
+           break;
+       }
+      else
+       {
+         SV **svp;
+         if (ravi > av_len(rav))
+           break;
+         svp = av_fetch(rav, ravi++, 0);
+         if (!svp || !*svp)
+           continue;
+         sv = *svp;
        }
-      if (s->evr)
-       s->provides = repo_addid_dep(repo, s->provides, pool_rel2id(pool, s->name, s->evr, REL_EQ, 1), 0);
-      str = hvlookupstr(hv, "checksum", 8);
+      if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVHV)
+       continue;
+      data2pkg(repo, data, (HV *)SvRV(sv), isdod);
+    }
+
+  /* set meta information */
+  repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
+  if (rhv)
+    {
+      char *str;
+      AV *av;
+      str = hvlookupstr(rhv, "/url", 4);
       if (str)
+       repodata_set_str(data, SOLVID_META, buildservice_dodurl, str);
+      str = hvlookupstr(rhv, "/dodcookie", 10);
+      if (str)
+       repodata_set_str(data, SOLVID_META, buildservice_dodcookie, str);
+      av = hvlookupav(rhv, "/dodresources", 13);
+      if (av)
        {
-         char *cp, typebuf[8];
-         Id ctype;
-         if (*str != ':' && (cp = strchr(str, ':')) != 0 && cp - str < sizeof(typebuf))
+         SSize_t i;
+         for (i = 0; i <= av_len(av); i++)
            {
-             strncpy(typebuf, str, cp - str);
-             typebuf[cp - str] = 0;
-             ctype = solv_chksum_str2type(typebuf);
-             if (ctype)
-               repodata_set_checksum(data, p, SOLVABLE_CHECKSUM, ctype, cp + 1);
+             Id id = pool_str2id(repo->pool, avlookupstr(av, i), 1);
+             repodata_add_idarray(data, SOLVID_META, buildservice_dodresources, id);
            }
        }
     }
-
-  repodata_set_str(data, SOLVID_META, buildservice_repocookie, REPOCOOKIE);
-  str = hvlookupstr(rhv, "/url", 4);
-  if (str)
-    repodata_set_str(data, SOLVID_META, buildservice_dodurl, str);
-  str = hvlookupstr(rhv, "/dodcookie", 10);
-  if (str)
-    repodata_set_str(data, SOLVID_META, buildservice_dodcookie, str);
 }
 
 static SV *
@@ -525,201 +663,890 @@ retrieve(unsigned char **srcp, STRLEN *srclp, int depth)
   return sv;
 }
 
-static void
-expander_dbg(Expander *xp, const char *format, ...)
+#define CPLXDEPS_TODNF (1 << 0)
+
+static int
+invert_depblocks(ExpanderCtx *xpctx, Queue *bq, int start, int r)
 {
-  va_list args;
-  char buf[1024];
-  int l;
-  if (!xp->debug)
-    return;
-  va_start(args, format);
-  vsnprintf(buf, sizeof(buf), format, args);
-  va_end(args);
-  printf("%s", buf);
-  l = strlen(buf);
-  if (buf[0] != ' ' || (l && buf[l - 1] == '\n'))
-    fflush(stdout);
-  if (l >= xp->debugstrf)      /* >= because of trailing \0 */
+  int i, j, end;
+  if (r == 0 || r == 1)
+    return r ? 0 : 1;
+  end = bq->count;
+  for (i = j = start; i < end; i++)
     {
-      xp->debugstr = solv_realloc(xp->debugstr, xp->debugstrl + l + 1024);
-      xp->debugstrf = l + 1024;
+      if (bq->elements[i])
+        {
+          bq->elements[i] = -bq->elements[i];
+          continue;
+        }
+      /* end of block reached, reverse */
+      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;
     }
-  strcpy(xp->debugstr + xp->debugstrl, buf);
-  xp->debugstrl += l;
-  xp->debugstrf -= l;
-}
-
-static const char *
-expander_solvid2name(Expander *xp, Id p)
-{
-  const char *n = pool_id2str(xp->pool, xp->pool->solvables[p].name);
-  Repo *r; 
-  if (!xp->debug)
-    return n;
-  r = xp->pool->solvables[p].repo;
-  if (!r) 
-    return n;
-  return pool_tmpjoin(xp->pool, n, "@", r->name);
+  return -1;
 }
 
-static inline void
-expander_installed(Expander *xp, Id p, Map *installed, Map *conflicts, Queue *conflictsinfo, int *cidone, Queue *out, Queue *todo)
+static int
+distribute_depblocks(ExpanderCtx *xpctx, Queue *bq, int start, int start2, int flags)
 {
-  Pool *pool = xp->pool;
-  Solvable *s = pool->solvables + p;
-  Id req, id, *reqp, con, *conp;
-  const char *n;
-
-  MAPSET(installed, p);
-  queue_push(out, p);
-  if (MAPTST(&xp->conflicts, s->name))
-    {
-      int i;
-      for (i = 0; i < xp->conflictsq.count; i++)
-       {
-         Id p2, pp2;
-         Id id = xp->conflictsq.elements[i];
-         if (id != s->name)
-           continue;
-         id = xp->conflictsq.elements[i ^ 1];
-         FOR_PROVIDES(p2, pp2, id)
-           {
-             if (pool->solvables[p2].name == id)
-               {
-                 MAPEXP(conflicts, pool->nsolvables);
-                 MAPSET(conflicts, p2);
-               }
-           }
-       }
-    }
-  if (s->requires)
+  int i, j, end2 = bq->count;
+  for (i = start; i < start2; i++)
     {
-      reqp = s->repo->idarraydata + s->requires;
-      while ((req = *reqp++) != 0)
+      for (j = start2; j < end2; j++)
        {
-         if (req == SOLVABLE_PREREQMARKER)
-           continue;
-         id = id2name(pool, req);
-         if (!xp->ignoreignore)
+         int a, b;
+         int bqcnt4 = bq->count;
+         int k = i;
+
+         /* distribute i block with j block, both blocks are sorted */
+         while (bq->elements[k] && bq->elements[j])
            {
-             if (MAPTST(&xp->ignored, id))
-               continue;
-             if (MAPTST(&xp->ignoredx, id))
+             if (bq->elements[k] < bq->elements[j])
+               queue_push(bq, bq->elements[k++]);
+             else
                {
-                 Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, s->name), ":", pool_id2str(pool, id)), 0);
-                 if (xid && MAPTST(&xp->ignored, xid))
-                   continue;
+                 if (bq->elements[k] == bq->elements[j])
+                   k++;
+                 queue_push(bq, bq->elements[j++]);
                }
            }
-         n = pool_id2str(pool, id);
-         if (!strncmp(n, "rpmlib(", 7))
-           {
-             MAPEXP(&xp->ignored, id);
-             MAPSET(&xp->ignored, id);
-             continue;
-           }
-         if (*n == '/')
+         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 (!xp->havefileprovides || pool->whatprovides[id] <= 1)
-               {
-                 MAPEXP(&xp->ignored, id);
-                 MAPSET(&xp->ignored, id);
-                 continue;
-               }
+             if (-bq->elements[a] == bq->elements[b])
+               break;
+             if (-bq->elements[a] > bq->elements[b])
+               a++;
+             else
+               b--;
            }
-         queue_push2(todo, req, p);
+         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++;
     }
-  if (!xp->ignoreconflicts)
+  queue_deleten(bq, start, end2 - start);
+  if (start == bq->count)
+    return flags & CPLXDEPS_TODNF ? 0 : 1;
+  return -1;
+}
+
+#if 0
+static void
+print_depblocks(ExpanderCtx *xpctx, Queue *bq, int start, int r)
+{
+  Pool *pool = xpctx->pool;
+  int i;
+
+  if (r == 0)
     {
-      if (s->conflicts)
-       {
-         conp = s->repo->idarraydata + s->conflicts;
-         while ((con = *conp++) != 0)
-           {
-             Id p2, pp2;
-             FOR_PROVIDES(p2, pp2, con)
-               {
-                 if (p2 == p)
-                   continue;
-                 MAPEXP(conflicts, pool->nsolvables);
-                 MAPSET(conflicts, p2);
-                 if (xp->debug)
-                   queue_push2(conflictsinfo, p2, p);
-               }
-           }
-       }
-      if (s->obsoletes)
-       {
+      printf("[NONE]\n");
+      return;
+    }
+  if (r == 1)
+    {
+      printf("[ALL]\n");
+      return;
+    }
+  for (i = start; i < bq->count; i++)
+    {
+      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");
+}
+#endif
+
+
+static int
+pool_is_complex_dep_rd(Pool *pool, Reldep *rd)
+{
+  for (;;)
+    {
+      if (rd->flags == REL_AND || rd->flags == REL_COND || rd->flags == REL_UNLESS)        /* those 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);
+    }
+}
+
+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;
+}
+
+static int normalize_dep(ExpanderCtx *xpctx, Id dep, Queue *bq, int flags);
+
+static int
+normalize_dep_or(ExpanderCtx *xpctx, Id dep1, Id dep2, Queue *bq, int flags, int invflags)
+{
+  int r1, r2, bqcnt2, bqcnt = bq->count;
+  r1 = normalize_dep(xpctx, dep1, bq, flags);
+  if (r1 == 1)
+    return 1;          /* early exit */
+  bqcnt2 = bq->count;
+  r2 = normalize_dep(xpctx, dep2, bq, flags ^ invflags);
+  if (invflags)
+    r2 = invert_depblocks(xpctx, bq, bqcnt2, r2);
+  if (r1 == 1 || r2 == 1)
+    {
+      queue_truncate(bq, bqcnt);
+      return 1;
+    }
+  if (r1 == 0)
+    return r2;
+  if (r2 == 0)
+    return r1;
+  if ((flags & CPLXDEPS_TODNF) == 0)
+    return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
+  return -1;
+}
+
+static int
+normalize_dep_and(ExpanderCtx *xpctx, Id dep1, Id dep2, Queue *bq, int flags, int invflags)
+{
+  int r1, r2, bqcnt2, bqcnt = bq->count;
+  r1 = normalize_dep(xpctx, dep1, bq, flags);
+  if (r1 == 0)
+    return 0;          /* early exit */
+  bqcnt2 = bq->count;
+  r2 = normalize_dep(xpctx, dep2, bq, flags ^ invflags);
+  if (invflags)
+    r2 = invert_depblocks(xpctx, bq, bqcnt2, r2);
+  if (r1 == 0 || r2 == 0)
+    {
+      queue_truncate(bq, bqcnt);
+      return 0;
+    }
+  if (r1 == 1)
+    return r2;
+  if (r2 == 1)
+    return r1;
+  if ((flags & CPLXDEPS_TODNF) != 0)
+    return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
+  return -1;
+}
+
+static int
+normalize_dep_if_else(ExpanderCtx *xpctx, Id dep1, Id dep2, Id dep3, Queue *bq, int flags)
+{
+  /* A IF (B ELSE C) -> (A OR ~B) AND (C OR B) */
+  int r1, r2, bqcnt2, bqcnt = bq->count;
+  r1 = normalize_dep_or(xpctx, dep1, dep2, bq, flags, CPLXDEPS_TODNF);
+  if (r1 == 0)
+    return 0;          /* early exit */
+  bqcnt2 = bq->count;
+  r2 = normalize_dep_or(xpctx, dep2, dep3, bq, flags, 0);
+  if (r1 == 0 || r2 == 0)
+    {
+      queue_truncate(bq, bqcnt);
+      return 0;
+    }
+  if (r1 == 1)
+    return r2;
+  if (r2 == 1)
+    return r1;
+  if ((flags & CPLXDEPS_TODNF) != 0)
+    return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
+  return -1;
+}
+
+static int
+normalize_dep_unless_else(ExpanderCtx *xpctx, Id dep1, Id dep2, Id dep3, Queue *bq, int flags)
+{
+  /* A UNLESS (B ELSE C) -> (A AND ~B) OR (C AND B) */
+  int r1, r2, bqcnt2, bqcnt = bq->count;
+  r1 = normalize_dep_and(xpctx, dep1, dep2, bq, flags, CPLXDEPS_TODNF);
+  if (r1 == 1)
+    return 1;          /* early exit */
+  bqcnt2 = bq->count;
+  r2 = normalize_dep_and(xpctx, dep2, dep3, bq, flags, 0);
+  if (r1 == 1 || r2 == 1)
+    {
+      queue_truncate(bq, bqcnt);
+      return 1;
+    }
+  if (r1 == 0)
+    return r2;
+  if (r2 == 0)
+    return r1;
+  if ((flags & CPLXDEPS_TODNF) == 0)
+    return distribute_depblocks(xpctx, bq, bqcnt, bqcnt2, flags);
+  return -1;
+}
+
+static int expander_isignored(Expander *xp, Solvable *s, Id req);
+
+static int
+normalize_dep(ExpanderCtx *xpctx, Id dep, Queue *bq, int flags)
+{
+  Pool *pool = xpctx->pool;
+  Id p, dp;
+  
+  if (pool_is_complex_dep(pool, dep))
+    {
+      Reldep *rd = GETRELDEP(pool, dep);
+      if (rd->flags == REL_COND)
+       {
+         Id evr = rd->evr;
+         if (ISRELDEP(evr))
+           {
+             Reldep *rd2 = GETRELDEP(pool, evr);
+             if (rd2->flags == REL_ELSE)
+               return normalize_dep_if_else(xpctx, rd->name, rd2->name, rd2->evr, bq, flags);
+           }
+         return normalize_dep_or(xpctx, rd->name, rd->evr, bq, flags, CPLXDEPS_TODNF);
+       }
+      if (rd->flags == REL_UNLESS)
+       {
+         Id evr = rd->evr;
+         if (ISRELDEP(evr))
+           {
+             Reldep *rd2 = GETRELDEP(pool, evr);
+             if (rd2->flags == REL_ELSE)
+               return normalize_dep_unless_else(xpctx, rd->name, rd2->name, rd2->evr, bq, flags);
+           }
+         return normalize_dep_and(xpctx, rd->name, rd->evr, bq, flags, CPLXDEPS_TODNF);
+       }
+      if (rd->flags == REL_OR)
+       return normalize_dep_or(xpctx, rd->name, rd->evr, bq, flags, 0);
+      if (rd->flags == REL_AND)
+       return normalize_dep_and(xpctx, rd->name, rd->evr, bq, flags, 0);
+    }
+
+  if (xpctx->ignore_s && (flags & CPLXDEPS_TODNF) == 0)
+    {
+      if (expander_isignored(xpctx->xp, xpctx->ignore_s, dep))
+       return 1;
+    }
+
+  dp = pool_whatprovides(pool, dep);
+  if (dp == 2)
+    return 1;
+  if (dp < 2 || !pool->whatprovidesdata[dp])
+    return 0;
+  if (pool->whatprovidesdata[dp] == SYSTEMSOLVABLE)
+    return 1;
+  if ((flags & CPLXDEPS_TODNF) != 0)
+    {
+      while ((p = pool->whatprovidesdata[dp++]) != 0)
+        queue_push2(bq, p, 0);
+    }
+  else
+    {
+      while ((p = pool->whatprovidesdata[dp++]) != 0)
+        queue_push(bq, p);
+      queue_push(bq, 0);
+    }
+  return -1;
+}
+
+#define ISCPLX(pool, d) (ISRELDEP(d) && GETRELID(d) >= pool->nrels)
+#define GETCPLX(pool, d) (GETRELID(d) - pool->nrels)
+#define MAKECPLX(pool, d) (MAKERELDEP(pool->nrels + d))
+
+#define DEPTYPE_REQUIRES               0
+#define DEPTYPE_CONFLICTS              1
+#define DEPTYPE_OBSOLETES              2
+#define DEPTYPE_RECOMMENDS             3
+#define DEPTYPE_PROVIDES               4
+
+#define ERROR_NOPROVIDER               1
+#define ERROR_CHOICE                   2
+#define ERROR_CONFLICTINGPROVIDERS     3
+#define ERROR_PROVIDERINFO             4
+#define ERROR_PROVIDERINFO2            5
+#define ERROR_BADDEPENDENCY            6
+#define ERROR_CONFLICT                 7
+#define ERROR_CONFLICT2                        8
+#define ERROR_ALLCONFLICT              9
+#define ERROR_NOPROVIDERINFO           10
+
+static void
+expander_dbg(Expander *xp, const char *format, ...)
+{
+  va_list args;
+  char buf[1024];
+  int l;
+
+  if (!xp->debug)
+    return;
+  va_start(args, format);
+  vsnprintf(buf, sizeof(buf), format, args);
+  va_end(args);
+  l = strlen(buf);
+  if ((xp->debug & (EXPANDER_DEBUG_ALL | EXPANDER_DEBUG_STDOUT)) != 0)
+    {
+      printf("%s", buf);
+      if (buf[0] != ' ' || (l && buf[l - 1] == '\n'))
+        fflush(stdout);
+    }
+  if ((xp->debug & (EXPANDER_DEBUG_ALL | EXPANDER_DEBUG_STR)) != 0)
+    {
+      if (l >= xp->debugstrf)  /* >= because of trailing \0 */
+       {
+         xp->debugstr = solv_realloc(xp->debugstr, xp->debugstrl + l + 1024);
+         xp->debugstrf = l + 1024;
+       }
+      strcpy(xp->debugstr + xp->debugstrl, buf);
+      xp->debugstrl += l;
+      xp->debugstrf -= l;
+    }
+}
+
+static void
+expander_clrdbg(Expander *xp)
+{
+  if (xp->debugstr)
+    free(xp->debugstr);
+  xp->debugstr = 0;
+  xp->debugstrl = xp->debugstrf = 0;
+}
+
+static const char *
+expander_solvid2name(Expander *xp, Id p)
+{
+  const char *n = pool_id2str(xp->pool, xp->pool->solvables[p].name);
+  Repo *r; 
+  if (!xp->debug)
+    return n;
+  r = xp->pool->solvables[p].repo;
+  if (!r) 
+    return n;
+  return pool_tmpjoin(xp->pool, n, "@", r->name);
+}
+
+static const char *
+expander_solvid2str(Expander *xp, Id p)
+{
+  const char *n = pool_solvid2str(xp->pool, p);
+  Repo *r; 
+  if (!xp->debug)
+    return n;
+  r = xp->pool->solvables[p].repo;
+  if (!r) 
+    return n;
+  return pool_tmpjoin(xp->pool, n, "@", r->name);
+}
+
+static int
+pkgname_sort_cmp(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = (Pool *)dp;
+  Id a = *(Id *)ap;
+  Id b = *(Id *)bp;
+  return strcmp(pool_id2str(pool, pool->solvables[a].name), pool_id2str(pool, pool->solvables[b].name));
+}
+
+static int
+expander_isignored(Expander *xp, Solvable *s, Id req)
+{
+  Pool *pool = xp->pool;
+  Id id = id2name(pool, req);
+  const char *n;
+
+  if (!xp->ignoreignore)
+    {
+      if (MAPTST(&xp->ignored, id))
+       return 1;
+      if (MAPTST(&xp->ignoredx, id))
+       {
+         Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, s->name), ":", pool_id2str(pool, id)), 0);
+         if (xid && MAPTST(&xp->ignored, xid))
+           return 1;
+       }
+    }
+  n = pool_id2str(pool, id);
+  if (!strncmp(n, "rpmlib(", 7))
+    {
+      MAPEXP(&xp->ignored, id);
+      MAPSET(&xp->ignored, id);
+      return 1;
+    }
+  if (*n == '/')
+    {
+      if (!xp->havefileprovides || pool->whatprovides[id] <= 1)
+       {
+         MAPEXP(&xp->ignored, id);
+         MAPSET(&xp->ignored, id);
+         return 1;
+       }
+    }
+  return 0;
+}
+
+static int
+expander_to_cplxblks(ExpanderCtx *xpctx, Id p, Id dep, int deptype, Id *ptr)
+{
+  int blkoff = xpctx->cplxblks.count;
+  queue_push(&xpctx->cplxblks, p);
+  queue_push(&xpctx->cplxblks, dep);
+  queue_push(&xpctx->cplxblks, deptype);
+  for (;;)
+    {
+      Id pp = *ptr++;
+      queue_push(&xpctx->cplxblks, pp);
+      if (!pp)
+       break;
+    }
+  return blkoff;
+}
+
+static int
+expander_check_cplxblock(ExpanderCtx *xpctx, Id p, Id dep, int deptype, Id *ptr, int blkoff)
+{
+  Pool *pool = xpctx->pool;
+  int posn = 0, posi = 0, negn = 0, negi = 0;
+  Id pp, *ptr2 = ptr;
+  Id lastcon = 0;
+
+  while ((pp = *ptr2++) != 0)
+    {
+      if (pp > 0)
+       {
+         posn++;
+         if (MAPTST(&xpctx->installed, pp))
+           posi++;
+       }
+      else
+       {
+         if (p == -pp)
+           continue;   /* ignore redundant self-entry */
+         negn++;
+         if (MAPTST(&xpctx->installed, -pp))
+           negi++;
+         else
+           lastcon = -pp;
+       }
+    }
+#if 0
+  printf("expander_check_cplxblock pos: %d,%d neg: %d,%d\n", posn, posi, negn, negi);
+#endif
+  if (posi)
+    return -1;
+  if (!posn && deptype == DEPTYPE_RECOMMENDS)
+    return -1;
+  if (negi == negn)
+    {
+      /* all neg installed */
+      if (posn)
+       {
+         /* posn > 0 and all neg installed, add to todo */
+         if (blkoff < 0)
+           blkoff = expander_to_cplxblks(xpctx, p, dep, deptype, ptr);
+#if 0
+         printf("put on todo, blkoff = %d\n", blkoff);
+#endif
+         queue_push2(&xpctx->todo, MAKECPLX(pool, blkoff), p);
+       }
+      else
+       {
+         /* no posn, conflict */
+         for (ptr2 = ptr; (pp = *ptr2++) != 0; )
+           {
+             if (p == -pp)
+               continue;       /* ignore redundant self-entry */
+             if (deptype == DEPTYPE_REQUIRES)
+               {
+                 /* do not report a requires as conflicts */
+                 queue_push(&xpctx->errors, ERROR_NOPROVIDER);
+                 queue_push2(&xpctx->errors, dep, p);
+                 break;
+               }
+             queue_push(&xpctx->errors, ERROR_CONFLICT);
+             queue_push2(&xpctx->errors, p, -pp);
+           }
+       }
+      return -1;
+    }
+  else if (!posn && negn && negi == negn - 1)
+    {
+      /* add conflict */
+#if 0
+      printf("add conflict %d %d\n", lastcon, p);
+#endif
+      MAPEXP(&xpctx->conflicts, pool->nsolvables);
+      MAPSET(&xpctx->conflicts, lastcon);
+      if (p)
+        queue_push2(&xpctx->conflictsinfo, lastcon, p);        /* always do this for rich deps */
+      return -1;
+    }
+  else
+    {
+#ifdef DEBUG_COND
+      printf("put/stay on cond queue, blkoff = %d\n", blkoff);
+#endif
+      /* either posn > 0 and 1 neg not installed or more than 1 neg not installed */
+      if (blkoff < 0)
+       blkoff = expander_to_cplxblks(xpctx, p, dep, deptype, ptr);
+      return blkoff;
+    }
+}
+
+static void
+expander_installed_complexdep(ExpanderCtx *xpctx, Id p, Id dep, int deptype)
+{
+  Queue *cplxq = &xpctx->cplxq;
+  int r, i, start = cplxq->count, blkoff;
+
+#if 0
+  printf("expander_installed_complexdep %s type %d\n", pool_dep2str(xpctx->pool, dep), deptype);
+#endif
+  if (deptype == DEPTYPE_CONFLICTS)
+    {
+      r = normalize_dep(xpctx, dep, cplxq, CPLXDEPS_TODNF);
+      r = invert_depblocks(xpctx, cplxq, start, r);
+    }
+  else
+    r = normalize_dep(xpctx, dep, cplxq, 0);
+#if 0
+  print_depblocks(xpctx, cplxq, start, r);
+#endif
+  if (r == 1)
+    return;
+  if (r == 0)
+    {
+      if (deptype == DEPTYPE_CONFLICTS)
+       {
+         queue_push(&xpctx->errors, ERROR_ALLCONFLICT);
+         queue_push2(&xpctx->errors, dep, p);
+       }
+      else if (deptype != DEPTYPE_RECOMMENDS)
+       {
+         queue_push(&xpctx->errors, ERROR_NOPROVIDER);
+         queue_push2(&xpctx->errors, dep, p);
+       }
+      return;
+    }
+  while (start < cplxq->count)
+    {
+      /* find end */
+      for (i = start; cplxq->elements[i] != 0; i++)
+       ;
+      blkoff = expander_check_cplxblock(xpctx, p, dep, deptype, cplxq->elements + start, -1);
+      if (blkoff >= 0)
+       {
+         Pool *pool = xpctx->pool;
+         Id p;
+         MAPEXP(&xpctx->todo_condmap, pool->nsolvables);
+         for (i = start; (p = cplxq->elements[i]) != 0; i++)
+           if (p < 0)
+             MAPSET(&xpctx->todo_condmap, -p);
+         queue_push(&xpctx->todo_cond, blkoff);
+       }
+      start = i + 1;
+    }
+  queue_truncate(cplxq, start);
+}
+
+static int
+expander_checkconflicts_complexdep(ExpanderCtx *xpctx, Id p, Id dep, int deptype, int recorderrors)
+{
+  Queue *cplxq = &xpctx->cplxq;
+  int r, i, start = cplxq->count;
+  Id pp;
+  int ret = 0;
+
+#if 0
+  printf("expander_checkconflicts_complexdep %s type %d\n", pool_dep2str(xpctx->pool, dep), deptype);
+#endif
+  if (deptype == DEPTYPE_CONFLICTS)
+    {
+      r = normalize_dep(xpctx, dep, cplxq, CPLXDEPS_TODNF);
+      r = invert_depblocks(xpctx, cplxq, start, r);
+    }
+  else
+    r = normalize_dep(xpctx, dep, cplxq, 0);
+#if 0
+  print_depblocks(xpctx, cplxq, start, r);
+#endif
+  /* r == 0: conflict with everything. Ignore here, pick error up when package gets installed */
+  if (r == 0 || r == 1)
+    return 0;
+  while (start < cplxq->count)
+    {
+      for (i = start; (pp = cplxq->elements[i]) != 0; i++)
+        if (pp > 0 || (pp < 0 && !MAPTST(&xpctx->installed, -pp)))
+         break;
+      if (pp == 0)
+       {
+         /* no pos and all neg installed -> conflict */
+         for (i = start; (pp = cplxq->elements[i]) != 0; i++)
+           {
+             pp = -cplxq->elements[i];
+             if (recorderrors)
+               {
+                 queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT : ERROR_PROVIDERINFO);
+                 queue_push2(&xpctx->errors, p, pp);
+               }
+             else if (xpctx->xp->debug)
+               {
+                 Pool *pool = xpctx->pool;
+                 expander_dbg(xpctx->xp, "ignoring provider %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_solvid2str(pool, pp));
+               }
+             ret = ret ? 1 : pp;
+           }
+       }
+      for (; cplxq->elements[i] != 0; i++)
+       ;
+      start = i + 1;
+    }
+  queue_truncate(cplxq, start);
+  return ret;
+}
+
+static void
+updateconflictsinfo(ExpanderCtx *xpctx)
+{
+  int i;
+  Pool *pool = xpctx->pool;
+  Queue *out = xpctx->out;
+  Queue *conflictsinfo = &xpctx->conflictsinfo;
+
+  if (xpctx->ignoreconflicts)
+    return;
+  for (i = xpctx->cidone; i < out->count; i++)
+    {
+      Id p, p2, pp2;
+      Id con, *conp;
+      Solvable *s;
+      p = out->elements[i];
+      s = pool->solvables + p;
+      /* keep in sync with expander_installed! */
+      if (s->conflicts)
+       {
+         conp = s->repo->idarraydata + s->conflicts;
+         while ((con = *conp++) != 0)
+           {
+             if (pool_is_complex_dep(pool, con))
+               continue;       /* already pushed */
+             FOR_PROVIDES(p2, pp2, con)
+               {
+                 if (p2 == p)
+                   continue;
+                 queue_push2(conflictsinfo, p2, p);
+               }
+           }
+       }
+      if (s->obsoletes)
+       {
          conp = s->repo->idarraydata + s->obsoletes;
          while ((con = *conp++) != 0)
            {
-             Id p2, pp2;
              FOR_PROVIDES(p2, pp2, con)
                {
                  if (p2 == p || !pool_match_nevr(pool, pool->solvables + p2, con))
                    continue;
-                 MAPEXP(conflicts, pool->nsolvables);
-                 MAPSET(conflicts, p2);
-                 if (xp->debug)
-                   queue_push2(conflictsinfo, p2, -p);
+                 queue_push2(conflictsinfo, p2, -p);
                }
            }
        }
-      if (xp->debug)
-       *cidone = out->count;
     }
+  xpctx->cidone = out->count;
 }
 
-static inline int
-expander_checkconflicts(Expander *xp, Id p, Map *installed, Id *conflicts, int isobsoletes)
+static int
+findconflictsinfo(ExpanderCtx *xpctx, Id p, int recorderrors)
 {
-  Pool *pool = xp->pool;
-  Id con, p2, pp2;
+  Queue *conflictsinfo = &xpctx->conflictsinfo;
+  int i, ret = 0;
 
-  if (xp->ignoreconflicts)
-    return 0;
-  while ((con = *conflicts++) != 0)
+  if (xpctx->cidone < xpctx->out->count)
+    updateconflictsinfo(xpctx);
+
+  for (i = 0; i < conflictsinfo->count; i++)
+    if (conflictsinfo->elements[i] == p)
+      {
+       ret = conflictsinfo->elements[i + 1];
+       if (recorderrors)
+         {
+           queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT2 : ERROR_PROVIDERINFO2);
+           queue_push2(&xpctx->errors, p, ret);
+         }
+       else if (xpctx->xp->debug)
+         {
+           Pool *pool = xpctx->pool;
+           expander_dbg(xpctx->xp, "ignoring provider %s because installed %s %s it\n", pool_solvid2str(pool, p), pool_solvid2str(pool, ret > 0 ? ret : -ret), ret > 0 ? "conflicts with" : "obsoletes");
+         }
+      }
+  if (!ret)
     {
-      FOR_PROVIDES(p2, pp2, con)
+      /* conflict from our job, i.e. a !xxx dep */
+      if (recorderrors)
        {
-         if (p == p2)
-           continue;
-         if (isobsoletes && !pool_match_nevr(pool, pool->solvables + p2, con))
-           continue;
-         if (MAPTST(installed, p2))
-           return p2;
+         queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT2 : ERROR_PROVIDERINFO2);
+         queue_push2(&xpctx->errors, p, 0);
+       }
+      else if (xpctx->xp->debug)
+       {
+         Pool *pool = xpctx->pool;
+         expander_dbg(xpctx->xp, "ignoring conflicted provider %s\n", pool_solvid2str(pool, p));
        }
     }
-  return 0;
+  return ret;
 }
 
+
 static void
-expander_updateconflictsinfo(Expander *xp, Queue *conflictsinfo, int *cidone, Queue *out)
+recheck_conddeps(ExpanderCtx *xpctx)
 {
-  Pool *pool = xp->pool;
   int i;
-  if (xp->ignoreconflicts)
-    return;
-  for (i = *cidone; i < out->count; i++)
+  for (i = 0; i < xpctx->todo_cond.count; i++)
+    {
+      int blkoff = xpctx->todo_cond.elements[i];
+#ifdef DEBUG_COND
+      printf("todo_cond %d\n", blkoff);
+#endif
+      Id *ptr = xpctx->cplxblks.elements + blkoff;
+      if (expander_check_cplxblock(xpctx, ptr[0], ptr[1], ptr[2], ptr + 3, blkoff) < 0)
+       {
+#ifdef DEBUG_COND
+         printf("remove no longer needed cond entry\n");
+#endif
+         queue_delete(&xpctx->todo_cond, i);
+         i--;
+       }
+    }
+}
+
+/* install a single package */
+static void
+expander_installed(ExpanderCtx *xpctx, Id p)
+{
+  Pool *pool = xpctx->pool;
+  Expander *xp = xpctx->xp;
+  Solvable *s = pool->solvables + p;
+  Id req, *reqp, con, *conp;
+
+#if 0
+printf("expander_installed %s\n", pool_solvid2str(pool, p));
+#endif
+  MAPSET(&xpctx->installed, p);
+  queue_push(xpctx->out, p);
+
+  if (xpctx->conflicts.size && MAPTST(&xpctx->conflicts, p))
+    findconflictsinfo(xpctx, p, 2);
+
+  /* add synthetic conflicts from the project config */
+  if (MAPTST(&xp->conflicts, s->name))
+    {
+      int i;
+      for (i = 0; i < xp->conflictsq.count; i++)
+       {
+         Id p2, pp2;
+         Id id = xp->conflictsq.elements[i];
+         if (id != s->name)
+           continue;
+         id = xp->conflictsq.elements[i ^ 1];
+         FOR_PROVIDES(p2, pp2, id)
+           {
+             if (pool->solvables[p2].name != id)
+               continue;
+             if (MAPTST(&xpctx->installed, p2))
+               {
+                 queue_push(&xpctx->errors, ERROR_CONFLICT);
+                 queue_push2(&xpctx->errors, p, p2);
+                 continue;
+               }
+             MAPEXP(&xpctx->conflicts, pool->nsolvables);
+             MAPSET(&xpctx->conflicts, p2);
+             queue_push2(&xpctx->conflictsinfo, p2, p);
+           }
+       }
+    }
+
+  if (s->requires)
+    {
+      reqp = s->repo->idarraydata + s->requires;
+      while ((req = *reqp++) != 0)
+       {
+         if (req == SOLVABLE_PREREQMARKER)
+           continue;
+         if (ISRELDEP(req) && GETRELDEP(pool, req)->flags == REL_ERROR)
+           {
+             queue_push(&xpctx->errors, ERROR_BADDEPENDENCY);
+             queue_push2(&xpctx->errors, GETRELDEP(pool, req)->evr, p);
+             continue;
+           }
+         if (pool_is_complex_dep(pool, req))
+           {
+             xpctx->ignore_s = s;
+             expander_installed_complexdep(xpctx, p, req, DEPTYPE_REQUIRES);
+             xpctx->ignore_s = 0;
+             continue;
+           }
+         if (expander_isignored(xp, s, req))
+           continue;
+         queue_push2(&xpctx->todo, req, p);
+       }
+    }
+  if (!xpctx->ignoreconflicts)
     {
-      Id p, p2, pp2;
-      Id con, *conp;
-      Solvable *s;
-      p = out->elements[i];
-      s = pool->solvables + p;
-      /* keep in sync with expander_installed! */
       if (s->conflicts)
        {
          conp = s->repo->idarraydata + s->conflicts;
          while ((con = *conp++) != 0)
            {
+             Id p2, pp2;
+             if (ISRELDEP(con) && GETRELDEP(pool, con)->flags == REL_ERROR)
+               {
+                 queue_push(&xpctx->errors, ERROR_BADDEPENDENCY);
+                 queue_push2(&xpctx->errors, GETRELDEP(pool, con)->evr, p);
+                 continue;
+               }
+             if (pool_is_complex_dep(pool, con))
+               {
+                 expander_installed_complexdep(xpctx, p, con, DEPTYPE_CONFLICTS);
+                 continue;
+               }
              FOR_PROVIDES(p2, pp2, con)
                {
                  if (p2 == p)
                    continue;
-                 queue_push2(conflictsinfo, p2, p);
+                 if (MAPTST(&xpctx->installed, p2))
+                   {
+                     queue_push(&xpctx->errors, ERROR_CONFLICT);
+                     queue_push2(&xpctx->errors, p, p2);
+                     continue;
+                   }
+                 MAPEXP(&xpctx->conflicts, pool->nsolvables);
+                 MAPSET(&xpctx->conflicts, p2);
+                 if (xp->debug)
+                   queue_push2(&xpctx->conflictsinfo, p2, p);
                }
            }
        }
@@ -728,115 +1555,583 @@ expander_updateconflictsinfo(Expander *xp, Queue *conflictsinfo, int *cidone, Qu
          conp = s->repo->idarraydata + s->obsoletes;
          while ((con = *conp++) != 0)
            {
+             Id p2, pp2;
              FOR_PROVIDES(p2, pp2, con)
                {
                  if (p2 == p || !pool_match_nevr(pool, pool->solvables + p2, con))
                    continue;
-                 queue_push2(conflictsinfo, p2, -p);
+                 if (MAPTST(&xpctx->installed, p2))
+                   {
+                     queue_push(&xpctx->errors, ERROR_CONFLICT);
+                     queue_push2(&xpctx->errors, p, -p2);
+                     continue;
+                   }
+                 MAPEXP(&xpctx->conflicts, pool->nsolvables);
+                 MAPSET(&xpctx->conflicts, p2);
+                 if (xp->debug)
+                   queue_push2(&xpctx->conflictsinfo, p2, -p);
                }
            }
        }
+      if (xp->debug)
+       xpctx->cidone = xpctx->out->count;
     }
-  *cidone = out->count;
+  if (xpctx->todo_condmap.size && MAPTST(&xpctx->todo_condmap, p))
+    recheck_conddeps(xpctx);
 }
 
+/* same as expander_installed, but install multiple packages
+ * in one block */
 static void
-expander_updaterecommendedmap(Expander *xp, Map *recommended, int *recdone, Queue *out)
+expander_installed_multiple(ExpanderCtx *xpctx, Queue *toinstall)
 {
-  Pool *pool = xp->pool;
+  int i, j, havecond = 0;
+
+  /* unify */
+  for (i = j = 0; i < toinstall->count; i++)
+    {
+      Id p = toinstall->elements[i];
+      if (MAPTST(&xpctx->installed, p))
+       continue;       /* already seen */
+      MAPSET(&xpctx->installed, p);
+      toinstall->elements[j++] = p;
+      if (xpctx->todo_condmap.size && MAPTST(&xpctx->todo_condmap, p))
+       {
+         havecond = 1;
+         MAPCLR(&xpctx->todo_condmap, p);      /* no longer needed */
+       }
+    }
+  queue_truncate(toinstall, j);
+  
+  /* run conditionals first */
+  if (havecond)
+    recheck_conddeps(xpctx);
+
+  if (!xpctx->errors.count)
+    for (i = 0; i < toinstall->count; i++)
+      expander_installed(xpctx, toinstall->elements[i]);
+  queue_empty(toinstall);
+}
+
+static int
+expander_checkconflicts(ExpanderCtx *xpctx, Id p, Id *conflicts, int isobsoletes, int recorderrors)
+{
+  Map *installed = &xpctx->installed;
+  Pool *pool = xpctx->pool;
+  Id con, p2, pp2;
+  int ret = 0;
+
+  if (xpctx->ignoreconflicts)
+    return 0;
+  while ((con = *conflicts++) != 0)
+    {
+      if (!isobsoletes && pool_is_complex_dep(pool, con))
+       {
+         p2 = expander_checkconflicts_complexdep(xpctx, p, con, DEPTYPE_CONFLICTS, recorderrors);
+         ret = ret ? 1 : p2;
+         continue;
+       }
+      FOR_PROVIDES(p2, pp2, con)
+       {
+         if (p == p2)
+           continue;
+         if (isobsoletes && !pool_match_nevr(pool, pool->solvables + p2, con))
+           continue;
+         if (MAPTST(installed, p2))
+           {
+             if (recorderrors)
+               {
+                 queue_push(&xpctx->errors, recorderrors == 2 ? ERROR_CONFLICT : ERROR_PROVIDERINFO);
+                 queue_push2(&xpctx->errors, p, isobsoletes ? -p2 : p2);
+               }
+             else if (xpctx->xp->debug)
+               {
+                 if (isobsoletes)
+                   expander_dbg(xpctx->xp, "ignoring provider %s because it obsoletes installed %s\n", pool_solvid2str(pool, p), pool_solvid2str(pool, p2));
+                 else
+                   expander_dbg(xpctx->xp, "ignoring provider %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_solvid2str(pool, p2));
+               }
+             ret = ret ? 1 : p2;
+           }
+       }
+    }
+  return ret;
+}
+
+static void
+expander_updaterecommendedmap(ExpanderCtx *xpctx)
+{
+  Pool *pool = xpctx->pool;
+  Queue *out = xpctx->out;
+  Map *recommended = &xpctx->recommended;
 
   int i;
   Id p, pp, rec, *recp;
-  for (i = *recdone; i < out->count; i++)
+  for (i = xpctx->recdone; i < out->count; i++)
     {
       Solvable *s;
       s = pool->solvables + out->elements[i];
       if (s->recommends)
-       {
+       {
           MAPEXP(recommended, pool->nsolvables);
           for (recp = s->repo->idarraydata + s->recommends; (rec = *recp++) != 0; )
-           FOR_PROVIDES(p, pp, rec)
-             MAPSET(recommended, p);
-       }
+           FOR_PROVIDES(p, pp, rec)
+             MAPSET(recommended, p);
+       }
     }
-  *recdone = out->count;
+  xpctx->recdone = out->count;
 }
 
-static inline int
-findconflictsinfo(Queue *conflictsinfo, Id p)
+static int
+expander_dep_fulfilled(ExpanderCtx *xpctx, Id dep)
 {
-  int i;
+  Pool *pool = xpctx->pool;
+  Id p, pp; 
 
-  for (i = 0; i < conflictsinfo->count; i++)
-    if (conflictsinfo->elements[i] == p)
-      return conflictsinfo->elements[i + 1];
+  if (ISRELDEP(dep))
+    {   
+      Reldep *rd = GETRELDEP(pool, dep);
+      if (rd->flags == REL_COND)
+       {
+         if (ISRELDEP(rd->evr))
+           {
+             Reldep *rd2 = GETRELDEP(pool, rd->evr);
+             if (rd2->flags == REL_ELSE)
+               {
+                 if (expander_dep_fulfilled(xpctx, rd2->name))
+                   return expander_dep_fulfilled(xpctx, rd->name);
+                 return expander_dep_fulfilled(xpctx, rd2->evr);
+               }
+           }
+         if (expander_dep_fulfilled(xpctx, rd->name))  /* A OR ~B */
+           return 1;
+         return !expander_dep_fulfilled(xpctx, rd->evr);
+       }
+      if (rd->flags == REL_UNLESS)
+       {
+         if (ISRELDEP(rd->evr))
+           {
+             Reldep *rd2 = GETRELDEP(pool, rd->evr);
+             if (rd2->flags == REL_ELSE)
+               {
+                 if (!expander_dep_fulfilled(xpctx, rd2->name))
+                   return expander_dep_fulfilled(xpctx, rd->name);
+                 return expander_dep_fulfilled(xpctx, rd2->evr);
+               }
+           }
+         if (!expander_dep_fulfilled(xpctx, rd->name)) /* A AND ~B */
+           return 0;
+         return !expander_dep_fulfilled(xpctx, rd->evr);
+       }
+      if (rd->flags == REL_AND)
+       {
+         if (!expander_dep_fulfilled(xpctx, rd->name))
+           return 0;
+         return expander_dep_fulfilled(xpctx, rd->evr);
+       }
+      if (rd->flags == REL_OR)
+       {
+         if (expander_dep_fulfilled(xpctx, rd->name))
+           return 1;
+         return expander_dep_fulfilled(xpctx, rd->evr);
+       }
+    }
+  FOR_PROVIDES(p, pp, dep)
+    {
+      if (MAPTST(&xpctx->installed, p))
+       return 1;
+    }
   return 0;
 }
 
-#define ERROR_NOPROVIDER               1
-#define ERROR_CHOICE                   2
-#define ERROR_CONFLICTINGPROVIDER      3
-#define ERROR_CONFLICTINGPROVIDERS     4
-#define ERROR_PROVIDERINFO             5
-#define ERROR_PROVIDERINFO2            6
+static int
+prune_neg_prefers(ExpanderCtx *xpctx, Id who, Id *e, int n)
+{
+  Expander *xp = xpctx->xp;
+  Pool *pool = xpctx->pool;
+  Id whon = who ? pool->solvables[who].name : 0;
+  int i, j;
+  for (i = j = 0; i < n; i++)
+    {
+      Id p = e[i];
+      Id pn = pool->solvables[p].name;
+      if (MAPTST(&xp->preferneg, pn))
+       continue;
+      if (who && MAPTST(&xp->prefernegx, pn))
+       {
+         Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
+         if (xid && MAPTST(&xp->preferneg, xid))
+           continue;
+       }
+      e[j++] = p;
+    }
+  return j ? j : n;
+}
+
+static int
+prune_pos_prefers(ExpanderCtx *xpctx, Id who, Id *e, int n, int domulti)
+{
+  Expander *xp = xpctx->xp;
+  Queue *pruneq = &xpctx->pruneq;
+  Pool *pool = xpctx->pool;
+  Id whon = who ? pool->solvables[who].name : 0;
+  int i, j;
+
+  if (pruneq->count)
+    queue_empty(pruneq);
+  for (i = j = 0; i < n; i++)
+    {
+      Id p = e[i];
+      Id pn = pool->solvables[p].name;
+      if (MAPTST(&xp->preferpos, pn))
+       queue_push2(pruneq, pn, p);
+      else if (who && MAPTST(&xp->preferposx, pn))
+       {
+         Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
+         if (xid && MAPTST(&xp->preferpos, xid))
+           queue_push2(pruneq, xid, p);
+       }
+    }
+  if (!pruneq->count)
+    return n;
+  if (pruneq->count > 2)
+    {
+      if (!domulti)
+       return n;
+      /* pos prefers are ordered, the first one wins */
+      for (i = 0; i < xp->preferposq.count; i++)
+       {
+         Id xid = xp->preferposq.elements[i];
+         for (j = 0; j < pruneq->count; j += 2)
+           if (pruneq->elements[j] == xid)
+             {
+               e[0] = pruneq->elements[j + 1];
+               return 1;
+             }
+       }
+    }
+  e[0] = pruneq->elements[1];  /* simple case, just one prefer */
+  return 1;
+}
+
+static int
+prune_or_dep(ExpanderCtx *xpctx, Id dep, Id *e, int n)
+{
+  Pool *pool = xpctx->pool;
+  int i, j;
+  Id p, pp;
+
+  for (;;)
+    {
+      Reldep *rd = 0;
+      if (ISRELDEP(dep))
+       {
+         rd = GETRELDEP(pool, dep);
+         if (rd->flags != REL_OR)
+           rd = 0;
+       }
+      if (rd)
+       dep = rd->name;
+      i = j = 0;
+      /* both sets are ordered */
+      FOR_PROVIDES(p, pp, dep)
+       {
+         if (p < e[i])
+           continue;
+         while (i < n && p > e[i])
+           i++;
+         if (i == n)
+           break;
+         if (p == e[i])
+           e[j++] = p;
+       }
+      if (j)
+       return j;
+      if (rd)
+       dep = rd->evr;
+      else
+       break;
+    }
+  return n;
+}
+
+static int
+prune_supplemented(ExpanderCtx *xpctx, Id *e, int n)
+{
+  Pool *pool = xpctx->pool;
+  int i, j;
+  Id sup, *supp;
+
+  for (i = j = 0; i < n; i++)
+    {
+      Id p = e[i];
+      Solvable *s = pool->solvables + p;
+      if (!s->supplements)
+       continue;
+      supp = s->repo->idarraydata + s->supplements;
+      while ((sup = *supp++) != 0)
+       if (expander_dep_fulfilled(xpctx, sup))
+         break;
+      if (sup)
+        e[j++] = p;
+    }
+  return j ? j : n;
+}
+
+static void
+add_recommended_packages(ExpanderCtx *xpctx, Solvable *s)
+{
+  Pool *pool = xpctx->pool;
+  Id p, pp, rec, *recp;
+  for (recp = s->repo->idarraydata + s->recommends; (rec = *recp++) != 0; )
+    {
+      int haveone = 0;
+      if (pool_is_complex_dep(pool, rec))
+       {
+         expander_installed_complexdep(xpctx, s - pool->solvables, rec, DEPTYPE_RECOMMENDS);
+         continue;
+       }
+      FOR_PROVIDES(p, pp, rec)
+       {
+         if (MAPTST(&xpctx->installed, p))
+           break;
+         if (haveone)
+           continue;
+         if (xpctx->conflicts.size && MAPTST(&xpctx->conflicts, p))
+           continue;
+         if (pool->solvables[p].conflicts && expander_checkconflicts(xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0, 0) != 0)
+           continue;
+         if (pool->solvables[p].obsoletes && expander_checkconflicts(xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1, 0) != 0)
+           continue;
+         haveone = 1;
+       }
+      if (p)
+       continue;       /* already fulfilled */
+      if (haveone)
+       queue_push2(&xpctx->todo, rec, s - pool->solvables);
+    }
+}
+
+static void
+expander_growmaps(Expander *xp)
+{
+  Pool *pool = xp->pool;
+  MAPEXP(&xp->ignored, pool->ss.nstrings);
+  MAPEXP(&xp->ignoredx, pool->ss.nstrings);
+  MAPEXP(&xp->preferpos, pool->ss.nstrings);
+  MAPEXP(&xp->preferposx, pool->ss.nstrings);
+  MAPEXP(&xp->preferneg, pool->ss.nstrings);
+  MAPEXP(&xp->prefernegx, pool->ss.nstrings);
+  MAPEXP(&xp->conflicts, pool->ss.nstrings);
+}
+
+static Id
+str2id_dup(Pool *pool, const char *str)
+{
+  char buf[256];
+  size_t l = strlen(str);
+  if (l < 256) {
+    memcpy(buf, str, l + 1);
+    return pool_str2id(pool, buf, 1);
+  } else {
+    return pool_str2id(pool, pool_tmpjoin(pool, str, 0, 0), 1);
+  }
+}
+
+static void
+add_noproviderinfo(ExpanderCtx *xpctx, Id dep, Id who)
+{
+  Pool *pool = xpctx->pool;
+  Reldep *rd, *prd;
+  Id p, pp, prov, *provp;
+  int nprovinfo;
 
+  if (xpctx->xp->debug)
+    {
+      if (who)
+        expander_dbg(xpctx->xp, "nothing provides %s needed by %s\n", pool_dep2str(pool, dep), expander_solvid2str(xpctx->xp, who));
+      else
+        expander_dbg(xpctx->xp, "nothing provides %s\n", pool_dep2str(pool, dep));
+    }
+  if (!ISRELDEP(dep))
+    return;
+  rd = GETRELDEP(pool, dep);
+  if (rd->flags >= 8 || ISRELDEP(rd->name) || ISRELDEP(rd->evr))
+    return;
+  nprovinfo = 0;
+  FOR_PROVIDES(p, pp, rd->name)
+    {
+      Solvable *s = pool->solvables + p;
+      if (!s->repo || !s->provides)
+       continue;
+      for (provp = s->repo->idarraydata + s->provides; (prov = *provp++) != 0; )
+       {
+         if (!ISRELDEP(prov))
+           continue;
+         prd = GETRELDEP(pool, prov);
+         if (prd->name != rd->name || ISRELDEP(prd->evr))
+           continue;
+         queue_push(&xpctx->errors, ERROR_NOPROVIDERINFO);
+         if (prd->name == s->name && prd->evr == s->evr)
+           {
+             if (xpctx->xp->debug)
+               expander_dbg(xpctx->xp, "%s has version %s\n", expander_solvid2str(xpctx->xp, p), pool_id2str(pool, prd->evr));
+             queue_push2(&xpctx->errors, prd->evr, 0);
+           }
+         else
+           {
+             if (xpctx->xp->debug)
+               expander_dbg(xpctx->xp, "%s provides version %s\n", expander_solvid2str(xpctx->xp, p), pool_id2str(pool, prd->evr));
+             queue_push2(&xpctx->errors, prd->evr, p);
+           }
+         if (++nprovinfo >= 4)
+           return;             /* only show the first 4 providers */
+       }
+    }
+}
 
-int
-expander_expand(Expander *xp, Queue *in, Queue *out, Queue *inconfl)
+static int
+expander_expand(Expander *xp, Queue *in, Queue *indep, Queue *out, Queue *ignoreq, int options)
 {
+  ExpanderCtx xpctx;
   Pool *pool = xp->pool;
-  Queue todo, errors, cerrors, qq, posfoundq;
-  Map installed;
-  Map conflicts;
-  Map recommended;
-  Queue conflictsinfo;
-  int cidone;
-  int recdone;
+  Queue toinstall;
+  Queue qq, choices;
   Solvable *s;
   Id q, p, pp;
-  int i, j, nerrors, doamb, ambcnt;
-  Id id, who, whon, pn;
-  Id conflprov, conflprovpc;
-  int haverecommended = 0;
-  int haverecommended_done = 0;
-
-  map_init(&installed, pool->nsolvables);
-  map_init(&conflicts, 0);
-  map_init(&recommended, 0);
-  queue_init(&conflictsinfo);
-  queue_init(&todo);
+  int i, j, nerrors;
+  int ti, tj, tc;
+  Id todoid, id, who, whon;
+  Id conflprovpc;
+  int pass;
+  Queue revertignore;
+  int oldignoreignore = xp->ignoreignore;
+  Map oldignored, oldignoredx;
+  int ignoremapssaved = 0;
+  int dorecstart = 0;
+
+  memset(&xpctx, 0, sizeof(xpctx));
+  xpctx.xp = xp;
+  xpctx.pool = pool;
+  xpctx.out = out;
+  xpctx.ignoreignore = options & EXPANDER_OPTION_IGNOREIGNORE ? 1 : xp->ignoreignore;
+  xpctx.ignoreconflicts = options & EXPANDER_OPTION_IGNORECONFLICTS ? 1 : xp->ignoreconflicts;
+  xpctx.userecommendsforchoices = options & EXPANDER_OPTION_USERECOMMENDSFORCHOICES ? 1 : xp->userecommendsforchoices;
+  xpctx.usesupplementsforchoices = options & EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES ? 1 : xp->usesupplementsforchoices;
+  xpctx.dorecommends = options & EXPANDER_OPTION_DORECOMMENDS ? 1 : xp->dorecommends;
+  xpctx.dosupplements = options & EXPANDER_OPTION_DOSUPPLEMENTS ? 1 : xp->dosupplements;
+  map_init(&xpctx.installed, pool->nsolvables);
+  map_init(&xpctx.conflicts, 0);
+  map_init(&xpctx.recommended, 0);
+  queue_init(&xpctx.conflictsinfo);
+  queue_init(&xpctx.todo);
+  queue_init(&xpctx.todo_cond);
+  map_init(&xpctx.todo_condmap, 0);
+  queue_init(&xpctx.errors);
+  queue_init(&xpctx.cplxq);
+  queue_init(&xpctx.cplxblks);
+  queue_init(&xpctx.pruneq);
+
+  queue_init(&toinstall);
   queue_init(&qq);
-  queue_init(&errors);
-  queue_init(&cerrors);
-  queue_init(&posfoundq);
+  queue_init(&choices);
+  queue_init(&revertignore);
 
   queue_empty(out);
-  cidone = 0;
-  recdone = 0;
-  if (inconfl)
+
+  /* process ignored. hack: we mess with the ignore config in xp */
+  xp->ignoreignore = 0;
+  if (xpctx.ignoreignore && ignoreq->count)
     {
-      for (i = 0; i < inconfl->count; i += 2)
+      /* bad: have direct ignores and we need to zero the project config ignores */
+      oldignored = xp->ignored;
+      oldignoredx = xp->ignoredx;
+      ignoremapssaved = 1;
+      /* clear project config maps */
+      memset(&xp->ignored, 0, sizeof(xp->ignored));
+      memset(&xp->ignoredx, 0, sizeof(xp->ignoredx));
+    }
+  if (ignoreq->count)
+    {
+      /* mix direct ignores with ignores from project config */
+      for (i = 0; i < ignoreq->count; i++)
        {
-         Id con = inconfl->elements[i];
-         FOR_PROVIDES(p, pp, con)
+         const char *ss;
+         id = ignoreq->elements[i];
+         MAPEXP(&xp->ignored, id);
+         if (MAPTST(&xp->ignored, id))
+           continue;
+         MAPSET(&xp->ignored, id);
+         queue_push(&revertignore, id);
+         if ((ss = strchr(pool_id2str(pool, id), ':')) != 0)
            {
-             if (inconfl->elements[i + 1] && !pool_match_nevr(pool, pool->solvables + p, con))
+             id = str2id_dup(pool, ss + 1);
+             MAPEXP(&xp->ignoredx, id);
+             if (MAPTST(&xp->ignoredx, id))
                continue;
-             MAPEXP(&conflicts, pool->nsolvables);
-             MAPSET(&conflicts, p);
+             MAPSET(&xp->ignoredx, id);
+             queue_push(&revertignore, -id);
            }
        }
     }
-  /* do direct expands */
+  else if (xpctx.ignoreignore)
+    {
+      /* no direct ignores, ignore project config ignores.
+       * easy: just disable ignore processing */
+      xp->ignoreignore = 1;
+    }
+
+  /* grow maps to make bit tests cheaper */
+  expander_growmaps(xp);
+
+  /* process standard dependencies */
+  if (indep)
+    {
+      for (i = 0; i < indep->count; i += 2)
+       {
+         int deptype = indep->elements[i];
+         Id dep = indep->elements[i + 1];
+         if (ISRELDEP(dep) && GETRELDEP(pool, dep)->flags == REL_ERROR)
+           {
+             queue_push(&xpctx.errors, ERROR_BADDEPENDENCY);
+             queue_push2(&xpctx.errors, GETRELDEP(pool, dep)->evr, 0);
+             continue;
+           }
+         if ((deptype == DEPTYPE_REQUIRES || deptype == DEPTYPE_CONFLICTS) && pool_is_complex_dep(pool, dep))
+           {
+             expander_installed_complexdep(&xpctx, 0, dep, deptype);
+             continue;
+           }
+         if (deptype == DEPTYPE_REQUIRES)
+           {
+             queue_push2(&xpctx.todo, dep, 0);
+           }
+         else if (deptype == DEPTYPE_CONFLICTS || deptype == DEPTYPE_OBSOLETES)
+           {
+             FOR_PROVIDES(p, pp, dep)
+               {
+                 if (deptype == DEPTYPE_OBSOLETES && !pool_match_nevr(pool, pool->solvables + p, dep))
+                   continue;
+                 MAPEXP(&xpctx.conflicts, pool->nsolvables);
+                 MAPSET(&xpctx.conflicts, p);
+               }
+           }
+       }
+    }
+  /* process direct dependencies */
   for (i = 0; i < in->count; i++)
     {
       id = in->elements[i];
-      if (id == expander_directdepsend)
+      if (ISRELDEP(id) && GETRELDEP(pool, id)->flags == REL_ERROR)
        {
-         for (i = i + 1; i < in->count; i++)
-           if (in->elements[i] != expander_directdepsend)
-             queue_push2(&todo, in->elements[i], 0);
-         break;
+         queue_push(&xpctx.errors, ERROR_BADDEPENDENCY);
+         queue_push2(&xpctx.errors, GETRELDEP(pool, id)->evr, 0);
+         continue;
+       }
+      if (pool_is_complex_dep(pool, id))
+       {
+         expander_installed_complexdep(&xpctx, 0, id, DEPTYPE_REQUIRES);
+         continue;
        }
       q = 0;
       FOR_PROVIDES(p, pp, id)
@@ -853,389 +2148,582 @@ expander_expand(Expander *xp, Queue *in, Queue *out, Queue *inconfl)
        }
       if (!q)
        {
-         /* unclear, resolve later */
-         queue_push2(&todo, id, 0);
+         queue_push2(&xpctx.todo, id, 0);      /* unclear, resolve later */
          continue;
        }
-      if (MAPTST(&installed, q))
+      if (xp->debug)
+       expander_dbg(xp, "added %s because of %s (direct dep)\n", expander_solvid2name(xp, q), pool_dep2str(pool, id));
+      queue_push(&toinstall, q);
+    }
+
+  /* unify toinstall, check against conflicts */
+  for (i = 0; i < toinstall.count; i++)
+    {
+      p = toinstall.elements[i];
+      MAPSET(&xpctx.installed, p);
+    }
+  for (i = j = 0; i < toinstall.count; i++)
+    {
+      p = toinstall.elements[i];
+      if (!MAPTST(&xpctx.installed, p))
        continue;
-      if (conflicts.size && MAPTST(&conflicts, q))
-       {
-         queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
-         queue_push2(&errors, id, 0);
-         if (cidone < out->count)
-           expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
-         queue_push(&errors, ERROR_PROVIDERINFO2);
-         queue_push2(&errors, q, findconflictsinfo(&conflictsinfo, q));
-         continue;
-       }
-      if (pool->solvables[q].conflicts && (pp = expander_checkconflicts(xp, q, &installed, pool->solvables[q].repo->idarraydata + pool->solvables[q].conflicts, 0)) != 0)
+      MAPCLR(&xpctx.installed, p);
+      toinstall.elements[j++] = p;
+    }
+  queue_truncate(&toinstall, j);
+  if (xpctx.conflicts.size)
+    {
+      for (i = 0; i < toinstall.count; i++)
        {
-         queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
-         queue_push2(&errors, id, 0);
-         queue_push(&errors, ERROR_PROVIDERINFO);
-         queue_push2(&errors, q, pp);
-         continue;
+         p = toinstall.elements[i];
+         if (MAPTST(&xpctx.conflicts, p))
+           findconflictsinfo(&xpctx, p, 2);
        }
-      if (pool->solvables[q].obsoletes && (pp = expander_checkconflicts(xp, q, &installed, pool->solvables[q].repo->idarraydata + pool->solvables[q].obsoletes, 1)) != 0)
+    }
+
+  /* here is the big expansion loop */
+  pass = 0;
+  while (!xpctx.errors.count)
+    {
+      if (toinstall.count)
        {
-         queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
-         queue_push2(&errors, id, 0);
-         queue_push(&errors, ERROR_PROVIDERINFO);
-         queue_push2(&errors, q, -pp);
+         expander_installed_multiple(&xpctx, &toinstall);
+         pass = 0;
          continue;
        }
-      if (xp->debug)
-       expander_dbg(xp, "added %s because of %s (direct dep)\n", expander_solvid2name(xp, q), pool_dep2str(pool, id));
-      expander_installed(xp, q, &installed, &conflicts, &conflictsinfo, &cidone, out, &todo); /* unique match! */
-    }
-
-  doamb = 0;
-  ambcnt = todo.count;
-  while (todo.count)
-    {
-      id = queue_shift(&todo);
-      who = queue_shift(&todo);
-      if (ambcnt == 0)
-       {
-         if (doamb >= 2)
-           break;      /* amb pass had no progress, stop */
-        doamb = xp->userecommendsforchoices ? doamb + 1 : 3;
-         if (doamb == 1 && !haverecommended)
-           {
-             for (i = haverecommended_done; i < out->count; i++)
-               if (pool->solvables[out->elements[i]].recommends)
-                 haverecommended = 1;
-             haverecommended_done = out->count;
-             if (!haverecommended)
-               doamb = 3;
-           }
-         if (xp->debug)
-      {
-        if (doamb == 2)
-                     expander_dbg(xp, "now doing undecided dependencies with recommends\n");
-        else
-          expander_dbg(xp, "now doing undecided dependencies\n");
-      }
-         ambcnt = todo.count;
-       }
-      else
-       ambcnt -= 2;
-// printf("todo %s %s ambcnt %d\n", pool_id2str(pool, pool->solvables[who].name), pool_dep2str(pool, id), ambcnt);
-// fflush(stdout);
-      whon = who ? pool->solvables[who].name : 0;
-      queue_empty(&qq);
-      conflprov = 0;
-      conflprovpc = 0;
-      FOR_PROVIDES(p, pp, id)
+
+      if (!xpctx.todo.count)
        {
-         Id pc;
-         if (MAPTST(&installed, p))
-           break;
-         if (who && !xp->ignoreignore)
+         /* almost finished. now do weak deps if requested */
+         pass = 0;
+         if (xpctx.dorecommends)
            {
-             Id pn = pool->solvables[p].name;
-             if (MAPTST(&xp->ignored, pn))
-               break;
-             if (MAPTST(&xp->ignoredx, pn))
+             expander_dbg(xp, "--- now doing recommended packages\n");
+             for (; dorecstart < out->count; dorecstart++)
                {
-                 Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
-                 if (xid && MAPTST(&xp->ignored, xid))
-                   break;
+                 s = pool->solvables + out->elements[dorecstart];
+                 if (s->recommends)
+                   add_recommended_packages(&xpctx, s);
                }
+             if (xpctx.todo.count)
+               continue;
            }
-         if (conflicts.size && MAPTST(&conflicts, p))
+         if (xpctx.dosupplements)
            {
-             if (xp->debug)
+             Id sup, *supp;
+             expander_dbg(xp, "--- now doing supplemented packages\n");
+             for (p = 1; p < pool->nsolvables; p++)
                {
-                 Id pc = findconflictsinfo(&conflictsinfo, p);
-                 if (pc)
-                   expander_dbg(xp, "ignoring provider %s of %s because installed %s %s it\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc > 0 ? pc : -pc), pc > 0 ? "conflicts with" : "obsoletes");
-                 else
-                   expander_dbg(xp, "ignoring conflicted provider %s of %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id));
+                 s = pool->solvables + p;
+                 if (!s->supplements || !s->repo)
+                   continue;
+                 if (MAPTST(&xpctx.installed, p))
+                   continue;
+                 if (!pool_installable(pool, s))
+                   continue;
+                 if (xpctx.conflicts.size && MAPTST(&xpctx.conflicts, p))
+                   continue;
+                 if (s->conflicts && expander_checkconflicts(&xpctx, p, s->repo->idarraydata + s->conflicts, 0, 0) != 0)
+                   continue;
+                 if (s->obsoletes && expander_checkconflicts(&xpctx, p, s->repo->idarraydata + s->obsoletes, 1, 0) != 0)
+                   continue;
+                 supp = s->repo->idarraydata + s->supplements;
+                 while ((sup = *supp++) != 0)
+                   if (expander_dep_fulfilled(&xpctx, sup))
+                     break;
+                 if (!sup)
+                   continue;
+                 expander_dbg(xp, "added %s because it supplements %s\n", expander_solvid2name(xp, p), pool_dep2str(pool, sup));
+                 queue_push(&toinstall, p);
                }
-             conflprov = conflprov ? 1 : p;
-             conflprovpc = 0;
-             continue;
+             if (toinstall.count)
+               continue;
            }
-         if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0)) != 0)
+         /* no new stuff to do, we're finished! */
+         break;
+       }
+
+      expander_dbg(xp, "--- now doing normal dependencies\n");
+
+      if (pass == 1)
+       queue_empty(&choices);
+       
+      for (ti = tj = 0; ti < xpctx.todo.count; ti += 2)
+       {
+         int deptype = DEPTYPE_REQUIRES;
+         todoid = id = xpctx.todo.elements[ti];
+         who = xpctx.todo.elements[ti + 1];
+         if (!id)                      /* deleted entry? */
+           continue;
+         queue_empty(&qq);
+         if (ISCPLX(pool, id))
            {
-             expander_dbg(xp, "ignoring provider %s of %s because it conflicts with installed %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc));
-             conflprov = conflprov ? 1 : p;
-             conflprovpc = pc;
-             continue;
+             pp = GETCPLX(pool, id);   /* p, dep, deptype, ids... */
+             id = xpctx.cplxblks.elements[pp + 1];
+             deptype = xpctx.cplxblks.elements[pp + 2];
+             pp += 3;
+             while ((p = xpctx.cplxblks.elements[pp++]))
+               if (p > 0)
+                 queue_push(&qq, p);
            }
-         if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1)) != 0)
+         else
            {
-             expander_dbg(xp, "ignoring provider %s of %s because it obsoletes installed %s\n", pool_solvid2str(pool, p), pool_dep2str(pool, id), pool_solvid2str(pool, pc));
-             conflprov = conflprov ? 1 : p;
-             conflprovpc = -pc;
-             continue;
+             FOR_PROVIDES(p, pp, id)
+               queue_push(&qq, p);
            }
-         queue_push(&qq, p);
-       }
-      if (p)
-       continue;
-      if (qq.count == 0)
-       {
-         if (!conflprov)
+
+         if (qq.count == 0)
            {
-             queue_push(&errors, ERROR_NOPROVIDER);
-             queue_push2(&errors, id, who);
+             if (deptype == DEPTYPE_RECOMMENDS)
+               continue;
+             queue_push(&xpctx.errors, ERROR_NOPROVIDER);
+             queue_push2(&xpctx.errors, id, who);
+             add_noproviderinfo(&xpctx, id, who);
              continue;
            }
-         /* more work for conflicts */
-         if (conflprov != 1)
+
+         /* check installed and ignores */
+         whon = who ? pool->solvables[who].name : 0;
+         for (i = 0; i < qq.count; i++)
            {
-             /* nice, just one provider */
-             queue_push(&errors, ERROR_CONFLICTINGPROVIDER);
-             queue_push2(&errors, id, who);
-             if (!conflprovpc)
-               {
-                 if (cidone < out->count)
-                   expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
-                 conflprovpc = findconflictsinfo(&conflictsinfo, conflprov);
-                 queue_push(&errors, ERROR_PROVIDERINFO2);
-                 queue_push2(&errors, conflprov, conflprovpc);
-               }
-             else
+             p = qq.elements[i];
+             if (MAPTST(&xpctx.installed, p))
+               break;
+             if (who && deptype == DEPTYPE_REQUIRES && !xp->ignoreignore)
                {
-                 queue_push(&errors, ERROR_PROVIDERINFO);
-                 queue_push2(&errors, conflprov, conflprovpc);
+                 Id pn = pool->solvables[p].name;
+                 if (MAPTST(&xp->ignored, pn))
+                   break;
+                 if (MAPTST(&xp->ignoredx, pn))
+                   {
+                     Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
+                     if (xid && MAPTST(&xp->ignored, xid))
+                       break;
+                   }
                }
+           }
+         if (i < qq.count)
+           continue;           /* ignored dependency or fulfilled */
+
+         if (pass == 0 && qq.count > 1)
+           {
+             xpctx.todo.elements[tj++] = todoid;
+             xpctx.todo.elements[tj++] = who;
              continue;
            }
-         /* even more work if all providers conflict */
-         queue_push(&errors, ERROR_CONFLICTINGPROVIDERS);
-         queue_push2(&errors, id, who);
-         if (cidone < out->count)
-           expander_updateconflictsinfo(xp, &conflictsinfo, &cidone, out);
-         FOR_PROVIDES(p, pp, id)
+
+         /* do conflict pruning */
+         conflprovpc = 0;
+         for (i = j = 0; i < qq.count; i++)
            {
              Id pc;
-             if (conflicts.size && MAPTST(&conflicts, p))
+             p = qq.elements[i];
+             if (xpctx.conflicts.size && MAPTST(&xpctx.conflicts, p))
                {
-                 pc = findconflictsinfo(&conflictsinfo, p);
-                 queue_push(&errors, ERROR_PROVIDERINFO2);
-                 queue_push2(&errors, p, pc);
+                 if (xp->debug)
+                   findconflictsinfo(&xpctx, p, 0);
+                 conflprovpc = 0;
                  continue;
                }
-             if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0)) != 0)
+             if (pool->solvables[p].conflicts && (pc = expander_checkconflicts(&xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0, 0)) != 0)
                {
-                 queue_push(&errors, ERROR_PROVIDERINFO);
-                 queue_push2(&errors, p, pc);
+                 conflprovpc = pc;
                  continue;
                }
-             if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(xp, p, &installed, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1)) != 0)
+             if (pool->solvables[p].obsoletes && (pc = expander_checkconflicts(&xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1, 0)) != 0)
                {
-                 queue_push(&errors, ERROR_PROVIDERINFO);
-                 queue_push2(&errors, p, -pc);
+                 conflprovpc = -pc;
                  continue;
                }
+             qq.elements[j++] = p;
            }
-         continue;
-       }
-      if (qq.count > 1 && !doamb)
-       {
-         /* try again later */
-         queue_push2(&todo, id, who);
+         if (j == 0)
+           {
+             if (deptype == DEPTYPE_RECOMMENDS)
+               continue;
+             queue_push(&xpctx.errors, ERROR_CONFLICTINGPROVIDERS);
+             queue_push2(&xpctx.errors, id, who);
+             if (qq.count == 1 && conflprovpc != 1 && conflprovpc != -1)
+               {
+                 p = qq.elements[0];
+                 if (conflprovpc)
+                   {
+                     queue_push(&xpctx.errors, ERROR_PROVIDERINFO);
+                     queue_push2(&xpctx.errors, p, conflprovpc);
+                     continue;
+                   }
+                 findconflictsinfo(&xpctx, p, 1);
+                 continue;
+               }
+             /* even more work if all providers conflict */
+             for (j = 0; j < qq.count; j++)
+               {
+                 p = qq.elements[j];
+                 if (xpctx.conflicts.size && MAPTST(&xpctx.conflicts, p))
+                   findconflictsinfo(&xpctx, p, 1);
+                 if (pool->solvables[p].conflicts)
+                   expander_checkconflicts(&xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].conflicts, 0, 1);
+                 if (pool->solvables[p].obsoletes)
+                   expander_checkconflicts(&xpctx, p, pool->solvables[p].repo->idarraydata + pool->solvables[p].obsoletes, 1, 1);
+               }
+             continue;
+           }
+         queue_truncate(&qq, j);
+          if (qq.count == 1)
+           {
+             p = qq.elements[0];
+             if (xp->debug)
+               expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
+             queue_push(&toinstall, p);
+             continue;
+           }
+         /* pass is == 1 and we have multiple choices */
          if (xp->debug)
            {
              expander_dbg(xp, "undecided about %s:%s:", whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
              for (i = 0; i < qq.count; i++)
-               expander_dbg(xp, " %s", expander_solvid2name(xp, qq.elements[i]));
-             expander_dbg(xp, "\n");
+               expander_dbg(xp, " %s", expander_solvid2name(xp, qq.elements[i]));
+              expander_dbg(xp, "\n");
            }
+         queue_push2(&choices, qq.count + 3, id);
+         queue_push(&choices, qq.count);
+         queue_insertn(&choices, choices.count, qq.count, qq.elements);
+         xpctx.todo.elements[tj++] = todoid;
+         xpctx.todo.elements[tj++] = who;
+       }
+      queue_truncate(&xpctx.todo, tj);
+
+      if (toinstall.count)
+       continue;
+
+      if (!xpctx.todo.count)
+       continue;
+
+      /* did not find a package to install, only choices left on todo list */
+      if (pass == 0)
+       {
+         pass = 1;     /* now do conflict pruning */
          continue;
        }
 
-      /* prune neg prefers */
-      if (qq.count > 1)
+      expander_dbg(xp, "--- now doing undecided dependencies\n");
+
+      /* prune prefers */
+      for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
        {
-         for (i = j = 0; i < qq.count; i++)
+         Id who = xpctx.todo.elements[ti + 1];
+         Id *qe = choices.elements + tc + 3;
+         Id id = choices.elements[tc + 1];
+         int qn = choices.elements[tc + 2];
+         whon = who ? pool->solvables[who].name : 0;
+         if (qn > 1)
+           qn = prune_neg_prefers(&xpctx, who, qe, qn);
+         if (qn > 1)
+           qn = prune_pos_prefers(&xpctx, who, qe, qn, 0);
+         if (qn == 1)
            {
-             p = qq.elements[i];
-             pn = pool->solvables[p].name;
-             if (MAPTST(&xp->preferneg, pn))
-               continue;
-             if (who && MAPTST(&xp->prefernegx, pn))
-               {
-                 Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
-                 if (xid && MAPTST(&xp->preferneg, xid))
-                   continue;
-               }
-             qq.elements[j++] = p;
+             p = qe[0];
+             if (xp->debug)
+               expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
+             queue_push(&toinstall, p);
+             xpctx.todo.elements[ti] = 0;      /* kill entry */
            }
-         if (j)
-           queue_truncate(&qq, j);
+         choices.elements[tc + 2] = qn;
+         tc += choices.elements[tc];
        }
+      if (toinstall.count)
+       continue;
 
-      /* prune pos prefers */
-      if (qq.count > 1)
+      /* prune pos prefers with domulti and debian or */
+      for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
        {
-         queue_empty(&posfoundq);
-         for (i = j = 0; i < qq.count; i++)
+         Id who = xpctx.todo.elements[ti + 1];
+         Id *qe = choices.elements + tc + 3;
+         Id id = choices.elements[tc + 1];
+         int qn = choices.elements[tc + 2];
+         whon = who ? pool->solvables[who].name : 0;
+         if (qn > 1)
+           qn = prune_pos_prefers(&xpctx, who, qe, qn, 1);
+         if (qn > 1 && pool->disttype != DISTTYPE_RPM)
            {
-             p = qq.elements[i];
-             pn = pool->solvables[p].name;
-             if (MAPTST(&xp->preferpos, pn))
-               {
-                 queue_push2(&posfoundq, pn, p);
-                 qq.elements[j++] = p;
-                 continue;
-               }
-             if (who && MAPTST(&xp->preferposx, pn))
-               {
-                 Id xid = pool_str2id(pool, pool_tmpjoin(pool, pool_id2str(pool, whon), ":", pool_id2str(pool, pn)), 0);
-                 if (xid && MAPTST(&xp->preferpos, xid))
-                   {
-                     queue_push2(&posfoundq, xid, p);
-                     qq.elements[j++] = p;
-                     continue;
-                   }
-               }
+             if (ISRELDEP(id) && GETRELDEP(pool, id)->flags == REL_OR)
+               qn = prune_or_dep(&xpctx, id, qe, qn);
            }
-         if (posfoundq.count == 2)
+         if (qn == 1)
            {
-             queue_empty(&qq);
-             queue_push(&qq, posfoundq.elements[1]);
+             p = qe[0];
+             if (xp->debug)
+               expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
+             queue_push(&toinstall, p);
+             xpctx.todo.elements[ti] = 0;      /* kill entry */
            }
-         else if (posfoundq.count)
+         choices.elements[tc + 2] = qn;
+         tc += choices.elements[tc];
+       }
+      if (toinstall.count)
+       continue;
+
+      /* prune recommended packages */
+      if (xpctx.userecommendsforchoices)
+        expander_updaterecommendedmap(&xpctx);
+      if (xpctx.recommended.size)
+       {
+         expander_dbg(xp, "now doing undecided dependencies with recommends\n");
+         for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
            {
-             /* found a pos prefer, now find first hit */
-             /* (prefers are ordered) */
-             for (i = 0; i < xp->preferposq.count; i++)
+             Id who = xpctx.todo.elements[ti + 1];
+             Id *qe = choices.elements + tc + 3;
+             Id id = choices.elements[tc + 1];
+             int qn = choices.elements[tc + 2];
+             whon = who ? pool->solvables[who].name : 0;
+             for (i = j = 0; i < qn; i++)
+               if (MAPTST(&xpctx.recommended, qe[i]))
+                 qe[j++] = qe[i];
+             if (j)
+               qn = j;
+             if (qn == 1)
                {
-                 Id xid = xp->preferposq.elements[i];
-                 for (j = 0; j < posfoundq.count; j += 2)
-                   if (posfoundq.elements[j] == xid)
-                     break;
-                 if (j < posfoundq.count)
-                   {
-                     queue_empty(&qq);
-                     queue_push(&qq, posfoundq.elements[j + 1]);
-                     break;
-                   }
+                 p = qe[0];
+                 if (xp->debug)
+                   expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
+                 queue_push(&toinstall, p);
+                 xpctx.todo.elements[ti] = 0;  /* kill entry */
                }
+             choices.elements[tc + 2] = qn;
+             tc += choices.elements[tc];
            }
+         if (toinstall.count)
+           continue;
        }
-
-      /* prune OR deps */
-      if (qq.count > 1 && ISRELDEP(id) && GETRELDEP(pool, id)->flags == REL_OR)
+      if (xpctx.usesupplementsforchoices)
        {
-         Id rid = id;
-         for (;;)
+         expander_dbg(xp, "now doing undecided dependencies with supplements\n");
+         for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
            {
-             Reldep *rd = 0;
-             if (ISRELDEP(rid))
+             Id who = xpctx.todo.elements[ti + 1];
+             Id *qe = choices.elements + tc + 3;
+             Id id = choices.elements[tc + 1];
+             int qn = choices.elements[tc + 2];
+             whon = who ? pool->solvables[who].name : 0;
+             qn = prune_supplemented(&xpctx, qe, qn);
+             if (qn == 1)
                {
-                 rd = GETRELDEP(pool, rid);
-                 if (rd->flags != REL_OR)
-                   rd = 0;
+                 p = qe[0];
+                 if (xp->debug)
+                   expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, p), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
+                 queue_push(&toinstall, p);
+                 xpctx.todo.elements[ti] = 0;  /* kill entry */
                }
-             if (rd)
-               rid = rd->name;
-             queue_empty(&qq);
-             FOR_PROVIDES(p, pp, rid)
-               queue_push(&qq, p);
-             if (qq.count)
-               break;
-             if (rd)
-               rid = rd->evr;
-             else
-               break;
+             choices.elements[tc + 2] = qn;
+             tc += choices.elements[tc];
            }
+         if (toinstall.count)
+           continue;
        }
 
-      if (qq.count > 1 && doamb == 1)
-       {
-         queue_push2(&todo, id, who);
-         continue;
-       }
-
-      /* prioritize recommended packages. */
-      if (qq.count > 1 && doamb == 2)
-       {
-         expander_updaterecommendedmap(xp, &recommended, &recdone, out);
-         if (recommended.size)
-           {
-             for (i = j = 0; i < qq.count; i++)
-               if (MAPTST(&recommended, qq.elements[i]))
-                 qq.elements[j++] = qq.elements[i];
-             if (j)
-               queue_truncate(&qq, j);
-           }
-       }
-
+      /* nothing more to prune. record errors. */
+      for (ti = tc = 0; ti < xpctx.todo.count; ti += 2)
+       {
+         Id who = xpctx.todo.elements[ti + 1];
+         Id *qe = choices.elements + tc + 3;
+         Id id = choices.elements[tc + 1];
+         int qn = choices.elements[tc + 2];
+         queue_push(&xpctx.errors, ERROR_CHOICE);
+         queue_push2(&xpctx.errors, id, who);
+         for (i = 0; i < qn; i++)
+           queue_push(&xpctx.errors, qe[i]);
+         queue_push(&xpctx.errors, 0);
+         tc += choices.elements[tc];
+       }
+    }
 
-      if (qq.count > 1)
+  /* free data */
+  map_free(&xpctx.installed);
+  map_free(&xpctx.conflicts);
+  map_free(&xpctx.recommended);
+  map_free(&xpctx.todo_condmap);
+  queue_free(&xpctx.conflictsinfo);
+  queue_free(&xpctx.todo_cond);
+  queue_free(&xpctx.todo);
+  queue_free(&toinstall);
+  queue_free(&qq);
+  queue_free(&choices);
+  queue_free(&xpctx.pruneq);
+  queue_free(&xpctx.cplxq);
+  queue_free(&xpctx.cplxblks);
+
+  /* revert ignores */
+  xp->ignoreignore = oldignoreignore;
+  if (ignoremapssaved)
+    {
+      map_free(&xp->ignored);
+      map_free(&xp->ignoredx);
+      xp->ignored = oldignored;
+      xp->ignoredx = oldignoredx;
+    }
+  else
+    {
+      for (i = 0; i < revertignore.count; i++)
        {
-         queue_push(&cerrors, ERROR_CHOICE);
-         queue_push2(&cerrors, id, who);
-         for (i = 0; i < qq.count; i++)
-           queue_push(&cerrors, qq.elements[i]);
-         queue_push(&cerrors, 0);
-         /* try again later */
-         queue_push2(&todo, id, who);
-         continue;
+         id = revertignore.elements[i];
+         if (id > 0)
+           MAPCLR(&xp->ignored, id);
+         else
+           MAPCLR(&xp->ignoredx, -id);
        }
-      if (xp->debug)
-       expander_dbg(xp, "added %s because of %s:%s\n", expander_solvid2name(xp, qq.elements[0]), whon ? pool_id2str(pool, whon) : "(direct)", pool_dep2str(pool, id));
-      expander_installed(xp, qq.elements[0], &installed, &conflicts, &conflictsinfo, &cidone, out, &todo);
-      doamb = 0;
-      ambcnt = todo.count;
-      queue_empty(&cerrors);
-    }
-  map_free(&installed);
-  map_free(&conflicts);
-  map_free(&recommended);
-  queue_free(&conflictsinfo);
+    }
+  queue_free(&revertignore);
+
+  /* finish return queue, count errors */
   nerrors = 0;
-  if (errors.count || cerrors.count)
+  if (xpctx.errors.count)
     {
       queue_empty(out);
-      for (i = 0; i < errors.count; i += 3)
+      queue_insertn(out, 0, xpctx.errors.count, xpctx.errors.elements);
+      for (i = 0; i < out->count; i += 3)
        {
-         queue_push(out, errors.elements[i]);
-         queue_push(out, errors.elements[i + 1]);
-         queue_push(out, errors.elements[i + 2]);
          nerrors++;
-       }
-      for (i = 0; i < cerrors.count; )
-       {
-         queue_push(out, cerrors.elements[i]);
-         queue_push(out, cerrors.elements[i + 1]);
-         queue_push(out, cerrors.elements[i + 2]);
-         i += 3;
-         while (cerrors.elements[i])
-           {
-             queue_push(out, cerrors.elements[i]);
+         if (out->elements[i] == ERROR_CHOICE)
+           while (out->elements[i + 3])
              i++;
-           }
-         queue_push(out, 0);
-         i++;
-         nerrors++;
        }
     }
-  else
+  queue_free(&xpctx.errors);
+  return nerrors;
+}
+
+static Expander *
+expander_create(Pool *pool, Queue *preferpos, Queue *preferneg, Queue *ignore, Queue *conflict, Queue *fileprovides, int debug, int options)
+{
+  Expander *xp;
+  int i, j;
+  Id id, id2;
+  const char *str;
+  Queue q;
+
+  xp = calloc(sizeof(Expander), 1);
+  xp->pool = pool;
+  xp->debug = debug;
+  xp->ignoreignore = options & EXPANDER_OPTION_IGNOREIGNORE ? 1 : 0;
+  xp->ignoreconflicts = options & EXPANDER_OPTION_IGNORECONFLICTS ? 1 : 0;
+  xp->userecommendsforchoices = options & EXPANDER_OPTION_USERECOMMENDSFORCHOICES ? 1 : 0;
+  xp->usesupplementsforchoices = options & EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES ? 1 : 0;
+  xp->dorecommends = options & EXPANDER_OPTION_DORECOMMENDS ? 1 : 0;
+  xp->dosupplements = options & EXPANDER_OPTION_DOSUPPLEMENTS ? 1 : 0;
+
+  queue_init(&xp->preferposq);
+  for (i = 0; i < preferpos->count; i++)
+    {
+      id = preferpos->elements[i];
+      queue_push(&xp->preferposq, id);
+      MAPEXP(&xp->preferpos, id);
+      MAPSET(&xp->preferpos, id);
+      if ((str = strchr(pool_id2str(pool, id), ':')) != 0)
+        {
+          id = str2id_dup(pool, str + 1);
+         MAPEXP(&xp->preferposx, id);
+         MAPSET(&xp->preferposx, id);
+        }
+    }
+  for (i = 0; i < preferneg->count; i++)
+    {
+      id = preferneg->elements[i];
+      MAPEXP(&xp->preferneg, id);
+      MAPSET(&xp->preferneg, id);
+      if ((str = strchr(pool_id2str(pool, id), ':')) != 0)
+        {
+          id = str2id_dup(pool, str + 1);
+         MAPEXP(&xp->prefernegx, id);
+         MAPSET(&xp->prefernegx, id);
+        }
+    }
+
+  for (i = 0; i < ignore->count; i++)
+    {
+      id = ignore->elements[i];
+      MAPEXP(&xp->ignored, id);
+      MAPSET(&xp->ignored, id);
+      if ((str = strchr(pool_id2str(pool, id), ':')) != 0)
+        {
+          id = str2id_dup(pool, str + 1);
+         MAPEXP(&xp->ignoredx, id);
+         MAPSET(&xp->ignoredx, id);
+        }
+    }
+
+  queue_init(&xp->conflictsq);
+  for (i = 0; i < conflict->count; i += 2)
+    {
+      id = conflict->elements[i];
+      id2 = conflict->elements[i + 1];
+      queue_push2(&xp->conflictsq, id, id2);
+      MAPEXP(&xp->conflicts, id);
+      MAPSET(&xp->conflicts, id);
+      MAPEXP(&xp->conflicts, id2);
+      MAPSET(&xp->conflicts, id2);
+    }
+
+  if (fileprovides->count)
+    xp->havefileprovides = 1;
+  queue_init(&q);
+  for (i = 0; i < fileprovides->count; i++)
     {
-      if (todo.count)
+      Id p, pp;
+      id = fileprovides->elements[i];
+      int havenew = 0;
+
+      /* XXX: this modifies the pool, which is somewhat unclean! */
+      /* get old providers */
+      queue_empty(&q);
+      FOR_PROVIDES(p, pp, id)
+        queue_push(&q, p);
+      for (j = i + 1; j < fileprovides->count && (id2 = fileprovides->elements[j]) != 0; j++)
        {
-         fprintf(stderr, "Internal expansion error!\n");
-         queue_empty(out);
-         queue_push(out, ERROR_NOPROVIDER);
-         queue_push(out, 0);
-         queue_push(out, 0);
+         FOR_PROVIDES(p, pp, id2)
+           {
+             int k;
+             if (pool->solvables[p].name != id2)
+               continue;               /* match name only */
+             /* insert sorted */
+             for (k = 0; ; k++)
+               {
+                 if (k == q.count || q.elements[k] > p)
+                   {
+                     queue_insert(&q, k, p);
+                     havenew = 1;
+                     break;
+                   }
+                 if (q.elements[k] == p)
+                   break;
+               }
+           }
        }
+      if (havenew)
+        pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
+      i = j;
     }
-  queue_free(&todo);
-  queue_free(&qq);
-  queue_free(&errors);
-  queue_free(&cerrors);
-  queue_free(&posfoundq);
-  return nerrors;
+  queue_free(&q);
+  return xp;
+}
+
+static void
+expander_free(Expander *xp)
+{
+  map_free(&xp->ignored);
+  map_free(&xp->ignoredx);
+  queue_free(&xp->preferposq);
+  map_free(&xp->preferpos);
+  map_free(&xp->preferposx);
+  map_free(&xp->preferneg);
+  map_free(&xp->prefernegx);
+  queue_free(&xp->conflictsq);
+  map_free(&xp->conflicts);
+  solv_free(xp->debugstr);
+  solv_free(xp);
 }
 
+
+
 static void
 set_disttype(Pool *pool, int disttype)
 {
@@ -1270,8 +2758,128 @@ set_disttype_from_location(Pool *pool, Solvable *so)
     set_disttype(pool, disttype);
 }
 
-void
-create_considered(Pool *pool, Repo *repoonly, Map *considered)
+static inline const char *
+solvid2name(Pool *pool, Id p)
+{
+  return pool_id2str(pool, pool->solvables[p].name);
+}
+
+#define ISNOARCH(arch) (arch == ARCH_NOARCH || arch == ARCH_ALL || arch == ARCH_ANY)
+
+static int
+has_keyname(Repo *repo, Id keyname)
+{
+  Repodata *data;
+  int rdid;
+  FOR_REPODATAS(repo, rdid, data)
+    if (repodata_has_keyname(data, keyname))
+      return 1;
+  return 0;
+}
+
+static inline int
+match_modules_req(Pool *pool, Id id)
+{
+  const char *dep = pool_id2str(pool, id);
+  Id *modules;
+  if (strncmp(dep, "platform", 8) == 0 && (dep[8] == 0 || dep[8] == '-'))
+    return 1;
+  for (modules = pool->appdata; *modules; modules++)
+    {
+      const char *name, *rname;
+      if (*modules == id)
+       return 1;
+      name = pool_id2str(pool, *modules);
+      if ((rname = strrchr(name, '-')) == 0 || rname == name)
+       continue;
+      if (!strncmp(dep, rname, rname - name) && dep[rname - name] == 0)
+       return 1;
+    }
+  return 0;
+}
+
+static void
+create_module_map(Repo *repo, Map *modulemap, Queue *modulemapq)
+{
+  Pool *pool = repo->pool;
+  Id *modules = pool->appdata;
+  int i, have_moduleinfo = 0;
+  Id id, p, *pp;
+  Solvable *s;
+
+  if (!modulemap->size)
+    map_grow(modulemap, pool->ss.nstrings);
+  if (!modules)
+    return;
+  if (!*modules)
+    {
+      map_setall(modulemap);
+      return;
+    }
+  /* clear old bits */
+  if (modulemapq->count)
+    {
+      for (i = 0; i < modulemapq->count; i++)
+       MAPCLR(modulemap, modulemapq->elements[i]);
+      queue_empty(modulemapq);
+    }
+  for (modules = pool->appdata; *modules; modules++)
+    MAPSET(modulemap, *modules);
+  /* look for module information stored in "buildservice:modules" solvables */
+  FOR_REPO_SOLVABLES(repo, p, s)
+    {
+      if (s->name != buildservice_modules || s->arch != ARCH_SRC)
+       continue;
+      have_moduleinfo = 1;
+      if (s->evr >= 1 && s->evr < pool->ss.nstrings && MAPTST(modulemap, s->evr))
+       {
+         queue_push(modulemapq, s->evr);       /* directly addressed */
+         continue;
+       }
+      id = s->repo->idarraydata[s->provides];
+      if (id < 1 || id >= pool->ss.nstrings || !MAPTST(modulemap, id))
+       continue;       /* not what we're looking for */
+      for (pp = s->repo->idarraydata + s->requires; (id = *pp) != 0; pp++)
+        {
+          /* check if the dep is fulfilled by any module in the list */
+          if (id < 1 || id >= pool->ss.nstrings)
+           break;      /* hey! */
+          if (!MAPTST(modulemap, id) && !match_modules_req(pool, id))
+           break;      /* could not fulfil requires */
+        }
+      if (id)
+       continue;       /* could not fulfil one of the requires, ignore module */
+      queue_push(modulemapq, s->evr);
+    }
+  if (!have_moduleinfo)
+    {
+      /* old style repo with no moduleinfo at all. simple use the unexpanded ids */
+      for (modules = pool->appdata; *modules; modules++)
+        queue_push(modulemapq, *modules);
+      return;
+    }
+  for (modules = pool->appdata; *modules; modules++)
+    MAPCLR(modulemap, *modules);
+  for (i = 0; i < modulemapq->count; i++)
+    MAPSET(modulemap, modulemapq->elements[i]);
+}
+
+static int
+in_module_map(Pool *pool, Map *modulemap, Queue *modules)
+{
+  int i;
+  for (i = 0; i < modules->count; i++)
+    { 
+      Id id = modules->elements[i];
+      if (id > 1 && id < pool->ss.nstrings && MAPTST(modulemap, id))
+       return 1;
+    }
+  return 0;
+}
+
+
+static void
+create_considered(Pool *pool, Repo *repoonly, Map *considered, int unorderedrepos)
 {
   Id p, pb,*best;
   Solvable *s, *sb;
@@ -1279,41 +2887,70 @@ create_considered(Pool *pool, Repo *repoonly, Map *considered)
   Repo *repo;
   int olddisttype = -1;
   int dodrepo;
+  int mayhave_modules;
+  Queue modules;
+  Map modulemap;
+  Queue modulemapq;
+  int modulemap_uptodate;
 
   map_init(considered, pool->nsolvables);
   best = solv_calloc(sizeof(Id), pool->ss.nstrings);
   
+  queue_init(&modules);
+  map_init(&modulemap, 0);
+  queue_init(&modulemapq);
   FOR_REPOS(ridx, repo)
     {
       if (repoonly && repo != repoonly)
        continue;
       dodrepo = repo_lookup_str(repo, SOLVID_META, buildservice_dodurl) != 0;
+      mayhave_modules = has_keyname(repo, buildservice_modules) ? 1 : 0;
+      modulemap_uptodate = 0;
       FOR_REPO_SOLVABLES(repo, p, s)
        {
+         int inmodule = 0;
          if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
            continue;
          pb = best[s->name];
-         if (pb)
+         sb = pb ? pool->solvables + pb : 0;
+         if (mayhave_modules)
            {
-             sb = pool->solvables + pb;
-             if (s->repo != sb->repo)
-               continue;       /* first repo wins */
-             if (s->arch != sb->arch)
+             solvable_lookup_idarray(s, buildservice_modules, &modules);
+             inmodule = modules.count ? 1 : 0;
+             if (inmodule)
                {
-                 int r;
-                 if (s->arch == ARCH_NOARCH || s->arch == ARCH_ALL || s->arch == ARCH_ANY)
-                   continue;
-                 if (sb->arch != ARCH_NOARCH && sb->arch != ARCH_ALL && sb->arch != ARCH_ANY)
+                 if (!modulemap_uptodate)
                    {
-                     /* the strcmp is kind of silly, but works for most archs */
-                     r = strcmp(pool_id2str(pool, sb->arch), pool_id2str(pool, s->arch));
-                     if (r >= 0)
-                       continue;
+                     create_module_map(repo, &modulemap, &modulemapq);
+                     modulemap_uptodate = 1;
                    }
+                 if (!in_module_map(pool, &modulemap, &modules))
+                   continue;           /* nope, ignore package */
+               }
+           }
+         if (unorderedrepos && sb && s->repo->priority != sb->repo->priority)
+           {
+             if (s->repo->priority < sb->repo->priority)
+               continue;       /* lower prio, ignore */
+           }
+         else if (sb)
+           {
+             int sbinmodule = 0;
+             /* we already have that name. decide which one to take */
+             if (!unorderedrepos && s->repo != sb->repo)
+               continue;       /* first repo wins */
+
+             if (s->repo == sb->repo && mayhave_modules)
+               sbinmodule = solvable_lookup_type(sb, buildservice_modules) ? 1 : 0;
+
+             if (inmodule != sbinmodule)
+               {
+                 if (inmodule < sbinmodule)
+                   continue;
                }
              else if (s->evr != sb->evr)
                {
-                 /* same repo, check versions */
+                 /* check versions */
                  int r;
                  if (olddisttype < 0)
                    {
@@ -1321,16 +2958,27 @@ create_considered(Pool *pool, Repo *repoonly, Map *considered)
                      set_disttype_from_location(pool, s);
                    }
                  r = pool_evrcmp(pool, sb->evr, s->evr, EVRCMP_COMPARE);
-                 if (r > 0)
+                 if (r == 0)
+                   r = strcmp(pool_id2str(pool, sb->evr), pool_id2str(pool, s->evr));
+                 if (r >= 0)
                    continue;
-                 else if (r == 0)
+               }
+             else if (s->arch != sb->arch)
+               {
+                 /* same versions, check arch */
+                 if (ISNOARCH(sb->arch) && !ISNOARCH(s->arch))
+                   continue;
+                 if (ISNOARCH(sb->arch) || !ISNOARCH(s->arch))
                    {
-                     r = strcmp(pool_id2str(pool, sb->evr), pool_id2str(pool, s->evr));
+                     int r;
+                     /* the strcmp is kind of silly, but works for most archs */
+                     r = strcmp(pool_id2str(pool, sb->arch), pool_id2str(pool, s->arch));
                      if (r >= 0)
                        continue;
                    }
                }
            }
+
           if (dodrepo)
            {
              /* we only consider dod packages */
@@ -1343,7 +2991,7 @@ create_considered(Pool *pool, Repo *repoonly, Map *considered)
          best[s->name] = p;
          MAPSET(considered, p);
        }
-      /* dodrepos have a second pass: replace dod entries with downloaded ones */
+      /* dodrepos have a second pass: replace dod entries with identical downloaded ones */
       if (dodrepo)
        {
          const char *bsid;
@@ -1367,15 +3015,18 @@ create_considered(Pool *pool, Repo *repoonly, Map *considered)
        }
     }
   solv_free(best);
+  queue_free(&modules);
+  map_free(&modulemap);
+  queue_free(&modulemapq);
   if (olddisttype >= 0 && pool->disttype != olddisttype)
     set_disttype(pool, olddisttype);
 }
 
 struct metaline {
-  char *l;           /* pointer to line */
-  int lastoff;       /* line offset of last path element */
-  int nslash;        /* number of slashes */
-  int killed;        /* 1: line has been killed. 2: because of a cycle package */
+  char *l;             /* pointer to line */
+  int lastoff;         /* line offset of last path element */
+  int nslash;          /* number of slashes */
+  int killed;          /* 1: line has been killed. 2: because of a cycle package */
 };
 
 static int metacmp(const void *ap, const void *bp)
@@ -1397,6 +3048,87 @@ static int metacmp(const void *ap, const void *bp)
   return a - b;
 }
 
+static char *
+slurp(FILE *fp, int *lenp)
+{
+  int l, ll;
+  char *buf = 0; 
+  int bufl = 0; 
+
+  for (l = 0; ; l += ll)
+    {    
+      if (bufl - l < 4096)
+        {
+          bufl += 4096;
+         if (bufl < 0)
+           {
+             buf = solv_free(buf);
+             l = 0;
+             break;
+           }
+          buf = solv_realloc(buf, bufl);
+        }
+      ll = fread(buf + l, 1, bufl - l, fp); 
+      if (ll < 0) 
+        {
+          buf = solv_free(buf);
+          l = 0; 
+          break;
+        }
+      if (ll == 0)
+        {
+          buf[l] = 0;   /* always zero-terminate */
+          break;
+        }
+    }    
+  if (lenp)
+    *lenp = l; 
+  return buf; 
+}
+
+
+Id
+repo_add_obsbinlnk(Repo *repo, const char *path, int flags)
+{
+  Repodata *data;
+  FILE *fp;
+  char *buf;
+  int len;
+  SV *sv;
+  unsigned char *src;
+  STRLEN srcl;
+  Id p;
+
+  if ((fp = fopen(path, "r")) == 0)
+    return 0;
+  buf = slurp(fp, &len);
+  fclose(fp);
+  if (!buf || len <= 0)
+    return 0;
+  src = (unsigned char *)buf;
+  srcl = len;
+  sv = 0;
+  if (srcl >= 7 && src[0] == 'p' && src[1] == 's' && src[2] == 't' && src[3] == '0' && (src[4] & 1) == 1 && src[4] >= 5) {
+    src += 6;
+    srcl -= 6;
+    sv = retrieve(&src, &srcl, 0);
+  }
+  free(buf);
+  if (!sv)
+    return 0;
+  if (SvTYPE(sv) != SVt_PVHV)
+    {
+      SvREFCNT_dec(sv);
+      return 0;
+    }
+  data = repo_add_repodata(repo, flags);
+  p = data2pkg(repo, data, (HV *)sv, 0);
+  SvREFCNT_dec(sv);
+  if (!(flags & REPO_NO_INTERNALIZE))
+    repodata_internalize(data);
+  return p;
+}
+
 #ifndef REPO_NO_LOCATION
 # define REPO_NO_LOCATION 0
 #endif
@@ -1413,9 +3145,20 @@ repodata_addbin(Repodata *data, char *prefix, char *s, int sl, char *sid)
   path = solv_dupjoin(prefix, "/", s);
   if (sl >= 4 && !strcmp(s + sl - 4, ".rpm"))
     p = repo_add_rpm(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|RPM_ADD_WITH_PKGID|RPM_ADD_NO_FILELIST|RPM_ADD_NO_RPMLIBREQS);
+#if defined(LIBSOLVEXT_FEATURE_DEBIAN)
   else if (sl >= 4 && !strcmp(s + sl - 4, ".deb"))
     p = repo_add_deb(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|DEBS_ADD_WITH_PKGID);
-#ifdef ARCH_ADD_WITH_PKGID
+#endif
+  else if (sl >= 10 && !strcmp(s + sl - 10, ".obsbinlnk"))
+    {
+      p = repo_add_obsbinlnk(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION);
+      /* do not overwrite location from obsbinlnk file */
+      solv_free(path);
+      if (p)
+        repodata_set_str(data, p, buildservice_id, sid);
+      return p;
+    }
+#if defined(LIBSOLVEXT_FEATURE_ARCHREPO) && defined(ARCH_ADD_WITH_PKGID)
   else if (sl >= 12 && (!strcmp(s + sl - 11, ".pkg.tar.gz") || !strcmp(s + sl - 11, ".pkg.tar.xz") || !strcmp(s + sl - 12, ".pkg.tar.zst")))
     p = repo_add_arch_pkg(data->repo, (const char *)path, REPO_REUSE_REPODATA|REPO_NO_INTERNALIZE|REPO_NO_LOCATION|ARCH_ADD_WITH_PKGID);
 #endif
@@ -3486,38 +5229,173 @@ printobscpioinstr(FILE *fp, int fdstore, int withmeta)
   printf("stats file_size %lld\n", (unsigned long long)ftell(fp));
 }
 
+static int
+unifymodules_cmp(const void *ap, const void *bp, void *dp)
+{
+  return *(Id *)ap - *(Id *)bp;
+}
+
+static int
+missingmodules_cmp(const void *ap, const void *bp, void *dp)
+{
+  const Id *a = ap;
+  const Id *b = bp;
+  if (a[0] != b[0])
+    return a[0] - b[0];
+  if (!a[1] && b[1])
+    return -1;
+  if (!b[1] && a[1])
+    return 1;
+  return a[1] - b[1];
+}
+
+static int
+is_dod_package(Solvable *s)
+{
+  const char *str = solvable_lookup_str(s, buildservice_id);
+  return str && !strcmp(str, "dod") ? 1 : 0;
+}
+
+static Solvable *
+find_corresponding_dod(Solvable *s)
+{
+  Repo *repo = s->repo;
+  Id p2;
+  Solvable *s2;
+
+  if (!repo)
+    return 0;
+  FOR_REPO_SOLVABLES(repo, p2, s2)
+    {
+      if (s->name == s2->name && s->evr == s2->evr && s->arch == s2->arch && s != s2 && is_dod_package(s2))
+       return s2;
+    }
+  return 0;
+}
+
+struct scc_data {
+  Id *edata;
+  Id *vedge;
+  Queue *sccs;
+  int *stack;
+  int nstack;
+  int *low;
+  int idx;
+};
+
+static void
+scc_collect(struct scc_data *scc, int node)
+{
+  int *low = scc->low;
+  Id *e;
+  queue_push(scc->sccs, node);
+  low[node] = -1;
+  for (e = scc->edata + scc->vedge[node]; *e; e++)
+    if (*e != -1 && low[*e] > 0)
+      scc_collect(scc, *e);
+}
+
+/* Tarjan's SCC algorithm */
+static int
+scc_visit(struct scc_data *scc, int node)
+{
+  int l, myidx, *low = scc->low, nontrivial = 0;
+  Id *e;
+  low[node] = myidx = scc->idx++;
+  for (e = scc->edata + scc->vedge[node]; *e; e++)
+    {
+      if (*e == -1 || *e == node)
+       continue;
+      if (!(l = low[*e]))
+       l = scc_visit(scc, *e);
+      if (l > 0)
+       nontrivial = 1;
+      if (l > 0 && l < low[node])
+       low[node] = l;
+    }
+  if (low[node] != myidx)
+    return low[node];
+  low[node] = -1;
+  if (nontrivial)
+    {
+      scc_collect(scc, node);
+      queue_push(scc->sccs, 0);
+    }
+  return -1;
+}
+
+static void
+find_sccs(Queue *edata, Queue *vedge, Queue *sccs)
+{
+  struct scc_data scc;
+  int i;
+  scc.edata = edata->elements;
+  scc.vedge = vedge->elements;
+  scc.sccs = sccs;
+  scc.low = solv_calloc(vedge->count, sizeof(int));
+  scc.idx = 1;
+  for (i = 1; i < vedge->count; i++)
+    if (!scc.edata[vedge->elements[i]])
+      scc.low[i] = -1;
+  for (i = 1; i < vedge->count; i++)
+    if (!scc.low[i])
+      scc_visit(&scc, i);
+  solv_free(scc.low);
+}
+
+
 MODULE = BSSolv                PACKAGE = BSSolv
 
 void
 depsort(HV *deps, SV *mapp, SV *cycp, ...)
+    ALIAS:
+       depsort2 = 1
     PPCODE:
        {
            int i, j, k, cy, cycstart, nv;
+           int pkgstart = 3;
            SV *sv, **svp;
+           SV *pkg2srcp = 0;
            Id id, *e;
            Id *mark;
            char **names;
+           char **depnames;
            Hashtable ht;
            Hashval h, hh, hm;
            HV *mhv = 0;
+           HV *pkg2srchv = 0;
 
            Queue edata;
            Queue vedge;
            Queue todo;
            Queue cycles;
+           Map edgeunifymap;
+           int didsccs = 0;
 
-           if (items == 3)
+           if (ix)
+             {
+               /* called as depsort2 */
+               if (items < 4)
+                 XSRETURN_EMPTY; /* nothing to sort */
+               pkgstart = 4;
+               pkg2srcp = cycp;
+               cycp = ST(3);
+             }
+           if (items == pkgstart)
              XSRETURN_EMPTY; /* nothing to sort */
-           if (items == 4)
+           if (items == pkgstart + 1)
              {
                /* only one item */
-               char *s = SvPV_nolen(ST(3));
+               char *s = SvPV_nolen(ST(pkgstart));
                EXTEND(SP, 1);
                sv = newSVpv(s, 0);
                PUSHs(sv_2mortal(sv));
                XSRETURN(1); /* nothing to sort */
              }
 
+           if (pkg2srcp && SvROK(pkg2srcp) && SvTYPE(SvRV(pkg2srcp)) == SVt_PVHV)
+             pkg2srchv = (HV *)SvRV(pkg2srcp);
+
            if (mapp && SvROK(mapp) && SvTYPE(SvRV(mapp)) == SVt_PVHV)
              mhv = (HV *)SvRV(mapp);
 
@@ -3528,9 +5406,11 @@ depsort(HV *deps, SV *mapp, SV *cycp, ...)
 
            hm = mkmask(items);
            ht = solv_calloc(hm + 1, sizeof(*ht));
-           names = solv_calloc(items, sizeof(char *));
+           names = depnames = solv_calloc(items, sizeof(char *));
+
+           /* create pkgname -> edge hash, store edge -> pkgname data */
            nv = 1;
-           for (i = 3; i < items; i++)
+           for (i = pkgstart; i < items; i++)
              {
                char *s = SvPV_nolen(ST(i));
                h = strhash(s) & hm;
@@ -3548,14 +5428,40 @@ depsort(HV *deps, SV *mapp, SV *cycp, ...)
                names[id] = s;
              }
 
+           if (pkg2srchv)
+             {
+               /* redo the hash with src names instead of pkg names */
+               depnames = solv_calloc(nv, sizeof(char *));
+               memset(ht, 0, (hm + 1) * sizeof(*ht));
+               for (i = 1; i < nv; i++)
+                 {
+                   char *s = names[i];
+                   svp = hv_fetch(pkg2srchv, s, strlen(s), 0);
+                   if (svp)
+                     {
+                       char *ns = SvPV_nolen(*svp);
+                       if (ns)
+                         s = ns;
+                     }
+                   depnames[i] = s;
+                   h = strhash(s) & hm;
+                   hh = HASHCHAIN_START;
+                   while ((id = ht[h]) != 0)
+                     h = HASHCHAIN_NEXT(h, hh, hm);
+                   ht[h] = i;
+                 }
+             }
+
            /* we now know all vertices, create edges */
            queue_push(&vedge, 0);
            queue_push(&edata, 0);
+           map_init(&edgeunifymap, nv);
            for (i = 1; i < nv; i++)
              {
+               int edgestart = edata.count;
                svp = hv_fetch(deps, names[i], strlen(names[i]), 0);
                sv = svp ? *svp : 0;
-               queue_push(&vedge, edata.count);
+               queue_push(&vedge, edgestart);
                if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
                  {
                    AV *av = (AV *)SvRV(sv);
@@ -3587,20 +5493,35 @@ depsort(HV *deps, SV *mapp, SV *cycp, ...)
                        hh = HASHCHAIN_START;
                        while ((id = ht[h]) != 0)
                          {
-                           if (!strcmp(names[id], s))
-                             break;
+                           if (!strcmp(depnames[id], s))
+                             {
+                               if (id != i && !MAPTST(&edgeunifymap, id))
+                                 {
+                                   MAPSET(&edgeunifymap, id);
+                                   queue_push(&edata, id);
+                                 }
+                               if (names == depnames)
+                                 break;        /* no other entry with same name */
+                             }
                            h = HASHCHAIN_NEXT(h, hh, hm);
                          }
-                       if (!id)
-                         continue;     /* not known, ignore */
-                       if (id == i)
-                         continue;     /* no self edge */
-                       queue_push(&edata, id);
                      }
                  }
-               queue_push(&edata, 0);
+               for (j = edgestart; j < edata.count; j++)
+                 {
+#ifdef MAPCLR_AT
+                   MAPCLR_AT(&edgeunifymap, edata.elements[j]);
+#else
+                   MAPCLR(&edgeunifymap, edata.elements[j]);
+#endif
+                 }
+               queue_push(&edata, 0);  /* terminate edge array */
              }
+           /* free no longer needed stuff */
+           map_free(&edgeunifymap);
            solv_free(ht);
+           if (depnames != names)
+             depnames = solv_free(depnames);
 
            if (0)
              {
@@ -3660,6 +5581,13 @@ depsort(HV *deps, SV *mapp, SV *cycp, ...)
                    continue;
                  }
                /* oh no, we found a cycle, record and break it */
+               if (depsortsccs && !didsccs && cycp)
+                 {
+                   /* use Tarjan's SCC algorithm */
+                   find_sccs(&edata, &vedge, &cycles);
+                   queue_push(&cycles, 0);
+                   didsccs = cycles.count;
+                 }
                cy = cycles.count;
                for (j = todo.count - 1; j >= 0; j--)
                  if (todo.elements[j] == -i)
@@ -3695,7 +5623,10 @@ depsort(HV *deps, SV *mapp, SV *cycp, ...)
                todo.count = cycstart + 1;
              }
 
-           /* recored cycles */
+           if (didsccs && depsortsccs != 2)
+             queue_truncate(&cycles, didsccs - 1);
+
+           /* record cycles */
            if (cycles.count && cycp && SvROK(cycp) && SvTYPE(SvRV(cycp)) == SVt_PVAV)
              {
                AV *av = (AV *)SvRV(cycp);
@@ -3721,16 +5652,25 @@ depsort(HV *deps, SV *mapp, SV *cycp, ...)
        }
 
 int
+setdepsortsccs(int flag)
+    CODE:
+       depsortsccs = flag;
+       RETVAL = flag;
+    OUTPUT:
+       RETVAL
+
+int
 setgenmetaalgo(int algo)
     CODE:
-      if (algo < 0)
-          algo = 1;
-      if (algo > 1)
-          croak("BSSolv::setgenmetaalgo: unsupported algo %d\n", algo);
-      genmetaalgo = algo;
-      RETVAL = algo;
+       if (algo < 0)
+           algo = 1;
+       if (algo > 1)
+           croak("BSSolv::setgenmetaalgo: unsupported algo %d\n", algo);
+       genmetaalgo = algo;
+       RETVAL = algo;
     OUTPUT:
-      RETVAL
+       RETVAL
+
 
 void
 gen_meta(AV *subp, ...)
@@ -3817,7 +5757,7 @@ gen_meta(AV *subp, ...)
                  }
                if (cycle)
                  {
-                   lp->killed = 1; /* killed because line includes a subpackage */
+                   lp->killed = 1;     /* killed because line includes a subpackage */
                    if (cycle > 1)      /* ignore self cycles */
                      queue_push(&cycles, i);
                  }
@@ -3855,15 +5795,16 @@ gen_meta(AV *subp, ...)
                      }
                    if (!id)
                      {
-                       int l = strlen(s);
-                       cycledata = solv_extend(cycledata, cycledatalen, l + 1, 1, 255);
-                       ht[h] = cycledatalen;   /* point to name */
-                       strcpy(cycledata + cycledatalen, s);
-                       cycledatalen += l + 1;
+                       int l = strlen(s);
+                       cycledata = solv_extend(cycledata, cycledatalen, l + 1, 1, 255);
+                       ht[h] = cycledatalen;   /* point to name */
+                       strcpy(cycledata + cycledatalen, s);
+                       cycledatalen += l + 1;
                      }
                    if (se)
                      *se = '/';
                  }
+
                for (i = 0, lp = lines; i < nlines; i++, lp++)
                  {
                    if (!lp->nslash)
@@ -3886,7 +5827,7 @@ gen_meta(AV *subp, ...)
                          *s2 = '/';
                          if (id)
                            {
-                             lp->killed = 2;  /* killed because it containes a cycle package */
+                             lp->killed = 2;   /* killed because it containes a cycle package */
                              break;
                            }
                          lo = s2 + 1;
@@ -3902,9 +5843,7 @@ gen_meta(AV *subp, ...)
                        h = HASHCHAIN_NEXT(h, hh, hm);
                      }
                    if (id)
-                     {
-                       lp->killed = 2; /* killed because it containes a cycle package */
-                     }
+                     lp->killed = 2;   /* killed because it containes a cycle package */
                  }
                solv_free(ht);
                cycledata = solv_free(cycledata);
@@ -3921,7 +5860,7 @@ gen_meta(AV *subp, ...)
              {
                if (lp->killed)
                  {
-               if (genmetaalgo == 0 || lp->killed != 2)
+                   if (genmetaalgo == 0 || lp->killed != 2)
                      continue;
                  }
                s = lp->l;
@@ -3964,6 +5903,66 @@ gen_meta(AV *subp, ...)
            solv_free(lines);
        }
 
+void
+add_meta(AV *new_meta, SV *sv, const char *bin, const char *packid = 0)
+    PPCODE:
+       {
+           const char *p, *np;
+           char *buf;
+           size_t l, bufl, binl, packidl;
+           int first = 1;
+           if (SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV) {
+               AV *av = (AV *)SvRV(sv);
+               SV **svp = av_fetch(av, 0, 0);
+               sv = svp ? *svp : 0;
+           }
+           if (!sv)
+               XSRETURN_EMPTY;
+           p = SvPV_nolen(sv);
+           binl = strlen(bin);
+           bufl = binl + 256;
+           buf = malloc(bufl);
+           if (!buf) {
+               croak("out of mem\n");
+               XSRETURN_EMPTY;
+           }
+           packidl = packid ? strlen(packid) : 0;
+           for (;;) {
+               np = strchr(p, '\n');
+               l = np ? np - p : strlen(p);
+               if (l > 34) {
+                   if (l + binl + 1 + 1 > bufl) {
+                       bufl = l + binl + 256;
+                       buf = realloc(buf, bufl);
+                       if (!buf) {
+                           croak("out of mem\n");
+                           XSRETURN_EMPTY;
+                       }
+                   }
+                   strncpy(buf, p, 34);
+                   strcpy(buf + 34, bin);
+                   buf[34 + binl] = '/';
+                   strncpy(buf + 34 + binl + 1, p + 34, l - 34);
+                   l += binl + 1;
+                   buf[l] = 0;
+                   if (first) {
+                       if (packidl && l > packidl + 1 && buf[l - packidl - 1] == '/' && !strcmp(buf + l - packidl, packid)) {
+                           free(buf);
+                           XSRETURN_EMPTY;
+                       }
+                       l = 34 + binl;
+                       buf[l] = 0;
+                       first = 0;
+                   }
+                   av_push(new_meta, newSVpvn(buf, l));
+               }
+               if (!np)
+                   break;
+               p = np + 1;
+           }
+           free(buf);
+       }
+
 SV *
 thawcache(SV *sv)
     CODE:
@@ -4293,6 +6292,9 @@ new(char *packname = "BSSolv::pool")
            buildservice_dodurl = pool_str2id(pool, "buildservice:dodurl", 1);
            expander_directdepsend = pool_str2id(pool, "-directdepsend--", 1);
            buildservice_dodcookie = pool_str2id(pool, "buildservice:dodcookie", 1);
+           buildservice_dodresources = pool_str2id(pool, "buildservice:dodresources", 1);
+           buildservice_annotation = pool_str2id(pool, "buildservice:annotation", 1);
+           buildservice_modules = pool_str2id(pool, "buildservice:modules", 1);
            pool_freeidhashes(pool);
            RETVAL = pool;
        }
@@ -4369,6 +6371,7 @@ repofrombins(BSSolv::pool pool, char *name, char *dir, ...)
                  continue;
                if (strcmp(s + sl - 4, ".rpm")
                     && strcmp(s + sl - 4, ".deb")
+                    && (sl < 10 || strcmp(s + sl - 10, ".obsbinlnk"))
 #ifdef ARCH_ADD_WITH_PKGID
                     && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
                     && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
@@ -4392,14 +6395,16 @@ repofrombins(BSSolv::pool pool, char *name, char *dir, ...)
        RETVAL
 
 BSSolv::repo
-repofromdata(BSSolv::pool pool, char *name, HV *rhv)
+repofromdata(BSSolv::pool pool, char *name, SV *rv)
     CODE:
        {
            Repo *repo;
            Repodata *data;
+           if (!SvROK(rv) || (SvTYPE(SvRV(rv)) != SVt_PVHV && SvTYPE(SvRV(rv)) != SVt_PVAV))
+               croak("BSSolv::pool::repofromdata: rv is not a HASH or ARRAY reference");
            repo = repo_create(pool, name);
            data = repo_add_repodata(repo, 0);
-           data2solvables(repo, data, rhv);
+           data2solvables(repo, data, SvRV(rv), 0);
            if (name && !strcmp(name, "/external/"))
              repodata_set_void(data, SOLVID_META, buildservice_external);
            repo_internalize(repo);
@@ -4409,7 +6414,7 @@ repofromdata(BSSolv::pool pool, char *name, HV *rhv)
        RETVAL
 
 void
-createwhatprovides(BSSolv::pool pool)
+createwhatprovides(BSSolv::pool pool, int unorderedrepos = 0)
     CODE:
        if (pool->considered)
          {
@@ -4417,7 +6422,7 @@ createwhatprovides(BSSolv::pool pool)
            solv_free(pool->considered);
          }
        pool->considered = solv_calloc(sizeof(Map), 1);
-       create_considered(pool, 0, pool->considered);
+       create_considered(pool, 0, pool->considered, unorderedrepos);
        pool_createwhatprovides(pool);
 
 void
@@ -4430,7 +6435,7 @@ whatprovides(BSSolv::pool pool, char *str)
     PPCODE:
        {
            Id p, pp, id;
-           id = dep2id(pool, str);
+           id = testcase_str2dep(pool, str);
            if (id)
              FOR_PROVIDES(p, pp, id)
                XPUSHs(sv_2mortal(newSViv((IV)p)));
@@ -4443,7 +6448,7 @@ whatrequires(BSSolv::pool pool, char *str)
            Id p, id;
            Id *pp;
            Solvable *s;
-           id = dep2id(pool, str);
+           id = testcase_str2dep(pool, str);
            if (id)
              {
                for (p = 2; p < pool->nsolvables; p++)
@@ -4498,6 +6503,20 @@ pkg2name(BSSolv::pool pool, int p)
        RETVAL
 
 const char *
+pkg2evr(BSSolv::pool pool, int p)
+    CODE:
+       RETVAL = pool_id2str(pool, pool->solvables[p].evr);
+    OUTPUT:
+       RETVAL
+
+const char *
+pkg2arch(BSSolv::pool pool, int p)
+    CODE:
+       RETVAL = pool_id2str(pool, pool->solvables[p].arch);
+    OUTPUT:
+       RETVAL
+
+const char *
 pkg2srcname(BSSolv::pool pool, int p)
     CODE:
        if (solvable_lookup_void(pool->solvables + p, SOLVABLE_SOURCENAME))
@@ -4583,6 +6602,33 @@ pkg2checksum(BSSolv::pool pool, int p)
        RETVAL
 
 int
+pkg2inmodule(BSSolv::pool pool, int p)
+    CODE:
+       RETVAL = solvable_lookup_type(pool->solvables + p, buildservice_modules) != 0;
+    OUTPUT:
+       RETVAL
+
+void
+pkg2modules(BSSolv::pool pool, int p)
+    PPCODE:
+       {
+         Solvable *s = pool->solvables + p;
+         Queue modules;
+         int i;
+         queue_init(&modules);
+         solvable_lookup_idarray(s, buildservice_modules, &modules);
+         if (!modules.count && !is_dod_package(s))
+           {
+             Solvable *s2 = find_corresponding_dod(s);
+             if (s2)
+               solvable_lookup_idarray(s2, buildservice_modules, &modules);
+           }
+         for (i = 0; i < modules.count; i++)
+           XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, modules.elements[i]), 0)));
+         queue_free(&modules);
+       }
+
+int
 verifypkgchecksum(BSSolv::pool pool, int p, char *path)
     CODE:
        {
@@ -4667,10 +6713,34 @@ pkg2data(BSSolv::pool pool, int p)
            ss = solvable_lookup_str(s, buildservice_id);
            if (ss)
              (void)hv_store(RETVAL, "id", 2, newSVpv(ss, 0), 0);
+           ss = solvable_lookup_str(s, buildservice_annotation);
+           if (ss)
+             (void)hv_store(RETVAL, "annotation", 10, newSVpv(ss, 0), 0);
+           if (solvable_lookup_type(s, buildservice_modules))
+             {
+               Queue modules;
+               int i;
+               queue_init(&modules);
+               solvable_lookup_idarray(s, buildservice_modules, &modules);
+               if (modules.count)
+                 {
+                   AV *av = newAV();
+                   for (i = 0; i < modules.count; i++)
+                     av_push(av, newSVpv(pool_id2str(pool, modules.elements[i]), 0));
+                   (void)hv_store(RETVAL, "modules", 7, newRV_noinc((SV*)av), 0);
+                 }
+             }
        }
     OUTPUT:
        RETVAL
 
+const char *
+pkg2annotation(BSSolv::pool pool, int p)
+    CODE:
+       RETVAL = solvable_lookup_str(pool->solvables + p, buildservice_annotation);
+    OUTPUT:
+       RETVAL
+
 void
 repos(BSSolv::pool pool)
     PPCODE:
@@ -4789,6 +6859,32 @@ preparehashes(BSSolv::pool pool, char *prp, SV *gctxprpnotreadysv = 0)
        }
 
 void
+setmodules(BSSolv::pool pool, AV *modulesav)
+    CODE:
+       {
+         SSize_t i, n = av_len(modulesav);
+         pool->appdata = solv_free(pool->appdata);
+         if (n >= 0 && n < 1000000)
+           {
+             Id *modules = pool->appdata = solv_calloc(n + 2, sizeof(Id));
+             for (i = 0; i <= n; i++)
+               modules[i] = pool_str2id(pool, avlookupstr(modulesav, i), 1);
+             modules[i] = 0;
+           }
+       }
+
+void
+getmodules(BSSolv::pool pool)
+    PPCODE:
+       if (pool->appdata)
+         {
+           Id *modules = pool->appdata;
+           int i;
+           for (i = 0; modules[i]; i++)
+             XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, modules[i]), 0)));
+         }
+
+void
 DESTROY(BSSolv::pool pool)
     CODE:
         if (pool->considered)
@@ -4796,6 +6892,7 @@ DESTROY(BSSolv::pool pool)
            map_free(pool->considered);
            pool->considered = solv_free(pool->considered);
          }
+       pool->appdata = solv_free(pool->appdata);
        pool_free(pool);
 
 
@@ -4804,6 +6901,24 @@ DESTROY(BSSolv::pool pool)
 MODULE = BSSolv                PACKAGE = BSSolv::repo          PREFIX = repo
 
 void
+freerepo(BSSolv::repo repo)
+    CODE:
+       {
+         repo_free(repo, 1);
+       }
+
+void
+allpackages(BSSolv::repo repo)
+    PPCODE:
+       {
+           Id p;
+           Solvable *s;
+           EXTEND(SP, repo->nsolvables);
+           FOR_REPO_SOLVABLES(repo, p, s)
+             PUSHs(sv_2mortal(newSViv(p)));
+       }
+
+void
 pkgnames(BSSolv::repo repo)
     PPCODE:
        {
@@ -4811,8 +6926,8 @@ pkgnames(BSSolv::repo repo)
            Id p;
            Solvable *s;
            Map c;
-       
-           create_considered(pool, repo, &c);
+
+           create_considered(pool, repo, &c, 0);
            EXTEND(SP, 2 * repo->nsolvables);
            FOR_REPO_SOLVABLES(repo, p, s)
              {
@@ -4835,7 +6950,7 @@ pkgpaths(BSSolv::repo repo)
            const char *str;
            unsigned int medianr;
        
-           create_considered(pool, repo, &c);
+           create_considered(pool, repo, &c, 0);
            EXTEND(SP, 2 * repo->nsolvables);
            FOR_REPO_SOLVABLES(repo, p, s)
              {
@@ -4951,7 +7066,7 @@ updatefrombins(BSSolv::repo repo, char *dir, ...)
                      continue;
                    h = strhash(str) & hm;
                    hh = HASHCHAIN_START;
-                   while ((id = ht[h]) != 0)
+                   while (ht[h])
                      h = HASHCHAIN_NEXT(h, hh, hm);
                    ht[h] = p;
                  }
@@ -4967,12 +7082,14 @@ updatefrombins(BSSolv::repo repo, char *dir, ...)
                  continue;
                if (strcmp(s + sl - 4, ".rpm")
                     && strcmp(s + sl - 4, ".deb")
+                    && (sl < 10 || strcmp(s + sl - 10, ".obsbinlnk"))
 #ifdef ARCH_ADD_WITH_PKGID
                     && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.gz"))
                     && (sl < 11 || strcmp(s + sl - 11, ".pkg.tar.xz"))
                     && (sl < 12 || strcmp(s + sl - 12, ".pkg.tar.zst"))
 #endif
                   )
+                 continue;
                if (sl > 10 && !strcmp(s + sl - 10, ".patch.rpm"))
                  continue;
                if (sl > 10 && !strcmp(s + sl - 10, ".nosrc.rpm"))
@@ -4986,12 +7103,12 @@ updatefrombins(BSSolv::repo repo, char *dir, ...)
                    const char *str = solvable_lookup_str(pool->solvables + id, buildservice_id);
                    if (!strcmp(str, sid))
                      {
-                       /* check location */
+                       /* check location (unless it's a obsbinlnk where the location comes from the content) */
                        unsigned int medianr;
                        str = solvable_get_location(pool->solvables + id, &medianr);
                        if (str[0] == '.' && str[1] == '/')
                          str += 2;
-                       if (!strcmp(str, s))
+                       if (!strcmp(str, s) || (sl >= 10 && !strcmp(s + sl - 10, ".obsbinlnk")))
                          break;
                      }
                    h = HASHCHAIN_NEXT(h, hh, hm);
@@ -5049,6 +7166,139 @@ updatefrombins(BSSolv::repo repo, char *dir, ...)
     OUTPUT:
        RETVAL
 
+void
+modulesfrombins(BSSolv::repo repo, ...)
+    PPCODE:
+       {
+           Pool *pool = repo->pool;
+           Hashtable ht;
+           Hashval h, hh, hm;
+           Queue modules;
+           Queue collectedmodules;
+            Id p, lastid;
+           Solvable *s;
+           int i, j;
+
+           queue_init(&collectedmodules);
+           queue_init(&modules);
+           hm = mkmask(2 * repo->nsolvables + 1);
+           ht = solv_calloc(hm + 1, sizeof(*ht));
+           FOR_REPO_SOLVABLES(repo, p, s)
+             {
+               const char *bsid = solvable_lookup_str(s, buildservice_id);
+               if (!bsid)
+                 continue;
+               if (!strcmp(bsid, "dod"))
+                 h = s->name + s->evr * 37 + s->arch * 129;
+               else
+                 h = strhash(bsid);
+               h &= hm;
+               hh = HASHCHAIN_START;
+               while (ht[h])
+                 h = HASHCHAIN_NEXT(h, hh, hm);
+               ht[h] = p;
+             }
+
+           for (i = 1; i + 1 < items; i += 2)
+             {
+               const char *bsid = SvPV_nolen(ST(i + 1));
+               h = strhash(bsid) & hm;
+               hh = HASHCHAIN_START;
+               while ((p = ht[h]) != 0)
+                 {
+                   const char *bsid2 = solvable_lookup_str(pool->solvables + p, buildservice_id);
+                   if (!strcmp(bsid, bsid2))
+                     break;
+                   h = HASHCHAIN_NEXT(h, hh, hm);
+                 }
+               if (!p)
+                 continue;
+               s = pool->solvables + p;
+               h = (s->name + s->evr * 37 + s->arch * 129) & hm;
+               hh = HASHCHAIN_START;
+               while ((p = ht[h]) != 0)
+                 {
+                   Solvable *s2 = pool->solvables + p;
+                   if (s->name == s2->name && s->evr == s2->evr && s->arch == s2->arch)
+                     {
+                       lastid = collectedmodules.count ? collectedmodules.elements[collectedmodules.count - 1] : 0;
+                       solvable_lookup_idarray(s2, buildservice_modules, &modules);
+                       for (j = 0; j < modules.count; j++)
+                         if (modules.elements[j] != lastid)
+                           queue_push(&collectedmodules, modules.elements[j]);
+                     }
+                   h = HASHCHAIN_NEXT(h, hh, hm);
+                 }
+             }
+           solv_free(ht);
+           queue_free(&modules);
+           /* sort and unify */
+           solv_sort(collectedmodules.elements, collectedmodules.count, sizeof(Id), unifymodules_cmp, 0);
+           lastid = -1;
+           for (i = 0; i < collectedmodules.count; i++)
+             {
+               if (collectedmodules.elements[i] == lastid)
+                 continue;
+               lastid = collectedmodules.elements[i];
+               XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, lastid), 0)));
+             }
+           queue_free(&collectedmodules);
+       }
+
+void
+missingmodules(BSSolv::repo repo, ...)
+    PPCODE:
+       {
+           Pool *pool = repo->pool;
+            Id p, *pp, *modules, id, req, lastid1, lastid2;
+           Solvable *s;
+           Queue missingq;
+           int i, missing;
+
+           queue_init(&missingq);
+           if (pool->appdata && ((Id *)pool->appdata)[0] && has_keyname(repo, buildservice_modules))
+             {
+               FOR_REPO_SOLVABLES(repo, p, s)
+                 {
+                   if (s->name != buildservice_modules || s->arch != ARCH_SRC || !s->requires)
+                     continue;
+                   id = s->repo->idarraydata[s->provides];
+                   for (modules = pool->appdata; *modules; modules++)
+                     if (id == *modules)
+                       break;
+                   if (!*modules)
+                     continue;
+                   missing = 0;
+                   for (pp = s->repo->idarraydata + s->requires; (req = *pp) != 0; pp++)
+                     if (!match_modules_req(pool, req))
+                       {
+                         missing = 1;
+                         queue_push2(&missingq, id, req);
+                       }
+                   if (!missing)       /* we're good */
+                     queue_push2(&missingq, id, 0);
+                 }
+               /* sort and unify */
+               solv_sort(missingq.elements, missingq.count / 2, sizeof(Id) * 2, missingmodules_cmp, 0);
+               lastid1 = lastid2 = -1;
+               for (i = 0; i < missingq.count; i += 2)
+                 {
+                   if (missingq.elements[i] == lastid1 && missingq.elements[i + 1] == lastid2)
+                     continue;
+                   if (missingq.elements[i] != lastid1)
+                     {
+                       lastid1 = missingq.elements[i];
+                       lastid2 = missingq.elements[i + 1];
+                     }
+                   if (!lastid2)
+                     continue;
+                   lastid2 = missingq.elements[i + 1];
+                   XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, lastid1), 0)));
+                   XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, lastid2), 0)));
+                 }
+               queue_free(&missingq);
+             }
+       }
 
 void
 getpathid(BSSolv::repo repo)
@@ -5062,6 +7312,11 @@ getpathid(BSSolv::repo repo)
                unsigned int medianr;
                const char *str;
                str = solvable_get_location(s, &medianr);
+               /* We need to special case .obsbinlink here where the location
+                * points back into the package. We currently assume that
+                * the name in the full tree is always <name>.obsbinlnk */
+               if (!strncmp(str, "../", 3))
+                 str = pool_tmpjoin(repo->pool, pool_id2str(repo->pool, s->name), ".obsbinlnk", 0);
                PUSHs(sv_2mortal(newSVpv(str, 0)));
                str = solvable_lookup_str(s, buildservice_id);
                PUSHs(sv_2mortal(newSVpv(str, 0)));
@@ -5097,6 +7352,21 @@ dodcookie(BSSolv::repo repo)
        RETVAL
 
 void
+dodresources(BSSolv::repo repo)
+    PPCODE:
+       {
+         Pool *pool = repo->pool;
+         Queue dodresources;
+         int i;
+
+         queue_init(&dodresources);
+         repo_lookup_idarray(repo, SOLVID_META, buildservice_dodresources, &dodresources);
+         for (i = 0; i < dodresources.count; i++)
+           XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, dodresources.elements[i]), 0)));
+         queue_free(&dodresources);
+       }
+
+void
 updatedoddata(BSSolv::repo repo, HV *rhv = 0)
     CODE:
        {
@@ -5113,28 +7383,131 @@ updatedoddata(BSSolv::repo repo, HV *rhv = 0)
            data = repo_add_repodata(repo, REPO_REUSE_REPODATA);
            repodata_unset(data, SOLVID_META, buildservice_dodurl);
            repodata_unset(data, SOLVID_META, buildservice_dodcookie);
+           repodata_unset(data, SOLVID_META, buildservice_dodresources);
            /* add new data */
            if (rhv)
-               data2solvables(repo, data, rhv);
+               data2solvables(repo, data, (SV *)rhv, 1);
            repo_internalize(repo);
        }
 
+void
+setpriority(BSSolv::repo repo, int priority)
+    PPCODE:
+       repo->priority = priority;
+
+int
+mayhavemodules(BSSolv::repo repo)
+    CODE:
+       RETVAL = has_keyname(repo, buildservice_modules);
+    OUTPUT:
+       RETVAL
+
+void
+getmodules(BSSolv::repo repo)
+    PPCODE:
+       if (has_keyname(repo, buildservice_modules))
+         {
+           Pool *pool = repo->pool;
+           Id p, lastid = -1;
+           Solvable *s;
+           Queue collectedmodules;
+           int i;
+
+           queue_init(&collectedmodules);
+           FOR_REPO_SOLVABLES(repo, p, s)
+             if (s->name == buildservice_modules && s->arch == ARCH_SRC && s->repo->idarraydata[s->provides])
+               queue_push(&collectedmodules, s->repo->idarraydata[s->provides]);
+           if (!collectedmodules.count)
+             {
+               Queue modules;
+               queue_init(&modules);
+               FOR_REPO_SOLVABLES(repo, p, s)
+                 {
+                   solvable_lookup_idarray(pool->solvables + p, buildservice_modules, &modules);
+                   for (i = 0; i < modules.count; i++)
+                     {
+                       if (modules.elements[i] == lastid)
+                         continue;
+                       lastid = modules.elements[i];
+                       queue_push(&collectedmodules, modules.elements[i]);
+                     }
+                 }
+               queue_free(&modules);
+             }
+           /* sort and unify */
+           solv_sort(collectedmodules.elements, collectedmodules.count, sizeof(Id), unifymodules_cmp, 0);
+           lastid = -1;
+           for (i = 0; i < collectedmodules.count; i++)
+             {
+               if (collectedmodules.elements[i] == lastid)
+                 continue;
+               lastid = collectedmodules.elements[i];
+               XPUSHs(sv_2mortal(newSVpv(pool_id2str(pool, lastid), 0)));
+             }
+           queue_free(&collectedmodules);
+         }
+
+void
+getdodblobs(BSSolv::repo repo)
+    PPCODE:
+       {
+           Pool *pool = repo->pool;
+           int i;
+           Id p;
+           Solvable *s;
+           Stringpool ss;
+           stringpool_init_empty(&ss);
+           FOR_REPO_SOLVABLES(repo, p, s)
+             {
+               const char *str = solvable_lookup_str(s, buildservice_id);
+               unsigned int medianr;
+               const char *s, *se;
+               if (!str || strcmp(str, "dod") != 0)
+                 continue;
+               s = solvable_get_location(pool->solvables + p, &medianr);
+               if ((s = strrchr(s, '?')) == 0)
+                 continue;
+               for (++s; s; s = se ? se + 1 : 0)
+                 {
+                   se = strchr(s, ',');
+                   if (se)
+                     stringpool_strn2id(&ss, s, se - s, 1);
+                   else
+                     stringpool_str2id(&ss, s, 1);
+                 }
+             }
+           for (i = 2; i < ss.nstrings; i++)
+             {
+               XPUSHs(sv_2mortal(newSVpv(stringpool_id2str(&ss, i), 0)));
+             }
+           stringpool_free(&ss);
+       }
 
-MODULE = BSSolv                PACKAGE = BSSolv::expander      PREFIX = expander
 
+MODULE = BSSolv                PACKAGE = BSSolv::expander      PREFIX = expander
 
 BSSolv::expander
 new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
     CODE:
        {
            SV *sv, **svp;
-           char *str;
-           int i, neg;
-           Id id, id2;
+           char *str, *p;
+           int i;
+           Id id;
            Expander *xp;
-
-           xp = calloc(sizeof(Expander), 1);
-           xp->pool = pool;
+           Queue preferpos;
+           Queue preferneg;
+           Queue ignore;
+           Queue conflict;
+           Queue fileprovides;
+           int debug = 0;
+           int options = 0;
+
+           queue_init(&preferpos);
+           queue_init(&preferneg);
+           queue_init(&ignore);
+           queue_init(&conflict);
+           queue_init(&fileprovides);
            svp = hv_fetch(config, "prefer", 6, 0);
            sv = svp ? *svp : 0;
            if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVAV)
@@ -5149,37 +7522,10 @@ new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
                    str = SvPV_nolen(sv);
                    if (!str)
                      continue;
-                   neg = 0;
                    if (*str == '-')
-                     {
-                       neg = 1;
-                       str++;
-                     }
-                   id = pool_str2id(pool, str, 1);
-                   id2 = 0;
-                   if ((str = strchr(str, ':')) != 0)
-                     id2 = pool_str2id(pool, str + 1, 1);
-                   if (neg)
-                     {
-                       MAPEXP(&xp->preferneg, id);
-                       MAPSET(&xp->preferneg, id);
-                       if (id2)
-                         {
-                           MAPEXP(&xp->prefernegx, id2);
-                           MAPSET(&xp->prefernegx, id2);
-                         }
-                     }
+                     queue_push(&preferneg, pool_str2id(pool, str + 1, 1));
                    else
-                     {
-                       queue_push(&xp->preferposq, id);
-                       MAPEXP(&xp->preferpos, id);
-                       MAPSET(&xp->preferpos, id);
-                       if (id2)
-                         {
-                           MAPEXP(&xp->preferposx, id2);
-                           MAPSET(&xp->preferposx, id2);
-                         }
-                     }
+                     queue_push(&preferpos, pool_str2id(pool, str, 1));
                  }
              }
            svp = hv_fetch(config, "ignoreh", 7, 0);
@@ -5188,7 +7534,6 @@ new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
              {
                HV *hv = (HV *)SvRV(sv);
                HE *he;
-
                hv_iterinit(hv);
                while ((he = hv_iternext(hv)) != 0)
                  {
@@ -5196,18 +7541,7 @@ new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
                    str = hv_iterkey(he, &strl);
                    if (!str)
                      continue;
-                
-                   id = pool_str2id(pool, str, 1);
-                   id2 = 0;
-                   if ((str = strchr(str, ':')) != 0)
-                     id2 = pool_str2id(pool, str + 1, 1);
-                   MAPEXP(&xp->ignored, id);
-                   MAPSET(&xp->ignored, id);
-                   if (id2)
-                     {
-                       MAPEXP(&xp->ignoredx, id2);
-                       MAPSET(&xp->ignoredx, id2);
-                     }
+                   queue_push(&ignore, pool_str2id(pool, str, 1));
                  }
              }
            svp = hv_fetch(config, "conflict", 8, 0);
@@ -5217,9 +7551,6 @@ new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
                AV *av = (AV *)SvRV(sv);
                for (i = 0; i <= av_len(av); i++)
                  {
-                   char *p;
-                   Id id2;
-
                    svp = av_fetch(av, i, 0);
                    if (!svp)
                      continue;
@@ -5234,46 +7565,28 @@ new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
                    str = p + 1;
                    while ((p = strchr(str, ',')) != 0)
                      {
-                       id2 = pool_strn2id(pool, str, p - str, 1);
-                       queue_push2(&xp->conflictsq, id, id2);
-                       MAPEXP(&xp->conflicts, id);
-                       MAPSET(&xp->conflicts, id);
-                       MAPEXP(&xp->conflicts, id2);
-                       MAPSET(&xp->conflicts, id2);
+                       queue_push2(&conflict, id, pool_strn2id(pool, str, p - str, 1));
                        str = p + 1;
                      }
-                   id2 = pool_str2id(pool, str, 1);
-                   queue_push2(&xp->conflictsq, id, id2);
-                   MAPEXP(&xp->conflicts, id);
-                   MAPSET(&xp->conflicts, id);
-                   MAPEXP(&xp->conflicts, id2);
-                   MAPSET(&xp->conflicts, id2);
+                   queue_push2(&conflict, id, pool_str2id(pool, str, 1));
                  }
              }
-           /* XXX: this modifies the pool, which is a bit unclean! */
            svp = hv_fetch(config, "fileprovides", 12, 0);
            sv = svp ? *svp : 0;
            if (sv && SvROK(sv) && SvTYPE(SvRV(sv)) == SVt_PVHV)
              {
                HV *hv = (HV *)SvRV(sv);
                I32 strl;
-               Queue q;
 
-               xp->havefileprovides = 1;
                hv_iterinit(hv);
-               queue_init(&q);
                while ((sv = hv_iternextsv(hv, &str, &strl)) != 0)
                  {
                    AV *av;
-                   Id p, pp;
-                   int havenew = 0;
+                   Id id2;
 
                    if (!SvROK(sv) || SvTYPE(SvRV(sv)) != SVt_PVAV)
                      continue;
                    id = pool_str2id(pool, str, 1);
-                   queue_empty(&q);
-                   FOR_PROVIDES(p, pp, id)
-                     queue_push(&q, p);
                    av = (AV *)SvRV(sv);
                    for (i = 0; i <= av_len(av); i++)
                      {
@@ -5285,44 +7598,48 @@ new(char *packname = "BSSolv::expander", BSSolv::pool pool, HV *config)
                        if (!str)
                          continue;
                        id2 = pool_str2id(pool, str, 0);
-                       FOR_PROVIDES(p, pp, id2)
+                       if (!id2)
+                         continue;
+                       if (id)
                          {
-                           int j;
-                           for (j = 0; j < q.count; j++)
-                             {
-                               if (q.elements[j] == p)
-                                 break;
-                               if (q.elements[j] > p)
-                                 {
-                                   queue_insert(&q, j, p);
-                                   havenew = 1;
-                                   break;
-                                 }
-                             }
-                           if (j == q.count)
-                             {
-                               queue_push(&q, p);
-                               havenew = 1;
-                             }
+                           queue_push(&fileprovides, id);      /* start name block */
+                           id = 0;
                          }
+                       queue_push(&fileprovides, id2);
                      }
-                   if (havenew)
-                     pool->whatprovides[id] = pool_queuetowhatprovides(pool, &q);
+                   if (id == 0)
+                     queue_push(&fileprovides, 0);     /* had at least one entry, finish name block */
                  }
-               queue_free(&q);
              }
+           options |= EXPANDER_OPTION_USERECOMMENDSFORCHOICES;
            svp = hv_fetch(config, "expandflags:ignoreconflicts", 27, 0);
            sv = svp ? *svp : 0;
            if (sv && SvTRUE(sv))
-             xp->ignoreconflicts = 1;
-           svp = hv_fetch(config, "expand_dbg", 10, 0);
+             options |= EXPANDER_OPTION_IGNORECONFLICTS;
+           svp = hv_fetch(config, "expandflags:dorecommends", 24, 0);
            sv = svp ? *svp : 0;
            if (sv && SvTRUE(sv))
-             xp->debug = 1;
-           sv = get_sv("Build::expand_dbg", FALSE);
+             options |= EXPANDER_OPTION_DORECOMMENDS;
+           svp = hv_fetch(config, "expandflags:dosupplements", 25, 0);
+           sv = svp ? *svp : 0;
            if (sv && SvTRUE(sv))
-             xp->debug = 1;
-        xp->userecommendsforchoices = 1;
+             options |= EXPANDER_OPTION_DOSUPPLEMENTS | EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES;
+           svp = hv_fetch(config, "expand_dbg", 10, 0);
+           sv = svp ? *svp : 0;
+           if (sv && SvOK(sv))
+             debug = SvIV(sv);
+           else
+             {
+               sv = get_sv("Build::expand_dbg", FALSE);
+               if (sv && SvOK(sv))
+                 debug = SvIV(sv);
+             }
+           xp = expander_create(pool, &preferpos, &preferneg, &ignore, &conflict, &fileprovides, debug, options);
+           queue_free(&preferpos);
+           queue_free(&preferneg);
+           queue_free(&ignore);
+           queue_free(&conflict);
+           queue_free(&fileprovides);
            RETVAL = xp;
        }
     OUTPUT:
@@ -5335,131 +7652,71 @@ expand(BSSolv::expander xp, ...)
        {
            Pool *pool;
            int i, nerrors;
-           Id id, who, conflbuf[16];
-           Queue revertignore, in, out, confl;
-           int oldignoreignore = xp->ignoreignore;
-           int ignoreignore = 0;
-           Map oldignored, oldignoredx;
-           int ignoremapssaved = 0;
-
-           queue_init(&revertignore);
+           Id id, who, indepbuf[64];
+           Queue ignoreq, in, out, indep;
+           int directdepsend = 0;
+           int options = 0;
+
+           queue_init(&ignoreq);
            queue_init(&in);
            queue_init(&out);
-           queue_init_buffer(&confl, conflbuf, sizeof(conflbuf)/sizeof(*conflbuf));
+           queue_init_buffer(&indep, indepbuf, sizeof(indepbuf)/sizeof(*indepbuf));
            pool = xp->pool;
            if (xp->debug)
              expander_dbg(xp, "expand args:");
            for (i = 1; i < items; i++)
              {
                char *s = SvPV_nolen(ST(i));
+               int deptype = DEPTYPE_REQUIRES;
+
                if (xp->debug)
                  expander_dbg(xp, " %s", s);
+               if (*s == '-' && s[1] == '-')
+                 {
+                   /* expand option */
+                   if (!strcmp(s, "--ignoreignore--"))
+                     options |= EXPANDER_OPTION_IGNOREIGNORE;
+                   else if (!strcmp(s, "--directdepsend--"))
+                     directdepsend = 1;
+                   else if (!strcmp(s, "--dorecommends--"))
+                     options |= EXPANDER_OPTION_DORECOMMENDS;
+                   else if (!strcmp(s, "--dosupplements--"))
+                     options |= EXPANDER_OPTION_DOSUPPLEMENTS | EXPANDER_OPTION_USESUPPLEMENTSFORCHOICES;
+                   else if (!strcmp(s, "--ignoreconflicts--"))
+                     options |= EXPANDER_OPTION_IGNORECONFLICTS;
+                   continue;
+                 }
                if (*s == '-')
                  {
-                   Id id;
-                   if (s[1] == '-' && !strcmp(s, "--ignoreignore--"))
-                     {
-                       ignoreignore = 1;
-                       continue;
-                     }
+                   /* ignore dependency */
                    id = pool_str2id(pool, s + 1, 1);
-                   if (id == expander_directdepsend)
-                     {
-                       queue_push(&in, id);
-                       continue;
-                     }
-                   queue_push(&revertignore, id);
+                   queue_push(&ignoreq, id);
+                   continue;
                  }
-               else if (*s == '!')
+               if (*s == '!')
                  {
-                   Id id = dep2id(pool, s + (s[1] == '!' ? 2 : 1));
-                   queue_push2(&confl, id, s[1] == '!' ? 1 : 0);
+                   deptype = DEPTYPE_CONFLICTS;
+                   s++;
+                   if (*s == '!')
+                     {
+                       deptype = DEPTYPE_OBSOLETES;
+                       s++;
+                     }
                  }
+               id = dep2id(pool, s);
+               if (deptype == DEPTYPE_REQUIRES && !directdepsend)
+                 queue_push(&in, id);
                else
-                 {
-                   Id id = dep2id(pool, s);
-                   queue_push(&in, id);
-                 }
+                 queue_push2(&indep, deptype, id);
              }
            if (xp->debug)
              expander_dbg(xp, "\n");
 
-           if (ignoreignore && revertignore.count)
-             {
-               /* bad: have direct ignores and project config ignores */
-               oldignored = xp->ignored;
-               oldignoredx = xp->ignoredx;
-               ignoremapssaved = 1;
-               /* clear project config maps */
-               memset(&xp->ignored, 0, sizeof(xp->ignored));
-               memset(&xp->ignoredx, 0, sizeof(xp->ignoredx));
-             }
-
-           if (revertignore.count)
-             {
-               /* mix direct ignores with ignores from project config */
-               int revertcnt = revertignore.count;
-               for (i = 0; i < revertcnt; i++)
-                 {
-                   const char *ss;
-                   id = revertignore.elements[i];
-                   MAPEXP(&xp->ignored, id);
-                   if (MAPTST(&xp->ignored, id))
-                     continue;
-                   MAPSET(&xp->ignored, id);
-                   queue_push(&revertignore, id);
-                   if ((ss = strchr(pool_id2str(pool, id), ':')) != 0)
-                     {
-                       id = pool_str2id(pool, ss + 1, 1);
-                       MAPEXP(&xp->ignoredx, id);
-                       if (MAPTST(&xp->ignoredx, id))
-                         continue;
-                       MAPSET(&xp->ignoredx, id);
-                       queue_push(&revertignore, -id);
-                     }
-                 }
-               queue_deleten(&revertignore, 0, revertcnt);
-             }
-           else if (ignoreignore)
-             {
-               /* no direct ignores, disable ignore processing */
-               xp->ignoreignore = 1;
-             }
-
-           MAPEXP(&xp->ignored, pool->ss.nstrings);
-           MAPEXP(&xp->ignoredx, pool->ss.nstrings);
-           MAPEXP(&xp->preferpos, pool->ss.nstrings);
-           MAPEXP(&xp->preferposx, pool->ss.nstrings);
-           MAPEXP(&xp->preferneg, pool->ss.nstrings);
-           MAPEXP(&xp->prefernegx, pool->ss.nstrings);
-           MAPEXP(&xp->conflicts, pool->ss.nstrings);
-
-           nerrors = expander_expand(xp, &in, &out, &confl);
-
-           /* revert ignores */
-           xp->ignoreignore = oldignoreignore;
-           if (ignoremapssaved)
-             {
-               map_free(&xp->ignored);
-               map_free(&xp->ignoredx);
-               xp->ignored = oldignored;
-               xp->ignoredx = oldignoredx;
-             }
-           else
-             {
-               for (i = 0; i < revertignore.count; i++)
-                 {
-                   id = revertignore.elements[i];
-                   if (id > 0)
-                     MAPCLR(&xp->ignored, id);
-                   else
-                     MAPCLR(&xp->ignoredx, -id);
-                 }
-             }
-           queue_free(&revertignore);
+           nerrors = expander_expand(xp, &in, &indep, &out, &ignoreq, options);
 
            queue_free(&in);
-           queue_free(&confl);
+           queue_free(&indep);
+           queue_free(&ignoreq);
 
            if (nerrors)
              {
@@ -5474,19 +7731,43 @@ expand(BSSolv::expander xp, ...)
                        id = out.elements[i + 1];
                        who = out.elements[i + 2];
                        if (who)
-                         sv = newSVpvf("nothing provides %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
+                         sv = newSVpvf("nothing provides %s needed by %s", pool_dep2str(pool, id), solvid2name(pool, who));
                        else
                          sv = newSVpvf("nothing provides %s", pool_dep2str(pool, id));
                        i += 3;
                      }
-                   else if (type == ERROR_CONFLICTINGPROVIDER)
+                   else if (type == ERROR_ALLCONFLICT)
                      {
                        id = out.elements[i + 1];
                        who = out.elements[i + 2];
                        if (who)
-                         sv = newSVpvf("conflict for provider of %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
+                         sv = newSVpvf("%s conflicts with always true %s", solvid2name(pool, who), pool_dep2str(pool, id));
+                       else
+                         sv = newSVpvf("conflict with always true %s", pool_dep2str(pool, id));
+                       i += 3;
+                     }
+                   else if (type == ERROR_CONFLICT)
+                     {
+                       Id who2 = out.elements[i + 2];
+                       who = out.elements[i + 1];
+                       if (!who && who2 >= 0)
+                         sv = newSVpvf("conflicts with %s", solvid2name(pool, who2));
+                       else if (who2 < 0)
+                         sv = newSVpvf("%s obsoletes %s", solvid2name(pool, who), solvid2name(pool, -who2));
+                       else
+                         sv = newSVpvf("%s conflicts with %s", solvid2name(pool, who), solvid2name(pool, who2));
+                       i += 3;
+                     }
+                   else if (type == ERROR_CONFLICT2)
+                     {
+                       Id who2 = out.elements[i + 2];
+                       who = out.elements[i + 1];
+                       if (who2 < 0)
+                         sv = newSVpvf("%s is obsoleted by %s", solvid2name(pool, who), solvid2name(pool, -who2));
+                       else if (who2 > 0)
+                         sv = newSVpvf("%s is in conflict with %s", solvid2name(pool, who), solvid2name(pool, who2));
                        else
-                         sv = newSVpvf("conflict for provider of %s", pool_dep2str(pool, id));
+                         sv = newSVpvf("%s is in conflict", solvid2name(pool, who));
                        i += 3;
                      }
                    else if (type == ERROR_CONFLICTINGPROVIDERS)
@@ -5494,9 +7775,9 @@ expand(BSSolv::expander xp, ...)
                        id = out.elements[i + 1];
                        who = out.elements[i + 2];
                        if (who)
-                         sv = newSVpvf("conflict for all providers of %s needed by %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name));
+                         sv = newSVpvf("conflict for providers of %s needed by %s", pool_dep2str(pool, id), solvid2name(pool, who));
                        else
-                         sv = newSVpvf("conflict for all providers of %s", pool_dep2str(pool, id));
+                         sv = newSVpvf("conflict for providers of %s", pool_dep2str(pool, id));
                        i += 3;
                      }
                    else if (type == ERROR_PROVIDERINFO)
@@ -5504,9 +7785,9 @@ expand(BSSolv::expander xp, ...)
                        Id who2 = out.elements[i + 2];
                        who = out.elements[i + 1];
                        if (who2 < 0)
-                         sv = newSVpvf("(provider %s obsoletes installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[-who2].name));
+                         sv = newSVpvf("(provider %s obsoletes %s)", solvid2name(pool, who), solvid2name(pool, -who2));
                        else
-                         sv = newSVpvf("(provider %s conflicts with installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[who2].name));
+                         sv = newSVpvf("(provider %s conflicts with %s)", solvid2name(pool, who), solvid2name(pool, who2));
                        i += 3;
                      }
                    else if (type == ERROR_PROVIDERINFO2)
@@ -5514,11 +7795,11 @@ expand(BSSolv::expander xp, ...)
                        Id who2 = out.elements[i + 2];
                        who = out.elements[i + 1];
                        if (who2 < 0)
-                         sv = newSVpvf("(provider %s is obsoleted by installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[-who2].name));
+                         sv = newSVpvf("(provider %s is obsoleted by %s)", solvid2name(pool, who), solvid2name(pool, -who2));
                        else if (who2 > 0)
-                         sv = newSVpvf("(provider %s is conflicted by installed %s)", pool_id2str(pool, pool->solvables[who].name), pool_id2str(pool, pool->solvables[who2].name));
+                         sv = newSVpvf("(provider %s is in conflict with %s)", solvid2name(pool, who), solvid2name(pool, who2));
                        else
-                         sv = newSVpvf("(provider %s is conflicted by the build config)", pool_id2str(pool, pool->solvables[who].name));
+                         sv = newSVpvf("(provider %s is in conflict)", solvid2name(pool, who));
                        i += 3;
                      }
                    else if (type == ERROR_CHOICE)
@@ -5526,6 +7807,9 @@ expand(BSSolv::expander xp, ...)
                        int j;
                        char *str = "";
                        for (j = i + 3; out.elements[j]; j++)
+                         ;
+                       solv_sort(out.elements + i + 3, j - (i + 3), sizeof(Id), pkgname_sort_cmp, pool);
+                       for (j = i + 3; out.elements[j]; j++)
                          {
                            Solvable *s = pool->solvables + out.elements[j];
                            str = pool_tmpjoin(pool, str, " ", pool_id2str(pool, s->name));
@@ -5535,11 +7819,31 @@ expand(BSSolv::expander xp, ...)
                        id = out.elements[i + 1];
                        who = out.elements[i + 2];
                        if (who)
-                         sv = newSVpvf("have choice for %s needed by %s: %s", pool_dep2str(pool, id), pool_id2str(pool, pool->solvables[who].name), str);
+                         sv = newSVpvf("have choice for %s needed by %s: %s", pool_dep2str(pool, id), solvid2name(pool, who), str);
                        else
                          sv = newSVpvf("have choice for %s: %s", pool_dep2str(pool, id), str);
                        i = j + 1;
                      }
+                   else if (type == ERROR_BADDEPENDENCY)
+                     {
+                       id = out.elements[i + 1];
+                       who = out.elements[i + 2];
+                       if (who)
+                         sv = newSVpvf("cannot parse dependency %s from %s", pool_dep2str(pool, id), solvid2name(pool, who));
+                       else
+                         sv = newSVpvf("cannot parse dependency %s", pool_dep2str(pool, id));
+                       i += 3;
+                     }
+                   else if (type == ERROR_NOPROVIDERINFO)
+                     {
+                       id = out.elements[i + 1];
+                       who = out.elements[i + 2];
+                       if (who)
+                         sv = newSVpvf("(got version %s provided by %s)", pool_id2str(pool, id), solvid2name(pool, who));
+                       else
+                         sv = newSVpvf("(got version %s)", pool_id2str(pool, id));
+                       i += 3;
+                     }
                    else
                      croak("expander: bad error type\n");
                    PUSHs(sv_2mortal(sv));
@@ -5558,27 +7862,31 @@ expand(BSSolv::expander xp, ...)
            queue_free(&out);
        }
 
+void
+debug(BSSolv::expander xp, const char *str)
+    CODE:
+       expander_dbg(xp, "%s", str);
+
+
 const char *
 debugstr(BSSolv::expander xp)
     CODE:
-       if (!xp->debugstr)
-         xp->debugstr = calloc(1, 1);
-       RETVAL = xp->debugstr;
+       RETVAL = xp->debugstr ? xp->debugstr : "";
     OUTPUT:
        RETVAL
 
+const char *
+debugstrclr(BSSolv::expander xp)
+       
+    CODE:
+       RETVAL = xp->debugstr ? xp->debugstr : "";
+    OUTPUT:
+       RETVAL
+    CLEANUP:
+       expander_clrdbg(xp);
 
 void
 DESTROY(BSSolv::expander xp)
     CODE:
-       map_free(&xp->ignored);
-       map_free(&xp->ignoredx);
-       queue_free(&xp->preferposq);
-       map_free(&xp->preferpos);
-       map_free(&xp->preferposx);
-       map_free(&xp->preferneg);
-       map_free(&xp->prefernegx);
-       queue_free(&xp->conflictsq);
-       map_free(&xp->conflicts);
-       solv_free(xp->debugstr);
-       solv_free(xp);
+       expander_free(xp);
+
index 8809eb3..8db04d9 100644 (file)
@@ -2,17 +2,15 @@ Source: perl-bssolv
 Section: devel
 Priority: extra
 Maintainer: Shuai Fu <shuai01.fu@samsung.com>
-Build-Depends: debhelper (>= 7.0.15), cdbs, cmake, dpatch, perl, libbz2-dev, librpm-dev, liblzma-dev, libcurl3 | libcurl4, libcurl4-openssl-dev, libmagic-dev, libexpat1, doxygen, pkg-config, libglib2.0-dev, libssl-dev, libzstd-dev, libdb-dev
+Build-Depends: debhelper (>= 7), perl (>= 5.6.10-12), cmake, libz-dev, librpm-dev, libdb-dev, liblzma-dev, libzstd-dev
 Standards-Version: 0.28.0
 Homepage: http://www.tizen.org
 
 Package: perl-bssolv
 Architecture: i386 amd64
-Depends: perl,
+Depends: ${perl:Depends}
  cpio,
  bzip2,
  gzip,
- git-core,
- git-buildpackage-rpm,
 Description: image creator for Linux distributions
   The tool perl-bssolv is used to gbs
index 6c0d30f..5663a32 100755 (executable)
@@ -22,7 +22,7 @@ install: build
        cd /usr/src/packages/BUILD/libsolv && ls
        export CFLAGS="-fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector -funwind-tables -fasynchronous-unwind-tables"
        export CXXFLAGS="$(CFLAGS)"
-       cd libsolv && cmake -DDISABLE_SHARED=1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_SKIP_RPATH=1 -DENABLE_RPMDB=1 -DENABLE_DEBIAN=1 -DENABLE_ARCHREPO=1 -DENABLE_LZMA_COMPRESSION=1 -DMULTI_SEMANTICS=1
+       cd libsolv && cmake -DDISABLE_SHARED=1 -DCMAKE_BUILD_TYPE=Release -DCMAKE_SKIP_RPATH=1 -DENABLE_RPMDB=1 -DENABLE_DEBIAN=1 -DENABLE_ARCHREPO=1 -DENABLE_LZMA_COMPRESSION=1 -DENABLE_ZSTD_COMPRESSION=1 -DENABLE_COMPLEX_DEPS=1 -DMULTI_SEMANTICS=1
        cd libsolv/src && make
        cd libsolv/ext && make
        perl Makefile.PL --bundled-libsolv && make