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