- add solver_private.h, make repo_ functions return an int, make solver_solv return...
[platform/upstream/libsolv.git] / tools / patchcheck.c
1 /* vim: sw=2 et
2  */
3
4 /*
5  * Copyright (c) 2009, Novell Inc.
6  *
7  * This program is licensed under the BSD license, read LICENSE.BSD
8  * for further information
9  */
10
11 #define _GNU_SOURCE
12
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <unistd.h>
16 #include <string.h>
17 #include <zlib.h>
18
19 #include "pool.h"
20 #include "evr.h"
21 #include "poolarch.h"
22 #include "repo_solv.h"
23 #include "repo_susetags.h"
24 #include "repo_updateinfoxml.h"
25 #include "repo_rpmmd.h"
26 #include "solver.h"
27 #include "solverdebug.h"
28
29 #include "sat_xfopen.h"
30
31 void
32 showproblems(Solver *solv, Solvable *s, Queue *cand, Queue *badguys)
33 {
34   Pool *pool = solv->pool;
35   Queue rids, rinfo;
36   Id problem = 0;
37   int jj;
38   int rerun = 0;
39
40   queue_init(&rids);
41   queue_init(&rinfo);
42   printf("can't install %s:\n", pool_solvable2str(pool, s));
43   while ((problem = solver_next_problem(solv, problem)) != 0)
44     {
45       solver_findallproblemrules(solv, problem, &rids);
46       for (jj = 0; jj < rids.count; jj++)
47         {
48           Id probr = rids.elements[jj];
49           int k, l;
50
51           queue_empty(&rinfo);
52           solver_allruleinfos(solv, probr, &rinfo);
53           for (k = 0; k < rinfo.count; k += 4)
54             {
55               Id dep, source, target;
56               source = rinfo.elements[k + 1];
57               target = rinfo.elements[k + 2];
58               dep = rinfo.elements[k + 3];
59               switch (rinfo.elements[k])
60                 {
61                 case SOLVER_RULE_DISTUPGRADE:
62                   break;
63                 case SOLVER_RULE_INFARCH:
64                   printf("  %s has inferior architecture\n", pool_solvid2str(pool, source));
65                   break;
66                 case SOLVER_RULE_UPDATE:
67                   printf("  update rule for %s\n", pool_solvid2str(pool, source));
68                   if (badguys)
69                     queue_pushunique(badguys, source);
70                   if (!cand)
71                     break;
72                   /* only drop update problem packages from cand so that we see all problems of this patch */
73                   for (l = 0; l < cand->count; l++)
74                     if (cand->elements[l] == source || cand->elements[l] == -source)
75                       break;
76                   if (l == cand->count)
77                     break;
78                   if (!rerun)
79                     {
80                       for (l = 0; l < cand->count; l++)
81                         if (cand->elements[l] < 0)
82                           cand->elements[l] = -cand->elements[l];
83                       rerun = 1;
84                     }
85                   for (l = 0; l < cand->count; l++)
86                     if (cand->elements[l] == source)
87                       {
88                         cand->elements[l] = -source;
89                       }
90                   break;
91                 case SOLVER_RULE_JOB:
92                   break;
93                 case SOLVER_RULE_RPM:
94                   printf("  some dependency problem\n");
95                   break;
96                 case SOLVER_RULE_JOB_NOTHING_PROVIDES_DEP:
97                   printf("  nothing provides requested %s\n", pool_dep2str(pool, dep));
98                   break;
99                 case SOLVER_RULE_RPM_NOT_INSTALLABLE:
100                   printf("  package %s is not installable\n", pool_solvid2str(pool, source));
101                   break;
102                 case SOLVER_RULE_RPM_NOTHING_PROVIDES_DEP:
103                   printf("  nothing provides %s needed by %s\n", pool_dep2str(pool, dep), pool_solvid2str(pool, source));
104                   if (ISRELDEP(dep))
105                     {
106                       Reldep *rd = GETRELDEP(pool, dep);
107                       if (!ISRELDEP(rd->name))
108                         {
109                           Id rp, rpp;
110                           FOR_PROVIDES(rp, rpp, rd->name)
111                             printf("    (we have %s)\n", pool_solvid2str(pool, rp));
112                         }
113                     }
114                   break;
115                 case SOLVER_RULE_RPM_SAME_NAME:
116                   printf("  cannot install both %s and %s\n", pool_solvid2str(pool, source), pool_solvid2str(pool, target));
117                   break;
118                 case SOLVER_RULE_RPM_PACKAGE_CONFLICT:
119                   printf("  package %s conflicts with %s provided by %s\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep), pool_solvid2str(pool, target));
120                   break;
121                 case SOLVER_RULE_RPM_PACKAGE_OBSOLETES:
122                   printf("  package %s obsoletes %s provided by %s\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep), pool_solvid2str(pool, target));
123                   break;
124                 case SOLVER_RULE_RPM_PACKAGE_REQUIRES:
125                   printf("  package %s requires %s, but none of the providers can be installed\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep));
126                   break;
127                 case SOLVER_RULE_RPM_SELF_CONFLICT:
128                   printf("  package %s conflicts with %s provided by itself\n", pool_solvid2str(pool, source), pool_dep2str(pool, dep));
129                   break;
130                 }
131             }
132         }
133     }
134   queue_free(&rids);
135   queue_free(&rinfo);
136 }
137
138 void
139 toinst(Solver *solv, Repo *repo, Repo *instrepo)
140 {
141   Pool *pool = solv->pool;
142   int k;
143   Id p;
144
145   for (k = 0; k < solv->decisionq.count; k++)
146     {
147       p = solv->decisionq.elements[k];
148       if (p < 0 || p == SYSTEMSOLVABLE)
149         continue;
150
151      /* printf(" toinstall %s\n", pool_solvid2str(pool, p));*/
152       /* oh my! */
153       pool->solvables[p].repo = instrepo;
154     }
155 }
156
157 void
158 dump_instrepo(Repo *instrepo, Pool *pool)
159 {
160   Solvable *s;
161   Id p;
162
163   printf("instrepo..\n");
164   FOR_REPO_SOLVABLES(instrepo, p, s)
165     printf("  %s\n", pool_solvable2str(pool, s));
166   printf("done.\n");
167 }
168
169 void
170 frominst(Solver *solv, Repo *repo, Repo *instrepo)
171 {
172   Pool *pool = solv->pool;
173   int k;
174
175   for (k = 1; k < pool->nsolvables; k++)
176     if (pool->solvables[k].repo == instrepo)
177       pool->solvables[k].repo = repo;
178 }
179
180 void
181 usage(char** argv)
182 {
183
184   printf("%s: <arch> <patchnameprefix>  [--install-available] [repos] [--updaterepos] [repos]...\n"
185       "\t --install-available: installation repository is available during update\n"
186       "\t repos: repository ending in\n"
187       "\t\tpackages, packages.gz, primary.xml.gz, updateinfo.xml.gz or .solv\n",
188       argv[0]);
189
190   exit(1);
191 }
192
193 typedef struct {
194   int updatestart;
195   int shown;
196   int status;
197   int install_available;
198   Repo *repo;
199   Repo *instrepo;
200 } context_t;
201
202 #define SHOW_PATCH(c) if (!(c)->shown++) printf("%s:\n", pool_solvable2str(pool, s));
203 #define PERF_DEBUGGING 0
204  
205 static Pool *pool;
206
207 void
208 test_all_old_patches_included(context_t *c, Id pid)
209 {
210   Id p, pp;
211   Id con, *conp;
212   Solvable *s = pool->solvables + pid;
213   /* Test 1: are all old patches included */
214   FOR_PROVIDES(p, pp, s->name)
215     {
216       Solvable *s2 = pool->solvables + p;
217       Id con2, *conp2;
218
219       if (!s2->conflicts)
220         continue;
221       if (pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) <= 0)
222         continue;
223       conp2 = s2->repo->idarraydata + s2->conflicts;
224       while ((con2 = *conp2++) != 0)
225         {
226           Reldep *rd2, *rd;
227           if (!ISRELDEP(con2))
228             continue;
229           rd2 = GETRELDEP(pool, con2);
230           conp = s->repo->idarraydata + s->conflicts;
231           while ((con = *conp++) != 0)
232             {
233               if (!ISRELDEP(con))
234                 continue;
235               rd = GETRELDEP(pool, con);
236               if (rd->name == rd2->name)
237                 break;
238             }
239           if (!con)
240             {
241               SHOW_PATCH(c);
242               printf("  %s contained %s\n", pool_solvable2str(pool, s2), pool_dep2str(pool, rd2->name));
243             }
244           else
245            {
246              if (pool_evrcmp(pool, rd->evr, rd2->evr, EVRCMP_COMPARE) < 0)
247                {
248                  SHOW_PATCH(c);
249                  printf("  %s required newer version %s-%s of %s-%s\n",
250                      pool_solvable2str(pool, s2), pool_dep2str(pool, rd2->name), pool_dep2str(pool, rd2->evr),
251                      pool_dep2str(pool, rd->name), pool_dep2str(pool, rd->evr));
252                }
253            }
254
255         }
256     }
257 }
258
259 void
260 test_all_packages_installable(context_t *c, Id pid)
261 {
262   Solver *solv;
263   Queue job;
264   Id p, pp;
265   Id con, *conp;
266   unsigned int now, solver_runs;
267   int i;
268   Solvable *s = pool->solvables + pid;
269
270   queue_init(&job);
271
272   now = sat_timems(0);
273   solver_runs = 0;
274
275   conp = s->repo->idarraydata + s->conflicts;
276   while ((con = *conp++) != 0)
277     {
278       FOR_PROVIDES(p, pp, con)
279         {
280           queue_empty(&job);
281           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
282           queue_push(&job, p);
283
284           /* also set up some minimal system */
285           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
286           queue_push(&job, pool_str2id(pool, "rpm", 1));
287           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
288           queue_push(&job, pool_str2id(pool, "aaa_base", 1));
289
290           solv = solver_create(pool);
291           solv->dontinstallrecommended = 0;
292           ++solver_runs;
293           if (solver_solve(solv, &job))
294             {
295               c->status = 1;
296               printf("error installing original package\n");
297               showproblems(solv, s, 0, 0);
298             }
299           toinst(solv, c->repo, c->instrepo);
300           solver_free(solv);
301
302 #if 0
303           dump_instrepo(instrepo, pool);
304
305 #endif
306           if (!c->install_available)
307             {
308               queue_empty(&job);
309               for (i = 1; i < c->updatestart; i++)
310                 {
311                   if (pool->solvables[i].repo != c->repo || i == pid)
312                     continue;
313                   queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
314                   queue_push(&job, i);
315                 }
316             }
317           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE);
318           queue_push(&job, pid);
319           solv = solver_create(pool);
320           /*solv->dontinstallrecommended = 1;*/
321           ++solver_runs;
322           if (solver_solve(solv, &job))
323             {
324               c->status = 1;
325               showproblems(solv, s, 0, 0);
326             }
327           frominst(solv, c->repo, c->instrepo);
328           solver_free(solv);
329         }
330     }
331
332   if (PERF_DEBUGGING)
333     printf("  test_all_packages_installable took %d ms in %d runs\n", sat_timems(now), solver_runs);
334 }
335
336 void
337 test_can_upgrade_all_packages(context_t *c, Id pid)
338 {
339   Solver *solv;
340   Id p;
341   Id con, *conp;
342   Queue job;
343   Queue cand;
344   Queue badguys;
345   int i, j;
346   unsigned int now, solver_runs;
347   Solvable *s = pool->solvables + pid;
348
349   queue_init(&job);
350   queue_init(&cand);
351   queue_init(&badguys);
352
353   now = sat_timems(0);
354   solver_runs = 0;
355
356   /* Test 3: can we upgrade all packages? */
357   for (p = 1; p < pool->nsolvables; p++)
358     {
359       Solvable *s = pool->solvables + p;
360       if (!s->repo)
361         continue;
362       if (strchr(pool_id2str(pool, s->name), ':'))
363         continue;       /* only packages, please */
364       if (!pool_installable(pool, s))
365         continue;
366       queue_push(&cand, p);
367     }
368   while (cand.count)
369     {
370       solv = solver_create(pool);
371       queue_empty(&job);
372       for (i = 0; i < badguys.count; i++)
373         {
374           queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE|SOLVER_WEAK);
375           queue_push(&job, badguys.elements[i]);
376         }
377       conp = s->repo->idarraydata + s->conflicts;
378       while ((con = *conp++) != 0)
379         {
380           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
381           queue_push(&job, con);
382         }
383       for (i = 0; i < cand.count; i++)
384         {
385           p = cand.elements[i];
386           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
387           queue_push(&job, p);
388         }
389       ++solver_runs;
390       solver_solve(solv, &job);
391 #if 0
392       solver_printdecisions(solv);
393 #endif
394       /* put packages into installed repo and prune them from cand */
395       toinst(solv, c->repo, c->instrepo);
396       for (i = 0; i < cand.count; i++)
397         {
398           p = cand.elements[i];
399           if (p > 0 && solv->decisionmap[p] > 0)
400             cand.elements[i] = -p;      /* drop candidate */
401         }
402       solver_free(solv);
403
404       /* now the interesting part: test patch */
405       queue_empty(&job);
406       if (!c->install_available)
407         {
408           for (i = 1; i < c->updatestart; i++)
409             {
410               if (pool->solvables[i].repo != c->repo || i == pid)
411                 continue;
412               queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
413               queue_push(&job, i);
414             }
415         }
416       queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE);
417       queue_push(&job, pid);
418       solv = solver_create(pool);
419       solv->dontinstallrecommended = 1;
420       ++solver_runs;
421       if (solver_solve(solv, &job))
422         {
423           c->status = 1;
424           showproblems(solv, s, &cand, &badguys);
425         }
426       frominst(solv, c->repo, c->instrepo);
427       solver_free(solv);
428       /* now drop all negative elements from cand */
429       for (i = j = 0; i < cand.count; i++)
430         {
431           if (cand.elements[i] < 0)
432             continue;
433           cand.elements[j++] = cand.elements[i];
434         }
435       if (i == j)
436         break;  /* no progress */
437       cand.count = j;
438     }
439   if (PERF_DEBUGGING)
440     printf("  test_can_upgrade_all_packages took %d ms in %d runs\n", sat_timems(now), solver_runs);
441 }
442
443 void
444 test_no_ga_package_fulfills_dependency(context_t *c, Id pid)
445 {
446   Id con, *conp;
447   Solvable *s = pool->solvables + pid;
448
449   /* Test 4: no GA package fulfills patch dependency */
450   conp = s->repo->idarraydata + s->conflicts;
451   while ((con = *conp++) != 0)
452     {
453       Reldep *rd;
454       Id rp, rpp;
455
456       if (!ISRELDEP(con))
457         continue;
458       rd = GETRELDEP(pool, con);
459       FOR_PROVIDES(rp, rpp, rd->name)
460         {
461           Solvable *s2 = pool_id2solvable(pool, rp);
462           if (rp < c->updatestart
463               && pool_evrcmp(pool, rd->evr, s2->evr, EVRCMP_COMPARE) < 0
464               && pool_match_nevr_rel(pool, s2, rd->name)
465              )
466             {
467               SHOW_PATCH(c);
468               printf("  conflict %s < %s satisfied by non-updated package %s\n",
469                   pool_dep2str(pool, rd->name), pool_dep2str(pool, rd->evr), pool_solvable2str(pool, s2));
470               break;
471             }
472         }
473     }
474 }
475
476 int
477 main(int argc, char **argv)
478 {
479   char *arch, *mypatch;
480   const char *pname;
481   int l;
482   FILE *fp;
483   int i;
484   Id pid, p, pp;
485   int tests = 0;
486   context_t c;
487
488   c.install_available = 0;
489   c.updatestart = 0;
490   c.status = 0;
491
492   if (argc <= 3)
493     usage(argv);
494
495   arch = argv[1];
496   pool = pool_create();
497   pool_setarch(pool, arch);
498   static const char* langs[] = {"en"};
499   pool_set_languages(pool, langs, 1);
500
501 #if 0
502   pool_setdebuglevel(pool, 2);
503 #endif
504
505   mypatch = argv[2];
506
507   c.repo = repo_create(pool, 0);
508   c.instrepo = repo_create(pool, 0);
509   for (i = 3; i < argc; i++)
510     {
511       if (!strcmp(argv[i], "--updaterepos"))
512         {
513           c.updatestart = pool->nsolvables;
514           continue;
515         }
516
517       if (!strcmp(argv[i], "--install-available"))
518         {
519           c.install_available = 1;
520           continue;
521         }
522  
523       l = strlen(argv[i]);
524       if (!strcmp(argv[i], "-"))
525         fp = stdin;
526       else if ((fp = sat_xfopen(argv[i], 0)) == 0)
527         {
528           perror(argv[i]);
529           exit(1);
530         }
531       if (l >= 8 && !strcmp(argv[i] + l - 8, "packages"))
532         {
533           repo_add_susetags(c.repo, fp, 0, 0, 0);
534         }
535       else if (l >= 11 && !strcmp(argv[i] + l - 11, "packages.gz"))
536         {
537           repo_add_susetags(c.repo, fp, 0, 0, 0);
538         }
539       else if (l >= 14 && !strcmp(argv[i] + l - 14, "primary.xml.gz"))
540         {
541           repo_add_rpmmd(c.repo, fp, 0, 0);
542         }
543       else if (l >= 17 && !strcmp(argv[i] + l - 17, "updateinfo.xml.gz"))
544         {
545           repo_add_updateinfoxml(c.repo, fp, 0);
546         }
547       else if (repo_add_solv(c.repo, fp))
548         {
549           fprintf(stderr, "could not add repo %s\n", argv[i]);
550           exit(1);
551         }
552       if (fp != stdin)
553         fclose(fp);
554     }
555
556   pool_addfileprovides(pool);
557
558   /* bad hack ahead: clone repo */
559   c.instrepo->idarraydata = c.repo->idarraydata;
560   c.instrepo->idarraysize = c.repo->idarraysize;
561   c.instrepo->start = c.repo->start;
562   c.instrepo->end = c.repo->end;
563   c.instrepo->nsolvables = c.repo->nsolvables;  /* sic! */
564   c.instrepo->lastoff = c.repo->lastoff;        /* sic! */
565   pool_set_installed(pool, c.instrepo);
566   pool_createwhatprovides(pool);
567
568   for (pid = 1; pid < pool->nsolvables; pid++)
569     {
570       c.shown = 0;
571       Solvable *s = pool->solvables + pid;
572       if (!s->repo)
573         continue;
574       if (!pool_installable(pool, s))
575         continue;
576       pname = pool_id2str(pool, s->name);
577       if (strncmp(pname, "patch:", 6) != 0)
578         continue;
579
580       if (*mypatch)
581         {
582           if (strncmp(mypatch, pname + 6, strlen(pname + 6)) != 0)
583             continue;
584           if (strcmp(mypatch, pname + 6) != 0)
585             {
586               l = strlen(pname + 6);
587               if (mypatch[l] != '-')
588                 continue;
589               if (strcmp(mypatch + l + 1, pool_id2str(pool, s->evr)) != 0)
590                 continue;
591             }
592         }
593       else
594         {
595           FOR_PROVIDES(p, pp, s->name)
596             {
597               Solvable *s2 = pool->solvables + p;
598               if (pool_evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) < 0)
599                 break;
600             }
601           if (p) {
602             /* printf("found a newer one for %s\n", pname+6); */
603             continue;   /* found a newer one */
604           }
605         }
606       tests++;
607       if (!s->conflicts)
608         continue;
609
610 #if 0
611       printf("testing patch %s-%s\n", pname + 6, pool_id2str(pool, s->evr));
612 #endif
613
614       test_all_old_patches_included(&c, pid);
615       test_all_packages_installable(&c, pid);
616       test_can_upgrade_all_packages(&c, pid);
617       test_no_ga_package_fulfills_dependency(&c, pid);
618     }
619
620   exit(c.status);
621 }