Upload Tizen:Main source
[external/libsatsolver.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 "sat_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", 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_PROBLEM_DISTUPGRADE_RULE:
62                   break;
63                 case SOLVER_PROBLEM_INFARCH_RULE:
64                   printf("  %s has inferior architecture\n", solvid2str(pool, source));
65                   break;
66                 case SOLVER_PROBLEM_UPDATE_RULE:
67                   printf("  update rule for %s\n", 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_PROBLEM_JOB_RULE:
92                   break;
93                 case SOLVER_PROBLEM_RPM_RULE:
94                   printf("  some dependency problem\n");
95                   break;
96                 case SOLVER_PROBLEM_JOB_NOTHING_PROVIDES_DEP:
97                   printf("  nothing provides requested %s\n", dep2str(pool, dep));
98                   break;
99                 case SOLVER_PROBLEM_NOT_INSTALLABLE:
100                   printf("  package %s is not installable\n", solvid2str(pool, source));
101                   break;
102                 case SOLVER_PROBLEM_NOTHING_PROVIDES_DEP:
103                   printf("  nothing provides %s needed by %s\n", dep2str(pool, dep), 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", solvid2str(pool, rp));
112                         }
113                     }
114                   break;
115                 case SOLVER_PROBLEM_SAME_NAME:
116                   printf("  cannot install both %s and %s\n", solvid2str(pool, source), solvid2str(pool, target));
117                   break;
118                 case SOLVER_PROBLEM_PACKAGE_CONFLICT:
119                   printf("  package %s conflicts with %s provided by %s\n", solvid2str(pool, source), dep2str(pool, dep), solvid2str(pool, target));
120                   break;
121                 case SOLVER_PROBLEM_PACKAGE_OBSOLETES:
122                   printf("  package %s obsoletes %s provided by %s\n", solvid2str(pool, source), dep2str(pool, dep), solvid2str(pool, target));
123                   break;
124                 case SOLVER_PROBLEM_DEP_PROVIDERS_NOT_INSTALLABLE:
125                   printf("  package %s requires %s, but none of the providers can be installed\n", solvid2str(pool, source), dep2str(pool, dep));
126                   break;
127                 case SOLVER_PROBLEM_SELF_CONFLICT:
128                   printf("  package %s conflicts with %s provided by itself\n", solvid2str(pool, source), 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   int k;
143   Id p;
144
145   for (k = 0; k < solv->decisionq.count; k++)
146     {
147       p = solv->decisionq.elements[k];
148       if (p < 0 || p == SYSTEMSOLVABLE)
149         continue;
150
151      /* printf(" toinstall %s\n", solvid2str(pool, p));*/
152       /* oh my! */
153       pool->solvables[p].repo = instrepo;
154     }
155 }
156
157 void
158 dump_instrepo(Repo *instrepo, Pool *pool)
159 {
160   Solvable *s;
161   Id p;
162
163   printf("instrepo..\n");
164   FOR_REPO_SOLVABLES(instrepo, p, s)
165     printf("  %s\n", solvable2str(pool, s));
166   printf("done.\n");
167 }
168
169 void
170 frominst(Solver *solv, Repo *repo, Repo *instrepo)
171 {
172   Pool *pool = solv->pool;
173   int k;
174
175   for (k = 1; k < pool->nsolvables; k++)
176     if (pool->solvables[k].repo == instrepo)
177       pool->solvables[k].repo = repo;
178 }
179
180 void
181 usage(char** argv)
182 {
183
184   printf("%s: <arch> <patchnameprefix>  [--install-available] [repos] [--updaterepos] [repos]...\n"
185       "\t --install-available: installation repository is available during update\n"
186       "\t repos: repository ending in\n"
187       "\t\tpackages, packages.gz, primary.xml.gz, updateinfo.xml.gz or .solv\n",
188       argv[0]);
189
190   exit(1);
191 }
192
193 typedef struct {
194   int updatestart;
195   int shown;
196   int status;
197   int install_available;
198   Repo *repo;
199   Repo *instrepo;
200 } context_t;
201
202 #define SHOW_PATCH(c) if (!(c)->shown++) printf("%s:\n", solvable2str(pool, s));
203 #define PERF_DEBUGGING 0
204  
205 static Pool *pool;
206
207 void
208 test_all_old_patches_included(context_t *c, Id pid)
209 {
210   Id p, pp;
211   Id con, *conp;
212   Solvable *s = pool->solvables + pid;
213   /* Test 1: are all old patches included */
214   FOR_PROVIDES(p, pp, s->name)
215     {
216       Solvable *s2 = pool->solvables + p;
217       Id con2, *conp2;
218
219       if (!s2->conflicts)
220         continue;
221       if (evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) <= 0)
222         continue;
223       conp2 = s2->repo->idarraydata + s2->conflicts;
224       while ((con2 = *conp2++) != 0)
225         {
226           Reldep *rd2, *rd;
227           if (!ISRELDEP(con2))
228             continue;
229           rd2 = GETRELDEP(pool, con2);
230           conp = s->repo->idarraydata + s->conflicts;
231           while ((con = *conp++) != 0)
232             {
233               if (!ISRELDEP(con))
234                 continue;
235               rd = GETRELDEP(pool, con);
236               if (rd->name == rd2->name)
237                 break;
238             }
239           if (!con)
240             {
241               SHOW_PATCH(c);
242               printf("  %s contained %s\n", solvable2str(pool, s2), dep2str(pool, rd2->name));
243             }
244           else
245            {
246              if (evrcmp(pool, rd->evr, rd2->evr, EVRCMP_COMPARE) < 0)
247                {
248                  SHOW_PATCH(c);
249                  printf("  %s required newer version %s-%s of %s-%s\n",
250                      solvable2str(pool, s2), dep2str(pool, rd2->name), dep2str(pool, rd2->evr),
251                      dep2str(pool, rd->name), dep2str(pool, rd->evr));
252                }
253            }
254
255         }
256     }
257 }
258
259 void
260 test_all_packages_installable(context_t *c, Id pid)
261 {
262   Solver *solv;
263   Queue job;
264   Id p, pp;
265   Id con, *conp;
266   unsigned int now, solver_runs;
267   int i;
268   Solvable *s = pool->solvables + pid;
269
270   queue_init(&job);
271
272   now = sat_timems(0);
273   solver_runs = 0;
274
275   conp = s->repo->idarraydata + s->conflicts;
276   while ((con = *conp++) != 0)
277     {
278       FOR_PROVIDES(p, pp, con)
279         {
280           queue_empty(&job);
281           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
282           queue_push(&job, p);
283
284           /* also set up some minimal system */
285           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
286           queue_push(&job, str2id(pool, "rpm", 1));
287           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
288           queue_push(&job, str2id(pool, "aaa_base", 1));
289
290           solv = solver_create(pool);
291           solv->dontinstallrecommended = 0;
292           ++solver_runs;
293           solver_solve(solv, &job);
294           if (solv->problems.count)
295             {
296               c->status = 1;
297               printf("error installing original package\n");
298               showproblems(solv, s, 0, 0);
299             }
300           toinst(solv, c->repo, c->instrepo);
301           solver_free(solv);
302
303 #if 0
304           dump_instrepo(instrepo, pool);
305
306 #endif
307           if (!c->install_available)
308             {
309               queue_empty(&job);
310               for (i = 1; i < c->updatestart; i++)
311                 {
312                   if (pool->solvables[i].repo != c->repo || i == pid)
313                     continue;
314                   queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
315                   queue_push(&job, i);
316                 }
317             }
318           queue_push(&job, SOLVER_INSTALL_SOLVABLE);
319           queue_push(&job, pid);
320           solv = solver_create(pool);
321           /*solv->dontinstallrecommended = 1;*/
322           ++solver_runs;
323           solver_solve(solv, &job);
324           if (solv->problems.count)
325             {
326               c->status = 1;
327               showproblems(solv, s, 0, 0);
328             }
329           frominst(solv, c->repo, c->instrepo);
330           solver_free(solv);
331         }
332     }
333
334   if (PERF_DEBUGGING)
335     printf("  test_all_packages_installable took %d ms in %d runs\n", sat_timems(now), solver_runs);
336 }
337
338 void
339 test_can_upgrade_all_packages(context_t *c, Id pid)
340 {
341   Solver *solv;
342   Id p;
343   Id con, *conp;
344   Queue job;
345   Queue cand;
346   Queue badguys;
347   int i, j;
348   unsigned int now, solver_runs;
349   Solvable *s = pool->solvables + pid;
350
351   queue_init(&job);
352   queue_init(&cand);
353   queue_init(&badguys);
354
355   now = sat_timems(0);
356   solver_runs = 0;
357
358   /* Test 3: can we upgrade all packages? */
359   for (p = 1; p < pool->nsolvables; p++)
360     {
361       Solvable *s = pool->solvables + p;
362       if (!s->repo)
363         continue;
364       if (strchr(id2str(pool, s->name), ':'))
365         continue;       /* only packages, please */
366       if (!pool_installable(pool, s))
367         continue;
368       queue_push(&cand, p);
369     }
370   while (cand.count)
371     {
372       solv = solver_create(pool);
373       queue_empty(&job);
374       for (i = 0; i < badguys.count; i++)
375         {
376           queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE|SOLVER_WEAK);
377           queue_push(&job, badguys.elements[i]);
378         }
379       conp = s->repo->idarraydata + s->conflicts;
380       while ((con = *conp++) != 0)
381         {
382           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
383           queue_push(&job, con);
384         }
385       for (i = 0; i < cand.count; i++)
386         {
387           p = cand.elements[i];
388           queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
389           queue_push(&job, p);
390         }
391       ++solver_runs;
392       solver_solve(solv, &job);
393 #if 0
394       solver_printdecisions(solv);
395 #endif
396       /* put packages into installed repo and prune them from cand */
397       toinst(solv, c->repo, c->instrepo);
398       for (i = 0; i < cand.count; i++)
399         {
400           p = cand.elements[i];
401           if (p > 0 && solv->decisionmap[p] > 0)
402             cand.elements[i] = -p;      /* drop candidate */
403         }
404       solver_free(solv);
405
406       /* now the interesting part: test patch */
407       queue_empty(&job);
408       if (!c->install_available)
409         {
410           for (i = 1; i < c->updatestart; i++)
411             {
412               if (pool->solvables[i].repo != c->repo || i == pid)
413                 continue;
414               queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
415               queue_push(&job, i);
416             }
417         }
418       queue_push(&job, SOLVER_INSTALL_SOLVABLE);
419       queue_push(&job, pid);
420       solv = solver_create(pool);
421       solv->dontinstallrecommended = 1;
422       ++solver_runs;
423       solver_solve(solv, &job);
424
425       if (solv->problems.count)
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", sat_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               && 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                   dep2str(pool, rd->name), dep2str(pool, rd->evr), 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 = sat_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   c.instrepo->lastoff = c.repo->lastoff;        /* sic! */
569   pool_set_installed(pool, c.instrepo);
570   pool_createwhatprovides(pool);
571
572   for (pid = 1; pid < pool->nsolvables; pid++)
573     {
574       c.shown = 0;
575       Solvable *s = pool->solvables + pid;
576       if (!s->repo)
577         continue;
578       if (!pool_installable(pool, s))
579         continue;
580       pname = id2str(pool, s->name);
581       if (strncmp(pname, "patch:", 6) != 0)
582         continue;
583
584       if (*mypatch)
585         {
586           if (strncmp(mypatch, pname + 6, strlen(pname + 6)) != 0)
587             continue;
588           if (strcmp(mypatch, pname + 6) != 0)
589             {
590               l = strlen(pname + 6);
591               if (mypatch[l] != '-')
592                 continue;
593               if (strcmp(mypatch + l + 1, id2str(pool, s->evr)) != 0)
594                 continue;
595             }
596         }
597       else
598         {
599           FOR_PROVIDES(p, pp, s->name)
600             {
601               Solvable *s2 = pool->solvables + p;
602               if (evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) < 0)
603                 break;
604             }
605           if (p) {
606             /* printf("found a newer one for %s\n", pname+6); */
607             continue;   /* found a newer one */
608           }
609         }
610       tests++;
611       if (!s->conflicts)
612         continue;
613
614 #if 0
615       printf("testing patch %s-%s\n", pname + 6, id2str(pool, s->evr));
616 #endif
617
618       test_all_old_patches_included(&c, pid);
619       test_all_packages_installable(&c, pid);
620       test_can_upgrade_all_packages(&c, pid);
621       test_no_ga_package_fulfills_dependency(&c, pid);
622     }
623
624   exit(c.status);
625 }