From: Michael Schroeder Date: Fri, 18 Nov 2022 14:05:01 +0000 (+0100) Subject: Add -W option to show why the solver installed/erased the specified package X-Git-Tag: 0.7.23~44 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=958f993dc05a017ce3727ec7973924625dc49258;p=thirdparty%2Flibsolv.git Add -W option to show why the solver installed/erased the specified package This shows everything that led to the decision. --- diff --git a/tools/testsolv.c b/tools/testsolv.c index 9d5d28b9..16189da5 100644 --- a/tools/testsolv.c +++ b/tools/testsolv.c @@ -76,6 +76,162 @@ free_considered(Pool *pool) } } +void +showwhy(Solver *solv, const char *showwhypkgstr) +{ + Pool *pool = solv->pool; + Queue dq, whyq, rq, riq; + int reason, info; + Map dm; + Id showwhypkg; + int ii, i; + + map_init(&dm, pool->nsolvables); + queue_init(&dq); + queue_init(&whyq); + queue_init(&rq); + queue_init(&riq); + + showwhypkg = testcase_str2solvid(pool, showwhypkgstr); + if (showwhypkg) + MAPSET(&dm, showwhypkg); + else + { + int selflags = SELECTION_NAME | SELECTION_CANON; + selection_make(pool, &dq, showwhypkgstr, selflags); + selection_solvables(pool, &dq, &whyq); + if (!whyq.count) + printf("No package matches %s\n", showwhypkgstr); + for (i = 0; i < whyq.count; i++) + MAPSET(&dm, whyq.elements[i]); + queue_empty(&dq); + queue_empty(&whyq); + } + solver_get_decisionqueue(solv, &dq); + for (ii = dq.count - 1; ii >= 0; ii--) + { + Id v = dq.elements[ii]; + const char *action = v > 0 ? "installed" : "conflicted"; + Id vv = (v > 0 ? v : -v); + int jobidx; + char *prefix; + int prefixlen; + + if (!MAPTST(&dm, vv)) + continue; + reason = solver_describe_decision(solv, vv, &info); + if (SOLVER_REASON_UNRELATED) + continue; + prefix = pool_tmpjoin(pool, action, " ", testcase_solvid2str(pool, vv)); + printf("%s ", prefix); + prefixlen = strlen(prefix) + 1; + if (SOLVER_REASON_WEAKDEP && v > 0) + { + solver_describe_weakdep_decision(solv, vv, &whyq); + if (whyq.count) + { + Id p2, pp2, id; + for (i = 0; i < whyq.count; i += 3) + { + if (i != 0) + printf("%*s", prefixlen, "and "); + reason = whyq.elements[i]; + p2 = whyq.elements[i + 1]; + id = whyq.elements[i + 2]; + if (reason == SOLVER_REASON_RECOMMENDED) + { + printf("because %s recommends %s\n", testcase_solvid2str(pool, p2), pool_dep2str(pool, id)); + MAPSET(&dm, p2); + } + else if (reason == SOLVER_REASON_SUPPLEMENTED) + { + if (p2) + { + printf("because it supplements %s provided by %s\n", pool_dep2str(pool, id), testcase_solvid2str(pool, p2)); + MAPSET(&dm, p2); + } + else + { + printf("because it supplements %s\n", pool_dep2str(pool, id)); + FOR_PROVIDES(p2, pp2, id) + if (solver_get_decisionlevel(solv, p2) > 0) + MAPSET(&dm, p2); + } + } + else + printf("because of some weak dependency\n"); + } + continue; + } + } + switch(reason) + { + case SOLVER_REASON_UNIT_RULE: + case SOLVER_REASON_RESOLVE: + solver_ruleliterals(solv, info, &rq); + for (i = 0; i < rq.count; i++) + { + Id p2 = rq.elements[i] > 0 ? rq.elements[i] : -rq.elements[i]; + MAPSET(&dm, p2); + } + solver_allruleinfos(solv, info, &riq); + if (!riq.count) + printf("because of some rule\n"); + for (i = 0; i < riq.count; i += 4) + { + if (i != 0) + printf("%*s", prefixlen, "and "); + if (riq.elements[i] == SOLVER_RULE_LEARNT) + { + printf("because of a learnt rule:\n"); + for (i = 0; i < rq.count; i++) + { + Id p2 = rq.elements[i]; + printf(" %c %s\n", p2 > 0 ? '+' : '-', testcase_solvid2str(pool, p2 > 0 ? p2 : -p2)); + } + continue; + } + printf("because %s\n", solver_ruleinfo2str(solv, riq.elements[i], riq.elements[i + 1], riq.elements[i + 2], riq.elements[i + 3])); + } + break; + case SOLVER_REASON_KEEP_INSTALLED: + printf("because we want to keep it installed\n"); + break; + case SOLVER_REASON_RESOLVE_JOB: + solver_ruleliterals(solv, info, &rq); + for (i = 0; i < rq.count; i++) + { + Id p2 = rq.elements[i]; + if (p2 > 0 && solver_get_decisionlevel(solv, p2) < 0) + MAPSET(&dm, p2); + } + jobidx = solver_rule2jobidx(solv, info); + printf("because a job was to %s\n", pool_job2str(pool, solv->job.elements[jobidx], solv->job.elements[jobidx + 1], 0)); + break; + case SOLVER_REASON_UPDATE_INSTALLED: + printf("because we want to update/keep it\n"); + break; + case SOLVER_REASON_CLEANDEPS_ERASE: + printf("because we want to cleandeps erase it\n"); + break; + case SOLVER_REASON_WEAKDEP: + printf("because of a weak dependency\n"); + break; + case SOLVER_REASON_RESOLVE_ORPHAN: + printf("because it is orphaned\n"); + break; + default: + printf("because of some reason\n"); + break; + } + } + queue_free(&riq); + queue_free(&rq); + queue_free(&whyq); + queue_free(&dq); + map_free(&dm); +} + int main(int argc, char **argv) { @@ -84,6 +240,7 @@ main(int argc, char **argv) Queue solq; Solver *solv, *reusesolv = 0; char *result = 0; + char *showwhypkgstr = 0; int resultflags = 0; int debuglevel = 0; int writeresult = 0; @@ -98,7 +255,7 @@ main(int argc, char **argv) const char *p; queue_init(&solq); - while ((c = getopt(argc, argv, "vmrhL:l:s:T:")) >= 0) + while ((c = getopt(argc, argv, "vmrhL:l:s:T:W:")) >= 0) { switch (c) { @@ -122,6 +279,9 @@ main(int argc, char **argv) list = optarg; list_with_deps = 1; break; + case 'W': + showwhypkgstr = optarg; + break; case 's': if ((p = strchr(optarg, ':'))) queue_push2(&solq, atoi(optarg), atoi(p + 1)); @@ -228,6 +388,11 @@ main(int argc, char **argv) queue_free(&q); } } + else if (showwhypkgstr) + { + solver_solve(solv, &job); + showwhy(solv, showwhypkgstr); + } else if (result || writeresult) { char *myresult, *resultdiff;