Prefer installed solvables first if there are two available with the
[platform/upstream/libsolv.git] / src / policy.c
1 /*
2  * Copyright (c) 2007, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7
8 /*
9  * Generic policy interface for SAT solver
10  *
11  */
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17
18 #include "solver.h"
19 #include "evr.h"
20 #include "policy.h"
21 #include "poolvendor.h"
22 #include "poolarch.h"
23
24
25 static Solver *prune_best_version_arch_sortcmp_data;
26
27 /*-----------------------------------------------------------------*/
28
29 /*
30  * prep for prune_best_version_arch
31  *   sort by name
32  */
33
34 static int
35 prune_best_version_arch_sortcmp(const void *ap, const void *bp)
36 {
37   Solver *solv = prune_best_version_arch_sortcmp_data;
38   Pool *pool = solv->pool;
39   int r;
40   Id a = *(Id *)ap;
41   Id b = *(Id *)bp;
42   r = pool->solvables[a].name - pool->solvables[b].name;
43   if (r)
44     {
45       const char *na, *nb;
46       /* different names. We use real strcmp here so that the result
47        * is not depending on some random solvable order */
48       na = id2str(pool, pool->solvables[a].name);
49       nb = id2str(pool, pool->solvables[b].name);
50       /* bring patterns to the front */
51       if (!strncmp(na, "pattern:", 8))
52         {
53           if (strncmp(nb, "pattern:", 8))
54             return -1;
55         }
56       else if (!strncmp(nb, "pattern:", 8))
57         {
58           if (strncmp(na, "pattern:", 8))
59             return 1;
60         }
61       return strcmp(na, nb);
62     } else {
63         /* the same name */
64         if ( pool->solvables[a].evr == pool->solvables[b].evr)
65         {
66             /* prefer installed solvables first */
67             if (solv->installed && pool->solvables[a].repo == solv->installed)
68                 return -1;
69             if (solv->installed && pool->solvables[b].repo == solv->installed)
70                 return 1;       
71         }
72     }
73   return a - b;
74 }
75
76 static void
77 prune_to_highest_prio(Pool *pool, Queue *plist)
78 {
79   int i, j;
80   Solvable *s;
81   int bestprio = 0;
82
83   /* prune to highest priority */
84   for (i = 0; i < plist->count; i++)
85     {
86       s = pool->solvables + plist->elements[i];
87       if (i == 0 || s->repo->priority > bestprio)
88         bestprio = s->repo->priority;
89     }
90   for (i = j = 0; i < plist->count; i++)
91     {
92       s = pool->solvables + plist->elements[i];
93       if (s->repo->priority == bestprio)
94         plist->elements[j++] = plist->elements[i];
95     }
96   plist->count = j;
97 }
98
99 /*
100  * prune_to_recommended
101  *
102  * XXX: should we prune to requires/suggests that are already
103  * fulfilled by other packages?
104  */
105 static void
106 prune_to_recommended(Solver *solv, Queue *plist)
107 {
108   Pool *pool = solv->pool;
109   int i, j;
110   Solvable *s;
111   Id p, *pp, rec, *recp, sug, *sugp;
112
113   if (solv->recommends_index < 0)
114     {
115       MAPZERO(&solv->recommendsmap);
116       MAPZERO(&solv->suggestsmap);
117       solv->recommends_index = 0;
118     }
119   while (solv->recommends_index < solv->decisionq.count)
120     {
121       p = solv->decisionq.elements[solv->recommends_index++];
122       if (p < 0)
123         continue;
124       s = pool->solvables + p;
125       if (s->recommends)
126         {
127           recp = s->repo->idarraydata + s->recommends;
128           while ((rec = *recp++) != 0)
129             FOR_PROVIDES(p, pp, rec)
130               MAPSET(&solv->recommendsmap, p);
131         }
132       if (s->suggests)
133         {
134           sugp = s->repo->idarraydata + s->suggests;
135           while ((sug = *sugp++) != 0)
136             FOR_PROVIDES(p, pp, sug)
137               MAPSET(&solv->suggestsmap, p);
138         }
139     }
140   /* prune to recommended/supplemented */
141   for (i = j = 0; i < plist->count; i++)
142     {
143       p = plist->elements[i];
144       if (MAPTST(&solv->recommendsmap, p))
145         {
146           plist->elements[j++] = p;
147           continue;
148         }
149       if (solver_is_supplementing(solv, pool->solvables + p))
150         plist->elements[j++] = p;
151     }
152   if (j)
153     plist->count = j;
154
155   /* prune to suggested/enhanced*/
156   if (plist->count < 2)
157     return;
158   for (i = j = 0; i < plist->count; i++)
159     {
160       p = plist->elements[i];
161       if (MAPTST(&solv->suggestsmap, p))
162         {
163           plist->elements[j++] = p;
164           continue;
165         }
166       if (solver_is_enhancing(solv, pool->solvables + p))
167         plist->elements[j++] = p;
168     }
169   if (j)
170     plist->count = j;
171 }
172
173 static void
174 prune_to_best_arch(Pool *pool, Queue *plist)
175 {
176   Id a, bestscore;
177   Solvable *s;
178   int i, j;
179
180   if (!pool->id2arch || plist->count < 2)
181     return;
182   bestscore = 0;
183   for (i = 0; i < plist->count; i++)
184     {
185       s = pool->solvables + plist->elements[i];
186       a = s->arch;
187       a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
188       if (a && a != 1 && (!bestscore || a < bestscore))
189         bestscore = a;
190     }
191   for (i = j = 0; i < plist->count; i++)
192     {
193       s = pool->solvables + plist->elements[i];
194       a = s->arch;
195       if (a > pool->lastarch)
196         continue;
197       a = pool->id2arch[a];
198       /* a == 1 -> noarch */
199       if (a != 1 && ((a ^ bestscore) & 0xffff0000) != 0)
200         continue;
201       plist->elements[j++] = plist->elements[i];
202     }
203   if (j)
204     plist->count = j;
205 }
206
207 /*
208  * prune_best_version_arch
209  *
210  * sort list of packages (given through plist) by name and evr
211  * return result through plist
212  *
213  */
214
215 static void
216 prune_to_best_version(Solver *solv, Queue *plist)
217 {
218   Pool *pool = solv->pool;
219   Id best = ID_NULL;
220   int i, j;
221   Solvable *s;
222
223   if (plist->count < 2)         /* no need to prune for a single entry */
224     return;
225   POOL_DEBUG(SAT_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
226
227   prune_best_version_arch_sortcmp_data = solv;
228   /* sort by name first, prefer installed */
229   qsort(plist->elements, plist->count, sizeof(Id), prune_best_version_arch_sortcmp);
230
231   /* delete obsoleted. hmm, looks expensive! */
232   /* FIXME maybe also check provides depending on noupdateprovide? */
233   /* FIXME do not prune cycles */
234   for (i = 0; i < plist->count; i++)
235     {
236       Id p, *pp, obs, *obsp;
237       s = pool->solvables + plist->elements[i];
238       if (!s->obsoletes)
239         continue;
240       obsp = s->repo->idarraydata + s->obsoletes;
241       while ((obs = *obsp++) != 0)
242         {
243           FOR_PROVIDES(p, pp, obs)
244             {
245               if (pool->solvables[p].name == s->name)
246                 continue;
247               for (j = 0; j < plist->count; j++)
248                 {
249                   if (i == j)
250                     continue;
251                   if (plist->elements[j] == p)
252                     plist->elements[j] = 0;
253                 }
254             }
255         }
256     }
257   for (i = j = 0; i < plist->count; i++)
258     if (plist->elements[i])
259       plist->elements[j++] = plist->elements[i];
260   plist->count = j;
261
262   /* now find best 'per name' */
263   for (i = j = 0; i < plist->count; i++)
264     {
265       s = pool->solvables + plist->elements[i];
266
267       POOL_DEBUG(SAT_DEBUG_POLICY, "- %s[%s]\n",
268                  solvable2str(pool, s),
269                  (solv->installed && s->repo == solv->installed) ? "installed" : "not installed");
270
271       if (!best)                       /* if no best yet, the current is best */
272         {
273           best = plist->elements[i];
274           continue;
275         }
276
277       /* name switch: re-init */
278       if (pool->solvables[best].name != s->name)   /* new name */
279         {
280           plist->elements[j++] = best; /* move old best to front */
281           best = plist->elements[i];   /* take current as new best */
282           continue;
283         }
284
285       if (pool->solvables[best].evr != s->evr)   /* compare evr */
286         {
287           if (evrcmp(pool, pool->solvables[best].evr, s->evr, EVRCMP_MATCH_RELEASE) < 0)
288             best = plist->elements[i];
289         }
290     }
291
292   if (best == ID_NULL)
293     best = plist->elements[0];
294
295   plist->elements[j++] = best;
296   plist->count = j;
297 }
298
299 /* legacy, do not use anymore! */
300 void
301 prune_best_version_arch(Solver *solv, Pool *pool, Queue *plist)
302 {
303   if (solv && solv->bestSolvableCb)
304      {   /* The application is responsible for */
305          return solv->bestSolvableCb (solv->pool, plist);
306      }
307
308   if (plist->count > 1)
309     prune_to_best_arch(pool, plist);
310   if (plist->count > 1)
311     prune_to_best_version(solv, plist);
312 }
313
314
315 void
316 policy_filter_unwanted(Solver *solv, Queue *plist, Id inst, int mode)
317 {
318   Pool *pool = solv->pool;
319   if (plist->count > 1 && mode != POLICY_MODE_SUGGEST)
320     prune_to_highest_prio(pool, plist);
321   if (plist->count > 1 && mode == POLICY_MODE_CHOOSE)
322     prune_to_recommended(solv, plist);
323   /* FIXME: do this different! */
324   if (inst)
325     queue_push(plist, inst);
326
327   prune_best_version_arch (solv, pool, plist);
328 }
329
330
331 int
332 policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
333 {
334   Pool *pool = solv->pool;
335   Id a1 = s1->arch, a2 = s2->arch;
336
337   if (solv && solv->archCheckCb)
338      {   /* The application is responsible for */
339          return solv->archCheckCb (solv->pool, s1, s2);
340      }
341
342   /* we allow changes to/from noarch */
343   if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH)
344     return 0;
345   if (!pool->id2arch)
346     return 0;
347   a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
348   a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
349   if (((a1 ^ a2) & 0xffff0000) != 0)
350     return 1;
351   return 0;
352 }
353
354 int
355 policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
356 {
357   Pool *pool = solv->pool;
358   Id vendormask1, vendormask2;
359
360   if (solv && solv->vendorCheckCb)
361      {   /* The application is responsible for */
362          return solv->vendorCheckCb (solv->pool, s1, s2);
363      }
364
365   if (s1->vendor == s2->vendor)
366     return 0;
367   vendormask1 = pool_vendor2mask(pool, s1->vendor);
368   if (!vendormask1)
369     return 0;
370   vendormask2 = pool_vendor2mask(pool, s2->vendor);
371   if ((vendormask1 & vendormask2) == 0)
372     return 0;
373   return 1;
374 }
375
376 void
377 policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allowall)
378 {
379   /* installed packages get a special upgrade allowed rule */
380   Pool *pool = solv->pool;
381   Id p, *pp, n, p2, *pp2;
382   Id obs, *obsp;
383   Solvable *ps;
384   Id vendormask;
385
386   queue_empty(qs);
387
388   if (solv && solv->updateCandidateCb)
389      {   /* The application is responsible for */
390          return solv->updateCandidateCb (solv->pool, s, qs);
391      }
392
393   /*
394    * s = solvable ptr
395    * n = solvable Id
396    */
397   n = s - pool->solvables;
398   vendormask = pool_vendor2mask(pool, s->vendor);
399
400   /*
401    * look for updates for s
402    */
403   FOR_PROVIDES(p, pp, s->name)  /* every provider of s' name */
404     {
405       if (p == n)               /* skip itself */
406         continue;
407
408       ps = pool->solvables + p;
409       if (s->name == ps->name)  /* name match */
410         {
411           if (!allowall)
412             {
413               if (!solv->allowdowngrade && evrcmp(pool, s->evr, ps->evr, EVRCMP_MATCH_RELEASE) > 0)
414                 continue;
415               if (!solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
416                 continue;
417               if (!solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
418                 continue;
419             }
420         }
421       else if (!solv->noupdateprovide && ps->obsoletes)   /* provides/obsoletes combination ? */
422         {
423           obsp = ps->repo->idarraydata + ps->obsoletes;
424           while ((obs = *obsp++) != 0)  /* for all obsoletes */
425             {
426               FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
427                 {
428                   if (p2 == n)          /* match ! */
429                     break;
430                 }
431               if (p2)                   /* match! */
432                 break;
433             }
434           if (!obs)                     /* continue if no match */
435             continue;
436           /* here we have 'p' with a matching provides/obsoletes combination
437            * thus flagging p as a valid update candidate for s
438            */
439         }
440       else
441         continue;
442       queue_push(qs, p);
443     }
444   if (solv->noupdateprovide && solv->obsoletes && solv->obsoletes[n - solv->installed->start])
445     {
446       for (pp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *pp++) != 0;)
447         queue_push(qs, p);
448     }
449 }
450