From 607511873490736fafca6dd9081fba5e3fce3e87 Mon Sep 17 00:00:00 2001 From: Michael Schroeder Date: Fri, 25 Nov 2022 11:36:19 +0100 Subject: [PATCH] Move decisionlist generation into the library This functionality is useful for multiple programs that use libsolv, so it does not make sense to duplicate the code into each of them. So we now offer solver_get_decisionlist() to get a list of decisions that let to the installation/deinstallation of a package. There is also solver_get_decisionlist_multiple() that can be used if multiple packages should be considered. The output is a list of (package, reason, info) triplets. Package is positive for installs, negative for conflicts. This commits also deprecates solver_describe_weakdep_decision(). it makes more sense to unify weakdep decisions with rule decisions as both come from package dependencies. So we now have solver_allweakdepinfos() and solver_weakdepinfo() that returns the ruleinfo for a weakdep decision. You can then use solver_ruleinfo2str() to convert the ruleinfo to a string. --- src/libsolv.ver | 4 ++ src/rules.c | 5 ++ src/rules.h | 5 ++ src/solver.c | 169 +++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 173 insertions(+), 10 deletions(-) diff --git a/src/libsolv.ver b/src/libsolv.ver index cf257610..3d175211 100644 --- a/src/libsolv.ver +++ b/src/libsolv.ver @@ -342,6 +342,7 @@ SOLV_1.0 { solvable_trivial_installable_repo; solvable_unset; solver_allruleinfos; + solver_allweakdepinfos; solver_alternative2str; solver_alternatives_count; solver_calc_duchanges; @@ -360,6 +361,8 @@ SOLV_1.0 { solver_get_alternative; solver_get_decisionblock; solver_get_decisionlevel; + solver_get_decisionlist; + solver_get_decisionlist_multiple; solver_get_decisionqueue; solver_get_flag; solver_get_lastdecisionblocklevel; @@ -411,6 +414,7 @@ SOLV_1.0 { solver_take_solutionelement; solver_trivial_installable; solver_unifyrules; + solver_weakdepinfo; stringpool_clone; stringpool_free; stringpool_freehash; diff --git a/src/rules.c b/src/rules.c index 61833c10..1b0bf48d 100644 --- a/src/rules.c +++ b/src/rules.c @@ -4375,6 +4375,11 @@ solver_ruleinfo2str(Solver *solv, SolverRuleinfo type, Id source, Id target, Id case SOLVER_RULE_PKG_CONSTRAINS: s = pool_tmpappend(pool, pool_solvid2str(pool, source), " has constraint ", pool_dep2str(pool, dep)); return pool_tmpappend(pool, s, " conflicting with ", pool_solvid2str(pool, target)); + case SOLVER_RULE_PKG_SUPPLEMENTS: + s = pool_tmpjoin(pool, pool_solvid2str(pool, source), " supplements ", pool_dep2str(pool, dep)); + if (target) + s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, target)); + return s; case SOLVER_RULE_YUMOBS: s = pool_tmpjoin(pool, "both ", pool_solvid2str(pool, source), " and "); s = pool_tmpjoin(pool, s, pool_solvid2str(pool, target), " obsolete "); diff --git a/src/rules.h b/src/rules.h index 98709b23..92efe8a7 100644 --- a/src/rules.h +++ b/src/rules.h @@ -60,6 +60,7 @@ typedef enum { SOLVER_RULE_PKG_INSTALLED_OBSOLETES, SOLVER_RULE_PKG_RECOMMENDS, SOLVER_RULE_PKG_CONSTRAINS, + SOLVER_RULE_PKG_SUPPLEMENTS, SOLVER_RULE_UPDATE = 0x200, SOLVER_RULE_FEATURE = 0x300, SOLVER_RULE_JOB = 0x400, @@ -163,6 +164,10 @@ extern void solver_rule2rules(struct s_Solver *solv, Id rid, Queue *q, int recur extern Id solver_rule2pkgrule(struct s_Solver *solv, Id rid); extern const char *solver_ruleinfo2str(struct s_Solver *solv, SolverRuleinfo type, Id source, Id target, Id dep); +/* rule infos for weakdep decisions */ +extern int solver_allweakdepinfos(struct s_Solver *solv, Id p, Queue *rq); +extern SolverRuleinfo solver_weakdepinfo(struct s_Solver *solv, Id p, Id *fromp, Id *top, Id *depp); + /* orphan handling */ extern void solver_breakorphans(struct s_Solver *solv); extern void solver_check_brokenorphanrules(struct s_Solver *solv, Queue *dq); diff --git a/src/solver.c b/src/solver.c index 42498fa2..468cbcb8 100644 --- a/src/solver.c +++ b/src/solver.c @@ -4662,7 +4662,7 @@ solver_describe_decision(Solver *solv, Id p, Id *infop) return SOLVER_REASON_UNRELATED; why = solv->decisionq_why.elements[i]; if (infop) - *infop = why > 0 ? why : -why; + *infop = why >= 0 ? why : -why; if (why > 0) return SOLVER_REASON_UNIT_RULE; i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p]; @@ -4670,8 +4670,8 @@ solver_describe_decision(Solver *solv, Id p, Id *infop) } -void -solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) +int +solver_allweakdepinfos(Solver *solv, Id p, Queue *whyq) { Pool *pool = solv->pool; int i; @@ -4681,15 +4681,15 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) queue_empty(whyq); if (level < 0) - return; /* huh? */ + return 0; /* huh? */ for (decisionno = 0; decisionno < solv->decisionq.count; decisionno++) if (solv->decisionq.elements[decisionno] == p) break; if (decisionno == solv->decisionq.count) - return; /* huh? */ + return 0; /* huh? */ i = solv->decisionmap[p] >= 0 ? solv->decisionmap[p] : -solv->decisionmap[p]; if (solv->decisionq_reason.elements[i] != SOLVER_REASON_WEAKDEP) - return; /* huh? */ + return 0; /* huh? */ /* 1) list all packages that recommend us */ for (i = 1; i < pool->nsolvables; i++) @@ -4719,8 +4719,8 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) } if (!p2 && found) { - queue_push(whyq, SOLVER_REASON_RECOMMENDED); - queue_push2(whyq, i, rec); + queue_push2(whyq, SOLVER_RULE_PKG_RECOMMENDS, i); + queue_push2(whyq, 0, rec); } } } @@ -4752,7 +4752,7 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) } if (solv->decisionmap[p2] > 0 && solv->decisionmap[p2] < level) { - queue_push(whyq, SOLVER_REASON_SUPPLEMENTED); + queue_push2(whyq, SOLVER_RULE_PKG_SUPPLEMENTS, p); queue_push2(whyq, p2, sup); found = 1; } @@ -4760,7 +4760,7 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) if (!found) { /* hard case, just note dependency with no package */ - queue_push(whyq, SOLVER_REASON_SUPPLEMENTED); + queue_push2(whyq, SOLVER_RULE_PKG_SUPPLEMENTS, p); queue_push2(whyq, 0, sup); } } @@ -4771,6 +4771,155 @@ solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) solv->decisionmap[p2] = -solv->decisionmap[p2]; } } + return whyq->count / 4; +} + +SolverRuleinfo +solver_weakdepinfo(Solver *solv, Id p, Id *fromp, Id *top, Id *depp) +{ + Queue iq; + queue_init(&iq); + solver_allweakdepinfos(solv, p, &iq); + if (fromp) + *fromp = iq.count ? iq.elements[1] : 0; + if (top) + *top = iq.count ? iq.elements[2] : 0; + if (depp) + *depp = iq.count ? iq.elements[3] : 0; + return iq.count ? iq.elements[0] : SOLVER_RULE_UNKNOWN; +} + +/* deprecated, use solver_allweakdepinfos instead */ +void +solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq) +{ + int i, j; + solver_allweakdepinfos(solv, p, whyq); + for (i = j = 0; i < whyq->count; i += 4) + { + if (whyq->elements[i] == SOLVER_RULE_PKG_RECOMMENDS) + { + whyq->elements[j++] = SOLVER_REASON_RECOMMENDED; + whyq->elements[j++] = whyq->elements[i + 1]; + whyq->elements[j++] = whyq->elements[i + 3]; + } + else if (whyq->elements[i] == SOLVER_RULE_PKG_SUPPLEMENTS) + { + whyq->elements[j++] = SOLVER_REASON_SUPPLEMENTED; + whyq->elements[j++] = whyq->elements[i + 2]; + whyq->elements[j++] = whyq->elements[i + 3]; + } + } + queue_truncate(whyq, j); +} + +static void +getdecisionlist(Solver *solv, Map *dm, Queue *decisionlistq) +{ + Pool *pool = solv->pool; + int i, ii, reason, info; + Queue iq; + + queue_empty(decisionlistq); + queue_init(&iq); + for (ii = solv->decisionq.count - 1; ii >= 0; ii--) + { + Id v = solv->decisionq.elements[ii]; + Id vv = (v > 0 ? v : -v); + if (!MAPTST(dm, vv)) + continue; + info = solv->decisionq_why.elements[ii]; + if (info > 0) + reason = SOLVER_REASON_UNIT_RULE; + else if (info <= 0) + { + info = -info; + reason = solv->decisionmap[vv]; + reason = solv->decisionq_reason.elements[reason >= 0 ? reason : -reason]; + } + queue_unshift(decisionlistq, info); + queue_unshift(decisionlistq, reason); + queue_unshift(decisionlistq, v); + switch (reason) + { + case SOLVER_REASON_WEAKDEP: + if (v <= 0) + break; + solver_allweakdepinfos(solv, v, &iq); + for (i = 0; i < iq.count; i += 4) + { + if (iq.elements[i + 1]) + MAPSET(dm, iq.elements[i + 1]); + if (iq.elements[i + 2]) + MAPSET(dm, iq.elements[i + 2]); + else if (iq.elements[i] == SOLVER_RULE_PKG_SUPPLEMENTS) + { + Id p2, pp2, id = iq.elements[i + 3]; + FOR_PROVIDES(p2, pp2, id) + if (solv->decisionmap[p2] > 0) + MAPSET(dm, p2); + } + } + break; + case SOLVER_REASON_RESOLVE_JOB: + case SOLVER_REASON_UNIT_RULE: + case SOLVER_REASON_RESOLVE: + solver_ruleliterals(solv, info, &iq); + for (i = 0; i < iq.count; i++) + { + Id p2 = iq.elements[i]; + if (p2 < 0) + MAPSET(dm, -p2); + else if (solv->decisionmap[p2] > 0) + MAPSET(dm, p2); + } + break; + default: + break; + } + } + queue_free(&iq); +} + +void +solver_get_decisionlist(Solver *solv, Id p, Queue *decisionlistq) +{ + Pool *pool = solv->pool; + Map dm; + map_init(&dm, pool->nsolvables); + MAPSET(&dm, p); + getdecisionlist(solv, &dm, decisionlistq); + if (!decisionlistq->count) + { + queue_push(decisionlistq, -p); + queue_push2(decisionlistq, SOLVER_REASON_UNRELATED, 0); + } + map_free(&dm); +} + +void +solver_get_decisionlist_multiple(Solver *solv, Queue *pq, Queue *decisionlistq) +{ + Pool *pool = solv->pool; + int i; + Map dm; + map_init(&dm, pool->nsolvables); + for (i = 0; i < pq->count; i++) + { + Id p = pq->elements[i]; + if (solv->decisionmap[p] != 0) + MAPSET(&dm, p); + } + getdecisionlist(solv, &dm, decisionlistq); + for (i = 0; i < pq->count; i++) + { + Id p = pq->elements[i]; + if (solv->decisionmap[p] != 0) + continue; + queue_push(decisionlistq, -p); + queue_push2(decisionlistq, SOLVER_REASON_UNRELATED, 0); + } + map_free(&dm); } void -- 2.47.2