Blame lib/order.c

2ff057
/** \ingroup rpmts
2ff057
 * \file lib/depends.c
2ff057
 */
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <rpm/rpmtag.h>
2ff057
#include <rpm/rpmmacro.h>
2ff057
#include <rpm/rpmlog.h>
2ff057
#include <rpm/rpmds.h>
2ff057
2ff057
#include "lib/rpmte_internal.h"	/* XXX tsortInfo_s */
2ff057
#include "lib/rpmts_internal.h"
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
/*
2ff057
 * Strongly Connected Components
2ff057
 * set of packages (indirectly) requiering each other
2ff057
 */
2ff057
struct scc_s {
2ff057
    int count; /* # of external requires this SCC has */
2ff057
    /* int qcnt;  # of external requires pointing to this SCC */
2ff057
    int size;  /* # of members */
2ff057
    tsortInfo * members;
2ff057
};
2ff057
2ff057
typedef struct scc_s * scc;
2ff057
2ff057
struct relation_s {
2ff057
    tsortInfo   rel_suc;  // pkg requiring this package
2ff057
    rpmsenseFlags rel_flags; // accumulated flags of the requirements
2ff057
    struct relation_s * rel_next;
2ff057
};
2ff057
2ff057
typedef struct relation_s * relation;
2ff057
2ff057
struct tsortInfo_s {
2ff057
    rpmte te;
2ff057
    int	     tsi_count;     // #pkgs this pkg requires
2ff057
    int	     tsi_qcnt;      // #pkgs requiring this package
2ff057
    int	     tsi_reqx;       // requires Idx/mark as (queued/loop)
2ff057
    struct relation_s * tsi_relations;
2ff057
    struct relation_s * tsi_forward_relations;
2ff057
    tsortInfo tsi_suc;        // used for queuing (addQ)
2ff057
    int      tsi_SccIdx;     // # of the SCC the node belongs to
2ff057
                             // (1 for trivial SCCs)
2ff057
    int      tsi_SccLowlink; // used for SCC detection
2ff057
};
2ff057
2ff057
static void rpmTSIFree(tsortInfo tsi)
2ff057
{
2ff057
    relation rel;
2ff057
2ff057
    while (tsi->tsi_relations != NULL) {
2ff057
	rel = tsi->tsi_relations;
2ff057
	tsi->tsi_relations = tsi->tsi_relations->rel_next;
2ff057
	free(rel);
2ff057
    }
2ff057
    while (tsi->tsi_forward_relations != NULL) {
2ff057
	rel = tsi->tsi_forward_relations;
2ff057
	tsi->tsi_forward_relations = \
2ff057
	    tsi->tsi_forward_relations->rel_next;
2ff057
	free(rel);
2ff057
    }
2ff057
}
2ff057
2ff057
static inline int addSingleRelation(rpmte p,
2ff057
				    rpmte q,
2ff057
				    rpmsenseFlags dsflags,
2ff057
				    int reversed)
