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