diff options
author | TizenOpenSource <tizenopensrc@samsung.com> | 2023-12-08 12:23:05 +0900 |
---|---|---|
committer | TizenOpenSource <tizenopensrc@samsung.com> | 2023-12-08 12:23:05 +0900 |
commit | 1aac48dff40ef592968a18058bad270da65ed847 (patch) | |
tree | 3e9a2fb59b3a9559e2a8f2aa1ad194929708d1df /src | |
parent | 2d757ccc60324e7bfcc07f6f46d7f38e30642fcb (diff) | |
download | libsolv-1aac48dff40ef592968a18058bad270da65ed847.tar.gz libsolv-1aac48dff40ef592968a18058bad270da65ed847.tar.bz2 libsolv-1aac48dff40ef592968a18058bad270da65ed847.zip |
Imported Upstream version 0.7.27upstream/0.7.27upstream
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 2 | ||||
-rw-r--r-- | src/conda.h | 11 | ||||
-rw-r--r-- | src/cplxdeps.c | 1 | ||||
-rw-r--r-- | src/dataiterator.h | 12 | ||||
-rw-r--r-- | src/decision.c | 1228 | ||||
-rw-r--r-- | src/knownid.h | 1 | ||||
-rw-r--r-- | src/libsolv.ver | 17 | ||||
-rw-r--r-- | src/linkedpkg.c | 40 | ||||
-rw-r--r-- | src/linkedpkg.h | 7 | ||||
-rw-r--r-- | src/policy.c | 65 | ||||
-rw-r--r-- | src/pool.c | 2 | ||||
-rw-r--r-- | src/pool.h | 33 | ||||
-rw-r--r-- | src/poolarch.c | 6 | ||||
-rw-r--r-- | src/pooltypes.h | 17 | ||||
-rw-r--r-- | src/problems.c | 157 | ||||
-rw-r--r-- | src/problems.h | 9 | ||||
-rw-r--r-- | src/repo.h | 4 | ||||
-rw-r--r-- | src/repo_write.c | 29 | ||||
-rw-r--r-- | src/repodata.h | 11 | ||||
-rw-r--r-- | src/rules.c | 249 | ||||
-rw-r--r-- | src/rules.h | 11 | ||||
-rw-r--r-- | src/solvable.c | 135 | ||||
-rw-r--r-- | src/solver.c | 452 | ||||
-rw-r--r-- | src/solver.h | 35 | ||||
-rw-r--r-- | src/solverdebug.c | 40 | ||||
-rw-r--r-- | src/transaction.h | 7 |
26 files changed, 2062 insertions, 519 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bbf30ba..ca04b39 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -20,7 +20,7 @@ SET (libsolv_SRCS transaction.c order.c rules.c problems.c linkedpkg.c cplxdeps.c chksum.c md5.c sha1.c sha2.c solvversion.c selection.c fileprovides.c diskusage.c suse.c solver_util.c cleandeps.c - userinstalled.c filelistfilter.c) + userinstalled.c filelistfilter.c decision.c) SET (libsolv_HEADERS bitmap.h evr.h hash.h policy.h poolarch.h poolvendor.h pool.h diff --git a/src/conda.h b/src/conda.h index 3bcfa2d..07703be 100644 --- a/src/conda.h +++ b/src/conda.h @@ -13,10 +13,21 @@ #ifndef LIBSOLV_CONDA_H #define LIBSOLV_CONDA_H +#include "pooltypes.h" +#include "solvable.h" + +#ifdef __cplusplus +extern "C" { +#endif + int pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode); int solvable_conda_matchversion(Solvable *s, const char *version); Id pool_addrelproviders_conda(Pool *pool, Id name, Id evr, Queue *plist); Id pool_conda_matchspec(Pool *pool, const char *name); +#ifdef __cplusplus +} +#endif + #endif /* LIBSOLV_CONDA_H */ diff --git a/src/cplxdeps.c b/src/cplxdeps.c index 6c40752..26e754d 100644 --- a/src/cplxdeps.c +++ b/src/cplxdeps.c @@ -405,6 +405,7 @@ pool_add_pos_literals_complex_dep(Pool *pool, Id dep, Queue *q, Map *m, int neg) Reldep *rd2 = GETRELDEP(pool, rd->evr); if (rd2->flags == REL_ELSE) { + pool_add_pos_literals_complex_dep(pool, rd2->name, q, m, !neg); pool_add_pos_literals_complex_dep(pool, rd2->evr, q, m, !neg); dep = rd2->name; } diff --git a/src/dataiterator.h b/src/dataiterator.h index 0649258..357ae59 100644 --- a/src/dataiterator.h +++ b/src/dataiterator.h @@ -20,8 +20,6 @@ extern "C" { #endif -struct s_Repo; - typedef struct s_KeyValue { Id id; const char *str; @@ -106,8 +104,8 @@ typedef struct s_Dataiterator int flags; Pool *pool; - struct s_Repo *repo; - struct s_Repodata *data; + Repo *repo; + Repodata *data; /* data pointers */ unsigned char *dp; @@ -165,9 +163,9 @@ typedef struct s_Dataiterator * keyname: if non-null, limit search to this keyname * match: if non-null, limit search to this match */ -int dataiterator_init(Dataiterator *di, Pool *pool, struct s_Repo *repo, Id p, Id keyname, const char *match, int flags); +int dataiterator_init(Dataiterator *di, Pool *pool, Repo *repo, Id p, Id keyname, const char *match, int flags); void dataiterator_init_clone(Dataiterator *di, Dataiterator *from); -void dataiterator_set_search(Dataiterator *di, struct s_Repo *repo, Id p); +void dataiterator_set_search(Dataiterator *di, Repo *repo, Id p); void dataiterator_set_keyname(Dataiterator *di, Id keyname); int dataiterator_set_match(Dataiterator *di, const char *match, int flags); @@ -181,7 +179,7 @@ void dataiterator_skip_attribute(Dataiterator *di); void dataiterator_skip_solvable(Dataiterator *di); void dataiterator_skip_repo(Dataiterator *di); void dataiterator_jump_to_solvid(Dataiterator *di, Id solvid); -void dataiterator_jump_to_repo(Dataiterator *di, struct s_Repo *repo); +void dataiterator_jump_to_repo(Dataiterator *di, Repo *repo); void dataiterator_entersub(Dataiterator *di); void dataiterator_clonepos(Dataiterator *di, Dataiterator *from); void dataiterator_seek(Dataiterator *di, int whence); diff --git a/src/decision.c b/src/decision.c new file mode 100644 index 0000000..c0cf4ad --- /dev/null +++ b/src/decision.c @@ -0,0 +1,1228 @@ +/* + * Copyright (c) 2022, SUSE LLC + * + * This program is licensed under the BSD license, read LICENSE.BSD + * for further information + */ + +/* + * decision.c + * + * solver decision and alternative introspection code + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> + +#include "solver.h" +#include "solver_private.h" +#include "bitmap.h" +#include "pool.h" +#include "util.h" +#include "evr.h" + +int +solver_get_decisionlevel(Solver *solv, Id p) +{ + return solv->decisionmap[p]; +} + +void +solver_get_decisionqueue(Solver *solv, Queue *decisionq) +{ + queue_free(decisionq); + queue_init_clone(decisionq, &solv->decisionq); +} + +int +solver_get_lastdecisionblocklevel(Solver *solv) +{ + Id p; + if (solv->decisionq.count == 0) + return 0; + p = solv->decisionq.elements[solv->decisionq.count - 1]; + if (p < 0) + p = -p; + return solv->decisionmap[p] < 0 ? -solv->decisionmap[p] : solv->decisionmap[p]; +} + +void +solver_get_decisionblock(Solver *solv, int level, Queue *decisionq) +{ + Id p; + int i; + + queue_empty(decisionq); + for (i = 0; i < solv->decisionq.count; i++) + { + p = solv->decisionq.elements[i]; + if (p < 0) + p = -p; + if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level) + break; + } + if (i == solv->decisionq.count) + return; + for (i = 0; i < solv->decisionq.count; i++) + { + p = solv->decisionq.elements[i]; + if (p < 0) + p = -p; + if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level) + queue_push(decisionq, p); + else + break; + } +} + +/* return the reason and some extra info (i.e. a rule id) why + * a package was installed/conflicted */ +int +solver_describe_decision(Solver *solv, Id p, Id *infop) +{ + int i; + Id pp, why; + + if (infop) + *infop = 0; + if (!solv->decisionmap[p]) + return SOLVER_REASON_UNRELATED; + pp = solv->decisionmap[p] < 0 ? -p : p; + for (i = 0; i < solv->decisionq.count; i++) + if (solv->decisionq.elements[i] == pp) + break; + if (i == solv->decisionq.count) /* just in case... */ + return SOLVER_REASON_UNRELATED; + why = solv->decisionq_why.elements[i]; + if (infop) + *infop = why >= 0 ? why : -why; + if (why > 0) + return SOLVER_REASON_UNIT_RULE; + i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p]; + return solv->decisionq_reason.elements[i]; +} + +/* create pseudo ruleinfo elements why a package was installed if + * the reason was SOLVER_REASON_WEAKDEP */ +int +solver_allweakdepinfos(Solver *solv, Id p, Queue *whyq) +{ + Pool *pool = solv->pool; + int i; + int level = solv->decisionmap[p]; + int decisionno; + Solvable *s; + + queue_empty(whyq); + if (level < 0) + return 0; /* huh? */ + for (decisionno = 0; decisionno < solv->decisionq.count; decisionno++) + if (solv->decisionq.elements[decisionno] == p) + break; + if (decisionno == solv->decisionq.count) + return 0; /* huh? */ + i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p]; + if (solv->decisionq_reason.elements[i] != SOLVER_REASON_WEAKDEP) + return 0; /* huh? */ + + /* 1) list all packages that recommend us */ + for (i = 1; i < pool->nsolvables; i++) + { + Id *recp, rec, pp2, p2; + if (solv->decisionmap[i] <= 0 || solv->decisionmap[i] >= level) + continue; + s = pool->solvables + i; + if (!s->recommends) + continue; + if (!solv->addalreadyrecommended && s->repo == solv->installed) + continue; + recp = s->repo->idarraydata + s->recommends; + while ((rec = *recp++) != 0) + { + int found = 0; + FOR_PROVIDES(p2, pp2, rec) + { + if (p2 == p) + found = 1; + else + { + /* if p2 is already installed, this recommends is ignored */ + if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level) + break; + } + } + if (!p2 && found) + { + queue_push2(whyq, SOLVER_RULE_PKG_RECOMMENDS, i); + queue_push2(whyq, 0, rec); + } + } + } + /* 2) list all supplements */ + s = pool->solvables + p; + if (s->supplements && level > 0) + { + Id *supp, sup, pp2, p2; + /* this is a hack. to use solver_dep_fulfilled we temporarily clear + * everything above our level in the decisionmap */ + for (i = decisionno; i < solv->decisionq.count; i++ ) + { + p2 = solv->decisionq.elements[i]; + if (p2 > 0) + solv->decisionmap[p2] = -solv->decisionmap[p2]; + } + supp = s->repo->idarraydata + s->supplements; + while ((sup = *supp++) != 0) + if (solver_dep_fulfilled(solv, sup)) + { + int found = 0; + /* let's see if this is an easy supp */ + FOR_PROVIDES(p2, pp2, sup) + { + if (!solv->addalreadyrecommended && solv->installed) + { + if (pool->solvables[p2].repo == solv->installed) + continue; + } + if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level) + { + queue_push2(whyq, SOLVER_RULE_PKG_SUPPLEMENTS, p); + queue_push2(whyq, p2, sup); + found = 1; + } + } + if (!found) + { + /* hard case, just note dependency with no package */ + queue_push2(whyq, SOLVER_RULE_PKG_SUPPLEMENTS, p); + queue_push2(whyq, 0, sup); + } + } + for (i = decisionno; i < solv->decisionq.count; i++) + { + p2 = solv->decisionq.elements[i]; + if (p2 > 0) + solv->decisionmap[p2] = -solv->decisionmap[p2]; + } + } + return whyq->count / 4; +} + +SolverRuleinfo +solver_weakdepinfo(Solver *solv, Id p, Id *fromp, Id *top, Id *depp) +{ + Queue iq; + queue_init(&iq); + solver_allweakdepinfos(solv, p, &iq); + if (fromp) + *fromp = iq.count ? iq.elements[1] : 0; + if (top) + *top = iq.count ? iq.elements[2] : 0; + if (depp) + *depp = iq.count ? iq.elements[3] : 0; + return iq.count ? iq.elements[0] : SOLVER_RULE_UNKNOWN; +} + +/* deprecated, use solver_allweakdepinfos instead */ +void +solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) +{ + int i, j; + solver_allweakdepinfos(solv, p, whyq); + for (i = j = 0; i < whyq->count; i += 4) + { + if (whyq->elements[i] == SOLVER_RULE_PKG_RECOMMENDS) + { + whyq->elements[j++] = SOLVER_REASON_RECOMMENDED; + whyq->elements[j++] = whyq->elements[i + 1]; + whyq->elements[j++] = whyq->elements[i + 3]; + } + else if (whyq->elements[i] == SOLVER_RULE_PKG_SUPPLEMENTS) + { + whyq->elements[j++] = SOLVER_REASON_SUPPLEMENTED; + whyq->elements[j++] = whyq->elements[i + 2]; + whyq->elements[j++] = whyq->elements[i + 3]; + } + } + queue_truncate(whyq, j); +} + +static int +decisionsort_cmp(const void *va, const void *vb, void *vd) +{ + Solver *solv = vd; + Pool *pool = solv->pool; + const Id *a = va, *b = vb; /* (decision, reason, rid, bits, type, from, to, dep) */ + Solvable *as, *bs; + if (a[4] != b[4]) /* type */ + return a[4] - b[4]; + if (a[7] != b[7]) /* dep id */ + return a[7] - b[7]; + as = pool->solvables + a[5]; + bs = pool->solvables + b[5]; + if (as->name != bs->name) + return strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name)); + if (as->evr != bs->evr) + { + int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE); + if (r) + return r; + } + as = pool->solvables + a[6]; + bs = pool->solvables + b[6]; + if (as->name != bs->name) + return strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name)); + if (as->evr != bs->evr) + { + int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE); + if (r) + return r; + } + return 0; +} + +static void +decisionmerge(Solver *solv, Queue *q) +{ + Pool *pool = solv->pool; + int i, j; + + for (i = 0; i < q->count; i += 8) + { + Id p = q->elements[i] >= 0 ? q->elements[i] : -q->elements[i]; + int reason = q->elements[i + 1]; + int bits = q->elements[i + 3]; + Id name = pool->solvables[p].name; + for (j = i + 8; j < q->count; j += 8) + { + int merged; + p = q->elements[j] >= 0 ? q->elements[j] : -q->elements[j]; + if (reason != q->elements[j + 1] || name != pool->solvables[p].name) + break; + merged = solver_merge_decisioninfo_bits(solv, bits, q->elements[i + 4], q->elements[i + 5], q->elements[i + 6], q->elements[i + 7], q->elements[j + 3], q->elements[j + 4], q->elements[j + 5], q->elements[j + 6], q->elements[j + 7]); + if (!merged) + break; + bits = merged; + } + j -= 8; + for (; i < j; i += 8) + q->elements[i + 3] = bits; + } +} + +/* move a decison from position "from" to a smaller position "to" */ +static inline void +move_decision(Queue *q, int to, int from) +{ + queue_insertn(q, to, 8, 0); + memmove(q->elements + to, q->elements + from + 8, 8 * sizeof(Id)); + queue_deleten(q, from + 8, 8); +} + +/* sort a block of SOLVER_REASON_UNIT_RULE decisions */ +static void +sort_unit_decisions(Solver *solv, Queue *q, int start, int end, Map *m) +{ + Pool *pool = solv->pool; + int i, j, k, doing = 1; + if (start + 8 == end) + { + Id truelit = q->elements[start]; + MAPSET(m, truelit >= 0 ? truelit : -truelit); + return; + } + /* alternate between conflicts and installs, starting with conflicts */ + for (i = start; i < end; doing ^= 1) + { + for (j = k = i; j < end; j += 8) + { + Rule *or; + Id p, pp; + Id truelit = q->elements[j]; + if ((doing == 0 && truelit < 0) || (doing != 0 && truelit >= 0)) + continue; + or = solv->rules + q->elements[j + 2]; + FOR_RULELITERALS(p, pp, or) + if (p != truelit && !MAPTST(m, p >= 0 ? p : -p)) + break; + if (p) + continue; /* not unit yet */ + if (j > k) + move_decision(q, k, j); + k += 8; + } + if (k == i) + continue; + if (i + 8 < k) + solv_sort(q->elements + i, (k - i) / 8, 8 * sizeof(Id), decisionsort_cmp, solv); + for (; i < k; i += 8) + { + Id truelit = q->elements[i]; + MAPSET(m, truelit >= 0 ? truelit : -truelit); + } + } +} + +static void +solver_get_proof(Solver *solv, Id id, int flags, Queue *q) +{ + Pool *pool = solv->pool; + Map seen, seent; /* seent: was the literal true or false */ + Id rid, truelit; + int first = 1; + int why, i, cnt; + + queue_empty(q); + if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_PROBLEM) + why = solv->problems.elements[2 * id - 2]; + else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE && id >= solv->learntrules && id < solv->nrules) + why = solv->learnt_why.elements[id - solv->learntrules]; + else + return; + if (!solv->learnt_pool.elements[why]) + return; + + map_init(&seen, pool->nsolvables); + map_init(&seent, pool->nsolvables); + while ((rid = solv->learnt_pool.elements[why++]) != 0) + { + Rule *r = solv->rules + rid; + Id p, pp; + truelit = 0; + FOR_RULELITERALS(p, pp, r) + { + Id vv = p > 0 ? p : -p; + if (MAPTST(&seen, vv)) + { + if ((p > 0 ? 1 : 0) == (MAPTST(&seent, vv) ? 1 : 0)) + { + if (truelit) + abort(); + truelit = p; /* the one true literal! */ + } + } + else + { + /* a new literal. it must be false as the rule is unit */ + MAPSET(&seen, vv); + if (p < 0) + MAPSET(&seent, vv); + } + } + if (truelit) + queue_push(q, truelit); + else if (!first) + abort(); + queue_push(q, rid); + first = 0; + } + + /* add ruleinfo data to all rules (and also reverse the queue) */ + cnt = q->count; + for (i = q->count - 1; i >= 0; i -= 2) + { + SolverRuleinfo type; + Id from = 0, to = 0, dep = 0; + rid = q->elements[i]; + type = solver_ruleinfo(solv, rid, &from, &to, &dep); + if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS) + { + /* use pkg ruleinfo for choice/recommends rules */ + Id rid2 = solver_rule2pkgrule(solv, rid); + if (rid2) + type = solver_ruleinfo(solv, rid2, &from, &to, &dep); + } + queue_insertn(q, q->count, 8, 0); + q->elements[q->count - 8] = i > 0 ? q->elements[i - 1] : 0; + q->elements[q->count - 8 + 1] = i > 0 ? SOLVER_REASON_UNIT_RULE : SOLVER_REASON_UNSOLVABLE; + q->elements[q->count - 8 + 2] = rid; + q->elements[q->count - 8 + 4] = type; + q->elements[q->count - 8 + 5] = from; + q->elements[q->count - 8 + 6] = to; + q->elements[q->count - 8 + 7] = dep; + } + queue_deleten(q, 0, cnt); + + /* switch last two decisions if the unsolvable rule is of type SOLVER_RULE_RPM_SAME_NAME */ + if (q->count >= 16 && q->elements[q->count - 8 + 3] == SOLVER_RULE_RPM_SAME_NAME && q->elements[q->count - 16] > 0) + { + Rule *r = solv->rules + q->elements[q->count - 8 + 1]; + /* make sure that the rule is a binary conflict and it matches the installed element */ + if (r->p < 0 && (r->d == 0 || r->d == -1) && r->w2 < 0 + && (q->elements[q->count - 16] == -r->p || q->elements[q->count - 16] -r->w2)) + { + /* looks good! swap decisions and fixup truelit entries */ + move_decision(q, q->count - 16, q->count - 8); + q->elements[q->count - 16] = -q->elements[q->count - 8]; + q->elements[q->count - 8] = 0; + } + } + + /* put learnt rule premises in front */ + MAPZERO(&seen); + MAPSET(&seen, 1); + i = 0; + if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE) + { + /* insert learnt prereqs at front */ + Rule *r = solv->rules + id; + Id p, pp; + i = 0; + FOR_RULELITERALS(p, pp, r) + { + queue_insertn(q, i, 8, 0); + q->elements[i] = -p; + q->elements[i + 1] = SOLVER_REASON_PREMISE; + q->elements[i + 5] = p >= 0 ? p : -p; + MAPSET(&seen, p >= 0 ? p : -p); + i += 8; + } + } + + if (flags & SOLVER_DECISIONLIST_SORTED) + { + /* sort premise block */ + if (i > 8) + solv_sort(q->elements, i / 8, 8 * sizeof(Id), decisionsort_cmp, solv); + sort_unit_decisions(solv, q, i, q->count - 8, &seen); + } + + map_free(&seen); + map_free(&seent); + + if (!(flags & SOLVER_DECISIONLIST_WITHINFO)) + { + int j; + for (i = j = 0; i < q->count; i += 8) + { + q->elements[j++] = q->elements[i]; + q->elements[j++] = q->elements[i + 1]; + q->elements[j++] = q->elements[i + 2]; + } + queue_truncate(q, j); + } + else + { + /* set decisioninfo bits */ + for (i = 0; i < q->count; i += 8) + q->elements[i + 3] = solver_calc_decisioninfo_bits(solv, q->elements[i], q->elements[i + 4], q->elements[i + 5], q->elements[i + 6], q->elements[i + 7]); + if (flags & SOLVER_DECISIONLIST_MERGEDINFO) + decisionmerge(solv, q); + } +} + +void +solver_get_learnt(Solver *solv, Id id, int flags, Queue *q) +{ + int why = -1; + Queue todo; + + queue_empty(q); + queue_init(&todo); + if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_PROBLEM) + why = solv->problems.elements[2 * id - 2]; + else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_LEARNTRULE && id >= solv->learntrules && id < solv->nrules) + why = solv->learnt_why.elements[id - solv->learntrules]; + else if ((flags & SOLVER_DECISIONLIST_TYPEMASK) == SOLVER_DECISIONLIST_SOLVABLE) + { + int i, j, cnt; + solver_get_decisionlist(solv, id, 0, &todo); + cnt = todo.count; + for (i = 0; i < cnt; i += 3) + { + int rid = todo.elements[i + 2]; + if (rid < solv->learntrules || rid >= solv->nrules) + continue; + /* insert sorted and unified */ + for (j = 0; j < q->count; j++) + { + if (q->elements[j] < rid) + continue; + if (q->elements[j] == rid) + rid = 0; + break; + } + if (!rid) + continue; /* already in list */ + queue_insert(q, j, rid); + queue_push(&todo, solv->learnt_why.elements[rid - solv->learntrules]); + } + queue_deleten(&todo, 0, cnt); + } + else + { + queue_free(&todo); + return; + } + if (why >= 0) + queue_push(&todo, why); + while (todo.count) + { + int i, rid; + why = queue_pop(&todo); + while ((rid = solv->learnt_pool.elements[why++]) != 0) + { + if (rid < solv->learntrules || rid >= solv->nrules) + continue; + /* insert sorted and unified */ + for (i = 0; i < q->count; i++) + { + if (q->elements[i] < rid) + continue; + if (q->elements[i] == rid) + rid = 0; + break; + } + if (!rid) + continue; /* already in list */ + queue_insert(q, i, rid); + queue_push(&todo, solv->learnt_why.elements[rid - solv->learntrules]); + } + } + queue_free(&todo); +} + +static void +getdecisionlist(Solver *solv, Map *dm, int flags, Queue *decisionlistq) +{ + Pool *pool = solv->pool; + int i, ii, reason, info; + Queue iq; + + queue_empty(decisionlistq); + if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE) + return; + queue_init(&iq); + for (ii = solv->decisionq.count - 1; ii > 0; ii--) + { + Id v = solv->decisionq.elements[ii]; + Id vv = (v > 0 ? v : -v); + if (!MAPTST(dm, vv)) + continue; + info = solv->decisionq_why.elements[ii]; + if (info > 0) + reason = SOLVER_REASON_UNIT_RULE; + else if (info <= 0) + { + info = -info; + reason = solv->decisionmap[vv]; + reason = solv->decisionq_reason.elements[reason >= 0 ? reason : -reason]; + } + if (flags & (SOLVER_DECISIONLIST_SORTED | SOLVER_DECISIONLIST_WITHINFO)) + { + queue_insertn(decisionlistq, 0, 5, 0); + if (reason == SOLVER_REASON_WEAKDEP) + { + solver_allweakdepinfos(solv, v, &iq); + if (iq.count) + { + decisionlistq->elements[1] = iq.elements[0]; + decisionlistq->elements[2] = iq.elements[1]; + decisionlistq->elements[3] = iq.elements[2]; + decisionlistq->elements[4] = iq.elements[3]; + } + } + else if (info > 0) + { + Id from, to, dep; + int type = solver_ruleinfo(solv, info, &from, &to, &dep); + if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS) + { + /* use pkg ruleinfo for choice/recommends rules */ + Id rid2 = solver_rule2pkgrule(solv, info); + if (rid2) + type = solver_ruleinfo(solv, rid2, &from, &to, &dep); + } + decisionlistq->elements[1] = type; + decisionlistq->elements[2] = from; + decisionlistq->elements[3] = to; + decisionlistq->elements[4] = dep; + } + } + queue_unshift(decisionlistq, info); + queue_unshift(decisionlistq, reason); + queue_unshift(decisionlistq, v); + switch (reason) + { + case SOLVER_REASON_WEAKDEP: + if (v <= 0) + break; + solver_allweakdepinfos(solv, v, &iq); + for (i = 0; i < iq.count; i += 4) + { + if (iq.elements[i + 1]) + MAPSET(dm, iq.elements[i + 1]); + if (iq.elements[i + 2]) + MAPSET(dm, iq.elements[i + 2]); + else if (iq.elements[i] == SOLVER_RULE_PKG_SUPPLEMENTS) + { + Id p2, pp2, id = iq.elements[i + 3]; + FOR_PROVIDES(p2, pp2, id) + if (solv->decisionmap[p2] > 0) + MAPSET(dm, p2); + } + } + break; + case SOLVER_REASON_RESOLVE_JOB: + case SOLVER_REASON_UNIT_RULE: + case SOLVER_REASON_RESOLVE: + solver_ruleliterals(solv, info, &iq); + for (i = 0; i < iq.count; i++) + { + Id p2 = iq.elements[i]; + if (p2 < 0) + MAPSET(dm, -p2); + else if (solv->decisionmap[p2] > 0) + MAPSET(dm, p2); + } + break; + default: + break; + } + } + queue_free(&iq); + if ((flags & SOLVER_DECISIONLIST_SORTED) != 0) + { + /* reuse dm as "seen" map */ + MAPZERO(dm); + MAPSET(dm, 1); + for (i = 0; i < decisionlistq->count; i += 8) + { + if (decisionlistq->elements[i + 1] != SOLVER_REASON_UNIT_RULE) + { + Id p = decisionlistq->elements[i] < 0 ? -decisionlistq->elements[i] : decisionlistq->elements[i]; + MAPSET(dm, p); + } + else + { + int j; + for (j = i + 8; j < decisionlistq->count; j += 8) + if (decisionlistq->elements[j + 1] != SOLVER_REASON_UNIT_RULE) + break; + sort_unit_decisions(solv, decisionlistq, i, j, dm); + } + } + } + if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0) + { + /* set decisioninfo bits */ + for (i = 0; i < decisionlistq->count; i += 8) + decisionlistq->elements[i + 3] = solver_calc_decisioninfo_bits(solv, decisionlistq->elements[i], decisionlistq->elements[i + 4], decisionlistq->elements[i + 5], decisionlistq->elements[i + 6], decisionlistq->elements[i + 7]); + if (flags & SOLVER_DECISIONLIST_MERGEDINFO) + decisionmerge(solv, decisionlistq); + } + else if ((flags & SOLVER_DECISIONLIST_SORTED) != 0) + { + /* strip the info elements we added for sorting */ + int j; + for (i = j = 0; i < decisionlistq->count; i += 8) + { + decisionlistq->elements[j++] = decisionlistq->elements[i]; + decisionlistq->elements[j++] = decisionlistq->elements[i + 1]; + decisionlistq->elements[j++] = decisionlistq->elements[i + 2]; + } + queue_truncate(decisionlistq, j); + } +} + +void +solver_get_decisionlist(Solver *solv, Id id, int flags, Queue *decisionlistq) +{ + Pool *pool = solv->pool; + Map dm; + if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE) + return solver_get_proof(solv, id, flags, decisionlistq); + map_init(&dm, pool->nsolvables); + MAPSET(&dm, id); + getdecisionlist(solv, &dm, flags, decisionlistq); + map_free(&dm); + if (!decisionlistq->count) + { + queue_push(decisionlistq, -id); + queue_push2(decisionlistq, SOLVER_REASON_UNRELATED, 0); + if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0) + { + queue_push(decisionlistq, solver_calc_decisioninfo_bits(solv, -id, 0, 0, 0, 0)); + queue_push2(decisionlistq, 0, 0); + queue_push2(decisionlistq, 0, 0); + } + } +} + +void +solver_get_decisionlist_multiple(Solver *solv, Queue *idq, int flags, Queue *decisionlistq) +{ + Pool *pool = solv->pool; + int i; + Map dm; + queue_empty(decisionlistq); + if ((flags & SOLVER_DECISIONLIST_TYPEMASK) != SOLVER_DECISIONLIST_SOLVABLE) + return; + map_init(&dm, pool->nsolvables); + for (i = 0; i < idq->count; i++) + { + Id p = idq->elements[i]; + if (solv->decisionmap[p] != 0) + MAPSET(&dm, p); + } + getdecisionlist(solv, &dm, flags, decisionlistq); + map_free(&dm); + for (i = 0; i < idq->count; i++) + { + Id p = idq->elements[i]; + if (solv->decisionmap[p] != 0) + continue; + queue_push(decisionlistq, -p); + queue_push2(decisionlistq, SOLVER_REASON_UNRELATED, 0); + if ((flags & SOLVER_DECISIONLIST_WITHINFO) != 0) + { + queue_push(decisionlistq, solver_calc_decisioninfo_bits(solv, -p, 0, 0, 0, 0)); + queue_push2(decisionlistq, 0, 0); + queue_push2(decisionlistq, 0, 0); + } + } +} + + +const char * +solver_reason2str(Solver *solv, int reason) +{ + switch(reason) + { + case SOLVER_REASON_WEAKDEP: + return "a weak dependency"; + case SOLVER_REASON_RESOLVE_JOB: + return "a job rule"; + case SOLVER_REASON_RESOLVE: + return "a rule"; + case SOLVER_REASON_UNIT_RULE: + return "an unit rule"; + case SOLVER_REASON_KEEP_INSTALLED: + return "update/keep installed"; + case SOLVER_REASON_UPDATE_INSTALLED: + return "update installed"; + case SOLVER_REASON_CLEANDEPS_ERASE: + return "cleandeps erase"; + case SOLVER_REASON_RESOLVE_ORPHAN: + return "orphaned package"; + case SOLVER_REASON_UNSOLVABLE: + return "unsolvable"; + case SOLVER_REASON_PREMISE: + return "learnt rule premise"; + case SOLVER_REASON_UNRELATED: + return "it is unrelated"; + default: + break; + } + return "an unknown reason"; +} + +static const char * +decisionruleinfo2str(Solver *solv, Id decision, int type, Id from, Id to, Id dep) +{ + int bits = solver_calc_decisioninfo_bits(solv, decision, type, from, to, dep); + return solver_decisioninfo2str(solv, bits, type, from, to, dep); +} + +const char * +solver_decisionreason2str(Solver *solv, Id decision, int reason, Id info) +{ + if (reason == SOLVER_REASON_WEAKDEP && decision > 0) + { + Id from, to, dep; + int type = solver_weakdepinfo(solv, decision, &from, &to, &dep); + if (type) + return decisionruleinfo2str(solv, decision, type, from, to, dep); + } + if ((reason == SOLVER_REASON_RESOLVE_JOB || reason == SOLVER_REASON_UNIT_RULE || reason == SOLVER_REASON_RESOLVE || reason == SOLVER_REASON_UNSOLVABLE) && info > 0) + { + Id from, to, dep; + int type = solver_ruleinfo(solv, info, &from, &to, &dep); + if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS) + { + Id rid2 = solver_rule2pkgrule(solv, info); + if (rid2) + { + type = solver_ruleinfo(solv, rid2, &from, &to, &dep); + if (type) + return decisionruleinfo2str(solv, decision, type, from, to, dep); + } + } + if (type) + return decisionruleinfo2str(solv, decision, type, from, to, dep); + } + return solver_reason2str(solv, reason); +} + +/* decision merge state bits */ +#define DMS_INITED (1 << 0) +#define DMS_IDENTICAL_FROM (1 << 1) +#define DMS_IDENTICAL_TO (1 << 2) +#define DMS_MERGED (1 << 3) +#define DMS_NEGATIVE (1 << 4) +#define DMS_NOMERGE (1 << 5) + +/* add some bits about the decision and the ruleinfo so we can join decisions */ +int +solver_calc_decisioninfo_bits(Solver *solv, Id decision, int type, Id from, Id to, Id dep) +{ + Id decisionpkg = decision >= 0 ? decision : -decision; + int bits = DMS_INITED | (decision < 0 ? DMS_NEGATIVE : 0); + if (!decision) + return bits | DMS_NOMERGE; + switch (type) + { + case SOLVER_RULE_DISTUPGRADE: + case SOLVER_RULE_INFARCH: + case SOLVER_RULE_UPDATE: + case SOLVER_RULE_FEATURE: + case SOLVER_RULE_BLACK: + case SOLVER_RULE_STRICT_REPO_PRIORITY: + case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP: + case SOLVER_RULE_PKG_REQUIRES: + case SOLVER_RULE_PKG_RECOMMENDS: + case SOLVER_RULE_PKG_SUPPLEMENTS: + if (decisionpkg == from) + bits |= DMS_IDENTICAL_FROM; + break; + case SOLVER_RULE_PKG_SAME_NAME: + case SOLVER_RULE_PKG_CONFLICTS: + case SOLVER_RULE_PKG_OBSOLETES: + case SOLVER_RULE_PKG_INSTALLED_OBSOLETES: + case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES: + case SOLVER_RULE_PKG_CONSTRAINS: + if (decisionpkg == from) + bits |= DMS_IDENTICAL_FROM; + else if (decisionpkg == to) + bits |= DMS_IDENTICAL_TO; + break; + default: + break; + } + return bits; +} + +/* try to merge the ruleinfos of two decisions */ +int +solver_merge_decisioninfo_bits(Solver *solv, int bits1, int type1, Id from1, Id to1, Id dep1, int bits2, int type2, Id from2, Id to2, Id dep2) +{ + int merged = 0; + if (type1 != type2 || dep1 != dep2) + return 0; + if (!bits1 || !bits2 || ((bits1 | bits2) & DMS_NOMERGE) != 0 || ((bits1 ^ bits2) & DMS_NEGATIVE) != 0) + return 0; + merged = (((bits1 ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO)) | (bits2 ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO))) ^ (DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO)) | DMS_MERGED; + if (((bits1 & DMS_MERGED) != 0 && bits1 != merged) || ((bits2 & DMS_MERGED) != 0 && bits2 != merged)) + return 0; + if (((merged & DMS_IDENTICAL_FROM) == 0 && from1 != from2) || ((merged & DMS_IDENTICAL_TO) == 0 && to1 != to2)) + return 0; + return merged; +} + +void +solver_decisionlist_solvables(Solver *solv, Queue *decisionlistq, int pos, Queue *q) +{ + queue_empty(q); + for (; pos < decisionlistq->count; pos += 8) + { + Id p = decisionlistq->elements[pos]; + queue_push(q, p > 0 ? p : -p); + if ((decisionlistq->elements[pos + 3] & DMS_MERGED) == 0) + break; + } +} + +int +solver_decisionlist_merged(Solver *solv, Queue *decisionlistq, int pos) +{ + int cnt = 0; + for (; pos < decisionlistq->count; pos += 8, cnt++) + if ((decisionlistq->elements[pos + 3] & DMS_MERGED) == 0) + break; + return cnt; +} + +/* special version of solver_ruleinfo2str which supports merged decisions */ +const char * +solver_decisioninfo2str(Solver *solv, int bits, int type, Id from, Id to, Id dep) +{ + Pool *pool = solv->pool; + const char *s; + int multiple = bits & DMS_MERGED; + + /* use it/they variants if DMS_IDENTICAL_FROM is set */ + if ((bits & DMS_IDENTICAL_FROM) != 0) + { + switch (type) + { + case SOLVER_RULE_DISTUPGRADE: + return multiple ? "they do not belong to a distupgrade repository" : "it does not belong to a distupgrade repository"; + case SOLVER_RULE_INFARCH: + return multiple ? "they have inferior architecture": "it has inferior architecture"; + case SOLVER_RULE_UPDATE: + return multiple ? "they need to stay installed or be updated" : "it needs to stay installed or be updated"; + case SOLVER_RULE_FEATURE: + return multiple ? "they need to stay installed or be updated/downgraded" : "it needs to stay installed or be updated/downgraded"; + case SOLVER_RULE_BLACK: + return multiple ? "they can only be installed by a direct request" : "it can only be installed by a direct request"; + case SOLVER_RULE_STRICT_REPO_PRIORITY: + return multiple ? "they are excluded by strict repo priority" : "it is excluded by strict repo priority"; + + case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP: + return pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0); + case SOLVER_RULE_PKG_REQUIRES: + return pool_tmpjoin(pool, multiple ? "they require " : "it requires ", pool_dep2str(pool, dep), 0); + case SOLVER_RULE_PKG_RECOMMENDS: + return pool_tmpjoin(pool, multiple ? "they recommend " : "it recommends ", pool_dep2str(pool, dep), 0); + case SOLVER_RULE_PKG_SUPPLEMENTS: + s = pool_tmpjoin(pool, multiple ? "they supplement " : "it supplements ", pool_dep2str(pool, dep), 0); + if (to) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to)); + return s; + case SOLVER_RULE_PKG_SAME_NAME: + return pool_tmpappend(pool, multiple ? "they have the same name as " : "it has the same name as ", pool_solvid2str(pool, to), 0); + case SOLVER_RULE_PKG_CONFLICTS: + s = pool_tmpappend(pool, multiple ? "they conflict with " : "it conflicts with ", pool_dep2str(pool, dep), 0); + if (to) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to)); + return s; + case SOLVER_RULE_PKG_OBSOLETES: + s = pool_tmpappend(pool, multiple ? "they obsolete " : "it obsoletes ", pool_dep2str(pool, dep), 0); + if (to) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to)); + return s; + case SOLVER_RULE_PKG_INSTALLED_OBSOLETES: + s = pool_tmpjoin(pool, multiple ? "they are installed and obsolete " : "it is installed and obsoletes ", pool_dep2str(pool, dep), 0); + if (to) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to)); + return s; + case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES: + s = pool_tmpjoin(pool, multiple ? "they implicitly obsolete " : "it implicitly obsoletes ", pool_dep2str(pool, dep), 0); + if (to) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to)); + return s; + case SOLVER_RULE_PKG_CONSTRAINS: + s = pool_tmpappend(pool, multiple ? "they have constraint " : "it has constraint ", pool_dep2str(pool, dep), 0); + if (to) + s = pool_tmpappend(pool, s, " conflicting with ", pool_solvid2str(pool, to)); + return s; + default: + break; + } + } + + /* in some cases we can drop the "to" part if DMS_IDENTICAL_TO is set */ + if ((bits & DMS_IDENTICAL_TO) != 0) + { + switch (type) + { + case SOLVER_RULE_PKG_SAME_NAME: + return pool_tmpappend(pool, multiple ? "they have the same name as " : "it has the same name as ", pool_solvid2str(pool, from), 0); + case SOLVER_RULE_PKG_CONFLICTS: + case SOLVER_RULE_PKG_OBSOLETES: + case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES: + case SOLVER_RULE_PKG_INSTALLED_OBSOLETES: + case SOLVER_RULE_PKG_CONSTRAINS: + bits &= ~DMS_IDENTICAL_TO; + to = 0; + break; + default: + break; + } + } + + /* fallback to solver_ruleinfo2str if we can */ + if (multiple && (bits & (DMS_IDENTICAL_FROM|DMS_IDENTICAL_TO)) != 0) + return "unsupported decision merge?"; + return solver_ruleinfo2str(solv, type, from, to, dep); +} + +int +solver_alternatives_count(Solver *solv) +{ + Id *elements = solv->branches.elements; + int res, count; + for (res = 0, count = solv->branches.count; count; res++) + count -= elements[count - 2]; + return res; +} + +int +solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp) +{ + int cnt = solver_alternatives_count(solv); + int count = solv->branches.count; + Id *elements = solv->branches.elements; + if (choices) + queue_empty(choices); + if (alternative <= 0 || alternative > cnt) + return 0; + elements += count; + for (; cnt > alternative; cnt--) + elements -= elements[-2]; + if (levelp) + *levelp = elements[-1]; + if (fromp) + *fromp = elements[-4]; + if (idp) + *idp = elements[-3]; + if (chosenp) + { + int i; + *chosenp = 0; + for (i = elements[-2]; i > 4; i--) + { + Id p = -elements[-i]; + if (p > 0 && solv->decisionmap[p] == elements[-1] + 1) + { + *chosenp = p; + break; + } + } + } + if (choices) + queue_insertn(choices, 0, elements[-2] - 4, elements - elements[-2]); + return elements[-4] ? SOLVER_ALTERNATIVE_TYPE_RECOMMENDS : SOLVER_ALTERNATIVE_TYPE_RULE; +} + +static int +find_alternative_rule_from_learnt_rec(Solver *solv, int rid, Map *m, int cnt) +{ + Pool *pool = solv->pool; + int why = solv->learnt_why.elements[rid - solv->learntrules]; + while ((rid = solv->learnt_pool.elements[why++]) != 0) + { + Rule *r = solv->rules + rid; + Id p, pp; + int c; + if (rid >= solv->learntrules) + { + if ((rid = find_alternative_rule_from_learnt_rec(solv, rid, m, cnt))) + return rid; + continue; + } + c = 0; + FOR_RULELITERALS(p, pp, r) + if (p > 0 && MAPTST(m, p)) + c++; + if (c == cnt) /* all bits hit */ + return rid; + } + return 0; +} + +static int +find_alternative_rule_from_learnt(Solver *solv, int rid) +{ + Pool *pool = solv->pool; + Map m; + int i, count, cnt; + Id *elements = solv->branches.elements; + + /* find alternative by rule id */ + for (count = solv->branches.count; count; count -= elements[count - 2]) + if (elements[count - 4] == 0 && elements[count - 3] == rid) + break; + if (!count) + return 0; + map_init(&m, pool->nsolvables); + cnt = 0; + for (i = count - elements[count - 2]; i < count - 4; i++) + if (elements[i] > 0) + { + MAPSET(&m, elements[i]); + cnt++; + } + rid = find_alternative_rule_from_learnt_rec(solv, rid, &m, cnt); + map_free(&m); + return rid; +} + +int +solver_alternativeinfo(Solver *solv, int type, Id id, Id from, Id *fromp, Id *top, Id *depp) +{ + if (fromp) + *fromp = 0; + if (top) + *top = 0; + if (depp) + *depp = 0; + if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS) + { + if (fromp) + *fromp = from; + if (depp) + *depp = id; + return SOLVER_RULE_PKG_RECOMMENDS; + } + else if (type == SOLVER_ALTERNATIVE_TYPE_RULE) + { + int rclass = solver_ruleclass(solv, id); + if (rclass == SOLVER_RULE_LEARNT) + { + id = find_alternative_rule_from_learnt(solv, id); + if (!id) + return SOLVER_RULE_LEARNT; + rclass = solver_ruleclass(solv, id); + } + if (rclass == SOLVER_RULE_CHOICE || rclass == SOLVER_RULE_RECOMMENDS) + id = solver_rule2pkgrule(solv, id); + else if (rclass == SOLVER_RULE_BEST) + { + Id info = solv->bestrules_info[id - solv->bestrules]; + if (info > 0) + { + /* best update */ + if (fromp) + *fromp = info; + return SOLVER_RULE_UPDATE; + } + id = -info; /* best job, delegate to job rule */ + } + return solver_ruleinfo(solv, id, fromp, top, depp); + } + return 0; +} + +const char * +solver_alternative2str(Solver *solv, int type, Id id, Id from) +{ + const char *s; + Pool *pool = solv->pool; + Id to, dep; + type = solver_alternativeinfo(solv, type, id, from, &from, &to, &dep); + switch (type) + { + case SOLVER_RULE_PKG_RECOMMENDS: + s = pool_dep2str(pool, dep); + if (from) + s = pool_tmpappend(pool, s, ", recommended by ", pool_solvid2str(pool, from)); + return s; + case SOLVER_RULE_PKG_REQUIRES: + s = pool_dep2str(pool, dep); + if (from) + s = pool_tmpappend(pool, s, ", required by ", pool_solvid2str(pool, from)); + return s; + case SOLVER_RULE_PKG_CONFLICTS: + s = pool_dep2str(pool, dep); + if (from) + s = pool_tmpappend(pool, s, ", conflicted by ", pool_solvid2str(pool, from)); + return s; + case SOLVER_RULE_YUMOBS: + return pool_tmpjoin(pool, pool_id2str(pool, pool->solvables[to].name), ", obsoleting ", pool_dep2str(pool, dep));; + case SOLVER_RULE_JOB: + if ((to & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES || (to & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_NAME) + return pool_dep2str(pool, dep); + return solver_select2str(pool, to & SOLVER_SELECTMASK, dep); + case SOLVER_RULE_UPDATE: + case SOLVER_RULE_FEATURE: + return pool_solvid2str(pool, from); + default: + break; + } + return solver_ruleinfo2str(solv, type, from, to, dep); +} + diff --git a/src/knownid.h b/src/knownid.h index 2bb27b2..19f67ae 100644 --- a/src/knownid.h +++ b/src/knownid.h @@ -272,6 +272,7 @@ KNOWNID(SOLVABLE_LANGONLY, "solvable:langonly"), KNOWNID(UPDATE_COLLECTIONLIST, "update:collectionlist"), /* list of UPDATE_COLLECTION (actually packages) and UPDATE_MODULE */ KNOWNID(SOLVABLE_MULTIARCH, "solvable:multiarch"), /* debian multi-arch field */ +KNOWNID(SOLVABLE_SIGNATUREDATA, "solvable:signaturedata"), /* conda */ KNOWNID(ID_NUM_INTERNAL, 0) diff --git a/src/libsolv.ver b/src/libsolv.ver index c77f8d3..b9f9f96 100644 --- a/src/libsolv.ver +++ b/src/libsolv.ver @@ -113,6 +113,7 @@ SOLV_1.0 { pool_shrink_rels; pool_shrink_strings; pool_solvable2str; + pool_solvidset2str; pool_str2id; pool_strn2id; pool_tmpappend; @@ -341,9 +342,13 @@ SOLV_1.0 { solvable_trivial_installable_queue; solvable_trivial_installable_repo; solvable_unset; + solver_all_solutionelements; solver_allruleinfos; + solver_allweakdepinfos; solver_alternative2str; + solver_alternativeinfo; solver_alternatives_count; + solver_calc_decisioninfo_bits; solver_calc_duchanges; solver_calc_installsizechange; solver_calculate_multiversionmap; @@ -351,6 +356,10 @@ SOLV_1.0 { solver_create; solver_create_state_maps; solver_create_transaction; + solver_decisioninfo2str; + solver_decisionlist_merged; + solver_decisionlist_solvables; + solver_decisionreason2str; solver_describe_decision; solver_describe_weakdep_decision; solver_findallproblemrules; @@ -360,13 +369,17 @@ SOLV_1.0 { solver_get_alternative; solver_get_decisionblock; solver_get_decisionlevel; + solver_get_decisionlist; + solver_get_decisionlist_multiple; solver_get_decisionqueue; solver_get_flag; solver_get_lastdecisionblocklevel; + solver_get_learnt; solver_get_orphaned; solver_get_recommendations; solver_get_unneeded; solver_get_userinstalled; + solver_merge_decisioninfo_bits; solver_next_problem; solver_next_solution; solver_next_solutionelement; @@ -387,6 +400,7 @@ SOLV_1.0 { solver_problem2str; solver_problem_count; solver_problemruleinfo2str; + solver_reason2str; solver_rule2job; solver_rule2jobidx; solver_rule2pkgrule; @@ -394,12 +408,14 @@ SOLV_1.0 { solver_rule2solvable; solver_ruleclass; solver_ruleinfo; + solver_ruleinfo2str; solver_ruleliterals; solver_rulecmp; solver_select2str; solver_set_flag; solver_solution_count; solver_solutionelement2str; + solver_solutionelementtype2str; solver_solutionelement_count; solver_solutionelement_internalid; solver_solutionelement_extrajobflags; @@ -408,6 +424,7 @@ SOLV_1.0 { solver_take_solutionelement; solver_trivial_installable; solver_unifyrules; + solver_weakdepinfo; stringpool_clone; stringpool_free; stringpool_freehash; diff --git a/src/linkedpkg.c b/src/linkedpkg.c index 5912f98..26fb706 100644 --- a/src/linkedpkg.c +++ b/src/linkedpkg.c @@ -44,7 +44,7 @@ #ifdef ENABLE_LINKED_PKGS -void +static void find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp) { Id req = 0; @@ -121,7 +121,7 @@ find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp *prvidp = prv; } -void +static void find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp) { Id p, pp, namerelid; @@ -232,7 +232,7 @@ find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Qu *prvidp = solvable_selfprovidedep(s); } -void +static void find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp) { Id p, pp, *pr, apevr = 0, aprel = 0; @@ -274,37 +274,31 @@ find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Qu *prvidp = aprel; } -/* the following two functions are used in solvable_lookup_str_base to do +/* the following function is used in solvable_lookup_str_base to do * translated lookups on the product/pattern packages */ Id -find_autopattern_name(Pool *pool, Solvable *s) +find_autopackage_name(Pool *pool, Solvable *s) { + const char *autoprv = 0; + const char *n = pool_id2str(pool, s->name); Id prv, *prvp; - if (!s->provides) - return 0; - for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; ) - if (ISRELDEP(prv)) - { - Reldep *rd = GETRELDEP(pool, prv); - if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autopattern()")) - return strncmp(pool_id2str(pool, rd->evr), "pattern:", 8) != 0 ? rd->evr : 0; - } - return 0; -} -Id -find_autoproduct_name(Pool *pool, Solvable *s) -{ - Id prv, *prvp; - if (!s->provides) + if (*n == 'p') + { + if (!strncmp("pattern:", n, 8)) + autoprv = "autopattern()"; + if (!strncmp("product:", n, 8)) + autoprv = "autoproduct()"; + } + if (!autoprv) return 0; for (prvp = s->repo->idarraydata + s->provides; (prv = *prvp++) != 0; ) if (ISRELDEP(prv)) { Reldep *rd = GETRELDEP(pool, prv); - if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), "autoproduct()")) - return strncmp(pool_id2str(pool, rd->evr), "product:", 8) != 0 ? rd->evr : 0; + if (rd->flags == REL_EQ && !strcmp(pool_id2str(pool, rd->name), autoprv)) + return rd->evr; } return 0; } diff --git a/src/linkedpkg.h b/src/linkedpkg.h index 51b82a5..4cf5561 100644 --- a/src/linkedpkg.h +++ b/src/linkedpkg.h @@ -25,12 +25,7 @@ has_package_link(Pool *pool, Solvable *s) return 0; } -extern void find_application_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp); -extern void find_product_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp); -extern void find_pattern_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp); - -extern Id find_autopattern_name(Pool *pool, Solvable *s); -extern Id find_autoproduct_name(Pool *pool, Solvable *s); +extern Id find_autopackage_name(Pool *pool, Solvable *s); /* generic */ extern void find_package_link(Pool *pool, Solvable *s, Id *reqidp, Queue *qr, Id *prvidp, Queue *qp); diff --git a/src/policy.c b/src/policy.c index c02d237..02548ea 100644 --- a/src/policy.c +++ b/src/policy.c @@ -232,13 +232,13 @@ check_complex_dep(Solver *solv, Id dep, Map *m, Queue **cqp) { Pool *pool = solv->pool; Queue q; - queue_init(&q); Id p; int i, qcnt; #if 0 printf("check_complex_dep %s\n", pool_dep2str(pool, dep)); #endif + queue_init(&q); i = pool_normalize_complex_dep(pool, dep, &q, CPLXDEPS_EXPAND); if (i == 0 || i == 1) { @@ -1511,7 +1511,7 @@ policy_create_obsolete_index(Solver *solv) { FOR_PROVIDES(p, pp, obs) { - Solvable *ps = pool->solvables + p;; + Solvable *ps = pool->solvables + p; if (ps->repo != installed) continue; if (ps->name == s->name) @@ -1545,7 +1545,7 @@ policy_create_obsolete_index(Solver *solv) { FOR_PROVIDES(p, pp, obs) { - Solvable *ps = pool->solvables + p;; + Solvable *ps = pool->solvables + p; if (ps->repo != installed) continue; if (ps->name == s->name) @@ -1562,6 +1562,29 @@ policy_create_obsolete_index(Solver *solv) } +/* return true if solvable s obsoletes solvable with id pi */ +static inline int +is_obsoleting(Pool *pool, Solvable *s, Id pi) +{ + Id p, pp, obs, *obsp; + Solvable *si = pool->solvables + pi; + if (pool->obsoleteusescolors && !pool_colormatch(pool, si, s)) + return 0; + obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) /* for all obsoletes */ + { + FOR_PROVIDES(p, pp, obs) /* and all matching providers of the obsoletes */ + { + if (p != pi) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, si, obs)) + continue; + return 1; + } + } + return 0; +} + /* * find update candidates * @@ -1576,8 +1599,7 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all) { /* installed packages get a special upgrade allowed rule */ Pool *pool = solv->pool; - Id p, pp, n, p2, pp2; - Id obs, *obsp; + Id p, pp, n; Solvable *ps; int haveprovobs = 0; int allowdowngrade = allow_all ? 1 : solv->allowdowngrade; @@ -1618,31 +1640,14 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all) continue; else if ((!solv->noupdateprovide || solv->needupdateprovide) && ps->obsoletes) /* provides/obsoletes combination ? */ { - /* check if package ps obsoletes installed package s */ + /* check if package ps that provides s->name obsoletes installed package s */ /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless * use it to limit our update candidates */ - if ((pool->obsoleteusescolors || pool->implicitobsoleteusescolors) && !pool_colormatch(pool, s, ps)) + if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps)) continue; - obsp = ps->repo->idarraydata + ps->obsoletes; - while ((obs = *obsp++) != 0) /* for all obsoletes */ - { - FOR_PROVIDES(p2, pp2, obs) /* and all matching providers of the obsoletes */ - { - Solvable *ps2 = pool->solvables + p2; - if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps2, obs)) - continue; - if (p2 == n) /* match ! */ - break; - } - if (p2) /* match! */ - break; - } - if (!obs) /* continue if no match */ + if (!is_obsoleting(pool, ps, n)) continue; - /* here we have 'p' with a matching provides/obsoletes combination - * thus flagging p as a valid update candidate for s - */ - haveprovobs = 1; + haveprovobs = 1; /* have matching provides/obsoletes combination */ } else continue; @@ -1664,14 +1669,14 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all) for (opp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *opp++) != 0;) { ps = pool->solvables + p; - if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps)) - continue; - if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps)) - continue; /* implicitobsoleteusescolors is somewhat wrong here, but we nevertheless * use it to limit our update candidates */ if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps)) continue; + if (!allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps)) + continue; + if (!allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps)) + continue; queue_push(qs, p); } } @@ -1461,7 +1461,7 @@ pool_whatmatchesdep(Pool *pool, Id keyname, Id dep, Queue *q, int marker) { Id pp; FOR_PROVIDES(p, pp, dep) - if (pool_match_dep(pool, p, dep)) + if (pool_match_nevr(pool, pool->solvables + p, dep)) queue_push(q, p); return; } @@ -36,13 +36,11 @@ extern "C" { /*----------------------------------------------- */ -struct s_Repo; -struct s_Repodata; struct s_Repokey; struct s_KeyValue; typedef struct s_Datapos { - struct s_Repo *repo; + Repo *repo; Id solvid; Id repodataid; Id schema; @@ -66,16 +64,16 @@ struct s_Pool_tmpspace { struct s_Pool { void *appdata; /* application private pointer */ - struct s_Stringpool ss; + Stringpool ss; Reldep *rels; /* table of rels: Id -> Reldep */ int nrels; /* number of unique rels */ - struct s_Repo **repos; + Repo **repos; int nrepos; /* repos allocated */ int urepos; /* repos in use */ - struct s_Repo *installed; /* packages considered installed */ + Repo *installed; /* packages considered installed */ Solvable *solvables; int nsolvables; /* solvables allocated */ @@ -109,16 +107,16 @@ struct s_Pool { Map *considered; /* callback for REL_NAMESPACE dependencies handled by the application */ - Id (*nscallback)(struct s_Pool *, void *data, Id name, Id evr); + Id (*nscallback)(Pool *, void *data, Id name, Id evr); void *nscallbackdata; /* debug mask and callback */ int debugmask; - void (*debugcallback)(struct s_Pool *, void *data, int type, const char *str); + void (*debugcallback)(Pool *, void *data, int type, const char *str); void *debugcallbackdata; /* load callback */ - int (*loadcallback)(struct s_Pool *, struct s_Repodata *, void *); + int (*loadcallback)(Pool *, Repodata *, void *); void *loadcallbackdata; /* search position */ @@ -155,7 +153,7 @@ struct s_Pool { char *rootdir; - int (*custom_vendorcheck)(struct s_Pool *, Solvable *, Solvable *); + int (*custom_vendorcheck)(Pool *, Solvable *, Solvable *); int addfileprovidesfiltered; /* 1: only use filtered file list for addfileprovides */ int addedfileprovides; /* true: application called addfileprovides */ @@ -251,14 +249,14 @@ extern int pool_set_flag(Pool *pool, int flag, int value); extern int pool_get_flag(Pool *pool, int flag); extern void pool_debug(Pool *pool, int type, const char *format, ...) __attribute__((format(printf, 3, 4))); -extern void pool_setdebugcallback(Pool *pool, void (*debugcallback)(struct s_Pool *, void *data, int type, const char *str), void *debugcallbackdata); +extern void pool_setdebugcallback(Pool *pool, void (*debugcallback)(Pool *pool, void *data, int type, const char *str), void *debugcallbackdata); extern void pool_setdebugmask(Pool *pool, int mask); -extern void pool_setloadcallback(Pool *pool, int (*cb)(struct s_Pool *, struct s_Repodata *, void *), void *loadcbdata); -extern void pool_setnamespacecallback(Pool *pool, Id (*cb)(struct s_Pool *, void *, Id, Id), void *nscbdata); +extern void pool_setloadcallback(Pool *pool, int (*cb)(Pool *, Repodata *, void *), void *loadcbdata); +extern void pool_setnamespacecallback(Pool *pool, Id (*cb)(Pool *, void *, Id, Id), void *nscbdata); extern void pool_flush_namespaceproviders(Pool *pool, Id ns, Id evr); -extern void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(struct s_Pool *, Solvable *, Solvable *)); -extern int (*pool_get_custom_vendorcheck(Pool *pool))(struct s_Pool *, Solvable *, Solvable *); +extern void pool_set_custom_vendorcheck(Pool *pool, int (*vendorcheck)(Pool *, Solvable *, Solvable *)); +extern int (*pool_get_custom_vendorcheck(Pool *pool))(Pool *, Solvable *, Solvable *); extern char *pool_alloctmpspace(Pool *pool, int len); extern void pool_freetmpspace(Pool *pool, const char *space); @@ -266,7 +264,7 @@ extern char *pool_tmpjoin(Pool *pool, const char *str1, const char *str2, const extern char *pool_tmpappend(Pool *pool, const char *str1, const char *str2, const char *str3); extern const char *pool_bin2hex(Pool *pool, const unsigned char *buf, int len); -extern void pool_set_installed(Pool *pool, struct s_Repo *repo); +extern void pool_set_installed(Pool *pool, Repo *repo); extern int pool_error(Pool *pool, int ret, const char *format, ...) __attribute__((format(printf, 3, 4))); extern char *pool_errstr(Pool *pool); @@ -297,6 +295,7 @@ static inline const char *pool_solvid2str(Pool *pool, Id p) { return pool_solvable2str(pool, pool->solvables + p); } +extern const char *pool_solvidset2str(Pool *pool, Queue *q); void pool_set_languages(Pool *pool, const char **languages, int nlanguages); Id pool_id2langid(Pool *pool, Id id, const char *lang, int create); @@ -362,7 +361,7 @@ void pool_set_whatprovides(Pool *pool, Id id, Id providers); * key - search only this key * match - key must match this string */ -void pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, struct s_Repodata *data, struct s_Repokey *key, struct s_KeyValue *kv), void *cbdata); +void pool_search(Pool *pool, Id p, Id key, const char *match, int flags, int (*callback)(void *cbdata, Solvable *s, Repodata *data, struct s_Repokey *key, struct s_KeyValue *kv), void *cbdata); void pool_clear_pos(Pool *pool); diff --git a/src/poolarch.c b/src/poolarch.c index 6727760..e5fb5bf 100644 --- a/src/poolarch.c +++ b/src/poolarch.c @@ -22,8 +22,14 @@ static const char *archpolicies[] = { #if defined(FEDORA) || defined(MAGEIA) + "x86_64_v4", "x86_64_v4:x86_64_v3:x86_64_v2:x86_64:athlon:i686:i586:i486:i386", + "x86_64_v3", "x86_64_v3:x86_64_v2:x86_64:athlon:i686:i586:i486:i386", + "x86_64_v2", "x86_64_v2:x86_64:athlon:i686:i586:i486:i386", "x86_64", "x86_64:athlon:i686:i586:i486:i386", #else + "x86_64_v4", "x86_64_v4:x86_64_v3:x86_64_v2:x86_64:i686:i586:i486:i386", + "x86_64_v3", "x86_64_v3:x86_64_v2:x86_64:i686:i586:i486:i386", + "x86_64_v2", "x86_64_v2:x86_64:i686:i586:i486:i386", "x86_64", "x86_64:i686:i586:i486:i386", #endif "i686", "i686:i586:i486:i386", diff --git a/src/pooltypes.h b/src/pooltypes.h index c69f375..bccdfc1 100644 --- a/src/pooltypes.h +++ b/src/pooltypes.h @@ -13,6 +13,10 @@ #ifndef LIBSOLV_POOLTYPES_H #define LIBSOLV_POOLTYPES_H +#ifdef __cplusplus +extern "C" { +#endif + /* format version number for .solv files */ #define SOLV_VERSION_0 0 #define SOLV_VERSION_1 1 @@ -36,10 +40,23 @@ typedef struct s_Stringpool Stringpool; struct s_Pool; typedef struct s_Pool Pool; +struct s_Repo; +typedef struct s_Repo Repo; + +struct s_Repodata; +typedef struct s_Repodata Repodata; + +struct s_Solvable; +typedef struct s_Solvable Solvable; + /* identifier for string values */ typedef int Id; /* must be signed!, since negative Id is used in solver rules to denote negation */ /* offset value, e.g. used to 'point' into the stringspace */ typedef unsigned int Offset; +#ifdef __cplusplus +} +#endif + #endif /* LIBSOLV_POOLTYPES_H */ diff --git a/src/problems.c b/src/problems.c index 0bd48d4..b0a421c 100644 --- a/src/problems.c +++ b/src/problems.c @@ -22,6 +22,7 @@ #include "pool.h" #include "util.h" #include "evr.h" +#include "policy.h" #include "solverdebug.h" /**********************************************************************************/ @@ -220,6 +221,16 @@ solver_disableproblemset(Solver *solv, int start) solver_disableproblem(solv, solv->problems.elements[i]); } +#ifdef SUSE +static inline int +suse_isptf(Pool *pool, Solvable *s) +{ + if (!strncmp("ptf-", pool_id2str(pool, s->name), 4)) + return 1; + return 0; +} +#endif + /*------------------------------------------------------------------- * try to fix a problem by auto-uninstalling packages */ @@ -250,6 +261,10 @@ solver_autouninstall(Solver *solv, int start) Id p = solv->installed->start + (v - solv->updaterules); if (m && !MAPTST(m, v - solv->updaterules)) continue; +#ifdef SUSE + if (suse_isptf(pool, pool->solvables + p)) + continue; /* do not autouninstall ptf packages */ +#endif if (pool->considered && !MAPTST(pool->considered, p)) continue; /* do not uninstalled disabled packages */ if (solv->bestrules_info && solv->bestrules_end > solv->bestrules) @@ -972,6 +987,7 @@ solver_solutionelement_internalid(Solver *solv, Id problem, Id solution) return solv->solutions.elements[solidx + 2 * solv->solutions.elements[solidx] + 3]; } +/* currently just SOLVER_CLEANDEPS */ Id solver_solutionelement_extrajobflags(Solver *solv, Id problem, Id solution) { @@ -1026,6 +1042,56 @@ solver_next_solutionelement(Solver *solv, Id problem, Id solution, Id element, I return element + 1; } +static inline void +queue_push3(Queue *q, Id id1, Id id2, Id id3) +{ + queue_push(q, id1); + queue_push2(q, id2, id3); +} + +static void +add_expanded_replace(Solver *solv, Id p, Id rp, Queue *q) +{ + int illegal = policy_is_illegal(solv, solv->pool->solvables + p, solv->pool->solvables + rp, 0); + if ((illegal & POLICY_ILLEGAL_DOWNGRADE) != 0) + queue_push3(q, SOLVER_SOLUTION_REPLACE_DOWNGRADE, p, rp); + if ((illegal & POLICY_ILLEGAL_ARCHCHANGE) != 0) + queue_push3(q, SOLVER_SOLUTION_REPLACE_ARCHCHANGE, p, rp); + if ((illegal & POLICY_ILLEGAL_VENDORCHANGE) != 0) + queue_push3(q, SOLVER_SOLUTION_REPLACE_VENDORCHANGE, p, rp); + if ((illegal & POLICY_ILLEGAL_NAMECHANGE) != 0) + queue_push3(q, SOLVER_SOLUTION_REPLACE_NAMECHANGE, p, rp); + if (!illegal || (illegal & ~(POLICY_ILLEGAL_DOWNGRADE | POLICY_ILLEGAL_ARCHCHANGE | POLICY_ILLEGAL_VENDORCHANGE | POLICY_ILLEGAL_NAMECHANGE))) + queue_push3(q, SOLVER_SOLUTION_REPLACE, p, rp); +} + +/* solutionelements are (type, p, rp) triplets */ +void +solver_all_solutionelements(Solver *solv, Id problem, Id solution, int expandreplaces, Queue *q) +{ + int i, cnt; + Id solidx = solv->problems.elements[problem * 2 - 1]; + solidx = solv->solutions.elements[solidx + solution]; + queue_empty(q); + if (!solidx) + return; + cnt = solv->solutions.elements[solidx++]; + for (i = 0; i < cnt; i++) + { + Id p = solv->solutions.elements[solidx++]; + Id rp = solv->solutions.elements[solidx++]; + if (p > 0) + { + if (rp && expandreplaces) + add_expanded_replace(solv, p, rp, q); + else + queue_push3(q, rp ? SOLVER_SOLUTION_REPLACE : SOLVER_SOLUTION_ERASE, p, rp); + } + else + queue_push3(q, p, rp, 0); + } +} + void solver_take_solutionelement(Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job) { @@ -1043,6 +1109,11 @@ solver_take_solutionelement(Solver *solv, Id p, Id rp, Id extrajobflags, Queue * job->elements[rp] = 0; return; } + if (p == SOLVER_SOLUTION_ERASE) + { + p = rp; + rp = 0; + } if (rp <= 0 && p <= 0) return; /* just in case */ if (rp > 0) @@ -1388,62 +1459,66 @@ solver_problem2str(Solver *solv, Id problem) } const char * -solver_solutionelement2str(Solver *solv, Id p, Id rp) +solver_solutionelementtype2str(Solver *solv, int type, Id p, Id rp) { Pool *pool = solv->pool; - if (p == SOLVER_SOLUTION_JOB || p == SOLVER_SOLUTION_POOLJOB) - { - Id how, what; - if (p == SOLVER_SOLUTION_JOB) - rp += solv->pooljobcnt; - how = solv->job.elements[rp - 1]; - what = solv->job.elements[rp]; - return pool_tmpjoin(pool, "do not ask to ", pool_job2str(pool, how, what, 0), 0); - } - else if (p == SOLVER_SOLUTION_INFARCH) + Solvable *s; + const char *str; + + switch (type) { - Solvable *s = pool->solvables + rp; + case SOLVER_SOLUTION_JOB: + case SOLVER_SOLUTION_POOLJOB: + if (type == SOLVER_SOLUTION_JOB) + p += solv->pooljobcnt; + return pool_tmpjoin(pool, "do not ask to ", pool_job2str(pool, solv->job.elements[p - 1], solv->job.elements[p], 0), 0); + case SOLVER_SOLUTION_INFARCH: + s = pool->solvables + p; if (solv->installed && s->repo == solv->installed) return pool_tmpjoin(pool, "keep ", pool_solvable2str(pool, s), " despite the inferior architecture"); else return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the inferior architecture"); - } - else if (p == SOLVER_SOLUTION_DISTUPGRADE) - { - Solvable *s = pool->solvables + rp; + case SOLVER_SOLUTION_DISTUPGRADE: + s = pool->solvables + p; if (solv->installed && s->repo == solv->installed) return pool_tmpjoin(pool, "keep obsolete ", pool_solvable2str(pool, s), 0); else return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " from excluded repository"); - } - else if (p == SOLVER_SOLUTION_BEST) - { - Solvable *s = pool->solvables + rp; + case SOLVER_SOLUTION_BEST: + s = pool->solvables + p; if (solv->installed && s->repo == solv->installed) return pool_tmpjoin(pool, "keep old ", pool_solvable2str(pool, s), 0); else return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the old version"); + case SOLVER_SOLUTION_BLACK: + return pool_tmpjoin(pool, "install ", pool_solvid2str(pool, p), 0); + case SOLVER_SOLUTION_STRICTREPOPRIORITY: + return pool_tmpjoin(pool, "install ", pool_solvid2str(pool, p), " despite the repo priority"); + + /* replace types: p -> rp */ + case SOLVER_SOLUTION_ERASE: + return pool_tmpjoin(pool, "allow deinstallation of ", pool_solvid2str(pool, p), 0); + case SOLVER_SOLUTION_REPLACE: + str = pool_tmpjoin(pool, "allow replacement of ", pool_solvid2str(pool, p), 0); + return pool_tmpappend(pool, str, " with ", pool_solvid2str(pool, rp)); + case SOLVER_SOLUTION_REPLACE_DOWNGRADE: + return pool_tmpjoin(pool, "allow ", policy_illegal2str(solv, POLICY_ILLEGAL_DOWNGRADE, pool->solvables + p, pool->solvables + rp), 0); + case SOLVER_SOLUTION_REPLACE_ARCHCHANGE: + return pool_tmpjoin(pool, "allow ", policy_illegal2str(solv, POLICY_ILLEGAL_ARCHCHANGE, pool->solvables + p, pool->solvables + rp), 0); + case SOLVER_SOLUTION_REPLACE_VENDORCHANGE: + return pool_tmpjoin(pool, "allow ", policy_illegal2str(solv, POLICY_ILLEGAL_VENDORCHANGE, pool->solvables + p, pool->solvables + rp), 0); + case SOLVER_SOLUTION_REPLACE_NAMECHANGE: + return pool_tmpjoin(pool, "allow ", policy_illegal2str(solv, POLICY_ILLEGAL_NAMECHANGE, pool->solvables + p, pool->solvables + rp), 0); + default: + break; } - else if (p == SOLVER_SOLUTION_BLACK) - { - Solvable *s = pool->solvables + rp; - return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), 0); - } - else if (p > 0 && rp == 0) - return pool_tmpjoin(pool, "allow deinstallation of ", pool_solvid2str(pool, p), 0); - else if (p == SOLVER_SOLUTION_STRICTREPOPRIORITY) - { - Solvable *s = pool->solvables + rp; - return pool_tmpjoin(pool, "install ", pool_solvable2str(pool, s), " despite the repo priority"); - } - else if (p > 0 && rp > 0) - { - const char *sp = pool_solvid2str(pool, p); - const char *srp = pool_solvid2str(pool, rp); - const char *str = pool_tmpjoin(pool, "allow replacement of ", sp, 0); - return pool_tmpappend(pool, str, " with ", srp); - } - else - return "bad solution element"; + return "bad solution element"; } +const char * +solver_solutionelement2str(Solver *solv, Id p, Id rp) +{ + if (p > 0) + return solver_solutionelementtype2str(solv, rp ? SOLVER_SOLUTION_REPLACE : SOLVER_SOLUTION_ERASE, p, rp); + return solver_solutionelementtype2str(solv, p, rp, 0); +} diff --git a/src/problems.h b/src/problems.h index f9b1efc..9e4ad1f 100644 --- a/src/problems.h +++ b/src/problems.h @@ -29,6 +29,13 @@ struct s_Solver; #define SOLVER_SOLUTION_POOLJOB (-4) #define SOLVER_SOLUTION_BLACK (-5) #define SOLVER_SOLUTION_STRICTREPOPRIORITY (-6) +/* replace solution types */ +#define SOLVER_SOLUTION_ERASE (-100) +#define SOLVER_SOLUTION_REPLACE (-101) +#define SOLVER_SOLUTION_REPLACE_DOWNGRADE (-102) +#define SOLVER_SOLUTION_REPLACE_ARCHCHANGE (-103) +#define SOLVER_SOLUTION_REPLACE_VENDORCHANGE (-104) +#define SOLVER_SOLUTION_REPLACE_NAMECHANGE (-105) void solver_recordproblem(struct s_Solver *solv, Id rid); void solver_fixproblem(struct s_Solver *solv, Id rid); @@ -45,6 +52,7 @@ unsigned int solver_solutionelement_count(struct s_Solver *solv, Id problem, Id Id solver_solutionelement_internalid(struct s_Solver *solv, Id problem, Id solution); Id solver_solutionelement_extrajobflags(struct s_Solver *solv, Id problem, Id solution); Id solver_next_solutionelement(struct s_Solver *solv, Id problem, Id solution, Id element, Id *p, Id *rp); +void solver_all_solutionelements(struct s_Solver *solv, Id problem, Id solution, int expandreplaces, Queue *q); void solver_take_solutionelement(struct s_Solver *solv, Id p, Id rp, Id extrajobflags, Queue *job); void solver_take_solution(struct s_Solver *solv, Id problem, Id solution, Queue *job); @@ -55,6 +63,7 @@ void solver_findallproblemrules(struct s_Solver *solv, Id problem, Queue *rules) extern const char *solver_problemruleinfo2str(struct s_Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep); extern const char *solver_problem2str(struct s_Solver *solv, Id problem); extern const char *solver_solutionelement2str(struct s_Solver *solv, Id p, Id rp); +extern const char *solver_solutionelementtype2str(struct s_Solver *solv, int type, Id p, Id rp); #ifdef __cplusplus } @@ -24,7 +24,7 @@ extern "C" { #endif -typedef struct s_Repo { +struct s_Repo { const char *name; /* name pointer */ Id repoid; /* our id */ void *appdata; /* application private pointer */ @@ -56,7 +56,7 @@ typedef struct s_Repo { int lastmarker; Offset lastmarkerpos; #endif /* LIBSOLV_INTERNAL */ -} Repo; +}; extern Repo *repo_create(Pool *pool, const char *name); extern void repo_free(Repo *repo, int reuseids); diff --git a/src/repo_write.c b/src/repo_write.c index a73eebf..c965a76 100644 --- a/src/repo_write.c +++ b/src/repo_write.c @@ -266,6 +266,8 @@ struct extdata { int len; }; +#define DIRIDCACHE_SIZE 1024 + struct cbdata { Pool *pool; Repo *repo; @@ -300,6 +302,8 @@ struct cbdata { Id lastdirid; /* last dir id seen in this repodata */ Id lastdirid_own; /* last dir id put in own pool */ + + Id diridcache[3 * DIRIDCACHE_SIZE]; }; #define NEEDID_BLOCK 1023 @@ -564,22 +568,40 @@ putinownpool(struct cbdata *cbdata, Repodata *data, Id id) static Id putinowndirpool_slow(struct cbdata *cbdata, Repodata *data, Dirpool *dp, Id dir) { - Id compid, parent; + Id compid, parent, id; + Id *cacheent; parent = dirpool_parent(dp, dir); if (parent) - parent = putinowndirpool_slow(cbdata, data, dp, parent); + { + /* put parent in own pool first */ + cacheent = cbdata->diridcache + (parent & (DIRIDCACHE_SIZE - 1)); + if (cacheent[0] == parent && cacheent[DIRIDCACHE_SIZE] == data->repodataid) + parent = cacheent[2 * DIRIDCACHE_SIZE]; + else + parent = putinowndirpool_slow(cbdata, data, dp, parent); + } compid = dirpool_compid(dp, dir); if (cbdata->ownspool && compid > 1 && (!cbdata->clonepool || data->localpool)) compid = putinownpool(cbdata, data, compid); - return dirpool_add_dir(cbdata->owndirpool, parent, compid, 1); + id = dirpool_add_dir(cbdata->owndirpool, parent, compid, 1); + /* cache result */ + cacheent = cbdata->diridcache + (dir & (DIRIDCACHE_SIZE - 1)); + cacheent[0] = dir; + cacheent[DIRIDCACHE_SIZE] = data->repodataid; + cacheent[2 * DIRIDCACHE_SIZE] = id; + return id; } static inline Id putinowndirpool(struct cbdata *cbdata, Repodata *data, Id dir) { + Id *cacheent; if (dir && dir == cbdata->lastdirid) return cbdata->lastdirid_own; + cacheent = cbdata->diridcache + (dir & (DIRIDCACHE_SIZE - 1)); + if (dir && cacheent[0] == dir && cacheent[DIRIDCACHE_SIZE] == data->repodataid) + return cacheent[2 * DIRIDCACHE_SIZE]; cbdata->lastdirid = dir; cbdata->lastdirid_own = putinowndirpool_slow(cbdata, data, &data->dirpool, dir); return cbdata->lastdirid_own; @@ -1019,6 +1041,7 @@ static Id verticals[] = { SOLVABLE_LEADSIGID, SOLVABLE_CHANGELOG_AUTHOR, SOLVABLE_CHANGELOG_TEXT, + SOLVABLE_SIGNATUREDATA, 0 }; diff --git a/src/repodata.h b/src/repodata.h index 2504f2a..fda463e 100644 --- a/src/repodata.h +++ b/src/repodata.h @@ -34,7 +34,6 @@ extern "C" { #define SIZEOF_SHA384 48 #define SIZEOF_SHA512 64 -struct s_Repo; struct s_KeyValue; typedef struct s_Repokey { @@ -67,13 +66,13 @@ struct dircache; #define REPODATA_FILELIST_FILTERED 1 #define REPODATA_FILELIST_EXTENSION 2 -typedef struct s_Repodata { +struct s_Repodata { Id repodataid; /* our id */ - struct s_Repo *repo; /* back pointer to repo */ + Repo *repo; /* back pointer to repo */ int state; /* available, stub or error */ - void (*loadcallback)(struct s_Repodata *); + void (*loadcallback)(Repodata *); int start; /* start of solvables this repodata is valid for */ int end; /* last solvable + 1 of this repodata */ @@ -140,7 +139,7 @@ typedef struct s_Repodata { struct dircache *dircache; #endif -} Repodata; +}; #define SOLVID_META -1 #define SOLVID_POS -2 @@ -149,7 +148,7 @@ typedef struct s_Repodata { /*----- * management functions */ -void repodata_initdata(Repodata *data, struct s_Repo *repo, int localpool); +void repodata_initdata(Repodata *data, Repo *repo, int localpool); void repodata_freedata(Repodata *data); void repodata_free(Repodata *data); diff --git a/src/rules.c b/src/rules.c index 2c56959..7d57cfd 100644 --- a/src/rules.c +++ b/src/rules.c @@ -183,7 +183,7 @@ solver_unifyrules(Solver *solv) binr++; else { - dp = solv->pool->whatprovidesdata + r->d; + dp = pool->whatprovidesdata + r->d; while (*dp++) lits++; } @@ -659,8 +659,12 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w int oldcount = solv->ruleinfoq->count; addpkgrule(solv, qele[0], 0, d, type, dep); /* fixup from element of ruleinfo */ - if (solv->ruleinfoq->count > oldcount) - solv->ruleinfoq->elements[oldcount + 1] = p; + if (solv->ruleinfoq->count > oldcount && solv->ruleinfoq->elements[oldcount + 1] != p) + { + if (solv->ruleinfoq->elements[oldcount + 2]) + solv->ruleinfoq->elements[oldcount + 2] = solv->ruleinfoq->elements[oldcount + 1]; + solv->ruleinfoq->elements[oldcount + 1] = p; + } } else addpkgrule(solv, qele[0], 0, d, type, dep); @@ -1668,6 +1672,16 @@ solver_addinfarchrules(Solver *solv, Map *addedmap) if (first) continue; /* not the first in the group */ + if (!bestscore && allowedarchs.count > 1 && pool->implicitobsoleteusescolors) + { + for (j = 0; j < allowedarchs.count; j++) + { + a = pool_arch2score(pool, allowedarchs.elements[j]); + if (a && a != 1 && (!bestscore || a < bestscore)) + bestscore = a; + } + } + if (!bestscore) continue; /* did not find a score for this group */ @@ -1931,8 +1945,8 @@ solver_addtodupmaps(Solver *solv, Id p, Id how, int targeted) void solver_createdupmaps(Solver *solv) { - Queue *job = &solv->job; Pool *pool = solv->pool; + Queue *job = &solv->job; Repo *installed = solv->installed; Id select, how, what, p, pp; Solvable *s; @@ -2199,12 +2213,12 @@ reenableblackrule(Solver *solv, Id p) void solver_addblackrules(Solver *solv) { - int i; - Id how, select, what, p, pp; - Queue *job = &solv->job; Pool *pool = solv->pool; Repo *installed = solv->installed; + Id how, select, what, p, pp; + Queue *job = &solv->job; Map updatemap; + int i; map_init(&updatemap, 0); solv->blackrules = solv->nrules; @@ -2353,6 +2367,31 @@ reenablerepopriorule(Solver *solv, Id name) #define DISABLE_BLACK 4 #define DISABLE_REPOPRIO 5 +/* check if installed package p is in lock-step with another installed package */ +static int +installed_is_in_lockstep(Solver *solv, Id p) +{ + Pool *pool = solv->pool; + Repo *installed = solv->installed; + int rid; + Id pp, l; + Rule *r; + + if (!installed) + return 0; + for (rid = solv->infarchrules, r = solv->rules + rid; rid < solv->infarchrules_end; rid++, r++) + { + if (r->p >= 0) + continue; + if (pool->solvables[-r->p].repo != installed) + continue; + FOR_RULELITERALS(l, pp, r) + if (l == p) + return 1; + } + return 0; +} + static void jobtodisablelist(Solver *solv, Id how, Id what, Queue *q) { @@ -2485,6 +2524,20 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q) return; /* now the hard part: disable some update rules */ + /* if the job asks for a single solvable to stay, disable the update rule */ + if (select == SOLVER_SOLVABLE && pool->solvables[what].repo == installed && solv->bestrules_info) + if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) + { + int ni = solv->bestrules_end - solv->bestrules; + for (i = solv->bestrules_up - solv->bestrules; i < ni; i++) + if (solv->bestrules_info[i] == what) + { + queue_push2(q, DISABLE_UPDATE, what); /* will also disable the best rule */ + break; + } + return; + } + /* first check if we have installed or multiversion packages in the job */ FOR_JOB_SELECT(p, pp, select, what) { @@ -2560,6 +2613,20 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q) case SOLVER_ERASE: if (!installed) break; + set = how & SOLVER_SETMASK; + if (!(set & (SOLVER_NOAUTOSET | SOLVER_SETARCH)) && pool->implicitobsoleteusescolors && solv->infarchrules != solv->infarchrules_end) + { + if (select == SOLVER_SOLVABLE) + set |= SOLVER_SETARCH; + else if ((select == SOLVER_SOLVABLE_NAME || select == SOLVER_SOLVABLE_PROVIDES) && ISRELDEP(what)) + { + Reldep *rd = GETRELDEP(pool, what); + if (rd->flags <= 7 && ISRELDEP(rd->name)) + rd = GETRELDEP(pool, rd->name); + if (rd->flags == REL_ARCH) + set |= SOLVER_SETARCH; + } + } if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid)) { FOR_REPO_SOLVABLES(installed, p, s) @@ -2569,6 +2636,9 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q) if (pool->solvables[p].repo == installed) { queue_push2(q, DISABLE_UPDATE, p); + if ((set & SOLVER_SETARCH) != 0 && pool->implicitobsoleteusescolors && solv->infarchrules != solv->infarchrules_end) + if (installed_is_in_lockstep(solv, p)) + queue_push2(q, DISABLE_INFARCH, pool->solvables[p].name); /* allow to break the lock-step */ #ifdef ENABLE_LINKED_PKGS if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1) queue_push2(q, DISABLE_UPDATE, solv->instbuddy[p - installed->start]); @@ -3008,7 +3078,7 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp) qp = rq.elements[i + 1]; qo = rq.elements[i + 2]; qd = rq.elements[i + 3]; - if (type == SOLVER_RULE_PKG || type > qt) + if (type == SOLVER_RULE_PKG || qt == SOLVER_RULE_PKG_SAME_NAME || type > qt) { type = qt; if (fromp) @@ -3017,6 +3087,8 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp) *top = qo; if (depp) *depp = qd; + if (qt == SOLVER_RULE_PKG_SAME_NAME) + break; /* prefer SOLVER_RULE_PKG_SAME_NAME */ } } queue_free(&rq); @@ -3076,8 +3148,12 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp) } if (rid >= solv->bestrules && rid < solv->bestrules_end) { + /* > 0: the package we are updating */ if (fromp && solv->bestrules_info[rid - solv->bestrules] > 0) *fromp = solv->bestrules_info[rid - solv->bestrules]; + /* < 0: the job rule */ + if (top && solv->bestrules_info[rid - solv->bestrules] < 0) + *top = -solv->bestrules_info[rid - solv->bestrules]; return SOLVER_RULE_BEST; } if (rid >= solv->yumobsrules && rid < solv->yumobsrules_end) @@ -3109,9 +3185,17 @@ solver_ruleinfo(Solver *solv, Id rid, Id *fromp, Id *top, Id *depp) return SOLVER_RULE_STRICT_REPO_PRIORITY; } if (rid >= solv->choicerules && rid < solv->choicerules_end) - return SOLVER_RULE_CHOICE; + { + if (solv->choicerules_info && fromp) + *fromp = solv->choicerules_info[rid - solv->choicerules]; + return SOLVER_RULE_CHOICE; + } if (rid >= solv->recommendsrules && rid < solv->recommendsrules_end) - return SOLVER_RULE_RECOMMENDS; + { + if (solv->recommendsrules_info && fromp) + *fromp = solv->recommendsrules_info[rid - solv->recommendsrules]; + return SOLVER_RULE_RECOMMENDS; + } if (rid >= solv->learntrules) return SOLVER_RULE_LEARNT; return SOLVER_RULE_UNKNOWN; @@ -3196,10 +3280,10 @@ solver_rule2job(Solver *solv, Id rid, Id *whatp) Id solver_rule2solvable(Solver *solv, Id rid) { - if (rid >= solv->updaterules && rid < solv->updaterules_end) - return rid - solv->updaterules; - if (rid >= solv->featurerules && rid < solv->featurerules_end) - return rid - solv->featurerules; + if (rid >= solv->updaterules && rid < solv->updaterules_end && solv->installed) + return solv->installed->start + (rid - solv->updaterules); + if (rid >= solv->featurerules && rid < solv->featurerules_end && solv->installed) + return solv->installed->start + (rid - solv->featurerules); return 0; } @@ -3365,6 +3449,7 @@ solver_addchoicerules(Solver *solv) int lastaddedcnt; unsigned int now; int isinstalled; + int dodowngradecheck = solv->allowdowngrade; solv->choicerules = solv->nrules; if (!pool->installed) @@ -3373,6 +3458,8 @@ solver_addchoicerules(Solver *solv) return; } now = solv_timems(0); + if ((solv->dupinvolvedmap_all || solv->dupinvolvedmap.size) && solv->dup_allowdowngrade) + dodowngradecheck = 1; queue_init(&q); queue_init(&qi); queue_init(&qcheck); @@ -3448,8 +3535,16 @@ solver_addchoicerules(Solver *solv) /* do extra checking for packages related to installed packages */ for (i = j = 0; i < qi.count; i += 2) { + int isdowngrade = 0; p2 = qi.elements[i]; - if (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p2 - solv->installed->start))) + if (dodowngradecheck) + { + p = qi.elements[i + 1]; + if (pool->solvables[p2].name == pool->solvables[p].name) + if (pool_evrcmp(pool, pool->solvables[p2].evr, pool->solvables[p].evr, EVRCMP_COMPARE) > 0) + isdowngrade = 1; + } + if (isdowngrade || solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, p2 - solv->installed->start))) { if (solver_choicerulecheck(solv, p2, r, &m, &qcheck)) continue; @@ -3593,6 +3688,7 @@ solver_disablechoicerules(Solver *solv, Rule *r) if (p) solver_disablerule(solv, r); } + map_free(&m); } static void @@ -3957,7 +4053,7 @@ find_obsolete_group(Solver *solv, Id obs, Queue *q) } /* find names so that we can build groups */ queue_init_clone(&qn, q); - prune_to_best_version(solv->pool, &qn); + prune_to_best_version(pool, &qn); #if 0 { for (i = 0; i < qn.count; i++) @@ -4078,7 +4174,7 @@ for (j = 0; j < qq.count; j++) if (!qq.count) continue; - /* at least two goups, build rules */ + /* at least two groups, build rules */ group = 0; for (j = 0; j < qq.count; j++) { @@ -4260,3 +4356,122 @@ solver_check_brokenorphanrules(Solver *solv, Queue *dq) } } +const char * +solver_ruleinfo2str(Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep) +{ + Pool *pool = solv->pool; + char *s; + Solvable *ss; + switch (type) + { + case SOLVER_RULE_DISTUPGRADE: + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not belong to a distupgrade repository", 0); + case SOLVER_RULE_INFARCH: + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " has inferior architecture", 0); + case SOLVER_RULE_UPDATE: + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " needs to stay installed or be updated", 0); + case SOLVER_RULE_FEATURE: + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " needs to stay installed or be updated/downgraded", 0); + case SOLVER_RULE_JOB: + return pool_tmpjoin(pool, "job ", pool_job2str(pool, target, dep, 0), 0); + case SOLVER_RULE_JOB_UNSUPPORTED: + return pool_tmpjoin(pool, "unsupported job ", pool_job2str(pool, target, dep, 0), 0); + case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP: + return pool_tmpjoin(pool, "nothing provides requested ", pool_dep2str(pool, dep), 0); + case SOLVER_RULE_JOB_UNKNOWN_PACKAGE: + return pool_tmpjoin(pool, "requested package ", pool_dep2str(pool, dep), " does not exist"); + case SOLVER_RULE_JOB_PROVIDED_BY_SYSTEM: + return pool_tmpjoin(pool, "requested ", pool_dep2str(pool, dep), " is provided by the system"); + case SOLVER_RULE_BEST: + if (source > 0) + return pool_tmpjoin(pool, "install best update candidate for ", pool_solvid2str(pool, source), 0); + if (target > 0) + { + target = solver_rule2job(solv, target, &dep); + return pool_tmpjoin(pool, "best package for job ", pool_job2str(pool, target, dep, 0), 0); + } + return "best rule"; + case SOLVER_RULE_PKG: + return "bad pkg rule type"; + case SOLVER_RULE_PKG_NOT_INSTALLABLE: + ss = pool->solvables + source; + if (pool_disabled_solvable(pool, ss)) + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is disabled", 0); + if (ss->arch && ss->arch != ARCH_SRC && ss->arch != ARCH_NOSRC && + pool->id2arch && pool_arch2score(pool, ss->arch) == 0) + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " does not have a compatible architecture", 0); + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is not installable", 0); + case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP: + s = pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0); + return pool_tmpappend(pool, s, " needed by ", pool_solvid2str(pool, source)); + case SOLVER_RULE_PKG_SAME_NAME: + s = pool_tmpjoin(pool, "cannot install both ", pool_solvid2str(pool, source), 0); + return pool_tmpappend(pool, s, " and ", pool_solvid2str(pool, target)); + case SOLVER_RULE_PKG_CONFLICTS: + s = pool_tmpappend(pool, pool_solvid2str(pool, source), " conflicts with ", pool_dep2str(pool, dep)); + if (target) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target)); + return s; + case SOLVER_RULE_PKG_SELF_CONFLICT: + s = pool_tmpjoin(pool, pool_solvid2str(pool, source), " conflicts with ", 0); + return pool_tmpappend(pool, s, pool_dep2str(pool, dep), " provided by itself"); + case SOLVER_RULE_PKG_OBSOLETES: + s = pool_tmpappend(pool, pool_solvid2str(pool, source), " obsoletes ", pool_dep2str(pool, dep)); + if (target) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target)); + return s; + case SOLVER_RULE_PKG_INSTALLED_OBSOLETES: + s = pool_tmpjoin(pool, "installed ", pool_solvid2str(pool, source), 0); + s = pool_tmpappend(pool, s, " obsoletes ", pool_dep2str(pool, dep)); + if (target) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target)); + return s; + case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES: + s = pool_tmpappend(pool, pool_solvid2str(pool, source), " implicitly obsoletes ", pool_dep2str(pool, dep)); + if (target) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target)); + return s; + case SOLVER_RULE_PKG_REQUIRES: + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " requires ", pool_dep2str(pool, dep)); + case SOLVER_RULE_PKG_RECOMMENDS: + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " recommends ", pool_dep2str(pool, dep)); + case SOLVER_RULE_PKG_CONSTRAINS: + s = pool_tmpappend(pool, pool_solvid2str(pool, source), " has constraint ", pool_dep2str(pool, dep)); + return pool_tmpappend(pool, s, " conflicting with ", pool_solvid2str(pool, target)); + case SOLVER_RULE_PKG_SUPPLEMENTS: + s = pool_tmpjoin(pool, pool_solvid2str(pool, source), " supplements ", pool_dep2str(pool, dep)); + if (target) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target)); + return s; + case SOLVER_RULE_YUMOBS: + s = pool_tmpjoin(pool, "both ", pool_solvid2str(pool, source), " and "); + s = pool_tmpjoin(pool, s, pool_solvid2str(pool, target), " obsolete "); + return pool_tmpappend(pool, s, pool_dep2str(pool, dep), 0); + case SOLVER_RULE_BLACK: + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " can only be installed by a direct request", 0); + case SOLVER_RULE_STRICT_REPO_PRIORITY: + return pool_tmpjoin(pool, pool_solvid2str(pool, source), " is excluded by strict repo priority", 0); + case SOLVER_RULE_LEARNT: + return "learnt rule"; + case SOLVER_RULE_CHOICE: + if (source > 0) + { + const char *s2; + type = solver_ruleinfo(solv, source, &source, &target, &dep); + s2 = solver_ruleinfo2str(solv, type, source, target, dep); + return pool_tmpjoin(pool, s2, " (limited version)", 0); + } + return "choice rule"; + case SOLVER_RULE_RECOMMENDS: + if (source > 0) + { + const char *s2; + type = solver_ruleinfo(solv, source, &source, &target, &dep); + s2 = solver_ruleinfo2str(solv, type, source, target, dep); + return pool_tmpjoin(pool, s2, " (limited version)", 0); + } + return "recommends rule"; + default: + return "bad rule type"; + } +} diff --git a/src/rules.h b/src/rules.h index 043d0a0..17af2df 100644 --- a/src/rules.h +++ b/src/rules.h @@ -13,10 +13,15 @@ #ifndef LIBSOLV_RULES_H #define LIBSOLV_RULES_H +#include "pooltypes.h" +#include "bitmap.h" +#include "queue.h" + #ifdef __cplusplus extern "C" { #endif + /* ---------------------------------------------- * Rule * @@ -60,6 +65,7 @@ typedef enum { SOLVER_RULE_PKG_INSTALLED_OBSOLETES, SOLVER_RULE_PKG_RECOMMENDS, SOLVER_RULE_PKG_CONSTRAINS, + SOLVER_RULE_PKG_SUPPLEMENTS, SOLVER_RULE_UPDATE = 0x200, SOLVER_RULE_FEATURE = 0x300, SOLVER_RULE_JOB = 0x400, @@ -161,6 +167,11 @@ extern Id solver_rule2job(struct s_Solver *solv, Id rid, Id *whatp); extern Id solver_rule2solvable(struct s_Solver *solv, Id rid); extern void solver_rule2rules(struct s_Solver *solv, Id rid, Queue *q, int recursive); extern Id solver_rule2pkgrule(struct s_Solver *solv, Id rid); +extern const char *solver_ruleinfo2str(struct s_Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep); + +/* rule infos for weakdep decisions */ +extern int solver_allweakdepinfos(struct s_Solver *solv, Id p, Queue *rq); +extern SolverRuleinfo solver_weakdepinfo(struct s_Solver *solv, Id p, Id *fromp, Id *top, Id *depp); /* orphan handling */ extern void solver_breakorphans(struct s_Solver *solv); diff --git a/src/solvable.c b/src/solvable.c index 181d9bc..657a8b8 100644 --- a/src/solvable.c +++ b/src/solvable.c @@ -24,6 +24,7 @@ #include "poolvendor.h" #include "chksum.h" #include "linkedpkg.h" +#include "evr.h" const char * pool_solvable2str(Pool *pool, Solvable *s) @@ -188,16 +189,11 @@ solvable_lookup_str_base(Solvable *s, Id keyname, Id basekeyname, int usebase) } #ifdef ENABLE_LINKED_PKGS /* autopattern/product translation magic */ - if (pass) + if (pass == 1 && name == s->name) { - const char *n = pool_id2str(pool, name); - if (*n == 'p') - { - if (!strncmp("pattern:", n, 8) && (name = find_autopattern_name(pool, s)) != 0) - pass = -1; - if (!strncmp("product:", n, 8) && (name = find_autoproduct_name(pool, s)) != 0) - pass = -1; - } + name = find_autopackage_name(pool, s); + if (name && name != s->name) + pass = -1; /* start over with new name */ } #endif } @@ -746,3 +742,124 @@ solvable_matchessolvable(Solvable *s, Id keyname, Id solvid, Queue *depq, int ma queue_free(&qq); return res; } + +static int +solvidset2str_evrcmp(Pool *pool, Id a, Id b) +{ + Solvable *as = pool->solvables + a, *bs = pool->solvables + b; + return as->evr != bs->evr ? pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE) : 0; +} + +static int +solvidset2str_sortcmp(const void *va, const void *vb, void *vd) +{ + Pool *pool = vd; + Solvable *as = pool->solvables + *(Id *)va, *bs = pool->solvables + *(Id *)vb; + if (as->name != bs->name) + { + int r = strcmp(pool_id2str(pool, as->name), pool_id2str(pool, bs->name)); + if (r) + return r; + return as->name - bs->name; + } + if (as->evr != bs->evr) + { + int r = pool_evrcmp(pool, as->evr, bs->evr, EVRCMP_COMPARE); + if (r) + return r; + } + return *(Id *)va - *(Id *)vb; +} + +static const char * +solvidset2str_striprelease(Pool *pool, Id evr, Id otherevr) +{ + const char *evrstr = pool_id2str(pool, evr); + const char *r = strchr(evrstr, '-'); + char *evrstr2; + int cmp; + if (!r) + return evrstr; + evrstr2 = pool_tmpjoin(pool, evrstr, 0, 0); + evrstr2[r - evrstr] = 0; + cmp = pool_evrcmp_str(pool, evrstr2, pool_id2str(pool, otherevr), pool->disttype != DISTTYPE_DEB ? EVRCMP_MATCH_RELEASE : EVRCMP_COMPARE); + return cmp == 1 ? evrstr2 : evrstr; +} + +const char * +pool_solvidset2str(Pool *pool, Queue *q) +{ + Queue pq; + Queue pr; + char *s = 0; + int i, j, k, kstart; + Id name = 0; + + if (!q->count) + return ""; + if (q->count == 1) + return pool_solvid2str(pool, q->elements[0]); + queue_init_clone(&pq, q); + queue_init(&pr); + solv_sort(pq.elements, pq.count, sizeof(Id), solvidset2str_sortcmp, pool); + + for (i = 0; i < pq.count; i++) + { + Id p = pq.elements[i]; + if (s) + s = pool_tmpappend(pool, s, ", ", 0); + + if (i == 0 || pool->solvables[p].name != name) + { + Id p2, pp2; + name = pool->solvables[p].name; + queue_empty(&pr); + FOR_PROVIDES(p2, pp2, name) + if (pool->solvables[p].name == name) + queue_push(&pr, p2); + if (pr.count > 1) + solv_sort(pr.elements, pr.count, sizeof(Id), solvidset2str_sortcmp, pool); + } + + for (k = 0; k < pr.count; k++) + if (pr.elements[k] == p) + break; + if (k == pr.count) + { + /* not in provides, list as singularity */ + s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0); + continue; + } + if (k && solvidset2str_evrcmp(pool, pr.elements[k], pr.elements[k - 1]) == 0) + { + /* unclear start, list as single package */ + s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0); + continue; + } + kstart = k; + for (j = i + 1, k = k + 1; j < pq.count; j++, k++) + if (k == pr.count || pq.elements[j] != pr.elements[k]) + break; + while (j > i + 1 && k && k < pr.count && solvidset2str_evrcmp(pool, pr.elements[k], pr.elements[k - 1]) == 0) + { + j--; + k--; + } + if (k == 0 || j == i + 1) + { + s = pool_tmpappend(pool, s, pool_solvid2str(pool, pq.elements[i]), 0); + continue; + } + /* create an interval */ + s = pool_tmpappend(pool, s, pool_id2str(pool, name), 0); + if (kstart > 0) + s = pool_tmpappend(pool, s, " >= ", solvidset2str_striprelease(pool, pool->solvables[pr.elements[kstart]].evr, pool->solvables[pr.elements[kstart - 1]].evr)); + if (k < pr.count) + s = pool_tmpappend(pool, s, " < ", solvidset2str_striprelease(pool, pool->solvables[pr.elements[k]].evr, pool->solvables[pr.elements[k - 1]].evr)); + i = j - 1; + } + queue_free(&pq); + queue_free(&pr); + return s; +} + diff --git a/src/solver.c b/src/solver.c index 28341d6..bdce9a9 100644 --- a/src/solver.c +++ b/src/solver.c @@ -151,11 +151,11 @@ makeruledecisions(Solver *solv, int disablerules) solv->decisionmap[vv] = v > 0 ? 1 : -1; IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE) { - Solvable *s = solv->pool->solvables + vv; + Solvable *s = pool->solvables + vv; if (v < 0) - POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (assertion)\n", pool_solvable2str(solv->pool, s)); + POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (assertion)\n", pool_solvable2str(pool, s)); else - POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing %s (assertion)\n", pool_solvable2str(solv->pool, s)); + POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing %s (assertion)\n", pool_solvable2str(pool, s)); } continue; } @@ -311,11 +311,11 @@ makeruledecisions(Solver *solv, int disablerules) solv->decisionmap[vv] = v > 0 ? 1 : -1; IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE) { - Solvable *s = solv->pool->solvables + vv; + Solvable *s = pool->solvables + vv; if (v < 0) - POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (weak assertion)\n", pool_solvable2str(solv->pool, s)); + POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "conflicting %s (weak assertion)\n", pool_solvable2str(pool, s)); else - POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing %s (weak assertion)\n", pool_solvable2str(solv->pool, s)); + POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "installing %s (weak assertion)\n", pool_solvable2str(pool, s)); } continue; } @@ -986,7 +986,7 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules) FOR_RULELITERALS(v, pp, r) { if (DECISIONMAP_TRUE(v)) /* the one true literal */ - continue; + abort(); vv = v > 0 ? v : -v; MAPSET(&involved, vv); } @@ -1005,8 +1005,12 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules) analyze_unsolvable_rule(solv, r, &weakq, &rseen); FOR_RULELITERALS(v, pp, r) { - if (DECISIONMAP_TRUE(v)) /* the one true literal */ + if (DECISIONMAP_TRUE(v)) /* the one true literal, i.e. our decision */ + { + if (v != solv->decisionq.elements[idx]) + abort(); continue; + } vv = v > 0 ? v : -v; MAPSET(&involved, vv); } @@ -1117,10 +1121,77 @@ setpropagatelearn(Solver *solv, int level, Id decision, int disablerules, Id rul } static void +queue_prunezeros(Queue *q) +{ + int i, j; + for (i = 0; i < q->count; i++) + if (q->elements[i] == 0) + break; + if (i == q->count) + return; + for (j = i++; i < q->count; i++) + if (q->elements[i]) + q->elements[j++] = q->elements[i]; + queue_truncate(q, j); +} + +static int +replaces_installed_package(Pool *pool, Id p, Map *noupdate) +{ + Repo *installed = pool->installed; + Solvable *s = pool->solvables + p, *s2; + Id p2, pp2; + Id obs, *obsp; + + if (s->repo == installed && !(noupdate && MAPTST(noupdate, p - installed->start))) + return 1; + FOR_PROVIDES(p2, pp2, s->name) + { + s2 = pool->solvables + p2; + if (s2->repo == installed && s2->name == s->name && !(noupdate && MAPTST(noupdate, p - installed->start))) + return 1; + } + if (!s->obsoletes) + return 0; + obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + { + FOR_PROVIDES(p2, pp2, obs) + { + s2 = pool->solvables + p2; + if (s2->repo != pool->installed || (noupdate && MAPTST(noupdate, p - installed->start))) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, s2, obs)) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, s2)) + continue; + return 1; + } + } + return 0; +} + +static void +prune_dq_for_future_installed(Solver *solv, Queue *dq) +{ + Pool *pool = solv->pool; + int i, j; + for (i = j = 0; i < dq->count; i++) + { + Id p = dq->elements[i]; + if (replaces_installed_package(pool, p, &solv->noupdate)) + dq->elements[j++] = p; + } + if (j) + queue_truncate(dq, j); +} + + +static void reorder_dq_for_future_installed(Solver *solv, int level, Queue *dq) { Pool *pool = solv->pool; - int i, j, haveone = 0, dqcount = dq->count; + int i, haveone = 0, dqcount = dq->count; int decisionqcount = solv->decisionq.count; Id p; Solvable *s; @@ -1161,10 +1232,7 @@ reorder_dq_for_future_installed(Solver *solv, int level, Queue *dq) dq->elements[i] = 0; } } - for (i = j = 0; i < dq->count; i++) - if (dq->elements[i]) - dq->elements[j++] = dq->elements[i]; - queue_truncate(dq, j); + queue_prunezeros(dq); FOR_REPO_SOLVABLES(solv->installed, p, s) if (solv->decisionmap[p] == level + 1) solv->decisionmap[p] = 0; @@ -1238,6 +1306,46 @@ takebranch(Solver *solv, int pos, int end, const char *msg, int disablerules) return setpropagatelearn(solv, level, p, disablerules, why, reason); } +static void +prune_yumobs(Solver *solv, Queue *dq, Id ruleid) +{ + Pool *pool = solv->pool; + Rule *r; + Map m; + int i, j, rid; + + map_init(&m, 0); + for (i = 0; i < dq->count - 1; i++) + { + Id p2, pp, p = dq->elements[i]; + if (!p || !pool->solvables[p].obsoletes) + continue; + for (rid = solv->yumobsrules, r = solv->rules + rid; rid < solv->yumobsrules_end; rid++, r++) + if (r->p == -p) + break; + if (rid == solv->yumobsrules_end) + continue; + if (!m.size) + map_grow(&m, pool->nsolvables); + else + MAPZERO(&m); + for (; rid < solv->yumobsrules_end; rid++, r++) + { + if (r->p != -p) + continue; + FOR_RULELITERALS(p2, pp, r) + if (p2 > 0) + MAPSET(&m, p2); + } + for (j = i + 1; j < dq->count; j++) + if (MAPTST(&m, dq->elements[j])) + dq->elements[j] = 0; + } + map_free(&m); + queue_prunezeros(dq); +} + + /*------------------------------------------------------------------- * * select and install @@ -1258,9 +1366,16 @@ selectandinstall(Solver *solv, int level, Queue *dq, int disablerules, Id ruleid if (dq->count > 1) policy_filter_unwanted(solv, dq, POLICY_MODE_CHOOSE); /* if we're resolving rules and didn't resolve the installed packages yet, - * do some special supplements ordering */ + * do some special pruning and supplements ordering */ if (dq->count > 1 && solv->do_extra_reordering) - reorder_dq_for_future_installed(solv, level, dq); + { + prune_dq_for_future_installed(solv, dq); + if (dq->count > 1) + reorder_dq_for_future_installed(solv, level, dq); + } + /* check if the candidates are all connected via yumobs rules */ + if (dq->count > 1 && solv->yumobsrules_end > solv->yumobsrules) + prune_yumobs(solv, dq, ruleid); /* if we have multiple candidates we open a branch */ if (dq->count > 1) createbranch(solv, level, dq, 0, ruleid); @@ -2921,6 +3036,8 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) continue; if (solv->favormap && solv->favormap[p] > solv->favormap[solv->branches.elements[lastsi]]) continue; /* current selection is more favored */ + if (replaces_installed_package(pool, p, &solv->noupdate)) + continue; /* current selection replaces an installed package */ if (!(MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p))) { lasti = lastsi; @@ -3533,6 +3650,7 @@ solver_solve(Solver *solv, Queue *job) map_zerosize(&solv->bestupdatemap); solv->fixmap_all = 0; map_zerosize(&solv->fixmap); + solv->dupinvolvedmap_all = 0; map_zerosize(&solv->dupmap); map_zerosize(&solv->dupinvolvedmap); solv->process_orphans = 0; @@ -4580,194 +4698,6 @@ solver_create_state_maps(Solver *solv, Map *installedmap, Map *conflictsmap) pool_create_state_maps(solv->pool, &solv->decisionq, installedmap, conflictsmap); } -/*------------------------------------------------------------------- - * - * decision introspection - */ - -int -solver_get_decisionlevel(Solver *solv, Id p) -{ - return solv->decisionmap[p]; -} - -void -solver_get_decisionqueue(Solver *solv, Queue *decisionq) -{ - queue_free(decisionq); - queue_init_clone(decisionq, &solv->decisionq); -} - -int -solver_get_lastdecisionblocklevel(Solver *solv) -{ - Id p; - if (solv->decisionq.count == 0) - return 0; - p = solv->decisionq.elements[solv->decisionq.count - 1]; - if (p < 0) - p = -p; - return solv->decisionmap[p] < 0 ? -solv->decisionmap[p] : solv->decisionmap[p]; -} - -void -solver_get_decisionblock(Solver *solv, int level, Queue *decisionq) -{ - Id p; - int i; - - queue_empty(decisionq); - for (i = 0; i < solv->decisionq.count; i++) - { - p = solv->decisionq.elements[i]; - if (p < 0) - p = -p; - if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level) - break; - } - if (i == solv->decisionq.count) - return; - for (i = 0; i < solv->decisionq.count; i++) - { - p = solv->decisionq.elements[i]; - if (p < 0) - p = -p; - if (solv->decisionmap[p] == level || solv->decisionmap[p] == -level) - queue_push(decisionq, p); - else - break; - } -} - -int -solver_describe_decision(Solver *solv, Id p, Id *infop) -{ - int i; - Id pp, why; - - if (infop) - *infop = 0; - if (!solv->decisionmap[p]) - return SOLVER_REASON_UNRELATED; - pp = solv->decisionmap[p] < 0 ? -p : p; - for (i = 0; i < solv->decisionq.count; i++) - if (solv->decisionq.elements[i] == pp) - break; - if (i == solv->decisionq.count) /* just in case... */ - return SOLVER_REASON_UNRELATED; - why = solv->decisionq_why.elements[i]; - if (infop) - *infop = why > 0 ? why : -why; - if (why > 0) - return SOLVER_REASON_UNIT_RULE; - i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p]; - return solv->decisionq_reason.elements[i]; -} - - -void -solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) -{ - Pool *pool = solv->pool; - int i; - int level = solv->decisionmap[p]; - int decisionno; - Solvable *s; - - queue_empty(whyq); - if (level < 0) - return; /* huh? */ - for (decisionno = 0; decisionno < solv->decisionq.count; decisionno++) - if (solv->decisionq.elements[decisionno] == p) - break; - if (decisionno == solv->decisionq.count) - return; /* huh? */ - i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p]; - if (solv->decisionq_reason.elements[i] != SOLVER_REASON_WEAKDEP) - return; /* huh? */ - - /* 1) list all packages that recommend us */ - for (i = 1; i < pool->nsolvables; i++) - { - Id *recp, rec, pp2, p2; - if (solv->decisionmap[i] <= 0 || solv->decisionmap[i] >= level) - continue; - s = pool->solvables + i; - if (!s->recommends) - continue; - if (!solv->addalreadyrecommended && s->repo == solv->installed) - continue; - recp = s->repo->idarraydata + s->recommends; - while ((rec = *recp++) != 0) - { - int found = 0; - FOR_PROVIDES(p2, pp2, rec) - { - if (p2 == p) - found = 1; - else - { - /* if p2 is already installed, this recommends is ignored */ - if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level) - break; - } - } - if (!p2 && found) - { - queue_push(whyq, SOLVER_REASON_RECOMMENDED); - queue_push2(whyq, i, rec); - } - } - } - /* 2) list all supplements */ - s = pool->solvables + p; - if (s->supplements && level > 0) - { - Id *supp, sup, pp2, p2; - /* this is a hack. to use solver_dep_fulfilled we temporarily clear - * everything above our level in the decisionmap */ - for (i = decisionno; i < solv->decisionq.count; i++ ) - { - p2 = solv->decisionq.elements[i]; - if (p2 > 0) - solv->decisionmap[p2] = -solv->decisionmap[p2]; - } - supp = s->repo->idarraydata + s->supplements; - while ((sup = *supp++) != 0) - if (solver_dep_fulfilled(solv, sup)) - { - int found = 0; - /* let's see if this is an easy supp */ - FOR_PROVIDES(p2, pp2, sup) - { - if (!solv->addalreadyrecommended && solv->installed) - { - if (pool->solvables[p2].repo == solv->installed) - continue; - } - if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level) - { - queue_push(whyq, SOLVER_REASON_SUPPLEMENTED); - queue_push2(whyq, p2, sup); - found = 1; - } - } - if (!found) - { - /* hard case, just note dependency with no package */ - queue_push(whyq, SOLVER_REASON_SUPPLEMENTED); - queue_push2(whyq, 0, sup); - } - } - for (i = decisionno; i < solv->decisionq.count; i++) - { - p2 = solv->decisionq.elements[i]; - if (p2 > 0) - solv->decisionmap[p2] = -solv->decisionmap[p2]; - } - } -} - void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what) { @@ -4799,7 +4729,7 @@ pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what) int pool_isemptyupdatejob(Pool *pool, Id how, Id what) { - Id p, pp, pi, pip; + Id p, pp; Id select = how & SOLVER_SELECTMASK; if ((how & SOLVER_JOBMASK) != SOLVER_UPDATE) return 0; @@ -4812,85 +4742,11 @@ pool_isemptyupdatejob(Pool *pool, Id how, Id what) return 0; /* hard work */ FOR_JOB_SELECT(p, pp, select, what) - { - Solvable *s = pool->solvables + p; - FOR_PROVIDES(pi, pip, s->name) - { - Solvable *si = pool->solvables + pi; - if (si->repo != pool->installed || si->name != s->name) - continue; - return 0; - } - if (s->obsoletes) - { - Id obs, *obsp = s->repo->idarraydata + s->obsoletes; - while ((obs = *obsp++) != 0) - { - FOR_PROVIDES(pi, pip, obs) - { - Solvable *si = pool->solvables + pi; - if (si->repo != pool->installed) - continue; - if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, si, obs)) - continue; - if (pool->obsoleteusescolors && !pool_colormatch(pool, s, si)) - continue; - return 0; - } - } - } - } + if (replaces_installed_package(pool, p, 0)) + return 0; return 1; } -int -solver_alternatives_count(Solver *solv) -{ - Id *elements = solv->branches.elements; - int res, count; - for (res = 0, count = solv->branches.count; count; res++) - count -= elements[count - 2]; - return res; -} - -int -solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp) -{ - int cnt = solver_alternatives_count(solv); - int count = solv->branches.count; - Id *elements = solv->branches.elements; - if (choices) - queue_empty(choices); - if (alternative <= 0 || alternative > cnt) - return 0; - elements += count; - for (; cnt > alternative; cnt--) - elements -= elements[-2]; - if (levelp) - *levelp = elements[-1]; - if (fromp) - *fromp = elements[-4]; - if (idp) - *idp = elements[-3]; - if (chosenp) - { - int i; - *chosenp = 0; - for (i = elements[-2]; i > 4; i--) - { - Id p = -elements[-i]; - if (p > 0 && solv->decisionmap[p] == elements[-1] + 1) - { - *chosenp = p; - break; - } - } - } - if (choices) - queue_insertn(choices, 0, elements[-2] - 4, elements - elements[-2]); - return elements[-4] ? SOLVER_ALTERNATIVE_TYPE_RECOMMENDS : SOLVER_ALTERNATIVE_TYPE_RULE; -} - const char * solver_select2str(Pool *pool, Id select, Id what) { @@ -5042,39 +4898,3 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask) return pool_tmpappend(pool, s, "]", 0); } -const char * -solver_alternative2str(Solver *solv, int type, Id id, Id from) -{ - Pool *pool = solv->pool; - if (type == SOLVER_ALTERNATIVE_TYPE_RECOMMENDS) - { - const char *s = pool_dep2str(pool, id); - return pool_tmpappend(pool, s, ", recommended by ", pool_solvid2str(pool, from)); - } - if (type == SOLVER_ALTERNATIVE_TYPE_RULE) - { - int rtype; - Id depfrom, depto, dep; - char buf[64]; - if (solver_ruleclass(solv, id) == SOLVER_RULE_CHOICE) - id = solver_rule2pkgrule(solv, id); - if (solver_ruleclass(solv, id) == SOLVER_RULE_RECOMMENDS) - id = solver_rule2pkgrule(solv, id); - rtype = solver_ruleinfo(solv, id, &depfrom, &depto, &dep); - if ((rtype & SOLVER_RULE_TYPEMASK) == SOLVER_RULE_JOB) - { - if ((depto & SOLVER_SELECTMASK) == SOLVER_SOLVABLE_PROVIDES) - return pool_dep2str(pool, dep); - return solver_select2str(pool, depto & SOLVER_SELECTMASK, dep); - } - if (rtype == SOLVER_RULE_PKG_REQUIRES) - { - const char *s = pool_dep2str(pool, dep); - return pool_tmpappend(pool, s, ", required by ", pool_solvid2str(pool, depfrom)); - } - sprintf(buf, "Rule #%d", id); - return pool_tmpjoin(pool, buf, 0, 0); - } - return "unknown alternative type"; -} - diff --git a/src/solver.h b/src/solver.h index 287003c..34c045e 100644 --- a/src/solver.h +++ b/src/solver.h @@ -302,8 +302,11 @@ typedef struct s_Solver Solver; #define SOLVER_REASON_WEAKDEP 7 #define SOLVER_REASON_RESOLVE_ORPHAN 8 -#define SOLVER_REASON_RECOMMENDED 16 -#define SOLVER_REASON_SUPPLEMENTED 17 +#define SOLVER_REASON_RECOMMENDED 16 /* deprecated */ +#define SOLVER_REASON_SUPPLEMENTED 17 /* deprecated */ + +#define SOLVER_REASON_UNSOLVABLE 18 +#define SOLVER_REASON_PREMISE 19 #define SOLVER_FLAG_ALLOW_DOWNGRADE 1 @@ -343,6 +346,16 @@ typedef struct s_Solver Solver; #define SOLVER_ALTERNATIVE_TYPE_RECOMMENDS 2 #define SOLVER_ALTERNATIVE_TYPE_SUGGESTS 3 +/* solver_get_decisionlist / solver_get_learnt flags */ +#define SOLVER_DECISIONLIST_SOLVABLE (1 << 1) +#define SOLVER_DECISIONLIST_PROBLEM (1 << 2) +#define SOLVER_DECISIONLIST_LEARNTRULE (1 << 3) +#define SOLVER_DECISIONLIST_WITHINFO (1 << 8) +#define SOLVER_DECISIONLIST_SORTED (1 << 9) +#define SOLVER_DECISIONLIST_MERGEDINFO (1 << 10) + +#define SOLVER_DECISIONLIST_TYPEMASK (0xff) + extern Solver *solver_create(Pool *pool); extern void solver_free(Solver *solv); extern int solver_solve(Solver *solv, Queue *job); @@ -363,10 +376,16 @@ extern void pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int fl extern void solver_get_cleandeps(Solver *solv, Queue *cleandepsq); extern int solver_describe_decision(Solver *solv, Id p, Id *infop); -extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq); + +extern void solver_get_decisionlist(Solver *solv, Id p, int flags, Queue *decisionlistq); +extern void solver_get_decisionlist_multiple(Solver *solv, Queue *pq, int flags, Queue *decisionlistq); +extern void solver_get_learnt(Solver *solv, Id id, int flags, Queue *q); +extern void solver_decisionlist_solvables(Solver *solv, Queue *decisionlistq, int pos, Queue *q); +extern int solver_decisionlist_merged(Solver *solv, Queue *decisionlistq, int pos); extern int solver_alternatives_count(Solver *solv); extern int solver_get_alternative(Solver *solv, Id alternative, Id *idp, Id *fromp, Id *chosenp, Queue *choices, int *levelp); +extern int solver_alternativeinfo(Solver *solv, int type, Id id, Id from, Id *fromp, Id *top, Id *depp); extern void solver_calculate_multiversionmap(Pool *pool, Queue *job, Map *multiversionmap); extern void solver_calculate_noobsmap(Pool *pool, Queue *job, Map *multiversionmap); /* obsolete */ @@ -378,11 +397,21 @@ extern int solver_calc_installsizechange(Solver *solv); extern void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what); extern int pool_isemptyupdatejob(Pool *pool, Id how, Id what); +/* decisioninfo merging */ +extern int solver_calc_decisioninfo_bits(Solver *solv, Id decision, int type, Id from, Id to, Id dep); +extern int solver_merge_decisioninfo_bits(Solver *solv, int state1, int type1, Id from1, Id to1, Id dep1, int state2, int type2, Id from2, Id to2, Id dep2); + extern const char *solver_select2str(Pool *pool, Id select, Id what); extern const char *pool_job2str(Pool *pool, Id how, Id what, Id flagmask); extern const char *solver_alternative2str(Solver *solv, int type, Id id, Id from); +extern const char *solver_reason2str(Solver *solv, int reason); +extern const char *solver_decisionreason2str(Solver *solv, Id decision, int reason, Id info); +extern const char *solver_decisioninfo2str(Solver *solv, int bits, int type, Id from, Id to, Id dep); +/* deprecated, use solver_allweakdepinfos/solver_weakdepinfo instead */ +extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq); + /* iterate over all literals of a rule */ #define FOR_RULELITERALS(l, pp, r) \ for (pp = r->d < 0 ? -r->d - 1 : r->d, \ diff --git a/src/solverdebug.c b/src/solverdebug.c index 5902cdb..1c53d4e 100644 --- a/src/solverdebug.c +++ b/src/solverdebug.c @@ -505,43 +505,17 @@ solver_printcompleteprobleminfo(Solver *solv, Id problem) queue_free(&q); } -static int illegals[] = { - POLICY_ILLEGAL_DOWNGRADE, - POLICY_ILLEGAL_NAMECHANGE, - POLICY_ILLEGAL_ARCHCHANGE, - POLICY_ILLEGAL_VENDORCHANGE, - 0 -}; - void solver_printsolution(Solver *solv, Id problem, Id solution) { Pool *pool = solv->pool; - Id p, rp, element; - - element = 0; - while ((element = solver_next_solutionelement(solv, problem, solution, element, &p, &rp)) != 0) - { - if (p > 0 && rp > 0) - { - /* for replacements we want to know why it was illegal */ - Solvable *s = pool->solvables + p, *rs = pool->solvables + rp; - int illegal = policy_is_illegal(solv, s, rs, 0); - if (illegal) - { - int i; - for (i = 0; illegals[i]; i++) - if ((illegal & illegals[i]) != 0) - { - POOL_DEBUG(SOLV_DEBUG_RESULT, " - allow %s\n", policy_illegal2str(solv, illegals[i], s, rs)); - illegal ^= illegals[i]; - } - if (!illegal) - continue; - } - } - POOL_DEBUG(SOLV_DEBUG_RESULT, " - %s\n", solver_solutionelement2str(solv, p, rp)); - } + Queue q; + int i; + queue_init(&q); + solver_all_solutionelements(solv, problem, solution, 1, &q); + for (i = 0; i < q.count; i += 3) + POOL_DEBUG(SOLV_DEBUG_RESULT, " - %s\n", solver_solutionelementtype2str(solv, q.elements[i], q.elements[i + 1], q.elements[i + 2])); + queue_free(&q); } void diff --git a/src/transaction.h b/src/transaction.h index 001412d..8b8fe6d 100644 --- a/src/transaction.h +++ b/src/transaction.h @@ -21,12 +21,11 @@ extern "C" { #endif -struct s_Pool; struct s_DUChanges; struct s_TransactionOrderdata; typedef struct s_Transaction { - struct s_Pool *pool; /* back pointer to pool */ + Pool *pool; /* back pointer to pool */ Queue steps; /* the transaction steps */ @@ -93,8 +92,8 @@ typedef struct s_Transaction { #define SOLVER_ORDERCYCLE_NORMAL 1 #define SOLVER_ORDERCYCLE_CRITICAL 2 -extern Transaction *transaction_create(struct s_Pool *pool); -extern Transaction *transaction_create_decisionq(struct s_Pool *pool, Queue *decisionq, Map *multiversionmap); +extern Transaction *transaction_create(Pool *pool); +extern Transaction *transaction_create_decisionq(Pool *pool, Queue *decisionq, Map *multiversionmap); extern Transaction *transaction_create_clone(Transaction *srctrans); extern void transaction_free(Transaction *trans); |