implement SOLVER_FLAG_BREAK_ORPHANS
[platform/upstream/libsolv.git] / src / solver.c
index 8c8e164..282dd78 100644 (file)
@@ -26,6 +26,7 @@
 #include "poolarch.h"
 #include "solverdebug.h"
 #include "cplxdeps.h"
+#include "linkedpkg.h"
 
 #define RULES_BLOCK 63
 
@@ -1572,6 +1573,7 @@ solver_free(Solver *solv)
   queuep_free(&solv->installsuppdepq);
   queuep_free(&solv->recommendscplxq);
   queuep_free(&solv->suggestscplxq);
+  queuep_free(&solv->brokenorphanrules);
 
   map_free(&solv->recommendsmap);
   map_free(&solv->suggestsmap);
@@ -1630,6 +1632,18 @@ solver_get_flag(Solver *solv, int flag)
     return solv->bestobeypolicy;
   case SOLVER_FLAG_NO_AUTOTARGET:
     return solv->noautotarget;
+  case SOLVER_FLAG_DUP_ALLOW_DOWNGRADE:
+    return solv->dup_allowdowngrade;
+  case SOLVER_FLAG_DUP_ALLOW_NAMECHANGE:
+    return solv->dup_allownamechange;
+  case SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE:
+    return solv->dup_allowarchchange;
+  case SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE:
+    return solv->dup_allowvendorchange;
+  case SOLVER_FLAG_KEEP_ORPHANS:
+    return solv->keep_orphans;
+  case SOLVER_FLAG_BREAK_ORPHANS:
+    return solv->break_orphans;
   default:
     break;
   }
@@ -1681,6 +1695,24 @@ solver_set_flag(Solver *solv, int flag, int value)
   case SOLVER_FLAG_NO_AUTOTARGET:
     solv->noautotarget = value;
     break;
+  case SOLVER_FLAG_DUP_ALLOW_DOWNGRADE:
+    solv->dup_allowdowngrade = value;
+    break;
+  case SOLVER_FLAG_DUP_ALLOW_NAMECHANGE:
+    solv->dup_allownamechange = value;
+    break;
+  case SOLVER_FLAG_DUP_ALLOW_ARCHCHANGE:
+    solv->dup_allowarchchange = value;
+    break;
+  case SOLVER_FLAG_DUP_ALLOW_VENDORCHANGE:
+    solv->dup_allowvendorchange = value;
+    break;
+  case SOLVER_FLAG_KEEP_ORPHANS:
+    solv->keep_orphans = value;
+    break;
+  case SOLVER_FLAG_BREAK_ORPHANS:
+    solv->break_orphans = value;
+    break;
   default:
     break;
   }
@@ -2656,6 +2688,26 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                break;
              continue;         /* back to main loop */
            }
+          if (solv->brokenorphanrules)
+           {
+             solver_check_brokenorphanrules(solv, &dq);
+             if (dq.count)
+               {
+                 policy_filter_unwanted(solv, &dq, POLICY_MODE_CHOOSE);
+                 for (i = 0; i < dq.count; i++)
+                   {
+                     p = dq.elements[i];
+                     POOL_DEBUG(SOLV_DEBUG_POLICY, "installing orphaned dep %s\n", pool_solvid2str(pool, p));
+                     olevel = level;
+                     level = setpropagatelearn(solv, level, p, 0, 0);
+                     if (level < olevel)
+                       break;
+                   }
+                 if (level == 0)
+                   break;
+                 continue;
+               }
+           }
        }
 
      /* one final pass to make sure we decided all installed packages */
@@ -3272,6 +3324,7 @@ solver_solve(Solver *solv, Queue *job)
       queuep_free(&solv->suggestscplxq);
       solv->recommends_index = 0;
     }
+  queuep_free(&solv->brokenorphanrules);
   solv->specialupdaters = solv_free(solv->specialupdaters);
 
 
@@ -3553,20 +3606,16 @@ solver_solve(Solver *solv, Queue *job)
           * check for and remove duplicate
           */
          r = solv->rules + solv->nrules - 1;           /* r: update rule */
+          if (!r->p)
+           continue;
          sr = r - (installed->end - installed->start); /* sr: feature rule */
-         /* it's orphaned if there is no feature rule or the feature rule
-           * consists just of the installed package */
-         if (!sr->p || (sr->p == i && !sr->d && !sr->w2))
+         /* it's also orphaned if the feature rule consists just of the installed package */
+         if (!solv->dupmap_all && sr->p == i && !sr->d && !sr->w2)
            queue_push(&solv->orphaned, i);
-          if (!r->p)
-           {
-             /* assert(solv->dupmap_all && !sr->p); */
-             continue;
-           }
          if (!solver_rulecmp(solv, r, sr))
            memset(sr, 0, sizeof(*sr));         /* delete unneeded feature rule */
          else
