obey droporphaned map in keep_orhans mode
[platform/upstream/libsolv.git] / src / rules.c
index 2be3027..264a1b7 100644 (file)
@@ -261,18 +261,17 @@ hashrule(Solver *solv, Id p, Id d, int n)
  *   Install:    p > 0, d = 0   (A)             user requested install
  *   Remove:     p < 0, d = 0   (-A)            user requested remove (also: uninstallable)
  *   Requires:   p < 0, d > 0   (-A|B1|B2|...)  d: <list of providers for requirement of p>
+ *   Requires:   p > 0, d < 0   (B|-A)         hack to save a whatprovides allocation, gets converted into (-A|B)
  *   Updates:    p > 0, d > 0   (A|B1|B2|...)   d: <list of updates for solvable p>
  *   Conflicts:  p < 0, d < 0   (-A|-B)         either p (conflict issuer) or d (conflict provider) (binary rule)
  *                                              also used for obsoletes
- *   ?:          p > 0, d < 0   (A|-B)
- *   No-op ?:    p = 0, d = 0   (null)          (used as policy rule placeholder)
+ *   No-op ?:    p = 0, d = 0   (null)          (used as placeholder in update/feature rules)
  *
  *   resulting watches:
  *   ------------------
  *   Direct assertion (no watch needed) --> d = 0, w1 = p, w2 = 0
  *   Binary rule: p = first literal, d = 0, w2 = second literal, w1 = p
  *   every other : w1 = p, w2 = whatprovidesdata[d];
- *   Disabled rule: w1 = 0
  *
  *   always returns a rule for non-rpm rules
  */
@@ -287,7 +286,7 @@ solver_addrule(Solver *solv, Id p, Id d)
   int n = 0;                          /* number of literals in rule - 1
                                          0 = direct assertion (single literal)
                                          1 = binary rule
-                                         >1 =
+                                         >1 = multi-literal rule
                                        */
 
   /* it often happenes that requires lead to adding the same rpm rule
@@ -301,69 +300,62 @@ solver_addrule(Solver *solv, Id p, Id d)
        return r;
     }
 
-    /*
-     * compute number of literals (n) in rule
-     */
-
+  /* compute number of literals (n) in rule */
   if (d < 0)
     {
-      /* always a binary rule */
+      if (p == -d)
+       return 0;                       /* rule is self-fulfilling */
       if (p == d)
-       return 0;                      /* ignore self conflict */
-      n = 1;
+       d = 0;                          /* normalize to assertion */
+      else
+        n = 1;                         /* binary rule */
     }
   else if (d > 0)
     {
       for (dp = pool->whatprovidesdata + d; *dp; dp++, n++)
        if (*dp == -p)
          return 0;                     /* rule is self-fulfilling */
-       
       if (n == 1)                      /* convert to binary rule */
        d = dp[-1];
     }
 
   if (n == 1 && p > d && !solv->rpmrules_end)
     {
-      /* smallest literal first so we can find dups */
+      /* put smallest literal first so we can find dups */
       n = p; p = d; d = n;             /* p <-> d */
       n = 1;                          /* re-set n, was used as temp var */
     }
 
   /*
-   * check for duplicate
+   * check for duplicate (r is only set if we're adding rpm rules)
    */
-
-  /* check if the last added rule (r) is exactly the same as what we're looking for. */
-  if (r && n == 1 && !r->d && r->p == p && r->w2 == d)
-    return r;  /* binary rule */
-
-    /* have n-ary rule with same first literal, check other literals */
-  if (r && n > 1 && r->d && r->p == p)
+  if (r)
     {
-      /* Rule where d is an offset in whatprovidesdata */
-      Id *dp2;
-      if (d == r->d)
-       return r;
-      dp2 = pool->whatprovidesdata + r->d;
-      for (dp = pool->whatprovidesdata + d; *dp; dp++, dp2++)
-       if (*dp != *dp2)
-         break;
-      if (*dp == *dp2)
+      /* check if the last added rule (r) is exactly the same as what we're looking for. */
+      if (n == 1 && !r->d && r->p == p && r->w2 == d)
        return r;
-   }
+      /* have n-ary rule with same first literal, check other literals */
+      if (n > 1 && r->d && r->p == p)
+       {
+         /* Rule where d is an offset in whatprovidesdata */
+         Id *dp2;
+         if (d == r->d)
+           return r;
+         dp2 = pool->whatprovidesdata + r->d;
+         for (dp = pool->whatprovidesdata + d; *dp; dp++, dp2++)
+           if (*dp != *dp2)
+             break;
+         if (*dp == *dp2)
+           return r;
+       }
+    }
 
   /*
-   * allocate new rule
+   * allocate new rule r
    */
