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;
34 tsortInfo rel_suc; // pkg requiring this package
35 rpmsenseFlags rel_flags; // accumulated flags of the requirements
36 struct relation_s * rel_next;
39 typedef struct relation_s * relation;
43 int tsi_count; // #pkgs this pkg requires
44 int tsi_qcnt; // #pkgs requiring this package
45 int tsi_reqx; // requires Idx/mark as (queued/loop)
46 struct relation_s * tsi_relations;
47 struct relation_s * tsi_forward_relations;
48 tsortInfo tsi_suc; // used for queuing (addQ)
49 int tsi_SccIdx; // # of the SCC the node belongs to
50 // (1 for trivial SCCs)
51 int tsi_SccLowlink; // used for SCC detection
59 static int badDepsInitialized = 0;
60 static struct badDeps_s * badDeps = NULL;
64 static void freeBadDeps(void)
67 struct badDeps_s * bdp;
68 /* bdp->qname is a pointer to pname so doesn't need freeing */
69 for (bdp = badDeps; bdp->pname != NULL && bdp->qname != NULL; bdp++)
70 bdp->pname = _free(bdp->pname);
71 badDeps = _free(badDeps);
73 badDepsInitialized = 0;
77 * Check for dependency relations to be ignored.
79 * @param ts transaction set
80 * @param p successor element (i.e. with Requires: )
81 * @param q predecessor element (i.e. with Provides: )
82 * @return 1 if dependency is to be ignored.
84 static int ignoreDep(const rpmts ts, const rpmte p, const rpmte q)
86 struct badDeps_s * bdp;
88 if (!badDepsInitialized) {
89 char * s = rpmExpand("%{?_dependency_whiteout}", NULL);
90 const char ** av = NULL;
91 int msglvl = (rpmtsFlags(ts) & RPMTRANS_FLAG_DEPLOOPS)
92 ? RPMLOG_WARNING : RPMLOG_DEBUG;
96 if (s != NULL && *s != '\0'
97 && !(i = poptParseArgvString(s, &ac, (const char ***)&av))
98 && ac > 0 && av != NULL)
100 bdp = badDeps = xcalloc(ac+1, sizeof(*badDeps));
101 for (i = 0; i < ac; i++, bdp++) {
102 char * pname, * qname;
106 pname = xstrdup(av[i]);
107 if ((qname = strchr(pname, '>')) != NULL)
112 _("ignore package name relation(s) [%d]\t%s -> %s\n"),
113 i, bdp->pname, (bdp->qname ? bdp->qname : "???"));
120 badDepsInitialized++;
124 for (bdp = badDeps; bdp->pname != NULL && bdp->qname != NULL; bdp++) {
125 if (rstreq(rpmteN(p), bdp->pname) && rstreq(rpmteN(q), bdp->qname))
131 static void rpmTSIFree(tsortInfo tsi)
135 while (tsi->tsi_relations != NULL) {
136 rel = tsi->tsi_relations;
137 tsi->tsi_relations = tsi->tsi_relations->rel_next;
140 while (tsi->tsi_forward_relations != NULL) {
141 rel = tsi->tsi_forward_relations;
142 tsi->tsi_forward_relations = \
143 tsi->tsi_forward_relations->rel_next;
149 * Record next "q <- p" relation (i.e. "p" requires "q").
150 * @param ts transaction set
151 * @param p predecessor (i.e. package that "Requires: q")
152 * @param requires relation
155 static inline int addRelation(rpmts ts,
161 struct tsortInfo_s * tsi_p, * tsi_q;
163 rpmElementType teType = rpmteType(p);
164 rpmsenseFlags flags, dsflags;
166 dsflags = rpmdsFlags(requires);
168 /* Avoid rpmlib feature and package config dependencies */
169 if (dsflags & (RPMSENSE_RPMLIB|RPMSENSE_CONFIG))
172 q = rpmalSatisfiesDepend(al, requires);
174 /* Avoid deps outside this transaction and self dependencies */
175 if (q == NULL || q == p)
178 /* Avoid certain dependency relations. */
179 if (ignoreDep(ts, p, q))
182 /* Erasures are reversed installs. */
183 if (teType == TR_REMOVED) {
187 flags = isErasePreReq(dsflags);
189 flags = isInstallPreReq(dsflags);
192 /* map legacy prereq to pre/preun as needed */
193 if (isLegacyPreReq(dsflags)) {
194 flags |= (teType == TR_ADDED) ?
195 RPMSENSE_SCRIPT_PRE : RPMSENSE_SCRIPT_PREUN;
201 /* if relation got already added just update the flags */
202 if (tsi_q->tsi_relations && tsi_q->tsi_relations->rel_suc == tsi_p) {
203 tsi_q->tsi_relations->rel_flags |= flags;
204 tsi_p->tsi_forward_relations->rel_flags |= flags;
208 /* Record next "q <- p" relation (i.e. "p" requires "q"). */
210 /* bump p predecessor count */
214 rel = xcalloc(1, sizeof(*rel));
215 rel->rel_suc = tsi_p;
216 rel->rel_flags = flags;
218 rel->rel_next = tsi_q->tsi_relations;
219 tsi_q->tsi_relations = rel;
221 /* bump q successor count */
225 rel = xcalloc(1, sizeof(*rel));
226 rel->rel_suc = tsi_q;
227 rel->rel_flags = flags;
229 rel->rel_next = tsi_p->tsi_forward_relations;
230 tsi_p->tsi_forward_relations = rel;
236 * Add element to list sorting by tsi_qcnt.
237 * @param p new element
238 * @retval qp address of first element
239 * @retval rp address of last element
242 static void addQ(tsortInfo p, tsortInfo * qp, tsortInfo * rp,
243 rpm_color_t prefcolor)
247 /* Mark the package as queued. */
250 if ((*rp) == NULL) { /* 1st element */
251 /* FIX: double indirection */
256 /* Find location in queue using metric tsi_qcnt. */
257 for (qprev = NULL, q = (*qp);
259 qprev = q, q = q->tsi_suc)
261 /* XXX Insure preferred color first. */
262 if (rpmteColor(p->te) != prefcolor && rpmteColor(p->te) != rpmteColor(q->te))
265 if (q->tsi_qcnt <= p->tsi_qcnt)
269 if (qprev == NULL) { /* insert at beginning of list */
271 (*qp) = p; /* new head */
272 } else if (q == NULL) { /* insert at end of list */
274 (*rp) = p; /* new tail */
275 } else { /* insert between qprev and q */
281 /* Search for SCCs and return an array last entry has a .size of 0 */
282 static scc detectSCCs(tsortInfo orderInfo, int nelem, int debugloops)
284 int index = 0; /* DFS node number counter */
285 tsortInfo stack[nelem]; /* An empty stack of nodes */
289 scc SCCs = xcalloc(nelem+3, sizeof(struct scc_s));
291 auto void tarjan(tsortInfo tsi);
293 void tarjan(tsortInfo tsi) {
297 /* use negative index numbers */
299 /* Set the depth index for p */
300 tsi->tsi_SccIdx = index;
301 tsi->tsi_SccLowlink = index;
303 stack[stackcnt++] = tsi; /* Push p on the stack */
304 for (rel=tsi->tsi_relations; rel != NULL; rel=rel->rel_next) {
305 /* Consider successors of p */
306 tsi_q = rel->rel_suc;
307 if (tsi_q->tsi_SccIdx > 0)
308 /* Ignore already found SCCs */
310 if (tsi_q->tsi_SccIdx == 0){
311 /* Was successor q not yet visited? */
312 tarjan(tsi_q); /* Recurse */
313 /* negative index numers: use max as it is closer to 0 */
314 tsi->tsi_SccLowlink = (
315 tsi->tsi_SccLowlink > tsi_q->tsi_SccLowlink
316 ? tsi->tsi_SccLowlink : tsi_q->tsi_SccLowlink);
318 tsi->tsi_SccLowlink = (
319 tsi->tsi_SccLowlink > tsi_q->tsi_SccIdx
320 ? tsi->tsi_SccLowlink : tsi_q->tsi_SccIdx);
324 if (tsi->tsi_SccLowlink == tsi->tsi_SccIdx) {
325 /* v is the root of an SCC? */
326 if (stack[stackcnt-1] == tsi) {
327 /* ignore trivial SCCs */
328 tsi_q = stack[--stackcnt];
329 tsi_q->tsi_SccIdx = 1;
331 int stackIdx = stackcnt;
333 tsi_q = stack[--stackIdx];
334 tsi_q->tsi_SccIdx = sccCnt;
335 } while (tsi_q != tsi);
339 tsi_q = stack[--stackIdx];
340 /* Calculate count for the SCC */
341 SCCs[sccCnt].count += tsi_q->tsi_count;
342 /* Subtract internal relations */
343 for (rel=tsi_q->tsi_relations; rel != NULL;
345 if (rel->rel_suc != tsi_q &&
346 rel->rel_suc->tsi_SccIdx == sccCnt)
347 SCCs[sccCnt].count--;
349 } while (tsi_q != tsi);
350 SCCs[sccCnt].size = stackcnt - stackIdx;
352 SCCs[sccCnt].members = xcalloc(SCCs[sccCnt].size,
354 memcpy(SCCs[sccCnt].members, stack + stackIdx,
355 SCCs[sccCnt].size * sizeof(tsortInfo));
362 for (int i = 0; i < nelem; i++) {
363 tsortInfo tsi = &orderInfo[i];
364 /* Start a DFS at each node */
365 if (tsi->tsi_SccIdx == 0)
369 SCCs = xrealloc(SCCs, (sccCnt+1)*sizeof(struct scc_s));
372 int msglvl = debugloops ? RPMLOG_WARNING : RPMLOG_DEBUG;
373 rpmlog(msglvl, "%i Strongly Connected Components\n", sccCnt-2);
374 for (int i = 2; i < sccCnt; i++) {
375 rpmlog(msglvl, "SCC #%i: requires %i packages\n",
377 /* loop over members */
378 for (int j = 0; j < SCCs[i].size; j++) {
379 tsortInfo member = SCCs[i].members[j];
380 rpmlog(msglvl, "\t%s\n", rpmteNEVRA(member->te));
381 /* show relations between members */
382 relation rel = member->tsi_forward_relations;
383 for (; rel != NULL; rel=rel->rel_next) {
384 if (rel->rel_suc->tsi_SccIdx!=i) continue;
385 rpmlog(msglvl, "\t\t%s %s\n",
386 rel->rel_flags ? "=>" : "->",
387 rpmteNEVRA(rel->rel_suc->te));
395 static void collectTE(rpm_color_t prefcolor, tsortInfo q,
396 rpmte * newOrder, int * newOrderCount,
398 tsortInfo * queue_end,
399 tsortInfo * outer_queue,
400 tsortInfo * outer_queue_end)
402 char deptypechar = (rpmteType(q->te) == TR_REMOVED ? '-' : '+');
406 /* figure depth in tree for nice formatting */
407 for (rpmte p = q->te; (p = rpmteParent(p)); depth++) {}
408 rpmlog(RPMLOG_DEBUG, "%5d%5d%5d%5d %*s%c%s\n",
409 *newOrderCount, q->tsi_count, q->tsi_qcnt,
410 depth, (2 * depth), "",
411 deptypechar, rpmteNEVRA(q->te));
414 newOrder[*newOrderCount] = q->te;
417 /* T6. Erase relations. */
418 for (relation rel = q->tsi_relations; rel != NULL; rel = rel->rel_next) {
419 tsortInfo p = rel->rel_suc;
420 /* ignore already collected packages */
421 if (p->tsi_SccIdx == 0) continue;
422 if (p == q) continue;
424 if (p && (--p->tsi_count) == 0) {
425 (void) rpmteSetParent(p->te, q->te);
427 if (q->tsi_SccIdx > 1 && q->tsi_SccIdx != p->tsi_SccIdx) {
428 /* Relation point outside of this SCC: add to outside queue */
429 assert(outer_queue != NULL && outer_queue_end != NULL);
430 addQ(p, outer_queue, outer_queue_end, prefcolor);
432 addQ(p, &q->tsi_suc, queue_end, prefcolor);
435 if (p && p->tsi_SccIdx > 1 &&
436 p->tsi_SccIdx != q->tsi_SccIdx) {
437 if (--SCCs[p->tsi_SccIdx].count == 0) {
438 /* New SCC is ready, add this package as representative */
439 (void) rpmteSetParent(p->te, q->te);
441 if (outer_queue != NULL) {
442 addQ(p, outer_queue, outer_queue_end, prefcolor);
444 addQ(p, &q->tsi_suc, queue_end, prefcolor);
452 static void collectSCC(rpm_color_t prefcolor, tsortInfo p_tsi,
453 rpmte * newOrder, int * newOrderCount,
454 scc SCCs, tsortInfo * queue_end)
456 int sccNr = p_tsi->tsi_SccIdx;
457 struct scc_s SCC = SCCs[sccNr];
462 /* remove p from the outer queue */
463 tsortInfo outer_queue_start = p_tsi->tsi_suc;
464 p_tsi->tsi_suc = NULL;
467 * Run a multi source Dijkstra's algorithm to find relations
468 * that can be zapped with least danger to pre reqs.
469 * As weight of the edges is always 1 it is not necessary to
470 * sort the vertices by distance as the queue gets them
474 /* can use a simple queue as edge weights are always 1 */
475 tsortInfo * queue = xmalloc((SCC.size+1) * sizeof(*queue));
478 * Find packages that are prerequired and use them as
479 * starting points for the Dijkstra algorithm
482 for (i = 0; i < SCC.size; i++) {
483 tsortInfo tsi = SCC.members[i];
484 tsi->tsi_SccLowlink = INT_MAX;
485 for (rel=tsi->tsi_forward_relations; rel != NULL; rel=rel->rel_next) {
486 if (rel->rel_flags && rel->rel_suc->tsi_SccIdx == sccNr) {
487 if (rel->rel_suc != tsi) {
488 tsi->tsi_SccLowlink = 0;
491 tsi->tsi_SccLowlink = INT_MAX/2;
498 if (start == end) { /* no regular prereqs; add self prereqs to queue */
499 for (i = 0; i < SCC.size; i++) {
500 tsortInfo tsi = SCC.members[i];
501 if (tsi->tsi_SccLowlink != INT_MAX) {
508 while (start != end) {
509 tsortInfo tsi = queue[start++];
510 for (rel=tsi->tsi_forward_relations; rel != NULL; rel=rel->rel_next) {
511 tsortInfo next_tsi = rel->rel_suc;
512 if (next_tsi->tsi_SccIdx != sccNr) continue;
513 if (next_tsi->tsi_SccLowlink > tsi->tsi_SccLowlink+1) {
514 next_tsi->tsi_SccLowlink = tsi->tsi_SccLowlink + 1;
515 queue[end++] = rel->rel_suc;
519 queue = _free(queue);
523 tsortInfo best = NULL;
524 tsortInfo inner_queue_start, inner_queue_end;
527 /* select best candidate to start with */
528 for (int i = 0; i < SCC.size; i++) {
529 tsortInfo tsi = SCC.members[i];
530 if (tsi->tsi_SccIdx == 0) /* package already collected */
532 if (tsi->tsi_SccLowlink >= best_score) {
534 best_score = tsi->tsi_SccLowlink;
538 if (best == NULL) /* done */
541 /* collect best candidate and all packages that get freed */
542 inner_queue_start = inner_queue_end = NULL;
543 addQ(best, &inner_queue_start, &inner_queue_end, prefcolor);
545 for (; inner_queue_start != NULL;
546 inner_queue_start = inner_queue_start->tsi_suc) {
547 /* Mark the package as unqueued. */
548 inner_queue_start->tsi_reqx = 0;
549 collectTE(prefcolor, inner_queue_start, newOrder, newOrderCount,
550 SCCs, &inner_queue_end, &outer_queue_start, queue_end);
554 /* restore outer queue */
555 p_tsi->tsi_suc = outer_queue_start;
558 int rpmtsOrder(rpmts ts)
560 tsMembers tsmem = rpmtsMembers(ts);
561 rpm_color_t prefcolor = rpmtsPrefColor(ts);
565 int newOrderCount = 0;
567 rpmal erasedPackages = rpmalCreate(5, rpmtsColor(ts), prefcolor);
569 int nelem = rpmtsNElements(ts);
570 tsortInfo sortInfo = xcalloc(nelem, sizeof(struct tsortInfo_s));
572 (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_ORDER), 0);
574 /* Create erased package index. */
576 while ((p = rpmtsiNext(pi, TR_REMOVED)) != NULL) {
577 rpmalAdd(erasedPackages, p);
581 for (int i = 0; i < nelem; i++) {
582 sortInfo[i].te = tsmem->order[i];
583 rpmteSetTSI(tsmem->order[i], &sortInfo[i]);
586 /* Record relations. */
587 rpmlog(RPMLOG_DEBUG, "========== recording tsort relations\n");
589 while ((p = rpmtsiNext(pi, 0)) != NULL) {
590 rpmal al = (rpmteType(p) == TR_REMOVED) ?
591 erasedPackages : tsmem->addedPackages;
592 rpmds requires = rpmdsInit(rpmteDS(p, RPMTAG_REQUIRENAME));
594 while (rpmdsNext(requires) >= 0) {
595 /* Record next "q <- p" relation (i.e. "p" requires "q"). */
596 (void) addRelation(ts, al, p, requires);
601 newOrder = xcalloc(tsmem->orderCount, sizeof(*newOrder));
602 SCCs = detectSCCs(sortInfo, nelem, (rpmtsFlags(ts) & RPMTRANS_FLAG_DEPLOOPS));
604 rpmlog(RPMLOG_DEBUG, "========== tsorting packages (order, #predecessors, #succesors, depth)\n");
606 for (int i = 0; i < 2; i++) {
607 /* Do two separate runs: installs first - then erases */
608 int oType = !i ? TR_ADDED : TR_REMOVED;
610 /* Scan for zeroes and add them to the queue */
611 for (int e = 0; e < nelem; e++) {
612 tsortInfo p = &sortInfo[e];
613 if (rpmteType(p->te) != oType) continue;
614 if (p->tsi_count != 0)
617 addQ(p, &q, &r, prefcolor);
620 /* Add one member of each leaf SCC */
621 for (int i = 2; SCCs[i].members != NULL; i++) {
622 tsortInfo member = SCCs[i].members[0];
623 if (SCCs[i].count == 0 && rpmteType(member->te) == oType) {
624 addQ(member, &q, &r, prefcolor);
629 /* Mark the package as unqueued. */
631 if (q->tsi_SccIdx > 1) {
632 collectSCC(prefcolor, q, newOrder, &newOrderCount, SCCs, &r);
634 collectTE(prefcolor, q, newOrder, &newOrderCount, SCCs, &r,
641 /* Clean up tsort data */
642 for (int i = 0; i < nelem; i++) {
643 rpmteSetTSI(tsmem->order[i], NULL);
644 rpmTSIFree(&sortInfo[i]);
648 assert(newOrderCount == tsmem->orderCount);
650 tsmem->order = _free(tsmem->order);
651 tsmem->order = newOrder;
652 tsmem->orderAlloced = tsmem->orderCount;
657 for (int i = 2; SCCs[i].members != NULL; i++) {
658 free(SCCs[i].members);
661 rpmalFree(erasedPackages);
663 (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_ORDER), 0);