- also check obsoletes when disabling update rules
[platform/upstream/libsolv.git] / src / solver.c
index f9e586f..f3826e4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2007, Novell Inc.
+ * Copyright (c) 2007-2008, Novell Inc.
  *
  * This program is licensed under the BSD license, read LICENSE.BSD
  * for further information
@@ -380,7 +380,7 @@ addrule(Solver *solv, Id p, Id d)
     }
 #endif
 
-  if (n == 1 && p > d)
+  if (n == 1 && p > d && !solv->rpmrules_end)
     {
       /* smallest literal first so we can find dups */
       n = p; p = d; d = n;             /* p <-> d */
@@ -838,7 +838,7 @@ enabledisablelearntrules(Solver *solv)
  * 
  */
 
-void
+static void
 enableweakrules(Solver *solv)
 {
   int i;
@@ -910,9 +910,23 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
          s = pool->solvables + what;
          if (solv->noobsoletes.size && MAPTST(&solv->noobsoletes, what))
            break;
+         if (s->obsoletes)
+           {
+             Id obs, *obsp;
+             obsp = s->repo->idarraydata + s->obsoletes;
+             while ((obs = *obsp++) != 0)
+               FOR_PROVIDES(p, pp, obs)
+                 {
+                   if (pool->solvables[p].repo != installed)
+                     continue;
+                   if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
+                     continue;
+                   MAPSET(&solv->noupdate, p - installed->start);
+                 }
+           }
          FOR_PROVIDES(p, pp, s->name)
            {
-             if (pool->solvables[p].name != s->name)
+             if (!solv->implicitobsoleteusesprovides && pool->solvables[p].name != s->name)
                continue;
              if (pool->solvables[p].repo == installed)
                MAPSET(&solv->noupdate, p - installed->start);
@@ -949,9 +963,33 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
        {
        case SOLVER_INSTALL_SOLVABLE:
          s = pool->solvables + what;
+         if (s->obsoletes)
+           {
+             Id obs, *obsp;
+             obsp = s->repo->idarraydata + s->obsoletes;
+             while ((obs = *obsp++) != 0)
+               FOR_PROVIDES(p, pp, obs)
+                 {
+                   if (pool->solvables[p].repo != installed)
+                     continue;
+                   if (!solv->obsoleteusesprovides && !pool_match_nevr(pool, pool->solvables + p, obs))
+                     continue;
+                   if (MAPTST(&solv->noupdate, p - installed->start))
+                     continue;
+                   r = solv->rules + solv->updaterules + (p - installed->start);
+                   if (r->d >= 0)
+                     continue;
+                   enablerule(solv, r);
+                   IF_POOLDEBUG (SAT_DEBUG_SOLUTIONS)
+                     {
+                       POOL_DEBUG(SAT_DEBUG_SOLUTIONS, "@@@ re-enabling ");
+                       solver_printrule(solv, SAT_DEBUG_SOLUTIONS, r);
+                     }
+                 }
+           }
          FOR_PROVIDES(p, pp, s->name)
            {
-             if (pool->solvables[p].name != s->name)
+             if (!solv->implicitobsoleteusesprovides && pool->solvables[p].name != s->name)
                continue;
              if (pool->solvables[p].repo != installed)
                continue;
@@ -1022,50 +1060,6 @@ disableupdaterules(Solver *solv, Queue *job, int jobidx)
     }
 }
 
-#if CODE10
-/*-------------------------------------------------------------------
- * add patch atom requires
- */
-
-static void
-addpatchatomrequires(Solver *solv, Solvable *s, Id *dp, Queue *q, Map *m)
-{
-  Pool *pool = solv->pool;
-  Id fre, *frep, p, *pp, ndp;
-  Solvable *ps;
-  Queue fq;
-  Id qbuf[64];
-  int i, used = 0;
-
-  queue_init_buffer(&fq, qbuf, sizeof(qbuf)/sizeof(*qbuf));
-  queue_push(&fq, -(s - pool->solvables));
-  for (; *dp; dp++)
-    queue_push(&fq, *dp);
-  ndp = pool_queuetowhatprovides(pool, &fq);
-  frep = s->repo->idarraydata + s->freshens;
-  while ((fre = *frep++) != 0)
-    {
-      FOR_PROVIDES(p, pp, fre)
-       {
-         ps = pool->solvables + p;
-         addrule(solv, -p, ndp);
-         used = 1;
-         if (!MAPTST(m, p))
-           queue_push(q, p);
-       }
-    }
-  if (used)
-    {
-      for (i = 1; i < fq.count; i++)
-       {
-         p = fq.elements[i];
-         if (!MAPTST(m, p))
-           queue_push(q, p);
-       }
-    }
-  queue_free(&fq);
-}
-#endif
 
 
 /*-------------------------------------------------------------------
@@ -1158,15 +1152,6 @@ addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
          POOL_DEBUG(SAT_DEBUG_RULE_CREATION, "package %s [%d] is not installable\n", solvable2str(pool, s), (Id)(s - pool->solvables));
          addrule(solv, -n, 0);            /* uninstallable */
        }
-#if CODE10
-      patchatom = 0;
-      if (s->freshens && !s->supplements)
-       {
-         const char *name = id2str(pool, s->name);
-         if (name[0] == 'a' && !strncmp(name, "atom:", 5))
-           patchatom = 1;
-       }
-#endif
 
       /*-----------------------------------------
        * check requires of s
@@ -1377,17 +1362,6 @@ addrpmrulesforweak(Solver *solv, Map *m)
              break;
        }
 
-       /* if nothing found, check for freshens
-        * (patterns use this)
-        */
-      if (!sup && s->freshens)
-       {
-         supp = s->repo->idarraydata + s->freshens;
-         while ((sup = *supp++) != ID_NULL)
-           if (dep_possible(solv, sup, m))
-             break;
-       }
-
        /* if nothing found, check for enhances */
       if (!sup && s->enhances)
        {
@@ -1396,7 +1370,7 @@ addrpmrulesforweak(Solver *solv, Map *m)
            if (dep_possible(solv, sup, m))
              break;
        }
-       /* if notthing found, goto next solvables */
+       /* if nothing found, goto next solvables */
       if (!sup)
        continue;
       addrpmrulesforsolvable(solv, s, m);
@@ -1546,6 +1520,7 @@ addwatches_rule(Solver *solv, Rule *r)
  */
 #define DECISIONMAP_TRUE(p) ((p) > 0 ? (decisionmap[p] > 0) : (decisionmap[-p] < 0))
 #define DECISIONMAP_FALSE(p) ((p) > 0 ? (decisionmap[p] < 0) : (decisionmap[-p] > 0))
+#define DECISIONMAP_UNDEF(p) (decisionmap[(p) > 0 ? (p) : -(p)] == 0)
 
 /*-------------------------------------------------------------------
  * 
@@ -1554,13 +1529,13 @@ addwatches_rule(Solver *solv, Rule *r)
  * make decision and propagate to all rules
  * 
  * Evaluate each term affected by the decision (linked through watches)
+ * If we find unit rules we make new decisions based on them
  * 
- * If the decision was positive (true), we're done since the whole term is true
- * If the decision was negative (false), we must force the other literal of
- *  the conjunctive normal form (CNF) to true.
+ * Everything's fixed there, it's just finding rules that are
+ * unit.
  * 
  * return : 0 = everything is OK
- *          watched rule = there is a conflict
+ *          rule = conflict found in this rule
  */
 
 static Rule *
@@ -1577,14 +1552,12 @@ propagate(Solver *solv, int level)
 
   POOL_DEBUG(SAT_DEBUG_PROPAGATE, "----- propagate -----\n");
 
-    /* foreach non-propagated decision */
+  /* foreach non-propagated decision */
   while (solv->propagate_index < solv->decisionq.count)
     {
        /*
         * 'pkg' was just decided
-        * 
         * negate because our watches trigger if literal goes FALSE
-        * If a literal goes TRUE in a CNF (terms of (x|y)), we're done for this term
         */
       pkg = -solv->decisionq.elements[solv->propagate_index++];
        
@@ -1594,7 +1567,7 @@ propagate(Solver *solv, int level)
          solver_printruleelement(solv, SAT_DEBUG_PROPAGATE, 0, -pkg);
         }
 
-       /* foreach rule involving 'pkg' */
+      /* foreach rule where 'pkg' is now FALSE */
       for (rp = watches + pkg; *rp; rp = next_rp)
        {
          r = solv->rules + *rp;
@@ -1614,9 +1587,10 @@ propagate(Solver *solv, int level)
              solver_printrule(solv, SAT_DEBUG_PROPAGATE, r);
            }
 
-           /* 'pkg' was just decided
-            *   now find other literal watch, check clause
-            *  and advance on linked list
+           /* 'pkg' was just decided (was set to FALSE)
+            * 
+            *  now find other literal watch, check clause
+            *   and advance on linked list
             */
          if (pkg == r->w1)
            {
@@ -1631,41 +1605,51 @@ propagate(Solver *solv, int level)
            
            /* 
             * This term is already true (through the other literal)
+            * so we have nothing to do
             */
          if (DECISIONMAP_TRUE(other_watch))
            continue;
 
            /*
-            * The other literal is false, try to get a 'true'
+            * The other literal is FALSE or UNDEF
+            * 
             */
            
           if (r->d)
            {
-             /* not a binary clause, check if we need to move our watch.
+             /* Not a binary clause, try to move our watch.
+              * 
+              * Go over all literals and find one that is
+              *   not other_watch
+              *   and not FALSE
               * 
-              * search for a literal that is not other_watch and not false
-              * (true is also ok, in that case the rule is fulfilled)
+              * (TRUE is also ok, in that case the rule is fulfilled)
               */
              if (r->p                                /* we have a 'p' */
-                 && r->p != other_watch              /* which is not what we just checked */
-                 && !DECISIONMAP_TRUE(-r->p))        /* and its not already decided 'negative' */
+                 && r->p != other_watch              /* which is not watched */
+                 && !DECISIONMAP_FALSE(r->p))        /* and not FALSE */
                {
-                 p = r->p;                           /* we must get this to 'true' */
+                 p = r->p;
                }
              else                                    /* go find a 'd' to make 'true' */
                {
-                 /* foreach 'd' */
+                 /* foreach p in 'd'
+                    we just iterate sequentially, doing it in another order just changes the order of decisions, not the decisions itself
+                  */
                  for (dp = pool->whatprovidesdata + r->d; (p = *dp++) != 0;)
-                   if (p != other_watch              /* which is not what we just checked */
-                       && !DECISIONMAP_TRUE(-p))     /* and its not already decided 'negative' */
-                     break;
+                   {
+                     if (p != other_watch              /* which is not watched */
+                         && !DECISIONMAP_FALSE(p))     /* and not FALSE */
+                       break;
+                   }
                }
 
-               /*
-                * if p is free to watch, move watch to p
-                */
              if (p)
                {
+                 /*
+                  * if we found some p that is UNDEF or TRUE, move
+                  * watch to it
+                  */
                  IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
                    {
                      if (p > 0)
@@ -1690,16 +1674,15 @@ propagate(Solver *solv, int level)
                  watches[p] = r - solv->rules;
                  continue;
                }
-               
-               /* !p */
+             /* search failed, thus all unwatched literals are FALSE */
                
            } /* not binary */
            
             /*
-            * unit clause found, force other watch to TRUE
+            * unit clause found, set literal other_watch to TRUE
             */
 
-         if (DECISIONMAP_TRUE(-other_watch))              /* decided before to 'negative' */
+         if (DECISIONMAP_FALSE(other_watch))      /* check if literal is FALSE */
            return r;                              /* eek, a conflict! */
            
          IF_POOLDEBUG (SAT_DEBUG_PROPAGATE)
@@ -1708,9 +1691,6 @@ propagate(Solver *solv, int level)
              solver_printrule(solv, SAT_DEBUG_PROPAGATE, r);
            }
 
-           /*
-            * decide: 'other_watch' to 'true'
-            */
          if (other_watch > 0)
             decisionmap[other_watch] = level;    /* install! */
          else
@@ -2349,7 +2329,8 @@ solver_free(Solver *solv)
 static void
 run_solver(Solver *solv, int disablerules, int doweak)
 {
-  Queue dq;         /* local decisionqueue */
+  Queue dq;            /* local decisionqueue */
+  Queue dqs;           /* local decisionqueue for supplements */
   int systemlevel;
   int level, olevel;
   Rule *r;
@@ -2376,6 +2357,7 @@ run_solver(Solver *solv, int disablerules, int doweak)
   POOL_DEBUG(SAT_DEBUG_STATS, "solving...\n");
 
   queue_init(&dq);
+  queue_init(&dqs);
 
   /*
    * here's the main loop:
@@ -2406,6 +2388,60 @@ run_solver(Solver *solv, int disablerules, int doweak)
            }
        }
 
+     if (level < systemlevel)
+       {
+         POOL_DEBUG(SAT_DEBUG_STATS, "resolving job rules\n");
+         for (i = solv->jobrules, r = solv->rules + i; i < solv->jobrules_end; i++, r++)
+           {
+             Id l;
+             if (r->d < 0)             /* ignore disabled rules */
+               continue;
+             queue_empty(&dq);
+             FOR_RULELITERALS(l, dp, r)
+               {
+                 if (l < 0)
+                   {
+                     if (solv->decisionmap[-l] <= 0)
+                       break;
+                   }
+                 else
+                   {
+                     if (solv->decisionmap[l] > 0)
+                       break;
+                     if (solv->decisionmap[l] == 0)
+                       queue_push(&dq, l);
+                   }
+               }
+             if (l || !dq.count)
+               continue;
+             if (!solv->updatesystem && solv->installed && dq.count > 1)
+               {
+                 int j, k;
+                 for (j = k = 0; j < dq.count; j++)
+                   {
+                     Solvable *s = pool->solvables + dq.elements[j];
+                     if (s->repo == solv->installed)
+                       dq.elements[k++] = dq.elements[j];
+                   }
+                 if (k)
+                   dq.count = k;
+               }
+             olevel = level;
+             level = selectandinstall(solv, level, &dq, 0, disablerules);
+             if (level == 0)
+               {
+                 queue_free(&dq);
+                 return;
+               }
+             if (level <= olevel)
+               break;
+           }
+         systemlevel = level + 1;
+         if (i < solv->jobrules_end)
+           continue;
+       }
+
+
       /*
        * installed packages
        */
@@ -2500,7 +2536,7 @@ run_solver(Solver *solv, int disablerules, int doweak)
              if (!dq.count && solv->decisionmap[i] != 0)
                continue;
              olevel = level;
-             /* FIXME: i is handled a bit different because we do not want
+             /* FIXME: it is handled a bit different because we do not want
               * to have it pruned just because it is not recommened.
               * we should not prune installed packages instead
               */
@@ -2513,11 +2549,14 @@ run_solver(Solver *solv, int disablerules, int doweak)
              if (level <= olevel)
                break;
            }
