From: Michael Schroeder Date: Mon, 10 Dec 2018 14:05:39 +0000 (+0100) Subject: Do favor evaluation before pruning X-Git-Tag: 0.7.3~18 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=2645bc64b322460ac77d0d051de63434d4a90a0a;p=thirdparty%2Flibsolv.git Do favor evaluation before pruning This allows to support favoring single packages. As a side effect, we'll have the best version displayed as alternative. This changes the code to use a favor id for every solvable, which means that it uses quite a bit of memory but makes the code much easier and faster. --- diff --git a/src/policy.c b/src/policy.c index 191327a1..45c7357d 100644 --- a/src/policy.c +++ b/src/policy.c @@ -454,86 +454,18 @@ prefer_suggested(Solver *solv, Queue *plist) } static int -sort_by_favorq_cmp(const void *ap, const void *bp, void *dp) +sort_by_favor_cmp(const void *ap, const void *bp, void *dp) { const Id *a = ap, *b = bp, *d = dp; return d[b[0]] - d[a[0]]; } -static void -sort_by_favorq(Queue *favorq, Id *el, int cnt) -{ - int i; - /* map to offsets into favorq */ - for (i = 0; i < cnt; i++) - { - Id p = el[i]; - /* lookup p in sorted favorq */ - int med = 0, low = 0; - int high = favorq->count / 2; - while (low != high) - { - med = (low + high) / 2; - Id pp = favorq->elements[2 * med]; - if (pp < p) - low = med; - else if (pp > p) - high = med; - else - break; - } - while(med && favorq->elements[2 * med - 2] == p) - med--; - if (favorq->elements[2 * med] == p) - el[i] = 2 * med + 1; - else - el[i] = 0; /* hmm */ - } - /* sort by position */ - solv_sort(el, cnt, sizeof(Id), sort_by_favorq_cmp, favorq->elements); - /* map back */ - for (i = 0; i < cnt; i++) - if (el[i]) - el[i] = favorq->elements[el[i] - 1]; -} - /* bring favored packages to front and disfavored packages to back */ void policy_prefer_favored(Solver *solv, Queue *plist) { - int i, fav, disfav, count; - if (!solv->favormap.size) - return; - for (i = fav = disfav = 0, count = plist->count; i < count; i++) - { - Id p = plist->elements[i]; - if (!MAPTST(&solv->favormap, p)) - continue; - if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, p)) - { - /* disfavored package. bring to back */ - if (i < plist->count - 1) - { - memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id)); - plist->elements[plist->count - 1] = p; - } - i--; - count--; - disfav++; - } - else - { - /* favored package. bring to front */ - if (i > fav) - memmove(plist->elements + fav + 1, plist->elements + fav, (i - fav) * sizeof(Id)); - plist->elements[fav++] = p; - } - } - /* if we have multiple favored/disfavored packages, sort by favorq index */ - if (fav > 1) - sort_by_favorq(solv->favorq, plist->elements, fav); - if (disfav > 1) - sort_by_favorq(solv->favorq, plist->elements + plist->count - disfav, disfav); + if (solv->favormap && plist->count > 1) + solv_sort(plist->elements, plist->count, sizeof(Id), sort_by_favor_cmp, solv->favormap); } /* @@ -1300,6 +1232,37 @@ urpm_reorder(Solver *solv, Queue *plist) queue_truncate(plist, count); } +/* support multiple favor groups by calling policy_filter_unwanted on + * each of them and combining the result */ +static void +policy_filter_unwanted_favored(Solver *solv, Queue *plist, int mode) +{ + int i, j, f; + Queue qin, qprune; + queue_init_clone(&qin, plist); + queue_empty(plist); + /* sort by favor group */ + solv_sort(qin.elements, qin.count, sizeof(Id), sort_by_favor_cmp, solv->favormap); + /* go over groups */ + queue_init(&qprune); + for (i = 0; i < qin.count; i = j) + { + /* find end of group */ + f = solv->favormap[qin.elements[i]]; + for (j = i + 1; j < qin.count; j++) + if (solv->favormap[qin.elements[j]] != f) + break; + /* prune this group */ + queue_empty(&qprune); + queue_insertn(&qprune, 0, j, qin.elements); + policy_filter_unwanted(solv, &qprune, mode | POLICY_MODE_FAVOR_REC); + for (i = 0; i < qprune.count; i++) + if (solv->favormap[qprune.elements[i]] == f) + queue_push(plist, qprune.elements[i]); + } + queue_free(&qprune); + queue_free(&qin); +} /* * POLICY_MODE_CHOOSE: default, do all pruning steps @@ -1321,6 +1284,21 @@ policy_filter_unwanted(Solver *solv, Queue *plist, int mode) policy_prefer_favored(solv, plist); return; } + if (mode & POLICY_MODE_FAVOR_REC) + mode ^= POLICY_MODE_FAVOR_REC; + else if (solv->favormap && plist->count > 1) + { + /* check if we have multiple favor groups */ + int i, f = solv->favormap[plist->elements[0]]; + for (i = 1; i < plist->count; i++) + if (solv->favormap[plist->elements[i]] != f) + break; + if (i < plist->count) + { + policy_filter_unwanted_favored(solv, plist, mode); + return; + } + } if (plist->count > 1) { if (mode != POLICY_MODE_SUGGEST) diff --git a/src/policy.h b/src/policy.h index 68f4db93..3ae1005a 100644 --- a/src/policy.h +++ b/src/policy.h @@ -21,6 +21,7 @@ extern "C" { #define POLICY_MODE_SUGGEST 2 #define POLICY_MODE_CHOOSE_NOREORDER 3 /* internal, do not use */ #define POLICY_MODE_SUPPLEMENT 4 /* internal, do not use */ +#define POLICY_MODE_FAVOR_REC (1 << 30) /* internal, do not use */ #define POLICY_ILLEGAL_DOWNGRADE 1 diff --git a/src/solver.c b/src/solver.c index 57fa3e49..cf41c915 100644 --- a/src/solver.c +++ b/src/solver.c @@ -243,7 +243,7 @@ makeruledecisions(Solver *solv, int disablerules) continue; if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, i)) /* weak: silently ignore */ continue; - + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, " - disabling rule #%d\n", i); solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + i); solver_recordproblem(solv, i); @@ -1370,9 +1370,8 @@ solver_free(Solver *solv) map_free(&solv->droporphanedmap); map_free(&solv->cleandepsmap); map_free(&solv->allowuninstallmap); - map_free(&solv->favormap); - map_free(&solv->isdisfavormap); + solv_free(solv->favormap); solv_free(solv->decisionmap); solv_free(solv->rules); solv_free(solv->watches); @@ -2058,14 +2057,12 @@ static void prune_disfavored(Solver *solv, Queue *plist) { int i, j; - if (!solv->isdisfavormap.size) - return; - for (i = j = 0; i < plist->count; i++) - { + for (i = j = 0; i < plist->count; i++) + { Id p = plist->elements[i]; - if (!MAPTST(&solv->isdisfavormap, p)) - plist->elements[j++] = p; - } + if (solv->favormap[p] >= 0) + plist->elements[j++] = p; + } if (i != j) queue_truncate(plist, j); } @@ -2141,14 +2138,14 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i continue; if (solv->process_orphans && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start)))) continue; - if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, i)) + if (solv->havedisfavored && solv->favormap[i] < 0) continue; /* disfavored supplements, do not install */ queue_push(dqs, i); } } /* filter out disfavored recommended packages */ - if (dq->count && solv->isdisfavormap.size) + if (dq->count && solv->havedisfavored) prune_disfavored(solv, dq); /* filter out all packages obsoleted by installed packages */ @@ -2617,7 +2614,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) if (rerun) continue; } - + /* one final pass to make sure we decided all installed packages */ if (solv->installed) { @@ -2700,7 +2697,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) lastsi = -1; break; } - if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, p)) + if (solv->havedisfavored && solv->favormap[p] < 0) continue; if (lastsi < 0 && (MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p))) lastsi = i; @@ -2714,9 +2711,8 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) p = -solv->branches.elements[i]; if (p <= 0 || solv->decisionmap[p] != l + 1) continue; - if (solv->favormap.size && MAPTST(&solv->favormap, p)) - if (!(solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, p))) - continue; /* current selection is favored */ + if (solv->favormap && solv->favormap[p] > solv->favormap[solv->branches.elements[lastsi]]) + continue; /* current selection is more favored */ if (!(MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p))) { lasti = lastsi; @@ -3187,7 +3183,7 @@ add_complex_jobrules(Solver *solv, Id dep, int flags, int jobidx, int weak) /* sort by package id, last entry wins */ static int -setup_favormaps_cmp(const void *ap, const void *bp, void *dp) +setup_favormap_cmp(const void *ap, const void *bp, void *dp) { const Id *a = ap, *b = bp; if ((*a - *b) != 0) @@ -3196,28 +3192,25 @@ setup_favormaps_cmp(const void *ap, const void *bp, void *dp) } static void -setup_favormaps(Solver *solv) +setup_favormap(Solver *solv) { Queue *q = solv->favorq; Pool *pool = solv->pool; int i; Id oldp = 0; if (q->count > 2) - solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), setup_favormaps_cmp, solv); - map_grow(&solv->favormap, pool->nsolvables); + solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), setup_favormap_cmp, solv); + solv->favormap = solv_calloc(pool->nsolvables, sizeof(Id)); + solv->havedisfavored = 0; for (i = 0; i < q->count; i += 2) { Id p = q->elements[i]; if (p == oldp) continue; oldp = p; - MAPSET(&solv->favormap, p); + solv->favormap[p] = q->elements[i + 1]; if (q->elements[i + 1] < 0) - { - if (!solv->isdisfavormap.size) - map_grow(&solv->isdisfavormap, pool->nsolvables); - MAPSET(&solv->isdisfavormap, p); - } + solv->havedisfavored = 1; } } @@ -3294,8 +3287,7 @@ solver_solve(Solver *solv, Queue *job) map_zerosize(&solv->allowuninstallmap); map_zerosize(&solv->cleandepsmap); map_zerosize(&solv->weakrulemap); - map_zerosize(&solv->favormap); - map_zerosize(&solv->isdisfavormap); + solv->favormap = solv_free(solv->favormap); queue_empty(&solv->weakruleq); solv->watches = solv_free(solv->watches); queue_empty(&solv->ruletojob); @@ -3839,14 +3831,12 @@ solver_solve(Solver *solv, Queue *job) POOL_DEBUG(SOLV_DEBUG_JOB, "job: %s %s\n", (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? "favor" : "disfavor", solver_select2str(pool, select, what)); FOR_JOB_SELECT(p, pp, select, what) { - int j; if (!solv->favorq) { solv->favorq = solv_calloc(1, sizeof(Queue)); queue_init(solv->favorq); } - j = solv->favorq->count + 1; - queue_push2(solv->favorq, p, (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? j : -j); + queue_push2(solv->favorq, p, (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? i + 1 : -(i + 1)); } break; default: @@ -3869,9 +3859,9 @@ solver_solve(Solver *solv, Queue *job) assert(solv->ruletojob.count == solv->nrules - solv->jobrules); solv->jobrules_end = solv->nrules; - /* transform favorq into two maps */ + /* sort favorq and transform it into two maps */ if (solv->favorq) - setup_favormaps(solv); + setup_favormap(solv); /* now create infarch and dup rules */ if (!solv->noinfarchcheck) diff --git a/src/solver.h b/src/solver.h index 6fc2e9f0..10b4506d 100644 --- a/src/solver.h +++ b/src/solver.h @@ -202,8 +202,8 @@ struct s_Solver { int allowuninstall_all; Queue *favorq; - Map favormap; /* favored / disfavored packages */ - Map isdisfavormap; + Id *favormap; /* favor job index, > 0: favored, < 0: disfavored */ + int havedisfavored; /* do we have disfavored packages? */ int installedpos; /* for resolve_installed */ int do_extra_reordering; /* reorder for future installed packages */ diff --git a/test/testcases/favor/recommends2.t b/test/testcases/favor/recommends2.t new file mode 100644 index 00000000..99506615 --- /dev/null +++ b/test/testcases/favor/recommends2.t @@ -0,0 +1,20 @@ +repo system 0 empty +repo available 0 testtags +#>=Pkg: a 1 1 noarch +#>=Req: b +#>=Rec: b1 +#>=Pkg: b1 1 1 noarch +#>=Prv: b +#>=Pkg: b2 1 1 noarch +#>=Prv: b +system x86_64 rpm system +poolflags implicitobsoleteusescolors +solverflags ignorerecommended +job install pkg a-1-1.noarch@available +job favor name b2 +result transaction,problems,alternatives +#>alternative 64eb4d87 0 a-1-1.noarch@available requires b +#>alternative 64eb4d87 1 + b2-1-1.noarch@available +#>alternative 64eb4d87 2 b1-1-1.noarch@available +#>install a-1-1.noarch@available +#>install b2-1-1.noarch@available diff --git a/test/testcases/favor/single.t b/test/testcases/favor/single.t new file mode 100644 index 00000000..3a1d93b2 --- /dev/null +++ b/test/testcases/favor/single.t @@ -0,0 +1,30 @@ +repo system 0 empty +repo available 0 testtags +#>=Pkg: gcc 5 1 noarch +#>=Prv: gcc +#>=Pkg: gcc 6 1 noarch +#>=Prv: gcc +#>=Pkg: build-tools 1 1 noarch +#>=Req: gcc +system unset * system + +job install name build-tools +job favor pkg gcc-5-1.noarch@available +result transaction,problems,alternatives +#>alternative 6e50ec1a 0 build-tools-1-1.noarch@available requires gcc +#>alternative 6e50ec1a 1 + gcc-5-1.noarch@available +#>alternative 6e50ec1a 2 gcc-6-1.noarch@available +#>install build-tools-1-1.noarch@available +#>install gcc-5-1.noarch@available + +nextjob + +job install name build-tools +job disfavor pkg gcc-6-1.noarch@available +result transaction,problems,alternatives +#>alternative 6e50ec1a 0 build-tools-1-1.noarch@available requires gcc +#>alternative 6e50ec1a 1 + gcc-5-1.noarch@available +#>alternative 6e50ec1a 2 gcc-6-1.noarch@available +#>install build-tools-1-1.noarch@available +#>install gcc-5-1.noarch@available +