Merge branch 'master' of git@git.opensuse.org:projects/zypp/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> [repos] [--updaterepos] [repos]...\n"
216       "\t repos: repository ending in\n"
217       "\t\tpackages, packages.gz, primary.xml.gz, updateinfo.xml.gz or .solv\n",
218       argv[0]);
219
220   exit(1);
221 }
222
223 int
224 main(int argc, char **argv)
225 {
226   Pool *pool;
227   char *arch, *mypatch;
228   const char *pname;
229   int l;
230   FILE *fp;
231   int i, j;
232   Queue job;
233   Queue cand;
234   Queue badguys;
235   Id pid, p, pp;
236   Id con, *conp;
237   Solver *solv;
238   Repo *repo, *instrepo;
239   int status = 0;
240   int tests = 0;
241   int updatestart = 0;
242
243   if (argc <= 3)
244     usage(argv);
245
246   arch = argv[1];
247   pool = pool_create();
248   pool_setarch(pool, arch);
249   static const char* langs[] = {"en"};
250   pool_set_languages(pool, langs, 1);
251
252 #if 0
253   pool_setdebuglevel(pool, 2);
254 #endif
255
256   mypatch = argv[2];
257
258   repo = repo_create(pool, 0);
259   instrepo = repo_create(pool, 0);
260   for (i = 3; i < argc; i++)
261     {
262       if (!strcmp(argv[i], "--updaterepos"))
263         {
264           updatestart = pool->nsolvables;
265           continue;
266         }
267       l = strlen(argv[i]);
268       if (!strcmp(argv[i], "-"))
269         fp = stdin;
270       else if ((fp = myfopen(argv[i])) == 0)
271         {
272           perror(argv[i]);
273           exit(1);
274         }
275       if (l >= 8 && !strcmp(argv[i] + l - 8, "packages"))
276         {
277           repo_add_susetags(repo, fp, 0, 0, 0);
278         }
279       else if (l >= 11 && !strcmp(argv[i] + l - 11, "packages.gz"))
280         {
281           repo_add_susetags(repo, fp, 0, 0, 0);
282         }
283       else if (l >= 14 && !strcmp(argv[i] + l - 14, "primary.xml.gz"))
284         {
285           repo_add_rpmmd(repo, fp, 0, 0);
286         }
287       else if (l >= 17 && !strcmp(argv[i] + l - 17, "updateinfo.xml.gz"))
288         {
289           repo_add_updateinfoxml(repo, fp, 0);
290         }
291       else if (repo_add_solv(repo, fp))
292         {
293           fprintf(stderr, "could not add repo %s\n", argv[i]);
294           exit(1);
295         }
296       if (fp != stdin)
297         fclose(fp);
298     }
299
300   pool_addfileprovides(pool);
301
302   /* bad hack ahead: clone repo */
303   instrepo->idarraydata = repo->idarraydata;
304   instrepo->idarraysize = repo->idarraysize;
305   instrepo->start = repo->start;
306   instrepo->end = repo->end;
307   instrepo->nsolvables = repo->nsolvables;      /* sic! */
308   instrepo->lastoff = repo->lastoff;    /* sic! */
309   pool_set_installed(pool, instrepo);
310   pool_createwhatprovides(pool);
311
312   queue_init(&job);
313   queue_init(&cand);
314   queue_init(&badguys);
315
316   for (pid = 1; pid < pool->nsolvables; pid++)
317     {
318       int shown = 0;
319 #define SHOW_PATCH() if (!shown++) printf("%s:\n", solvable2str(pool, s));
320       Solvable *s = pool->solvables + pid;
321       if (!s->repo)
322         continue;
323       if (!pool_installable(pool, s))
324         continue;
325       pname = id2str(pool, s->name);
326       if (strncmp(pname, "patch:", 6) != 0)
327         continue;
328
329       if (*mypatch)
330         {
331           if (strncmp(mypatch, pname + 6, strlen(pname + 6)) != 0)
332             continue;
333           if (strcmp(mypatch, pname + 6) != 0)
334             {
335               l = strlen(pname + 6);
336               if (mypatch[l] != '-')
337                 continue;
338               if (strcmp(mypatch + l + 1, id2str(pool, s->evr)) != 0)
339                 continue;
340             }
341         }
342       else
343         {
344           FOR_PROVIDES(p, pp, s->name)
345             {
346               Solvable *s2 = pool->solvables + p;
347               if (evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) < 0)
348                 break;
349             }
350           if (p) {
351             /* printf("found a newer one for %s\n", pname+6); */
352             continue;   /* found a newer one */
353           }
354         }
355       tests++;
356       if (!s->conflicts)
357         continue;
358
359 #if 0
360       printf("testing patch %s-%s\n", pname + 6, id2str(pool, s->evr));
361 #endif
362
363       if (1) {
364
365       /* Test 1: are all old patches included */
366       FOR_PROVIDES(p, pp, s->name)
367         {
368           Solvable *s2 = pool->solvables + p;
369           Id con2, *conp2;
370
371           if (!s2->conflicts)
372             continue;
373           if (evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) <= 0)
374             continue;
375           conp2 = s2->repo->idarraydata + s2->conflicts;
376           while ((con2 = *conp2++) != 0)
377             {
378               Reldep *rd2, *rd;
379               if (!ISRELDEP(con2))
380                 continue;
381               rd2 = GETRELDEP(pool, con2);
382               conp = s->repo->idarraydata + s->conflicts;
383               while ((con = *conp++) != 0)
384                 {
385                   if (!ISRELDEP(con))
386                     continue;
387                   rd = GETRELDEP(pool, con);
388                   if (rd->name == rd2->name)
389                     break;
390                 }
391               if (!con)
392                 {
393                   SHOW_PATCH();
394                   printf("  %s contained %s\n", solvable2str(pool, s2), dep2str(pool, rd2->name));
395                 }
396               else
397                {
398                  if (evrcmp(pool, rd->evr, rd2->evr, EVRCMP_COMPARE) < 0)
399                    {
400                      SHOW_PATCH();
401                      printf("  %s required newer version %s-%s of %s-%s\n",
402                          solvable2str(pool, s2), dep2str(pool, rd2->name), dep2str(pool, rd2->evr),
403                          dep2str(pool, rd->name), dep2str(pool, rd->evr));
404                    }
405                }
406         
407             }
408         }
409       }
410
411       if (1) {
412       /* Test 2: are the packages installable */
413       conp = s->repo->idarraydata + s->conflicts;
414       while ((con = *conp++) != 0)
415         {
416           FOR_PROVIDES(p, pp, con)
417             {
418               queue_empty(&job);
419               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
420               queue_push(&job, p);
421
422               /* also set up some minimal system */
423               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
424               queue_push(&job, str2id(pool, "rpm", 1));
425               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
426               queue_push(&job, str2id(pool, "aaa_base", 1));
427
428               solv = solver_create(pool);
429               solv->dontinstallrecommended = 0;
430               solver_solve(solv, &job);
431               if (solv->problems.count)
432                 {
433                   status = 1;
434                   printf("error installing original package\n");
435                   showproblems(solv, s, 0, 0);
436                 }
437               toinst(solv, repo, instrepo);
438               solver_free(solv);
439
440 #if 0
441               dump_instrepo(instrepo, pool);
442
443 #endif
444 #if 1
445               queue_empty(&job);
446               for (i = 1; i < updatestart; i++)
447                 {
448                   if (pool->solvables[i].repo != repo || i == pid)
449                     continue;
450                   queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
451                   queue_push(&job, i);
452                 }
453               queue_push(&job, SOLVER_INSTALL_SOLVABLE);
454               queue_push(&job, pid);
455               solv = solver_create(pool);
456               /*solv->dontinstallrecommended = 1;*/
457               solver_solve(solv, &job);
458               if (solv->problems.count)
459                 {
460                   status = 1;
461                   showproblems(solv, s, 0, 0);
462                 }
463               frominst(solv, repo, instrepo);
464               solver_free(solv);
465 #endif
466             }
467         }
468       }
469
470       if (1) {
471
472       /* Test 3: can we upgrade all packages? */
473       queue_empty(&cand);
474       queue_empty(&badguys);
475       for (p = 1; p < pool->nsolvables; p++)
476         {
477           Solvable *s = pool->solvables + p;
478           if (!s->repo)
479             continue;
480           if (strchr(id2str(pool, s->name), ':'))
481             continue;   /* only packages, please */
482           if (!pool_installable(pool, s))
483             continue;
484           queue_push(&cand, p);
485         }
486       while (cand.count)
487         {
488           solv = solver_create(pool);
489           queue_empty(&job);
490           for (i = 0; i < badguys.count; i++)
491             {
492               queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE|SOLVER_WEAK);
493               queue_push(&job, badguys.elements[i]);
494             }
495           conp = s->repo->idarraydata + s->conflicts;
496           while ((con = *conp++) != 0)
497             {
498               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
499               queue_push(&job, con);
500             }
501           for (i = 0; i < cand.count; i++)
502             {
503               p = cand.elements[i];
504               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
505               queue_push(&job, p);
506             }
507           solver_solve(solv, &job);
508 #if 0
509           solver_printdecisions(solv);
510 #endif
511           /* put packages into installed repo and prune them from cand */
512           toinst(solv, repo, instrepo);
513           for (i = 0; i < cand.count; i++)
514             {
515               p = cand.elements[i];
516               if (p > 0 && solv->decisionmap[p] > 0)
517                 cand.elements[i] = -p;  /* drop candidate */
518             }
519           solver_free(solv);
520
521           /* now the interesting part: test patch */
522           queue_empty(&job);
523 #if 0
524           for (i = 1; i < updatestart; i++)
525             {
526               if (pool->solvables[i].repo != repo || i == pid)
527                 continue;
528               queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
529               queue_push(&job, i);
530             }
531 #endif
532           queue_push(&job, SOLVER_INSTALL_SOLVABLE);
533           queue_push(&job, pid);
534           solv = solver_create(pool);
535           solv->dontinstallrecommended = 1;
536           solver_solve(solv, &job);
537
538           if (solv->problems.count)
539             {
540               status = 1;
541               showproblems(solv, s, &cand, &badguys);
542             }
543           frominst(solv, repo, instrepo);
544           solver_free(solv);
545           /* now drop all negative elements from cand */
546           for (i = j = 0; i < cand.count; i++)
547             {
548               if (cand.elements[i] < 0)
549                 continue;
550               cand.elements[j++] = cand.elements[i];
551             }
552           if (i == j)
553             break;      /* no progress */
554           cand.count = j;
555         }
556       }
557
558       if (1)
559         {
560           /* Test 4: no GA package fulfills patch dependency */
561           conp = s->repo->idarraydata + s->conflicts;
562           while ((con = *conp++) != 0)
563             {
564               Reldep *rd;
565               Id rp, rpp;
566
567               if (!ISRELDEP(con))
568                 continue;
569               rd = GETRELDEP(pool, con);
570               FOR_PROVIDES(rp, rpp, rd->name)
571                 {
572                   Solvable *s2 = pool_id2solvable(pool, rp);
573                   if (rp < updatestart
574                       && evrcmp(pool, rd->evr, s2->evr, EVRCMP_COMPARE) < 0
575                       && pool_match_nevr_rel(pool, s2, rd->name)
576                       )
577                     {
578                       SHOW_PATCH();
579                       printf("  conflict %s < %s satisfied by non-updated package %s\n",
580                           dep2str(pool, rd->name), dep2str(pool, rd->evr), solvable2str(pool, s2));
581                       break;
582                     }
583                 }
584             }
585         }
586     }
587
588   exit(status);
589 }