+         systemlevel = level + 1;
          if (i < solv->installed->end)
            continue;
-         systemlevel = level;
        }
 
+      if (level < systemlevel)
+        systemlevel = level;
+
       /*
        * decide
        */
@@ -2613,15 +2652,18 @@ run_solver(Solver *solv, int disablerules, int doweak)
 
          POOL_DEBUG(SAT_DEBUG_STATS, "installing recommended packages\n");
          queue_empty(&dq);
+         queue_empty(&dqs);
          for (i = 1; i < pool->nsolvables; i++)
            {
              if (solv->decisionmap[i] < 0)
                continue;
              if (solv->decisionmap[i] > 0)
                {
+                 /* installed, check for recommends */
                  Id *recp, rec, *pp, p;
                  s = pool->solvables + i;
-                 /* installed, check for recommends */
+                 if (solv->ignorealreadyrecommended && s->repo == solv->installed)
+                   continue;
                  /* XXX need to special case AND ? */
                  if (s->recommends)
                    {
@@ -2647,14 +2689,51 @@ run_solver(Solver *solv, int disablerules, int doweak)
              else
                {
                  s = pool->solvables + i;
-                 if (!s->supplements && !s->freshens)
+                 if (!s->supplements)
                    continue;
                  if (!pool_installable(pool, s))
                    continue;
-                 if (solver_is_supplementing(solv, s))
+                 if (!solver_is_supplementing(solv, s))
+                   continue;
+                 if (solv->ignorealreadyrecommended && solv->installed)
+                   queue_pushunique(&dqs, i);  /* needs filter */
+                 else
                    queue_pushunique(&dq, i);
                }
            }
+          if (solv->ignorealreadyrecommended && dqs.count)
+           {
+             /* turn off all new packages */
+             for (i = 0; i < solv->decisionq.count; i++)
+               {
+                 p = solv->decisionq.elements[i];
+                 if (p < 0)
+                   continue;
+                 s = pool->solvables + p;
+                 if (s->repo && s->repo != solv->installed)
+                   solv->decisionmap[p] = -solv->decisionmap[p];
+               }
+             /* filter out old supplements */
+             for (i = 0; i < dqs.count; i++)
+               {
+                 p = dqs.elements[i];
+                 s = pool->solvables + p;
+                 if (!s->supplements)
+                   continue;
+                 if (!solver_is_supplementing(solv, s))
+                   queue_pushunique(&dq, p);
+               }
+             /* undo turning off */
+             for (i = 0; i < solv->decisionq.count; i++)
+               {
+                 p = solv->decisionq.elements[i];
+                 if (p < 0)
+                   continue;
+                 s = pool->solvables + p;
+                 if (s->repo && s->repo != solv->installed)
+                   solv->decisionmap[p] = -solv->decisionmap[p];
+               }
+           }
          if (dq.count)
            {
              if (dq.count > 1)
@@ -2742,6 +2821,7 @@ run_solver(Solver *solv, int disablerules, int doweak)
 
   POOL_DEBUG(SAT_DEBUG_STATS, "done solving.\n\n");
   queue_free(&dq);
+  queue_free(&dqs);
 }
 
 
@@ -3226,17 +3306,20 @@ solver_problemruleinfo(Solver *solv, Queue *job, Id rid, Id *depp, Id *sourcep,
              return SOLVER_PROBLEM_NOTHING_PROVIDES_DEP;
            }
        }
-      assert(!solv->allowselfconflicts);
-      assert(s->conflicts);
-      conp = s->repo->idarraydata + s->conflicts;
-      while ((con = *conp++) != 0)
-       FOR_PROVIDES(p, pp, con)
-         if (p == -r->p)
-           {
-             *depp = con;
-             return SOLVER_PROBLEM_SELF_CONFLICT;
-           }
-      assert(0);
+      if (!solv->allowselfconflicts && s->conflicts)
+       {
+         conp = s->repo->idarraydata + s->conflicts;
+         while ((con = *conp++) != 0)
+           FOR_PROVIDES(p, pp, con)
+             if (p == -r->p)
+               {
+                 *depp = con;
+                 return SOLVER_PROBLEM_SELF_CONFLICT;
+               }
+       }
+      /* should never happen */
+      *depp = 0;
+      return SOLVER_PROBLEM_RPM_RULE;
     }
   s = pool->solvables - r->p;
   if (installed && !solv->fixsystem && s->repo == installed)
