Imported Upstream version 0.6.28
[platform/upstream/libsolv.git] / src / rules.c
index 6f35720..26b93ea 100644 (file)
@@ -114,24 +114,27 @@ unifyrules_sortcmp(const void *ap, const void *bp, void *dp)
 
   x = a->p - b->p;
   if (x)
-    return x;                         /* p differs */
+    return x;                          /* p differs */
 
   /* identical p */
-  if (a->d == 0 && b->d == 0)
-    return a->w2 - b->w2;             /* assertion: return w2 diff */
+  if (a->d == 0 && b->d == 0)          /* both assertions or binary */
+    return a->w2 - b->w2;
 
-  if (a->d == 0)                      /* a is assertion, b not */
+  if (a->d == 0)                       /* a is assertion or binary, b not */
     {
       x = a->w2 - pool->whatprovidesdata[b->d];
       return x ? x : -1;
     }
 
-  if (b->d == 0)                      /* b is assertion, a not */
+  if (b->d == 0)                       /* b is assertion or binary, a not */
     {
       x = pool->whatprovidesdata[a->d] - b->w2;
       return x ? x : 1;
     }
 
+  if (a->d == b->d)
+    return 0;
+
   /* compare whatprovidesdata */
   ad = pool->whatprovidesdata + a->d;
   bd = pool->whatprovidesdata + b->d;
@@ -161,22 +164,31 @@ solver_unifyrules(Solver *solv)
   int i, j;
   Rule *ir, *jr;
 
-  if (solv->nrules <= 2)              /* nothing to unify */
+  if (solv->nrules <= 2)               /* nothing to unify */
     return;
 
