9 #include <rpm/rpmtag.h>
10 #include <rpm/rpmmacro.h> /* XXX rpmExpand("%{_dependency_whiteout */
11 #include <rpm/rpmlog.h>
12 #include <rpm/rpmds.h>
13 #include <rpm/rpmfi.h>
15 #include "lib/rpmte_internal.h" /* XXX tsortInfo_s */
16 #include "lib/rpmts_internal.h"
21 * Strongly Connected Components
22 * set of packages (indirectly) requiering each other
25 int count; /* # of external requires this SCC has */
26 /* int qcnt; # of external requires pointing to this SCC */
27 int size; /* # of members */
31 typedef struct scc_s * scc;
38 static int badDepsInitialized = 0;
39 static struct badDeps_s * badDeps = NULL;
43 static void freeBadDeps(void)
46 struct badDeps_s * bdp;
47 /* bdp->qname is a pointer to pname so doesn't need freeing */
48 for (bdp = badDeps; bdp->pname != NULL && bdp->qname != NULL; bdp++)
49 bdp->pname = _free(bdp->pname);
50 badDeps = _free(badDeps);
52 badDepsInitialized = 0;
56 * Check for dependency relations to be ignored.
58 * @param ts transaction set
59 * @param p successor element (i.e. with Requires: )
60 * @param q predecessor element (i.e. with Provides: )
61 * @return 1 if dependency is to be ignored.
63 static int ignoreDep(const rpmts ts, const rpmte p, const rpmte q)
65 struct badDeps_s * bdp;
67 if (!badDepsInitialized) {
68 char * s = rpmExpand("%{?_dependency_whiteout}", NULL);
69 const char ** av = NULL;
70 int msglvl = (rpmtsFlags(ts) & RPMTRANS_FLAG_DEPLOOPS)
71 ? RPMLOG_WARNING : RPMLOG_DEBUG;
75 if (s != NULL && *s != '\0'
76 && !(i = poptParseArgvString(s, &ac, (const char ***)&av))
77 && ac > 0 && av != NULL)
79 bdp = badDeps = xcalloc(ac+1, sizeof(*badDeps));
80 for (i = 0; i < ac; i++, bdp++) {
81 char * pname, * qname;
85 pname = xstrdup(av[i]);
86 if ((qname = strchr(pname, '>')) != NULL)
91 _("ignore package name relation(s) [%d]\t%s -> %s\n"),
92 i, bdp->pname, (bdp->qname ? bdp->qname : "???"));
103 for (bdp = badDeps; bdp->pname != NULL && bdp->qname != NULL; bdp++) {
104 if (rstreq(rpmteN(p), bdp->pname) && rstreq(rpmteN(q), bdp->qname))
111 * Record next "q <- p" relation (i.e. "p" requires "q").
112 * @param ts transaction set
113 * @param p predecessor (i.e. package that "Requires: q")
114 * @param requires relation
117 static inline int addRelation(rpmts ts,
123 struct tsortInfo_s * tsi_p, * tsi_q;
125 rpmElementType teType = rpmteType(p);
126 rpmsenseFlags flags, dsflags;
128 dsflags = rpmdsFlags(requires);
130 /* Avoid rpmlib feature and package config dependencies */
131 if (dsflags & (RPMSENSE_RPMLIB|RPMSENSE_CONFIG))
134 q = rpmalSatisfiesDepend(al, requires);
136 /* Avoid deps outside this transaction and self dependencies */
137 if (q == NULL || q == p)
140 /* Avoid certain dependency relations. */
141 if (ignoreDep(ts, p, q))
144 /* Erasures are reversed installs. */
145 if (teType == TR_REMOVED) {
149 flags = isErasePreReq(dsflags);
151 flags = isInstallPreReq(dsflags);
154 /* map legacy prereq to pre/preun as needed */
155 if (isLegacyPreReq(dsflags)) {
156 flags |= (teType == TR_ADDED) ?
157 RPMSENSE_SCRIPT_PRE : RPMSENSE_SCRIPT_PREUN;
163 /* if relation got already added just update the flags */
164 if (tsi_q->tsi_relations && tsi_q->tsi_relations->rel_suc == p) {
165 tsi_q->tsi_relations->rel_flags |= flags;
166 tsi_p->tsi_forward_relations->rel_flags |= flags;
170 /* Record next "q <- p" relation (i.e. "p" requires "q"). */
172 /* bump p predecessor count */
173 rpmteTSI(p)->tsi_count++;
176 /* Save max. depth in dependency tree */
177 if (rpmteDepth(p) <= rpmteDepth(q))
178 (void) rpmteSetDepth(p, (rpmteDepth(q) + 1));
179 if (rpmteDepth(p) > ts->maxDepth)
180 ts->maxDepth = rpmteDepth(p);
182 rel = xcalloc(1, sizeof(*rel));
184 rel->rel_flags = flags;
186 rel->rel_next = rpmteTSI(q)->tsi_relations;
187 rpmteTSI(q)->tsi_relations = rel;
189 /* bump q successor count */
190 rpmteTSI(q)->tsi_qcnt++;
193 rel = xcalloc(1, sizeof(*rel));
195 rel->rel_flags = flags;
197 rel->rel_next = rpmteTSI(p)->tsi_forward_relations;
198 rpmteTSI(p)->tsi_forward_relations = rel;
204 * Add element to list sorting by tsi_qcnt.
205 * @param p new element
206 * @retval qp address of first element
207 * @retval rp address of last element
210 static void addQ(rpmte p,
213 rpm_color_t prefcolor)
217 /* Mark the package as queued. */
218 rpmteTSI(p)->tsi_reqx = 1;
220 if ((*rp) == NULL) { /* 1st element */
221 /* FIX: double indirection */
226 /* Find location in queue using metric tsi_qcnt. */
227 for (qprev = NULL, q = (*qp);
229 qprev = q, q = rpmteTSI(q)->tsi_suc)
231 /* XXX Insure preferred color first. */
232 if (rpmteColor(p) != prefcolor && rpmteColor(p) != rpmteColor(q))
235 if (rpmteTSI(q)->tsi_qcnt <= rpmteTSI(p)->tsi_qcnt)
239 if (qprev == NULL) { /* insert at beginning of list */
240 rpmteTSI(p)->tsi_suc = q;
241 (*qp) = p; /* new head */
242 } else if (q == NULL) { /* insert at end of list */
243 rpmteTSI(qprev)->tsi_suc = p;
244 (*rp) = p; /* new tail */
245 } else { /* insert between qprev and q */
246 rpmteTSI(p)->tsi_suc = q;
247 rpmteTSI(qprev)->tsi_suc = p;
251 /* Search for SCCs and return an array last entry has a .size of 0 */
252 static scc detectSCCs(rpmts ts)
254 int index = 0; /* DFS node number counter */
255 rpmte stack[ts->orderCount]; /* An empty stack of nodes */
261 scc SCCs = xcalloc(ts->orderCount+3, sizeof(struct scc_s));
263 auto void tarjan(rpmte p);
265 void tarjan(rpmte p) {
266 tsortInfo tsi = rpmteTSI(p);
271 /* use negative index numbers */
273 /* Set the depth index for p */
274 tsi->tsi_SccIdx = index;
275 tsi->tsi_SccLowlink = index;
277 stack[stackcnt++] = p; /* Push p on the stack */
278 for (rel=tsi->tsi_relations; rel != NULL; rel=rel->rel_next) {
279 /* Consider successors of p */
282 if (tsi_q->tsi_SccIdx > 0)
283 /* Ignore already found SCCs */
285 if (tsi_q->tsi_SccIdx == 0){
286 /* Was successor q not yet visited? */
287 tarjan(q); /* Recurse */
288 /* negative index numers: use max as it is closer to 0 */
289 tsi->tsi_SccLowlink = (
290 tsi->tsi_SccLowlink > tsi_q->tsi_SccLowlink
291 ? tsi->tsi_SccLowlink : tsi_q->tsi_SccLowlink);
293 tsi->tsi_SccLowlink = (
294 tsi->tsi_SccLowlink > tsi_q->tsi_SccIdx
295 ? tsi->tsi_SccLowlink : tsi_q->tsi_SccIdx);
299 if (tsi->tsi_SccLowlink == tsi->tsi_SccIdx) {
300 /* v is the root of an SCC? */
301 if (stack[stackcnt-1] == p) {
302 /* ignore trivial SCCs */
303 q = stack[--stackcnt];
305 tsi_q->tsi_SccIdx = 1;
307 int stackIdx = stackcnt;
309 q = stack[--stackIdx];
311 tsi_q->tsi_SccIdx = sccCnt;
316 q = stack[--stackIdx];
318 /* Calculate count for the SCC */
319 SCCs[sccCnt].count += tsi_q->tsi_count;
320 /* Subtract internal relations */
321 for (rel=tsi_q->tsi_relations; rel != NULL;
323 if (rel->rel_suc != q &&
324 rpmteTSI(rel->rel_suc)->tsi_SccIdx == sccCnt)
325 SCCs[sccCnt].count--;
328 SCCs[sccCnt].size = stackcnt - stackIdx;
330 SCCs[sccCnt].members = xcalloc(SCCs[sccCnt].size,
332 memcpy(SCCs[sccCnt].members, stack + stackIdx,
333 SCCs[sccCnt].size * sizeof(rpmte));
341 while ((p=rpmtsiNext(pi, 0)) != NULL) {
342 /* Start a DFS at each node */
343 if (rpmteTSI(p)->tsi_SccIdx == 0)
348 SCCs = xrealloc(SCCs, (sccCnt+1)*sizeof(struct scc_s));
351 int msglvl = (rpmtsFlags(ts) & RPMTRANS_FLAG_DEPLOOPS) ?
352 RPMLOG_WARNING : RPMLOG_DEBUG;
353 rpmlog(msglvl, "%i Strongly Connected Components\n", sccCnt-2);
354 for (int i = 2; i < sccCnt; i++) {
355 rpmlog(msglvl, "SCC #%i: requires %i packages\n",
357 /* loop over members */
358 for (int j = 0; j < SCCs[i].size; j++) {
359 rpmlog(msglvl, "\t%s\n", rpmteNEVRA(SCCs[i].members[j]));
360 /* show relations between members */
361 rpmte member = SCCs[i].members[j];
362 relation rel = rpmteTSI(member)->tsi_forward_relations;
363 for (; rel != NULL; rel=rel->rel_next) {
364 if (rpmteTSI(rel->rel_suc)->tsi_SccIdx!=i) continue;
365 rpmlog(msglvl, "\t\t%s %s\n",
366 rel->rel_flags ? "=>" : "->",
367 rpmteNEVRA(rel->rel_suc));
375 static void collectTE(rpm_color_t prefcolor, rpmte q,
376 rpmte * newOrder, int * newOrderCount,
380 rpmte * outer_queue_end)
382 int treex = rpmteTree(q);
383 int depth = rpmteDepth(q);
384 char deptypechar = (rpmteType(q) == TR_REMOVED ? '-' : '+');
385 tsortInfo tsi = rpmteTSI(q);
387 rpmlog(RPMLOG_DEBUG, "%5d%5d%5d%5d%5d %*s%c%s\n",
388 *newOrderCount, rpmteNpreds(q),
389 rpmteTSI(q)->tsi_qcnt,
393 (rpmteNEVRA(q) ? rpmteNEVRA(q) : "???"));
395 (void) rpmteSetDegree(q, 0);
397 newOrder[*newOrderCount] = q;
400 /* T6. Erase relations. */
401 for (relation rel = rpmteTSI(q)->tsi_relations;
402 rel != NULL; rel = rel->rel_next) {
403 rpmte p = rel->rel_suc;
404 tsortInfo p_tsi = rpmteTSI(p);
405 /* ignore already collected packages */
406 if (p_tsi->tsi_SccIdx == 0) continue;
407 if (p == q) continue;
409 if (p && (--p_tsi->tsi_count) == 0) {
411 (void) rpmteSetTree(p, treex);
412 (void) rpmteSetDepth(p, depth+1);
413 (void) rpmteSetParent(p, q);
414 (void) rpmteSetDegree(q, rpmteDegree(q)+1);
416 if (tsi->tsi_SccIdx > 1 && tsi->tsi_SccIdx != p_tsi->tsi_SccIdx) {
417 /* Relation point outside of this SCC: add to outside queue */
418 assert(outer_queue != NULL && outer_queue_end != NULL);
419 addQ(p, outer_queue, outer_queue_end, prefcolor);
421 addQ(p, &tsi->tsi_suc, queue_end, prefcolor);
424 if (p && p_tsi->tsi_SccIdx > 1 &&
425 p_tsi->tsi_SccIdx != rpmteTSI(q)->tsi_SccIdx) {
426 if (--SCCs[p_tsi->tsi_SccIdx].count == 0) {
427 /* New SCC is ready, add this package as representative */
429 (void) rpmteSetTree(p, treex);
430 (void) rpmteSetDepth(p, depth+1);
431 (void) rpmteSetParent(p, q);
432 (void) rpmteSetDegree(q, rpmteDegree(q)+1);
434 if (outer_queue != NULL) {
435 addQ(p, outer_queue, outer_queue_end, prefcolor);
437 addQ(p, &rpmteTSI(q)->tsi_suc, queue_end, prefcolor);
445 static void collectSCC(rpm_color_t prefcolor, rpmte p,
446 rpmte * newOrder, int * newOrderCount,
447 scc SCCs, rpmte * queue_end)
449 int sccNr = rpmteTSI(p)->tsi_SccIdx;
450 struct scc_s SCC = SCCs[sccNr];
455 /* remove p from the outer queue */
456 rpmte outer_queue_start = rpmteTSI(p)->tsi_suc;
457 rpmteTSI(p)->tsi_suc = NULL;
460 * Run a multi source Dijkstra's algorithm to find relations
461 * that can be zapped with least danger to pre reqs.
462 * As weight of the edges is always 1 it is not necessary to
463 * sort the vertices by distance as the queue gets them
467 /* can use a simple queue as edge weights are always 1 */
468 rpmte * queue = xmalloc((SCC.size+1) * sizeof(rpmte));
471 * Find packages that are prerequired and use them as
472 * starting points for the Dijkstra algorithm
475 for (i = 0; i < SCC.size; i++) {
476 rpmte q = SCC.members[i];
477 tsortInfo tsi = rpmteTSI(q);
478 tsi->tsi_SccLowlink = INT_MAX;
479 for (rel=tsi->tsi_forward_relations; rel != NULL; rel=rel->rel_next) {
480 if (rel->rel_flags && rpmteTSI(rel->rel_suc)->tsi_SccIdx == sccNr) {
481 if (rel->rel_suc != q) {
482 tsi->tsi_SccLowlink = 0;
485 tsi->tsi_SccLowlink = INT_MAX/2;
492 if (start == end) { /* no regular prereqs; add self prereqs to queue */
493 for (i = 0; i < SCC.size; i++) {
494 rpmte q = SCC.members[i];
495 tsortInfo tsi = rpmteTSI(q);
496 if (tsi->tsi_SccLowlink != INT_MAX) {
503 while (start != end) {
504 rpmte q = queue[start++];
505 tsortInfo tsi = rpmteTSI(q);
506 for (rel=tsi->tsi_forward_relations; rel != NULL; rel=rel->rel_next) {
507 tsortInfo next_tsi = rpmteTSI(rel->rel_suc);
508 if (next_tsi->tsi_SccIdx != sccNr) continue;
509 if (next_tsi->tsi_SccLowlink > tsi->tsi_SccLowlink+1) {
510 next_tsi->tsi_SccLowlink = tsi->tsi_SccLowlink + 1;
511 queue[end++] = rel->rel_suc;
515 queue = _free(queue);
520 rpmte inner_queue_start, inner_queue_end;
523 /* select best candidate to start with */
524 for (int i = 0; i < SCC.size; i++) {
525 rpmte q = SCC.members[i];
526 tsortInfo tsi = rpmteTSI(q);
527 if (tsi->tsi_SccIdx == 0) /* package already collected */
529 if (tsi->tsi_SccLowlink >= best_score) {
531 best_score = tsi->tsi_SccLowlink;
535 if (best == NULL) /* done */
538 /* collect best candidate and all packages that get freed */
539 inner_queue_start = inner_queue_end = NULL;
540 addQ(best, &inner_queue_start, &inner_queue_end, prefcolor);
542 for (; inner_queue_start != NULL;
543 inner_queue_start = rpmteTSI(inner_queue_start)->tsi_suc) {
544 /* Mark the package as unqueued. */
545 rpmteTSI(inner_queue_start)->tsi_reqx = 0;
546 collectTE(prefcolor, inner_queue_start, newOrder, newOrderCount,
547 SCCs, &inner_queue_end, &outer_queue_start, queue_end);
551 /* restore outer queue */
552 rpmteTSI(p)->tsi_suc = outer_queue_start;
555 int rpmtsOrder(rpmts ts)
557 rpm_color_t prefcolor = rpmtsPrefColor(ts);
562 int newOrderCount = 0;
565 rpmal erasedPackages = rpmalCreate(5, rpmtsColor(ts), prefcolor);
568 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_ORDER), 0);
571 * XXX FIXME: this gets needlesly called twice on normal usage patterns,
572 * should track the need for generating the index somewhere
574 rpmalMakeIndex(ts->addedPackages);
576 /* Create erased package index. */
578 while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
579 rpmalAdd(erasedPackages, p);
582 rpmalMakeIndex(erasedPackages);
585 while ((p = rpmtsiNext(pi, 0)) != NULL)
589 /* Record relations. */
590 rpmlog(RPMLOG_DEBUG, "========== recording tsort relations\n");
592 while ((p = rpmtsiNext(pi, 0)) != NULL) {
593 rpmal al = (rpmteType(p) == TR_REMOVED) ?
594 erasedPackages : ts->addedPackages;
595 rpmds requires = rpmdsInit(rpmteDS(p, RPMTAG_REQUIRENAME));
597 while (rpmdsNext(requires) >= 0) {
598 /* Record next "q <- p" relation (i.e. "p" requires "q"). */
599 (void) addRelation(ts, al, p, requires);
604 /* Save predecessor count and mark tree roots. */
607 while ((p = rpmtsiNext(pi, 0)) != NULL) {
608 int npreds = rpmteTSI(p)->tsi_count;
610 (void) rpmteSetNpreds(p, npreds);
611 (void) rpmteSetDepth(p, 1);
614 (void) rpmteSetTree(p, treex++);
616 (void) rpmteSetTree(p, -1);
621 newOrder = xcalloc(ts->orderCount, sizeof(*newOrder));
622 SCCs = detectSCCs(ts);
624 rpmlog(RPMLOG_DEBUG, "========== tsorting packages (order, #predecessors, #succesors, tree, depth)\n");
626 for (int i = 0; i < 2; i++) {
627 /* Do two separate runs: installs first - then erases */
628 int oType = !i ? TR_ADDED : TR_REMOVED;
631 /* Scan for zeroes and add them to the queue */
632 while ((p = rpmtsiNext(pi, oType)) != NULL) {
633 if (rpmteTSI(p)->tsi_count != 0)
635 rpmteTSI(p)->tsi_suc = NULL;
636 addQ(p, &q, &r, prefcolor);
640 /* Add one member of each leaf SCC */
641 for (int i = 2; SCCs[i].members != NULL; i++) {
642 if (SCCs[i].count == 0 && rpmteType(SCCs[i].members[0]) == oType) {
643 p = SCCs[i].members[0];
644 rpmteTSI(p)->tsi_suc = NULL;
645 addQ(p, &q, &r, prefcolor);
650 for (; q != NULL; q = rpmteTSI(q)->tsi_suc) {
651 /* Mark the package as unqueued. */
652 rpmteTSI(q)->tsi_reqx = 0;
653 if (rpmteTSI(q)->tsi_SccIdx > 1) {
654 collectSCC(prefcolor, q, newOrder, &newOrderCount, SCCs, &r);
656 collectTE(prefcolor, q, newOrder, &newOrderCount, SCCs, &r,
662 /* Clean up tsort data */
664 while ((p = rpmtsiNext(pi, 0)) != NULL)
668 assert(newOrderCount == ts->orderCount);
670 ts->order = _free(ts->order);
671 ts->order = newOrder;
672 ts->orderAlloced = ts->orderCount;
677 for (int i = 2; SCCs[i].members != NULL; i++) {
678 free(SCCs[i].members);
681 rpmalFree(erasedPackages);
683 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_ORDER), 0);