-
-  /* extend rule buffer */
   solv->rules = solv_extend(solv->rules, solv->nrules, 1, sizeof(Rule), RULES_BLOCK);
   r = solv->rules + solv->nrules++;    /* point to rule space */
 
-    /*
-     * r = new rule
-     */
-
   r->p = p;
   if (n == 0)
     {
@@ -902,7 +894,7 @@ solver_addrpmrulesforsolvable(Solver *solv, Solvable *s, Map *m)
                      p = -makemultiversionconflict(solv, p, con);
                    }
                   /* rule: -n|-p: either solvable _or_ provider of conflict */
-                 addrpmrule(solv, -n, -p, SOLVER_RULE_RPM_PACKAGE_CONFLICT, con);
+                 addrpmrule(solv, -n, p == SYSTEMSOLVABLE ? 0 : -p, SOLVER_RULE_RPM_PACKAGE_CONFLICT, con);
                }
            }
        }
@@ -1225,9 +1217,11 @@ solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
   if (solv->dupmap_all)
     p = finddistupgradepackages(solv, s, &qs, allow_all);
   else
-    policy_findupdatepackages(solv, s, &qs, allow_all);
-  if (!allow_all && !solv->dupmap_all && solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
-    addduppackages(solv, s, &qs);
+    {
+      policy_findupdatepackages(solv, s, &qs, allow_all);
+      if (!allow_all && solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p))
+        addduppackages(solv, s, &qs);
+    }
 
 #ifdef ENABLE_LINKED_PKGS
   if (solv->instbuddy && solv->instbuddy[s - pool->solvables - solv->installed->start])
@@ -1252,52 +1246,65 @@ solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
     }
 #endif
 
