diff options
Diffstat (limited to 'src/solver.c')
-rw-r--r-- | src/solver.c | 2513 |
1 files changed, 2513 insertions, 0 deletions
diff --git a/src/solver.c b/src/solver.c new file mode 100644 index 0000000..4a875b8 --- /dev/null +++ b/src/solver.c @@ -0,0 +1,2513 @@ +/* + * solver.c + * + * SAT based dependency solver + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "solver.h" +#include "bitmap.h" +#include "pool.h" +#include "util.h" +#include "evr.h" + +#define RULES_BLOCK 63 + +static Pool *prune_best_version_arch_sortcmp_data; + +/*-----------------------------------------------------------------*/ + +/* + * prep for prune_best_version_arch + * sort by name + */ + +static int +prune_best_version_arch_sortcmp(const void *ap, const void *bp) +{ + Pool *pool = prune_best_version_arch_sortcmp_data; + Id a = *(Id *)ap; + Id b = *(Id *)bp; + return pool->solvables[a].name - pool->solvables[b].name; +} + + +#if 0 +static Id +replaces_system(Solver *solv, Id id) +{ + Pool *pool = solv->pool; + Source *system = solv->system; + Id *name = pool->solvables[id].name; + + FOR_PROVIDES(p, pp, id) + { + s = pool->solvables + p; + if (s->name != name) + continue; + if (p >= system->start && p < system->start + system->nsolvables) + return p; + } +} +#endif + +/* + * prune_best_version_arch + * + * sort list of packages (given through plist) by name and evr + * return result through plist + * + */ + +/* FIXME: must also look at update packages */ + +void +prune_best_version_arch(Pool *pool, Queue *plist) +{ + Id best = ID_NULL; + int i, j; + Solvable *s; + Id a, bestscore; + + if (plist->count < 2) /* no need to prune for a single entry */ + return; + if (pool->verbose) printf("prune_best_version_arch %d\n", plist->count); + + /* prune to best architecture */ + if (pool->id2arch) + { + bestscore = 0; + for (i = 0; i < plist->count; i++) + { + s = pool->solvables + plist->elements[i]; + a = s->arch; + if (a > pool->lastarch) + continue; + a = pool->id2arch[a]; + if ((a & 0xffff0000) > bestscore) + bestscore = a & 0xffff0000; + } + for (i = j = 0; i < plist->count; i++) + { + s = pool->solvables + plist->elements[i]; + a = s->arch; + if (a > pool->lastarch) + continue; + a = pool->id2arch[a]; + /* a == 1 -> noarch */ + if (a != 1 && (a & 0xffff0000) != bestscore) + continue; + plist->elements[j++] = plist->elements[i]; + } + plist->count = j; + if (j == 0) + return; + } + + prune_best_version_arch_sortcmp_data = pool; + /* sort by name first */ + qsort(plist->elements, plist->count, sizeof(Id), prune_best_version_arch_sortcmp); + + /* now find best 'per name' */ + for (i = j = 0; i < plist->count; i++) + { + s = pool->solvables + plist->elements[i]; + if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC) + continue; + + if (pool->verbose) printf("- %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + + if (!best) /* if no best yet, the current is best */ + { + best = plist->elements[i]; + continue; + } + + /* name switch: re-init */ + if (pool->solvables[best].name != s->name) /* new name */ + { + if (pool->verbose) printf("BEST: %s-%s.%s\n", id2str(pool, pool->solvables[best].name), id2str(pool, pool->solvables[best].evr), id2str(pool, pool->solvables[best].arch)); + plist->elements[j++] = best; /* move old best to front */ + best = plist->elements[i]; /* take current as new best */ + continue; + } + + if (pool->solvables[best].evr != s->evr) /* compare evr */ + { + if (evrcmp(pool, pool->solvables[best].evr, s->evr) < 0) + best = plist->elements[i]; + } + } + + if (best == ID_NULL) + best = plist->elements[0]; + + /* XXX also check obsoletes! */ + if (pool->verbose) printf("BEST: %s-%s.%s\n", id2str(pool, pool->solvables[best].name), id2str(pool, pool->solvables[best].evr), id2str(pool, pool->solvables[best].arch)); + + plist->elements[j++] = best; + plist->count = j; + +} + +/*-----------------------------------------------------------------*/ + +/* + * print rules + */ + +static void +printruleelement(Solver *solv, Rule *r, Id v) +{ + Pool *pool = solv->pool; + Solvable *s; + if (v < 0) + { + s = pool->solvables + -v; + printf(" !%s-%s.%s [%d]", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), -v); + } + else + { + s = pool->solvables + v; + printf(" %s-%s.%s [%d]", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), v); + } + if (r) + { + if (r->w1 == v) + printf(" (w1)"); + if (r->w2 == v) + printf(" (w2)"); + } + if (solv->decisionmap[s - pool->solvables] > 0) + printf(" I.%d", solv->decisionmap[s - pool->solvables]); + if (solv->decisionmap[s - pool->solvables] < 0) + printf(" C.%d", -solv->decisionmap[s - pool->solvables]); + printf("\n"); +} + + +/* + * print rule + */ + +static void +printrule(Solver *solv, Rule *r) +{ + int i; + Id v; + + if (r >= solv->rules && r < solv->rules + solv->nrules) /* r is a solver rule */ + printf("Rule #%d:\n", (int)(r - solv->rules)); + else + printf("Rule:\n"); /* r is any rule */ + for (i = 0; ; i++) + { + if (i == 0) + v = r->p; + else if (r->d == ID_NULL) + { + if (i == 2) + break; + v = r->w2; + } + else + v = solv->pool->whatprovidesdata[r->d + i - 1]; + if (v == ID_NULL) + break; + printruleelement(solv, r, v); + } + printf(" next: %d %d\n", r->n1, r->n2); +} + + +/*-----------------------------------------------------------------*/ + +/* + * Rule handling + */ + +static Pool *unifyrules_sortcmp_data; + +/* + * compare rules for unification sort + */ + +static int +unifyrules_sortcmp(const void *ap, const void *bp) +{ + Pool *pool = unifyrules_sortcmp_data; + Rule *a = (Rule *)ap; + Rule *b = (Rule *)bp; + Id *ad, *bd; + int x; + + x = a->p - b->p; + if (x) + return x; /* p differs */ + + /* identical p */ + if (a->d == 0 && b->d == 0) + return a->w2 - b->w2; /* assertion: return w2 diff */ + + if (a->d == 0) /* a is assertion, b not */ + { + x = a->w2 - pool->whatprovidesdata[b->d]; + return x ? x : -1; + } + + if (b->d == 0) /* b is assertion, a not */ + { + x = pool->whatprovidesdata[a->d] - b->w2; + return x ? x : 1; + } + + /* compare whatprovidesdata */ + ad = pool->whatprovidesdata + a->d; + bd = pool->whatprovidesdata + b->d; + for (; *ad && *ad == *bd; ad++, bd++) + ; + return *ad - *bd; +} + + +/* + * unify rules + */ + +static void +unifyrules(Solver *solv) +{ + int i, j; + Rule *ir, *jr; + + if (solv->nrules <= 1) /* nothing to unify */ + return; + + /* sort rules first */ + unifyrules_sortcmp_data = solv->pool; + qsort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp); + + /* prune rules + * i = unpruned + * j = pruned + */ + jr = 0; + for (i = j = 1, ir = solv->rules + 1; i < solv->nrules; i++, ir++) + { + if (jr && !unifyrules_sortcmp(ir, jr)) + continue; /* prune! */ + jr = solv->rules + j++; /* keep! */ + if (ir != jr) + *jr = *ir; + } + + /* reduced count from nrules to j rules */ + if (solv->pool->verbose) printf("pruned rules from %d to %d\n", solv->nrules, j); + + /* adapt rule buffer */ + solv->rules = (Rule *)xrealloc(solv->rules, ((solv->nrules + RULES_BLOCK) & ~RULES_BLOCK) * sizeof(Rule)); + solv->nrules = j; +#if 1 + { + int binr = 0; + int dc = 0; + Id *dp; + Rule *r; + + for (i = 1; i < solv->nrules; i++) + { + r = solv->rules + i; + if (r->d == 0) /* assertion */ + { + binr++; + continue; + } + dp = solv->pool->whatprovidesdata + r->d; + while (*dp++) + dc++; + } + if (solv->pool->verbose) + { + printf(" binary: %d\n", binr); + printf(" normal: %d\n", solv->nrules - 1 - binr); + printf(" normal lits: %d\n", dc); + } + } +#endif +} + +#if 0 + +/* + * hash rule + */ + +static Hashval +hashrule(Solver *solv, Id p, Id d, int n) +{ + unsigned int x = (unsigned int)p; + int *dp; + + if (n <= 1) + return (x * 37) ^ (unsigned int)d; + dp = solv->pool->whatprovidesdata + d; + while (*dp) + x = (x * 37) ^ (unsigned int)*dp++; + return x; +} +#endif + + +/* + * add rule + * p = direct literal; > 0 for learnt, < 0 for installed pkg (rpm) + * d, if < 0 direct literal, if > 0 offset into whatprovides, if == 0 rule is assertion (look at p only) + * + * + * A requires b, b provided by B1,B2,B3 => (-A|B1|B2|B3) + * + * p < 0 : rule from rpm (installed pkg) + * d > 0 : Offset in whatprovidesdata (list of providers) + * + * A conflicts b, b provided by B1,B2,B3 => (-A|-B1), (-A|-B2), (-A|-B3) + * d < 0: Id of solvable (e.g. B1) + * + * d == 0: unary rule, assertion => (A) or (-A) + * + * Install: p > 0, d = 0 (A) user requested install + * Remove: p < 0, d = 0 (-A) user requested remove + * Requires: p < 0, d > 0 (-A|B1|B2|...) d: <list of providers for requirement of p> + * Updates: p > 0, d > 0 (A|B1|B2|...) d: <list of updates for solvable p> + * Conflicts: p < 0, d < 0 (-A|-B) either p (conflict issuer) or d (conflict provider) + * ? p > 0, d < 0 (A|-B) + * No-op ?: p = 0, d = 0 (null) (used as policy rule placeholder) + */ + +static Rule * +addrule(Solver *solv, Id p, Id d) +{ + Rule *r = NULL; + Id *dp = NULL; + + int n = 0; /* number of literals in rule - 1 + 0 = direct assertion (single literal) + 1 = binary rule + */ + + /* it often happenes that requires lead to adding the same rpm rule + * multiple times, so we prune those duplicates right away to make + * the work for unifyrules a bit easier */ + + if (solv->nrules && !solv->jobrules) + { + r = solv->rules + solv->nrules - 1; /* get the last added rule */ + if (r->p == p && r->d == d && d != 0) /* identical and not user requested */ + return r; + } + + if (d < 0) + { + if (p == d) + return NULL; /* ignore self conflict */ + n = 1; + } + else if (d == 0) /* user requested */ + n = 0; + else + { + for (dp = solv->pool->whatprovidesdata + d; *dp; dp++, n++) + if (*dp == -p) + return NULL; /* rule is self-fulfilling */ + if (n == 1) + d = dp[-1]; + } + + if (n == 0) /* direct assertion */ + { + if (!solv->jobrules) + { + /* this is a rpm rule assertion, we do not have to allocate it */ + /* we can identify it by looking at the decision level, it will be 1 */ + if (p > 0) /* */ + abort(); + if (solv->decisionmap[-p] > 0) /* */ + abort(); + if (solv->decisionmap[-p]) /* */ + return NULL; + queuepush(&solv->decisionq, p); + queuepush(&solv->decisionq_why, 0); + solv->decisionmap[-p] = -1; + + return NULL; + } + } + else if (n == 1 && p > d) + { + /* smallest literal first so we can find dups */ + n = p; + p = d; + d = n; + n = 1; /* re-set n, was used as temp var */ + } + + if (r + && n == 1 + && r->p == p + && r->w2 == d) + { + return r; + } + + if (r + && r->d + && n > 1 + && r->p == p) + { + Id *dp2 = solv->pool->whatprovidesdata + r->d; + for (dp = solv->pool->whatprovidesdata + d; *dp; dp++, dp2++) + { + if (*dp != *dp2) + break; + } + if (*dp == *dp2) + return r; + } + + /* + * allocate new rule + */ + + /* check and extend rule buffer */ + if ((solv->nrules & RULES_BLOCK) == 0) + { + solv->rules = (Rule *)xrealloc(solv->rules, (solv->nrules + (RULES_BLOCK + 1)) * sizeof(Rule)); + } + + r = solv->rules + solv->nrules++; /* point to rule space */ + + r->p = p; + if (n == 0) + { + /* direct assertion, no watch needed */ + r->d = 0; + r->w1 = p; + r->w2 = 0; + } + else if (n == 1) + { + /* binary rule */ + r->d = 0; + r->w1 = p; + r->w2 = d; + } + else + { + r->d = d; + r->w1 = p; + r->w2 = solv->pool->whatprovidesdata[d]; + } + r->n1 = 0; + r->n2 = 0; + + /* we don't add the decision for learnt rules, as the code does that + * right after calling addrule anyway */ + if (n == 0 + && p + && !solv->learntrules) + { + /* must be system or job rule, as there are only negative unary rpm rules */ + Id vv = p > 0 ? p : -p; + if (solv->decisionmap[vv]) + { + int i; + if (solv->decisionmap[vv] > 0 && p > 0) + return r; + if (solv->decisionmap[vv] < 0 && p < 0) + return r; + /* direct conflict! */ + for (i = 0; i < solv->decisionq.count; i++) + { + if (solv->decisionq.elements[i] == -p) + break; + } + if (i == solv->decisionq.count) + abort(); + if (solv->decisionq_why.elements[i] == 0) + { + /* conflict with rpm rule */ + queuepush(&solv->problems, r - solv->rules); + queuepush(&solv->problems, 0); + r->w1 = 0; /* disable */ + return r; + } + /* conflict with other job or system rule */ + queuepush(&solv->problems, solv->decisionq_why.elements[i]); + queuepush(&solv->problems, r - solv->rules); + queuepush(&solv->problems, 0); + r->w1 = 0; /* disable */ + /* also disable conflicting rule */ + solv->rules[solv->decisionq_why.elements[i]].w1 = 0; + /* XXX: remove from decisionq! */ +printf("XXX remove from decisionq\n"); + return r; + } + queuepush(&solv->decisionq, p); + queuepush(&solv->decisionq_why, r - solv->rules); + solv->decisionmap[p > 0 ? p : -p] = p > 0 ? 1 : -1; + } + return r; +} + + +/* + * add (install) rules for solvable + * + */ + +static void +addrulesforsolvable(Solver *solv, Solvable *s, Map *m) +{ + Pool *pool = solv->pool; + Source *system = solv->system; + Queue q; + int i; + int dontfix; + Id req, *reqp; + Id con, *conp; + Id obs, *obsp; + Id rec, *recp; + Id p, *pp; + Id *dp; + Id n; + + queueinit(&q); + queuepush(&q, s - pool->solvables);/* push solvable Id */ + + while (q.count) + { + /* + * n: Id of solvable + * s: Pointer to solvable + */ + + n = queueshift(&q); + if (MAPTST(m, n)) /* continue if already set in map */ + continue; + + MAPSET(m, n); + s = pool->solvables + n; /* s = Solvable in question */ + + dontfix = 0; + if (system /* have rpm */ + && !solv->fixsystem + && n >= system->start /* its an rpm rule */ + && n < system->start + system->nsolvables) + { + dontfix = 1; /* dont care about broken rpm deps */ + } + + /*----------------------------------------- + * check requires of s + */ + + if ((reqp = s->requires) != ID_NULL) + { + while ((req = *reqp++) != ID_NULL) + { + if (req == SOLVABLE_PREREQMARKER) /* skip the marker */ + continue; + + dp = GET_PROVIDESP(req, p); /* get providers of req */ + + if (!*dp /* dont care if noone provides rpmlib() */ + && !strncmp(id2str(pool, req), "rpmlib(", 7)) + { + continue; + } + + if (dontfix) /* rpm rule, dont care about breakage */ + { + for (i = 0; dp[i]; i++)/* for all providers */ + { + if (dp[i] >= system->start && dp[i] < system->start + system->nsolvables) + break; /* provider is installed */ + } + if (!dp[i]) /* no provider found */ + { + if (pool->verbose) printf("ignoring broken requires %s%s%s of system package %s-%s.%s\n", id2str(pool, req), id2rel(pool, req), id2evr(pool, req), id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + continue; + } + } + + if (!*dp) + { + /* nothing provides req! */ + #if 1 + if (pool->verbose) printf("package %s-%s.%s is not installable (%s%s%s)\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), id2str(pool, req), id2rel(pool, req), id2evr(pool, req)); + #endif + addrule(solv, -n, 0); /* mark requestor as uninstallable */ + if (solv->rc_output) + printf(">!> !unflag %s-%s.%s[%s]\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), source_name(pool_source(pool, s))); + continue; + } + #if 0 + printf("addrule %s-%s.%s %s%s%s %d %d\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), id2str(pool, req), id2rel(pool, req), id2evr(pool, req), -n, dp - pool->whatprovidesdata); + for (i = 0; dp[i]; i++) + printf(" %s-%s.%s\n", id2str(pool, pool->solvables[dp[i]].name), id2str(pool, pool->solvables[dp[i]].evr), id2str(pool, pool->solvables[dp[i]].arch)); + #endif + /* add 'requires' dependency */ + addrule(solv, -n, dp - pool->whatprovidesdata); /* rule: (-requestor|provider1|provider2|...|providerN) */ + + /* descend the dependency tree */ + for (; *dp != ID_NULL; dp++) /* loop through all providers */ + { + if (!MAPTST(m, *dp)) /* if not already marked */ + queuepush(&q, *dp); /* queue for installation */ + } + + } /* while, requirements of n */ + + } /* if, requirements */ + + + /*----------------------------------------- + * check conflicts of s + */ + + if ((conp = s->conflicts) != ID_NULL) + { + while ((con = *conp++) != ID_NULL) + { + FOR_PROVIDES(p, pp, con) /* loop through all providers of this conflict */ + { + /* dontfix: dont care about conflicts with already installed packs */ + if (dontfix && p >= system->start && p < system->start + system->nsolvables) + continue; + addrule(solv, -n, -p); /* rule: -n|-p: either solvable _or_ provider of conflict */ + } + } + } + + /*----------------------------------------- + * check obsoletes if not installed + */ + if (!system || n < system->start || n >= (system->start + system->nsolvables)) + { /* not installed */ + if ((obsp = s->obsoletes) != ID_NULL) + { + while ((obs = *obsp++) != ID_NULL) + { + FOR_PROVIDES(p, pp, obs) + addrule(solv, -n, -p); + } + } + FOR_PROVIDES(p, pp, s->name) + { + if (s->name == pool->solvables[p].name) + addrule(solv, -n, -p); + } + } + + /*----------------------------------------- + * add recommends to the rule list + */ + if ((recp = s->recommends) != ID_NULL) + while ((rec = *recp++) != ID_NULL) + { + FOR_PROVIDES(p, pp, rec) + if (!MAPTST(m, p)) + queuepush(&q, p); + } + } + queuefree(&q); +} + +static void +addrulesforsupplements(Solver *solv, Map *m) +{ + Pool *pool = solv->pool; + Solvable *s; + Id sup, *supp; + Id p, *pp; + int i, n; + + if (pool->verbose) printf("addrulesforsupplements... (%d)\n", solv->nrules); + for (i = n = 1; n < pool->nsolvables; i++, n++) + { + if (i == pool->nsolvables) + i = 1; + if (MAPTST(m, i)) + continue; + s = pool->solvables + i; + if (!(supp = s->supplements)) + continue; + while ((sup = *supp++) != ID_NULL) + { + FOR_PROVIDES(p, pp, sup) + if (MAPTST(m, p)) + break; + if (p) + break; + } + if (!sup) + continue; + addrulesforsolvable(solv, s, m); + n = 0; + } + if (pool->verbose) printf("done. (%d)\n", solv->nrules); +} + + +static inline int +archchanges(Pool *pool, Solvable *s1, Solvable *s2) +{ + Id a1 = s1->arch, a2 = s2->arch; + + /* we allow changes to/from noarch */ + if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH) + return 0; + if (!pool->id2arch) + return 0; + a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0; + a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0; + if (((a1 ^ a2) & 0xffff0000) != 0) + return 1; + return 0; +} + + +static void +findupdatepackages(Solver *solv, Solvable *s, Queue *qs, Map *m, int allowdowngrade, int allowarchchange) +{ + /* system packages get a special upgrade allowed rule */ + Pool *pool = solv->pool; + Id p, *pp, n, p2, *pp2; + Id obs, *obsp; + + QUEUEEMPTY(qs); + /* + * s = solvable ptr + * n = solvable Id + */ + n = s - pool->solvables; + if (m && !MAPTST(m, n)) /* add rule for s if not already done */ + addrulesforsolvable(solv, s, m); + + /* + * look for updates for s + */ + FOR_PROVIDES(p, pp, s->name) /* every provider of s' name */ + { + if (p == n) /* skip itself */ + continue; + + if (s->name == pool->solvables[p].name) /* name match */ + { + if (!allowdowngrade /* consider downgrades ? */ + && evrcmp(pool, s->evr, pool->solvables[p].evr) > 0) + continue; + /* XXX */ + if (!allowarchchange && archchanges(pool, s, pool->solvables + p)) + continue; + } + else if ((obsp = pool->solvables[p].obsoletes) != 0) /* provides/obsoletes combination ? */ + { + while ((obs = *obsp++) != 0) /* for all obsoletes */ + { + FOR_PROVIDES(p2, pp2, obs) /* and all matching providers of the obsoletes */ + { + if (p2 == n) /* match ! */ + break; + } + if (p2) /* match! */ + break; + } + if (!obs) /* continue if no match */ + continue; + /* here we have 'p' with a matching provides/obsoletes combination + * thus flagging p as a valid update candidate for s + */ + } + queuepush(qs, p); + + if (m && !MAPTST(m, p)) /* mark p for install if not already done */ + addrulesforsolvable(solv, pool->solvables + p, m); + } +} + +/* + * add rule for update + * (A|A1|A2|A3...) An = update candidates for A + * + * s = (installed) solvable + * m = 'addedmap', bit set if 'install' rule for solvable exists + */ + +static void +addupdaterule(Solver *solv, Solvable *s, Map *m, int allowdowngrade, int allowarchchange, int dontaddrule) +{ + /* system packages get a special upgrade allowed rule */ + Pool *pool = solv->pool; + Id d, n; + Rule *r; + Queue qs; + + queueinit(&qs); + findupdatepackages(solv, s, &qs, m, allowdowngrade, allowarchchange); + n = s - pool->solvables; + if (dontaddrule) /* we consider update candidates but dont force them */ + { + queuefree(&qs); + return; + } + + if (qs.count == 0) /* no updates found */ + { +#if 0 + printf("new update rule: must keep %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); +#endif + addrule(solv, n, 0); /* request 'install' of s */ + queuefree(&qs); + return; + } + + d = pool_queuetowhatprovides(pool, &qs); /* intern computed provider queue */ + queuefree(&qs); + r = addrule(solv, n, d); /* allow update of s */ +#if 0 + printf("new update rule "); + if (r) + printrule(solv, r); +#endif +} + + +/*-----------------------------------------------------------------*/ +/* watches */ + + +/* + * makewatches + * + * initial setup for all watches + */ + +static void +makewatches(Solver *solv) +{ + Rule *r; + int i; + int nsolvables = solv->pool->nsolvables; + + xfree(solv->watches); + /* lower half for removals, upper half for installs */ + solv->watches = (Id *)xcalloc(2 * nsolvables, sizeof(Id)); +#if 1 + /* do it reverse so rpm rules get triggered first */ + for (i = 1, r = solv->rules + solv->nrules - 1; i < solv->nrules; i++, r--) +#else + for (i = 1, r = solv->rules + 1; i < solv->nrules; i++, r++) +#endif + { + if (!r->w1 /* rule is disabled */ + || !r->w2) /* rule is assertion */ + continue; + + /* see addwatches(solv, r) */ + r->n1 = solv->watches[nsolvables + r->w1]; + solv->watches[nsolvables + r->w1] = r - solv->rules; + + r->n2 = solv->watches[nsolvables + r->w2]; + solv->watches[nsolvables + r->w2] = r - solv->rules; + } +} + + +/* + * add watches (for rule) + */ + +static void +addwatches(Solver *solv, Rule *r) +{ + int nsolvables = solv->pool->nsolvables; + + r->n1 = solv->watches[nsolvables + r->w1]; + solv->watches[nsolvables + r->w1] = r - solv->rules; + + r->n2 = solv->watches[nsolvables + r->w2]; + solv->watches[nsolvables + r->w2] = r - solv->rules; +} + + +/*-----------------------------------------------------------------*/ +/* rule propagation */ + +#define DECISIONMAP_TRUE(p) ((p) > 0 ? (decisionmap[p] > 0) : (decisionmap[-p] < 0)) + +/* + * propagate + * + * propagate decision to all rules + */ + +static Rule * +propagate(Solver *solv, int level) +{ + Pool *pool = solv->pool; + Id *rp, *nrp; + Rule *r; + Id p, pkg, ow; + Id *dp; + Id *decisionmap = solv->decisionmap; + Id *watches = solv->watches + pool->nsolvables; + + while (solv->propagate_index < solv->decisionq.count) + { + /* negative because our watches trigger if literal goes FALSE */ + pkg = -solv->decisionq.elements[solv->propagate_index++]; +#if 0 + printf("popagate for decision %d level %d\n", -pkg, level); + printruleelement(solv, 0, -pkg); +#endif + for (rp = watches + pkg; *rp; rp = nrp) + { + r = solv->rules + *rp; +#if 0 + printf(" watch triggered "); + printrule(solv, r); +#endif + if (pkg == r->w1) + { + ow = r->w2; + nrp = &r->n1; + } + else + { + ow = r->w1; + nrp = &r->n2; + } + /* if clause is TRUE, nothing to do */ + if (DECISIONMAP_TRUE(ow)) + continue; + + if (r->d) + { + /* not a binary clause, check if we need to move our watch */ + if (r->p && r->p != ow && !DECISIONMAP_TRUE(-r->p)) + p = r->p; + else + for (dp = pool->whatprovidesdata + r->d; (p = *dp++) != 0;) + if (p != ow && !DECISIONMAP_TRUE(-p)) + break; + if (p) + { + /* p is free to watch, move watch to p */ +#if 0 + if (p > 0) + printf(" -> move w%d to %s-%s.%s\n", (pkg == r->w1 ? 1 : 2), id2str(pool, pool->solvables[p].name), id2str(pool, pool->solvables[p].evr), id2str(pool, pool->solvables[p].arch)); + else + printf(" -> move w%d to !%s-%s.%s\n", (pkg == r->w1 ? 1 : 2), id2str(pool, pool->solvables[-p].name), id2str(pool, pool->solvables[-p].evr), id2str(pool, pool->solvables[-p].arch)); +#endif + *rp = *nrp; + nrp = rp; + if (pkg == r->w1) + { + r->w1 = p; + r->n1 = watches[p]; + } + else + { + r->w2 = p; + r->n2 = watches[p]; + } + watches[p] = r - solv->rules; + continue; + } + } + /* unit clause found, set other watch to TRUE */ + if (DECISIONMAP_TRUE(-ow)) + return r; /* eek, a conflict! */ +#if 0 + printf("unit "); + printrule(solv, r); +#endif + if (ow > 0) + decisionmap[ow] = level; + else + decisionmap[-ow] = -level; + queuepush(&solv->decisionq, ow); + queuepush(&solv->decisionq_why, r - solv->rules); +#if 0 + { + Solvable *s = pool->solvables + (ow > 0 ? ow : -ow); + if (ow > 0) + printf(" -> decided to install %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + else + printf(" -> decided to conflict %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + } +#endif + } + } + return 0; /* all is well */ +} + + +/*-----------------------------------------------------------------*/ +/* Analysis */ + +/* + * analyze + * and learn + */ + +static int +analyze(Solver *solv, int level, Rule *c, int *pr, int *dr, int *why) +{ + Pool *pool = solv->pool; + Queue r; + int rlevel = 1; + Map seen; /* global? */ + Id v, vv, *dp; + int l, i, idx; + int num = 0; + int learnt_why = solv->learnt_pool.count; + Id *decisionmap = solv->decisionmap; + + queueinit(&r); + + if (pool->verbose) printf("ANALYZE at %d ----------------------\n", level); + mapinit(&seen, pool->nsolvables); + idx = solv->decisionq.count; + for (;;) + { + printrule(solv, c); + queuepush(&solv->learnt_pool, c - solv->rules); + dp = c->d ? pool->whatprovidesdata + c->d : 0; + for (i = -1; ; i++) + { + if (i == -1) + v = c->p; + else if (c->d == 0) + v = i ? 0 : c->w2; + else + v = *dp++; + if (v == 0) + break; + if (DECISIONMAP_TRUE(v)) /* the one true literal */ + continue; + vv = v > 0 ? v : -v; + if (MAPTST(&seen, vv)) + continue; + l = solv->decisionmap[vv]; + if (l < 0) + l = -l; + if (l == 1) + { +#if 0 + int j; + for (j = 0; j < solv->decisionq.count; j++) + if (solv->decisionq.elements[j] == v) + break; + if (j == solv->decisionq.count) + abort(); + queuepush(&rulq, -(j + 1)); +#endif + continue; /* initial setting */ + } + MAPSET(&seen, vv); + if (l == level) + num++; /* need to do this one as well */ + else + { + queuepush(&r, v); +#if 0 + printf("PUSH %d ", v); + printruleelement(solv, 0, v); +#endif + if (l > rlevel) + rlevel = l; + } + } +#if 0 + printf("num = %d\n", num); +#endif + if (num <= 0) + abort(); + for (;;) + { + v = solv->decisionq.elements[--idx]; + vv = v > 0 ? v : -v; + if (MAPTST(&seen, vv)) + break; + } + c = solv->rules + solv->decisionq_why.elements[idx]; + MAPCLR(&seen, vv); + if (--num == 0) + break; + } + *pr = -v; + if (r.count == 0) + *dr = 0; + else if (r.count == 1 && r.elements[0] < 0) + *dr = r.elements[0]; + else + *dr = pool_queuetowhatprovides(pool, &r); + if (pool->verbose) + { + printf("learned rule for level %d (am %d)\n", rlevel, level); + printruleelement(solv, 0, -v); + for (i = 0; i < r.count; i++) + { + v = r.elements[i]; + printruleelement(solv, 0, v); + } + } + mapfree(&seen); + queuepush(&solv->learnt_pool, 0); +#if 0 + for (i = learnt_why; solv->learnt_pool.elements[i]; i++) + { + printf("learnt_why "); + printrule(solv, solv->rules + solv->learnt_pool.elements[i]); + } +#endif + if (why) + *why = learnt_why; + return rlevel; +} + + +/* + * reset_solver + * reset the solver decisions to right after the rpm rules + */ + +static void +reset_solver(Solver *solv) +{ + int i; + Id v; + Rule *r; + + /* delete all learnt rules */ + solv->nrules = solv->learntrules; + QUEUEEMPTY(&solv->learnt_why); + QUEUEEMPTY(&solv->learnt_pool); + + /* redo all direct decision without the disabled rules */ + for (i = 0; i < solv->decisionq.count; i++) + { + v = solv->decisionq.elements[i]; + solv->decisionmap[v > 0 ? v : -v] = 0; + } + for (i = 0; i < solv->decisionq_why.count; i++) + if (solv->decisionq_why.elements[i]) + break; + else + { + v = solv->decisionq.elements[i]; + solv->decisionmap[v > 0 ? v : -v] = v > 0 ? 1 : -1; + } + + if (solv->pool->verbose) + printf("decisions done reduced from %d to %d\n", solv->decisionq.count, i); + + solv->decisionq_why.count = i; + solv->decisionq.count = i; + if (i < solv->propagate_index) + solv->propagate_index = i; + /* make direct decisions from enabled unary rules */ + for (i = solv->jobrules, r = solv->rules + solv->jobrules; i < solv->nrules; i++, r++) + { + if (!r->w1 || r->w2) + continue; +#if 0 + printrule(solv, r); +#endif + v = r->p; + queuepush(&solv->decisionq, v); + queuepush(&solv->decisionq_why, r - solv->rules); + solv->decisionmap[v > 0 ? v : -v] = v > 0 ? 1 : -1; + } + if (solv->pool->verbose) + printf("decisions after adding job and system rules: %d\n", solv->decisionq.count); + /* recreate watches */ + makewatches(solv); +} + + +/* + * analyze_unsolvable_rule + */ + +static void +analyze_unsolvable_rule(Solver *solv, Rule *c, int disablerules) +{ + Id why; + int i; + + why = c - solv->rules; +#if 0 + if (why >= solv->jobrules && why < solv->systemrules) + printf("JOB "); + if (why >= solv->systemrules && why < solv->learntrules) + printf("SYSTEM %d ", why - solv->systemrules); + if (solv->learntrules && why >= solv->learntrules) + printf("LEARNED "); + printrule(solv, c); +#endif + if (solv->learntrules && why >= solv->learntrules) + { + for (i = solv->learnt_why.elements[why - solv->learntrules]; solv->learnt_pool.elements[i]; i++) + analyze_unsolvable_rule(solv, solv->rules + solv->learnt_pool.elements[i], disablerules); + return; + } + if (why >= solv->jobrules && why < solv->learntrules) + { + if (disablerules) + { + /* turn off rule for further analysis */ + c->w1 = 0; + } + /* unify problem */ + if (solv->problems.count) + { + for (i = solv->problems.count - 1; i >= 0; i--) + if (solv->problems.elements[i] == 0) + break; + else if (solv->problems.elements[i] == why) + return; + } + queuepush(&solv->problems, why); + } +} + + +/* + * analyze_unsolvable + */ + +static void +analyze_unsolvable(Solver *solv, Rule *c, int disablerules) +{ + Pool *pool = solv->pool; + Map seen; /* global? */ + Id v, vv, *dp, why; + int l, i, idx; + Id *decisionmap = solv->decisionmap; + +#if 0 + printf("ANALYZE UNSOLVABLE ----------------------\n"); +#endif + mapinit(&seen, pool->nsolvables); + analyze_unsolvable_rule(solv, c, disablerules); + dp = c->d ? pool->whatprovidesdata + c->d : 0; + for (i = -1; ; i++) + { + if (i == -1) + v = c->p; + else if (c->d == 0) + v = i ? 0 : c->w2; + else + v = *dp++; + if (v == 0) + break; + if (DECISIONMAP_TRUE(v)) /* the one true literal */ + continue; + vv = v > 0 ? v : -v; + l = solv->decisionmap[vv]; + if (l < 0) + l = -l; + MAPSET(&seen, vv); + } + idx = solv->decisionq.count; + while (idx > 0) + { + v = solv->decisionq.elements[--idx]; + vv = v > 0 ? v : -v; + if (!MAPTST(&seen, vv)) + continue; + why = solv->decisionq_why.elements[idx]; + if (!why) + { +#if 0 + printf("RPM "); + printruleelement(solv, 0, v); +#endif + continue; + } + c = solv->rules + why; + analyze_unsolvable_rule(solv, c, disablerules); + dp = c->d ? pool->whatprovidesdata + c->d : 0; + for (i = -1; ; i++) + { + if (i == -1) + v = c->p; + else if (c->d == 0) + v = i ? 0 : c->w2; + else + v = *dp++; + if (v == 0) + break; + if (DECISIONMAP_TRUE(v)) /* the one true literal */ + continue; + vv = v > 0 ? v : -v; + l = solv->decisionmap[vv]; + if (l < 0) + l = -l; + MAPSET(&seen, vv); + } + } + mapfree(&seen); + queuepush(&solv->problems, 0); /* mark end of this problem */ + if (disablerules) + reset_solver(solv); +#if 0 + printf("analyze_unsolvables done\n"); +#endif +} + + +/*-----------------------------------------------------------------*/ +/* Decision revert */ + +/* + * revert + * revert decision at level + */ + +static void +revert(Solver *solv, int level) +{ + Id v, vv; + while (solv->decisionq.count) + { + v = solv->decisionq.elements[solv->decisionq.count - 1]; + vv = v > 0 ? v : -v; + if (solv->decisionmap[vv] <= level && solv->decisionmap[vv] >= -level) + break; +#if 0 + printf("reverting decision %d at %d\n", v, solv->decisionmap[vv]); +#endif + solv->decisionmap[vv] = 0; + solv->decisionq.count--; + solv->decisionq_why.count--; + solv->propagate_index = solv->decisionq.count; + } +} + + +/* + * watch2onhighest + */ + +static void +watch2onhighest(Solver *solv, Rule *r) +{ + int l, wl = 0; + Id v, *dp; + + if (!r->d) + return; /* binary rule, both watches are set */ + dp = solv->pool->whatprovidesdata + r->d; + while ((v = *dp++) != 0) + { + l = solv->decisionmap[v < 0 ? -v : v]; + if (l < 0) + l = -l; + if (l > wl) + { + r->w2 = dp[-1]; + wl = l; + } + } +} + + +/* + * setpropagatelearn + */ + +static int +setpropagatelearn(Solver *solv, int level, Id decision, int disablerules) +{ + Rule *r; + Id p, d; + int l, why; + + if (decision) + { + level++; + if (decision > 0) + solv->decisionmap[decision] = level; + else + solv->decisionmap[-decision] = -level; + queuepush(&solv->decisionq, decision); + queuepush(&solv->decisionq_why, 0); + } + for (;;) + { + r = propagate(solv, level); + if (!r) + break; + if (level == 1) + { + analyze_unsolvable(solv, r, disablerules); + if (disablerules) + return 1; + return 0; + } + printf("conflict with rule #%d\n", (int)(r - solv->rules)); + l = analyze(solv, level, r, &p, &d, &why); + if (l >= level || l <= 0) + abort(); + printf("reverting decisions (level %d -> %d)\n", level, l); + level = l; + revert(solv, level); + r = addrule(solv, p, d); /* p requires d */ + if (!r) + abort(); + if (solv->learnt_why.count != (r - solv->rules) - solv->learntrules) + { + printf("%d %d\n", solv->learnt_why.count, (int)(r - solv->rules) - solv->learntrules); + abort(); + } + queuepush(&solv->learnt_why, why); + if (d) + { + /* at least 2 literals, needs watches */ + watch2onhighest(solv, r); + addwatches(solv, r); + } + solv->decisionmap[p > 0 ? p : -p] = p > 0 ? level : -level; + queuepush(&solv->decisionq, p); + queuepush(&solv->decisionq_why, r - solv->rules); + printf("decision: "); + printruleelement(solv, 0, p); + printf("new rule: "); + printrule(solv, r); + } + return level; +} + +/*-----------------------------------------------------------------*/ +/* Main solver interface */ + + +/* + * solver_create + * create solver structure + * + * pool: all available solvables + * system: installed Solvables + * + * + * Upon solving, rules are created to flag the Solvables + * of the 'system' Source as installed. + */ + +Solver * +solver_create(Pool *pool, Source *system) +{ + Solver *solv; + solv = (Solver *)xcalloc(1, sizeof(Solver)); + solv->pool = pool; + solv->system = system; + pool->verbose = 1; + + queueinit(&solv->decisionq); + queueinit(&solv->decisionq_why); + queueinit(&solv->problems); + queueinit(&solv->learnt_why); + queueinit(&solv->learnt_pool); + + solv->decisionmap = (Id *)xcalloc(pool->nsolvables, sizeof(Id)); + solv->rules = (Rule *)xmalloc((solv->nrules + (RULES_BLOCK + 1)) * sizeof(Rule)); + memset(solv->rules, 0, sizeof(Rule)); + + solv->nrules = 1; + + return solv; +} + + +/* + * solver_free + */ + +void +solver_free(Solver *solv) +{ + queuefree(&solv->decisionq); + queuefree(&solv->decisionq_why); + queuefree(&solv->learnt_why); + queuefree(&solv->learnt_pool); + xfree(solv->decisionmap); + xfree(solv->rules); + xfree(solv->watches); + xfree(solv->weaksystemrules); + xfree(solv); +} + + +/* + * reenablerule + * + * r->w1 was set to 0, now find proper value for w1 + */ + +static void +reenablerule(Solver *solv, Rule *r) +{ + int i; + Id v, l, good; + + if (!r->w2) /* not a rule, but an assertion */ + { + r->w1 = r->p; + return; + } + if (!r->d) + { + if (r->w2 != r->p) + r->w1 = r->p; + else + r->w2 = r->d; /* mls: shouldn't this be r->w1 ? */ + return; + } + good = 0; + /* put it on the first not-false literal */ + for (i = -1; ; i++) + { + if (i == -1) + v = r->p; + else + v = solv->pool->whatprovidesdata[r->d + i]; + if (!v) + { + printrule(solv, r); + abort(); + } + if (v == r->w2) + continue; + l = solv->decisionmap[v > 0 ? v : -v]; + if (!l || (v < 0 && l < 0) || (v > 0 && l > 0)) + break; + } + r->w1 = v; +} + + +/*-------------------------------------------------------*/ + +/* + * run_solver + * + * all rules have been set up, not actually run the solver + * + */ + +static void +run_solver(Solver *solv, int disablerules, int doweak) +{ + Queue dq; + int systemlevel; + int level, olevel; + Rule *r; + int i, n; + Solvable *s; + Pool *pool = solv->pool; + Id p, *dp; + +#if 0 + printf("number of rules: %d\n", solv->nrules); +{ + int i; + for (i = 0; i < solv->nrules; i++) + { + printrule(solv, solv->rules + i); + } +} +#endif + + /* all new rules are learnt after this point */ + solv->learntrules = solv->nrules; + /* crate watches lists */ + makewatches(solv); + + if (pool->verbose) printf("initial decisions: %d\n", solv->decisionq.count); + + /* start SAT algorithm */ + level = 1; + if (!solv->updatesystem) + systemlevel = 2; + else + systemlevel = 0; + if (pool->verbose) printf("solving...\n"); + + queueinit(&dq); + for (;;) + { + /* + * propagate + */ + + if (level == 1) + { + if (pool->verbose) printf("propagating (%d %d)...\n", solv->propagate_index, solv->decisionq.count); + if ((r = propagate(solv, level)) != 0) + { + analyze_unsolvable(solv, r, disablerules); + if (disablerules) + continue; + printf("UNSOLVABLE\n"); + queuefree(&dq); + return; + } + } + + /* + * system packages + */ + + if (level < systemlevel && solv->system->nsolvables) + { + if (pool->verbose) printf("installing system packages\n"); + for (i = solv->system->start, n = 0; ; i++, n++) + { + if (n == solv->system->nsolvables) + break; + if (i == solv->system->start + solv->system->nsolvables) + i = solv->system->start; + s = pool->solvables + i; +#if 0 + if (solv->decisionmap[i] < 0) + { + int j; + printf("system %s-%s.%s conflicts\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + for (j = 0; j < solv->decisionq.count; j++) + if (solv->decisionq.elements[j] == -i) + break; + if (solv->decisionq_why.elements[j]) + printrule(solv, solv->rules + solv->decisionq_why.elements[j]); + } +#endif + if (solv->decisionmap[i] != 0) + continue; +#if 0 + printf("system installing %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); +#endif + olevel = level; + level = setpropagatelearn(solv, level, i, disablerules); + if (level == 0) + { + printf("UNSOLVABLE\n"); + queuefree(&dq); + return; + } + if (level <= olevel) + n = 0; + } + if (solv->weaksystemrules) + { + if (pool->verbose) printf("installing weak system packages\n"); + for (i = solv->system->start, n = 0; ; i++, n++) + { + if (n == solv->system->nsolvables) + break; + if (solv->decisionmap[i] > 0 || solv->weaksystemrules[i - solv->system->start] == 0) + continue; + QUEUEEMPTY(&dq); + dp = pool->whatprovidesdata + solv->weaksystemrules[i - solv->system->start]; + while ((p = *dp++) != 0) + { + if (solv->decisionmap[p] > 0) + break; + if (solv->decisionmap[p] == 0) + queuepush(&dq, p); + } + if (p || !dq.count) + continue; + + + if (dq.count > 1) + prune_best_version_arch(pool, &dq); +#if 0 + s = pool->solvables + dq.elements[0]; + printf("weak system installing %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); +#endif + olevel = level; + level = setpropagatelearn(solv, level, dq.elements[0], disablerules); + if (level == 0) + { + printf("UNSOLVABLE\n"); + queuefree(&dq); + return; + } + if (level <= olevel) + { + n = 0; + break; + } + } + if (n != solv->system->nsolvables) + continue; + } + systemlevel = level; + } + + /* + * decide + */ + + if (pool->verbose) printf("deciding unresolved rules\n"); + for (i = 1, n = 1; ; i++, n++) + { + if (n == solv->nrules) + break; + if (i == solv->nrules) + i = 1; + r = solv->rules + i; + if (!r->w1) + continue; + QUEUEEMPTY(&dq); + if (r->d == 0) + { + /* binary or unary rule */ + /* need two positive undecided literals */ + if (r->p < 0 || r->w2 <= 0) + continue; + if (solv->decisionmap[r->p] || solv->decisionmap[r->w2]) + continue; + queuepush(&dq, r->p); + queuepush(&dq, r->w2); + } + else + { + /* make sure that + * all negative literals are installed + * no positive literal is installed + * i.e. the rule is not fulfilled and we + * just need to decide on the positive literals + */ + if (r->p < 0) + { + if (solv->decisionmap[-r->p] <= 0) + continue; + } + else + { + if (solv->decisionmap[r->p] > 0) + continue; + if (solv->decisionmap[r->p] == 0) + queuepush(&dq, r->p); + } + dp = pool->whatprovidesdata + r->d; + while ((p = *dp++) != 0) + { + if (p < 0) + { + if (solv->decisionmap[-p] <= 0) + break; + } + else + { + if (solv->decisionmap[p] > 0) + break; + if (solv->decisionmap[p] == 0) + queuepush(&dq, p); + } + } + if (p) + continue; + } + if (dq.count < 2) + { + /* cannot happen as this means that + * the rule is unit */ + printrule(solv, r); + abort(); + } + prune_best_version_arch(pool, &dq); + p = dq.elements[dq.count - 1]; + s = pool->solvables + p; +#if 0 + printf("installing %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); +#endif + olevel = level; + level = setpropagatelearn(solv, level, p, disablerules); + if (level == 0) + { + printf("UNSOLVABLE\n"); + queuefree(&dq); + return; + } + if (level < systemlevel) + break; + if (level <= olevel) + n = 0; + } /* for(), decide */ + + /* + * check for end + */ + + if (n != solv->nrules) + continue; + if (doweak && !solv->problems.count) + { + int qcount; + + if (pool->verbose) printf("installing recommended packages\n"); + QUEUEEMPTY(&dq); + for (i = 1; i < pool->nsolvables; i++) + { + if (solv->decisionmap[i] < 0) + continue; + if (solv->decisionmap[i] > 0) + { + Id *recp, rec, *pp, p; + s = pool->solvables + i; + /* installed, check for recommends */ + if ((recp = s->recommends) != 0) + { + while ((rec = *recp++) != 0) + { + qcount = dq.count; + FOR_PROVIDES(p, pp, rec) + { + if (solv->decisionmap[p] > 0) + break; + else if (solv->decisionmap[p] == 0) + queuepushunique(&dq, p); + } + if (p) + dq.count = qcount; /* already fulfilled */ + } + } + } + else + { + Id *supp, sup, *pp, p; + s = pool->solvables + i; + if ((supp = s->supplements) != 0) + { + while ((sup = *supp++) != 0) + { + FOR_PROVIDES(p, pp, sup) + { + if (solv->decisionmap[p] > 0) + break; + } + if (p) + queuepushunique(&dq, i); + } + } + } + } + if (dq.count) + { + prune_best_version_arch(pool, &dq); + p = dq.elements[dq.count - 1]; + s = pool->solvables + p; +#if 0 + printf("weak installing %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); +#endif + level = setpropagatelearn(solv, level, p, 0); + continue; + } + } + break; + } + queuefree(&dq); +} + + +/* + * refine_suggestion + */ + +static void +refine_suggestion(Solver *solv, Id *problem, Id sug, Queue *refined) +{ + Rule *r; + int i, j; + Id v; + Queue disabled; + int disabledcnt; + + printf("refine_suggestion start\n"); + queueinit(&disabled); + QUEUEEMPTY(refined); + queuepush(refined, sug); + + /* re-enable all rules but rule "sug" of the problem */ + for (i = 0; problem[i]; i++) + { + if (problem[i] == sug) + continue; + r = solv->rules + problem[i]; +#if 0 + printf("enable "); + printrule(solv, r); +#endif + reenablerule(solv, r); + } + for (;;) + { + revert(solv, 1); + reset_solver(solv); + QUEUEEMPTY(&solv->problems); + run_solver(solv, 0, 0); + if (!solv->problems.count) + { + printf("no more problems!\n"); +#if 0 + printdecisions(solv); +#endif + break; /* great, no more problems */ + } + disabledcnt = disabled.count; + for (i = 0; i < solv->problems.elements[i]; i++) + { + /* ignore solutions in refined */ + v = solv->problems.elements[i]; + for (j = 0; problem[j]; j++) + if (problem[j] != sug && problem[j] == v) + break; + if (problem[j]) + continue; + queuepush(&disabled, v); + } + if (disabled.count == disabledcnt) + { + /* no solution found, this was an invalid suggestion! */ + printf("no solution found!\n"); + for (i = 0; i < refined->count; i++) + reenablerule(solv, solv->rules + refined->elements[i]); + refined->count = 0; + break; + } + if (disabled.count == disabledcnt + 1) + { + /* just one solution, add it to refined list */ + queuepush(refined, disabled.elements[disabledcnt]); + } + else + { + printf("############################################## more than one solution found.\n"); +#if 1 + for (i = 0; i < solv->problems.elements[i]; i++) + { + printrule(solv, solv->rules + solv->problems.elements[i]); + } + printf("##############################################\n"); +#endif + /* more than one solution */ + /* for now return */ + } + for (i = disabledcnt; i < disabled.count; i++) + { + r = solv->rules + disabled.elements[i];; + /* disable it */ + r->w1 = 0; +#if 0 + printf("disable "); + printrule(solv, r); +#endif + } + } + /* enable refined rules again */ + reset_solver(solv); + for (i = 0; i < disabled.count; i++) + reenablerule(solv, solv->rules + disabled.elements[i]); + /* disable problem rules again so that we are in the same state as before */ + for (i = 0; problem[i]; i++) + { + r = solv->rules + problem[i]; + r->w1 = 0; + } + printf("refine_suggestion end\n"); +} + + +/* + * printdecisions + */ + +static const char * +id2rc(Solver *solv, Id id) +{ + const char *evr; + if (solv->rc_output != 2) + return ""; + evr = id2str(solv->pool, id); + if (*evr < '0' || *evr > '9') + return "0:"; + while (*evr >= '0' && *evr <= '9') + evr++; + if (*evr != ':') + return "0:"; + return ""; +} + +void +printdecisions(Solver *solv) +{ + Pool *pool = solv->pool; + Id p, *obsoletesmap; + int i; + Solvable *s; + + obsoletesmap = (Id *)xcalloc(pool->nsolvables, sizeof(Id)); + for (i = 0; i < solv->decisionq.count; i++) + { + Id obs, *obsp; + Id *pp, n; + + n = solv->decisionq.elements[i]; + if (n < 0) + continue; + if (n >= solv->system->start && n < solv->system->start + solv->system->nsolvables) + continue; + s = pool->solvables + n; + if ((obsp = s->obsoletes) != 0) + while ((obs = *obsp++) != 0) + FOR_PROVIDES(p, pp, obs) + { + if (p >= solv->system->start && p < solv->system->start + solv->system->nsolvables) + { + obsoletesmap[p] = n; + obsoletesmap[n]++; + } + } + FOR_PROVIDES(p, pp, s->name) + if (s->name == pool->solvables[p].name) + { + if (p >= solv->system->start && p < solv->system->start + solv->system->nsolvables) + { + obsoletesmap[p] = n; + obsoletesmap[n]++; + } + } + } + + if (solv->rc_output) + printf(">!> Solution #1:\n"); + + int installs = 0, uninstalls = 0, upgrades = 0; + + /* print solvables to be erased */ + + for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++) + { + if (solv->decisionmap[i] > 0) + continue; + if (obsoletesmap[i]) + continue; + s = pool->solvables + i; + if (solv->rc_output) + { + printf(">!> "); + if (solv->rc_output == 2) + { + printf("remove "); + printf(" %s-%s%s", id2str(pool, s->name), id2rc(solv, s->evr), id2str(pool, s->evr)); + } + else + { + printf("remove %s-%s.%s", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + } + } + else + { + printf("erase %s-%s.%s", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + } + printf("\n"); + uninstalls++; + } + + /* print solvables to be installed */ + + for (i = 0; i < solv->decisionq.count; i++) + { + p = solv->decisionq.elements[i]; + if (p < 0) + continue; + if (p >= solv->system->start && p < solv->system->start + solv->system->nsolvables) + continue; + s = pool->solvables + p; + + if (solv->rc_output) + printf(">!> "); + + if (!obsoletesmap[p]) + { + printf("install %s-%s%s", id2str(pool, s->name), id2rc(solv, s->evr), id2str(pool, s->evr)); + if (solv->rc_output != 2) + printf(".%s", id2str(pool, s->arch)); + installs++; + } + else + { + int j; + Solvable *from = NULL, *to = NULL; + if (solv->rc_output) + to = s; + else + printf("update %s-%s.%s (obsoletes", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + upgrades++; + for (j = solv->system->start; j < solv->system->start + solv->system->nsolvables; j++) + { + if (obsoletesmap[j] == p) + { + s = pool->solvables + j; + if (solv->rc_output) + from = s; + else + printf(" %s-%s.%s", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + } + } + if (solv->rc_output) + { + if (solv->rc_output == 2) + printf("upgrade %s-%s => %s-%s%s", id2str(pool, from->name), id2str(pool, from->evr), id2str(pool, to->name), id2rc(solv, to->evr), id2str(pool, to->evr)); + else + printf("upgrade %s-%s.%s => %s-%s.%s", id2str(pool, from->name), id2str(pool, from->evr), id2str(pool, from->arch), id2str(pool, to->name), id2str(pool, to->evr), id2str(pool, to->arch)); + s = to; /* for final source name */ + } + else + printf(")"); + } + if (solv->rc_output) + { + Source *source = pool_source(pool, s); + if (source) + printf("[%s]", source_name(source)); + } + printf("\n"); + } + + if (solv->rc_output) + printf(">!> installs=%d, upgrades=%d, uninstalls=%d\n", installs, upgrades, uninstalls); + + xfree(obsoletesmap); +} + + +/*-----------------------------------------------------------------*/ +/* main() */ + +/* + * + * solve job queue + * + */ + +void +solve(Solver *solv, Queue *job) +{ + Pool *pool = solv->pool; + int i; + Map addedmap; /* '1' == have rule for solvable */ + Map noupdaterule; /* '1' == don't update (scheduled for removal) */ + Id how, what, p, *pp, d; + Queue q; + Rule *r; + Solvable *s; + + /* + * create basic rule set of all involved packages + * as bitmaps + * + */ + + mapinit(&addedmap, pool->nsolvables); + mapinit(&noupdaterule, pool->nsolvables); + + queueinit(&q); + + /* + * create rules for installed solvables -> keep them installed + * so called: rpm rules + * + */ + + for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++) + addrulesforsolvable(solv, pool->solvables + i, &addedmap); + + /* + * create install rules + * + * two passes, as we want to keep the rpm rules distinct from the job rules + * + */ + + /* + * solvable rules + * process job rules for solvables + */ + + for (i = 0; i < job->count; i += 2) + { + how = job->elements[i]; + what = job->elements[i + 1]; + + switch(how) + { + case SOLVER_INSTALL_SOLVABLE: + addrulesforsolvable(solv, pool->solvables + what, &addedmap); + break; + case SOLVER_INSTALL_SOLVABLE_NAME: + case SOLVER_INSTALL_SOLVABLE_PROVIDES: + QUEUEEMPTY(&q); + FOR_PROVIDES(p, pp, what) + { + /* if by name, ensure that the name matches */ + if (how == SOLVER_INSTALL_SOLVABLE_NAME && pool->solvables[p].name != what) + continue; + addrulesforsolvable(solv, pool->solvables + p, &addedmap); + } + break; + case SOLVER_INSTALL_SOLVABLE_UPDATE: + /* dont allow downgrade */ + addupdaterule(solv, pool->solvables + what, &addedmap, 0, 0, 1); + break; + } + } + + /* + * if unstalls are disallowed, add update rules for every + * installed solvables in the hope to circumvent uninstall + * by upgrading + * + */ + +#if 0 + if (!solv->allowuninstall) + { + /* add update rule for every installed package */ + for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++) + addupdaterule(solv, pool->solvables + i, &addedmap, solv->allowdowngrade, solv->allowarchchange, 1); + } +#else /* this is just to add the needed rpm rules to our set */ + for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++) + addupdaterule(solv, pool->solvables + i, &addedmap, 1, 1, 1); +#endif + + /* + * first passed done + * + * unify existing rules before going over all job rules + * + */ + + addrulesforsupplements(solv, &addedmap); + + unifyrules(solv); /* remove duplicate rpm rules */ + + /* + * at this point the system is always solvable, + * as an empty system (remove all packages) is a valid solution + */ + if (pool->verbose) printf("decisions based on rpms: %d\n", solv->decisionq.count); + + /* + * now add all job rules + */ + + solv->jobrules = solv->nrules; + + for (i = 0; i < job->count; i += 2) + { + how = job->elements[i]; + what = job->elements[i + 1]; + switch(how) + { + case SOLVER_INSTALL_SOLVABLE: /* install specific solvable */ + if (solv->rc_output) { + Solvable *s = pool->solvables + what; + printf(">!> Installing %s from channel %s\n", id2str(pool, s->name), source_name(pool_source(pool, s))); + } + addrule(solv, what, 0); /* install by Id */ + break; + case SOLVER_ERASE_SOLVABLE: + addrule(solv, -what, 0); /* remove by Id */ + MAPSET(&noupdaterule, what); + break; + case SOLVER_INSTALL_SOLVABLE_NAME: /* install by capability */ + case SOLVER_INSTALL_SOLVABLE_PROVIDES: + QUEUEEMPTY(&q); + FOR_PROVIDES(p, pp, what) /* check all providers */ + { + /* if by name, ensure that the name matches */ + if (how == SOLVER_INSTALL_SOLVABLE_NAME && pool->solvables[p].name != what) + continue; + queuepush(&q, p); + } + if (!q.count) { /* no provider found -> abort */ + fprintf(stderr, "Nothing provides '%s'\n", id2str(pool, what)); + /* XXX make this a problem! */ + return; + abort(); + } + + p = queueshift(&q); /* get first provider */ + if (!q.count) + d = 0; /* single provider ? -> make assertion */ + else + d = pool_queuetowhatprovides(pool, &q); /* get all providers */ + addrule(solv, p, d); /* add 'requires' rule */ + break; + case SOLVER_ERASE_SOLVABLE_NAME: /* remove by capability */ + case SOLVER_ERASE_SOLVABLE_PROVIDES: + FOR_PROVIDES(p, pp, what) + { + /* if by name, ensure that the name matches */ + if (how == SOLVER_ERASE_SOLVABLE_NAME && pool->solvables[p].name != what) + continue; + + addrule(solv, -p, 0); /* add 'remove' rule */ + MAPSET(&noupdaterule, p); + } + break; + case SOLVER_INSTALL_SOLVABLE_UPDATE: /* find update for solvable */ + addupdaterule(solv, pool->solvables + what, &addedmap, 0, 0, 0); + break; + } + } + + if (pool->verbose) printf("problems so far: %d\n", solv->problems.count); + + /* + * now add policy rules + * + */ + + solv->systemrules = solv->nrules; + + /* + * create rules for updating installed solvables + * + * (Again ?) + * + */ + + if (!solv->allowuninstall) + { /* loop over all installed solvables */ + for (i = solv->system->start; i < solv->system->start + solv->system->nsolvables; i++) + { + if (!MAPTST(&noupdaterule, i)) /* if not marked as 'noupdate' */ + addupdaterule(solv, pool->solvables + i, &addedmap, solv->allowdowngrade, solv->allowarchchange, 0); + else + addrule(solv, 0, 0); /* place holder */ + } + /* consistency check: we added a rule for _every_ system solvable */ + if (solv->nrules - solv->systemrules != solv->system->nsolvables) + abort(); + } + + if (pool->verbose) printf("problems so far: %d\n", solv->problems.count); + + /* create special weak system rules */ + if (solv->system->nsolvables) + { + solv->weaksystemrules = xcalloc(solv->system->nsolvables, sizeof(Id)); + for (i = 0; i < solv->system->nsolvables; i++) + { + findupdatepackages(solv, pool->solvables + solv->system->start + i, &q, (Map *)0, 1, 1); + if (q.count) + solv->weaksystemrules[i] = pool_queuetowhatprovides(pool, &q); + } + } + + /* free unneeded memory */ + mapfree(&addedmap); + mapfree(&noupdaterule); + queuefree(&q); + + /* + * solve ! + * + */ + + run_solver(solv, 1, 1); + + /* + * + * print solver result + * + */ + + if (pool->verbose) printf("-------------------------------------------------------------\n"); + + if (solv->problems.count) + { + Queue problems; + Queue solution; + Id *problem; + Id why; + int j; + + if (!pool->verbose) + return; + clonequeue(&problems, &solv->problems); + queueinit(&solution); + printf("Encountered problems! Here are the solutions:\n"); + problem = problems.elements; + for (i = 0; i < problems.count; i++) + { + Id v = problems.elements[i]; + if (v == 0) + { + printf("====================================\n"); + problem = problems.elements + i + 1; + continue; + } + refine_suggestion(solv, problem, v, &solution); + for (j = 0; j < solution.count; j++) + { + r = solv->rules + solution.elements[j]; + why = solution.elements[j]; +#if 0 + printrule(solv, r); +#endif + if (why >= solv->jobrules && why < solv->systemrules) + { + /* do a sloppy job of analyzing the job rule */ + if (r->p > 0) + { + s = pool->solvables + r->p; + printf("- do not install %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + } + else + { + s = pool->solvables - r->p; + printf("- do not erase %s-%s.%s\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch)); + } + } + else if (why >= solv->systemrules && why < solv->learntrules) + { + s = pool->solvables + solv->system->start + (why - solv->systemrules); + printf("- allow deinstallation/downgrade of %s-%s.%s [%d]\n", id2str(pool, s->name), id2str(pool, s->evr), id2str(pool, s->arch), why); + } + else + { + abort(); + } + } + printf("------------------------------------\n"); + } + return; + } + + printdecisions(solv); +} + + +// EOF |