summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMichael Schroeder <mls@suse.de>2008-08-29 18:31:45 +0000
committerMichael Schroeder <mls@suse.de>2008-08-29 18:31:45 +0000
commit2a270f741c5123d4aa7ec409be09da5498d754a5 (patch)
tree47457df0f45a1fb86fcdf2903011adc7bf3baff1 /src
parente39167fb292391ba3ae555a2ce5b902be025d981 (diff)
downloadlibsolv-2a270f741c5123d4aa7ec409be09da5498d754a5.tar.gz
libsolv-2a270f741c5123d4aa7ec409be09da5498d754a5.tar.bz2
libsolv-2a270f741c5123d4aa7ec409be09da5498d754a5.zip
- new job commands, now combinded from job type and select type
- support for distupgrade mode
Diffstat (limited to 'src')
-rw-r--r--src/policy.c8
-rw-r--r--src/pool.c4
-rw-r--r--src/pool.h1
-rw-r--r--src/solvable.c36
-rw-r--r--src/solver.c466
-rw-r--r--src/solver.h63
-rw-r--r--src/solverdebug.c99
-rw-r--r--src/solverdebug.h2
8 files changed, 403 insertions, 276 deletions
diff --git a/src/policy.c b/src/policy.c
index ffc8233..c6f9a10 100644
--- a/src/policy.c
+++ b/src/policy.c
@@ -299,7 +299,7 @@ prune_to_best_version(Solver *solv, Queue *plist)
}
}
- if (best == ID_NULL)
+ if (!best)
best = plist->elements[0];
plist->elements[j++] = best;
@@ -461,7 +461,11 @@ policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
continue;
queue_push(qs, p);
}
- if (solv->noupdateprovide && solv->obsoletes && solv->obsoletes[n - solv->installed->start])
+ /* if we have found some valid candidates and noupdateprovide is not set, we're
+ done. otherwise we fallback to all obsoletes */
+ if (!solv->noupdateprovide && qs->count)
+ return;
+ if (solv->obsoletes && solv->obsoletes[n - solv->installed->start])
{
for (pp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *pp++) != 0;)
{
diff --git a/src/pool.c b/src/pool.c
index db9ec8d..1761d42 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -376,8 +376,8 @@ pool_queuetowhatprovides(Pool *pool, Queue *q)
Offset off;
int count = q->count;
- if (count == 0) /* queue empty -> ID_EMPTY */
- return ID_EMPTY;
+ if (count == 0) /* queue empty -> 1 */
+ return 1;
/* extend whatprovidesdata if needed, +1 for ID_NULL-termination */
if (pool->whatprovidesdataleft < count + 1)
diff --git a/src/pool.h b/src/pool.h
index 879bfd8..cd3d747 100644
--- a/src/pool.h
+++ b/src/pool.h
@@ -178,6 +178,7 @@ int solvable_lookup_void(Solvable *s, Id keyname);
char * solvable_get_location(Solvable *s, unsigned int *medianrp);
const unsigned char *solvable_lookup_bin_checksum(Solvable *s, Id keyname, Id *typep);
const char *solvable_lookup_checksum(Solvable *s, Id keyname, Id *typep);
+int solvable_identical(Pool *pool, Solvable *s1, Solvable *s2);
int solvable_trivial_installable_map(Solvable *s, Map *installedmap, Map *conflictsmap);
int solvable_trivial_installable_repo(Solvable *s, struct _Repo *installed);
diff --git a/src/solvable.c b/src/solvable.c
index 5f79c70..c17fe25 100644
--- a/src/solvable.c
+++ b/src/solvable.c
@@ -604,3 +604,39 @@ pool_create_state_maps(Pool *pool, Queue *installed, Map *installedmap, Map *con
}
}
+int
+solvable_identical(Pool *pool, Solvable *s1, Solvable *s2)
+{
+ unsigned int bt1, bt2;
+ Id rq1, rq2;
+ Id *reqp;
+
+ if (s1->name != s2->name)
+ return 0;
+ if (s1->arch != s2->arch)
+ return 0;
+ if (s1->evr != s2->evr)
+ return 0;
+ if (s1->vendor != s2->vendor)
+ return 0;
+
+ /* first tests passed, try requires */
+ rq1 = rq2 = 0;
+ if (s1->requires)
+ for (reqp = s1->repo->idarraydata + s1->requires; *reqp; reqp++)
+ rq1 ^= *reqp++;
+ if (s2->requires)
+ for (reqp = s2->repo->idarraydata + s2->requires; *reqp; reqp++)
+ rq2 ^= *reqp++;
+ if (rq1 != rq2)
+ return 0;
+
+ /* looking good, try some fancier stuff */
+ bt1 = solvable_lookup_num(s1, SOLVABLE_BUILDTIME, 0);
+ bt2 = solvable_lookup_num(s2, SOLVABLE_BUILDTIME, 0);
+ if (bt1 && bt2 && bt1 != bt2)
+ return 0;
+
+ /* might also look up the package checksum here */
+ return 1;
+}
diff --git a/src/solver.c b/src/solver.c
index 4354511..2d50e63 100644
--- a/src/solver.c
+++ b/src/solver.c
@@ -651,7 +651,7 @@ makeruledecisions(Solver *solv)
disableproblem(solv, v);
continue;
}
-
+
assert(solv->decisionq_why.elements[i]);
/*
@@ -856,7 +856,7 @@ enableweakrules(Solver *solv)
/* FIXME: bad code ahead, replace as soon as possible */
-/* FIXME: should probably look at SOLVER_INSTALL_SOLVABLE_ONE_OF */
+/* FIXME: should probably look at SOLVER_INSTALL|SOLVABLE_ONE_OF */
/*-------------------------------------------------------------------
* disable update rules
@@ -867,7 +867,7 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
{
Pool *pool = solv->pool;
int i, j;
- Id how, what, p, *pp;
+ Id how, select, what, p, *pp;
Solvable *s;
Repo *installed;
Rule *r;
@@ -879,13 +879,15 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
if (jobidx != -1)
{
- how = job->elements[jobidx] & ~SOLVER_WEAK;
- switch(how)
+ how = job->elements[jobidx];
+ select = how & SOLVER_SELECTMASK;
+ switch (how & SOLVER_JOBMASK)
{
- case SOLVER_INSTALL_SOLVABLE:
- case SOLVER_ERASE_SOLVABLE:
- case SOLVER_ERASE_SOLVABLE_NAME:
- case SOLVER_ERASE_SOLVABLE_PROVIDES:
+ case SOLVER_ERASE:
+ break;
+ case SOLVER_INSTALL:
+ if (select != SOLVER_SOLVABLE)
+ return;
break;
default:
return;
@@ -902,14 +904,19 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
if (j == lastjob)
continue;
lastjob = j;
- how = job->elements[j] & ~SOLVER_WEAK;
+ how = job->elements[j];
what = job->elements[j + 1];
- switch(how)
+ select = how & SOLVER_SELECTMASK;
+ switch (how & SOLVER_JOBMASK)
{
- case SOLVER_INSTALL_SOLVABLE: /* install specific solvable */
+ case SOLVER_INSTALL:
+ if (select != SOLVER_SOLVABLE)
+ break;
s = pool->solvables + what;
if (solv->noobsoletes.size && MAPTST(&solv->noobsoletes, what))
break;
+ if (s->repo == installed)
+ break;
if (s->obsoletes)
{
Id obs, *obsp;
@@ -932,20 +939,10 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
MAPSET(&solv->noupdate, p - installed->start);
}
break;
- case SOLVER_ERASE_SOLVABLE:
- s = pool->solvables + what;
- if (s->repo == installed)
- MAPSET(&solv->noupdate, what - installed->start);
- break;
- case SOLVER_ERASE_SOLVABLE_NAME: /* remove by capability */
- case SOLVER_ERASE_SOLVABLE_PROVIDES:
- FOR_PROVIDES(p, pp, what)
- {
- if (how == SOLVER_ERASE_SOLVABLE_NAME && !pool_match_nevr(pool, pool->solvables + p, what))
- continue;
- if (pool->solvables[p].repo == installed)
- MAPSET(&solv->noupdate, p - installed->start);
- }
+ case SOLVER_ERASE:
+ FOR_JOB_SELECT(p, pp, select, what)
+ if (pool->solvables[p].repo == installed)
+ MAPSET(&solv->noupdate, p - installed->start);
break;
default:
break;
@@ -957,12 +954,17 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
{
/* we just disabled job #jobidx. enable all update rules
* that aren't disabled by the remaining job rules */
- how = job->elements[jobidx] & ~SOLVER_WEAK;
+ how = job->elements[jobidx];
what = job->elements[jobidx + 1];
- switch(how)
+ select = how & SOLVER_SELECTMASK;
+ switch (how & SOLVER_JOBMASK)
{
- case SOLVER_INSTALL_SOLVABLE:
+ case SOLVER_INSTALL:
+ if (select != SOLVER_SOLVABLE)
+ break;
s = pool->solvables + what;
+ if (s->repo == installed)
+ break;
if (s->obsoletes)
{
Id obs, *obsp;
@@ -1006,28 +1008,9 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
}
}
break;
- case SOLVER_ERASE_SOLVABLE:
- s = pool->solvables + what;
- if (s->repo != installed)
- break;
- if (MAPTST(&solv->noupdate, what - installed->start))
- break;
- r = solv->rules + solv->updaterules + (what - installed->start);
- if (r->d >= 0)
- break;
- enablerule(solv, r);
- IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
- {
- POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
- solver_printrule(solv, SAT_DEBUG_SOLUTIONS, r);
- }
- break;
- case SOLVER_ERASE_SOLVABLE_NAME: /* remove by capability */
- case SOLVER_ERASE_SOLVABLE_PROVIDES:
- FOR_PROVIDES(p, pp, what)
+ case SOLVER_ERASE:
+ FOR_JOB_SELECT(p, pp, select, what)
{
- if (how == SOLVER_ERASE_SOLVABLE_NAME && !pool_match_nevr(pool, pool->solvables + p, what))
- continue;
if (pool->solvables[p].repo != installed)
continue;
if (MAPTST(&solv->noupdate, p - installed->start))
@@ -1415,6 +1398,35 @@ addrpmrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addrpmrulesforupdaters -----\n");
}
+static Id
+finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
+{
+ Pool *pool = solv->pool;
+ int i;
+
+ policy_findupdatepackages(solv, s, qs, allow_all);
+ if (!qs->count)
+ {
+ if (allow_all)
+ return 0;
+ policy_findupdatepackages(solv, s, qs, 1);
+ if (!qs->count)
+ return 0;
+ qs->count = 0;
+ return -SYSTEMSOLVABLE;
+ }
+ if (allow_all)
+ return s - pool->solvables;
+ /* check if it is ok to keep the installed package */
+ for (i = 0; i < qs->count; i++)
+ {
+ Solvable *ns = pool->solvables + qs->elements[i];
+ if (s->evr == ns->evr && solvable_identical(pool, s, ns))
+ return s - pool->solvables;
+ }
+ /* nope, it must be some other package */
+ return queue_shift(qs);
+}
/*-------------------------------------------------------------------
*
@@ -1429,21 +1441,21 @@ addupdaterule(Solver *solv, Solvable *s, int allow_all)
{
/* installed packages get a special upgrade allowed rule */
Pool *pool = solv->pool;
- Id d;
+ Id p, d;
Queue qs;
Id qsbuf[64];
POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addupdaterule -----\n");
-
queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
- /* find update candidates for 's' */
- policy_findupdatepackages(solv, s, &qs, allow_all);
- if (qs.count == 0) /* no updaters found */
- d = 0; /* assertion (keep installed) */
+ p = s - pool->solvables;
+ /* find update candidates for 's' */
+ if (solv->distupgrade)
+ p = finddistupgradepackages(solv, s, &qs, allow_all);
else
- d = pool_queuetowhatprovides(pool, &qs); /* intern computed queue */
+ policy_findupdatepackages(solv, s, &qs, allow_all);
+ d = qs.count ? pool_queuetowhatprovides(pool, &qs) : 0;
queue_free(&qs);
- addrule(solv, s - pool->solvables, d); /* allow update of s */
+ addrule(solv, p, d); /* allow update of s */
POOL_DEBUG(SAT_DEBUG_SCHUBI, "----- addupdaterule end -----\n");
}
@@ -1812,7 +1824,7 @@ l1retry:
goto l1retry;
}
why = solv->decisionq_why.elements[idx];
- if (!why) /* just in case, maye for SYSTEMSOLVABLE */
+ if (!why) /* just in case, maybe for SYSTEMSOLVABLE */
goto l1retry;
c = solv->rules + why;
}
@@ -2261,6 +2273,7 @@ solver_create(Pool *pool, Repo *installed)
queue_init(&solv->problems);
queue_init(&solv->suggestions);
queue_init(&solv->recommendations);
+ queue_init(&solv->orphaned);
queue_init(&solv->learnt_why);
queue_init(&solv->learnt_pool);
queue_init(&solv->branches);
@@ -2298,6 +2311,7 @@ solver_free(Solver *solv)
queue_free(&solv->problems);
queue_free(&solv->suggestions);
queue_free(&solv->recommendations);
+ queue_free(&solv->orphaned);
queue_free(&solv->branches);
queue_free(&solv->covenantq);
queue_free(&solv->weakruleq);
@@ -2414,6 +2428,7 @@ run_solver(Solver *solv, int disablerules, int doweak)
}
if (l || !dq.count)
continue;
+ /* prune to installed if not updating */
if (!solv->updatesystem && solv->installed && dq.count > 1)
{
int j, k;
@@ -2490,7 +2505,7 @@ run_solver(Solver *solv, int disablerules, int doweak)
for (i = solv->installed->start, r = solv->rules + solv->updaterules; i < solv->installed->end; i++, r++)
{
Rule *rr;
- Id d;
+ Id inst;
s = pool->solvables + i;
/* skip if not installed (can't update) */
@@ -2505,42 +2520,34 @@ run_solver(Solver *solv, int disablerules, int doweak)
continue;
queue_empty(&dq);
+
rr = r;
if (rr->d < 0) /* disabled -> look at feature rule ? */
rr -= solv->installed->end - solv->installed->start;
if (!rr->p) /* identical to update rule? */
rr = r;
- d = (rr->d < 0) ? -rr->d - 1 : rr->d;
- if (d == 0)
- {
- if (!rr->w2 || solv->decisionmap[rr->w2] > 0)
- continue;
- /* decide w2 if yet undecided */
- if (solv->decisionmap[rr->w2] == 0)
- queue_push(&dq, rr->w2);
- }
- else
+ if (rr->p <= 0)
+ continue;
+
+ FOR_RULELITERALS(p, dp, rr)
{
- dp = pool->whatprovidesdata + d;
- while ((p = *dp++) != 0)
- {
- if (solv->decisionmap[p] > 0)
- break;
- /* decide p if yet undecided */
- if (solv->decisionmap[p] == 0)
- queue_push(&dq, p);
- }
- if (p)
- continue;
+ if (solv->decisionmap[p] > 0)
+ break;
+ if (solv->decisionmap[p] == 0)
+ queue_push(&dq, p);
}
- if (!dq.count && solv->decisionmap[i] != 0)
+ if (p || !dq.count) /* already fulfilled or empty */
continue;
+ if (dq.elements[0] == i)
+ inst = queue_shift(&dq);
+ else
+ inst = 0;
olevel = level;
/* FIXME: it is handled a bit different because we do not want
* to have it pruned just because it is not recommened.
- * we should not prune installed packages instead
+ * we should not prune installed packages instead.
*/
- level = selectandinstall(solv, level, &dq, (solv->decisionmap[i] ? 0 : i), disablerules);
+ level = selectandinstall(solv, level, &dq, inst, disablerules);
if (level == 0)
{
queue_free(&dq);
@@ -2746,6 +2753,37 @@ run_solver(Solver *solv, int disablerules, int doweak)
}
}
+ if (solv->distupgrade && solv->installed)
+ {
+ /* let's see if we can install some unsupported package */
+ int ri;
+ POOL_DEBUG(SAT_DEBUG_STATS, "deciding unsupported packages\n");
+ for (i = solv->installed->start, ri = 0; i < solv->installed->end; i++, ri++)
+ {
+ s = pool->solvables + i;
+ if (s->repo != solv->installed)
+ continue;
+ if (solv->decisionmap[i])
+ continue;
+ if (!solv->rules[solv->updaterules + ri].p && !solv->rules[solv->featurerules + ri].p)
+ break;
+ }
+ if (i < solv->installed->end)
+ {
+ if (solv->distupgrade_removeunsupported)
+ {
+ POOL_DEBUG(SAT_DEBUG_STATS, "removing unsupported %s\n", solvable2str(pool, pool->solvables + i));
+ level = setpropagatelearn(solv, level, -i, 0);
+ }
+ else
+ {
+ POOL_DEBUG(SAT_DEBUG_STATS, "keeping unsupported %s\n", solvable2str(pool, pool->solvables + i));
+ level = setpropagatelearn(solv, level, i, 0);
+ }
+ continue;
+ }
+ }
+
if (solv->solution_callback)
{
solv->solution_callback(solv, solv->solution_callback_data);
@@ -3102,11 +3140,19 @@ problems_to_solutions(Solver *solv, Queue *job)
Id p, d, *dp, rp = 0;
Rule *rr;
p = solv->installed->start + (why - solv->updaterules);
- if (solv->decisionmap[p] > 0)
- continue; /* false alarm, turned out we can keep the package */
rr = solv->rules + solv->featurerules + (why - solv->updaterules);
if (!rr->p)
rr = solv->rules + why;
+ if (solv->distupgrade && solv->rules[why].p != p && solv->decisionmap[p] > 0)
+ {
+ /* distupgrade case, allow to keep old package */
+ queue_push(&solutions, p);
+ queue_push(&solutions, p);
+ nsol++;
+ continue;
+ }
+ if (solv->decisionmap[p] > 0)
+ continue; /* false alarm, turned out we can keep the package */
if (rr->w2)
{
d = rr->d < 0 ? -rr->d - 1 : rr->d;
@@ -3258,7 +3304,7 @@ solver_problemruleinfo(Solver *solv, Queue *job, Id rid, Id *depp, Id *sourcep,
*sourcep = p;
*targetp = job->elements[p];
d = r->d < 0 ? -r->d - 1 : r->d;
- if (d == 0 && r->w2 == 0 && r->p == -SYSTEMSOLVABLE && job->elements[p] != SOLVER_INSTALL_SOLVABLE_ONE_OF)
+ if (d == 0 && r->w2 == 0 && r->p == -SYSTEMSOLVABLE && (job->elements[p] & SOLVER_SELECTMASK) != SOLVER_SOLVABLE_ONE_OF)
return SOLVER_PROBLEM_JOB_NOTHING_PROVIDES_DEP;
return SOLVER_PROBLEM_JOB_RULE;
}
@@ -3776,7 +3822,7 @@ weaken_solvable_deps(Solver *solv, Id p)
int i;
Rule *r;
- for (i = 1, r = solv->rules + i; i < solv->featurerules; i++, r++)
+ for (i = 1, r = solv->rules + i; i < solv->rpmrules_end; i++, r++)
{
if (r->p != -p)
continue;
@@ -3804,19 +3850,25 @@ solver_solve(Solver *solv, Queue *job)
int oldnrules;
Map addedmap; /* '1' == have rpm-rules for solvable */
Map installcandidatemap;
- Id how, what, name, weak, p, *pp, d;
+ Id how, what, select, name, weak, p, *pp, d;
Queue q, redoq;
Solvable *s;
int goterase;
Rule *r;
+ POOL_DEBUG(SAT_DEBUG_STATS, "solver started\n");
+ POOL_DEBUG(SAT_DEBUG_STATS, "fixsystem=%d updatesystem=%d dosplitprovides=%d, noupdateprovide=%d\n", solv->fixsystem, solv->updatesystem, solv->dosplitprovides, solv->noupdateprovide);
+ POOL_DEBUG(SAT_DEBUG_STATS, "distupgrade=%d distupgrade_removeunsupported=%d\n", solv->distupgrade, solv->distupgrade_removeunsupported);
+ POOL_DEBUG(SAT_DEBUG_STATS, "allowuninstall=%d, allowdowngrade=%d, allowarchchange=%d, allowvendorchange=%d\n", solv->allowuninstall, solv->allowdowngrade, solv->allowarchchange, solv->allowvendorchange);
+ POOL_DEBUG(SAT_DEBUG_STATS, "promoteepoch=%d, allowvirtualconflicts=%d, allowselfconflicts=%d\n", pool->promoteepoch, solv->allowvirtualconflicts, solv->allowselfconflicts);
+ POOL_DEBUG(SAT_DEBUG_STATS, "obsoleteusesprovides=%d, implicitobsoleteusesprovides=%d\n", solv->obsoleteusesprovides, solv->implicitobsoleteusesprovides);
+ POOL_DEBUG(SAT_DEBUG_STATS, "dontinstallrecommended=%d, ignorealreadyrecommended=%d, dontshowinstalledrecommended=%d\n", solv->dontinstallrecommended, solv->ignorealreadyrecommended, solv->dontshowinstalledrecommended);
/* create whatprovides if not already there */
if (!pool->whatprovides)
pool_createwhatprovides(pool);
/* create obsolete index if needed */
- if (solv->noupdateprovide)
- create_obsolete_index(solv);
+ create_obsolete_index(solv);
/*
* create basic rule set of all involved packages
@@ -3828,29 +3880,14 @@ solver_solve(Solver *solv, Queue *job)
for (i = 0; i < job->count; i += 2)
{
how = job->elements[i] & ~SOLVER_WEAK;
+ if ((how & SOLVER_JOBMASK) != SOLVER_NOOBSOLETES)
+ continue;
what = job->elements[i + 1];
- switch(how)
- {
- case SOLVER_NOOBSOLETES_SOLVABLE:
- case SOLVER_NOOBSOLETES_SOLVABLE_NAME:
- case SOLVER_NOOBSOLETES_SOLVABLE_PROVIDES:
- if (!solv->noobsoletes.size)
- map_init(&solv->noobsoletes, pool->nsolvables);
- if (how == SOLVER_NOOBSOLETES_SOLVABLE)
- {
- MAPSET(&solv->noobsoletes, what);
- break;
- }
- FOR_PROVIDES(p, pp, what)
- {
- if (how == SOLVER_NOOBSOLETES_SOLVABLE_NAME && !pool_match_nevr(pool, pool->solvables + p, what))
- continue;
- MAPSET(&solv->noobsoletes, p);
- }
- break;
- default:
- break;
- }
+ select = how & SOLVER_SELECTMASK;
+ if (!solv->noobsoletes.size)
+ map_init(&solv->noobsoletes, pool->nsolvables);
+ FOR_JOB_SELECT(p, pp, select, what)
+ MAPSET(&solv->noobsoletes, p);
}
map_init(&addedmap, pool->nsolvables);
@@ -3893,34 +3930,23 @@ solver_solve(Solver *solv, Queue *job)
oldnrules = solv->nrules;
for (i = 0; i < job->count; i += 2)
{
- how = job->elements[i] & ~SOLVER_WEAK;
+ how = job->elements[i];
what = job->elements[i + 1];
+ select = how & SOLVER_SELECTMASK;
- switch(how)
+ switch (how & SOLVER_JOBMASK)
{
- case SOLVER_INSTALL_SOLVABLE:
- MAPSET(&installcandidatemap, what);
- addrpmrulesforsolvable(solv, pool->solvables + what, &addedmap);
- break;
- case SOLVER_INSTALL_SOLVABLE_NAME:
- case SOLVER_INSTALL_SOLVABLE_PROVIDES:
- FOR_PROVIDES(p, pp, what)
+ case SOLVER_INSTALL:
+ FOR_JOB_SELECT(p, pp, select, what)
{
- /* if by name, ensure that the name matches */
- if (how == SOLVER_INSTALL_SOLVABLE_NAME && !pool_match_nevr(pool, pool->solvables + p, what))
- continue;
MAPSET(&installcandidatemap, p);
addrpmrulesforsolvable(solv, pool->solvables + p, &addedmap);
}
break;
- case SOLVER_INSTALL_SOLVABLE_UPDATE:
- /* dont allow downgrade */
- addrpmrulesforupdaters(solv, pool->solvables + what, &addedmap, 0);
- break;
- case SOLVER_INSTALL_SOLVABLE_ONE_OF:
- pp = pool->whatprovidesdata + what;
- while ((p = *pp++) != 0)
- addrpmrulesforsolvable(solv, pool->solvables + p, &addedmap);
+ case SOLVER_UPDATE:
+ /* FIXME: semantics? */
+ FOR_JOB_SELECT(p, pp, select, what)
+ addrpmrulesforupdaters(solv, pool->solvables + what, &addedmap, 0);
break;
}
}
@@ -4003,7 +4029,7 @@ solver_solve(Solver *solv, Queue *job)
* Add update rules for installed solvables
*
* almost identical to feature rules
- * except that downgrades are allowed
+ * except that downgrades/archchanges/vendorchanges are not allowed
*/
POOL_DEBUG(SAT_DEBUG_SCHUBI, "*** Add update rules ***\n");
@@ -4021,22 +4047,28 @@ solver_solve(Solver *solv, Queue *job)
addrule(solv, 0, 0); /* create dummy rule */
continue;
}
-
- addupdaterule(solv, s, 0); /* allowall = 0: downgrades allowed */
-
+ addupdaterule(solv, s, 0); /* allowall = 0: downgrades not allowed */
/*
* check for and remove duplicate
*/
-
r = solv->rules + solv->nrules - 1; /* r: update rule */
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))
+ queue_push(&solv->orphaned, i);
+ if (!r->p)
+ {
+ assert(!sr->p); /* can't have feature rule and no update rule */
+ continue;
+ }
unifyrules_sortcmp_data = pool;
if (!unifyrules_sortcmp(r, sr))
{
/* identical rule, kill unneeded rule */
if (solv->allowuninstall)
{
- /* keep feature rule */
+ /* keep feature rule, make it weak */
memset(r, 0, sizeof(*r));
queue_push(&solv->weakruleq, sr - solv->rules);
}
@@ -4072,101 +4104,77 @@ solver_solve(Solver *solv, Queue *job)
{
oldnrules = solv->nrules;
- how = job->elements[i] & ~SOLVER_WEAK;
- weak = job->elements[i] & SOLVER_WEAK;
+ how = job->elements[i];
what = job->elements[i + 1];
- switch(how)
+ weak = how & SOLVER_WEAK;
+ select = how & SOLVER_SELECTMASK;
+ switch (how & SOLVER_JOBMASK)
{
- case SOLVER_INSTALL_SOLVABLE: /* install specific solvable */
- s = pool->solvables + what;
- POOL_DEBUG(SAT_DEBUG_JOB, "job: %sinstall solvable %s\n", weak ? "weak " : "", solvable2str(pool, s));
- addrule(solv, what, 0); /* install by Id */
+ case SOLVER_INSTALL:
+ POOL_DEBUG(SAT_DEBUG_JOB, "job: %sinstall %s\n", weak ? "weak " : "", solver_select2str(solv, select, what));
+ if (select == SOLVER_SOLVABLE)
+ {
+ p = what;
+ d = 0;
+ }
+ else
+ {
+ queue_empty(&q);
+ FOR_JOB_SELECT(p, pp, select, what)
+ queue_push(&q, p);
+ if (!q.count)
+ {
+ /* no candidate found, make this an impossible rule */
+ queue_push(&q, -SYSTEMSOLVABLE);
+ }
+ p = queue_shift(&q); /* get first candidate */
+ d = !q.count ? 0 : pool_queuetowhatprovides(pool, &q); /* internalize */
+ }
+ addrule(solv, p, d); /* add install rule */
queue_push(&solv->ruletojob, i);
if (weak)
queue_push(&solv->weakruleq, solv->nrules - 1);
break;
- case SOLVER_ERASE_SOLVABLE:
- s = pool->solvables + what;
- POOL_DEBUG(SAT_DEBUG_JOB, "job: %serase solvable %s\n", weak ? "weak " : "", solvable2str(pool, s));
- name = s->name;
- if (solv->installed && s->repo == solv->installed)
+ case SOLVER_ERASE:
+ POOL_DEBUG(SAT_DEBUG_JOB, "job: %serase %s\n", weak ? "weak " : "", solver_select2str(solv, select, what));
+ if (select == SOLVER_SOLVABLE && solv->installed && pool->solvables[what].repo == solv->installed)
{
- FOR_PROVIDES(p, pp, s->name)
+ /* special case for "erase a specific solvable": we also
+ * erase all other solvables with that name, so that they
+ * don't get picked up as replacement */
+ name = pool->solvables[what].name;
+ FOR_PROVIDES(p, pp, name)
{
+ if (p == what)
+ continue;
s = pool->solvables + p;
if (s->name != name)
continue;
- if (p != what)
- {
- /* keep other versions installed */
- if (s->repo == solv->installed)
- continue;
- /* keep installcandidates of other jobs */
- if (MAPTST(&installcandidatemap, p))
- continue;
- }
+ /* keep other versions installed */
+ if (s->repo == solv->installed)
+ continue;
+ /* keep installcandidates of other jobs */
+ if (MAPTST(&installcandidatemap, p))
+ continue;
addrule(solv, -p, 0); /* remove by Id */
queue_push(&solv->ruletojob, i);
if (weak)
queue_push(&solv->weakruleq, solv->nrules - 1);
}
}
- else
+ FOR_JOB_SELECT(p, pp, select, what)
{
- addrule(solv, -what, 0); /* remove by Id */
+ addrule(solv, -p, 0);
queue_push(&solv->ruletojob, i);
if (weak)
queue_push(&solv->weakruleq, solv->nrules - 1);
}
break;
- case SOLVER_INSTALL_SOLVABLE_NAME: /* install by capability */
- case SOLVER_INSTALL_SOLVABLE_PROVIDES:
- if (how == SOLVER_INSTALL_SOLVABLE_NAME)
- POOL_DEBUG(SAT_DEBUG_JOB, "job: %sinstall name %s\n", weak ? "weak " : "", dep2str(pool, what));
- if (how == SOLVER_INSTALL_SOLVABLE_PROVIDES)
- POOL_DEBUG(SAT_DEBUG_JOB, "job: %sinstall provides %s\n", weak ? "weak " : "", dep2str(pool, what));
- queue_empty(&q);
- FOR_PROVIDES(p, pp, what)
- {
- /* if by name, ensure that the name matches */
- if (how == SOLVER_INSTALL_SOLVABLE_NAME && !pool_match_nevr(pool, pool->solvables + p, what))
- continue;
- queue_push(&q, p);
- }
- if (!q.count)
- {
- /* no provider, make this an impossible rule */
- queue_push(&q, -SYSTEMSOLVABLE);
- }
- p = queue_shift(&q); /* get first provider */
- if (!q.count)
- d = 0; /* single provider ? -> make assertion */
- else
- d = pool_queuetowhatprovides(pool, &q); /* get all providers */
- addrule(solv, p, d); /* add 'requires' rule */
- queue_push(&solv->ruletojob, i);
- if (weak)
- queue_push(&solv->weakruleq, solv->nrules - 1);
- break;
- case SOLVER_ERASE_SOLVABLE_NAME: /* remove by capability */
- case SOLVER_ERASE_SOLVABLE_PROVIDES:
- if (how == SOLVER_ERASE_SOLVABLE_NAME)
- POOL_DEBUG(SAT_DEBUG_JOB, "job: %serase name %s\n", weak ? "weak " : "", dep2str(pool, what));
- if (how == SOLVER_ERASE_SOLVABLE_PROVIDES)
- POOL_DEBUG(SAT_DEBUG_JOB, "job: %serase provides %s\n", weak ? "weak " : "", dep2str(pool, what));
- FOR_PROVIDES(p, pp, what)
- {
- /* if by name, ensure that the name matches */
- if (how == SOLVER_ERASE_SOLVABLE_NAME && !pool_match_nevr(pool, pool->solvables + p, what))
- continue;
- addrule(solv, -p, 0); /* add 'remove' rule */
- queue_push(&solv->ruletojob, i);
- if (weak)
- queue_push(&solv->weakruleq, solv->nrules - 1);
- }
- break;
- case SOLVER_INSTALL_SOLVABLE_UPDATE: /* find update for solvable */
+ case SOLVER_UPDATE:
+ POOL_DEBUG(SAT_DEBUG_JOB, "job: %supdate %s\n", weak ? "weak " : "", solver_select2str(solv, select, what));
+ if (select != SOLVER_SOLVABLE)
+ break;
s = pool->solvables + what;
POOL_DEBUG(SAT_DEBUG_JOB, "job: %supdate %s\n", weak ? "weak " : "", solvable2str(pool, s));
addupdaterule(solv, s, 0);
@@ -4174,28 +4182,32 @@ solver_solve(Solver *solv, Queue *job)
if (weak)
queue_push(&solv->weakruleq, solv->nrules - 1);
break;
- case SOLVER_INSTALL_SOLVABLE_ONE_OF:
- POOL_DEBUG(SAT_DEBUG_JOB, "job: %sone of\n", weak ? "weak " : "");
- for (pp = pool->whatprovidesdata + what; *pp; pp++)
- POOL_DEBUG(SAT_DEBUG_JOB, " %s\n", solvable2str(pool, pool->solvables + *pp));
- addrule(solv, -SYSTEMSOLVABLE, what);
- queue_push(&solv->ruletojob, i);
- if (weak)
- queue_push(&solv->weakruleq, solv->nrules - 1);
- break;
- case SOLVER_WEAKEN_SOLVABLE_DEPS:
+ case SOLVER_WEAKENDEPS:
+ POOL_DEBUG(SAT_DEBUG_JOB, "job: %sweaken deps %s\n", weak ? "weak " : "", solver_select2str(solv, select, what));
+ if (select != SOLVER_SOLVABLE)
+ break;
s = pool->solvables + what;
- POOL_DEBUG(SAT_DEBUG_JOB, "job: weaken deps %s\n", solvable2str(pool, s));
weaken_solvable_deps(solv, what);
break;
- case SOLVER_NOOBSOLETES_SOLVABLE:
- POOL_DEBUG(SAT_DEBUG_JOB, "job: no obsolete %s\n", solvable2str(pool, pool->solvables + what));
+ case SOLVER_NOOBSOLETES:
+ POOL_DEBUG(SAT_DEBUG_JOB, "job: %sno obsolete %s\n", weak ? "weak " : "", solver_select2str(solv, select, what));
break;
- case SOLVER_NOOBSOLETES_SOLVABLE_NAME:
- POOL_DEBUG(SAT_DEBUG_JOB, "job: no obsolete name %s\n", dep2str(pool, what));
+ case SOLVER_LOCK:
+ POOL_DEBUG(SAT_DEBUG_JOB, "job: %slock %s\n", weak ? "weak " : "", solver_select2str(solv, select, what));
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ s = pool->solvables + p;
+ if (installed && s->repo == installed)
+ addrule(solv, p, 0);
+ else
+ addrule(solv, -p, 0);
+ queue_push(&solv->ruletojob, i);
+ if (weak)
+ queue_push(&solv->weakruleq, solv->nrules - 1);
+ }
break;
- case SOLVER_NOOBSOLETES_SOLVABLE_PROVIDES:
- POOL_DEBUG(SAT_DEBUG_JOB, "job: no obsolete provides %s\n", dep2str(pool, what));
+ default:
+ POOL_DEBUG(SAT_DEBUG_JOB, "job: unknown job\n");
break;
}
diff --git a/src/solver.h b/src/solver.h
index 4751394..13366a3 100644
--- a/src/solver.h
+++ b/src/solver.h
@@ -126,6 +126,7 @@ typedef struct solver {
Queue problems; /* index of conflicting rules, < 0 for job rules */
Queue recommendations; /* recommended packages */
Queue suggestions; /* suggested packages */
+ Queue orphaned; /* orphaned packages */
int stats_learned; /* statistic */
int stats_unsolvable; /* statistic */
@@ -157,6 +158,10 @@ typedef struct solver {
int ignorealreadyrecommended; /* true: ignore recommended packages that were already recommended by the installed packages */
int dontshowinstalledrecommended; /* true: do not show recommended packages that are already installed */
+ /* distupgrade also needs updatesystem and dosplitprovides */
+ int distupgrade;
+ int distupgrade_removeunsupported;
+
/* Callbacks for defining the bahaviour of the SAT solver */
/* Finding best candidate
@@ -208,24 +213,39 @@ typedef struct solver {
* queue commands
*/
-typedef enum {
- SOLVCMD_NULL=0,
- SOLVER_INSTALL_SOLVABLE,
- SOLVER_ERASE_SOLVABLE,
- SOLVER_INSTALL_SOLVABLE_NAME,
- SOLVER_ERASE_SOLVABLE_NAME,
- SOLVER_INSTALL_SOLVABLE_PROVIDES,
- SOLVER_ERASE_SOLVABLE_PROVIDES,
- SOLVER_INSTALL_SOLVABLE_UPDATE,
- SOLVER_INSTALL_SOLVABLE_ONE_OF,
- SOLVER_WEAKEN_SOLVABLE_DEPS,
- SOLVER_NOOBSOLETES_SOLVABLE,
- SOLVER_NOOBSOLETES_SOLVABLE_NAME,
- SOLVER_NOOBSOLETES_SOLVABLE_PROVIDES,
-
- /* flags */
- SOLVER_WEAK = 0x100,
-} SolverCmd;
+#define SOLVER_SOLVABLE 0x01
+#define SOLVER_SOLVABLE_NAME 0x02
+#define SOLVER_SOLVABLE_PROVIDES 0x03
+#define SOLVER_SOLVABLE_ONE_OF 0x04
+
+#define SOLVER_SELECTMASK 0xff
+
+#define SOLVER_INSTALL 0x0100
+#define SOLVER_ERASE 0x0200
+#define SOLVER_UPDATE 0x0300
+#define SOLVER_WEAKENDEPS 0x0400
+#define SOLVER_NOOBSOLETES 0x0500
+#define SOLVER_LOCK 0x0600
+
+#define SOLVER_JOBMASK 0xff00
+
+#define SOLVER_WEAK 0x010000
+
+/* old API compatibility, do not use in new code */
+#if 1
+#define SOLVER_INSTALL_SOLVABLE (SOLVER_INSTALL|SOLVER_SOLVABLE)
+#define SOLVER_ERASE_SOLVABLE (SOLVER_ERASE|SOLVER_SOLVABLE)
+#define SOLVER_INSTALL_SOLVABLE_NAME (SOLVER_INSTALL|SOLVER_SOLVABLE_NAME)
+#define SOLVER_ERASE_SOLVABLE_NAME (SOLVER_ERASE|SOLVER_SOLVABLE_NAME)
+#define SOLVER_INSTALL_SOLVABLE_PROVIDES (SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES)
+#define SOLVER_ERASE_SOLVABLE_PROVIDES (SOLVER_ERASE|SOLVER_SOLVABLE_PROVIDES)
+#define SOLVER_INSTALL_SOLVABLE_UPDATE (SOLVER_UPDATE|SOLVER_SOLVABLE)
+#define SOLVER_INSTALL_SOLVABLE_ONE_OF (SOLVER_INSTALL|SOLVER_SOLVABLE_ONE_OF)
+#define SOLVER_WEAKEN_SOLVABLE_DEPS (SOLVER_WEAKENDEPS|SOLVER_SOLVABLE)
+#define SOLVER_NOOBSOLETES_SOLVABLE (SOLVER_NOOBSOLETES|SOLVER_SOLVABLE)
+#define SOLVER_NOOBSOLETES_SOLVABLE_NAME (SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_NAME)
+#define SOLVER_NOOBSOLETES_SOLVABLE_PROVIDES (SOLVER_NOOBSOLETES|SOLVER_SOLVABLE_PROVIDES)
+#endif
typedef enum {
SOLVER_PROBLEM_UPDATE_RULE,
@@ -325,6 +345,13 @@ solver_create_state_maps(Solver *solv, Map *installedmap, Map *conflictsmap)
dp = !l ? &r->w2 : pool->whatprovidesdata + l, \
l = r->p; l; l = (dp != &r->w2 + 1 ? *dp++ : 0))
+#define FOR_JOB_SELECT(p, pp, select, what) \
+ for (pp = (select == SOLVER_SOLVABLE ? pool->whatprovidesdata : \
+ select == SOLVER_SOLVABLE_ONE_OF ? pool->whatprovidesdata + what : \
+ pool_whatprovides(pool, what)), \
+ p = (select == SOLVER_SOLVABLE ? what : *pp++) ; p ; p = *pp++) \
+ if (select != SOLVER_SOLVABLE_NAME || pool_match_nevr(pool, pool->solvables + p, what))
+
#ifdef __cplusplus
}
#endif
diff --git a/src/solverdebug.c b/src/solverdebug.c
index 84166de..2e314cf 100644
--- a/src/solverdebug.c
+++ b/src/solverdebug.c
@@ -18,6 +18,7 @@
#include <assert.h>
#include "solver.h"
+#include "solverdebug.h"
#include "bitmap.h"
#include "pool.h"
#include "util.h"
@@ -362,6 +363,19 @@ solver_printdecisions(Solver *solv)
}
POOL_DEBUG(SAT_DEBUG_RESULT, "\n");
}
+ if (solv->orphaned.count)
+ {
+ POOL_DEBUG(SAT_DEBUG_RESULT, "orphaned packages:\n");
+ for (i = 0; i < solv->orphaned.count; i++)
+ {
+ s = pool->solvables + solv->orphaned.elements[i];
+ if (solv->decisionmap[solv->orphaned.elements[i]] > 0)
+ POOL_DEBUG(SAT_DEBUG_RESULT, " %s (kept)\n", solvable2str(pool, s));
+ else
+ POOL_DEBUG(SAT_DEBUG_RESULT, " %s (erased)\n", solvable2str(pool, s));
+ }
+ POOL_DEBUG(SAT_DEBUG_RESULT, "\n");
+ }
}
void
@@ -427,7 +441,7 @@ solver_printsolutions(Solver *solv, Queue *job)
{
Pool *pool = solv->pool;
int pcnt;
- Id p, rp, how, what;
+ Id p, rp, how, what, select;
Id problem, solution, element;
Solvable *s, *sd;
@@ -451,37 +465,30 @@ solver_printsolutions(Solver *solv, Queue *job)
/* job, rp is index into job queue */
how = job->elements[rp - 1] & ~SOLVER_WEAK;
what = job->elements[rp];
- switch (how)
+ select = how & SOLVER_SELECTMASK;
+ switch (how & SOLVER_JOBMASK)
{
- case SOLVER_INSTALL_SOLVABLE:
- s = pool->solvables + what;
- if (solv->installed && s->repo == solv->installed)
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not keep %s installed\n", solvable2str(pool, s));
+ case SOLVER_INSTALL:
+ if (select == SOLVER_SOLVABLE && solv->installed && pool->solvables[what].repo == solv->installed)
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- do not keep %s installed\n", solvable2str(pool, pool->solvables + what));
+ else if (select == SOLVER_SOLVABLE_PROVIDES)
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- do not install a solvable %s\n", solver_select2str(solv, select, what));
else
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not install %s\n", solvable2str(pool, s));
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- do not install %s\n", solver_select2str(solv, select, what));
break;
- case SOLVER_ERASE_SOLVABLE:
- s = pool->solvables + what;
- if (solv->installed && s->repo == solv->installed)
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not deinstall %s\n", solvable2str(pool, s));
+ case SOLVER_ERASE:
+ if (select == SOLVER_SOLVABLE && !(solv->installed && pool->solvables[what].repo == solv->installed))
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- do not forbid installation of %s\n", solvable2str(pool, pool->solvables + what));
+ else if (select == SOLVER_SOLVABLE_PROVIDES)
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- do not deinstall all solvables %s\n", solver_select2str(solv, select, what));
else
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not forbid installation of %s\n", solvable2str(pool, s));
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- do not deinstall %s\n", solver_select2str(solv, select, what));
break;
- case SOLVER_INSTALL_SOLVABLE_NAME:
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not install %s\n", dep2str(pool, what));
+ case SOLVER_UPDATE:
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- do not install most recent version of %s\n", solver_select2str(solv, select, what));
break;
- case SOLVER_ERASE_SOLVABLE_NAME:
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not deinstall %s\n", dep2str(pool, what));
- break;
- case SOLVER_INSTALL_SOLVABLE_PROVIDES:
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not install a solvable providing %s\n", dep2str(pool, what));
- break;
- case SOLVER_ERASE_SOLVABLE_PROVIDES:
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not deinstall all solvables providing %s\n", dep2str(pool, what));
- break;
- case SOLVER_INSTALL_SOLVABLE_UPDATE:
- s = pool->solvables + what;
- POOL_DEBUG(SAT_DEBUG_RESULT, "- do not install most recent version of %s\n", solvable2str(pool, s));
+ case SOLVER_LOCK:
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- do not lock %s\n", solver_select2str(solv, select, what));
break;
default:
POOL_DEBUG(SAT_DEBUG_RESULT, "- do something different\n");
@@ -493,7 +500,11 @@ solver_printsolutions(Solver *solv, Queue *job)
/* policy, replace p with rp */
s = pool->solvables + p;
sd = rp ? pool->solvables + rp : 0;
- if (sd)
+ if (s == sd && solv->distupgrade)
+ {
+ POOL_DEBUG(SAT_DEBUG_RESULT, "- keep obsolete %s\n", solvable2str(pool, s));
+ }
+ else if (sd)
{
int gotone = 0;
if (!solv->allowdowngrade && evrcmp(pool, s->evr, sd->evr, EVRCMP_MATCH_RELEASE) > 0)
@@ -564,3 +575,37 @@ solver_printtrivial(Solver *solv)
queue_free(&in);
queue_free(&out);
}
+
+const char *
+solver_select2str(Solver *solv, Id select, Id what)
+{
+ Pool *pool = solv->pool;
+ const char *s;
+ char *b;
+ if (select == SOLVER_SOLVABLE)
+ return solvable2str(pool, pool->solvables + what);
+ if (select == SOLVER_SOLVABLE_NAME)
+ return dep2str(pool, what);
+ if (select == SOLVER_SOLVABLE_PROVIDES)
+ {
+ s = dep2str(pool, what);
+ b = pool_alloctmpspace(pool, 11 + strlen(s));
+ sprintf(b, "providing %s", s);
+ return b;
+ }
+ if (select == SOLVER_SOLVABLE_ONE_OF)
+ {
+ Id p;
+ char *b2;
+ b = "";
+ while ((p = pool->whatprovidesdata[what++]) != 0)
+ {
+ s = solvable2str(pool, pool->solvables + p);
+ b2 = pool_alloctmpspace(pool, strlen(b) + strlen(s) + 3);
+ sprintf(b2, "%s, %s", b, s);
+ b = b2;
+ }
+ return *b ? b + 2 : "nothing";
+ }
+ return "unknown job select";
+}
diff --git a/src/solverdebug.h b/src/solverdebug.h
index cb90fdb..2b8493b 100644
--- a/src/solverdebug.h
+++ b/src/solverdebug.h
@@ -27,6 +27,8 @@ extern void solver_printdecisions(Solver *solv);
extern void solver_printprobleminfo(Solver *solv, Queue *job, Id problem);
extern void solver_printsolutions(Solver *solv, Queue *job);
extern void solver_printtrivial(Solver *solv);
+extern const char *solver_select2str(Solver *solv, Id select, Id what);
+
#endif /* SATSOLVER_SOLVERDEBUG_H */