honor Solver.limittokind
[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
77 /*
78  * prune queue, only keep solvables of kind
79  * 
80  */
81
82 static void
83 prune_to_kind(Pool *pool, Queue *plist, solvable_kind kind)
84 {
85   int i, j;
86   Solvable *s;
87
88   /* prune to highest priority */
89   for (i = j = 0; i < plist->count; i++)
90     {
91       s = pool->solvables + plist->elements[i];
92       if (s->kind == kind)
93         plist->elements[j++] = plist->elements[i];
94     }
95   plist->count = j;
96 }
97
98
99 /*
100  * prune to repository with highest priority
101  * 
102  */
103
104 static void
105 prune_to_highest_prio(Pool *pool, Queue *plist)
106 {
107   int i, j;
108   Solvable *s;
109   int bestprio = 0;
110
111   /* prune to highest priority */
112   for (i = 0; i < plist->count; i++)  /* find highest prio in queue */
113     {
114       s = pool->solvables + plist->elements[i];
115       if (i == 0 || s->repo->priority > bestprio)
116         bestprio = s->repo->priority;
117     }
118   for (i = j = 0; i < plist->count; i++) /* remove all with lower prio */
119     {
120       s = pool->solvables + plist->elements[i];
121       if (s->repo->priority == bestprio)
122         plist->elements[j++] = plist->elements[i];
123     }
124   plist->count = j;
125 }
126
127
128 /*
129  * prune_to_recommended
130  *
131  * XXX: should we prune to requires/suggests that are already
132  * fulfilled by other packages?
133  */
134
135 static void
136 prune_to_recommended(Solver *solv, Queue *plist)
137 {
138   Pool *pool = solv->pool;
139   int i, j;
140   Solvable *s;
141   Id p, *pp, rec, *recp, sug, *sugp;
142
143   if (solv->recommends_index < 0)
144     {
145       MAPZERO(&solv->recommendsmap);
146       MAPZERO(&solv->suggestsmap);
147       solv->recommends_index = 0;
148     }
149   while (solv->recommends_index < solv->decisionq.count)
150     {
151       p = solv->decisionq.elements[solv->recommends_index++];
152       if (p < 0)
153         continue;
154       s = pool->solvables + p;
155       if (s->recommends)
156         {
157           recp = s->repo->idarraydata + s->recommends;
158           while ((rec = *recp++) != 0)
159             FOR_PROVIDES(p, pp, rec)
160               MAPSET(&solv->recommendsmap, p);
161         }
162       if (s->suggests)
163         {
164           sugp = s->repo->idarraydata + s->suggests;
165           while ((sug = *sugp++) != 0)
166             FOR_PROVIDES(p, pp, sug)
167               MAPSET(&solv->suggestsmap, p);
168         }
169     }
170   /* prune to recommended/supplemented */
171   for (i = j = 0; i < plist->count; i++)
172     {
173       p = plist->elements[i];
174       if (MAPTST(&solv->recommendsmap, p))
175         {
176           plist->elements[j++] = p;
177           continue;
178         }
179       if (solver_is_supplementing(solv, pool->solvables + p))
180         plist->elements[j++] = p;
181     }
182   if (j)
183     plist->count = j;
184
185   /* prune to suggested/enhanced*/
186   if (plist->count < 2)
187     return;
188   for (i = j = 0; i < plist->count; i++)
189     {
190       p = plist->elements[i];
191       if (MAPTST(&solv->suggestsmap, p))
192         {
193           plist->elements[j++] = p;
194           continue;
195         }
196       if (solver_is_enhancing(solv, pool->solvables + p))
197         plist->elements[j++] = p;
198     }
199   if (j)
200     plist->count = j;
201 }
202
203 void
204 prune_to_best_arch(Pool *pool, Queue *plist)
205 {
206   Id a, bestscore;
207   Solvable *s;
208   int i, j;
209
210   if (!pool->id2arch || plist->count < 2)
211     return;
212   bestscore = 0;
213   for (i = 0; i < plist->count; i++)
214     {
215       s = pool->solvables + plist->elements[i];
216       a = s->arch;
217       a = (a <= pool->lastarch) ? pool->id2arch[a] : 0;
218       if (a && a != 1 && (!bestscore || a < bestscore))
219         bestscore = a;
220     }
221   for (i = j = 0; i < plist->count; i++)
222     {
223       s = pool->solvables + plist->elements[i];
224       a = s->arch;
225       if (a > pool->lastarch)
226         continue;
227       a = pool->id2arch[a];
228       /* a == 1 -> noarch */
229       if (a != 1 && ((a ^ bestscore) & 0xffff0000) != 0)
230         continue;
231       plist->elements[j++] = plist->elements[i];
232     }
233   if (j)
234     plist->count = j;
235 }
236
237 /*
238  * prune_best_version_arch
239  *
240  * sort list of packages (given through plist) by name and evr
241  * return result through plist
242  *
243  */
244
245 void
246 prune_to_best_version(Solver *solv, Queue *plist)
247 {
248   Pool *pool = solv->pool;
249   Id best = ID_NULL;
250   int i, j;
251   Solvable *s;
252
253   if (plist->count < 2)         /* no need to prune for a single entry */
254     return;
255   POOL_DEBUG(SAT_DEBUG_POLICY, "prune_to_best_version %d\n", plist->count);
256
257   prune_best_version_arch_sortcmp_data = solv;
258   /* sort by name first, prefer installed */
259   qsort(plist->elements, plist->count, sizeof(Id), prune_best_version_arch_sortcmp);
260
261   /* delete obsoleted. hmm, looks expensive! */
262   /* FIXME maybe also check provides depending on noupdateprovide? */
263   /* FIXME do not prune cycles */
264   for (i = 0; i < plist->count; i++)
265     {
266       Id p, *pp, obs, *obsp;
267       s = pool->solvables + plist->elements[i];
268       if (!s->obsoletes)
269         continue;
270       obsp = s->repo->idarraydata + s->obsoletes;
271       while ((obs = *obsp++) != 0)
272         {
273           FOR_PROVIDES(p, pp, obs)
274             {
275               if (pool->solvables[p].name == s->name)
276                 continue;
277               for (j = 0; j < plist->count; j++)
278                 {
279                   if (i == j)
280                     continue;
281                   if (plist->elements[j] == p)
282                     plist->elements[j] = 0;
283                 }
284             }
285         }
286     }
287   for (i = j = 0; i < plist->count; i++)
288     if (plist->elements[i])
289       plist->elements[j++] = plist->elements[i];
290   plist->count = j;
291
292   /* now find best 'per name' */
293   for (i = j = 0; i < plist->count; i++)
294     {
295       s = pool->solvables + plist->elements[i];
296
297       POOL_DEBUG(SAT_DEBUG_POLICY, "- %s[%s]\n",
298                  solvable2str(pool, s),
299                  (solv->installed && s->repo == solv->installed) ? "installed" : "not installed");
300
301       if (!best)                       /* if no best yet, the current is best */
302         {
303           best = plist->elements[i];
304           continue;
305         }
306
307       /* name switch: re-init */
308       if (pool->solvables[best].name != s->name)   /* new name */
309         {
310           plist->elements[j++] = best; /* move old best to front */
311           best = plist->elements[i];   /* take current as new best */
312           continue;
313         }
314
315       if (pool->solvables[best].evr != s->evr)   /* compare evr */
316         {
317           if (evrcmp(pool, pool->solvables[best].evr, s->evr, EVRCMP_MATCH_RELEASE) < 0)
318             best = plist->elements[i];
319         }
320     }
321
322   if (best == ID_NULL)
323     best = plist->elements[0];
324
325   plist->elements[j++] = best;
326   plist->count = j;
327 }
328
329
330 /* legacy, do not use anymore!
331  * (rates arch higher than version, but thats a policy)
332  */
333
334 void
335 prune_best_arch_name_version(Solver *solv, Pool *pool, Queue *plist)
336 {
337   if (solv && solv->bestSolvableCb)
338      {   /* The application is responsible for */
339          return solv->bestSolvableCb (solv->pool, plist);
340      }
341
342   if (plist->count > 1)
343     prune_to_best_arch(pool, plist);
344   if (plist->count > 1)
345     prune_to_best_version(solv, plist);
346 }
347
348
349 void
350 policy_filter_unwanted(Solver *solv, Queue *plist, Id inst, int mode)
351 {
352   Pool *pool = solv->pool;
353   if (plist->count > 1 && solv->limittokind)
354     prune_to_kind(pool, plist, solv->limittokind-1);
355   if (plist->count > 1 && mode != POLICY_MODE_SUGGEST)
356     prune_to_highest_prio(pool, plist);
357   if (plist->count > 1 && mode == POLICY_MODE_CHOOSE)
358     prune_to_recommended(solv, plist);
359   /* FIXME: do this different! */
360   if (inst)
361     queue_push(plist, inst);
362
363   prune_best_arch_name_version (solv, pool, plist);
364 }
365
366
367 int
368 policy_illegal_archchange(Solver *solv, Solvable *s1, Solvable *s2)
369 {
370   Pool *pool = solv->pool;
371   Id a1 = s1->arch, a2 = s2->arch;
372
373   if (solv && solv->archCheckCb)
374      {   /* The application is responsible for */
375          return solv->archCheckCb (solv->pool, s1, s2);
376      }
377
378   /* we allow changes to/from noarch */
379   if (a1 == a2 || a1 == ARCH_NOARCH || a2 == ARCH_NOARCH)
380     return 0;
381   if (!pool->id2arch)
382     return 0;
383   a1 = a1 <= pool->lastarch ? pool->id2arch[a1] : 0;
384   a2 = a2 <= pool->lastarch ? pool->id2arch[a2] : 0;
385   if (((a1 ^ a2) & 0xffff0000) != 0)
386     return 1;
387   return 0;
388 }
389
390 int
391 policy_illegal_vendorchange(Solver *solv, Solvable *s1, Solvable *s2)
392 {
393   Pool *pool = solv->pool;
394   Id vendormask1, vendormask2;
395
396   if (solv && solv->vendorCheckCb)
397      {   /* The application is responsible for */
398          return solv->vendorCheckCb (solv->pool, s1, s2);
399      }
400
401   if (s1->vendor == s2->vendor)
402     return 0;
403   vendormask1 = pool_vendor2mask(pool, s1->vendor);
404   if (!vendormask1)
405     return 0;
406   vendormask2 = pool_vendor2mask(pool, s2->vendor);
407   if ((vendormask1 & vendormask2) == 0)
408     return 0;
409   return 1;
410 }
411
412 void
413 policy_findupdatepackages(Solver *solv, Solvable *s, Queue *qs, int allowall)
414 {
415   /* installed packages get a special upgrade allowed rule */
416   Pool *pool = solv->pool;
417   Id p, *pp, n, p2, *pp2;
418   Id obs, *obsp;
419   Solvable *ps;
420   Id vendormask;
421
422   queue_empty(qs);
423
424   if (solv && solv->updateCandidateCb)
425      {   /* The application is responsible for */
426          return solv->updateCandidateCb (solv->pool, s, qs);
427      }
428
429   /*
430    * s = solvable ptr
431    * n = solvable Id
432    */
433   n = s - pool->solvables;
434   vendormask = pool_vendor2mask(pool, s->vendor);
435
436   /*
437    * look for updates for s
438    */
439   FOR_PROVIDES(p, pp, s->name)  /* every provider of s' name */
440     {
441       if (p == n)               /* skip itself */
442         continue;
443
444       ps = pool->solvables + p;
445       if (s->name == ps->name)  /* name match */
446         {
447           if (!allowall)
448             {
449               if (!solv->allowdowngrade && evrcmp(pool, s->evr, ps->evr, EVRCMP_MATCH_RELEASE) > 0)
450                 continue;
451               if (!solv->allowarchchange && s->arch != ps->arch && policy_illegal_archchange(solv, s, ps))
452                 continue;
453               if (!solv->allowvendorchange && s->vendor != ps->vendor && policy_illegal_vendorchange(solv, s, ps))
454                 continue;
455             }
456         }
457       else if (!solv->noupdateprovide && ps->obsoletes)   /* provides/obsoletes combination ? */
458         {
459           obsp = ps->repo->idarraydata + ps->obsoletes;
460           while ((obs = *obsp++) != 0)  /* for all obsoletes */
461             {
462               FOR_PROVIDES(p2, pp2, obs)   /* and all matching providers of the obsoletes */
463                 {
464                   if (p2 == n)          /* match ! */
465                     break;
466                 }
467               if (p2)                   /* match! */
468                 break;
469             }
470           if (!obs)                     /* continue if no match */
471             continue;
472           /* here we have 'p' with a matching provides/obsoletes combination
473            * thus flagging p as a valid update candidate for s
474            */
475         }
476       else
477         continue;
478       queue_push(qs, p);
479     }
480   if (solv->noupdateprovide && solv->obsoletes && solv->obsoletes[n - solv->installed->start])
481     {
482       for (pp = solv->obsoletes_data + solv->obsoletes[n - solv->installed->start]; (p = *pp++) != 0;)
483         queue_push(qs, p);
484     }
485 }
486