+  if (solv->recommendsruleq)
+    {
+      /* mis-use n2 as recommends rule marker */
+      for (i = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
+       ir->n2 = 0;
+      for (i = 0; i < solv->recommendsruleq->count; i++)
+       solv->rules[solv->recommendsruleq->elements[i]].n2 = 1;
+    }
+
   /* sort rules first */
   solv_sort(solv->rules + 1, solv->nrules - 1, sizeof(Rule), unifyrules_sortcmp, solv->pool);
 
-  /* prune rules
-   * i = unpruned
-   * j = pruned
-   */
+  /* prune rules */
   jr = 0;
   for (i = j = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
     {
       if (jr && !unifyrules_sortcmp(ir, jr, pool))
-       continue;                      /* prune! */
-      jr = solv->rules + j++;         /* keep! */
+       {
+         jr->n2 &= ir->n2;             /* bitwise-and recommends marker */
+         continue;                     /* prune! */
+       }
+      jr = solv->rules + j++;          /* keep! */
       if (ir != jr)
         *jr = *ir;
     }
@@ -185,8 +197,19 @@ solver_unifyrules(Solver *solv)
   POOL_DEBUG(SOLV_DEBUG_STATS, "pruned rules from %d to %d\n", solv->nrules, j);
 
   /* adapt rule buffer */
-  solv->nrules = j;
-  solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
+  solver_shrinkrules(solv, j);
+
+  if (solv->recommendsruleq)
+    {
+      /* rebuild recommendsruleq */
+      queue_empty(solv->recommendsruleq);
+      for (i = 1, ir = solv->rules + i; i < solv->nrules; i++, ir++)
+       if (ir->n2)
+         {
+           ir->n2 = 0;
+           queue_push(solv->recommendsruleq, i);
+         }
+    }
 
   /*
    * debug: log rule statistics
@@ -300,7 +323,7 @@ solver_addrule(Solver *solv, Id p, Id p2, Id d)
    * the work for unifyrules a bit easier */
   if (!solv->pkgrules_end)             /* we add pkg rules */
     {
-      r = solv->rules + solv->nrules - 1;
+      r = solv->rules + solv->lastpkgrule;
       if (d)
        {
          Id *dp;
@@ -335,6 +358,7 @@ solver_addrule(Solver *solv, Id p, Id p2, Id d)
          if (p == -p2)
            return 0;                   /* rule is self-fulfilling */
        }
+      solv->lastpkgrule = solv->nrules;
     }
 
   solv->rules = solv_extend(solv->rules, solv->nrules, 1, sizeof(Rule), RULES_BLOCK);
@@ -359,6 +383,7 @@ solver_shrinkrules(Solver *solv, int nrules)
 {
   solv->nrules = nrules;
   solv->rules = solv_extend_resize(solv->rules, solv->nrules, sizeof(Rule), RULES_BLOCK);
+  solv->lastpkgrule = 0;
 }
 
 /******************************************************************************
@@ -417,6 +442,19 @@ addpkgrule(Solver *solv, Id p, Id p2, Id d, int type, Id dep)
 
 #ifdef ENABLE_LINKED_PKGS
 
+static int
+addlinks_cmp(const void *ap, const void *bp, void *dp)
+{
+  Pool *pool = dp;
+  Id a = *(Id *)ap;
+  Id b = *(Id *)bp;
+  Solvable *sa = pool->solvables + a;
+  Solvable *sb = pool->solvables + b;
+  if (sa->name != sb->name)
+    return sa->name - sb->name;
+  return sa - sb;
+}
+
 static void
 addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m, Queue *workq)
 {
@@ -424,6 +462,8 @@ addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m
   int i;
   if (!qr->count)
     return;
+  if (qp->count > 1)
+    solv_sort(qp->elements, qp->count, sizeof(Id), addlinks_cmp, pool);
 #if 0
   printf("ADDLINKS %s\n -> %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
   for (i = 0; i < qr->count; i++)
@@ -439,9 +479,17 @@ addlinks(Solver *solv, Solvable *s, Id req, Queue *qr, Id prv, Queue *qp, Map *m
     addpkgrule(solv, -(s - pool->solvables), 0, pool_queuetowhatprovides(pool, qr), SOLVER_RULE_PKG_REQUIRES, req);
   if (qp->count > 1)
     {
-      Id d = pool_queuetowhatprovides(pool, qp);
-      for (i = 0; i < qr->count; i++)
-       addpkgrule(solv, -qr->elements[i], 0, d, SOLVER_RULE_PKG_REQUIRES, prv);
+      int j;
+      for (i = j = 0; i < qp->count; i = j)
+       {
+         Id d = pool->solvables[qp->elements[i]].name;
+         for (j = i + 1; j < qp->count; j++)
+           if (d != pool->solvables[qp->elements[j]].name)
+             break;
+         d = pool_ids2whatprovides(pool, qp->elements + i, j - i);
+         for (i = 0; i < qr->count; i++)
+           addpkgrule(solv, -qr->elements[i], 0, d, SOLVER_RULE_PKG_REQUIRES, prv);
+       }
     }
   else if (qp->count)
     {
@@ -549,13 +597,15 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
              if (!dp[j])
                continue;
            }
+         if (type == SOLVER_RULE_PKG_RECOMMENDS && !*dp)
+           continue;
          /* check if the rule contains both p and -p */
          for (j = 0; dp[j] != 0; j++)
            if (dp[j] == p)
              break;
          if (dp[j])
            continue;
-         addpkgrule(solv, -p, 0, dp - pool->whatprovidesdata, SOLVER_RULE_PKG_REQUIRES, dep);
+         addpkgrule(solv, -p, 0, dp - pool->whatprovidesdata, type, dep);
          /* push all non-visited providers on the work queue */
          if (m)
            for (; *dp; dp++)
@@ -648,7 +698,7 @@ add_complex_deprules(Solver *solv, Id p, Id dep, int type, int dontfix, Queue *w
 
 /*-------------------------------------------------------------------
  *
- * add (install) rules for solvable
+ * add dependency rules for solvable
  *
  * s: Solvable for which to add rules
  * m: m[s] = 1 for solvables which have rules, prevent rule duplication
@@ -674,6 +724,8 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
 
   Queue workq; /* list of solvables we still have to work on */
   Id workqbuf[64];
+  Queue prereqq;       /* list of pre-req ids to ignore */
+  Id prereqbuf[16];
 
   int i;
   int dontfix;         /* ignore dependency errors for installed solvables */
@@ -689,6 +741,8 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
   queue_init_buffer(&workq, workqbuf, sizeof(workqbuf)/sizeof(*workqbuf));
   queue_push(&workq, s - pool->solvables);     /* push solvable Id to work queue */
 
+  queue_init_buffer(&prereqq, prereqbuf, sizeof(prereqbuf)/sizeof(*prereqbuf));
+
   /* loop until there's no more work left */
   while (workq.count)
     {
@@ -739,11 +793,33 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
 
       if (s->requires)
        {
+         int filterpre = 0;
          reqp = s->repo->idarraydata + s->requires;
          while ((req = *reqp++) != 0)            /* go through all requires */
            {
              if (req == SOLVABLE_PREREQMARKER)   /* skip the marker */
-               continue;
+               {
+                 if (installed && s->repo == installed)
+                   {
+                     if (prereqq.count)
+                       queue_empty(&prereqq);
+                     solvable_lookup_idarray(s, SOLVABLE_PREREQ_IGNOREINST, &prereqq);
+                     filterpre = prereqq.count;
+                   }
+                 continue;
+               }
+             if (filterpre)
+               {
+                 /* check if this id is filtered. assumes that prereqq.count is small */
+                 for (i = 0; i < prereqq.count; i++)
+                   if (req == prereqq.elements[i])
+                     break;
+                 if (i < prereqq.count)
+                   {
+                     POOL_DEBUG(SOLV_DEBUG_RULE_CREATION, "package %s: ignoring filtered pre-req dependency %s\n", pool_solvable2str(pool, s), pool_dep2str(pool, req));
+                     continue;
+                   }
+               }
 
 #ifdef ENABLE_COMPLEX_DEPS
              if (pool_is_complex_dep(pool, req))
@@ -809,6 +885,48 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
            }
        }
 
+      if (s->recommends && solv->strongrecommends)
+       {
+         int start = solv->nrules;
+         solv->lastpkgrule = 0;
+         reqp = s->repo->idarraydata + s->recommends;
+         while ((req = *reqp++) != 0)            /* go through all recommends */
+           {
+#ifdef ENABLE_COMPLEX_DEPS
+             if (pool_is_complex_dep(pool, req))
+               {
+                 /* we have AND/COND deps, normalize */
+                 add_complex_deprules(solv, n, req, SOLVER_RULE_PKG_RECOMMENDS, dontfix, &workq, m);
+                 continue;
+               }
+#endif
+             dp = pool_whatprovides_ptr(pool, req);
+             if (*dp == SYSTEMSOLVABLE || !*dp)          /* always installed or not installable */
+               continue;
+             for (i = 0; dp[i] != 0; i++)
+               if (n == dp[i])
+                 break;
+             if (dp[i])
+               continue;               /* provided by itself, no need to add rule */
+             addpkgrule(solv, -n, 0, dp - pool->whatprovidesdata, SOLVER_RULE_PKG_RECOMMENDS, req);
+             if (m)
+               for (; *dp; dp++)
+                 if (!MAPTST(m, *dp))
+                   queue_push(&workq, *dp);
+           }
+         if (!solv->ruleinfoq && start < solv->nrules)
+           {
+             if (!solv->recommendsruleq)
+               {
+                 solv->recommendsruleq = solv_calloc(1, sizeof(Queue));
+                 queue_init(solv->recommendsruleq);
+               }
+             for (i = start; i < solv->nrules; i++)
+               queue_push(solv->recommendsruleq, i);
+             solv->lastpkgrule = 0;
+           }
+       }
+
       /* that's all we check for src packages */
       if (s->arch == ARCH_SRC || s->arch == ARCH_NOSRC)
        continue;
@@ -1011,6 +1129,7 @@ solver_addpkgrulesforsolvable(Solver *solv, Solvable *s, Map *m)
            }
        }
     }
+  queue_free(&prereqq);
   queue_free(&workq);
 }
 
