]> git.ipfire.org Git - thirdparty/libsolv.git/commitdiff
Do favor evaluation before pruning
authorMichael Schroeder <mls@suse.de>
Mon, 10 Dec 2018 14:05:39 +0000 (15:05 +0100)
committerMichael Schroeder <mls@suse.de>
Mon, 10 Dec 2018 14:05:39 +0000 (15:05 +0100)
This allows to support favoring single packages. As a side effect,
we'll have the best version displayed as alternative.

This changes the code to use a favor id for every solvable, which
means that it uses quite a bit of memory but makes the code much
easier and faster.

src/policy.c
src/policy.h
src/solver.c
src/solver.h
test/testcases/favor/recommends2.t [new file with mode: 0644]
test/testcases/favor/single.t [new file with mode: 0644]

index 191327a1d6ddf118e220405b3db4802f94e1d2b1..45c7357de71ee2e2881ee139d3c03e2d8baa51fa 100644 (file)
@@ -454,86 +454,18 @@ prefer_suggested(Solver *solv, Queue *plist)
 }
 
 static int
-sort_by_favorq_cmp(const void *ap, const void *bp, void *dp)
+sort_by_favor_cmp(const void *ap, const void *bp, void *dp)
 {
   const Id *a = ap, *b = bp, *d = dp;
   return d[b[0]] - d[a[0]];
 }
 