-           solver_disablerule(solv, sr);       /* disable feature rule */
+           solver_disablerule(solv, sr);       /* disable feature rule for now */
        }
       /* consistency check: we added a rule for _every_ installed solvable */
       assert(solv->nrules - solv->updaterules == installed->end - installed->start);
@@ -3853,6 +3902,10 @@ solver_solve(Solver *solv, Queue *job)
   /* disable update rules that conflict with our job */
   solver_disablepolicyrules(solv);
 
+  /* break orphans if requested */
+  if (solv->dupmap_all && solv->orphaned.count && solv->break_orphans)
+    solver_breakorphans(solv);
+
   /* make initial decisions based on assertion rules */
   makeruledecisions(solv);
   POOL_DEBUG(SOLV_DEBUG_SOLVER, "problems so far: %d\n", solv->problems.count);
@@ -4460,6 +4513,250 @@ pool_isemptyupdatejob(Pool *pool, Id how, Id what)
   return 1;
 }
 
+static int
+get_userinstalled_cmp(const void *ap, const void *bp, void *dp)
+{
+  return *(Id *)ap - *(Id *)bp;
+}
+
+static int
+get_userinstalled_cmp_names(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = dp;
+  return strcmp(pool_id2str(pool, *(Id *)ap), pool_id2str(pool, *(Id *)bp));
+}
+
+static void
+get_userinstalled_sort_uniq(Pool *pool, Queue *q, int flags)
+{
+  Id lastp = -1;
+  int i, j;
+  if ((flags & GET_USERINSTALLED_NAMES) != 0)
+    solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp_names, pool);
+  else
+    solv_sort(q->elements, q->count, sizeof(Id), get_userinstalled_cmp, 0);
+  for (i = j = 0; i < q->count; i++)
+    if (q->elements[i] != lastp)
+      q->elements[j++] = lastp = q->elements[i];
+  queue_truncate(q, j);
+}
+
+void
+solver_get_userinstalled(Solver *solv, Queue *q, int flags)
+{
+  Pool *pool = solv->pool;
+  Id p, p2, pp;
+  Solvable *s;
+  Repo *installed = solv->installed;
+  int i, j;
+  Map userinstalled;
+  
+  map_init(&userinstalled, 0);
+  queue_empty(q);
+  /* first process jobs */
+  for (i = 0; i < solv->job.count; i += 2)
+    {
+      Id how = solv->job.elements[i];
+      Id what, select;
+      if (installed && (how & SOLVER_JOBMASK) == SOLVER_USERINSTALLED)
+       {
+         if (!userinstalled.size)
+           map_grow(&userinstalled, installed->end - installed->start);
+         what = solv->job.elements[i + 1];
+         select = how & SOLVER_SELECTMASK;
+         if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
+           FOR_REPO_SOLVABLES(installed, p, s)
+             MAPSET(&userinstalled, p - installed->start);
+         FOR_JOB_SELECT(p, pp, select, what)
+           if (pool->solvables[p].repo == installed)
+             MAPSET(&userinstalled, p - installed->start);
+         continue;
+       }
+      if ((how & SOLVER_JOBMASK) != SOLVER_INSTALL)
+       continue;
+      if ((how & SOLVER_NOTBYUSER) != 0)
+       continue;
+      what = solv->job.elements[i + 1];
+      select = how & SOLVER_SELECTMASK;
+      FOR_JOB_SELECT(p, pp, select, what)
+        if (solv->decisionmap[p] > 0)
+         {
+           queue_push(q, p);
+#ifdef ENABLE_LINKED_PKGS
+           if (has_package_link(pool, pool->solvables + p))
+             {
+               int j;
+               Queue lq;
+               queue_init(&lq);
+               find_package_link(pool, pool->solvables + p, 0, &lq, 0, 0);
+               for (j = 0; j < lq.count; j++)
+                 if (solv->decisionmap[lq.elements[j]] > 0)
+                   queue_push(q, lq.elements[j]);
+             }
+#endif
+         }
+    }
+  /* now process updates of userinstalled packages */
+  if (installed && userinstalled.size)
+    {
+      for (i = 1; i < solv->decisionq.count; i++)
+       {
+         p = solv->decisionq.elements[i];
+         if (p <= 0)
+           continue;
+         s = pool->solvables + p;
+         if (!s->repo)
+           continue;
+         if (s->repo == installed)
+           {
+             if (MAPTST(&userinstalled, p - installed->start))
+               queue_push(q, p);
+             continue;
+           }
+         /* new package, check if we replace a userinstalled one */
+         FOR_PROVIDES(p2, pp, s->name)
+           {
+             Solvable *ps = pool->solvables + p2;
+             if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
+               continue;
+             if (!pool->implicitobsoleteusesprovides && s->name != ps->name)
+               continue;
+             if (pool->implicitobsoleteusescolors && !pool_colormatch(pool, s, ps))
+               continue;
+             queue_push(q, p);
+             break;
+           }
+         if (!p2 && s->repo != installed && s->obsoletes)
+           {
+             Id obs, *obsp = s->repo->idarraydata + s->obsoletes;
+             while ((obs = *obsp++) != 0)
+               {
+                 FOR_PROVIDES(p2, pp, obs)
+                   {
+                     Solvable *ps = pool->solvables + p2;
+                     if (p2 == p || ps->repo != installed || !MAPTST(&userinstalled, p2 - installed->start))
+                       continue;
+                     if (!pool->obsoleteusesprovides && !pool_match_nevr(pool, ps, obs))
+                       continue;
+                     if (pool->obsoleteusescolors && !pool_colormatch(pool, s, ps)) 
+                       continue;
+                     queue_push(q, p); 
+                     break;
+                   }
+                 if (p2)
+                   break;
+               }
+           }
+       }
+    }
+  map_free(&userinstalled);
+  /* convert to names if asked */
+  if ((flags & GET_USERINSTALLED_NAMES) != 0)
+    {
+      for (i = 0; i < q->count; i++)
+       {
+         s = pool->solvables + q->elements[i];
+         q->elements[i] = s->name;
+       }
+    }
+  /* sort and unify */
+  if (q->count > 1)
+    get_userinstalled_sort_uniq(pool, q, flags);
+  /* invert if asked */
+  if ((flags & GET_USERINSTALLED_INVERTED) != 0)
+    {
+      /* first generate queue with all installed packages */
+      Queue invq;
+      queue_init(&invq);
+      for (i = 1; i < solv->decisionq.count; i++)
+       {
+         p = solv->decisionq.elements[i];
+         if (p <= 0)
+           continue;
+         s = pool->solvables + p;
+         if (!s->repo)
+           continue;
+         if ((flags & GET_USERINSTALLED_NAMES) != 0)
+           queue_push(&invq, s->name);
+         else
+           queue_push(&invq, p);
+       }
+      /* push q on invq, just in case... */
+      queue_insertn(&invq, invq.count, q->count, q->elements);
+      if (invq.count > 1)
+       get_userinstalled_sort_uniq(pool, &invq, flags);
+      /* subtract queues (easy as they are sorted and invq is a superset of q) */
+      if (q->count)
+       {
+         for (i = j = 0; i < invq.count; i++)
+           if (invq.elements[i] == q->elements[j])
+             {
+               invq.elements[i] = 0;
+               if (++j >= q->count)
+                 break;
+             }
+         queue_empty(q);
+       }
+      for (i = j = 0; i < invq.count; i++)
+       if (invq.elements[i])
+         queue_push(q, invq.elements[i]);
+      queue_free(&invq);
+    }
+}
+
+void
+pool_add_userinstalled_jobs(Pool *pool, Queue *q, Queue *job, int flags)
+{
+  int i;
+
+  if (flags & GET_USERINSTALLED_INVERTED)
+    {
+      Queue invq;
+      Id p, lastid;
+      Solvable *s;
+      int bad;
+      if (!pool->installed)
+       return;
+      queue_init(&invq);
+      FOR_REPO_SOLVABLES(pool->installed, p, s)
+       queue_push(&invq, flags & GET_USERINSTALLED_NAMES ? s->name : p);
+      queue_insertn(&invq, invq.count, q->count, q->elements);
+      if (invq.count > 1)
+        get_userinstalled_sort_uniq(pool, &invq, flags);
+      /* now the fun part, add q again, sort, and remove all dups */
+      queue_insertn(&invq, invq.count, q->count, q->elements);
+      if (invq.count > 1)
+       {
+         if ((flags & GET_USERINSTALLED_NAMES) != 0)
+           solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp_names, pool);
+         else
+           solv_sort(invq.elements, invq.count, sizeof(Id), get_userinstalled_cmp, 0);
+       }
+      lastid = -1;
+      bad = 1;
+      for (i = 0; i < invq.count; i++)
+       {
+         if (invq.elements[i] == lastid)
+           {
+             bad = 1;
+             continue;
+           }
+         if (!bad)
+           queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
+         bad = 0;
+         lastid = invq.elements[i];
+       }
+      if (!bad)
+       queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), lastid);
+      queue_free(&invq);
+    }
+  else
+    {
+      for (i = 0; i < q->count; i++)
+       queue_push2(job, SOLVER_USERINSTALLED | (flags & GET_USERINSTALLED_NAMES ? SOLVER_SOLVABLE_NAME : SOLVER_SOLVABLE), q->elements[i]);
+    }
+}
+
 const char *
 solver_select2str(Pool *pool, Id select, Id what)
 {
@@ -4543,7 +4840,7 @@ pool_job2str(Pool *pool, Id how, Id what, Id flagmask)
       strstart = "multi version ";
       break;
     case SOLVER_LOCK:
-      strstart = "update ";
+      strstart = "lock ";
       break;
     case SOLVER_DISTUPGRADE:
       strstart = "dist upgrade ";