+  if (!allow_all && !p && solv->dupmap_all)
+    {
+      queue_push(&solv->orphaned, s - pool->solvables);                /* an orphaned package */
+      if (solv->keep_orphans && !(solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, s - pool->solvables - solv->installed->start))))
+       p = s - pool->solvables;        /* keep this orphaned package installed */
+    }
+
   if (!allow_all && qs.count && solv->multiversion.size)
     {
       int i, j;
 
-      d = pool_queuetowhatprovides(pool, &qs);
-      /* filter out all multiversion packages as they don't update */
-      for (i = j = 0; i < qs.count; i++)
+      for (i = 0; i < qs.count; i++)
+       if (MAPTST(&solv->multiversion, qs.elements[i]))
+         break;
+      if (i < qs.count)
        {
-         if (MAPTST(&solv->multiversion, qs.elements[i]))
-           {
-             Solvable *ps = pool->solvables + qs.elements[i];
-             /* if keepexplicitobsoletes is set and the name is different,
-              * we assume that there is an obsoletes. XXX: not 100% correct */
-             if (solv->keepexplicitobsoletes && ps->name != s->name)
+         /* filter out all multiversion packages as they don't update */
+         d = pool_queuetowhatprovides(pool, &qs);
+         for (j = i; i < qs.count; i++)
+            {
+             if (MAPTST(&solv->multiversion, qs.elements[i]))
                {
-                 qs.elements[j++] = qs.elements[i];
-                 continue;
+                 Solvable *ps = pool->solvables + qs.elements[i];
+                 /* if keepexplicitobsoletes is set and the name is different,
+                  * we assume that there is an obsoletes. XXX: not 100% correct */
+                 if (solv->keepexplicitobsoletes && ps->name != s->name)
+                   {
+                     qs.elements[j++] = qs.elements[i];
+                     continue;
+                   }
+                 /* it's ok if they have same nevra */
+                 if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
+                   continue;
                }
-             /* it's ok if they have same nevra */
-             if (ps->name != s->name || ps->evr != s->evr || ps->arch != s->arch)
-               continue;
+             qs.elements[j++] = qs.elements[i];
            }
-         qs.elements[j++] = qs.elements[i];
-       }
-      if (j < qs.count)
-       {
-         if (d && solv->installed && s->repo == solv->installed &&
-              (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, s - pool->solvables - solv->installed->start))))
+         if (j < qs.count)
            {
-             if (!solv->specialupdaters)
-               solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
-             solv->specialupdaters[s - pool->solvables - solv->installed->start] = d;
+             if (d && solv->installed && s->repo == solv->installed &&
+                 (solv->updatemap_all || (solv->updatemap.size && MAPTST(&solv->updatemap, s - pool->solvables - solv->installed->start))))
+               {
+                 if (!solv->specialupdaters)
+                   solv->specialupdaters = solv_calloc(solv->installed->end - solv->installed->start, sizeof(Id));
+                 solv->specialupdaters[s - pool->solvables - solv->installed->start] = d;
+               }
+             if (j == 0 && p == -SYSTEMSOLVABLE && solv->dupmap_all)
+               {
+                 queue_push(&solv->orphaned, s - pool->solvables);     /* also treat as orphaned */
+                 j = qs.count;
+               }
+             qs.count = j;
            }
-         if (j == 0 && p == -SYSTEMSOLVABLE && solv->dupmap_all)
+         else if (p != -SYSTEMSOLVABLE)
            {
-             queue_push(&solv->orphaned, s - pool->solvables); /* treat as orphaned */
-             j = qs.count;
+             /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
+             queue_free(&qs);
+             solver_addrule(solv, p, d);       /* allow update of s */
+             return;
            }
-         qs.count = j;
-       }
-      else if (p != -SYSTEMSOLVABLE)
-       {
-         /* could fallthrough, but then we would do pool_queuetowhatprovides twice */
-         queue_free(&qs);
-         solver_addrule(solv, p, d);   /* allow update of s */
-         return;
        }
     }
   if (qs.count && p == -SYSTEMSOLVABLE)
@@ -2426,6 +2433,9 @@ addrpmruleinfo(Solver *solv, Id p, Id d, int type, Id dep)
          if (p != op || w2 != ow2)
            return;
        }
+      /* should use a different type instead */
+      if (type == SOLVER_RULE_RPM_PACKAGE_CONFLICT && !w2)
+       w2 = -SYSTEMSOLVABLE;
     }
   /* yep, rule matches. record info */
   queue_push(solv->ruleinfoq, type);
@@ -2482,8 +2492,12 @@ getrpmruleinfos(Solver *solv, Rule *r, Queue *rq)
 #ifdef ENABLE_LINKED_PKGS
   FOR_RULELITERALS(l, pp, r)
     {
-      if (l < 0 || l != r->p)
-       break;
+      if (l < 0)
+       {
+         if (l == r->p)
+           continue;
+         break;
+       }
       if (!strchr(pool_id2str(pool, pool->solvables[l].name), ':') || !has_package_link(pool, pool->solvables + l))
        break;
       add_package_link(solv, pool->solvables + l, 0, 0);
@@ -2853,7 +2867,7 @@ solver_addchoicerules(Solver *solv)
   for (rid = 1; rid < solv->rpmrules_end ; rid++)
     {
       r = solv->rules + rid;
-      if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
+      if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 <= 0))
        continue;       /* only look at requires rules */
       /* solver_printrule(solv, SOLV_DEBUG_RESULT, r); */
       queue_empty(&q);