-static void
-sort_by_favorq(Queue *favorq, Id *el, int cnt)
-{
-  int i;
-  /* map to offsets into favorq */
-  for (i = 0; i < cnt; i++)
-    {
-      Id p = el[i];
-      /* lookup p in sorted favorq */
-      int med = 0, low = 0;
-      int high = favorq->count / 2;
-      while (low != high)
-       {
-         med = (low + high) / 2;
-         Id pp = favorq->elements[2 * med];
-         if (pp < p)
-           low = med;
-         else if (pp > p)
-           high = med;
-         else
-           break;
-       }
-      while(med && favorq->elements[2 * med - 2] == p)
-       med--;
-      if (favorq->elements[2 * med] == p)
-        el[i] = 2 * med + 1;
-      else
-        el[i] = 0;     /* hmm */
-    }
-  /* sort by position */
-  solv_sort(el, cnt, sizeof(Id), sort_by_favorq_cmp, favorq->elements);
-  /* map back */
-  for (i = 0; i < cnt; i++)
-    if (el[i])
-      el[i] = favorq->elements[el[i] - 1];
-}
-
 /* bring favored packages to front and disfavored packages to back */
 void
 policy_prefer_favored(Solver *solv, Queue *plist)
 {
-  int i, fav, disfav, count;
-  if (!solv->favormap.size)
-    return;
-  for (i = fav = disfav = 0, count = plist->count; i < count; i++)
-    {
-      Id p = plist->elements[i];
-      if (!MAPTST(&solv->favormap, p))
-       continue;
-      if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, p))
-       {
-         /* disfavored package. bring to back */
-        if (i < plist->count - 1)
-           {
-             memmove(plist->elements + i, plist->elements + i + 1, (plist->count - 1 - i) * sizeof(Id));
-             plist->elements[plist->count - 1] = p;
-           }
-         i--;
-         count--;
-         disfav++;
-       }
-      else
-       {
-         /* favored package. bring to front */
-         if (i > fav)
-           memmove(plist->elements + fav + 1, plist->elements + fav, (i - fav) * sizeof(Id));
-         plist->elements[fav++] = p;
-       }
-    }
-  /* if we have multiple favored/disfavored packages, sort by favorq index */
-  if (fav > 1)
-    sort_by_favorq(solv->favorq, plist->elements, fav);
-  if (disfav > 1)
-    sort_by_favorq(solv->favorq, plist->elements + plist->count - disfav, disfav);
+  if (solv->favormap && plist->count > 1)
+    solv_sort(plist->elements, plist->count, sizeof(Id), sort_by_favor_cmp, solv->favormap);
 }
 
 /*
@@ -1300,6 +1232,37 @@ urpm_reorder(Solver *solv, Queue *plist)
   queue_truncate(plist, count);
 }
 
+/* support multiple favor groups by calling policy_filter_unwanted on
+ * each of them and combining the result */
+static void
+policy_filter_unwanted_favored(Solver *solv, Queue *plist, int mode)
+{
+  int i, j, f;
+  Queue qin, qprune;
+  queue_init_clone(&qin, plist);
+  queue_empty(plist);
+  /* sort by favor group */
+  solv_sort(qin.elements, qin.count, sizeof(Id), sort_by_favor_cmp, solv->favormap);
+  /* go over groups */
+  queue_init(&qprune);
+  for (i = 0; i < qin.count; i = j)
+    {
+      /* find end of group */
+      f = solv->favormap[qin.elements[i]];
+      for (j = i + 1; j < qin.count; j++)
+       if (solv->favormap[qin.elements[j]] != f)
+         break;
+      /* prune this group */
+      queue_empty(&qprune);
+      queue_insertn(&qprune, 0, j, qin.elements);
+      policy_filter_unwanted(solv, &qprune, mode | POLICY_MODE_FAVOR_REC);
+      for (i = 0; i < qprune.count; i++)
+       if (solv->favormap[qprune.elements[i]] == f)
+         queue_push(plist, qprune.elements[i]);
+    }
+  queue_free(&qprune);
+  queue_free(&qin);
+}
 
 /*
  *  POLICY_MODE_CHOOSE:     default, do all pruning steps
@@ -1321,6 +1284,21 @@ policy_filter_unwanted(Solver *solv, Queue *plist, int mode)
       policy_prefer_favored(solv, plist);
       return;
     }
+  if (mode & POLICY_MODE_FAVOR_REC)
+    mode ^= POLICY_MODE_FAVOR_REC;
+  else if (solv->favormap && plist->count > 1)
+    {
+      /* check if we have multiple favor groups */
+      int i, f = solv->favormap[plist->elements[0]];
+      for (i = 1; i < plist->count; i++)
+       if (solv->favormap[plist->elements[i]] != f)
+         break;
+      if (i < plist->count)
+       {
+         policy_filter_unwanted_favored(solv, plist, mode);
+         return;
+       }
+    }
   if (plist->count > 1)
     {
       if (mode != POLICY_MODE_SUGGEST)
index 68f4db93d1f3091c2e6b5add446f8eacfa2615d3..3ae1005a1184b0515bd09a6ddb9f6a360e9a2dcf 100644 (file)
@@ -21,6 +21,7 @@ extern "C" {
 #define POLICY_MODE_SUGGEST    2
 #define POLICY_MODE_CHOOSE_NOREORDER   3       /* internal, do not use */
 #define POLICY_MODE_SUPPLEMENT 4       /* internal, do not use */
+#define POLICY_MODE_FAVOR_REC  (1 << 30)       /* internal, do not use */
 
 
 #define POLICY_ILLEGAL_DOWNGRADE       1
index 57fa3e494a8faf47cd010689b79fe6c4d41136a1..cf41c9150ee6b82bbb6f92ee1f004ec3764e1641 100644 (file)
@@ -243,7 +243,7 @@ makeruledecisions(Solver *solv, int disablerules)
                    continue;
                  if (solv->weakrulemap.size && MAPTST(&solv->weakrulemap, i))     /* weak: silently ignore */
                    continue;
-                   
+
                  POOL_DEBUG(SOLV_DEBUG_UNSOLVABLE, " - disabling rule #%d\n", i);
                  solver_printruleclass(solv, SOLV_DEBUG_UNSOLVABLE, solv->rules + i);
                  solver_recordproblem(solv, i);
@@ -1370,9 +1370,8 @@ solver_free(Solver *solv)
   map_free(&solv->droporphanedmap);
   map_free(&solv->cleandepsmap);
   map_free(&solv->allowuninstallmap);
-  map_free(&solv->favormap);
-  map_free(&solv->isdisfavormap);
 
+  solv_free(solv->favormap);
   solv_free(solv->decisionmap);
   solv_free(solv->rules);
   solv_free(solv->watches);
@@ -2058,14 +2057,12 @@ static void
 prune_disfavored(Solver *solv, Queue *plist)
 {
   int i, j;
-  if (!solv->isdisfavormap.size)
-    return;
-  for (i = j = 0; i < plist->count; i++) 
-    {    
+  for (i = j = 0; i < plist->count; i++)
+    {
       Id p = plist->elements[i];
-      if (!MAPTST(&solv->isdisfavormap, p))
-        plist->elements[j++] = p; 
-    }    
+      if (solv->favormap[p] >= 0)
+        plist->elements[j++] = p;
+    }
   if (i != j)
     queue_truncate(plist, j);
 }
@@ -2141,14 +2138,14 @@ resolve_weak(Solver *solv, int level, int disablerules, Queue *dq, Queue *dqs, i
            continue;
          if (solv->process_orphans && solv->installed && s->repo == solv->installed && (solv->droporphanedmap_all || (solv->droporphanedmap.size && MAPTST(&solv->droporphanedmap, i - solv->installed->start))))
            continue;
-         if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, i))
+         if (solv->havedisfavored && solv->favormap[i] < 0)
            continue;   /* disfavored supplements, do not install */
          queue_push(dqs, i);
        }
     }
 
   /* filter out disfavored recommended packages */
