From a382fd9103823eb2da9f6deeff86da922091d6e5 Mon Sep 17 00:00:00 2001 From: wangbiao Date: Wed, 21 Feb 2024 18:35:39 +0900 Subject: [PATCH] apply the same code as https://build.opensuse.org/package/show/OBS:Server:2.10/perl-BSSolv Enable zstd compress and supoort complex(rich) dependency in Ubuntu Change-Id: Ic77f31278e11a1e632002011ac12970fca3200a6 Signed-off-by: wangbiao --- BSSolv.xs | 4210 +++++++++++++++++++++++++++++++++++++++++++------------- debian/control | 6 +- debian/rules | 2 +- 3 files changed, 3262 insertions(+), 956 deletions(-) diff --git a/BSSolv.xs b/BSSolv.xs index 68814e0..ffd992c 100644 --- 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 @@ -8,19 +15,49 @@ #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 .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); + diff --git a/debian/control b/debian/control index 8809eb3..8db04d9 100644 --- a/debian/control +++ b/debian/control @@ -2,17 +2,15 @@ Source: perl-bssolv Section: devel Priority: extra Maintainer: Shuai Fu -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 diff --git a/debian/rules b/debian/rules index 6c0d30f..5663a32 100755 --- a/debian/rules +++ b/debian/rules @@ -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 -- 2.7.4