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