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