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