]> git.ipfire.org Git - thirdparty/libsolv.git/commitdiff
Add support for decisioninfo merging
authorMichael Schroeder <mls@suse.de>
Tue, 6 Dec 2022 14:24:32 +0000 (15:24 +0100)
committerMichael Schroeder <mls@suse.de>
Tue, 6 Dec 2022 14:24:32 +0000 (15:24 +0100)
This is not 100% fixed, the names might still change
if I come up with something better.
Also add code to stringify the decision reason.

src/decision.c
src/libsolv.ver
src/solver.h

index 47a6821530d175edfd3bb8135553e60466932b5e..8e8d9c0ff213a1e5ef6a72dc624f564022ee1f77 100644 (file)
@@ -704,3 +704,242 @@ solver_get_decisionlist_multiple(Solver *solv, Queue *idq, int flags, Queue *dec
   map_free(&dm);
 }
 
+
+const char *
+solver_reason2str(Solver *solv, int reason)
+{
+  switch(reason)
+    {
+    case SOLVER_REASON_WEAKDEP:
+      return "a weak dependency";
+    case SOLVER_REASON_RESOLVE_JOB:
+      return "a job rule";
+    case SOLVER_REASON_RESOLVE:
+      return "a rule";
+    case SOLVER_REASON_UNIT_RULE:
+      return "an unit rule";
+    case SOLVER_REASON_KEEP_INSTALLED:
+      return "update/keep installed";
+    case SOLVER_REASON_UPDATE_INSTALLED:
+      return "update installed";
+    case SOLVER_REASON_CLEANDEPS_ERASE:
+      return "cleandeps erase";
+    case SOLVER_REASON_RESOLVE_ORPHAN:
+      return "orphaned package";
+    case SOLVER_REASON_UNSOLVABLE:
+      return "unsolvable";
+    case SOLVER_REASON_PREMISE:
+      return "learnt rule premise";
+    default:
+      break;
+    }
+  return "an unknown reason";
+}
+
+const char *
+solver_decisionreason2str(Solver *solv, Id decision, int reason, Id info)
+{
+  if (reason == SOLVER_REASON_WEAKDEP && decision > 0)
+    {
+      Id from, to, dep;
+      int type = solver_weakdepinfo(solv, decision, &from, &to, &dep);
+      if (type)
+       {
+         int state = solver_init_decisioninfo(solv, decision, type, from, to, dep);
+         return solver_decisioninfo2str(solv, state, type, from, to, dep);
+       }
+    }
+  if ((reason == SOLVER_REASON_RESOLVE_JOB || reason == SOLVER_REASON_UNIT_RULE || reason == SOLVER_REASON_RESOLVE || reason == SOLVER_REASON_UNSOLVABLE) && info > 0)
+    {
+      Id from, to, dep;
+      int type = solver_ruleinfo(solv, info, &from, &to, &dep);
+      if (type == SOLVER_RULE_CHOICE || type == SOLVER_RULE_RECOMMENDS)
+       {
+         Id rid2 = solver_rule2pkgrule(solv, info);
+         if (rid2)
+           {
+              type = solver_ruleinfo(solv, rid2, &from, &to, &dep);
+             if (type)
+               {
+                 int state = solver_init_decisioninfo(solv, decision, type, from, to, dep);
+                 return pool_tmpappend(solv->pool, solver_decisioninfo2str(solv, state, type, from, to, dep), " (limited)", 0);
+               }
+            }
+       }
+      if (type)
+       {
+         int state = solver_init_decisioninfo(solv, decision, type, from, to, dep);
+         return solver_decisioninfo2str(solv, state, type, from, to, dep);
+       }
+    }
+  return solver_reason2str(solv, reason);
+}
+
+/* decision merge state bits */
+#define DMS_INITED             (1 << 0)
+#define DMS_IDENTICAL_FROM     (1 << 1)
+#define DMS_IDENTICAL_TO       (1 << 2)
+#define DMS_MULTIPLE           (1 << 3)
+#define DMS_NEGATIVE           (1 << 4)
+#define DMS_NOMERGE            (1 << 5)
+
+/* add some bits about the decision to the ruleinfo type so we can joint decisions */
+int
+solver_init_decisioninfo(Solver *solv, Id decision, int type, Id from, Id to, Id dep)
+{
+  Id decisionpkg = decision >= 0 ? decision : -decision;
+  int state = DMS_INITED | (decision < 0 ? DMS_NEGATIVE : 0);
+  switch (type)
+    {
+    case SOLVER_RULE_DISTUPGRADE:
+    case SOLVER_RULE_INFARCH:
+    case SOLVER_RULE_UPDATE:
+    case SOLVER_RULE_FEATURE:
+    case SOLVER_RULE_BLACK:
+    case SOLVER_RULE_STRICT_REPO_PRIORITY:
+    case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
+    case SOLVER_RULE_PKG_REQUIRES:
+    case SOLVER_RULE_PKG_RECOMMENDS:
+    case SOLVER_RULE_PKG_SUPPLEMENTS:
+      if (decisionpkg == from)
+       state |= DMS_IDENTICAL_FROM;
+      break;
+    case SOLVER_RULE_PKG_SAME_NAME:
+    case SOLVER_RULE_PKG_CONFLICTS:
+    case SOLVER_RULE_PKG_OBSOLETES:
+    case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+    case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+    case SOLVER_RULE_PKG_CONSTRAINS:
+      if (decisionpkg == from)
+       state |= DMS_IDENTICAL_FROM;
+      else if (decisionpkg == to)
+       state |= DMS_IDENTICAL_TO;
+      break;
+    default:
+      break;
+    }
+  if (!decision)
+    state |= DMS_NOMERGE;
+  return state;
+}
+
+/* try to merge the ruleinfos of two decisions */
+int
+solver_merge_decisioninfo(Solver *solv, int *statep, int oldtype, Id oldfrom, Id oldto, Id olddep, Id decision, int type, Id from, Id to, Id dep)
+{
+  int state = *statep, newstate;
+  Id decisionpkg = decision >= 0 ? decision : -decision;
+  if (!state || (state & DMS_NOMERGE) != 0 || !type || type != oldtype)
+    return 0;
+  if (!decision || (decision > 0 && (state & DMS_NEGATIVE) != 0) || (decision < 0 && (state & DMS_NEGATIVE) == 0))
+    return 0;
+  newstate = (state & ~(DMS_IDENTICAL_FROM | DMS_IDENTICAL_TO)) | DMS_MULTIPLE;
+  if (decisionpkg == from && (state & DMS_IDENTICAL_FROM) != 0)
+    newstate |= DMS_IDENTICAL_FROM;
+  else if (oldfrom != from)
+    return 0;
+  if (decisionpkg == to && (state & DMS_IDENTICAL_TO) != 0)
+    newstate |= DMS_IDENTICAL_TO;
+  else if (oldto != to)
+    return 0;
+  /* if MULTIPLE is set we need to keep the identical from/to bits */
+  if ((state & DMS_MULTIPLE) != 0 && ((state ^ newstate) & (DMS_IDENTICAL_FROM|DMS_IDENTICAL_TO)) != 0)
+    return 0;
+  *statep = newstate;
+  return 1;
+}
+
+/* special version of solver_ruleinfo2str which supports merged decisions */
+const char *
+solver_decisioninfo2str(Solver *solv, int state, int type, Id from, Id to, Id dep)
+{
+  Pool *pool = solv->pool;
+  const char *s;
+  int multiple = state & DMS_MULTIPLE;
+
+  /* use it/they variants if DMS_IDENTICAL_FROM is set */
+  if ((state & DMS_IDENTICAL_FROM) != 0)
+    {
+      switch (type)
+       {
+       case SOLVER_RULE_DISTUPGRADE:
+         return multiple ? "they do not belong to a distupgrade repository" : "it does not belong to a distupgrade repository";
+       case SOLVER_RULE_INFARCH:
+         return multiple ? "they have inferior architecture": "it has inferior architecture";
+       case SOLVER_RULE_UPDATE:
+         return multiple ? "they need to stay installed or be updated" : "it needs to stay installed or be updated";
+       case SOLVER_RULE_FEATURE:
+         return multiple ? "they need to stay installed or be updated/downgraded" : "it needs to stay installed or be updated/downgraded";
+       case SOLVER_RULE_BLACK:
+         return multiple ? "they can only be installed by a direct request" : "it can only be installed by a direct request";
+       case SOLVER_RULE_STRICT_REPO_PRIORITY:
+         return multiple ? "they are excluded by strict repo priority" : "it is excluded by strict repo priority";
+
+       case SOLVER_RULE_PKG_NOTHING_PROVIDES_DEP:
+         return pool_tmpjoin(pool, "nothing provides ", pool_dep2str(pool, dep), 0);
+       case SOLVER_RULE_PKG_REQUIRES:
+         return pool_tmpjoin(pool, multiple ? "they require " : "it requires ", pool_dep2str(pool, dep), 0);
+       case SOLVER_RULE_PKG_RECOMMENDS:
+         return pool_tmpjoin(pool,  multiple ? "they recommend " : "it recommends ", pool_dep2str(pool, dep), 0);
+       case SOLVER_RULE_PKG_SUPPLEMENTS:
+         s = pool_tmpjoin(pool, multiple ? "they  supplement " : "it supplements ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_SAME_NAME:
+         return pool_tmpappend(pool, multiple ? "they have the same name as " : "it has the same name as ", pool_solvid2str(pool, to), 0);
+       case SOLVER_RULE_PKG_CONFLICTS:
+         s = pool_tmpappend(pool, multiple ? "they conflict with " : "it conflicts with ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_OBSOLETES:
+         s = pool_tmpappend(pool, multiple ? "they obsolete " : "it obsoletes ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+         s = pool_tmpjoin(pool, multiple ? "they are installed and obsolete " : "it is installed and obsoletes ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+         s = pool_tmpjoin(pool, multiple ? "they implicitly obsolete " : "it implicitly obsoletes ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " provided by ", pool_solvid2str(pool, to));
+         return s;
+       case SOLVER_RULE_PKG_CONSTRAINS:
+         s = pool_tmpappend(pool, multiple ? "they have constraint " : "it has constraint ", pool_dep2str(pool, dep), 0);
+         if (to)
+           s = pool_tmpappend(pool, s, " conflicting with ", pool_solvid2str(pool, to));
+         return s;
+       default:
+         break;
+       }
+    }
+
+  /* in some cases we can drop the "to" part if DMS_IDENTICAL_TO is set */
+  if ((state & DMS_IDENTICAL_TO) != 0)
+    {
+      switch (type)
+       {
+       case SOLVER_RULE_PKG_SAME_NAME:
+         return pool_tmpappend(pool, multiple ? "they have the same name as " : "it has the same name as ", pool_solvid2str(pool, from), 0);
+       case SOLVER_RULE_PKG_CONFLICTS:
+       case SOLVER_RULE_PKG_OBSOLETES:
+       case SOLVER_RULE_PKG_IMPLICIT_OBSOLETES:
+       case SOLVER_RULE_PKG_INSTALLED_OBSOLETES:
+       case SOLVER_RULE_PKG_CONSTRAINS:
+         state &= ~DMS_IDENTICAL_TO;
+         to = 0;
+         break;
+       default:
+         break;
+       }
+    }
+
+  /* fallback to solver_ruleinfo2str if we can */
+  if (multiple && (state & (DMS_IDENTICAL_FROM|DMS_IDENTICAL_TO)) != 0)
+      return "unsupported decision merge?";
+  return solver_ruleinfo2str(solv, type, from, to, dep);
+}
index f411c29018d4b042444f2a87e001bd50292a6d0f..d8a8496d1f527653c31ffb9d2b4d4d929e2669b1 100644 (file)
@@ -353,6 +353,8 @@ SOLV_1.0 {
                solver_create;
                solver_create_state_maps;
                solver_create_transaction;
+               solver_decisioninfo2str;
+               solver_decisionreason2str;
                solver_describe_decision;
                solver_describe_weakdep_decision;
                solver_findallproblemrules;
@@ -372,6 +374,8 @@ SOLV_1.0 {
                solver_get_recommendations;
                solver_get_unneeded;
                solver_get_userinstalled;
+               solver_init_decisioninfo;
+               solver_merge_decisioninfo;
                solver_next_problem;
                solver_next_solution;
                solver_next_solutionelement;
@@ -392,6 +396,7 @@ SOLV_1.0 {
                solver_problem2str;
                solver_problem_count;
                solver_problemruleinfo2str;
+               solver_reason2str;
                solver_rule2job;
                solver_rule2jobidx;
                solver_rule2pkgrule;
index 5f837bdeff335d10d67c2376bc46a467fd0c98e1..24221ce4385a4fe3044d1a002134877455abe43d 100644 (file)
@@ -393,9 +393,17 @@ extern int solver_calc_installsizechange(Solver *solv);
 extern void pool_job2solvables(Pool *pool, Queue *pkgs, Id how, Id what);
 extern int  pool_isemptyupdatejob(Pool *pool, Id how, Id what);
 
+/* decisioninfo merging */
+extern int solver_init_decisioninfo(Solver *solv, Id decision, int type, Id from, Id to, Id dep);
+extern int solver_merge_decisioninfo(Solver *solv, int *statep, int oldtype, Id oldfrom, Id oldto, Id olddep, Id decision, int type, Id from, Id to, Id dep);
+
 extern const char *solver_select2str(Pool *pool, Id select, Id what);
 extern const char *pool_job2str(Pool *pool, Id how, Id what, Id flagmask);
 extern const char *solver_alternative2str(Solver *solv, int type, Id id, Id from);
+extern const char *solver_reason2str(Solver *solv, int reason);
+extern const char *solver_decisionreason2str(Solver *solv, Id decision, int reason, Id info);
+extern const char *solver_decisioninfo2str(Solver *solv, int state, int type, Id from, Id to, Id dep);
+
 
 /* deprecated, use solver_allweakdepinfos/solver_weakdepinfo instead */
 extern void solver_describe_weakdep_decision(Solver *solv, Id p, Queue *whyq);