- start transaction ordering
[platform/upstream/libsolv.git] / tools / patchcheck.c
1 #define _GNU_SOURCE
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include <string.h>
7 #include <zlib.h>
8
9 #include "pool.h"
10 #include "evr.h"
11 #include "poolarch.h"
12 #include "repo_solv.h"
13 #include "repo_susetags.h"
14 #include "repo_updateinfoxml.h"
15 #include "repo_rpmmd.h"
16 #include "solver.h"
17 #include "solverdebug.h"
18
19 static ssize_t
20 cookie_gzread(void *cookie, char *buf, size_t nbytes)
21 {
22   return gzread((gzFile *)cookie, buf, nbytes);
23 }
24
25 static int
26 cookie_gzclose(void *cookie)
27 {
28   return gzclose((gzFile *)cookie);
29 }
30
31 FILE *
32 myfopen(const char *fn)
33 {
34   cookie_io_functions_t cio;
35   char *suf;
36   gzFile *gzf;
37
38   if (!fn)
39     return 0;
40   suf = strrchr(fn, '.');
41   if (!suf || strcmp(suf, ".gz") != 0)
42     return fopen(fn, "r");
43   gzf = gzopen(fn, "r");
44   if (!gzf)
45     return 0;
46   memset(&cio, 0, sizeof(cio));
47   cio.read = cookie_gzread;
48   cio.close = cookie_gzclose;
49   return  fopencookie(gzf, "r", cio);
50 }
51
52 void
53 showproblems(Solver *solv, Solvable *s, Queue *cand, Queue *badguys)
54 {
55   Pool *pool = solv->pool;
56   Queue rids, rinfo;
57   Id problem = 0;
58   int jj;
59   int rerun = 0;
60
61   queue_init(&rids);
62   queue_init(&rinfo);
63   printf("can't install %s:\n", solvable2str(pool, s));
64   while ((problem = solver_next_problem(solv, problem)) != 0)
65     {
66       solver_findallproblemrules(solv, problem, &rids);
67       for (jj = 0; jj < rids.count; jj++)
68         {
69           Id probr = rids.elements[jj];
70           int k, l;
71
72           queue_empty(&rinfo);
73           solver_allruleinfos(solv, probr, &rinfo);
74           for (k = 0; k < rinfo.count; k += 4)
75             {
76               Id dep, source, target;
77               source = rinfo.elements[k + 1];
78               target = rinfo.elements[k + 2];
79               dep = rinfo.elements[k + 3];
80               switch (rinfo.elements[k])
81                 {
82                 case SOLVER_PROBLEM_DISTUPGRADE_RULE:
83                   break;
84                 case SOLVER_PROBLEM_INFARCH_RULE:
85                   printf("  %s has inferior architecture\n", solvid2str(pool, source));
86                   break;
87                 case SOLVER_PROBLEM_UPDATE_RULE:
88                   printf("  update rule for %s\n", solvid2str(pool, source));
89                   if (badguys)
90                     queue_pushunique(badguys, source);
91                   if (!cand)
92                     break;
93                   /* only drop update problem packages from cand so that we see all problems of this patch */
94                   for (l = 0; l < cand->count; l++)
95                     if (cand->elements[l] == source || cand->elements[l] == -source)
96                       break;
97                   if (l == cand->count)
98                     break;
99                   if (!rerun)
100                     {
101                       for (l = 0; l < cand->count; l++)
102                         if (cand->elements[l] < 0)
103                           cand->elements[l] = -cand->elements[l];
104                       rerun = 1;
105                     }
106                   for (l = 0; l < cand->count; l++)
107                     if (cand->elements[l] == source)
108                       {
109                         cand->elements[l] = -source;
110                       }
111                   break;
112                 case SOLVER_PROBLEM_JOB_RULE:
113                   break;
114                 case SOLVER_PROBLEM_RPM_RULE:
115                   printf("  some dependency problem\n");
116                   break;
117                 case SOLVER_PROBLEM_JOB_NOTHING_PROVIDES_DEP:
118                   printf("  nothing provides requested %s\n", dep2str(pool, dep));
119                   break;
120                 case SOLVER_PROBLEM_NOT_INSTALLABLE:
121                   printf("  package %s is not installable\n", solvid2str(pool, source));
122                   break;
123                 case SOLVER_PROBLEM_NOTHING_PROVIDES_DEP:
124                   printf("  nothing provides %s needed by %s\n", dep2str(pool, dep), solvid2str(pool, source));
125                   if (ISRELDEP(dep))
126                     {
127                       Reldep *rd = GETRELDEP(pool, dep);
128                       if (!ISRELDEP(rd->name))
129                         {
130                           Id rp, rpp;
131                           FOR_PROVIDES(rp, rpp, rd->name)
132                             printf("    (we have %s)\n", solvid2str(pool, rp));
133                         }
134                     }
135                   break;
136                 case SOLVER_PROBLEM_SAME_NAME:
137                   printf("  cannot install both %s and %s\n", solvid2str(pool, source), solvid2str(pool, target));
138                   break;
139                 case SOLVER_PROBLEM_PACKAGE_CONFLICT:
140                   printf("  package %s conflicts with %s provided by %s\n", solvid2str(pool, source), dep2str(pool, dep), solvid2str(pool, target));
141                   break;
142                 case SOLVER_PROBLEM_PACKAGE_OBSOLETES:
143                   printf("  package %s obsoletes %s provided by %s\n", solvid2str(pool, source), dep2str(pool, dep), solvid2str(pool, target));
144                   break;
145                 case SOLVER_PROBLEM_DEP_PROVIDERS_NOT_INSTALLABLE:
146                   printf("  package %s requires %s, but none of the providers can be installed\n", solvid2str(pool, source), dep2str(pool, dep));
147                   break;
148                 case SOLVER_PROBLEM_SELF_CONFLICT:
149                   printf("  package %s conflicts with %s provided by itself\n", solvid2str(pool, source), dep2str(pool, dep));
150                   break;
151                 }
152             }
153         }
154     }
155   queue_free(&rids);
156   queue_free(&rinfo);
157 }
158
159 void
160 toinst(Solver *solv, Repo *repo, Repo *instrepo)
161 {
162   Pool *pool = solv->pool;
163   int k;
164   Id p;
165
166   for (k = 0; k < solv->decisionq.count; k++)
167     {
168       p = solv->decisionq.elements[k];
169       if (p < 0 || p == SYSTEMSOLVABLE)
170         continue;
171       /* oh my! */
172       pool->solvables[p].repo = instrepo;
173     }
174 }
175
176 void
177 frominst(Solver *solv, Repo *repo, Repo *instrepo)
178 {
179   Pool *pool = solv->pool;
180   int k;
181
182   for (k = 1; k < pool->nsolvables; k++)
183     if (pool->solvables[k].repo == instrepo)
184       pool->solvables[k].repo = repo;
185 }
186
187 int
188 main(int argc, char **argv)
189 {
190   Pool *pool;
191   char *arch, *mypatch;
192   const char *pname;
193   int l;
194   FILE *fp;
195   int i, j;
196   Queue job;
197   Queue cand;
198   Queue badguys;
199   Id pid, p, pp;
200   Id con, *conp;
201   Solver *solv;
202   Repo *repo, *instrepo;
203   int status = 0;
204   int tests = 0;
205   int updatestart = 0;
206
207   arch = argv[1];
208   pool = pool_create();
209   pool_setarch(pool, arch);
210   mypatch = argv[2];
211
212   repo = repo_create(pool, 0);
213   instrepo = repo_create(pool, 0);
214   for (i = 3; i < argc; i++)
215     {
216       if (!strcmp(argv[i], "--updaterepos"))
217         {
218           updatestart = pool->nsolvables;
219           continue;
220         }
221       l = strlen(argv[i]);
222       if (!strcmp(argv[i], "-"))
223         fp = stdin;
224       else if ((fp = myfopen(argv[i])) == 0)
225         {
226           perror(argv[i]);
227           exit(1);
228         }
229       if (l >= 8 && !strcmp(argv[i] + l - 8, "packages"))
230         {
231           repo_add_susetags(repo, fp, 0, 0, 0);
232         }
233       else if (l >= 11 && !strcmp(argv[i] + l - 11, "packages.gz"))
234         {
235           repo_add_susetags(repo, fp, 0, 0, 0);
236         }
237       else if (l >= 14 && !strcmp(argv[i] + l - 14, "primary.xml.gz"))
238         {
239           repo_add_rpmmd(repo, fp, 0, 0);
240         }
241       else if (l >= 17 && !strcmp(argv[i] + l - 17, "updateinfo.xml.gz"))
242         {
243           repo_add_updateinfoxml(repo, fp, 0);
244         }
245       else if (repo_add_solv(repo, fp))
246         {
247           fprintf(stderr, "could not add repo %s\n", argv[i]);
248           exit(1);
249         }
250       if (fp != stdin)
251         fclose(fp);
252     }
253
254   /* bad hack ahead: clone repo */
255   instrepo->idarraydata = repo->idarraydata;
256   instrepo->start = repo->start;
257   instrepo->end = repo->end;
258   instrepo->nsolvables = repo->nsolvables;      /* sic! */
259   pool_set_installed(pool, instrepo);
260
261   pool_addfileprovides(pool);
262   pool_createwhatprovides(pool);
263
264   queue_init(&job);
265   queue_init(&cand);
266   queue_init(&badguys);
267
268 #if 0
269   pool_setdebuglevel(pool, 2);
270 #endif
271
272   for (pid = 1; pid < pool->nsolvables; pid++)
273     {
274       Solvable *s = pool->solvables + pid;
275       if (!s->repo)
276         continue;
277       if (!pool_installable(pool, s))
278         continue;
279       pname = id2str(pool, s->name);
280       if (strncmp(pname, "patch:", 6) != 0)
281         continue;
282       if (*mypatch)
283         {
284           if (strncmp(mypatch, pname + 6, strlen(pname + 6)) != 0)
285             continue;
286           if (strcmp(mypatch, pname + 6) != 0)
287             {
288               l = strlen(pname + 6);
289               if (mypatch[l] != '-')
290                 continue;
291               if (strcmp(mypatch + l + 1, id2str(pool, s->evr)) != 0)
292                 continue;
293             }
294         }
295       else
296         {
297           FOR_PROVIDES(p, pp, s->name)
298             {
299               Solvable *s2 = pool->solvables + p;
300               if (evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) < 0)
301                 break;
302             }
303           if (p)
304             continue;   /* found a newer one */
305         }
306       tests++;
307       if (!s->conflicts)
308         continue;
309
310 #if 0
311       printf("testing patch %s-%s\n", pname + 6, id2str(pool, s->evr));
312 #endif
313
314       /* Test 1: are all old patches included */
315       FOR_PROVIDES(p, pp, s->name)
316         {
317           Solvable *s2 = pool->solvables + p;
318           Id con2, *conp2;
319           int shown = 0;
320
321           if (evrcmp(pool, s->evr, s2->evr, EVRCMP_COMPARE) <= 0)
322             continue;
323           if (!s2->conflicts)
324             continue;
325           conp2 = s2->repo->idarraydata + s2->conflicts;
326           while ((con2 = *conp2++) != 0)
327             {
328               Reldep *rd2;
329               if (!ISRELDEP(con2))
330                 continue;
331               rd2 = GETRELDEP(pool, con2);
332               conp = s->repo->idarraydata + s->conflicts;
333               while ((con = *conp++) != 0)
334                 {
335                   Reldep *rd;
336                   if (!ISRELDEP(con))
337                     continue;
338                   rd = GETRELDEP(pool, con);
339                   if (rd->name == rd2->name)
340                     break;
341                 }
342               if (!con)
343                 {
344                   if (!shown++)
345                     printf("%s:\n", solvable2str(pool, s));
346                   printf("  %s contained %s\n", solvable2str(pool, s2), dep2str(pool, rd2->name));
347                 }
348             }
349         }
350
351       /* Test 2: are the packages installable */
352       conp = s->repo->idarraydata + s->conflicts;
353       while ((con = *conp++) != 0)
354         {
355           FOR_PROVIDES(p, pp, con)
356             {
357               queue_empty(&job);
358               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
359               queue_push(&job, p);
360
361               /* also set up some minimal system */
362               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
363               queue_push(&job, str2id(pool, "rpm", 1));
364               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
365               queue_push(&job, str2id(pool, "aaa_base", 1));
366
367               solv = solver_create(pool);
368               solv->dontinstallrecommended = 1;
369               solv = solver_create(pool);
370               solver_solve(solv, &job);
371               toinst(solv, repo, instrepo);
372               solver_free(solv);
373               queue_empty(&job);
374               for (i = 1; i < updatestart; i++)
375                 {
376                   if (pool->solvables[i].repo != repo || i == pid)
377                     continue;
378                   queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
379                   queue_push(&job, i);
380                 }
381               queue_push(&job, SOLVER_INSTALL_SOLVABLE);
382               queue_push(&job, pid);
383               solv = solver_create(pool);
384               solv->dontinstallrecommended = 1;
385               solver_solve(solv, &job);
386               if (solv->problems.count)
387                 {
388                   status = 1;
389                   showproblems(solv, s, 0, 0);
390                 }
391               frominst(solv, repo, instrepo);
392               solver_free(solv);
393             }
394         }
395
396       if (0)
397         continue;
398
399       /* Test 3: can we upgrade all packages? */
400       queue_empty(&cand);
401       queue_empty(&badguys);
402       for (p = 1; p < pool->nsolvables; p++)
403         {
404           Solvable *s = pool->solvables + p;
405           if (!s->repo)
406             continue;
407           if (strchr(id2str(pool, s->name), ':'))
408             continue;   /* only packages, please */
409           if (!pool_installable(pool, s))
410             continue;
411           queue_push(&cand, p);
412         }
413       while (cand.count)
414         {
415           solv = solver_create(pool);
416           solv->dontinstallrecommended = 1;
417           solv = solver_create(pool);
418           queue_empty(&job);
419           for (i = 0; i < badguys.count; i++)
420             {
421               queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE|SOLVER_WEAK);
422               queue_push(&job, badguys.elements[i]);
423             }
424           conp = s->repo->idarraydata + s->conflicts;
425           while ((con = *conp++) != 0)
426             {
427               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE_PROVIDES|SOLVER_WEAK);
428               queue_push(&job, con);
429             }
430           for (i = 0; i < cand.count; i++)
431             {
432               p = cand.elements[i];
433               queue_push(&job, SOLVER_INSTALL|SOLVER_SOLVABLE|SOLVER_WEAK);
434               queue_push(&job, p);
435             }
436           solver_solve(solv, &job);
437 #if 0
438           solver_printdecisions(solv);
439 #endif
440           /* put packages into installed repo and prune them from cand */
441           toinst(solv, repo, instrepo);
442           for (i = 0; i < cand.count; i++)
443             {
444               p = cand.elements[i];
445               if (p > 0 && solv->decisionmap[p] > 0)
446                 cand.elements[i] = -p;  /* drop candidate */
447             }
448           solver_free(solv);
449
450           /* now the interesting part: test patch */
451           queue_empty(&job);
452 #if 0
453           for (i = 1; i < updatestart; i++)
454             {
455               if (pool->solvables[i].repo != repo || i == pid)
456                 continue;
457               queue_push(&job, SOLVER_ERASE|SOLVER_SOLVABLE);
458               queue_push(&job, i);
459             }
460 #endif
461           queue_push(&job, SOLVER_INSTALL_SOLVABLE);
462           queue_push(&job, pid);
463           solv = solver_create(pool);
464           solv->dontinstallrecommended = 1;
465           solver_solve(solv, &job);
466
467           if (solv->problems.count)
468             {
469               status = 1;
470               showproblems(solv, s, &cand, &badguys);
471             }
472           frominst(solv, repo, instrepo);
473           solver_free(solv);
474           /* now drop all negative elements from cand */
475           for (i = j = 0; i < cand.count; i++)
476             {
477               if (cand.elements[i] < 0)
478                 continue;
479               cand.elements[j++] = cand.elements[i];
480             }
481           if (i == j)
482             break;      /* no progress */
483           cand.count = j;
484         }
485     }
486   exit(status);
487 }