@@ -3365,29 +3448,41 @@ solver_problemruleinfo(Solver *solv, Queue *job, Id rid, Id *depp, Id *sourcep,
            }
        }
       /* all cases checked, can't happen */
-      assert(0);
+      *depp = 0;
+      *sourcep = -r->p;
+      *targetp = 0;
+      return SOLVER_PROBLEM_RPM_RULE;
     }
   /* simple requires */
-  assert(s->requires);
-  reqp = s->repo->idarraydata + s->requires;
-  while ((req = *reqp++) != 0)
+  if (s->requires)
     {
-      if (req == SOLVABLE_PREREQMARKER)
-       continue;
-      dp = pool_whatprovides(pool, req);
-      if (d == 0)
+      reqp = s->repo->idarraydata + s->requires;
+      while ((req = *reqp++) != 0)
        {
-         if (*dp == r->w2 && dp[1] == 0)
+         if (req == SOLVABLE_PREREQMARKER)
+           continue;
+         dp = pool_whatprovides(pool, req);
+         if (d == 0)
+           {
+             if (*dp == r->w2 && dp[1] == 0)
+               break;
+           }
+         else if (dp - pool->whatprovidesdata == d)
            break;
        }
-      else if (dp - pool->whatprovidesdata == d)
-       break;
+      if (req)
+       {
+         *depp = req;
+         *sourcep = -r->p;
+         *targetp = 0;
+         return SOLVER_PROBLEM_DEP_PROVIDERS_NOT_INSTALLABLE;
+       }
     }
