]> git.ipfire.org Git - thirdparty/libsolv.git/commitdiff
Add -W option to show why the solver installed/erased the specified package
authorMichael Schroeder <mls@suse.de>
Fri, 18 Nov 2022 14:05:01 +0000 (15:05 +0100)
committerMichael Schroeder <mls@suse.de>
Fri, 18 Nov 2022 14:05:01 +0000 (15:05 +0100)
This shows everything that led to the decision.

tools/testsolv.c

index 9d5d28b9f75da79693411db3813efcdbeb5d3a0e..16189da516f9dde2c789301f3a1879915d7a57e4 100644 (file)
@@ -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;