@@ -1147,35 +1266,56 @@ solver_addpkgrulesforupdaters(Solver *solv, Solvable *s, Map *m, int allow_all)
  ***
  ***/
 
+static int
+dup_maykeepinstalled(Solver *solv, Solvable *s)
+{
+  Pool *pool = solv->pool;
+  Id ip, pp;
+
+  if (solv->dupmap.size && MAPTST(&solv->dupmap,  s - pool->solvables))
+    return 1;
+  /* is installed identical to a good one? */
+  FOR_PROVIDES(ip, pp, s->name)
+    {
+      Solvable *is = pool->solvables + ip;
+      if (is->evr != s->evr)
+       continue;
+      if (solv->dupmap.size)
+       {
+         if (!MAPTST(&solv->dupmap, ip))
+           continue;
+       }
+      else if (is->repo == pool->installed)
+       continue;
+      if (solvable_identical(s, is))
+       return 1;
+    }
+  return 0;
+}
+
+
 static Id
-finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs, int allow_all)
+finddistupgradepackages(Solver *solv, Solvable *s, Queue *qs)
 {
   Pool *pool = solv->pool;
-  int i;
+  int i, j;
 
-  policy_findupdatepackages(solv, s, qs, allow_all ? allow_all : 2);
-  if (!qs->count)
+  policy_findupdatepackages(solv, s, qs, 2);
+  if (qs->count)
     {
-      if (allow_all)
-        return 0;              /* orphaned, don't create feature rule */
-      /* check if this is an orphaned package */
-      policy_findupdatepackages(solv, s, qs, 1);
-      if (!qs->count)
-       return 0;               /* orphaned, don't create update rule */
-      qs->count = 0;
-      return -SYSTEMSOLVABLE;  /* supported but not installable */
+      /* remove installed packages we can't keep */
+      for (i = j = 0; i < qs->count; i++)
+       {
+         Solvable *ns = pool->solvables + qs->elements[i];
+         if (ns->repo == pool->installed && !dup_maykeepinstalled(solv, ns))
+           continue;
+         qs->elements[j++] = qs->elements[i];
+       }
+      queue_truncate(qs, j);
     }
-  if (allow_all)
-    return s - pool->solvables;
   /* check if it is ok to keep the installed package */
-  if (solv->dupmap.size && MAPTST(&solv->dupmap,  s - pool->solvables))
+  if (dup_maykeepinstalled(solv, s))
     return s - pool->solvables;
-  for (i = 0; i < qs->count; i++)
-    {
-      Solvable *ns = pool->solvables + qs->elements[i];
-      if (s->evr == ns->evr && solvable_identical(s, ns))
-        return s - pool->solvables;
-    }
   /* nope, it must be some other package */
   return -SYSTEMSOLVABLE;
 }