-  assert(req);
-  *depp = req;
+  /* all cases checked, can't happen */
+  *depp = 0;
   *sourcep = -r->p;
   *targetp = 0;
-  return SOLVER_PROBLEM_DEP_PROVIDERS_NOT_INSTALLABLE;
+  return SOLVER_PROBLEM_RPM_RULE;
 }
 
 
@@ -3832,7 +3927,7 @@ solver_solve(Solver *solv, Queue *job)
   oldnrules = solv->nrules;
     
     /*
-     * add rules for suggests, [freshens,] enhances
+     * add rules for suggests, enhances
      */
   addrpmrulesforweak(solv, &addedmap);
   POOL_DEBUG(SAT_DEBUG_STATS, "added %d rpm rules because of weak dependencies\n", solv->nrules - oldnrules);
@@ -3881,23 +3976,9 @@ solver_solve(Solver *solv, Queue *job)
   solv->featurerules = solv->nrules;              /* mark start of feature rules */
   if (installed)
     {
-       /* foreach installed solvable */
+       /* foreach possibly installed solvable */
       for (i = installed->start, s = pool->solvables + i; i < installed->end; i++, s++)
        {
-           /*
-            * patterns use this
-            */
-
-         if (s->freshens && !s->supplements)
-           {
-             const char *name = id2str(pool, s->name);
-             if (name[0] == 'a' && !strncmp(name, "atom:", 5))
-               {
-                 addrule(solv, 0, 0);
-                 continue;
-               }
-           }
-
          if (s->repo != installed)
            {
              addrule(solv, 0, 0);      /* create dummy rule */
@@ -3923,6 +4004,7 @@ solver_solve(Solver *solv, Queue *job)
     
   POOL_DEBUG(SAT_DEBUG_SCHUBI, "*** Add update rules ***\n");
   solv->updaterules = solv->nrules;
+
   if (installed)
     { /* foreach installed solvables */
       /* we create all update rules, but disable some later on depending on the job */
@@ -3930,25 +4012,14 @@ solver_solve(Solver *solv, Queue *job)
        {
          Rule *sr;
 
-#if CODE10
-         /* no update rules for patch atoms */
-         if (s->freshens && !s->supplements)
-           {
-             const char *name = id2str(pool, s->name);
-             if (name[0] == 'a' && !strncmp(name, "atom:", 5))
-               {
-                 addrule(solv, 0, 0);
-                 continue;
-               }
-           }
-#endif
          if (s->repo != installed)
            {
              addrule(solv, 0, 0);      /* create dummy rule */
              continue;
            }
+
          addupdaterule(solv, s, 0);    /* allowall = 0: downgrades allowed */
-           
+
            /*
             * check for and remove duplicate
             */
@@ -4187,7 +4258,7 @@ solver_solve(Solver *solv, Queue *job)
     
   /* if redoq.count == 0 we already found all recommended in the
    * solver run */
-  if (redoq.count || solv->dontinstallrecommended || !solv->dontshowinstalledrecommended)
+  if (redoq.count || solv->dontinstallrecommended || !solv->dontshowinstalledrecommended || solv->ignorealreadyrecommended)
     {
       Id rec, *recp, p, *pp;