/
niceSOFT
/
libsolv
Обзор
Документация
Войти
/
niceSOFT
/
libsolv
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/decision.c
1 233 строки
35 KB
Michael Schroeder
Fix indent
03 фев 2026, 15:04
Не верифицирован
03 фев 2026, 15:04
d8a0110
Код
Авторство
О чём код?
/* * 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 + 4] == SOLVER_RULE_RPM_SAME_NAME && q->elements[q->count - 16] > 0) { Rule *r = solv->rules + q->elements[q->count - 8 + 2]; /* 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/reason entries */ move_decision(q, q->count - 16, q->count - 8); q->elements[q->count - 16] = -q->elements[q->count - 8]; q->elements[q->count - 16 + 1] = SOLVER_REASON_UNIT_RULE; q->elements[q->count - 8] = 0; q->elements[q->count - 8 + 1] = SOLVER_REASON_UNSOLVABLE; } } /* 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 { 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 (reason == SOLVER_REASON_UNIT_RULE || 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); i = j - 8; } } } 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) { solver_get_proof(solv, id, flags, decisionlistq); return; } 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); }