@@ -1217,6 +1357,73 @@ set_specialupdaters(Solver *solv, Solvable *s, Id d)
   solv->specialupdaters[s - solv->pool->solvables - installed->start] = d;
 }
 
+#ifdef ENABLE_LINKED_PKGS
+/* Check if this is a linked pseudo package. As it is linked, we do not need an update/feature rule */
+static inline int
+is_linked_pseudo_package(Solver *solv, Solvable *s)
+{
+  Pool *pool = solv->pool;
+  if (solv->instbuddy && solv->instbuddy[s - pool->solvables - solv->installed->start])
+    {
+      const char *name = pool_id2str(pool, s->name);
+      if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
+       return 1;
+    }
+  return 0;
+}
+#endif
+
+void
+solver_addfeaturerule(Solver *solv, Solvable *s)
+{
+  Pool *pool = solv->pool;
+  int i;
+  Id p;
+  Queue qs;
+  Id qsbuf[64];
+
+#ifdef ENABLE_LINKED_PKGS
+  if (is_linked_pseudo_package(solv, s))
+    {
+      solver_addrule(solv, 0, 0, 0);   /* no feature rules for those */
+      return;
+    }
+#endif
+  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
+  p = s - pool->solvables;
+  policy_findupdatepackages(solv, s, &qs, 1);
+  if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+    {
+      if (!dup_maykeepinstalled(solv, s))
+       {
+         for (i = 0; i < qs.count; i++)
+           {
+             Solvable *ns = pool->solvables + qs.elements[i];
+             if (ns->repo != pool->installed || dup_maykeepinstalled(solv, ns))
+               break;
+           }
+         if (i == qs.count)
+           {
+             solver_addrule(solv, 0, 0, 0);    /* this is an orphan */
+             queue_free(&qs);
+             return;
+           }
+       }
+    }
+  if (qs.count > 1)
+    {
+      Id d = pool_queuetowhatprovides(pool, &qs);
+      queue_free(&qs);
+      solver_addrule(solv, p, 0, d);   /* allow update of s */
+    }
+  else
+    {
+      Id d = qs.count ? qs.elements[0] : 0;
+      queue_free(&qs);
+      solver_addrule(solv, p, d, 0);   /* allow update of s */
+    }
+}
+
 /*-------------------------------------------------------------------
  *
  * add rule for update
@@ -1226,7 +1433,7 @@ set_specialupdaters(Solver *solv, Solvable *s, Id d)
  */
 
 void
-solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
+solver_addupdaterule(Solver *solv, Solvable *s)
 {
   /* installed packages get a special upgrade allowed rule */
   Pool *pool = solv->pool;
@@ -1234,48 +1441,37 @@ solver_addupdaterule(Solver *solv, Solvable *s, int allow_all)
   Queue qs;
   Id qsbuf[64];
   int isorphaned = 0;
+  Rule *r;
 
-  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
   p = s - pool->solvables;
-  /* find update candidates for 's' */
-  if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
-    p = finddistupgradepackages(solv, s, &qs, allow_all);
-  else
-    policy_findupdatepackages(solv, s, &qs, allow_all);
-
-#ifdef ENABLE_LINKED_PKGS
-  if (solv->instbuddy && solv->instbuddy[s - pool->solvables - solv->installed->start])
+  /* Orphan detection. We cheat by looking at the feature rule, which
+   * we already calculated */
+  r = solv->rules + solv->featurerules + (p - solv->installed->start);
+  if (!r->p)
     {
-      const char *name = pool_id2str(pool, s->name);
-      if (strncmp(name, "pattern:", 8) == 0 || strncmp(name, "application:", 12) == 0)
+#ifdef ENABLE_LINKED_PKGS
+      if (is_linked_pseudo_package(solv, s))
        {
-         /* a linked pseudo package. As it is linked, we do not need an update/feature rule */
-         /* nevertheless we set specialupdaters so we can update */
          solver_addrule(solv, 0, 0, 0);
-         if (!allow_all && qs.count)
-           {
-             if (p != -SYSTEMSOLVABLE)
-               queue_unshift(&qs, p);
-             if (qs.count)
-               set_specialupdaters(solv, s, pool_queuetowhatprovides(pool, &qs));
-           }
-         queue_free(&qs);
          return;
        }
-    }
 #endif
-
-  if (!allow_all && !p)                /* !p implies qs.count == 0 */
-    {
+      p = 0;
       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 */
-      queue_free(&qs);
       solver_addrule(solv, p, 0, 0);
       return;
     }
 
-  if (!allow_all && qs.count && solv->multiversion.size)
+  queue_init_buffer(&qs, qsbuf, sizeof(qsbuf)/sizeof(*qsbuf));
+  /* find update candidates for 's' */
+  if (solv->dupmap_all || (solv->dupinvolvedmap.size && MAPTST(&solv->dupinvolvedmap, p)))
+    p = finddistupgradepackages(solv, s, &qs);
+  else
+    policy_findupdatepackages(solv, s, &qs, 0);
+
+  if (qs.count && solv->multiversion.size)
     {
       int i, j;
 
@@ -2234,8 +2430,10 @@ jobtodisablelist(Solver *solv, Id how, Id what, Queue *q)
       if (!installed)
        break;
       if (select == SOLVER_SOLVABLE_ALL || (select == SOLVER_SOLVABLE_REPO && what == installed->repoid))
-       FOR_REPO_SOLVABLES(installed, p, s)
-         queue_push2(q, DISABLE_UPDATE, p);
+       {
+         FOR_REPO_SOLVABLES(installed, p, s)
+           queue_push2(q, DISABLE_UPDATE, p);
+       }
       FOR_JOB_SELECT(p, pp, select, what)
        if (pool->solvables[p].repo == installed)
          {
@@ -3287,6 +3485,12 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
 
   if (solv->bestupdatemap_all || solv->bestupdatemap.size)
     {
+      Map m;
+
+      if (solv->allowuninstall || solv->allowuninstall_all || solv->allowuninstallmap.size)
+       map_init(&m, pool->nsolvables);
+      else
+       map_init(&m, 0);
       FOR_REPO_SOLVABLES(installed, p, s)
        {
          Id d, p2, pp2;
@@ -3345,6 +3549,29 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
                    queue_pushunique(&q, q2.elements[j]);
                }
            }
+         if (solv->allowuninstall || solv->allowuninstall_all || (solv->allowuninstallmap.size && MAPTST(&solv->allowuninstallmap, p - installed->start)))
+           {
+             /* package is flagged both for allowuninstall and best, add negative rules */
+             d = q.count == 1 ? q.elements[0] : -pool_queuetowhatprovides(pool, &q);
+             for (i = 0; i < q.count; i++)
+               MAPSET(&m, q.elements[i]);
+             r = solv->rules + solv->featurerules + (p - installed->start);
+             if (!r->p)        /* identical to update rule? */
+               r = solv->rules + solv->updaterules + (p - installed->start);
+             FOR_RULELITERALS(p2, pp2, r)
+               {
+                 if (MAPTST(&m, p2))
+                   continue;
+                 if (d >= 0)
+                   solver_addrule(solv, -p2, d, 0);
+                 else
+                   solver_addrule(solv, -p2, 0, -d);
+                 queue_push(&r2pkg, p);
+               }
+             for (i = 0; i < q.count; i++)
+               MAPCLR(&m, q.elements[i]);
+             continue;
+           }
          p2 = queue_shift(&q);
          if (q.count < 2)
            solver_addrule(solv, p2, q.count ? q.elements[0] : 0, 0);
@@ -3352,6 +3579,7 @@ solver_addbestrules(Solver *solv, int havebestinstalljobs)
            solver_addrule(solv, p2, 0, pool_queuetowhatprovides(pool, &q));
          queue_push(&r2pkg, p);
        }
+      map_free(&m);
     }
   if (r2pkg.count)
     solv->bestrules_pkg = solv_memdup2(r2pkg.elements, r2pkg.count, sizeof(Id));
@@ -3742,9 +3970,8 @@ complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Q
            {
              if (!MAPTST(installedm, -p))
                break;
-             continue;
            }
-         if (MAPTST(im, p))
+         else if (p == ip)
            break;
        }
       if (!p)