2ff057
{
2ff057
    struct tsortInfo_s *tsi_p, *tsi_q;
2ff057
    relation rel;
2ff057
    rpmElementType teType = rpmteType(p);
2ff057
    rpmsenseFlags flags;
2ff057
2ff057
    /* Avoid deps outside this transaction and self dependencies */
2ff057
    if (q == NULL || q == p)
2ff057
	return 0;
2ff057
2ff057
    /* Erasures are reversed installs. */
2ff057
    if (teType == TR_REMOVED) {
2ff057
	reversed = ! reversed;
2ff057
	flags = isErasePreReq(dsflags);
2ff057
    } else {
2ff057
	flags = isInstallPreReq(dsflags);
2ff057
    }
2ff057
2ff057
    /* map legacy prereq to pre/preun as needed */
2ff057
    if (isLegacyPreReq(dsflags)) {
2ff057
	flags |= (teType == TR_ADDED) ?
2ff057
	    RPMSENSE_SCRIPT_PRE : RPMSENSE_SCRIPT_PREUN;
2ff057
    }
2ff057
2ff057
    if (reversed) {
2ff057
	rpmte r = p;
2ff057
	p = q;
2ff057
	q = r;
2ff057
    }
2ff057
2ff057
    tsi_p = rpmteTSI(p);
2ff057
    tsi_q = rpmteTSI(q);
2ff057
2ff057
    /* if relation got already added just update the flags */
2ff057
    if (!reversed &&
2ff057
	tsi_q->tsi_relations && tsi_q->tsi_relations->rel_suc == tsi_p) {
2ff057
	/* must be latest one added to q as we add all rels to p at once */
2ff057
	tsi_q->tsi_relations->rel_flags |= flags;
2ff057
	/* search entry in p */
2ff057
	for (struct relation_s * tsi = tsi_p->tsi_forward_relations;
2ff057
	     tsi; tsi = tsi->rel_next) {
2ff057
	    if (tsi->rel_suc == tsi_q) {
2ff057
		tsi->rel_flags |= flags;
2ff057
		return 0;
2ff057
	    }
2ff057
	}
2ff057
	assert(0);
2ff057
    }
2ff057
2ff057
    /* if relation got already added just update the flags */
2ff057
    if (reversed && tsi_q->tsi_forward_relations &&
2ff057
	tsi_q->tsi_forward_relations->rel_suc == tsi_p) {
2ff057
	/* must be latest one added to q as we add all rels to p at once */
2ff057
	tsi_q->tsi_forward_relations->rel_flags |= flags;
2ff057
	/* search entry in p */
2ff057
	for (struct relation_s * tsi = tsi_p->tsi_relations;
2ff057
	     tsi; tsi = tsi->rel_next) {
2ff057
	    if (tsi->rel_suc == tsi_q) {
2ff057
		tsi->rel_flags |= flags;
2ff057
		return 0;
2ff057
	    }
2ff057
	}
2ff057
	assert(0);
2ff057
    }
2ff057
2ff057
    /* Record next "q <- p" relation (i.e. "p" requires "q"). */
2ff057
2ff057
    /* bump p predecessor count */
2ff057
    tsi_p->tsi_count++;
2ff057
2ff057
    rel = xcalloc(1, sizeof(*rel));
2ff057
    rel->rel_suc = tsi_p;
2ff057
    rel->rel_flags = flags;
2ff057
2ff057
    rel->rel_next = tsi_q->tsi_relations;
2ff057
    tsi_q->tsi_relations = rel;
2ff057
2ff057
    
2ff057
    /* bump q successor count */
2ff057
    tsi_q->tsi_qcnt++;
2ff057
2ff057
    rel = xcalloc(1, sizeof(*rel));
2ff057
    rel->rel_suc = tsi_q;
2ff057
    rel->rel_flags = flags;
2ff057
2ff057
    rel->rel_next = tsi_p->tsi_forward_relations;
2ff057
    tsi_p->tsi_forward_relations = rel;
2ff057
2ff057
    return 0;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Record next "q <- p" relation (i.e. "p" requires "q").
2ff057
 * @param ts		transaction set
2ff057
 * @param al		packages list
2ff057
 * @param p		predecessor (i.e. package that "Requires: q")
2ff057
 * @param requires	relation
2ff057
 * @return		0 always
2ff057
 */
2ff057
static inline int addRelation(rpmts ts,
2ff057
			      rpmal al,
2ff057
			      rpmte p,
2ff057
			      rpmds requires,
2ff057
			      int reversed)
2ff057
{
2ff057
    rpmte q;
2ff057
    rpmsenseFlags dsflags;
2ff057
2ff057
    dsflags = rpmdsFlags(requires);
2ff057
2ff057
    /* Avoid dependendencies which are not relevant for ordering */
2ff057
    if (dsflags & (RPMSENSE_RPMLIB|RPMSENSE_CONFIG|RPMSENSE_PRETRANS|RPMSENSE_POSTTRANS))
2ff057
	return 0;
2ff057
2ff057
    if (rpmdsIsRich(requires)) {
2ff057
	rpmds ds1, ds2;
2ff057
	rpmrichOp op;
2ff057
	if (rpmdsParseRichDep(requires, &ds1, &ds2, &op, NULL) == RPMRC_OK) {
2ff057
	    if (op != RPMRICHOP_ELSE)
2ff057
		addRelation(ts, al, p, ds1, reversed);
2ff057
	    if (op == RPMRICHOP_IF || op == RPMRICHOP_UNLESS) {
2ff057
	      rpmds ds21, ds22;
2ff057
	      rpmrichOp op2;
2ff057
	      if (rpmdsParseRichDep(requires, &ds21, &ds22, &op2, NULL) == RPMRC_OK && op2 == RPMRICHOP_ELSE) {
2ff057
		  addRelation(ts, al, p, ds22, reversed);
2ff057
	      }
2ff057
	      ds21 = rpmdsFree(ds21);
2ff057
	      ds22 = rpmdsFree(ds22);
2ff057
	    }
2ff057
	    if (op == RPMRICHOP_AND || op == RPMRICHOP_OR)
2ff057
		addRelation(ts, al, p, ds2, reversed);
2ff057
	    ds1 = rpmdsFree(ds1);
2ff057
	    ds2 = rpmdsFree(ds2);
2ff057
	}
2ff057
	return 0;
2ff057
    }
2ff057
    q = rpmalSatisfiesDepend(al, p, requires);
2ff057
2ff057
    /* Avoid deps outside this transaction and self dependencies */
2ff057
    if (q == NULL || q == p)
2ff057
	return 0;
2ff057
2ff057
    addSingleRelation(p, q, dsflags, reversed);
2ff057
2ff057
    return 0;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Add element to list sorting by tsi_qcnt.
2ff057
 * @param p		new element
2ff057
 * @retval qp		address of first element
2ff057
 * @retval rp		address of last element
2ff057
 * @param prefcolor
2ff057
 */
2ff057
static void addQ(tsortInfo p, tsortInfo * qp, tsortInfo * rp,
2ff057
		rpm_color_t prefcolor)
2ff057
{
2ff057
    tsortInfo q, qprev;
2ff057
    rpm_color_t pcolor = rpmteColor(p->te);
2ff057
    int tailcond;
2ff057
2ff057
    /* Mark the package as queued. */
2ff057
    p->tsi_reqx = 1;
2ff057
2ff057
    if ((*rp) == NULL) {	/* 1st element */
2ff057
	/* FIX: double indirection */
2ff057
	(*rp) = (*qp) = p;
2ff057
	return;
2ff057
    }
2ff057
2ff057
    if (rpmteType(p->te) == TR_ADDED)
2ff057
	tailcond = (pcolor && pcolor != prefcolor);
2ff057
    else
2ff057
	tailcond = (pcolor && pcolor == prefcolor);
2ff057
2ff057
    /* Find location in queue using metric tsi_qcnt and color. */
2ff057
    for (qprev = NULL, q = (*qp);
2ff057
	 q != NULL;
2ff057
	 qprev = q, q = q->tsi_suc)
2ff057
    {
2ff057
	/* Place preferred color towards queue head on install, tail on erase */
2ff057
	if (tailcond && (pcolor != rpmteColor(q->te)))
2ff057
	    continue;
2ff057
2ff057
	if (q->tsi_qcnt <= p->tsi_qcnt)
2ff057
	    break;
2ff057
    }
2ff057
2ff057
    if (qprev == NULL) {	/* insert at beginning of list */
2ff057
	p->tsi_suc = q;
2ff057
	(*qp) = p;		/* new head */
2ff057
    } else if (q == NULL) {	/* insert at end of list */
2ff057
	qprev->tsi_suc = p;
2ff057
	(*rp) = p;		/* new tail */
2ff057
    } else {			/* insert between qprev and q */
2ff057
	p->tsi_suc = q;
2ff057
	qprev->tsi_suc = p;
2ff057
    }
2ff057
}
2ff057
2ff057
typedef struct sccData_s {
2ff057
    int index;			/* DFS node number counter */
2ff057
    tsortInfo *stack;		/* Stack of nodes */
2ff057
    int stackcnt;		/* Stack top counter */
2ff057
    scc SCCs;			/* Array of SCC's found */
2ff057
    int sccCnt;			/* Number of SCC's found */
2ff057
} * sccData;
2ff057
2ff057
static void tarjan(sccData sd, tsortInfo tsi)
2ff057
{
2ff057
    tsortInfo tsi_q;
2ff057
    relation rel;
2ff057
2ff057
    /* use negative index numbers */
2ff057
    sd->index--;
2ff057
    /* Set the depth index for p */
2ff057
    tsi->tsi_SccIdx = sd->index;
2ff057
    tsi->tsi_SccLowlink = sd->index;
2ff057
2ff057
    sd->stack[sd->stackcnt++] = tsi;                   /* Push p on the stack */
2ff057
    for (rel=tsi->tsi_relations; rel != NULL; rel=rel->rel_next) {
2ff057
	/* Consider successors of p */
2ff057
	tsi_q = rel->rel_suc;
2ff057
	if (tsi_q->tsi_SccIdx > 0)
2ff057
	    /* Ignore already found SCCs */
2ff057
	    continue;
2ff057
	if (tsi_q->tsi_SccIdx == 0){
2ff057
	    /* Was successor q not yet visited? */
2ff057
	    tarjan(sd, tsi_q);                       /* Recurse */
2ff057
	    /* negative index numers: use max as it is closer to 0 */
2ff057
	    tsi->tsi_SccLowlink = (
2ff057
		tsi->tsi_SccLowlink > tsi_q->tsi_SccLowlink
2ff057
		? tsi->tsi_SccLowlink : tsi_q->tsi_SccLowlink);
2ff057
	} else {
2ff057
	    tsi->tsi_SccLowlink = (
2ff057
		tsi->tsi_SccLowlink > tsi_q->tsi_SccIdx
2ff057
		? tsi->tsi_SccLowlink : tsi_q->tsi_SccIdx);
2ff057
	}
2ff057
    }
2ff057
2ff057
    if (tsi->tsi_SccLowlink == tsi->tsi_SccIdx) {
2ff057
	/* v is the root of an SCC? */
2ff057
	if (sd->stack[sd->stackcnt-1] == tsi) {
2ff057
	    /* ignore trivial SCCs */
2ff057
	    tsi_q = sd->stack[--sd->stackcnt];
2ff057
	    tsi_q->tsi_SccIdx = 1;
2ff057
	} else {
2ff057
	    int stackIdx = sd->stackcnt;
2ff057
	    do {
2ff057
		tsi_q = sd->stack[--stackIdx];
2ff057
		tsi_q->tsi_SccIdx = sd->sccCnt;
2ff057
	    } while (tsi_q != tsi);
2ff057
2ff057
	    stackIdx = sd->stackcnt;
2ff057
	    do {
2ff057
		tsi_q = sd->stack[--stackIdx];
2ff057
		/* Calculate count for the SCC */
2ff057
		sd->SCCs[sd->sccCnt].count += tsi_q->tsi_count;
2ff057
		/* Subtract internal relations */
2ff057
		for (rel=tsi_q->tsi_relations; rel != NULL;
2ff057
						    rel=rel->rel_next) {
2ff057
		    if (rel->rel_suc != tsi_q &&
2ff057
			    rel->rel_suc->tsi_SccIdx == sd->sccCnt)
2ff057
			sd->SCCs[sd->sccCnt].count--;
2ff057
		}
2ff057
	    } while (tsi_q != tsi);
2ff057
	    sd->SCCs[sd->sccCnt].size = sd->stackcnt - stackIdx;
2ff057
	    /* copy members */
2ff057
	    sd->SCCs[sd->sccCnt].members = xcalloc(sd->SCCs[sd->sccCnt].size,
2ff057
					   sizeof(tsortInfo));
2ff057
	    memcpy(sd->SCCs[sd->sccCnt].members, sd->stack + stackIdx,
2ff057
		   sd->SCCs[sd->sccCnt].size * sizeof(tsortInfo));
2ff057
	    sd->stackcnt = stackIdx;
2ff057
	    sd->sccCnt++;
2ff057
	}
2ff057
    }
2ff057
}
2ff057
2ff057
/* Search for SCCs and return an array last entry has a .size of 0 */
2ff057
static scc detectSCCs(tsortInfo orderInfo, int nelem, int debugloops)
2ff057
{
2ff057
    /* Set up data structures needed for the tarjan algorithm */
2ff057
    scc SCCs = xcalloc(nelem+3, sizeof(*SCCs));
2ff057
    tsortInfo *stack = xcalloc(nelem, sizeof(*stack));
2ff057
    struct sccData_s sd = { 0, stack, 0, SCCs, 2 };
2ff057
2ff057
    for (int i = 0; i < nelem; i++) {
2ff057
	tsortInfo tsi = &orderInfo[i];
2ff057
	/* Start a DFS at each node */
2ff057
	if (tsi->tsi_SccIdx == 0)
2ff057
	    tarjan(&sd, tsi);
2ff057
    }
2ff057
2ff057
    free(stack);
2ff057
2ff057
    SCCs = xrealloc(SCCs, (sd.sccCnt+1)*sizeof(struct scc_s));
2ff057
2ff057
    /* Debug output */
2ff057
    if (sd.sccCnt > 2) {
2ff057
	int msglvl = debugloops ?  RPMLOG_WARNING : RPMLOG_DEBUG;
2ff057
	rpmlog(msglvl, "%i Strongly Connected Components\n", sd.sccCnt-2);
2ff057
	for (int i = 2; i < sd.sccCnt; i++) {
2ff057
	    rpmlog(msglvl, "SCC #%i: %i members (%i external dependencies)\n",
2ff057
			   i-1, SCCs[i].size, SCCs[i].count);
2ff057
2ff057
	    /* loop over members */
2ff057
	    for (int j = 0; j < SCCs[i].size; j++) {
2ff057
		tsortInfo member = SCCs[i].members[j];
2ff057
		rpmlog(msglvl, "\t%s\n", rpmteNEVRA(member->te));
2ff057
		/* show relations between members */
2ff057
		relation rel = member->tsi_forward_relations;
2ff057
		for (; rel != NULL; rel=rel->rel_next) {
2ff057
		    if (rel->rel_suc->tsi_SccIdx!=i) continue;
2ff057
		    rpmlog(msglvl, "\t\t%s %s\n",
2ff057
			   rel->rel_flags ? "=>" : "->",
2ff057
			   rpmteNEVRA(rel->rel_suc->te));
2ff057
		}
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
    return SCCs;
2ff057
}
2ff057
2ff057
static void collectTE(rpm_color_t prefcolor, tsortInfo q,
2ff057
		      rpmte * newOrder, int * newOrderCount,
2ff057
		      scc SCCs,
2ff057
		      tsortInfo * queue_end,
2ff057
		      tsortInfo * outer_queue,
2ff057
		      tsortInfo * outer_queue_end)
2ff057
{
2ff057
    char deptypechar = (rpmteType(q->te) == TR_REMOVED ? '-' : '+');
2ff057
2ff057
    if (rpmIsDebug()) {
2ff057
	int depth = 1;
2ff057
	/* figure depth in tree for nice formatting */
2ff057
	for (rpmte p = q->te; (p = rpmteParent(p)); depth++) {}
2ff057
	rpmlog(RPMLOG_DEBUG, "%5d%5d%5d%5d %*s%c%s\n",
2ff057
	       *newOrderCount, q->tsi_count, q->tsi_qcnt,
2ff057
	       depth, (2 * depth), "",
2ff057
	       deptypechar, rpmteNEVRA(q->te));
2ff057
    }
2ff057
2ff057
    newOrder[*newOrderCount] = q->te;
2ff057
    (*newOrderCount)++;
2ff057
2ff057
    /* T6. Erase relations. */
2ff057
    for (relation rel = q->tsi_relations; rel != NULL; rel = rel->rel_next) {
2ff057
	tsortInfo p = rel->rel_suc;
2ff057
	/* ignore already collected packages */
2ff057
	if (p->tsi_SccIdx == 0) continue;
2ff057
	if (p == q) continue;
2ff057
2ff057
	if (p && (--p->tsi_count) == 0) {
2ff057
	    (void) rpmteSetParent(p->te, q->te);
2ff057
2ff057
	    if (q->tsi_SccIdx > 1 && q->tsi_SccIdx != p->tsi_SccIdx) {
2ff057
                /* Relation point outside of this SCC: add to outside queue */
2ff057
		assert(outer_queue != NULL && outer_queue_end != NULL);
2ff057
		addQ(p, outer_queue, outer_queue_end, prefcolor);
2ff057
	    } else {
2ff057
		addQ(p, &q->tsi_suc, queue_end, prefcolor);
2ff057
	    }
2ff057
	}
2ff057
	if (p && p->tsi_SccIdx > 1 &&
2ff057
	                 p->tsi_SccIdx != q->tsi_SccIdx) {
2ff057
	    if (--SCCs[p->tsi_SccIdx].count == 0) {
2ff057
		/* New SCC is ready, add this package as representative */
2ff057
		(void) rpmteSetParent(p->te, q->te);
2ff057
2ff057
		if (outer_queue != NULL) {
2ff057
		    addQ(p, outer_queue, outer_queue_end, prefcolor);
2ff057
		} else {
2ff057
		    addQ(p, &q->tsi_suc, queue_end, prefcolor);
2ff057
		}
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
    q->tsi_SccIdx = 0;
2ff057
}
2ff057
2ff057
static void dijkstra(const struct scc_s *SCC, int sccNr)
2ff057
{
2ff057
    int start, end;
2ff057
    relation rel;
2ff057
2ff057
    /* can use a simple queue as edge weights are always 1 */
2ff057
    tsortInfo * queue = xmalloc((SCC->size+1) * sizeof(*queue));
2ff057
2ff057
    /*
2ff057
     * Find packages that are prerequired and use them as
2ff057
     * starting points for the Dijkstra algorithm
2ff057
     */
2ff057
    start = end = 0;
2ff057
    for (int i = 0; i < SCC->size; i++) {
2ff057
	tsortInfo tsi = SCC->members[i];
2ff057
	tsi->tsi_SccLowlink = INT_MAX;
2ff057
	for (rel=tsi->tsi_forward_relations; rel != NULL; rel=rel->rel_next) {
2ff057
	    if (rel->rel_flags && rel->rel_suc->tsi_SccIdx == sccNr) {
2ff057
		if (rel->rel_suc != tsi) {
2ff057
		    tsi->tsi_SccLowlink =  0;
2ff057
		    queue[end++] = tsi;
2ff057
		} else {
2ff057
		    tsi->tsi_SccLowlink =  INT_MAX/2;
2ff057
		}
2ff057
		break;
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
2ff057
    if (start == end) { /* no regular prereqs; add self prereqs to queue */
2ff057
	for (int i = 0; i < SCC->size; i++) {
2ff057
	    tsortInfo tsi = SCC->members[i];
2ff057
	    if (tsi->tsi_SccLowlink != INT_MAX) {
2ff057
		queue[end++] = tsi;
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
2ff057
    /* Do Dijkstra */
2ff057
    while (start != end) {
2ff057
	tsortInfo tsi = queue[start++];
2ff057
	for (rel=tsi->tsi_forward_relations; rel != NULL; rel=rel->rel_next) {
2ff057
	    tsortInfo next_tsi = rel->rel_suc;
2ff057
	    if (next_tsi->tsi_SccIdx != sccNr) continue;
2ff057
	    if (next_tsi->tsi_SccLowlink > tsi->tsi_SccLowlink+1) {
2ff057
		next_tsi->tsi_SccLowlink = tsi->tsi_SccLowlink + 1;
2ff057
		queue[end++] = rel->rel_suc;
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
    free(queue);
2ff057
}
2ff057
2ff057
static void collectSCC(rpm_color_t prefcolor, tsortInfo p_tsi,
2ff057
		       rpmte * newOrder, int * newOrderCount,
2ff057
		       scc SCCs, tsortInfo * queue_end)
2ff057
{
2ff057
    int sccNr = p_tsi->tsi_SccIdx;
2ff057
    const struct scc_s * SCC = SCCs+sccNr;
2ff057
2ff057
    /* remove p from the outer queue */
2ff057
    tsortInfo outer_queue_start = p_tsi->tsi_suc;
2ff057
    p_tsi->tsi_suc = NULL;
2ff057
2ff057
    /*
2ff057
     * Run a multi source Dijkstra's algorithm to find relations
2ff057
     * that can be zapped with least danger to pre reqs.
2ff057
     * As weight of the edges is always 1 it is not necessary to
2ff057
     * sort the vertices by distance as the queue gets them
2ff057
     * already in order
2ff057
    */
2ff057
    dijkstra(SCC, sccNr);
2ff057
2ff057
    while (1) {
2ff057
	tsortInfo best = NULL;
2ff057
	tsortInfo inner_queue_start, inner_queue_end;
2ff057
	int best_score = 0;
2ff057
2ff057
	/* select best candidate to start with */
2ff057
	for (int i = 0; i < SCC->size; i++) {
2ff057
	    tsortInfo tsi = SCC->members[i];
2ff057
	    if (tsi->tsi_SccIdx == 0) /* package already collected */
2ff057
		continue;
2ff057
	    if (tsi->tsi_SccLowlink >= best_score) {
2ff057
		best = tsi;
2ff057
		best_score = tsi->tsi_SccLowlink;
2ff057
	    }
2ff057
	}
2ff057
2ff057
	if (best == NULL) /* done */
2ff057
	    break;
2ff057
2ff057
	/* collect best candidate and all packages that get freed */
2ff057
	inner_queue_start = inner_queue_end = NULL;
2ff057
	addQ(best, &inner_queue_start, &inner_queue_end, prefcolor);
2ff057
2ff057
	for (; inner_queue_start != NULL;
2ff057
	     inner_queue_start = inner_queue_start->tsi_suc) {
2ff057
	    /* Mark the package as unqueued. */
2ff057
	    inner_queue_start->tsi_reqx = 0;
2ff057
	    collectTE(prefcolor, inner_queue_start, newOrder, newOrderCount,
2ff057
		      SCCs, &inner_queue_end, &outer_queue_start, queue_end);
2ff057
	}
2ff057
    }
2ff057
2ff057
    /* restore outer queue */
2ff057
    p_tsi->tsi_suc = outer_queue_start;
2ff057
}
2ff057
2ff057
int rpmtsOrder(rpmts ts)
2ff057
{
2ff057
    tsMembers tsmem = rpmtsMembers(ts);
2ff057
    rpm_color_t prefcolor = rpmtsPrefColor(ts);
2ff057
    rpmtsi pi; rpmte p;
2ff057
    tsortInfo q, r;
2ff057
    rpmte * newOrder;
2ff057
    int newOrderCount = 0;
2ff057
    int rc;
2ff057
    rpmal erasedPackages;
2ff057
    scc SCCs;
2ff057
    int nelem = rpmtsNElements(ts);
2ff057
    tsortInfo sortInfo = xcalloc(nelem, sizeof(struct tsortInfo_s));
2ff057
2ff057
    (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_ORDER), 0);
2ff057
2ff057
    /* Create erased package index. */
2ff057
    erasedPackages = rpmtsCreateAl(ts, TR_REMOVED);
2ff057
2ff057
    for (int i = 0; i < nelem; i++) {
2ff057
	sortInfo[i].te = tsmem->order[i];
2ff057
	rpmteSetTSI(tsmem->order[i], &sortInfo[i]);
2ff057
    }
2ff057
2ff057
    /* Record relations. */
2ff057
    rpmlog(RPMLOG_DEBUG, "========== recording tsort relations\n");
2ff057
    pi = rpmtsiInit(ts);
2ff057
    while ((p = rpmtsiNext(pi, 0)) != NULL) {
2ff057
	rpmal al = (rpmteType(p) == TR_REMOVED) ? 
2ff057
		   erasedPackages : tsmem->addedPackages;
2ff057
	rpmds requires = rpmdsInit(rpmteDS(p, RPMTAG_REQUIRENAME));
2ff057
	rpmds recommends = rpmdsInit(rpmteDS(p, RPMTAG_RECOMMENDNAME));
2ff057
	rpmds suggests = rpmdsInit(rpmteDS(p, RPMTAG_SUGGESTNAME));
2ff057
	rpmds supplements = rpmdsInit(rpmteDS(p, RPMTAG_SUPPLEMENTNAME));
2ff057
	rpmds enhances = rpmdsInit(rpmteDS(p, RPMTAG_ENHANCENAME));
2ff057
	rpmds order = rpmdsInit(rpmteDS(p, RPMTAG_ORDERNAME));
2ff057
2ff057
	while (rpmdsNext(requires) >= 0) {
2ff057
	    /* Record next "q <- p" relation (i.e. "p" requires "q"). */
2ff057
	    (void) addRelation(ts, al, p, requires, 0);
2ff057
	}
2ff057
2ff057
	while (rpmdsNext(recommends) >= 0) {
2ff057
	    /* Record next "q <- p" relation (i.e. "p" recommends "q"). */
2ff057
	    (void) addRelation(ts, al, p, recommends, 0);
2ff057
	}
2ff057
2ff057
	while (rpmdsNext(suggests) >= 0) {
2ff057
	    /* Record next "q <- p" relation (i.e. "p" suggests "q"). */
2ff057
	    (void) addRelation(ts, al, p, suggests, 0);
2ff057
	}
2ff057
2ff057
	while (rpmdsNext(order) >= 0) {
2ff057
	    /* Record next "q <- p" ordering request */
2ff057
	    (void) addRelation(ts, al, p, order, 0);
2ff057
	}
2ff057
2ff057
	while (rpmdsNext(supplements) >= 0) {
2ff057
	    /* Record next "p -> q" relation (i.e. "q" supplemented by "p"). */
2ff057
	    (void) addRelation(ts, al, p, supplements, 1);
2ff057
	}
2ff057
2ff057
	while (rpmdsNext(enhances) >= 0) {
2ff057
	    /* Record next "p <- q" relation (i.e. "q" is enhanced by  "p"). */
2ff057
	    (void) addRelation(ts, al, p, enhances, 1);
2ff057
	}
2ff057
    }
2ff057
2ff057
    rpmtsiFree(pi);
2ff057
2ff057
    newOrder = xcalloc(tsmem->orderCount, sizeof(*newOrder));
2ff057
    SCCs = detectSCCs(sortInfo, nelem, (rpmtsFlags(ts) & RPMTRANS_FLAG_DEPLOOPS));
2ff057
2ff057
    rpmlog(RPMLOG_DEBUG, "========== tsorting packages (order, #predecessors, #succesors, depth)\n");
2ff057
2ff057
    for (int i = 0; i < 2; i++) {
2ff057
	/* Do two separate runs: installs first - then erases */
2ff057
	int oType = !i ? TR_ADDED : TR_REMOVED;
2ff057
	q = r = NULL;
2ff057
	/* Scan for zeroes and add them to the queue */
2ff057
	for (int e = 0; e < nelem; e++) {
2ff057
	    tsortInfo p = &sortInfo[e];
2ff057
	    if (rpmteType(p->te) != oType) continue;
2ff057
	    if (p->tsi_count != 0)
2ff057
		continue;
2ff057
	    p->tsi_suc = NULL;
2ff057
	    addQ(p, &q, &r, prefcolor);
2ff057
	}
2ff057
2ff057
	/* Add one member of each leaf SCC */
2ff057
	for (int i = 2; SCCs[i].members != NULL; i++) {
2ff057
	    tsortInfo member = SCCs[i].members[0];
2ff057
	    if (SCCs[i].count == 0 && rpmteType(member->te) == oType) {
2ff057
		addQ(member, &q, &r, prefcolor);
2ff057
	    }
2ff057
	}
2ff057
2ff057
	while (q != NULL) {
2ff057
	    /* Mark the package as unqueued. */
2ff057
	    q->tsi_reqx = 0;
2ff057
	    if (q->tsi_SccIdx > 1) {
2ff057
		collectSCC(prefcolor, q, newOrder, &newOrderCount, SCCs, &r);
2ff057
	    } else {
2ff057
		collectTE(prefcolor, q, newOrder, &newOrderCount, SCCs, &r,
2ff057
			  NULL, NULL);
2ff057
	    }
2ff057
	    q = q->tsi_suc;
2ff057
	}
2ff057
    }
2ff057
2ff057
    /* Clean up tsort data */
2ff057
    for (int i = 0; i < nelem; i++) {
2ff057
	rpmteSetTSI(tsmem->order[i], NULL);
2ff057
	rpmTSIFree(&sortInfo[i]);
2ff057
    }
2ff057
    free(sortInfo);
2ff057
2ff057
    assert(newOrderCount == tsmem->orderCount);
2ff057
2ff057
    tsmem->order = _free(tsmem->order);
2ff057
    tsmem->order = newOrder;
2ff057
    tsmem->orderAlloced = tsmem->orderCount;
2ff057
    rc = 0;
2ff057
2ff057
    for (int i = 2; SCCs[i].members != NULL; i++) {
2ff057
	free(SCCs[i].members);
2ff057
    }
2ff057
    free(SCCs);
2ff057
    rpmalFree(erasedPackages);
2ff057
2ff057
    (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_ORDER), 0);
2ff057
2ff057
    return rc;
2ff057
}