#define DISABLE_INFARCH 2
#define DISABLE_DUP 3
+/*
+ * add all installed packages that package p obsoletes to Queue q.
+ * Package p is not installed and not in oobs map.
+ * Entries may get added multiple times.
+ */
+static void
+add_obsoletes(Solver *solv, Id p, Queue *q)
+{
+ Pool *pool = solv->pool;
+ Repo *installed = solv->installed;
+ Id p2, pp2;
+ Solvable *s = pool->solvables + p;
+ Id obs, *obsp;
+ Id lastp2 = 0;
+
+ /* we already know: p is not installed, p is not noobs */
+ FOR_PROVIDES(p2, pp2, s->name)
+ {
+ Solvable *ps = pool->solvables + p2;
+ if (ps->repo != installed)
+ continue;
+ if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ queue_push(q, p2);
+ lastp2 = p2;
+ }
+ if (!s->obsoletes)
+ return;
+ obsp = s->repo->idarraydata + s->obsoletes;
+ while ((obs = *obsp++) != 0)
+ FOR_PROVIDES(p2, pp2, obs)
+ {
+ Solvable *ps = pool->solvables + p2;
+ if (ps->repo != installed)
+ continue;
+ if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+ continue;
+ if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
+ continue;
+ if (p2 == lastp2)
+ continue;
+ queue_push(q, p2);
+ lastp2 = p2;
+ }
+}
+
+/*
+ * Call add_obsoletes and intersect the result with the
+ * elements in Queue q starting at qstart.
+ * Assumes that it's the first call if qstart == q->count.
+ * May use auxillary map m for the intersection process, all
+ * elements of q starting at qstart must have their bit cleared.
+ * (This is also true after the function returns.)
+ */
+static void
+intersect_obsoletes(Solver *solv, Id p, Queue *q, int qstart, Map *m)
+{
+ int i, j;
+ int qcount = q->count;
+
+ add_obsoletes(solv, p, q);
+ if (qcount == qstart)
+ return; /* first call */
+ if (qcount == q->count)
+ j = qstart;
+ else if (qcount == qstart + 1)
+ {
+ /* easy if there's just one element */
+ j = qstart;
+ for (i = qcount; i < q->count; i++)
+ if (q->elements[i] == q->elements[qstart])
+ {
+ j++; /* keep the element */
+ break;
+ }
+ }
+ else if (!m->size && q->count - qstart <= 8)
+ {
+ /* faster than a map most of the time */
+ int k;
+ for (i = j = qstart; i < qcount; i++)
+ {
+ Id ip = q->elements[i];
+ for (k = qcount; k < q->count; k++)
+ if (q->elements[k] == ip)
+ {
+ q->elements[j++] = ip;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* for the really pathologic cases we use the map */
+ Repo *installed = solv->installed;
+ if (!m->size)
+ map_init(m, installed->end - installed->start);
+ for (i = qcount; i < q->count; i++)
+ MAPSET(m, q->elements[i] - installed->start);
+ for (i = j = qstart; i < qcount; i++)
+ if (MAPTST(m, q->elements[i] - installed->start))
+ {
+ MAPCLR(m, q->elements[i] - installed->start);
+ q->elements[j++] = q->elements[i];
+ }
+ }
+ queue_truncate(q, j);
+}
+
static void
jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
{
Id select, p, pp;
Repo *installed;
Solvable *s;
- int i, j, set, qstart, pass;
+ int i, j, set, qstart;
Map omap;
installed = solv->installed;
}
}
}
- if (!installed)
+ if (!installed || installed->end == installed->start)
return;
/* now the hard part: disable some update rules */
/* first check if we have noobs or installed packages in the job */
+ i = j = 0;
FOR_JOB_SELECT(p, pp, select, what)
{
if (pool->solvables[p].repo == installed)
- {
- if (select == SOLVER_SOLVABLE)
- queue_push2(q, DISABLE_UPDATE, what);
- return;
- }
- if (solv->noobsoletes.size && MAPTST(&solv->noobsoletes, p))
+ j = p;
+ else if (solv->noobsoletes.size && MAPTST(&solv->noobsoletes, p))
return;
+ i++;
+ }
+ if (j) /* have installed packages */
+ {
+ /* this is for dupmap_all jobs, it can go away if we create
+ * duprules for them */
+ if (i == 1 && (set & SOLVER_SETREPO) != 0)
+ queue_push2(q, DISABLE_UPDATE, j);
+ return;
}
- /* all job packages obsolete */
+ omap.size = 0;
qstart = q->count;
- pass = 0;
- memset(&omap, 0, sizeof(omap));
FOR_JOB_SELECT(p, pp, select, what)
{
- Id p2, pp2;
-
- if (pass == 1)
- map_grow(&omap, installed->end - installed->start);
- s = pool->solvables + p;
- if (s->obsoletes)
- {
- Id obs, *obsp;
- obsp = s->repo->idarraydata + s->obsoletes;
- while ((obs = *obsp++) != 0)
- FOR_PROVIDES(p2, pp2, obs)
- {
- Solvable *ps = pool->solvables + p2;
- if (ps->repo != installed)
- continue;
- if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- if (pass)
- MAPSET(&omap, p2 - installed->start);
- else
- queue_push2(q, DISABLE_UPDATE, p2);
- }
- }
- FOR_PROVIDES(p2, pp2, s->name)
- {
- Solvable *ps = pool->solvables + p2;
- if (ps->repo != installed)
- continue;
- if (!pool->implicitobsoleteusesprovides && ps->name != s->name)
- continue;
- if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps))
- continue;
- if (pass)
- MAPSET(&omap, p2 - installed->start);
- else
- queue_push2(q, DISABLE_UPDATE, p2);
- }
- if (pass)
- {
- for (i = j = qstart; i < q->count; i += 2)
- {
- if (MAPTST(&omap, q->elements[i + 1] - installed->start))
- {
- MAPCLR(&omap, q->elements[i + 1] - installed->start);
- q->elements[j + 1] = q->elements[i + 1];
- j += 2;
- }
- }
- queue_truncate(q, j);
- }
+ intersect_obsoletes(solv, p, q, qstart, &omap);
if (q->count == qstart)
break;
- pass++;
}
if (omap.size)
map_free(&omap);
if (qstart == q->count)
return; /* nothing to prune */
- if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
- return; /* all is set */
+
+ /* convert result to (DISABLE_UPDATE, p) pairs */
+ i = q->count;
+ for (j = qstart; j < i; j++)
+ queue_push(q, q->elements[j]);
+ for (j = qstart; j < q->count; j += 2)
+ {
+ q->elements[j] = DISABLE_UPDATE;
+ q->elements[j + 1] = q->elements[i++];
+ }
/* now that we know which installed packages are obsoleted check each of them */
+ if ((set & (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR)) == (SOLVER_SETEVR | SOLVER_SETARCH | SOLVER_SETVENDOR))
+ return; /* all is set, nothing to do */
+
for (i = j = qstart; i < q->count; i += 2)
{
Solvable *is = pool->solvables + q->elements[i + 1];
}
}
+#undef CLEANDEPSDEBUG
+
+static inline void
+dep_pkgcheck(Solver *solv, Id dep, Map *m, Queue *q)
+{
+ Pool *pool = solv->pool;
+ Id p, pp;
+
+ if (ISRELDEP(dep))
+ {
+ Reldep *rd = GETRELDEP(pool, dep);
+ if (rd->flags >= 8)
+ {
+ if (rd->flags == REL_AND)
+ {
+ dep_pkgcheck(solv, rd->name, m, q);
+ dep_pkgcheck(solv, rd->evr, m, q);
+ return;
+ }
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_SPLITPROVIDES)
+ return;
+ if (rd->flags == REL_NAMESPACE && rd->name == NAMESPACE_INSTALLED)
+ return;
+ }
+ }
+ FOR_PROVIDES(p, pp, dep)
+ if (MAPTST(m, p))
+ queue_push(q, p);
+}
+
static void solver_createcleandepsmap(Solver *solv)
{
Pool *pool = solv->pool;
Queue iq;
int i;
+ if (installed->end == installed->start)
+ return;
map_init(&userinstalled, installed->end - installed->start);
map_init(&im, pool->nsolvables);
map_init(&installedm, pool->nsolvables);
r = solv->rules + rid;
if (r->d < 0)
continue;
+ i = solv->ruletojob.elements[rid - solv->jobrules];
+ if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
+ continue;
FOR_RULELITERALS(p, jp, r)
if (p > 0 && pool->solvables[p].repo == installed)
MAPSET(&userinstalled, p - installed->start);
}
+
+ /* add all cleandeps candidates to iq */
for (rid = solv->jobrules; rid < solv->jobrules_end; rid++)
{
r = solv->rules + rid;
- if (r->p >= 0 || r->d != 0)
- continue; /* disabled or not erase */
- p = -r->p;
- if (pool->solvables[p].repo != installed)
+ if (r->d < 0)
continue;
- MAPCLR(&userinstalled, p - installed->start);
- i = solv->ruletojob.elements[rid - solv->jobrules];
- how = job->elements[i];
- if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS))
- queue_push(&iq, p);
+ if (r->d == 0 && r->p < 0 && r->w2 == 0)
+ {
+ p = -r->p;
+ if (pool->solvables[p].repo != installed)
+ continue;
+ MAPCLR(&userinstalled, p - installed->start);
+ i = solv->ruletojob.elements[rid - solv->jobrules];
+ how = job->elements[i];
+ if ((how & (SOLVER_JOBMASK|SOLVER_CLEANDEPS)) == (SOLVER_ERASE|SOLVER_CLEANDEPS))
+ queue_push(&iq, p);
+ }
+ else if (r->p > 0)
+ {
+ i = solv->ruletojob.elements[rid - solv->jobrules];
+ if ((job->elements[i] & SOLVER_CLEANDEPS) == SOLVER_CLEANDEPS)
+ {
+ /* check if the literals all obsolete some installed package */
+ Map om;
+ int iqstart;
+
+ /* just one installed literal */
+ if (r->d == 0 && r->w2 == 0 && pool->solvables[r->p].repo == installed)
+ continue;
+ /* noobs is bad */
+ if (solv->noobsoletes.size)
+ {
+ FOR_RULELITERALS(p, jp, r)
+ if (MAPTST(&solv->noobsoletes, p))
+ break;
+ if (p)
+ continue;
+ }
+
+ om.size = 0;
+ iqstart = iq.count;
+ FOR_RULELITERALS(p, jp, r)
+ {
+ if (p < 0)
+ {
+ queue_truncate(&iq, iqstart); /* abort */
+ break;
+ }
+ if (pool->solvables[p].repo == installed)
+ {
+ if (iq.count == iqstart)
+ queue_push(&iq, p);
+ else
+ {
+ for (i = iqstart; i < iq.count; i++)
+ if (iq.elements[i] == p)
+ break;
+ queue_truncate(&iq, iqstart);
+ if (i < iq.count)
+ queue_push(&iq, p);
+ }
+ }
+ else
+ intersect_obsoletes(solv, p, &iq, iqstart, &om);
+ if (iq.count == iqstart)
+ break;
+ }
+ if (om.size)
+ map_free(&om);
+ }
+ }
}
+ if (solv->cleandeps_updatepkgs)
+ for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
+ queue_push(&iq, solv->cleandeps_updatepkgs->elements[i]);
+
for (p = installed->start; p < installed->end; p++)
{
if (pool->solvables[p].repo != installed)
MAPSET(&im, p);
}
- while (iq.count)
+#ifdef CLEANDEPSDEBUG
+ printf("HELLO PASS\n");
+#endif
+ for (;;)
{
+ if (!iq.count)
+ {
+ /* supplements pass */
+ for (ip = solv->installed->start; ip < solv->installed->end; ip++)
+ {
+ if (!MAPTST(&installedm, ip))
+ continue;
+ s = pool->solvables + ip;
+ if (!s->supplements)
+ continue;
+ if (!MAPTST(&im, ip))
+ continue;
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (dep_possible(solv, sup, &im))
+ break;
+ if (!sup)
+ {
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (dep_possible(solv, sup, &installedm))
+ {
+ /* no longer supplemented, also erase */
+ int iqcount = iq.count;
+ dep_pkgcheck(solv, sup, &im, &iq);
+ for (i = iqcount; i < iq.count; i++)
+ {
+ Id pqp = iq.elements[i];
+ if (pool->solvables[pqp].repo == installed)
+ MAPSET(&userinstalled, pqp - installed->start);
+ }
+ queue_truncate(&iq, iqcount);
+#ifdef CLEANDEPSDEBUG
+ printf("%s supplemented\n", pool_solvid2str(pool, ip));
+#endif
+ queue_push(&iq, ip);
+ }
+ }
+ }
+ if (!iq.count)
+ break;
+ }
ip = queue_shift(&iq);
s = pool->solvables + ip;
if (!MAPTST(&im, ip))
}
}
}
- if (!iq.count)
- {
- /* supplements pass */
- for (ip = solv->installed->start; ip < solv->installed->end; ip++)
- {
- if (!MAPTST(&installedm, ip))
- continue;
- s = pool->solvables + ip;
- if (!s->supplements)
- continue;
- if (!MAPTST(&im, ip))
- continue;
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (!dep_possible(solv, sup, &im) && dep_possible(solv, sup, &installedm))
- break;
- /* no longer supplemented, also erase */
- if (sup)
- {
-#ifdef CLEANDEPSDEBUG
- printf("%s supplemented\n", pool_solvid2str(pool, ip));
-#endif
- queue_push(&iq, ip);
- }
- }
- }
}
/* turn userinstalled into remove set for pruning */
queue_push(&iq, what);
}
}
- while (iq.count)
+
+#ifdef CLEANDEPSDEBUG
+ printf("BYE PASS\n");
+#endif
+ for (;;)
{
+ if (!iq.count)
+ {
+ /* supplements pass */
+ for (ip = installed->start; ip < installed->end; ip++)
+ {
+ if (!MAPTST(&installedm, ip))
+ continue;
+ if (MAPTST(&userinstalled, ip - installed->start))
+ continue;
+ s = pool->solvables + ip;
+ if (!s->supplements)
+ continue;
+ if (MAPTST(&im, ip))
+ continue;
+ supp = s->repo->idarraydata + s->supplements;
+ while ((sup = *supp++) != 0)
+ if (dep_possible(solv, sup, &im))
+ break;
+ if (sup)
+ {
+#ifdef CLEANDEPSDEBUG
+ printf("%s supplemented\n", pool_solvid2str(pool, ip));
+#endif
+ MAPSET(&im, ip);
+ queue_push(&iq, ip);
+ }
+ }
+ if (!iq.count)
+ break;
+ }
ip = queue_shift(&iq);
s = pool->solvables + ip;
#ifdef CLEANDEPSDEBUG
}
}
}
- if (!iq.count)
- {
- /* supplements pass */
- for (ip = installed->start; ip < installed->end; ip++)
- {
- if (!MAPTST(&installedm, ip))
- continue;
- if (MAPTST(&userinstalled, ip - installed->start))
- continue;
- s = pool->solvables + ip;
- if (!s->supplements)
- continue;
- if (MAPTST(&im, ip) || !MAPTST(&installedm, ip))
- continue;
- supp = s->repo->idarraydata + s->supplements;
- while ((sup = *supp++) != 0)
- if (dep_possible(solv, sup, &im))
- break;
- if (sup)
- {
-#ifdef CLEANDEPSDEBUG
- printf("%s supplemented\n", pool_solvid2str(pool, ip));
-#endif
- MAPSET(&im, ip);
- queue_push(&iq, ip);
- }
- }
- }
}
queue_free(&iq);
+ if (solv->cleandeps_updatepkgs)
+ for (i = 0; i < solv->cleandeps_updatepkgs->count; i++)
+ MAPSET(&im, solv->cleandeps_updatepkgs->elements[i]);
+ if (solv->cleandeps_mistakes)
+ for (i = 0; i < solv->cleandeps_mistakes->count; i++)
+ MAPSET(&im, solv->cleandeps_mistakes->elements[i]);
for (p = installed->start; p < installed->end; p++)
{
if (pool->solvables[p].repo != installed)
queue_free(&solv->branches);
queue_free(&solv->weakruleq);
queue_free(&solv->ruleassertions);
+ 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);
+ }
map_free(&solv->recommendsmap);
map_free(&solv->suggestsmap);
return old;
}
+int
+cleandeps_check_mistakes(Solver *solv, int level)
+{
+ Pool *pool = solv->pool;
+ Rule *r;
+ Id p, *dp;
+ int i;
+ int mademistake = 0;
+
+ if (!solv->cleandepsmap.size)
+ return level;
+ /* check for mistakes */
+ for (i = solv->installed->start; i < solv->installed->end; i++)
+ {
+ if (!MAPTST(&solv->cleandepsmap, i - solv->installed->start))
+ continue;
+ r = solv->rules + solv->featurerules + (i - solv->installed->start);
+ /* a mistake is when the featurerule is true but the updaterule is false */
+ if (r->p)
+ {
+ FOR_RULELITERALS(p, dp, r)
+ if (p > 0 && solv->decisionmap[p] > 0)
+ break;
+ if (p)
+ {
+ r = solv->rules + solv->updaterules + (i - solv->installed->start);
+ if (!r->p)
+ continue;
+ FOR_RULELITERALS(p, dp, r)
+ if (p > 0 && solv->decisionmap[p] > 0)
+ break;
+ if (!p)
+ {
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "cleandeps mistake: ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLVER, r);
+ POOL_DEBUG(SOLV_DEBUG_SOLVER, "feature rule: ");
+ solver_printruleclass(solv, SOLV_DEBUG_SOLVER, solv->rules + solv->featurerules + (i - solv->installed->start));
+ if (!solv->cleandeps_mistakes)
+ {
+ solv->cleandeps_mistakes = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->cleandeps_mistakes);
+ }
+ queue_push(solv->cleandeps_mistakes, i);
+ MAPCLR(&solv->cleandepsmap, i - solv->installed->start);
+ mademistake = 1;
+ }
+ }
+ }
+ }
+ if (mademistake)
+ {
+ level = 1;
+ revert(solv, level);
+ }
+ return level;
+}
/*-------------------------------------------------------------------
*
p = solv->orphaned.elements[i];
if (solv->decisionmap[p])
continue; /* already decided */
- olevel = level;
if (solv->droporphanedmap_all)
continue;
if (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - solv->installed->start))
continue;
POOL_DEBUG(SOLV_DEBUG_SOLVER, "keeping orphaned %s\n", pool_solvid2str(pool, p));
+ olevel = level;
level = setpropagatelearn(solv, level, p, 0, 0);
installedone = 1;
if (level < olevel)
break;
}
- if (level == 0)
- break;
if (installedone || i < solv->orphaned.count)
{
if (level == 0)
}
}
+ if (solv->installed && solv->cleandepsmap.size)
+ {
+ olevel = level;
+ level = cleandeps_check_mistakes(solv, level);
+ if (level < olevel)
+ continue;
+ }
+
if (solv->solution_callback)
{
solv->solution_callback(solv, solv->solution_callback_data);
*/
solv->jobrules = solv->nrules;
+ if (solv->cleandeps_updatepkgs)
+ {
+ queue_free(solv->cleandeps_updatepkgs);
+ solv->cleandeps_updatepkgs = solv_free(solv->cleandeps_updatepkgs);
+ }
for (i = 0; i < job->count; i += 2)
{
oldnrules = solv->nrules;
{
case SOLVER_INSTALL:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: %sinstall %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
+ if ((how & SOLVER_CLEANDEPS) != 0 && !solv->cleandepsmap.size && installed)
+ map_grow(&solv->cleandepsmap, installed->end - installed->start);
if (select == SOLVER_SOLVABLE)
{
p = what;
break;
case SOLVER_ERASE:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: %s%serase %s\n", weak ? "weak " : "", how & SOLVER_CLEANDEPS ? "clean deps " : "", solver_select2str(pool, select, what));
- if ((how & SOLVER_CLEANDEPS) != 0 && !solv->cleandepsmap.size && solv->installed)
- map_grow(&solv->cleandepsmap, solv->installed->end - solv->installed->start);
- if (select == SOLVER_SOLVABLE && solv->installed && pool->solvables[what].repo == solv->installed)
+ if ((how & SOLVER_CLEANDEPS) != 0 && !solv->cleandepsmap.size && installed)
+ map_grow(&solv->cleandepsmap, installed->end - installed->start);
+ if (select == SOLVER_SOLVABLE && installed && pool->solvables[what].repo == installed)
{
/* special case for "erase a specific solvable": we also
* erase all other solvables with that name, so that they
if (s->name != name)
continue;
/* keep other versions installed */
- if (s->repo == solv->installed)
+ if (s->repo == installed)
continue;
/* keep installcandidates of other jobs */
if (MAPTST(&installcandidatemap, p))
break;
case SOLVER_UPDATE:
+ if ((how & SOLVER_CLEANDEPS) != 0 && installed)
+ {
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ if (s->repo != installed)
+ continue;
+ if (!solv->cleandeps_updatepkgs)
+ {
+ solv->cleandeps_updatepkgs = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->cleandeps_updatepkgs);
+ }
+ queue_pushunique(solv->cleandeps_updatepkgs, p);
+ if (!solv->cleandepsmap.size)
+ map_grow(&solv->cleandepsmap, installed->end - installed->start);
+ }
+ }
POOL_DEBUG(SOLV_DEBUG_JOB, "job: %supdate %s\n", weak ? "weak " : "", solver_select2str(pool, select, what));
break;
case SOLVER_VERIFY:
makeruledecisions(solv);
POOL_DEBUG(SOLV_DEBUG_SOLVER, "problems so far: %d\n", solv->problems.count);
+ /* no mistakes */
+ if (solv->cleandeps_mistakes)
+ {
+ queue_free(solv->cleandeps_mistakes);
+ solv->cleandeps_mistakes = solv_free(solv->cleandeps_mistakes);
+ }
+
/*
* ********************************************
* solve!