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