@@ -3571,12 +3585,14 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
          if (!strncmp("product:", pool_id2str(pool, s->name), 8))
            {
              MAPSET(&userinstalled, p - installed->start);
-             if (pool->nscallback)
+#ifdef ENABLE_LINKED_PKGS
+             if (solv->instbuddy && solv->instbuddy[p - installed->start] > 1)
                {
-                 Id buddy = pool->nscallback(pool, pool->nscallbackdata, NAMESPACE_PRODUCTBUDDY, p);
-                 if (buddy >= installed->start && buddy < installed->end && pool->solvables[buddy].repo == installed)
+                 Id buddy = solv->instbuddy[p - installed->start];
+                 if (buddy >= installed->start && buddy < installed->end)
                    MAPSET(&userinstalled, buddy - installed->start);
                }
+#endif
            }
        }
     }
@@ -4313,4 +4329,96 @@ solver_get_unneeded(Solver *solv, Queue *unneededq, int filtered)
   map_free(&cleandepsmap);
 }
 
-/* EOF */
+
+void
+solver_breakorphans(Solver *solv)
+{
+  Pool *pool = solv->pool;
+  Repo *installed = solv->installed;
+  int i, rid;
+  Map m;
+
+  if (!installed || solv->droporphanedmap_all)
+    return;
+  solv->brokenorphanrules = solv_calloc(1, sizeof(Queue));
+  queue_init(solv->brokenorphanrules);
+  map_init(&m, installed->end - installed->start);
+  for (i = 0; i < solv->orphaned.count; i++)
+    {
+      Id p = solv->orphaned.elements[i];
+      if (pool->solvables[p].repo != installed)
+       continue;
+      if (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, p - installed->start))
+       continue;
+      MAPSET(&m, p - installed->start);
+    }
+  for (rid = 1; rid < solv->rpmrules_end ; rid++)
+    {
+      Id p, *dp;
+      Rule *r = solv->rules + rid;
+      /* ignore non-deps and simple conflicts */
+      if (r->p >= 0 || ((r->d == 0 || r->d == -1) && r->w2 < 0))
+       continue;
+      p = -r->p;
+      if (p < installed->start || p >= installed->end || !MAPTST(&m, p - installed->start))
+       {
+         /* need to check other literals */
+         if (r->d == 0 || r->d == -1)
+           continue;
+         for (dp = pool->whatprovidesdata + (r->d < 0 ? -r->d - 1 : r->d); *dp < 0; dp++)
+           {
+             p = -*dp;
+             if (p >= installed->start && p < installed->end && MAPTST(&m, p - installed->start))
+               break;
+           }
+         if (*dp >= 0)
+           continue;
+       }
+      /* ok, disable this rule */
+      queue_push(solv->brokenorphanrules, rid);
+      if (r->d >= 0)
+       solver_disablerule(solv, r);
+    }
+  map_free(&m);
+  if (!solv->brokenorphanrules->count)
+    {
+      queue_free(solv->brokenorphanrules);
+      solv->brokenorphanrules = solv_free(solv->brokenorphanrules);
+    }
+}
+
+void
+solver_check_brokenorphanrules(Solver *solv, Queue *dq)
+{
+  Pool *pool = solv->pool;
+  int i;
+  Id l, pp;
+  
+  queue_empty(dq);
+  if (!solv->brokenorphanrules)
+    return;
+  for (i = 0; i < solv->brokenorphanrules->count; i++)
+    {
+      int rid = solv->brokenorphanrules->elements[i];
+      Rule *r = solv->rules + rid;
+      FOR_RULELITERALS(l, pp, r)
+       {
+         if (l < 0)
+           {
+             if (solv->decisionmap[-l] <= 0)
+               break;
+           }
+         else
+           {
+             if (solv->decisionmap[l] > 0 && pool->solvables[l].repo != solv->installed)
+               break;
+           }
+       }
+      if (l)
+       continue;
+      FOR_RULELITERALS(l, pp, r)
+        if (l > 0 && solv->decisionmap[l] == 0 && pool->solvables[l].repo != solv->installed)
+         queue_pushunique(dq, l);
+    }
+}
+