-  if (dq->count && solv->isdisfavormap.size)
+  if (dq->count && solv->havedisfavored)
     prune_disfavored(solv, dq);
 
   /* filter out all packages obsoleted by installed packages */
@@ -2617,7 +2614,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
          if (rerun)
            continue;
        }
-         
+
      /* one final pass to make sure we decided all installed packages */
       if (solv->installed)
        {
@@ -2700,7 +2697,7 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                      lastsi = -1;
                      break;
                    }
-                 if (solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, p))
+                 if (solv->havedisfavored && solv->favormap[p] < 0)
                    continue;
                  if (lastsi < 0 && (MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
                    lastsi = i;
@@ -2714,9 +2711,8 @@ solver_run_sat(Solver *solv, int disablerules, int doweak)
                      p = -solv->branches.elements[i];
                      if (p <= 0 || solv->decisionmap[p] != l + 1)
                        continue;
-                     if (solv->favormap.size && MAPTST(&solv->favormap, p))
-                       if (!(solv->isdisfavormap.size && MAPTST(&solv->isdisfavormap, p)))
-                         continue;     /* current selection is favored */
+                     if (solv->favormap && solv->favormap[p] > solv->favormap[solv->branches.elements[lastsi]])
+                       continue;       /* current selection is more favored */
                      if (!(MAPTST(&solv->recommendsmap, p) || solver_is_supplementing(solv, pool->solvables + p)))
                        {
                          lasti = lastsi;
@@ -3187,7 +3183,7 @@ add_complex_jobrules(Solver *solv, Id dep, int flags, int jobidx, int weak)
 
 /* sort by package id, last entry wins */
 static int
-setup_favormaps_cmp(const void *ap, const void *bp, void *dp)
+setup_favormap_cmp(const void *ap, const void *bp, void *dp)
 {
   const Id *a = ap, *b = bp;
   if ((*a - *b) != 0)
@@ -3196,28 +3192,25 @@ setup_favormaps_cmp(const void *ap, const void *bp, void *dp)
 }
 
 static void
-setup_favormaps(Solver *solv)
+setup_favormap(Solver *solv)
 {
   Queue *q = solv->favorq;
   Pool *pool = solv->pool;
   int i;
   Id oldp = 0;
   if (q->count > 2)
-    solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), setup_favormaps_cmp, solv);
-  map_grow(&solv->favormap, pool->nsolvables);
+    solv_sort(q->elements, q->count / 2, 2 * sizeof(Id), setup_favormap_cmp, solv);
+  solv->favormap = solv_calloc(pool->nsolvables, sizeof(Id));
+  solv->havedisfavored = 0;
   for (i = 0; i < q->count; i += 2)
     {
       Id p = q->elements[i];
       if (p == oldp)
        continue;
       oldp = p;
-      MAPSET(&solv->favormap, p);
+      solv->favormap[p] = q->elements[i + 1];
       if (q->elements[i + 1] < 0)
-       {
-         if (!solv->isdisfavormap.size)
-           map_grow(&solv->isdisfavormap, pool->nsolvables);
-         MAPSET(&solv->isdisfavormap, p);
-       }
+       solv->havedisfavored = 1;
     }
 }
 
@@ -3294,8 +3287,7 @@ solver_solve(Solver *solv, Queue *job)
   map_zerosize(&solv->allowuninstallmap);
   map_zerosize(&solv->cleandepsmap);
   map_zerosize(&solv->weakrulemap);
-  map_zerosize(&solv->favormap);
-  map_zerosize(&solv->isdisfavormap);
+  solv->favormap = solv_free(solv->favormap);
   queue_empty(&solv->weakruleq);
   solv->watches = solv_free(solv->watches);
   queue_empty(&solv->ruletojob);
@@ -3839,14 +3831,12 @@ solver_solve(Solver *solv, Queue *job)
          POOL_DEBUG(SOLV_DEBUG_JOB, "job: %s %s\n", (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? "favor" : "disfavor", solver_select2str(pool, select, what));
          FOR_JOB_SELECT(p, pp, select, what)
            {
-             int j;
              if (!solv->favorq)
                {
                  solv->favorq = solv_calloc(1, sizeof(Queue));
                  queue_init(solv->favorq);
                }
-             j = solv->favorq->count + 1;
-             queue_push2(solv->favorq, p, (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? j : -j);
+             queue_push2(solv->favorq, p, (how & SOLVER_JOBMASK) == SOLVER_FAVOR ? i + 1 : -(i + 1));
            }
          break;
        default:
@@ -3869,9 +3859,9 @@ solver_solve(Solver *solv, Queue *job)
   assert(solv->ruletojob.count == solv->nrules - solv->jobrules);
   solv->jobrules_end = solv->nrules;
 
-  /* transform favorq into two maps */
+  /* sort favorq and transform it into two maps */
   if (solv->favorq)
-    setup_favormaps(solv);
+    setup_favormap(solv);
 
   /* now create infarch and dup rules */
   if (!solv->noinfarchcheck)
index 6fc2e9f009b0878a20edab173f14cad90c15ba27..10b4506d46f033c0a64db49d27338aaad3f894eb 100644 (file)
@@ -202,8 +202,8 @@ struct s_Solver {
   int allowuninstall_all;
 
   Queue *favorq;
-  Map favormap;                                /* favored / disfavored packages */
-  Map isdisfavormap;
+  Id *favormap;                                /* favor job index, > 0: favored, < 0: disfavored */
+  int havedisfavored;                  /* do we have disfavored packages? */
 
   int installedpos;                    /* for resolve_installed */
   int do_extra_reordering;             /* reorder for future installed packages */
diff --git a/test/testcases/favor/recommends2.t b/test/testcases/favor/recommends2.t
new file mode 100644 (file)
index 0000000..9950661
--- /dev/null
@@ -0,0 +1,20 @@
+repo system 0 empty
+repo available 0 testtags <inline>
+#>=Pkg: a 1 1 noarch
+#>=Req: b
+#>=Rec: b1
+#>=Pkg: b1 1 1 noarch
+#>=Prv: b
+#>=Pkg: b2 1 1 noarch
+#>=Prv: b
+system x86_64 rpm system
+poolflags implicitobsoleteusescolors
+solverflags ignorerecommended
+job install pkg a-1-1.noarch@available
+job favor name b2
+result transaction,problems,alternatives <inline>
+#>alternative 64eb4d87  0 a-1-1.noarch@available requires b
+#>alternative 64eb4d87  1 + b2-1-1.noarch@available
+#>alternative 64eb4d87  2   b1-1-1.noarch@available
+#>install a-1-1.noarch@available
+#>install b2-1-1.noarch@available
diff --git a/test/testcases/favor/single.t b/test/testcases/favor/single.t
new file mode 100644 (file)
index 0000000..3a1d93b
--- /dev/null
@@ -0,0 +1,30 @@
+repo system 0 empty
+repo available 0 testtags <inline>
+#>=Pkg: gcc 5 1 noarch
+#>=Prv: gcc
+#>=Pkg: gcc 6 1 noarch
+#>=Prv: gcc
+#>=Pkg: build-tools 1 1 noarch
+#>=Req: gcc
+system unset * system
+
+job install name build-tools
+job favor pkg gcc-5-1.noarch@available
+result transaction,problems,alternatives <inline>
+#>alternative 6e50ec1a  0 build-tools-1-1.noarch@available requires gcc
+#>alternative 6e50ec1a  1 + gcc-5-1.noarch@available
+#>alternative 6e50ec1a  2   gcc-6-1.noarch@available
+#>install build-tools-1-1.noarch@available
+#>install gcc-5-1.noarch@available
+
+nextjob
+
+job install name build-tools
+job disfavor pkg gcc-6-1.noarch@available
+result transaction,problems,alternatives <inline>
+#>alternative 6e50ec1a  0 build-tools-1-1.noarch@available requires gcc
+#>alternative 6e50ec1a  1 + gcc-5-1.noarch@available
+#>alternative 6e50ec1a  2   gcc-6-1.noarch@available
+#>install build-tools-1-1.noarch@available
+#>install gcc-5-1.noarch@available
+