static const Id SOLVER_DROP_ORPHANED = SOLVER_DROP_ORPHANED;
static const Id SOLVER_USERINSTALLED = SOLVER_USERINSTALLED;
static const Id SOLVER_ALLOWUNINSTALL = SOLVER_ALLOWUNINSTALL;
+ static const Id SOLVER_FAVOR = SOLVER_FAVOR;
+ static const Id SOLVER_DISFAVOR = SOLVER_DISFAVOR;
static const Id SOLVER_JOBMASK = SOLVER_JOBMASK;
static const Id SOLVER_WEAK = SOLVER_WEAK;
static const Id SOLVER_ESSENTIAL = SOLVER_ESSENTIAL;
'\" t
.\" Title: Libsolv-Bindings
.\" Author: [see the "Author" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.0 <http://docbook.sf.net/>
-.\" Date: 12/14/2015
+.\" Generator: DocBook XSL Stylesheets v1.78.1 <http://docbook.sf.net/>
+.\" Date: 05/03/2016
.\" Manual: LIBSOLV
.\" Source: libsolv
.\" Language: English
.\"
-.TH "LIBSOLV\-BINDINGS" "3" "12/14/2015" "libsolv" "LIBSOLV"
+.TH "LIBSOLV\-BINDINGS" "3" "05/03/2016" "libsolv" "LIBSOLV"
.\" -----------------------------------------------------------------
.\" * Define some portability stuff
.\" -----------------------------------------------------------------
Allow the solver to deinstall the matching installed packages if they get into the way of resolving a dependency\&. This is like the SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages\&.
.RE
.PP
+\fBSOLVER_FAVOR\fR
+.RS 4
+Prefer the specified packages if the solver encounters an alternative\&. If a job contains multiple matching favor/disfavor elements, the last one takes precedence\&.
+.RE
+.PP
+\fBSOLVER_DISFAVOR\fR
+.RS 4
+Avoid the specified packages if the solver encounters an alternative\&. This can also be used to block recommended or supplemented packages from being installed\&.
+.RE
+.PP
\fBSOLVER_JOBMASK\fR
.RS 4
A mask containing all the above action bits\&.
into the way of resolving a dependency. This is like the
SOLVER_FLAG_ALLOW_UNINSTALL flag, but limited to a specific set of packages.
+*SOLVER_FAVOR*::
+Prefer the specified packages if the solver encounters an alternative. If
+a job contains multiple matching favor/disfavor elements, the last one takes
+precedence.
+
+*SOLVER_DISFAVOR*::
+Avoid the specified packages if the solver encounters an alternative. This
+can also be used to block recommended or supplemented packages from being
+installed.
+
*SOLVER_JOBMASK*::
A mask containing all the above action bits.
{ SOLVER_DROP_ORPHANED, "droporphaned" },
{ SOLVER_USERINSTALLED, "userinstalled" },
{ SOLVER_ALLOWUNINSTALL, "allowuninstall" },
+ { SOLVER_FAVOR, "favor" },
+ { SOLVER_DISFAVOR, "disfavor" },
{ 0, 0 }
};
}
}
+static int
+sort_by_favorq_cmp(const void *ap, const void *bp, void *dp)
+{
+ const Id *a = ap, *b = bp, *d = dp;
+ return d[b[0]] - d[a[0]];
+}
+
+static void
+sort_by_favorq(Queue *favorq, Id *el, int cnt)
+{
+ int i;
+ /* map to offsets into favorq */
+ for (i = 0; i < cnt; i++)
+ {
+ Id p = el[i];
+ /* lookup p in sorted favorq */
+ int med = 0, low = 0;
+ int high = favorq->count / 2;
+ while (low != high)
+ {
+ med = (low + high) / 2;
+ Id pp = favorq->elements[2 * med];
+ if (pp < p)
+ low = med;
+ else if (pp > p)
+ high = med;
+ else
+ break;
+ }
+ while(med && favorq->elements[2 * med - 2] == p)
+ med--;
+ if (favorq->elements[2 * med] == p)
+ el[i] = 2 * med + 1;
+ else
+ el[i] = 0; /* hmm */
+ }
+ /* sort by position */
+ solv_sort(el, cnt, sizeof(Id), sort_by_favorq_cmp, favorq->elements);
+ /* map back */
+ for (i = 0; i < cnt; i++)
+ if (el[i])
+ el[i] = favorq->elements[el[i] - 1];
+}
+
+/* bring favored packages to front and disfavored packages to back */
+static void
+prefer_favored(Solver *solv, Queue *plist)
+{
+ int i, fav, disfav, count;
+ if (!solv->favormap.size)
+ return;
+ for (i = fav = disfav = 0, count = plist->count; i < count; i++)
+ {
+ Id p = plist->elements[i];
+ if (!MAPTST(&solv->favormap, p))
+ continue;
+ if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, p))
+ {
+ /* disfavored package. bring to back */
+ if (i < plist->count - 1)
+ {
+ memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id));
+ plist->elements[plist->count - 1] = p;
+ }
+ i--;
+ count--;
+ disfav++;
+ }
+ else
+ {
+ /* favored package. bring to front */
+ if (i > fav)
+ memmove(plist->elements + fav + 1, plist->elements + fav, (i - fav) * sizeof(Id));
+ plist->elements[fav++] = p;
+ }
+ }
+ /* if we have multiple favored/disfavored packages, sort by favorq index */
+ if (fav > 1)
+ sort_by_favorq(solv->favorq, plist->elements, fav);
+ if (disfav > 1)
+ sort_by_favorq(solv->favorq, plist->elements + plist->count - disfav, disfav);
+}
+
/*
* prune to recommended/suggested packages.
* does not prune installed packages (they are also somewhat recommended).
policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
{
Pool *pool = solv->pool;
+ if (mode == POLICY_MODE_SUPPLEMENT)
+ {
+ /* reorder only */
+ dislike_old_versions(pool, plist);
+ sort_by_common_dep(pool, plist);
+ prefer_suggested(solv, plist);
+ prefer_favored(solv, plist);
+ return;
+ }
if (plist->count > 1)
{
if (mode != POLICY_MODE_SUGGEST)
dislike_old_versions(pool, plist);
sort_by_common_dep(pool, plist);
prefer_suggested(solv, plist);
+ prefer_favored(solv, plist);
}
}
}
#define POLICY_MODE_RECOMMEND 1
#define POLICY_MODE_SUGGEST 2
#define POLICY_MODE_CHOOSE_NOREORDER 3 /* internal, do not use */
+#define POLICY_MODE_SUPPLEMENT 4 /* internal, do not use */
#define POLICY_ILLEGAL_DOWNGRADE 1
queuep_free(&solv->recommendscplxq);
queuep_free(&solv->suggestscplxq);
queuep_free(&solv->brokenorphanrules);
+ queuep_free(&solv->favorq);
map_free(&solv->recommendsmap);
map_free(&solv->suggestsmap);
map_free(&solv->droporphanedmap);
map_free(&solv->cleandepsmap);
map_free(&solv->allowuninstallmap);
+ map_free(&solv->favormap);
+ map_free(&solv->isdisfavormap);
solv_free(solv->decisionmap);
solv_free(solv->rules);
#endif
+static void
+prune_disfavored(Solver *solv, Queue *plist)
+{
+ int i, j;
+ if (!solv->isdisfavormap.size)
+ return;
+ for (i = j = 0; i < plist->count; i++)
+ {
+ Id p = plist->elements[i];
+ if (!MAPTST(&solv->isdisfavormap, p))
+ plist->elements[j++] = p;
+ }
+ if (i != j)
+ queue_truncate(plist, j);
+}
+
/*-------------------------------------------------------------------
*
* solver_run_sat
continue;
if (solv->dupmap_all && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start))))
continue;
+ if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, i))
+ continue; /* disfavored supplements, do not install */
queue_push(&dqs, i);
}
}
+ /* filter out disfavored recommended packages */
+ if (dq.count && solv->isdisfavormap.size)
+ prune_disfavored(solv, &dq);
+
/* filter out all packages obsoleted by installed packages */
/* this is no longer needed if we have reverse obsoletes */
if ((dqs.count || dq.count) && solv->installed)
for (i = 0; i < dq.count; i++)
MAPSET(&dqmap, dq.elements[i]);
- /* install all supplemented packages */
+ /* prune dqs so that it only contains the best versions */
+ for (i = j = 0; i < dqs.count; i++)
+ {
+ p = dqs.elements[i];
+ if (MAPTST(&dqmap, p))
+ dqs.elements[j++] = p;
+ }
+ dqs.count = j;
+
+ /* install all supplemented packages, but order first */
+ if (dqs.count > 1)
+ policy_filter_unwanted(solv, &dqs, POLICY_MODE_SUPPLEMENT);
for (i = 0; i < dqs.count; i++)
{
p = dqs.elements[i];
- if (solv->decisionmap[p] || !MAPTST(&dqmap, p))
+ if (solv->decisionmap[p])
continue;
POOL_DEBUG(SOLV_DEBUG_POLICY, "installing supplemented %s\n", pool_solvid2str(pool, p));
olevel = level;
}
#endif
+/* sort by package id, last entry wins */
+static int
+setup_favormaps_cmp(const void *ap, const void *bp, void *dp)
+{
+ const Id *a = ap, *b = bp;
+ if ((*a - *b) != 0)
+ return *a - *b;
+ return (b[1] < 0 ? -b[1] : b[1]) - (a[1] < 0 ? -a[1] : a[1]);
+}
+
+static void
+setup_favormaps(Solver *solv)
+{
+ Queue *q = solv->favorq;
+ Pool *pool = solv->pool;
+ int i;
+ Id oldp = 0;
+ if (q->count > 2)
+ solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), setup_favormaps_cmp, solv);
+ map_grow(&solv->favormap, pool->nsolvables);
+ for (i = 0; i < q->count; i += 2)
+ {
+ Id p = q->elements[i];
+ if (p == oldp)
+ continue;
+ oldp = p;
+ MAPSET(&solv->favormap, p);
+ if (q->elements[i + 1] < 0)
+ {
+ if (!solv->isdisfavormap.size)
+ map_grow(&solv->isdisfavormap, pool->nsolvables);
+ MAPSET(&solv->isdisfavormap, p);
+ }
+ }
+}
+
/*
*
* solve job queue
map_zerosize(&solv->allowuninstallmap);
map_zerosize(&solv->cleandepsmap);
map_zerosize(&solv->weakrulemap);
+ map_zerosize(&solv->favormap);
+ map_zerosize(&solv->isdisfavormap);
queue_empty(&solv->weakruleq);
solv->watches = solv_free(solv->watches);
queue_empty(&solv->ruletojob);
case SOLVER_ALLOWUNINSTALL:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: allowuninstall %s\n", solver_select2str(pool, select, what));
break;
+ case SOLVER_FAVOR:
+ case SOLVER_DISFAVOR:
+ POOL_DEBUG(SOLV_DEBUG_JOB, "job: %s %s\n", (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? "favor" : "disfavor", solver_select2str(pool, select, what));
+ FOR_JOB_SELECT(p, pp, select, what)
+ {
+ int j;
+ if (!solv->favorq)
+ {
+ solv->favorq = solv_calloc(1, sizeof(Queue));
+ queue_init(solv->favorq);
+ }
+ j = solv->favorq->count + 1;
+ queue_push2(solv->favorq, p, (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? j : -j);
+ }
+ break;
default:
POOL_DEBUG(SOLV_DEBUG_JOB, "job: unknown job\n");
break;
assert(solv->ruletojob.count == solv->nrules - solv->jobrules);
solv->jobrules_end = solv->nrules;
+ /* transform favorq into two maps */
+ if (solv->favorq)
+ setup_favormaps(solv);
+
/* now create infarch and dup rules */
if (!solv->noinfarchcheck)
{
case SOLVER_ALLOWUNINSTALL:
strstart = "allow deinstallation of ";
break;
+ case SOLVER_FAVOR:
+ strstart = "favor ";
+ break;
+ case SOLVER_DISFAVOR:
+ strstart = "disfavor ";
+ break;
default:
strstart = "unknown job ";
break;
Map allowuninstallmap; /* ok to uninstall those */
int allowuninstall_all;
+ Queue *favorq;
+ Map favormap; /* favored / disfavored packages */
+ Map isdisfavormap;
#endif /* LIBSOLV_INTERNAL */
};
#define SOLVER_DROP_ORPHANED 0x0900
#define SOLVER_USERINSTALLED 0x0a00
#define SOLVER_ALLOWUNINSTALL 0x0b00
+#define SOLVER_FAVOR 0x0c00
+#define SOLVER_DISFAVOR 0x0d00
#define SOLVER_JOBMASK 0xff00
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: A 1 1 noarch
+#>=Rec: X
+#>=Pkg: B 1 1 noarch
+#>=Prv: X
+#>=Pkg: C 1 1 noarch
+#>=Prv: X
+system unset * system
+
+# first favor B
+job install name A
+job favor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+# then favor C
+nextjob
+job install name A
+job favor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+# check disfavor
+nextjob
+job install name A
+job disfavor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+nextjob
+job install name A
+job disfavor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+# check disfavor both, this is different from
+# the requires case
+
+nextjob
+job install name A
+job disfavor name B
+job disfavor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: A 1 1 noarch
+#>=Req: X
+#>=Pkg: B 1 1 noarch
+#>=Prv: X
+#>=Pkg: C 1 1 noarch
+#>=Prv: X
+system unset * system
+
+# first favor B
+job install name A
+job favor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+# then favor C
+nextjob
+job install name A
+job favor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+
+# if both are favored, the last one wins
+nextjob
+job install name A
+job favor name C
+job favor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+nextjob
+job install name A
+job favor name B
+job favor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+# now test disfavor
+
+# first disfavor B
+nextjob
+job install name A
+job disfavor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+# then disfavor C
+nextjob
+job install name A
+job disfavor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+# then both
+nextjob
+job install name A
+job disfavor name B
+job disfavor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+nextjob
+job install name A
+job disfavor name C
+job disfavor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+# then test combination
+nextjob
+job install name A
+job favor name B
+job disfavor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+nextjob
+job install name A
+job disfavor name B
+job favor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+nextjob
+job install name A
+job favor name C
+job disfavor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+nextjob
+job install name A
+job disfavor name C
+job favor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
--- /dev/null
+repo system 0 empty
+repo test 0 testtags <inline>
+#>=Ver: 2.0
+#>=Pkg: A 1 1 noarch
+#>=Pkg: B 1 1 noarch
+#>=Sup: A
+#>=Pkg: C 1 1 noarch
+#>=Sup: A
+#>=Pkg: A2 1 1 noarch
+#>=Pkg: B2 1 1 noarch
+#>=Sup: A2
+#>=Pkg: C2 1 1 noarch
+#>=Sup: A2
+#>=Con: B2
+system unset * system
+
+# first favor B
+job install name A
+job favor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+# then favor C
+nextjob
+job install name A
+job favor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+# same with A2 where B2 and C2 conflict
+
+nextjob
+job install name A2
+job favor name B2
+result transaction,problems <inline>
+#>install A2-1-1.noarch@test
+#>install B2-1-1.noarch@test
+
+nextjob
+job install name A2
+job favor name C2
+result transaction,problems <inline>
+#>install A2-1-1.noarch@test
+#>install C2-1-1.noarch@test
+
+
+# check disfavor
+nextjob
+job install name A
+job disfavor name B
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install C-1-1.noarch@test
+
+nextjob
+job install name A
+job disfavor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test
+#>install B-1-1.noarch@test
+
+nextjob
+job install name A
+job disfavor name B
+job disfavor name C
+result transaction,problems <inline>
+#>install A-1-1.noarch@test