@@ -3753,6 +3980,8 @@ complex_cleandeps_addback(Pool *pool, Id ip, Id req, Map *im, Map *installedm, Q
            {
              if (p < 0)
                continue;
+             if (MAPTST(im, p))
+               continue;
              if (!MAPTST(installedm, p))
                continue;
              if (p == ip || MAPTST(userinstalled, p - pool->installed->start))
@@ -3828,8 +4057,10 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
          what = 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_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);
@@ -4260,6 +4491,36 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
 #ifdef CLEANDEPSDEBUG
       printf("adding back %s\n", pool_solvable2str(pool, s));
 #endif
+      if (s->repo == installed && pool->implicitobsoleteusescolors)
+       {
+         Id a, bestarch = 0;
+         FOR_PROVIDES(p, pp, s->name)
+           {
+             Solvable *ps = pool->solvables + p;
+             if (ps->name != s->name || ps->repo == installed)
+               continue;
+             a = ps->arch;
+             a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
+             if (a && a != 1 && (!bestarch || a < bestarch))
+               bestarch = a;
+           }
+         if (bestarch && (s->arch > pool->lastarch || pool->id2arch[s->arch] != bestarch))
+           {
+             FOR_PROVIDES(p, pp, s->name)
+               {
+                 Solvable *ps = pool->solvables + p;
+                 if (ps->repo == installed && ps->name == s->name && ps->evr == s->evr && ps->arch != s->arch && ps->arch < pool->lastarch && pool->id2arch[ps->arch] == bestarch)
+                   if (!MAPTST(&im, p))
+                     {
+#ifdef CLEANDEPSDEBUG
+                       printf("%s lockstep %s\n", pool_solvid2str(pool, ip), pool_solvid2str(pool, p));
+#endif
+                       MAPSET(&im, p);
+                       queue_push(&iq, p);
+                     }
+               }
+           }
+       }
       if (s->requires)
        {
          reqp = s->repo->idarraydata + s->requires;
@@ -4273,12 +4534,14 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
                }
 #endif
              FOR_PROVIDES(p, pp, req)
-               if (MAPTST(&im, p))
+               if (p == ip)
                  break;
              if (p)
                continue;
              FOR_PROVIDES(p, pp, req)
                {
+                 if (MAPTST(&im, p))
+                   continue;
                  if (MAPTST(&installedm, p))
                    {
                      if (p == ip)
@@ -4307,12 +4570,14 @@ solver_createcleandepsmap(Solver *solv, Map *cleandepsmap, int unneeded)
                }
 #endif
              FOR_PROVIDES(p, pp, req)
-               if (MAPTST(&im, p))
+               if (p == ip)
                  break;
              if (p)
                continue;
              FOR_PROVIDES(p, pp, req)
                {
+                 if (MAPTST(&im, p))
+                   continue;
                  if (MAPTST(&installedm, p))
                    {
                      if (p == ip)