X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fpolicy.c;h=5b72517ac67344e4a17d9a3b6a940cb1611df6b7;hb=04b192f26d73bd4394451b2b9faacad3c11d5163;hp=4c632e590da53a2a74c15633410a184087191ea4;hpb=c9b149fcab3c154479acbcb3d356a3d976211650;p=platform%2Fupstream%2Flibsolv.git diff --git a/src/policy.c b/src/policy.c index 4c632e5..5b72517 100644 --- a/src/policy.c +++ b/src/policy.c @@ -7,7 +7,7 @@ /* * Generic policy interface for SAT solver - * + * */ #include @@ -16,103 +16,345 @@ #include #include "solver.h" +#include "solver_private.h" #include "evr.h" #include "policy.h" #include "poolvendor.h" #include "poolarch.h" +#include "cplxdeps.h" -static Pool *prune_best_version_arch_sortcmp_data; - /*-----------------------------------------------------------------*/ /* - * prep for prune_best_version_arch + * prep for prune_best_version * sort by name */ static int -prune_best_version_arch_sortcmp(const void *ap, const void *bp) +prune_to_best_version_sortcmp(const void *ap, const void *bp, void *dp) { - Pool *pool = prune_best_version_arch_sortcmp_data; + Pool *pool = dp; int r; Id a = *(Id *)ap; Id b = *(Id *)bp; - r = pool->solvables[a].name - pool->solvables[b].name; + Solvable *sa, *sb; + + sa = pool->solvables + a; + sb = pool->solvables + b; + r = sa->name - sb->name; if (r) { const char *na, *nb; /* different names. We use real strcmp here so that the result * is not depending on some random solvable order */ - na = id2str(pool, pool->solvables[a].name); - nb = id2str(pool, pool->solvables[b].name); - /* bring selections and patterns to the front */ - if (!strncmp(na, "pattern:", 8)) - { - if (strncmp(nb, "pattern:", 8)) - return -1; - } - else if (!strncmp(nb, "pattern:", 8)) - { - if (strncmp(na, "pattern:", 8)) - return 1; - } - if (!strncmp(na, "selection:", 10)) + na = pool_id2str(pool, sa->name); + nb = pool_id2str(pool, sb->name); + return strcmp(na, nb); + } + if (sa->arch != sb->arch) + { + int aa, ab; + aa = (sa->arch <= pool->lastarch) ? pool->id2arch[sa->arch] : 0; + ab = (sb->arch <= pool->lastarch) ? pool->id2arch[sb->arch] : 0; + if (aa != ab && aa > 1 && ab > 1) + return aa - ab; /* lowest score first */ + } + + /* the same name, bring installed solvables to the front */ + if (pool->installed) + { + if (sa->repo == pool->installed) { - if (strncmp(nb, "selection:", 10)) + if (sb->repo != pool->installed) return -1; } - else if (!strncmp(nb, "selection:", 10)) - { - if (strncmp(na, "selection:", 10)) - return 1; - } - return strcmp(na, nb); + else if (sb->repo == pool->installed) + return 1; } + /* sort by repository sub-prio (installed repo handled above) */ + r = (sb->repo ? sb->repo->subpriority : 0) - (sa->repo ? sa->repo->subpriority : 0); + if (r) + return r; + /* no idea about the order, sort by id */ return a - b; } + +/* + * prune to repository with highest priority. + * does not prune installed solvables. + */ + static void prune_to_highest_prio(Pool *pool, Queue *plist) { int i, j; Solvable *s; - int bestprio = 0; + int bestprio = 0, bestprioset = 0; /* prune to highest priority */ - for (i = 0; i < plist->count; i++) + for (i = 0; i < plist->count; i++) /* find highest prio in queue */ { s = pool->solvables + plist->elements[i]; - if (i == 0 || s->repo->priority > bestprio) - bestprio = s->repo->priority; + if (pool->installed && s->repo == pool->installed) + continue; + if (!bestprioset || s->repo->priority > bestprio) + { + bestprio = s->repo->priority; + bestprioset = 1; + } } - for (i = j = 0; i < plist->count; i++) + if (!bestprioset) + return; + for (i = j = 0; i < plist->count; i++) /* remove all with lower prio */ { s = pool->solvables + plist->elements[i]; - if (s->repo->priority == bestprio) + if (s->repo->priority == bestprio || (pool->installed && s->repo == pool->installed)) plist->elements[j++] = plist->elements[i]; } plist->count = j; } + +/* installed packages involed in a dup operation can only be kept + * if they are identical to a non-installed one */ +static void +solver_prune_installed_dup_packages(Solver *solv, Queue *plist) +{ + Pool *pool = solv->pool; + int i, j, bestprio = 0; + + /* find bestprio (again) */ + for (i = 0; i < plist->count; i++) + { + Solvable *s = pool->solvables + plist->elements[i]; + if (s->repo != pool->installed) + { + bestprio = s->repo->priority; + break; + } + } + if (i == plist->count) + return; /* only installed packages, could not find prio */ + for (i = j = 0; i < plist->count; i++) + { + Id p = plist->elements[i]; + Solvable *s = pool->solvables + p; + if (s->repo != pool->installed && s->repo->priority < bestprio) + continue; + if (s->repo == pool->installed && (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))) + { + Id p2, pp2; + int keepit = 0; + FOR_PROVIDES(p2, pp2, s->name) + { + Solvable *s2 = pool->solvables + p2; + if (s2->repo == pool->installed || s2->evr != s->evr || s2->repo->priority < bestprio) + continue; + if (!solvable_identical(s, s2)) + continue; + keepit = 1; + if (s2->repo->priority > bestprio) + { + /* new max prio! */ + bestprio = s2->repo->priority; + j = 0; + } + } + if (!keepit) + continue; /* no identical package found, ignore installed package */ + } + plist->elements[j++] = p; + } + if (j) + plist->count = j; +} + /* - * prune_to_recommended - * - * XXX: should we prune to requires/suggests that are already - * fulfilled by other packages? + * like prune_to_highest_prio, but calls solver prune_installed_dup_packages + * when there are dup packages */ +static inline void +solver_prune_to_highest_prio(Solver *solv, Queue *plist) +{ + prune_to_highest_prio(solv->pool, plist); + if (plist->count > 1 && solv->pool->installed && (solv->dupmap_all || solv->dupinvolvedmap.size)) + solver_prune_installed_dup_packages(solv, plist); +} + + static void -prune_to_recommended(Solver *solv, Queue *plist) +solver_prune_to_highest_prio_per_name(Solver *solv, Queue *plist) +{ + Pool *pool = solv->pool; + Queue pq; + int i, j, k; + Id name; + + queue_init(&pq); + solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool); + queue_push(&pq, plist->elements[0]); + name = pool->solvables[pq.elements[0]].name; + for (i = 1, j = 0; i < plist->count; i++) + { + if (pool->solvables[plist->elements[i]].name != name) + { + if (pq.count > 2) + solver_prune_to_highest_prio(solv, &pq); + for (k = 0; k < pq.count; k++) + plist->elements[j++] = pq.elements[k]; + queue_empty(&pq); + queue_push(&pq, plist->elements[i]); + name = pool->solvables[pq.elements[0]].name; + } + } + if (pq.count > 2) + solver_prune_to_highest_prio(solv, &pq); + for (k = 0; k < pq.count; k++) + plist->elements[j++] = pq.elements[k]; + queue_free(&pq); + plist->count = j; +} + + +#ifdef ENABLE_COMPLEX_DEPS + +static void +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 + i = pool_normalize_complex_dep(pool, dep, &q, CPLXDEPS_EXPAND); + if (i == 0 || i == 1) + { + queue_free(&q); + return; + } + qcnt = q.count; + for (i = 0; i < qcnt; i++) + { + for (; (p = q.elements[i]) != 0; i++) + { + if (p < 0) + { + if (solv->decisionmap[-p] > 0) + continue; + if (solv->decisionmap[-p] < 0) + break; + queue_push(&q, -p); + } + if (p > 0 && qcnt == q.count) + MAPSET(m, p); + } + if (q.elements[i]) + { +#if 0 + printf("complex dep block cannot be true\n"); +#endif + while (q.elements[i]) + i++; + if (qcnt != q.count) + queue_truncate(&q, qcnt); + } + else if (qcnt != q.count) + { + int j, k; + Queue *cq = *cqp; +#if 0 + printf("add new complex dep block\n"); + for (j = qcnt; j < q.count; j++) + printf(" - %s\n", pool_solvid2str(pool, q.elements[j])); +#endif + if (!cq) + { + cq = solv_calloc(1, sizeof(Queue)); + queue_init(cq); + *cqp = cq; + queue_insertn(cq, 0, 256, 0); + } + for (j = qcnt; j < q.count; j++) + { + p = q.elements[j]; + for (k = 256; k < cq->count; k += 2) + if (cq->elements[k + 1] == dep && cq->elements[k] == p) + break; + if (k == cq->count) + { + queue_push2(cq, p, dep); + cq->elements[p & 255] |= (1 << (p >> 8 & 31)); + } + } + queue_truncate(&q, qcnt); + } + } + queue_free(&q); +} + +static void +recheck_complex_dep(Solver *solv, Id p, Map *m, Queue **cqp) +{ + Queue *cq = *cqp; + int i; +#if 0 + printf("recheck_complex_dep for package %s\n", pool_solvid2str(solv->pool, p)); +#endif + for (i = 256; i < cq->count; i += 2) + if (cq->elements[i] == p) + break; + if (i == cq->count) + return; /* false alert */ + if (solv->decisionmap[p] <= 0) + return; /* just in case... */ + memset(cq->elements, 0, sizeof(Id) * 256); + for (i = 256; i < cq->count; i += 2) + if (cq->elements[i] == p) + { + Id dep = cq->elements[i + 1]; + queue_deleten(cq, i, 2); + i -= 2; + check_complex_dep(solv, dep, m, &cq); + } + else + { + Id pp = cq->elements[i]; + cq->elements[pp & 255] |= (1 << (pp >> 8 & 31)); + } +} + +#endif + + +void +policy_update_recommendsmap(Solver *solv) { Pool *pool = solv->pool; - int i, j; Solvable *s; - Id p, *pp, rec, *recp, sug, *sugp; + Id p, pp, rec, *recp, sug, *sugp; if (solv->recommends_index < 0) { MAPZERO(&solv->recommendsmap); MAPZERO(&solv->suggestsmap); +#ifdef ENABLE_COMPLEX_DEPS + if (solv->recommendscplxq) + { + queue_free(solv->recommendscplxq); + solv->recommendscplxq = solv_free(solv->recommendscplxq); + } + if (solv->suggestscplxq) + { + queue_free(solv->suggestscplxq); + solv->suggestscplxq = solv_free(solv->suggestscplxq); + } +#endif solv->recommends_index = 0; } while (solv->recommends_index < solv->decisionq.count) @@ -121,56 +363,147 @@ prune_to_recommended(Solver *solv, Queue *plist) if (p < 0) continue; s = pool->solvables + p; +#ifdef ENABLE_COMPLEX_DEPS + if (solv->recommendscplxq && solv->recommendscplxq->elements[p & 255]) + if (solv->recommendscplxq->elements[p & 255] & (1 << (p >> 8 & 31))) + recheck_complex_dep(solv, p, &solv->recommendsmap, &solv->recommendscplxq); + if (solv->suggestscplxq && solv->suggestscplxq->elements[p & 255]) + if (solv->suggestscplxq->elements[p & 255] & (1 << (p >> 8 & 31))) + recheck_complex_dep(solv, p, &solv->suggestsmap, &solv->suggestscplxq); +#endif if (s->recommends) { recp = s->repo->idarraydata + s->recommends; while ((rec = *recp++) != 0) - FOR_PROVIDES(p, pp, rec) - MAPSET(&solv->recommendsmap, p); + { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, rec)) + { + check_complex_dep(solv, rec, &solv->recommendsmap, &solv->recommendscplxq); + continue; + } +#endif + FOR_PROVIDES(p, pp, rec) + MAPSET(&solv->recommendsmap, p); + } } if (s->suggests) { sugp = s->repo->idarraydata + s->suggests; while ((sug = *sugp++) != 0) - FOR_PROVIDES(p, pp, sug) - MAPSET(&solv->suggestsmap, p); + { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, sug)) + { + check_complex_dep(solv, sug, &solv->suggestsmap, &solv->suggestscplxq); + continue; + } +#endif + FOR_PROVIDES(p, pp, sug) + MAPSET(&solv->suggestsmap, p); + } } } +} + +/* + * prune to recommended/suggested packages. + * does not prune installed packages (they are also somewhat recommended). + */ + +static void +prune_to_recommended(Solver *solv, Queue *plist) +{ + Pool *pool = solv->pool; + int i, j, k, ninst; + Solvable *s; + Id p; + + ninst = 0; + if (pool->installed) + { + for (i = 0; i < plist->count; i++) + { + p = plist->elements[i]; + s = pool->solvables + p; + if (pool->installed && s->repo == pool->installed) + ninst++; + } + } + if (plist->count - ninst < 2) + return; + + /* update our recommendsmap/suggestsmap */ + if (solv->recommends_index < solv->decisionq.count) + policy_update_recommendsmap(solv); + /* prune to recommended/supplemented */ + ninst = 0; for (i = j = 0; i < plist->count; i++) { p = plist->elements[i]; - if (MAPTST(&solv->recommendsmap, p)) + s = pool->solvables + p; + if (pool->installed && s->repo == pool->installed) { - plist->elements[j++] = p; + ninst++; + if (j) + plist->elements[j++] = p; continue; } - if (solver_is_supplementing(solv, pool->solvables + p)) - plist->elements[j++] = p; + if (!MAPTST(&solv->recommendsmap, p)) + if (!solver_is_supplementing(solv, s)) + continue; + if (!j && ninst) + { + for (k = 0; j < ninst; k++) + { + s = pool->solvables + plist->elements[k]; + if (pool->installed && s->repo == pool->installed) + plist->elements[j++] = plist->elements[k]; + } + } + plist->elements[j++] = p; } if (j) plist->count = j; - /* prune to suggested/enhanced*/ - if (plist->count < 2) + /* anything left to prune? */ + if (plist->count - ninst < 2) return; + + /* prune to suggested/enhanced */ + ninst = 0; for (i = j = 0; i < plist->count; i++) { p = plist->elements[i]; - if (MAPTST(&solv->suggestsmap, p)) + s = pool->solvables + p; + if (pool->installed && s->repo == pool->installed) { - plist->elements[j++] = p; + ninst++; + if (j) + plist->elements[j++] = p; continue; } - if (solver_is_enhancing(solv, pool->solvables + p)) - plist->elements[j++] = p; + if (!MAPTST(&solv->suggestsmap, p)) + if (!solver_is_enhancing(solv, s)) + continue; + if (!j && ninst) + { + for (k = 0; j < ninst; k++) + { + s = pool->solvables + plist->elements[k]; + if (pool->installed && s->repo == pool->installed) + plist->elements[j++] = plist->elements[k]; + } + } + plist->elements[j++] = p; } if (j) plist->count = j; } static void -prune_to_best_arch(Pool *pool, Queue *plist) +prune_to_best_arch(const Pool *pool, Queue *plist) { Id a, bestscore; Solvable *s; @@ -187,6 +520,8 @@ prune_to_best_arch(Pool *pool, Queue *plist) if (a && a != 1 && (!bestscore || a < bestscore)) bestscore = a; } + if (!bestscore) + return; for (i = j = 0; i < plist->count; i++) { s = pool->solvables + plist->elements[i]; @@ -203,133 +538,339 @@ prune_to_best_arch(Pool *pool, Queue *plist) plist->count = j; } -/* - * prune_best_version_arch - * - * sort list of packages (given through plist) by name and evr - * return result through plist - * - */ -/* FIXME: should prefer installed if identical version */ +struct trj_data { + Pool *pool; + Queue *plist; + Id *stack; + Id nstack; + Id *low; + Id firstidx; + Id idx; +}; +/* This is Tarjan's SCC algorithm, slightly modified */ static void -prune_to_best_version(Pool *pool, Queue *plist) +trj_visit(struct trj_data *trj, Id node) { - Id best = ID_NULL; - int i, j; + Id *low = trj->low; + Pool *pool = trj->pool; + Queue *plist = trj->plist; + Id myidx, stackstart; Solvable *s; + int i; + Id p, pp, obs, *obsp; - if (plist->count < 2) /* no need to prune for a single entry */ - return; - POOL_DEBUG(SAT_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count); - - prune_best_version_arch_sortcmp_data = pool; - /* sort by name first */ - qsort(plist->elements, plist->count, sizeof(Id), prune_best_version_arch_sortcmp); + low[node] = myidx = trj->idx++; + trj->stack[(stackstart = trj->nstack++)] = node; - /* delete obsoleted. hmm, looks expensive! */ - /* FIXME maybe also check provides depending on noupdateprovide? */ - /* FIXME do not prune cycles */ - for (i = 0; i < plist->count; i++) + s = pool->solvables + plist->elements[node]; + if (s->obsoletes) { - Id p, *pp, obs, *obsp; - s = pool->solvables + plist->elements[i]; - if (!s->obsoletes) - continue; obsp = s->repo->idarraydata + s->obsoletes; while ((obs = *obsp++) != 0) { FOR_PROVIDES(p, pp, obs) { - if (pool->solvables[p].name == s->name) + Solvable *ps = pool->solvables + p; + if (ps->name == s->name) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) continue; - for (j = 0; j < plist->count; j++) + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + /* hmm, expensive. should use hash if plist is big */ + for (i = 0; i < plist->count; i++) { - if (i == j) - continue; - if (plist->elements[j] == p) - plist->elements[j] = 0; + if (node != i && plist->elements[i] == p) + { + Id l = low[i]; + if (!l) + { + if (!ps->obsoletes) + { + /* don't bother */ + trj->idx++; + low[i] = -1; + continue; + } + trj_visit(trj, i); + l = low[i]; + } + if (l < 0) + continue; + if (l < trj->firstidx) + { + int k; + /* this means we have reached an old SCC found earlier. + * delete it as we obsolete it */ + for (k = l; ; k++) + { + if (low[trj->stack[k]] == l) + low[trj->stack[k]] = -1; + else + break; + } + } + else if (l < low[node]) + low[node] = l; + } } } } } + if (low[node] == myidx) /* found a SCC? */ + { + /* we're only interested in SCCs that contain the first node, + * as all others are "obsoleted" */ + if (myidx != trj->firstidx) + myidx = -1; + for (i = stackstart; i < trj->nstack; i++) + low[trj->stack[i]] = myidx; + trj->nstack = stackstart; /* empty stack */ + } +} + +/* + * remove entries from plist that are obsoleted by other entries + * with different name. + */ +static void +prune_obsoleted(Pool *pool, Queue *plist) +{ + Id data_buf[2 * 16], *data; + struct trj_data trj; + int i, j; + Solvable *s; + + if (plist->count <= 16) + { + memset(data_buf, 0, sizeof(data_buf)); + data = data_buf; + } + else + data = solv_calloc(plist->count, 2 * sizeof(Id)); + trj.pool = pool; + trj.plist = plist; + trj.low = data; + trj.idx = 1; + trj.stack = data + plist->count - 1; /* -1 so we can index with idx (which starts with 1) */ + for (i = 0; i < plist->count; i++) + { + if (trj.low[i]) + continue; + s = pool->solvables + plist->elements[i]; + if (s->obsoletes) + { + trj.firstidx = trj.nstack = trj.idx; + trj_visit(&trj, i); + } + else + { + Id myidx = trj.idx++; + trj.low[i] = myidx; + trj.stack[myidx] = i; + } + } for (i = j = 0; i < plist->count; i++) - if (plist->elements[i]) + if (trj.low[i] >= 0) plist->elements[j++] = plist->elements[i]; plist->count = j; + if (data != data_buf) + solv_free(data); +} + +/* this is prune_obsoleted special-cased for two elements */ +static void +prune_obsoleted_2(Pool *pool, Queue *plist) +{ + int i; + Solvable *s; + Id p, pp, obs, *obsp; + Id other; + int obmap = 0; + + for (i = 0; i < 2; i++) + { + s = pool->solvables + plist->elements[i]; + other = plist->elements[1 - i]; + if (s->obsoletes) + { + obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + { + FOR_PROVIDES(p, pp, obs) + { + Solvable *ps; + if (p != other) + continue; + ps = pool->solvables + p; + if (ps->name == s->name) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + obmap |= 1 << i; + break; + } + if (p) + break; + } + } + } + if (obmap == 0 || obmap == 3) + return; + if (obmap == 2) + plist->elements[0] = plist->elements[1]; + plist->count = 1; +} + +/* + * bring those elements to the front of the queue that + * have a installed solvable with the same name + */ +static void +move_installed_to_front(Pool *pool, Queue *plist) +{ + int i, j; + Solvable *s; + Id p, pp; + + for (i = j = 0; i < plist->count; i++) + { + s = pool->solvables + plist->elements[i]; + if (s->repo != pool->installed) + { + FOR_PROVIDES(p, pp, s->name) + { + Solvable *ps = pool->solvables + p; + if (s->name == ps->name && ps->repo == pool->installed) + { + s = ps; + break; + } + } + } + if (s->repo == pool->installed) + { + if (i != j) + { + p = plist->elements[i]; + if (i - j == 1) + plist->elements[i] = plist->elements[j]; + else + memmove(plist->elements + j + 1, plist->elements + j, (i - j) * sizeof(Id)); + plist->elements[j] = p; + } + else if (j + 2 == plist->count) + break; /* no need to check last element if all prev ones are installed */ + j++; + } + } +} + +/* + * prune_to_best_version + * + * sort list of packages (given through plist) by name and evr + * return result through plist + */ +static void +prune_to_best_version(Pool *pool, Queue *plist) +{ + int i, j; + Solvable *s, *best; + + if (plist->count < 2) /* no need to prune for a single entry */ + return; + POOL_DEBUG(SOLV_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count); + + /* sort by name first, prefer installed */ + solv_sort(plist->elements, plist->count, sizeof(Id), prune_to_best_version_sortcmp, pool); /* now find best 'per name' */ + best = 0; for (i = j = 0; i < plist->count; i++) { s = pool->solvables + plist->elements[i]; - POOL_DEBUG(SAT_DEBUG_POLICY, "- %s\n", solvable2str(pool, s)); + POOL_DEBUG(SOLV_DEBUG_POLICY, "- %s[%s]\n", + pool_solvable2str(pool, s), + (pool->installed && s->repo == pool->installed) ? "installed" : "not installed"); - if (!best) /* if no best yet, the current is best */ + if (!best) /* if no best yet, the current is best */ { - best = plist->elements[i]; + best = s; continue; } - /* name switch: re-init */ - if (pool->solvables[best].name != s->name) /* new name */ + /* name switch: finish group, re-init */ + if (best->name != s->name) /* new name */ { - plist->elements[j++] = best; /* move old best to front */ - best = plist->elements[i]; /* take current as new best */ + plist->elements[j++] = best - pool->solvables; /* move old best to front */ + best = s; /* take current as new best */ continue; } - if (pool->solvables[best].evr != s->evr) /* compare evr */ + if (best->evr != s->evr) /* compare evr */ { - if (evrcmp(pool, pool->solvables[best].evr, s->evr, EVRCMP_MATCH_RELEASE) < 0) - best = plist->elements[i]; + if (pool_evrcmp(pool, best->evr, s->evr, EVRCMP_COMPARE) < 0) + best = s; } } - - if (best == ID_NULL) - best = plist->elements[0]; - - plist->elements[j++] = best; + plist->elements[j++] = best - pool->solvables; /* finish last group */ plist->count = j; -} -/* legacy, do not use anymore! */ -void -prune_best_version_arch(Pool *pool, Queue *plist) -{ - if (plist->count > 1) - prune_to_best_arch(pool, plist); + /* we reduced the list to one package per name, now look at + * package obsoletes */ if (plist->count > 1) - prune_to_best_version(pool, plist); + { + if (plist->count == 2) + prune_obsoleted_2(pool, plist); + else + prune_obsoleted(pool, plist); + } + if (plist->count > 1 && pool->installed) + move_installed_to_front(pool, plist); } +/* + * POLICY_MODE_CHOOSE: default, do all pruning steps + * POLICY_MODE_RECOMMEND: leave out prune_to_recommended + * POLICY_MODE_SUGGEST: leave out prune_to_recommended, do prio pruning just per name + */ void -policy_filter_unwanted(Solver *solv, Queue *plist, Id inst, int mode) +policy_filter_unwanted(Solver *solv, Queue *plist, int mode) { Pool *pool = solv->pool; - if (plist->count > 1 && mode != POLICY_MODE_SUGGEST) - prune_to_highest_prio(pool, plist); - if (plist->count > 1 && mode == POLICY_MODE_CHOOSE) - prune_to_recommended(solv, plist); - /* FIXME: do this different! */ - if (inst) - queue_push(plist, inst); + if (plist->count > 1) + { + if (mode != POLICY_MODE_SUGGEST) + solver_prune_to_highest_prio(solv, plist); + else + solver_prune_to_highest_prio_per_name(solv, plist); + } if (plist->count > 1) prune_to_best_arch(pool, plist); if (plist->count > 1) prune_to_best_version(pool, plist); + if (plist->count > 1 && mode == POLICY_MODE_CHOOSE) + prune_to_recommended(solv, plist); } +/* check if there is an illegal architecture change if + * installed solvable s1 is replaced by s2 */ int -policy_illegal_archchange(Pool *pool, Solvable *s1, Solvable *s2) +policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2) { + Pool *pool = solv->pool; Id a1 = s1->arch, a2 = s2->arch; /* we allow changes to/from noarch */ - if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH) + if (a1 == a2 || a1 == pool->noarchid || a2 == pool->noarchid) return 0; if (!pool->id2arch) return 0; @@ -340,38 +881,183 @@ policy_illegal_archchange(Pool *pool, Solvable *s1, Solvable *s2) return 0; } +/* check if there is an illegal vendor change if + * installed solvable s1 is replaced by s2 */ int -policy_illegal_vendorchange(Pool *pool, Solvable *s1, Solvable *s2) +policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2) { + Pool *pool = solv->pool; + Id v1, v2; Id vendormask1, vendormask2; - if (s1->vendor == s2->vendor) + + if (pool->custom_vendorcheck) + return pool->custom_vendorcheck(pool, s1, s2); + + /* treat a missing vendor as empty string */ + v1 = s1->vendor ? s1->vendor : ID_EMPTY; + v2 = s2->vendor ? s2->vendor : ID_EMPTY; + if (v1 == v2) return 0; - vendormask1 = pool_vendor2mask(pool, s1->vendor); + vendormask1 = pool_vendor2mask(pool, v1); if (!vendormask1) + return 1; /* can't match */ + vendormask2 = pool_vendor2mask(pool, v2); + if ((vendormask1 & vendormask2) != 0) return 0; - vendormask2 = pool_vendor2mask(pool, s2->vendor); - if ((vendormask1 & vendormask2) == 0) - return 0; - return 1; + return 1; /* no class matches */ } +/* check if it is illegal to replace installed + * package "is" with package "s" (which must obsolete "is") + */ +int +policy_is_illegal(Solver *solv, Solvable *is, Solvable *s, int ignore) +{ + Pool *pool = solv->pool; + int ret = 0; + int duppkg = solv->dupmap_all ? 1 : 0; + if (!(ignore & POLICY_ILLEGAL_DOWNGRADE) && !(duppkg ? solv->dup_allowdowngrade : solv->allowdowngrade)) + { + if (is->name == s->name && pool_evrcmp(pool, is->evr, s->evr, EVRCMP_COMPARE) > 0) + ret |= POLICY_ILLEGAL_DOWNGRADE; + } + if (!(ignore & POLICY_ILLEGAL_ARCHCHANGE) && !(duppkg ? solv->dup_allowarchchange : solv->allowarchchange)) + { + if (is->arch != s->arch && policy_illegal_archchange(solv, is, s)) + ret |= POLICY_ILLEGAL_ARCHCHANGE; + } + if (!(ignore & POLICY_ILLEGAL_VENDORCHANGE) && !(duppkg ? solv->dup_allowvendorchange : solv->allowvendorchange)) + { + if (is->vendor != s->vendor && policy_illegal_vendorchange(solv, is, s)) + ret |= POLICY_ILLEGAL_VENDORCHANGE; + } + if (!(ignore & POLICY_ILLEGAL_NAMECHANGE) && !(duppkg ? solv->dup_allownamechange : solv->allownamechange)) + { + if (is->name != s->name) + ret |= POLICY_ILLEGAL_NAMECHANGE; + } + return ret; +} + +/*------------------------------------------------------------------- + * + * create reverse obsoletes map for installed solvables + * + * For each installed solvable find which packages with *different* names + * obsolete the solvable. + * This index is used in policy_findupdatepackages() below. + */ void -policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allowall) +policy_create_obsolete_index(Solver *solv) +{ + Pool *pool = solv->pool; + Solvable *s; + Repo *installed = solv->installed; + Id p, pp, obs, *obsp, *obsoletes, *obsoletes_data; + int i, n, cnt; + + solv->obsoletes = solv_free(solv->obsoletes); + solv->obsoletes_data = solv_free(solv->obsoletes_data); + if (!installed || installed->start == installed->end) + return; + cnt = installed->end - installed->start; + solv->obsoletes = obsoletes = solv_calloc(cnt, sizeof(Id)); + for (i = 1; i < pool->nsolvables; i++) + { + s = pool->solvables + i; + if (!s->obsoletes) + continue; + if (!pool_installable(pool, s)) + continue; + obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + { + FOR_PROVIDES(p, pp, obs) + { + Solvable *ps = pool->solvables + p;; + if (ps->repo != installed) + continue; + if (ps->name == s->name) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + obsoletes[p - installed->start]++; + } + } + } + n = 0; + for (i = 0; i < cnt; i++) + if (obsoletes[i]) + { + n += obsoletes[i] + 1; + obsoletes[i] = n; + } + solv->obsoletes_data = obsoletes_data = solv_calloc(n + 1, sizeof(Id)); + POOL_DEBUG(SOLV_DEBUG_STATS, "obsoletes data: %d entries\n", n + 1); + for (i = pool->nsolvables - 1; i > 0; i--) + { + s = pool->solvables + i; + if (!s->obsoletes) + continue; + if (!pool_installable(pool, s)) + continue; + obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + { + FOR_PROVIDES(p, pp, obs) + { + Solvable *ps = pool->solvables + p;; + if (ps->repo != installed) + continue; + if (ps->name == s->name) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + if (obsoletes_data[obsoletes[p - installed->start]] != i) + obsoletes_data[--obsoletes[p - installed->start]] = i; + } + } + } +} + + +/* + * find update candidates + * + * s: installed solvable to be updated + * qs: [out] queue to hold Ids of candidates + * allow_all: 0 = dont allow downgrades, 1 = allow all candidates + * 2 = dup mode + * + */ +void +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 p, pp, n, p2, pp2; Id obs, *obsp; Solvable *ps; - Id vendormask; + int haveprovobs = 0; + int allowdowngrade = allow_all ? 1 : solv->allowdowngrade; + int allownamechange = allow_all ? 1 : solv->allownamechange; + int allowarchchange = allow_all ? 1 : solv->allowarchchange; + int allowvendorchange = allow_all ? 1 : solv->allowvendorchange; + if (allow_all == 2) + { + allowdowngrade = solv->dup_allowdowngrade; + allownamechange = solv->dup_allownamechange; + allowarchchange = solv->dup_allowarchchange; + allowvendorchange = solv->dup_allowvendorchange; + } queue_empty(qs); - /* - * s = solvable ptr - * n = solvable Id - */ + n = s - pool->solvables; - vendormask = pool_vendor2mask(pool, s->vendor); /* * look for updates for s @@ -384,23 +1070,28 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allowall) ps = pool->solvables + p; if (s->name == ps->name) /* name match */ { - if (!allowall) - { - if (!solv->allowdowngrade && evrcmp(pool, s->evr, ps->evr, EVRCMP_MATCH_RELEASE) > 0) - continue; - if (!solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(pool, s, ps)) - continue; - if (!solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(pool, s, ps)) - continue; - } + if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + if (!allowdowngrade && pool_evrcmp(pool, s->evr, ps->evr, EVRCMP_COMPARE) > 0) + continue; } + else if (!allownamechange) + continue; else if (!solv->noupdateprovide && ps->obsoletes) /* provides/obsoletes combination ? */ { + /* check if package ps 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)) + 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; } @@ -412,15 +1103,73 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allowall) /* here we have 'p' with a matching provides/obsoletes combination * thus flagging p as a valid update candidate for s */ + haveprovobs = 1; } else 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); } - if (solv->noupdateprovide && solv->obsoletes && solv->obsoletes[n - solv->installed->start]) + if (!allownamechange) + return; + /* if we have found some valid candidates and noupdateprovide is not set, we're + done. otherwise we fallback to all obsoletes */ + if (!solv->noupdateprovide && haveprovobs) + return; + if (solv->obsoletes && solv->obsoletes[n - solv->installed->start]) { - for (pp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *pp++) != 0;) - queue_push(qs, p); + Id *opp; + 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; + queue_push(qs, p); + } + } +} + +const char * +policy_illegal2str(Solver *solv, int illegal, Solvable *s, Solvable *rs) +{ + Pool *pool = solv->pool; + const char *str; + if (illegal == POLICY_ILLEGAL_DOWNGRADE) + { + str = pool_tmpjoin(pool, "downgrade of ", pool_solvable2str(pool, s), 0); + return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs)); + } + if (illegal == POLICY_ILLEGAL_NAMECHANGE) + { + str = pool_tmpjoin(pool, "name change of ", pool_solvable2str(pool, s), 0); + return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs)); + } + if (illegal == POLICY_ILLEGAL_ARCHCHANGE) + { + str = pool_tmpjoin(pool, "architecture change of ", pool_solvable2str(pool, s), 0); + return pool_tmpappend(pool, str, " to ", pool_solvable2str(pool, rs)); + } + if (illegal == POLICY_ILLEGAL_VENDORCHANGE) + { + str = pool_tmpjoin(pool, "vendor change from '", pool_id2str(pool, s->vendor), "' ("); + if (rs->vendor) + { + str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to '"); + str = pool_tmpappend(pool, str, pool_id2str(pool, rs->vendor), "' ("); + } + else + str = pool_tmpappend(pool, str, pool_solvable2str(pool, s), ") to no vendor ("); + return pool_tmpappend(pool, str, pool_solvable2str(pool, rs), ")"); } + return "unknown illegal change"; }