X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=src%2Fsolver.c;h=14fe78d7789b4348d2f44332392523f717fb5f9c;hb=refs%2Ftags%2Fupstream%2F0.6.6;hp=182a0e84b21a942588850168578b073eb71ce3f0;hpb=ff7ade8c1fda1acd54e537c2a66f6c924201cfe4;p=platform%2Fupstream%2Flibsolv.git diff --git a/src/solver.c b/src/solver.c index 182a0e8..14fe78d 100644 --- a/src/solver.c +++ b/src/solver.c @@ -25,6 +25,8 @@ #include "policy.h" #include "poolarch.h" #include "solverdebug.h" +#include "cplxdeps.h" +#include "linkedpkg.h" #define RULES_BLOCK 63 @@ -238,7 +240,7 @@ autouninstall(Solver *solv, Id *problem) /************************************************************************/ /* - * enable/disable learnt rules + * enable/disable learnt rules * * we have enabled or disabled some of our rules. We now reenable all * of our learnt rules except the ones that were learnt from rules that @@ -287,7 +289,7 @@ enabledisablelearntrules(Solver *solv) /* * make assertion rules into decisions - * + * * Go through rules and add direct assertions to the decisionqueue. * If we find a conflict, disable rules and add them to problem queue. */ @@ -327,7 +329,7 @@ makeruledecisions(Solver *solv) { ri = solv->ruleassertions.elements[ii]; r = solv->rules + ri; - + if (havedisabled && ri >= solv->learntrules) { /* just started with learnt rule assertions. If we have disabled @@ -335,7 +337,7 @@ makeruledecisions(Solver *solv) enabledisablelearntrules(solv); havedisabled = 0; } - + if (r->d < 0 || !r->p || r->w2) /* disabled, dummy or no assertion */ continue; @@ -345,7 +347,7 @@ makeruledecisions(Solver *solv) v = r->p; vv = v > 0 ? v : -v; - + if (!solv->decisionmap[vv]) /* if not yet decided */ { queue_push(&solv->decisionq, v); @@ -367,15 +369,15 @@ makeruledecisions(Solver *solv) continue; if (v < 0 && solv->decisionmap[vv] < 0) /* ok to remove */ continue; - + /* * found a conflict! - * + * * The rule (r) we're currently processing says something * different (v = r->p) than a previous decision (decisionmap[abs(v)]) * on this literal */ - + if (ri >= solv->learntrules) { /* conflict with a learnt rule */ @@ -385,7 +387,7 @@ makeruledecisions(Solver *solv) solver_disablerule(solv, r); continue; } - + /* * find the decision which is the "opposite" of the rule */ @@ -394,7 +396,7 @@ makeruledecisions(Solver *solv) break; assert(i < solv->decisionq.count); /* assert that we found it */ oldproblemcount = solv->problems.count; - + /* * conflict with system solvable ? */ @@ -425,9 +427,9 @@ makeruledecisions(Solver *solv) assert(solv->decisionq_why.elements[i] > 0); /* - * conflict with an rpm rule ? + * conflict with a pkg rule ? */ - if (solv->decisionq_why.elements[i] < solv->rpmrules_end) + if (solv->decisionq_why.elements[i] < solv->pkgrules_end) { if (record_proof) { @@ -439,7 +441,7 @@ makeruledecisions(Solver *solv) else queue_push(&solv->problems, 0); assert(v > 0 || v == -SYSTEMSOLVABLE); - POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with rpm rule, disabling rule #%d\n", ri); + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "conflict with pkg rule, disabling rule #%d\n", ri); if (ri >= solv->jobrules && ri < solv->jobrules_end) v = -(solv->ruletojob.elements[ri - solv->jobrules] + 1); else @@ -456,7 +458,7 @@ makeruledecisions(Solver *solv) /* * conflict with another job or update/feature rule */ - + /* record proof */ if (record_proof) { @@ -540,7 +542,7 @@ makeruledecisions(Solver *solv) continue; if (v < 0 && solv->decisionmap[vv] < 0) continue; - + POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, "assertion conflict, but I am weak, disabling "); solver_printrule(solv, SOLV_DEBUG_UNSOLVABLE, r); @@ -581,7 +583,7 @@ makewatches(Solver *solv) /* lower half for removals, upper half for installs */ solv->watches = solv_calloc(2 * nsolvables, sizeof(Id)); #if 1 - /* do it reverse so rpm rules get triggered first (XXX: obsolete?) */ + /* do it reverse so pkg rules get triggered first (XXX: obsolete?) */ for (i = 1, r = solv->rules + solv->nrules - 1; i < solv->nrules; i++, r--) #else for (i = 1, r = solv->rules + 1; i < solv->nrules; i++, r++) @@ -604,7 +606,7 @@ makewatches(Solver *solv) * * add watches (for a new learned rule) * sets up watches for a single rule - * + * * see also makewatches() above. */ @@ -635,14 +637,14 @@ addwatches_rule(Solver *solv, Rule *r) #define DECISIONMAP_UNDEF(p) (decisionmap[(p) > 0 ? (p) : -(p)] == 0) /*------------------------------------------------------------------- - * + * * propagate * * make decision and propagate to all rules - * + * * Evaluate each term affected by the decision (linked through watches). * If we find unit rules we make new decisions based on them. - * + * * return : 0 = everything is OK * rule = conflict found in this rule */ @@ -656,7 +658,7 @@ propagate(Solver *solv, int level) Id p, pkg, other_watch; Id *dp; Id *decisionmap = solv->decisionmap; - + Id *watches = solv->watches + pool->nsolvables; /* place ptr in middle */ POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "----- propagate -----\n"); @@ -697,7 +699,7 @@ propagate(Solver *solv, int level) } /* 'pkg' was just decided (was set to FALSE) - * + * * now find other literal watch, check clause * and advance on linked list */ @@ -711,8 +713,8 @@ propagate(Solver *solv, int level) other_watch = r->w1; next_rp = &r->n2; } - - /* + + /* * This term is already true (through the other literal) * so we have nothing to do */ @@ -721,17 +723,17 @@ propagate(Solver *solv, int level) /* * The other literal is FALSE or UNDEF - * + * */ - + if (r->d) { /* Not a binary clause, try to move our watch. - * + * * Go over all literals and find one that is * not other_watch * and not FALSE - * + * * (TRUE is also ok, in that case the rule is fulfilled) */ if (r->p /* we have a 'p' */ @@ -766,10 +768,10 @@ propagate(Solver *solv, int level) else POOL_DEBUG(SOLV_DEBUG_PROPAGATE," -> move w%d to !%s\n", (pkg == r->w1 ? 1 : 2), pool_solvid2str(pool, -p)); } - + *rp = *next_rp; next_rp = rp; - + if (pkg == r->w1) { r->w1 = p; @@ -786,14 +788,14 @@ propagate(Solver *solv, int level) /* search failed, thus all unwatched literals are FALSE */ } /* not binary */ - + /* * unit clause found, set literal other_watch to TRUE */ if (DECISIONMAP_FALSE(other_watch)) /* check if literal is FALSE */ return r; /* eek, a conflict! */ - + IF_POOLDEBUG (SOLV_DEBUG_PROPAGATE) { POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " unit "); @@ -804,7 +806,7 @@ propagate(Solver *solv, int level) decisionmap[other_watch] = level; /* install! */ else decisionmap[-other_watch] = -level; /* remove! */ - + queue_push(&solv->decisionq, other_watch); queue_push(&solv->decisionq_why, r - solv->rules); @@ -815,11 +817,11 @@ propagate(Solver *solv, int level) else POOL_DEBUG(SOLV_DEBUG_PROPAGATE, " -> decided to conflict %s\n", pool_solvid2str(pool, -other_watch)); } - + } /* foreach rule involving 'pkg' */ } /* while we have non-decided decisions */ - + POOL_DEBUG(SOLV_DEBUG_PROPAGATE, "----- propagate end-----\n"); return 0; /* all is well */ @@ -830,7 +832,7 @@ propagate(Solver *solv, int level) /* Analysis */ /*------------------------------------------------------------------- - * + * * analyze * and learn */ @@ -957,9 +959,9 @@ l1retry: /*------------------------------------------------------------------- - * + * * solver_reset - * + * * reset all solver decisions * called after rules have been enabled/disabled */ @@ -994,7 +996,7 @@ solver_reset(Solver *solv) /*------------------------------------------------------------------- - * + * * analyze_unsolvable_rule * * recursion helper used by analyze_unsolvable @@ -1022,8 +1024,8 @@ analyze_unsolvable_rule(Solver *solv, Rule *r, Id *lastweakp, Map *rseen) if (MAPTST(&solv->weakrulemap, why)) if (!*lastweakp || why > *lastweakp) *lastweakp = why; - /* do not add rpm rules to problem */ - if (why < solv->rpmrules_end) + /* do not add pkg rules to problem */ + if (why < solv->pkgrules_end) return; /* turn rule into problem */ if (why >= solv->jobrules && why < solv->jobrules_end) @@ -1056,12 +1058,12 @@ analyze_unsolvable_rule(Solver *solv, Rule *r, Id *lastweakp, Map *rseen) /*------------------------------------------------------------------- - * + * * analyze_unsolvable * * We know that the problem is not solvable. Record all involved * rules (i.e. the "proof") into solv->learnt_pool. - * Record the learnt pool index and all non-rpm rules into + * Record the learnt pool index and all non-pkg rules into * solv->problems. (Our solutions to fix the problems are to * disable those rules.) * @@ -1132,7 +1134,7 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules) { v = solv->decisionq.elements[--idx]; vv = v > 0 ? v : -v; - if (!MAPTST(&seen, vv)) + if (!MAPTST(&seen, vv) || vv == SYSTEMSOLVABLE) continue; why = solv->decisionq_why.elements[idx]; assert(why > 0); @@ -1218,7 +1220,7 @@ analyze_unsolvable(Solver *solv, Rule *cr, int disablerules) /* Decision revert */ /*------------------------------------------------------------------- - * + * * revert * revert decisionq to a level */ @@ -1240,28 +1242,33 @@ revert(Solver *solv, int level) solv->decisionq_why.count--; solv->propagate_index = solv->decisionq.count; } - while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] <= -level) + while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] >= level) { solv->branches.count--; while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] >= 0) solv->branches.count--; + while (solv->branches.count && solv->branches.elements[solv->branches.count - 1] < 0) + solv->branches.count--; } - solv->recommends_index = -1; + if (solv->recommends_index > solv->decisionq.count) + solv->recommends_index = -1; /* rebuild recommends/suggests maps */ + if (solv->decisionq.count < solv->decisioncnt_jobs) + solv->decisioncnt_jobs = 0; if (solv->decisionq.count < solv->decisioncnt_update) - solv->decisioncnt_update = 0; + solv->decisioncnt_update = 0; if (solv->decisionq.count < solv->decisioncnt_keep) - solv->decisioncnt_keep = 0; + solv->decisioncnt_keep = 0; if (solv->decisionq.count < solv->decisioncnt_resolve) - solv->decisioncnt_resolve = 0; + solv->decisioncnt_resolve = 0; if (solv->decisionq.count < solv->decisioncnt_weak) solv->decisioncnt_weak= 0; if (solv->decisionq.count < solv->decisioncnt_orphan) - solv->decisioncnt_orphan = 0; + solv->decisioncnt_orphan = 0; } /*------------------------------------------------------------------- - * + * * watch2onhighest - put watch2 on literal with highest level */ @@ -1290,7 +1297,7 @@ watch2onhighest(Solver *solv, Rule *r) /*------------------------------------------------------------------- - * + * * setpropagatelearn * * add free decision (solvable to install) to decisionq @@ -1383,7 +1390,7 @@ reorder_dq_for_jobrules(Solver *solv, int level, Queue *dq) continue; if (solv->decisionmap[p] == 0) { - solv->decisionmap[p] = level; + solv->decisionmap[p] = level + 1; haveone = 1; } } @@ -1407,14 +1414,14 @@ reorder_dq_for_jobrules(Solver *solv, int level, Queue *dq) dq->elements[j++] = dq->elements[i]; queue_truncate(dq, j); FOR_REPO_SOLVABLES(solv->installed, p, s) - if (solv->decisionmap[p] == level) + if (solv->decisionmap[p] == level + 1) solv->decisionmap[p] = 0; } /*------------------------------------------------------------------- - * + * * select and install - * + * * install best package from the queue. We add an extra package, inst, if * provided. See comment in weak install section. * @@ -1445,14 +1452,17 @@ selectandinstall(Solver *solv, int level, Queue *dq, int disablerules, Id ruleid break; } } - if (dq->count > 1 && ruleid >= solv->jobrules && ruleid < solv->jobrules_end && solv->installed) + /* if we're resolving job rules and didn't resolve the installed packages yet, + * do some special supplements ordering */ + if (dq->count > 1 && ruleid >= solv->jobrules && ruleid < solv->jobrules_end && solv->installed && !solv->focus_installed) reorder_dq_for_jobrules(solv, level, dq); if (dq->count > 1) { /* multiple candidates, open a branch */ + queue_push(&solv->branches, -dq->elements[0]); for (i = 1; i < dq->count; i++) queue_push(&solv->branches, dq->elements[i]); - queue_push(&solv->branches, -level); + queue_push(&solv->branches, level); } p = dq->elements[0]; @@ -1467,7 +1477,7 @@ selectandinstall(Solver *solv, int level, Queue *dq, int disablerules, Id ruleid /*------------------------------------------------------------------- - * + * * solver_create * create solver structure * @@ -1524,11 +1534,107 @@ solver_create(Pool *pool) } +static int +resolve_jobrules(Solver *solv, int level, int disablerules, Queue *dq) +{ + Pool *pool = solv->pool; + int oldlevel = level; + int i, olevel; + Rule *r; + + POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving job rules\n"); + if (!solv->decisioncnt_jobs) + solv->decisioncnt_jobs = solv->decisionq.count; + for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++) + { + Id l, pp; + if (r->d < 0) /* ignore disabled rules */ + continue; + queue_empty(dq); + FOR_RULELITERALS(l, pp, r) + { + if (l < 0) + { + if (solv->decisionmap[-l] <= 0) + break; + } + else + { + if (solv->decisionmap[l] > 0) + break; + if (solv->decisionmap[l] == 0) + queue_push(dq, l); + } + } + if (l || !dq->count) + continue; + /* prune to installed if not updating */ + if (dq->count > 1 && solv->installed && !solv->updatemap_all && + !(solv->job.elements[solv->ruletojob.elements[i - solv->jobrules]] & SOLVER_ORUPDATE)) + { + int j, k; + for (j = k = 0; j < dq->count; j++) + { + Solvable *s = pool->solvables + dq->elements[j]; + if (s->repo == solv->installed) + { + dq->elements[k++] = dq->elements[j]; + if (solv->updatemap.size && MAPTST(&solv->updatemap, dq->elements[j] - solv->installed->start)) + { + k = 0; /* package wants to be updated, do not prune */ + break; + } + } + } + if (k) + dq->count = k; + } + olevel = level; + level = selectandinstall(solv, level, dq, disablerules, i); + if (level <= olevel) + { + if (level == 0) + return 0; /* unsolvable */ + if (level == olevel) + { + i--; + r--; + continue; /* try something else */ + } + if (level < oldlevel) + return level; + /* redo from start of jobrules */ + i = solv->jobrules - 1; + r = solv->rules + i; + } + } + return level; +} + /*------------------------------------------------------------------- - * + * * solver_free */ +static inline void +queuep_free(Queue **qp) +{ + if (!*qp) + return; + queue_free(*qp); + *qp = solv_free(*qp); +} + +static inline void +map_zerosize(Map *m) +{ + if (m->size) + { + map_free(m); + map_init(m, 0); + } +} + void solver_free(Solver *solv) { @@ -1545,26 +1651,13 @@ solver_free(Solver *solv) queue_free(&solv->weakruleq); queue_free(&solv->ruleassertions); queue_free(&solv->addedmap_deduceq); - if (solv->cleandeps_updatepkgs) - { - queue_free(solv->cleandeps_updatepkgs); - solv->cleandeps_updatepkgs = solv_free(solv->cleandeps_updatepkgs); - } - if (solv->cleandeps_mistakes) - { - queue_free(solv->cleandeps_mistakes); - solv->cleandeps_mistakes = solv_free(solv->cleandeps_mistakes); - } - if (solv->update_targets) - { - queue_free(solv->update_targets); - solv->update_targets = solv_free(solv->update_targets); - } - if (solv->installsuppdepq) - { - queue_free(solv->installsuppdepq); - solv->installsuppdepq = solv_free(solv->installsuppdepq); - } + queuep_free(&solv->cleandeps_updatepkgs); + queuep_free(&solv->cleandeps_mistakes); + queuep_free(&solv->update_targets); + queuep_free(&solv->installsuppdepq); + queuep_free(&solv->recommendscplxq); + queuep_free(&solv->suggestscplxq); + queuep_free(&solv->brokenorphanrules); map_free(&solv->recommendsmap); map_free(&solv->suggestsmap); @@ -1585,9 +1678,11 @@ solver_free(Solver *solv) solv_free(solv->watches); solv_free(solv->obsoletes); solv_free(solv->obsoletes_data); - solv_free(solv->multiversionupdaters); + solv_free(solv->specialupdaters); solv_free(solv->choicerules_ref); solv_free(solv->bestrules_pkg); + solv_free(solv->yumobsrules_info); + solv_free(solv->instbuddy); solv_free(solv); } @@ -1622,6 +1717,22 @@ solver_get_flag(Solver *solv, int flag) return solv->bestobeypolicy; case SOLVER_FLAG_NO_AUTOTARGET: return solv->noautotarget; + case SOLVER_FLAG_DUP_ALLOW_DOWNGRADE: + return solv->dup_allowdowngrade; + case SOLVER_FLAG_DUP_ALLOW_NAMECHANGE: + return solv->dup_allownamechange; + case SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE: + return solv->dup_allowarchchange; + case SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE: + return solv->dup_allowvendorchange; + case SOLVER_FLAG_KEEP_ORPHANS: + return solv->keep_orphans; + case SOLVER_FLAG_BREAK_ORPHANS: + return solv->break_orphans; + case SOLVER_FLAG_FOCUS_INSTALLED: + return solv->focus_installed; + case SOLVER_FLAG_YUM_OBSOLETES: + return solv->do_yum_obsoletes; default: break; } @@ -1673,6 +1784,30 @@ solver_set_flag(Solver *solv, int flag, int value) case SOLVER_FLAG_NO_AUTOTARGET: solv->noautotarget = value; break; + case SOLVER_FLAG_DUP_ALLOW_DOWNGRADE: + solv->dup_allowdowngrade = value; + break; + case SOLVER_FLAG_DUP_ALLOW_NAMECHANGE: + solv->dup_allownamechange = value; + break; + case SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE: + solv->dup_allowarchchange = value; + break; + case SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE: + solv->dup_allowvendorchange = value; + break; + case SOLVER_FLAG_KEEP_ORPHANS: + solv->keep_orphans = value; + break; + case SOLVER_FLAG_BREAK_ORPHANS: + solv->break_orphans = value; + break; + case SOLVER_FLAG_FOCUS_INSTALLED: + solv->focus_installed = value; + break; + case SOLVER_FLAG_YUM_OBSOLETES: + solv->do_yum_obsoletes = value; + break; default: break; } @@ -1749,8 +1884,140 @@ prune_to_update_targets(Solver *solv, Id *cp, Queue *q) queue_truncate(q, j); } +#ifdef ENABLE_COMPLEX_DEPS + +static void +add_complex_recommends(Solver *solv, Id rec, Queue *dq, Map *dqmap) +{ + Pool *pool = solv->pool; + int oldcnt = dq->count; + int cutcnt, blkcnt; + Id p; + int i, j; + +#if 0 + printf("ADD_COMPLEX_RECOMMENDS %s\n", pool_dep2str(pool, rec)); +#endif + i = pool_normalize_complex_dep(pool, rec, dq, CPLXDEPS_EXPAND); + if (i == 0 || i == 1) + return; + cutcnt = dq->count; + for (i = oldcnt; i < cutcnt; i++) + { + blkcnt = dq->count; + for (; (p = dq->elements[i]) != 0; i++) + { + if (p < 0) + { + if (solv->decisionmap[-p] <= 0) + break; + continue; + } + if (solv->decisionmap[p] > 0) + { + queue_truncate(dq, blkcnt); + break; + } + if (dqmap) + { + if (!MAPTST(dqmap, p)) + continue; + } + else + { + if (solv->decisionmap[p] < 0) + continue; + if (solv->dupmap_all && solv->installed && pool->solvables[p].repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start)))) + continue; + } + queue_push(dq, p); + } + while (dq->elements[i]) + i++; + } + queue_deleten(dq, oldcnt, cutcnt - oldcnt); + /* unify */ + if (dq->count != oldcnt) + { + for (j = oldcnt; j < dq->count; j++) + { + p = dq->elements[j]; + for (i = 0; i < j; i++) + if (dq->elements[i] == p) + { + dq->elements[j] = 0; + break; + } + } + for (i = j = oldcnt; j < dq->count; j++) + if (dq->elements[j]) + dq->elements[i++] = dq->elements[j]; + queue_truncate(dq, i); + } +#if 0 + printf("RETURN:\n"); + for (i = oldcnt; i < dq->count; i++) + printf(" - %s\n", pool_solvid2str(pool, dq->elements[i])); +#endif +} + +static void +do_complex_recommendations(Solver *solv, Id rec, Map *m, int noselected) +{ + Pool *pool = solv->pool; + Queue dq; + Id p; + int i, blk; + +#if 0 + printf("DO_COMPLEX_RECOMMENDATIONS %s\n", pool_dep2str(pool, rec)); +#endif + queue_init(&dq); + i = pool_normalize_complex_dep(pool, rec, &dq, CPLXDEPS_EXPAND); + if (i == 0 || i == 1) + { + queue_free(&dq); + return; + } + for (i = 0; i < dq.count; i++) + { + blk = i; + for (; (p = dq.elements[i]) != 0; i++) + { + if (p < 0) + { + if (solv->decisionmap[-p] <= 0) + break; + continue; + } + if (solv->decisionmap[p] > 0) + { + if (noselected) + break; + MAPSET(m, p); + for (i++; (p = dq.elements[i]) != 0; i++) + if (p > 0 && solv->decisionmap[p] > 0) + MAPSET(m, p); + p = 1; + break; + } + } + if (!p) + { + for (i = blk; (p = dq.elements[i]) != 0; i++) + if (p > 0) + MAPSET(m, p); + } + while (dq.elements[i]) + i++; + } + queue_free(&dq); +} + +#endif + /*------------------------------------------------------------------- - * + * * solver_run_sat * * all rules have been set up, now actually run the solver @@ -1800,7 +2067,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) * if we encounter a problem, we rewind to a safe level and restart * with step 1 */ - + minimizationsteps = 0; for (;;) { @@ -1820,86 +2087,39 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) } /* - * resolve jobs first + * resolve jobs first (unless focus_installed is set) */ - if (level < systemlevel) + if (level < systemlevel && !solv->focus_installed) { - POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving job rules\n"); - for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++) + olevel = level; + level = resolve_jobrules(solv, level, disablerules, &dq); + if (level < olevel) { - Id l; - if (r->d < 0) /* ignore disabled rules */ - continue; - queue_empty(&dq); - FOR_RULELITERALS(l, pp, r) - { - if (l < 0) - { - if (solv->decisionmap[-l] <= 0) - break; - } - else - { - if (solv->decisionmap[l] > 0) - break; - if (solv->decisionmap[l] == 0) - queue_push(&dq, l); - } - } - if (l || !dq.count) - continue; - /* prune to installed if not updating */ - if (dq.count > 1 && solv->installed && !solv->updatemap_all && - !(solv->job.elements[solv->ruletojob.elements[i - solv->jobrules]] & SOLVER_ORUPDATE)) - { - int j, k; - for (j = k = 0; j < dq.count; j++) - { - Solvable *s = pool->solvables + dq.elements[j]; - if (s->repo == solv->installed) - { - dq.elements[k++] = dq.elements[j]; - if (solv->updatemap.size && MAPTST(&solv->updatemap, dq.elements[j] - solv->installed->start)) - { - k = 0; /* package wants to be updated, do not prune */ - break; - } - } - } - if (k) - dq.count = k; - } - olevel = level; - level = selectandinstall(solv, level, &dq, disablerules, i); if (level == 0) - break; - if (level <= olevel) - break; + break; /* unsolvable */ + continue; } - if (level == 0) - break; /* unsolvable */ systemlevel = level + 1; - if (i < solv->jobrules_end) - continue; - if (!solv->decisioncnt_update) - solv->decisioncnt_update = solv->decisionq.count; } + /* * installed packages */ + if (!solv->decisioncnt_update) + solv->decisioncnt_update = solv->decisionq.count; if (level < systemlevel && solv->installed && solv->installed->nsolvables && !solv->installed->disabled) { Repo *installed = solv->installed; int pass; POOL_DEBUG(SOLV_DEBUG_SOLVER, "resolving installed packages\n"); - /* we use two passes if we need to update packages + /* we use two passes if we need to update packages * to create a better user experience */ for (pass = solv->updatemap.size ? 0 : 1; pass < 2; pass++) { int passlevel = level; - Id *multiversionupdaters = solv->multiversion.size ? solv->multiversionupdaters : 0; + Id *specialupdaters = solv->specialupdaters; if (pass == 1 && !solv->decisioncnt_keep) solv->decisioncnt_keep = solv->decisionq.count; /* start with installedpos, the position that gave us problems the last time */ @@ -1914,7 +2134,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) if (s->repo != installed) continue; - if (solv->decisionmap[i] > 0 && (!multiversionupdaters || !multiversionupdaters[i - installed->start])) + if (solv->decisionmap[i] > 0 && (!specialupdaters || !specialupdaters[i - installed->start])) continue; /* already decided */ if (!pass && solv->updatemap.size && !MAPTST(&solv->updatemap, i - installed->start)) continue; /* updates first */ @@ -1924,16 +2144,31 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) rr -= solv->installed->end - solv->installed->start; if (!rr->p) /* identical to update rule? */ rr = r; - if (!rr->p) + if (!rr->p && !(specialupdaters && specialupdaters[i - installed->start])) continue; /* orpaned package */ /* check if we should update this package to the latest version * noupdate is set for erase jobs, in that case we want to deinstall - * the installed package and not replace it with a newer version */ + * the installed package and not replace it with a newer version + * rr->p != i is for dup jobs where the installed package cannot be kept */ queue_empty(&dq); - if (!MAPTST(&solv->noupdate, i - installed->start) && (solv->decisionmap[i] < 0 || solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, i - installed->start)) || rr->p != i)) + if (!MAPTST(&solv->noupdate, i - installed->start) && (solv->decisionmap[i] < 0 || solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, i - installed->start)) || (rr->p && rr->p != i))) { - if (multiversionupdaters && (d = multiversionupdaters[i - installed->start]) != 0) + if (!rr->p) + { + /* specialupdater with no update/feature rule */ + for (d = specialupdaters[i - installed->start]; (p = pool->whatprovidesdata[d++]) != 0; ) + { + if (solv->decisionmap[p] > 0) + { + dq.count = 0; + break; + } + if (!solv->decisionmap[p]) + queue_push(&dq, p); + } + } + else if (specialupdaters && (d = specialupdaters[i - installed->start]) != 0) { /* special multiversion handling, make sure best version is chosen */ if (rr->p == i && solv->decisionmap[i] >= 0) @@ -2045,6 +2280,19 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) if (!solv->decisioncnt_keep) solv->decisioncnt_keep = solv->decisionq.count; + if (level < systemlevel && solv->focus_installed) + { + olevel = level; + level = resolve_jobrules(solv, level, disablerules, &dq); + if (level < olevel) + { + if (level == 0) + break; /* unsolvable */ + continue; + } + systemlevel = level + 1; + } + if (level < systemlevel) systemlevel = level; @@ -2061,12 +2309,18 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) r = solv->rules + i; if (r->d < 0) /* ignore disabled rules */ continue; - queue_empty(&dq); + if (r->p < 0) /* most common cases first */ + { + if (r->d == 0 || solv->decisionmap[-r->p] <= 0) + continue; + } + if (dq.count) + queue_empty(&dq); if (r->d == 0) { /* binary or unary rule */ - /* need two positive undecided literals */ - if (r->p < 0 || r->w2 <= 0) + /* need two positive undecided literals, r->p already checked above */ + if (r->w2 <= 0) continue; if (solv->decisionmap[r->p] || solv->decisionmap[r->w2]) continue; @@ -2080,13 +2334,9 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) * no positive literal is installed * i.e. the rule is not fulfilled and we * just need to decide on the positive literals + * (decisionmap[-r->p] for the r->p < 0 case is already checked above) */ - if (r->p < 0) - { - if (solv->decisionmap[-r->p] <= 0) - continue; - } - else + if (r->p >= 0) { if (solv->decisionmap[r->p] > 0) continue; @@ -2152,9 +2402,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) continue; /* start over */ } - /* at this point we have a consistent system. now do the extras... */ - - /* first decide leftover cleandeps packages */ + /* decide leftover cleandeps packages */ if (solv->cleandepsmap.size && solv->installed) { for (p = solv->installed->start; p < solv->installed->end; p++) @@ -2175,6 +2423,8 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) continue; } + /* at this point we have a consistent system. now do the extras... */ + if (!solv->decisioncnt_weak) solv->decisioncnt_weak = solv->decisionq.count; if (doweak) @@ -2201,6 +2451,13 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) recp = s->repo->idarraydata + s->recommends; while ((rec = *recp++) != 0) { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, rec)) + { + add_complex_recommends(solv, rec, &dq, 0); + continue; + } +#endif qcount = dq.count; FOR_PROVIDES(p, pp, rec) { @@ -2404,6 +2661,11 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) while ((rec = *recp++) != 0) { queue_empty(&dq); +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, rec)) + add_complex_recommends(solv, rec, &dq, &dqmap); + else +#endif FOR_PROVIDES(p, pp, rec) { if (solv->decisionmap[p] > 0) @@ -2412,16 +2674,17 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) break; } else if (solv->decisionmap[p] == 0 && MAPTST(&dqmap, p)) - queue_pushunique(&dq, p); + queue_push(&dq, p); } if (!dq.count) continue; if (dq.count > 1) { /* multiple candidates, open a branch */ + queue_push(&solv->branches, -dq.elements[0]); for (i = 1; i < dq.count; i++) queue_push(&solv->branches, dq.elements[i]); - queue_push(&solv->branches, -level); + queue_push(&solv->branches, level); } p = dq.elements[0]; POOL_DEBUG(SOLV_DEBUG_POLICY, "installing recommended %s\n", pool_solvid2str(pool, p)); @@ -2487,9 +2750,53 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) break; continue; /* back to main loop */ } + if (solv->brokenorphanrules) + { + solver_check_brokenorphanrules(solv, &dq); + if (dq.count) + { + policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE); + for (i = 0; i < dq.count; i++) + { + p = dq.elements[i]; + POOL_DEBUG(SOLV_DEBUG_POLICY, "installing orphaned dep %s\n", pool_solvid2str(pool, p)); + olevel = level; + level = setpropagatelearn(solv, level, p, 0, 0); + if (level < olevel) + break; + } + if (level == 0) + break; + continue; + } + } } - if (solv->installed && solv->cleandepsmap.size) + /* one final pass to make sure we decided all installed packages */ + if (solv->installed) + { + for (p = solv->installed->start; p < solv->installed->end; p++) + { + if (solv->decisionmap[p]) + continue; /* already decided */ + s = pool->solvables + p; + if (s->repo != solv->installed) + continue; + POOL_DEBUG(SOLV_DEBUG_SOLVER, "removing unwanted %s\n", pool_solvid2str(pool, p)); + olevel = level; + level = setpropagatelearn(solv, level, -p, 0, 0); + if (level < olevel) + break; + } + if (p < solv->installed->end) + { + if (level == 0) + break; + continue; /* back to main loop */ + } + } + + if (solv->installed && solv->cleandepsmap.size) { if (cleandeps_check_mistakes(solv, level)) { @@ -2499,69 +2806,130 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) } } - if (solv->solution_callback) + if (solv->solution_callback) { solv->solution_callback(solv, solv->solution_callback_data); if (solv->branches.count) { - int i = solv->branches.count - 1; - int l = -solv->branches.elements[i]; + int l, endi = 0; Id why; - - for (; i > 0; i--) - if (solv->branches.elements[i - 1] < 0) - break; - p = solv->branches.elements[i]; - POOL_DEBUG(SOLV_DEBUG_SOLVER, "branching with %s\n", pool_solvid2str(pool, p)); - queue_empty(&dq); - for (j = i + 1; j < solv->branches.count; j++) - queue_push(&dq, solv->branches.elements[j]); - solv->branches.count = i; - level = l; - revert(solv, level); - if (dq.count > 1) - for (j = 0; j < dq.count; j++) - queue_push(&solv->branches, dq.elements[j]); - olevel = level; - why = -solv->decisionq_why.elements[solv->decisionq_why.count]; - assert(why >= 0); - level = setpropagatelearn(solv, level, p, disablerules, why); - if (level == 0) - break; - continue; + p = l = 0; + for (i = solv->branches.count - 1; i >= 0; i--) + { + p = solv->branches.elements[i]; + if (p > 0 && !l) + { + endi = i + 1; + l = p; + } + else if (p > 0) + break; + else if (p < 0) + l = 0; + } + if (i >= 0) + { + while (i > 0 && solv->branches.elements[i - 1] > 0) + i--; + p = solv->branches.elements[i]; + solv->branches.elements[i] = -p; + while (i > 0 && solv->branches.elements[i - 1] < 0) + i--; + POOL_DEBUG(SOLV_DEBUG_SOLVER, "branching with %s\n", pool_solvid2str(pool, p)); + queue_empty(&dq); + queue_insertn(&dq, 0, endi - i, solv->branches.elements + i); + level = l; + revert(solv, level); + queue_insertn(&solv->branches, solv->branches.count, dq.count, dq.elements); + /* hack: revert simply sets the count, so we can still access the reverted elements */ + why = -solv->decisionq_why.elements[solv->decisionq_why.count]; + assert(why >= 0); + olevel = level; + level = setpropagatelearn(solv, level, p, disablerules, why); + if (level == 0) + break; + continue; + } } /* all branches done, we're finally finished */ break; } /* auto-minimization step */ - if (solv->branches.count) + if (solv->branches.count) { - int l = 0, lasti = -1, lastl = -1; + int l = 0, lasti = -1, lastsi = -1, endi = 0; Id why; - - p = 0; + p = l = 0; + if (solv->recommends_index < solv->decisionq.count) + policy_update_recommendsmap(solv); for (i = solv->branches.count - 1; i >= 0; i--) { p = solv->branches.elements[i]; - if (p < 0) - l = -p; - else if (p > 0 && solv->decisionmap[p] > l + 1) + if (p > 0 && !l) { - lasti = i; - lastl = l; + l = p; + endi = i + 1; + lastsi = -1; + } + else if (p > 0) + { + if (solv->decisionmap[p] > l + 1) + lasti = i; + else + { + if (MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)) + { + lastsi = p; + } + } + } + else if (p < 0) + { + l = 0; + if (lastsi >= 0) + { + p = -p; + if (solv->decisionmap[p] == l) + { + if (!(MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p))) + lasti = lastsi; + } + } } } if (lasti >= 0) { - /* kill old solvable so that we do not loop */ + int starti; + /* find start of branch */ + for (i = lasti; i && solv->branches.elements[i] >= 0; ) + i--; + while (i > 0 && solv->branches.elements[i] < 0) + i--; + starti = i ? i + 1 : 0; +#if 0 + printf("minimization group level %d [%d-%d]:\n", solv->branches.elements[endi - 1], starti, endi); + for (i = starti; i < endi - 1; i++) + printf("%c %c%s\n", i == lasti ? 'x' : ' ', solv->branches.elements[i] >= 0 ? ' ' : '-', pool_solvid2str(pool, solv->branches.elements[i] >= 0 ? solv->branches.elements[i] : -solv->branches.elements[i])); +#endif + l = solv->branches.elements[endi - 1]; p = solv->branches.elements[lasti]; - solv->branches.elements[lasti] = 0; - POOL_DEBUG(SOLV_DEBUG_SOLVER, "minimizing %d -> %d with %s\n", solv->decisionmap[p], lastl, pool_solvid2str(pool, p)); + solv->branches.elements[lasti] = -p; + POOL_DEBUG(SOLV_DEBUG_SOLVER, "minimizing %d -> %d with %s\n", solv->decisionmap[p], l, pool_solvid2str(pool, p)); minimizationsteps++; - - level = lastl; + queue_empty(&dq); + for (i = starti; i < endi - 1; i++) + if (solv->branches.elements[i] < 0) + queue_push(&dq, solv->branches.elements[i]); + for (i = starti; i < endi; i++) + if (solv->branches.elements[i] > 0) + queue_push(&dq, solv->branches.elements[i]); + if (dq.elements[dq.count - 2] <= 0) + queue_empty(&dq); + level = l; revert(solv, level); + queue_insertn(&solv->branches, solv->branches.count, dq.count, dq.elements); + /* hack: revert simply sets the count, so we can still access the reverted elements */ why = -solv->decisionq_why.elements[solv->decisionq_why.count]; assert(why >= 0); olevel = level; @@ -2583,6 +2951,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) if (level == 0) { /* unsolvable */ + solv->decisioncnt_jobs = solv->decisionq.count; solv->decisioncnt_update = solv->decisionq.count; solv->decisioncnt_keep = solv->decisionq.count; solv->decisioncnt_resolve = solv->decisionq.count; @@ -2596,7 +2965,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak) /*------------------------------------------------------------------- - * + * * remove disabled conflicts * * purpose: update the decisionmap after some rules were disabled. @@ -2724,7 +3093,7 @@ weaken_solvable_deps(Solver *solv, Id p) int i; Rule *r; - for (i = 1, r = solv->rules + i; i < solv->rpmrules_end; i++, r++) + for (i = 1, r = solv->rules + i; i < solv->pkgrules_end; i++, r++) { if (r->p != -p) continue; @@ -2841,7 +3210,7 @@ add_update_target(Solver *solv, Id p, Id how) Id obs, *obsp = s->repo->idarraydata + s->obsoletes; while ((obs = *obsp++) != 0) { - FOR_PROVIDES(pi, pip, obs) + FOR_PROVIDES(pi, pip, obs) { Solvable *si = pool->solvables + pi; if (si->repo != installed) @@ -2926,7 +3295,7 @@ addedmap2deduceq(Solver *solv, Map *addedmap) Rule *r; queue_empty(&solv->addedmap_deduceq); - for (i = 2, j = solv->rpmrules_end - 1; i < pool->nsolvables && j > 0; j--) + for (i = 2, j = solv->pkgrules_end - 1; i < pool->nsolvables && j > 0; j--) { r = solv->rules + j; if (r->p >= 0) @@ -2956,13 +3325,13 @@ addedmap2deduceq(Solver *solv, Map *addedmap) j++; } -static void +static void deduceq2addedmap(Solver *solv, Map *addedmap) { int j; Id p; Rule *r; - for (j = solv->rpmrules_end - 1; j > 0; j--) + for (j = solv->pkgrules_end - 1; j > 0; j--) { r = solv->rules + j; if (r->d < 0 && r->p) @@ -2998,7 +3367,7 @@ solver_solve(Solver *solv, Queue *job) Repo *installed = solv->installed; int i; int oldnrules, initialnrules; - Map addedmap; /* '1' == have rpm-rules for solvable */ + Map addedmap; /* '1' == have pkg-rules for solvable */ Map installcandidatemap; Id how, what, select, name, weak, p, pp, d; Queue q; @@ -3033,68 +3402,29 @@ solver_solve(Solver *solv, Queue *job) queue_insertn(&solv->job, 0, pool->pooljobs.count, pool->pooljobs.elements); job = &solv->job; - /* free old stuff */ - if (solv->update_targets) - { - queue_free(solv->update_targets); - solv->update_targets = solv_free(solv->update_targets); - } - if (solv->cleandeps_updatepkgs) - { - queue_free(solv->cleandeps_updatepkgs); - solv->cleandeps_updatepkgs = solv_free(solv->cleandeps_updatepkgs); - } + /* free old stuff in jase we re-run a solver */ + queuep_free(&solv->update_targets); + queuep_free(&solv->cleandeps_updatepkgs); queue_empty(&solv->ruleassertions); solv->bestrules_pkg = solv_free(solv->bestrules_pkg); + solv->yumobsrules_info = solv_free(solv->yumobsrules_info); solv->choicerules_ref = solv_free(solv->choicerules_ref); if (solv->noupdate.size) map_empty(&solv->noupdate); - if (solv->multiversion.size) - { - map_free(&solv->multiversion); - map_init(&solv->multiversion, 0); - } + map_zerosize(&solv->multiversion); solv->updatemap_all = 0; - if (solv->updatemap.size) - { - map_free(&solv->updatemap); - map_init(&solv->updatemap, 0); - } + map_zerosize(&solv->updatemap); solv->bestupdatemap_all = 0; - if (solv->bestupdatemap.size) - { - map_free(&solv->bestupdatemap); - map_init(&solv->bestupdatemap, 0); - } + map_zerosize(&solv->bestupdatemap); solv->fixmap_all = 0; - if (solv->fixmap.size) - { - map_free(&solv->fixmap); - map_init(&solv->fixmap, 0); - } + map_zerosize(&solv->fixmap); solv->dupmap_all = 0; - if (solv->dupmap.size) - { - map_free(&solv->dupmap); - map_init(&solv->dupmap, 0); - } - if (solv->dupinvolvedmap.size) - { - map_free(&solv->dupinvolvedmap); - map_init(&solv->dupinvolvedmap, 0); - } + map_zerosize(&solv->dupmap); + map_zerosize(&solv->dupinvolvedmap); solv->droporphanedmap_all = 0; - if (solv->droporphanedmap.size) - { - map_free(&solv->droporphanedmap); - map_init(&solv->droporphanedmap, 0); - } - if (solv->cleandepsmap.size) - { - map_free(&solv->cleandepsmap); - map_init(&solv->cleandepsmap, 0); - } - + map_zerosize(&solv->droporphanedmap); + map_zerosize(&solv->cleandepsmap); + map_zerosize(&solv->weakrulemap); queue_empty(&solv->weakruleq); solv->watches = solv_free(solv->watches); queue_empty(&solv->ruletojob); @@ -3102,7 +3432,7 @@ solver_solve(Solver *solv, Queue *job) memset(solv->decisionmap, 0, pool->nsolvables * sizeof(Id)); queue_empty(&solv->decisionq); queue_empty(&solv->decisionq_why); - solv->decisioncnt_update = solv->decisioncnt_keep = solv->decisioncnt_resolve = solv->decisioncnt_weak = solv->decisioncnt_orphan = 0; + solv->decisioncnt_jobs = solv->decisioncnt_update = solv->decisioncnt_keep = solv->decisioncnt_resolve = solv->decisioncnt_weak = solv->decisioncnt_orphan = 0; queue_empty(&solv->learnt_why); queue_empty(&solv->learnt_pool); queue_empty(&solv->branches); @@ -3115,10 +3445,13 @@ solver_solve(Solver *solv, Queue *job) { map_empty(&solv->recommendsmap); map_empty(&solv->suggestsmap); + queuep_free(&solv->recommendscplxq); + queuep_free(&solv->suggestscplxq); solv->recommends_index = 0; } - solv->multiversionupdaters = solv_free(solv->multiversionupdaters); - + queuep_free(&solv->brokenorphanrules); + solv->specialupdaters = solv_free(solv->specialupdaters); + /* * create basic rule set of all involved packages @@ -3137,20 +3470,21 @@ solver_solve(Solver *solv, Queue *job) now = solv_timems(0); /* * create rules for all package that could be involved with the solving - * so called: rpm rules + * so called: pkg rules * */ - initialnrules = solv->rpmrules_end ? solv->rpmrules_end : 1; + initialnrules = solv->pkgrules_end ? solv->pkgrules_end : 1; if (initialnrules > 1) deduceq2addedmap(solv, &addedmap); if (solv->nrules != initialnrules) solver_shrinkrules(solv, initialnrules); solv->nrules = initialnrules; - solv->rpmrules_end = 0; - + solv->pkgrules_end = 0; + if (installed) { /* check for update/verify jobs as they need to be known early */ + /* also setup the droporphaned map, we need it when creating update rules */ for (i = 0; i < job->count; i += 2) { how = job->elements[i]; @@ -3236,6 +3570,19 @@ solver_solve(Solver *solv, Queue *job) add_update_target(solv, p, how); } break; + case SOLVER_DROP_ORPHANED: + if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid)) + solv->droporphanedmap_all = 1; + FOR_JOB_SELECT(p, pp, select, what) + { + s = pool->solvables + p; + if (s->repo != installed) + continue; + if (!solv->droporphanedmap.size) + map_grow(&solv->droporphanedmap, installed->end - installed->start); + MAPSET(&solv->droporphanedmap, p - installed->start); + } + break; default: break; } @@ -3246,19 +3593,19 @@ solver_solve(Solver *solv, Queue *job) oldnrules = solv->nrules; FOR_REPO_SOLVABLES(installed, p, s) - solver_addrpmrulesforsolvable(solv, s, &addedmap); - POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules for installed solvables\n", solv->nrules - oldnrules); + solver_addpkgrulesforsolvable(solv, s, &addedmap); + POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for installed solvables\n", solv->nrules - oldnrules); oldnrules = solv->nrules; FOR_REPO_SOLVABLES(installed, p, s) - solver_addrpmrulesforupdaters(solv, s, &addedmap, 1); - POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules for updaters of installed solvables\n", solv->nrules - oldnrules); + solver_addpkgrulesforupdaters(solv, s, &addedmap, 1); + POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for updaters of installed solvables\n", solv->nrules - oldnrules); } /* * create rules for all packages involved in the job * (to be installed or removed) */ - + oldnrules = solv->nrules; for (i = 0; i < job->count; i += 2) { @@ -3272,7 +3619,7 @@ solver_solve(Solver *solv, Queue *job) FOR_JOB_SELECT(p, pp, select, what) { MAPSET(&installcandidatemap, p); - solver_addrpmrulesforsolvable(solv, pool->solvables + p, &addedmap); + solver_addpkgrulesforsolvable(solv, pool->solvables + p, &addedmap); } break; case SOLVER_DISTUPGRADE: @@ -3290,18 +3637,24 @@ solver_solve(Solver *solv, Queue *job) break; } } - POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules for packages involved in a job\n", solv->nrules - oldnrules); + POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules for packages involved in a job\n", solv->nrules - oldnrules); + - /* * add rules for suggests, enhances */ oldnrules = solv->nrules; - solver_addrpmrulesforweak(solv, &addedmap); - POOL_DEBUG(SOLV_DEBUG_STATS, "added %d rpm rules because of weak dependencies\n", solv->nrules - oldnrules); + solver_addpkgrulesforweak(solv, &addedmap); + POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules because of weak dependencies\n", solv->nrules - oldnrules); + +#ifdef ENABLE_LINKED_PKGS + oldnrules = solv->nrules; + solver_addpkgrulesforlinked(solv, &addedmap); + POOL_DEBUG(SOLV_DEBUG_STATS, "added %d pkg rules because of linked packages\n", solv->nrules - oldnrules); +#endif /* - * first pass done, we now have all the rpm rules we need. + * first pass done, we now have all the pkg rules we need. * unify existing rules before going over all job rules and * policy rules. * at this point the system is always solvable, @@ -3322,14 +3675,14 @@ solver_solve(Solver *solv, Queue *job) } if (solv->nrules > initialnrules) - solver_unifyrules(solv); /* remove duplicate rpm rules */ - solv->rpmrules_end = solv->nrules; /* mark end of rpm rules */ + solver_unifyrules(solv); /* remove duplicate pkg rules */ + solv->pkgrules_end = solv->nrules; /* mark end of pkg rules */ if (solv->nrules > initialnrules) addedmap2deduceq(solv, &addedmap); /* so that we can recreate the addedmap */ - POOL_DEBUG(SOLV_DEBUG_STATS, "rpm rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024); - POOL_DEBUG(SOLV_DEBUG_STATS, "rpm rule creation took %d ms\n", solv_timems(now)); + POOL_DEBUG(SOLV_DEBUG_STATS, "pkg rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024); + POOL_DEBUG(SOLV_DEBUG_STATS, "pkg rule creation took %d ms\n", solv_timems(now)); /* create dup maps if needed. We need the maps early to create our * update rules */ @@ -3338,16 +3691,16 @@ solver_solve(Solver *solv, Queue *job) /* * create feature rules - * + * * foreach installed: * create assertion (keep installed, if no update available) * or * create update rule (A|update1(A)|update2(A)|...) - * + * * those are used later on to keep a version of the installed packages in * best effort mode */ - + solv->featurerules = solv->nrules; /* mark start of feature rules */ if (installed) { @@ -3368,11 +3721,11 @@ solver_solve(Solver *solv, Queue *job) /* * Add update rules for installed solvables - * + * * almost identical to feature rules * except that downgrades/archchanges/vendorchanges are not allowed */ - + solv->updaterules = solv->nrules; if (installed) @@ -3392,20 +3745,16 @@ solver_solve(Solver *solv, Queue *job) * check for and remove duplicate */ r = solv->rules + solv->nrules - 1; /* r: update rule */ + if (!r->p) + continue; sr = r - (installed->end - installed->start); /* sr: feature rule */ - /* it's orphaned if there is no feature rule or the feature rule - * consists just of the installed package */ - if (!sr->p || (sr->p == i && !sr->d && !sr->w2)) + /* it's also orphaned if the feature rule consists just of the installed package */ + if (!solv->dupmap_all && sr->p == i && !sr->d && !sr->w2) queue_push(&solv->orphaned, i); - if (!r->p) - { - assert(solv->dupmap_all && !sr->p); - continue; - } if (!solver_rulecmp(solv, r, sr)) memset(sr, 0, sizeof(*sr)); /* delete unneeded feature rule */ else - solver_disablerule(solv, sr); /* disable feature rule */ + solver_disablerule(solv, sr); /* disable feature rule for now */ } /* consistency check: we added a rule for _every_ installed solvable */ assert(solv->nrules - solv->updaterules == installed->end - installed->start); @@ -3567,17 +3916,6 @@ solver_solve(Solver *solv, Queue *job) break; case SOLVER_DROP_ORPHANED: POOL_DEBUG(SOLV_DEBUG_JOB, "job: drop orphaned %s\n", solver_select2str(pool, select, what)); - if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && installed && what == installed->repoid)) - solv->droporphanedmap_all = 1; - FOR_JOB_SELECT(p, pp, select, what) - { - s = pool->solvables + p; - if (!installed || s->repo != installed) - continue; - if (!solv->droporphanedmap.size) - map_grow(&solv->droporphanedmap, installed->end - installed->start); - MAPSET(&solv->droporphanedmap, p - installed->start); - } break; case SOLVER_USERINSTALLED: POOL_DEBUG(SOLV_DEBUG_JOB, "job: user installed %s\n", solver_select2str(pool, select, what)); @@ -3587,10 +3925,6 @@ solver_solve(Solver *solv, Queue *job) break; } - /* - * debug - */ - IF_POOLDEBUG (SOLV_DEBUG_JOB) { int j; @@ -3610,6 +3944,7 @@ solver_solve(Solver *solv, Queue *job) if (!solv->noinfarchcheck) { solver_addinfarchrules(solv, &addedmap); +#if 0 if (pool->implicitobsoleteusescolors) { /* currently doesn't work well with infarch rules, so make @@ -3617,6 +3952,7 @@ solver_solve(Solver *solv, Queue *job) for (i = solv->infarchrules; i < solv->infarchrules_end; i++) queue_push(&solv->weakruleq, i); } +#endif } else solv->infarchrules = solv->infarchrules_end = solv->nrules; @@ -3634,6 +3970,11 @@ solver_solve(Solver *solv, Queue *job) if (hasdupjob) solver_freedupmaps(solv); /* no longer needed */ + if (solv->do_yum_obsoletes) + solver_addyumobsrules(solv); + else + solv->yumobsrules = solv->yumobsrules_end = solv->nrules; + if (1) solver_addchoicerules(solv); else @@ -3648,13 +3989,13 @@ solver_solve(Solver *solv, Queue *job) * -------------------------------------------------------------- * prepare for solving */ - + /* free unneeded memory */ map_free(&addedmap); map_free(&installcandidatemap); queue_free(&q); - POOL_DEBUG(SOLV_DEBUG_STATS, "%d rpm rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules\n", solv->rpmrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules); + POOL_DEBUG(SOLV_DEBUG_STATS, "%d pkg rules, 2 * %d update rules, %d job rules, %d infarch rules, %d dup rules, %d choice rules, %d best rules\n", solv->pkgrules_end - 1, solv->updaterules_end - solv->updaterules, solv->jobrules_end - solv->jobrules, solv->infarchrules_end - solv->infarchrules, solv->duprules_end - solv->duprules, solv->choicerules_end - solv->choicerules, solv->bestrules_end - solv->bestrules); POOL_DEBUG(SOLV_DEBUG_STATS, "overall rule memory used: %d K\n", solv->nrules * (int)sizeof(Rule) / 1024); /* create weak map */ @@ -3670,10 +4011,10 @@ solver_solve(Solver *solv, Queue *job) map_grow(&solv->cleandepsmap, installed->end - installed->start); /* no mistakes */ if (solv->cleandeps_mistakes) - { + { queue_free(solv->cleandeps_mistakes); solv->cleandeps_mistakes = solv_free(solv->cleandeps_mistakes); - } + } /* all new rules are learnt after this point */ solv->learntrules = solv->nrules; @@ -3690,6 +4031,10 @@ solver_solve(Solver *solv, Queue *job) /* disable update rules that conflict with our job */ solver_disablepolicyrules(solv); + /* break orphans if requested */ + if (solv->dupmap_all && solv->orphaned.count && solv->break_orphans) + solver_breakorphans(solv); + /* make initial decisions based on assertion rules */ makeruledecisions(solv); POOL_DEBUG(SOLV_DEBUG_SOLVER, "problems so far: %d\n", solv->problems.count); @@ -3699,7 +4044,7 @@ solver_solve(Solver *solv, Queue *job) * solve! * ******************************************** */ - + now = solv_timems(0); solver_run_sat(solv, 1, solv->dontinstallrecommended ? 0 : 1); POOL_DEBUG(SOLV_DEBUG_STATS, "solver took %d ms\n", solv_timems(now)); @@ -3775,7 +4120,7 @@ void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *su solver_disablerule(solv, r); goterase++; } - + if (goterase) { enabledisablelearntrules(solv); @@ -3821,6 +4166,13 @@ void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *su recp = s->repo->idarraydata + s->recommends; while ((rec = *recp++) != 0) { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, rec)) + { + do_complex_recommendations(solv, rec, &solv->recommendsmap, noselected); + continue; + } +#endif FOR_PROVIDES(p, pp, rec) if (solv->decisionmap[p] > 0) break; @@ -3866,7 +4218,7 @@ void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *su /* * find suggested packages */ - + if (suggestionsq) { Id sug, *sugp, p, pp; @@ -3886,6 +4238,13 @@ void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *su sugp = s->repo->idarraydata + s->suggests; while ((sug = *sugp++) != 0) { +#ifdef ENABLE_COMPLEX_DEPS + if (pool_is_complex_dep(pool, sug)) + { + do_complex_recommendations(solv, sug, &solv->suggestsmap, noselected); + continue; + } +#endif FOR_PROVIDES(p, pp, sug) if (solv->decisionmap[p] > 0) break; @@ -3931,7 +4290,7 @@ void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *su if (redoq.count) undo_removedisabledconflicts(solv, &redoq); queue_free(&redoq); - + /* undo job rule disabling */ for (i = 0; i < disabledq.count; i++) solver_enablerule(solv, solv->rules + disabledq.elements[i]); @@ -3944,7 +4303,7 @@ void solver_get_recommendations(Solver *solv, Queue *recommendationsq, Queue *su /* disk usage computations */ /*------------------------------------------------------------------- - * + * * calculate DU changes */ @@ -3960,7 +4319,7 @@ solver_calc_duchanges(Solver *solv, DUChanges *mps, int nmps) /*------------------------------------------------------------------- - * + * * calculate changes in install size */ @@ -4001,7 +4360,7 @@ solver_trivial_installable(Solver *solv, Queue *pkgs, Queue *res) } /*------------------------------------------------------------------- - * + * * decision introspection */ @@ -4064,7 +4423,7 @@ solver_describe_decision(Solver *solv, Id p, Id *infop) { int i; Id pp, why; - + if (infop) *infop = 0; if (!solv->decisionmap[p]) @@ -4081,12 +4440,10 @@ solver_describe_decision(Solver *solv, Id p, Id *infop) if (why > 0) return SOLVER_REASON_UNIT_RULE; why = -why; + if (i == 0) + return SOLVER_REASON_KEEP_INSTALLED; /* the systemsolvable */ if (i < solv->decisioncnt_update) - { - if (i == 0) - return SOLVER_REASON_KEEP_INSTALLED; - return SOLVER_REASON_RESOLVE_JOB; - } + return SOLVER_REASON_RESOLVE_JOB; if (i < solv->decisioncnt_keep) { if (why == 0 && pp < 0) @@ -4095,6 +4452,8 @@ solver_describe_decision(Solver *solv, Id p, Id *infop) } if (i < solv->decisioncnt_resolve) { + if (solv->focus_installed && i >= solv->decisioncnt_jobs) + return SOLVER_REASON_RESOLVE_JOB; if (why == 0 && pp < 0) return SOLVER_REASON_CLEANDEPS_ERASE; return SOLVER_REASON_KEEP_INSTALLED; @@ -4102,7 +4461,7 @@ solver_describe_decision(Solver *solv, Id p, Id *infop) if (why > 0) return SOLVER_REASON_RESOLVE; /* weak or orphaned */ - if (solv->decisionq.count < solv->decisioncnt_orphan) + if (i < solv->decisioncnt_orphan) return SOLVER_REASON_WEAKDEP; return SOLVER_REASON_RESOLVE_ORPHAN; } @@ -4157,7 +4516,7 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) if (!p2 && found) { queue_push(whyq, SOLVER_REASON_RECOMMENDED); - queue_push2(whyq, p2, rec); + queue_push2(whyq, i, rec); } } } @@ -4266,7 +4625,7 @@ pool_isemptyupdatejob(Pool *pool, Id how, Id what) Id obs, *obsp = s->repo->idarraydata + s->obsoletes; while ((obs = *obsp++) != 0) { - FOR_PROVIDES(pi, pip, obs) + FOR_PROVIDES(pi, pip, obs) { Solvable *si = pool->solvables + pi; if (si->repo != pool->installed) @@ -4283,6 +4642,250 @@ pool_isemptyupdatejob(Pool *pool, Id how, Id what) return 1; } +static int +get_userinstalled_cmp(const void *ap, const void *bp, void *dp) +{ + return *(Id *)ap - *(Id *)bp; +} + +static int +get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp) +{ + Pool *pool = dp; + return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp)); +} + +static void +get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags) +{ + Id lastp = -1; + int i, j; + if ((flags & GET_USERINSTALLED_NAMES) != 0) + solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool); + else + solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0); + for (i = j = 0; i < q->count; i++) + if (q->elements[i] != lastp) + q->elements[j++] = lastp = q->elements[i]; + queue_truncate(q, j); +} + +void +solver_get_userinstalled(Solver *solv, Queue *q, int flags) +{ + Pool *pool = solv->pool; + Id p, p2, pp; + Solvable *s; + Repo *installed = solv->installed; + int i, j; + Map userinstalled; + + map_init(&userinstalled, 0); + queue_empty(q); + /* first process jobs */ + for (i = 0; i < solv->job.count; i += 2) + { + Id how = solv->job.elements[i]; + Id what, select; + if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED) + { + if (!userinstalled.size) + map_grow(&userinstalled, installed->end - installed->start); + what = solv->job.elements[i + 1]; + select = how & SOLVER_SELECTMASK; + if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid)) + FOR_REPO_SOLVABLES(installed, p, s) + MAPSET(&userinstalled, p - installed->start); + FOR_JOB_SELECT(p, pp, select, what) + if (pool->solvables[p].repo == installed) + MAPSET(&userinstalled, p - installed->start); + continue; + } + if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL) + continue; + if ((how & SOLVER_NOTBYUSER) != 0) + continue; + what = solv->job.elements[i + 1]; + select = how & SOLVER_SELECTMASK; + FOR_JOB_SELECT(p, pp, select, what) + if (solv->decisionmap[p] > 0) + { + queue_push(q, p); +#ifdef ENABLE_LINKED_PKGS + if (has_package_link(pool, pool->solvables + p)) + { + int j; + Queue lq; + queue_init(&lq); + find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0); + for (j = 0; j < lq.count; j++) + if (solv->decisionmap[lq.elements[j]] > 0) + queue_push(q, lq.elements[j]); + } +#endif + } + } + /* now process updates of userinstalled packages */ + if (installed && userinstalled.size) + { + for (i = 1; i < solv->decisionq.count; i++) + { + p = solv->decisionq.elements[i]; + if (p <= 0) + continue; + s = pool->solvables + p; + if (!s->repo) + continue; + if (s->repo == installed) + { + if (MAPTST(&userinstalled, p - installed->start)) + queue_push(q, p); + continue; + } + /* new package, check if we replace a userinstalled one */ + FOR_PROVIDES(p2, pp, s->name) + { + Solvable *ps = pool->solvables + p2; + if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start)) + continue; + if (!pool->implicitobsoleteusesprovides && s->name != ps->name) + continue; + if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + queue_push(q, p); + break; + } + if (!p2 && s->repo != installed && s->obsoletes) + { + Id obs, *obsp = s->repo->idarraydata + s->obsoletes; + while ((obs = *obsp++) != 0) + { + FOR_PROVIDES(p2, pp, obs) + { + Solvable *ps = pool->solvables + p2; + if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start)) + continue; + if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs)) + continue; + if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) + continue; + queue_push(q, p); + break; + } + if (p2) + break; + } + } + } + } + map_free(&userinstalled); + /* convert to names if asked */ + if ((flags & GET_USERINSTALLED_NAMES) != 0) + { + for (i = 0; i < q->count; i++) + { + s = pool->solvables + q->elements[i]; + q->elements[i] = s->name; + } + } + /* sort and unify */ + if (q->count > 1) + get_userinstalled_sort_uniq(pool, q, flags); + /* invert if asked */ + if ((flags & GET_USERINSTALLED_INVERTED) != 0) + { + /* first generate queue with all installed packages */ + Queue invq; + queue_init(&invq); + for (i = 1; i < solv->decisionq.count; i++) + { + p = solv->decisionq.elements[i]; + if (p <= 0) + continue; + s = pool->solvables + p; + if (!s->repo) + continue; + if ((flags & GET_USERINSTALLED_NAMES) != 0) + queue_push(&invq, s->name); + else + queue_push(&invq, p); + } + /* push q on invq, just in case... */ + queue_insertn(&invq, invq.count, q->count, q->elements); + if (invq.count > 1) + get_userinstalled_sort_uniq(pool, &invq, flags); + /* subtract queues (easy as they are sorted and invq is a superset of q) */ + if (q->count) + { + for (i = j = 0; i < invq.count; i++) + if (invq.elements[i] == q->elements[j]) + { + invq.elements[i] = 0; + if (++j >= q->count) + break; + } + queue_empty(q); + } + for (i = j = 0; i < invq.count; i++) + if (invq.elements[i]) + queue_push(q, invq.elements[i]); + queue_free(&invq); + } +} + +void +pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags) +{ + int i; + + if (flags & GET_USERINSTALLED_INVERTED) + { + Queue invq; + Id p, lastid; + Solvable *s; + int bad; + if (!pool->installed) + return; + queue_init(&invq); + FOR_REPO_SOLVABLES(pool->installed, p, s) + queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p); + queue_insertn(&invq, invq.count, q->count, q->elements); + if (invq.count > 1) + get_userinstalled_sort_uniq(pool, &invq, flags); + /* now the fun part, add q again, sort, and remove all dups */ + queue_insertn(&invq, invq.count, q->count, q->elements); + if (invq.count > 1) + { + if ((flags & GET_USERINSTALLED_NAMES) != 0) + solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool); + else + solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0); + } + lastid = -1; + bad = 1; + for (i = 0; i < invq.count; i++) + { + if (invq.elements[i] == lastid) + { + bad = 1; + continue; + } + if (!bad) + queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid); + bad = 0; + lastid = invq.elements[i]; + } + if (!bad) + queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid); + queue_free(&invq); + } + else + { + for (i = 0; i < q->count; i++) + queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]); + } +} + const char * solver_select2str(Pool *pool, Id select, Id what) { @@ -4340,7 +4943,7 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask) return "do nothing"; case SOLVER_INSTALL: if (select == SOLVER_SOLVABLE && pool->installed && pool->solvables[what].repo == pool->installed) - strstart = "keep ", strend = "installed"; + strstart = "keep ", strend = " installed"; else if (select == SOLVER_SOLVABLE || select == SOLVER_SOLVABLE_NAME) strstart = "install "; else if (select == SOLVER_SOLVABLE_PROVIDES) @@ -4350,7 +4953,7 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask) break; case SOLVER_ERASE: if (select == SOLVER_SOLVABLE && !(pool->installed && pool->solvables[what].repo == pool->installed)) - strstart = "keep ", strend = "unstalled"; + strstart = "keep ", strend = " uninstalled"; else if (select == SOLVER_SOLVABLE_PROVIDES) strstart = "deinstall all packages "; else @@ -4366,7 +4969,7 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask) strstart = "multi version "; break; case SOLVER_LOCK: - strstart = "update "; + strstart = "lock "; break; case SOLVER_DISTUPGRADE: strstart = "dist upgrade "; @@ -4375,10 +4978,10 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask) strstart = "verify "; break; case SOLVER_DROP_ORPHANED: - strstart = "deinstall ", strend = "if orphaned"; + strstart = "deinstall ", strend = " if orphaned"; break; case SOLVER_USERINSTALLED: - strstart = "regard ", strend = "as userinstalled"; + strstart = "regard ", strend = " as userinstalled"; break; default: strstart = "unknown job ";