rpm-build/lib/order.c

525 lines
14 KiB
C

#include "system.h"
#include "rpmlib.h"
#include "debug.h"
#include "rpmmacro.h" /* XXX for rpmExpand() */
#include "depends.h"
#include "al.h"
/**
* Recursively mark all nodes with their predecessors.
* @param tsi successor chain
* @param q predecessor
*/
static void markLoop(/*@special@*/ tsortInfo tsi,
struct availablePackage * q)
/*@uses tsi @*/
/*@modifies internalState @*/
{
struct availablePackage * p;
while (tsi != NULL && (p = tsi->tsi_suc) != NULL) {
tsi = tsi->tsi_next;
if (p->tsi.tsi_pkg != NULL)
continue;
p->tsi.tsi_pkg = q;
if (p->tsi.tsi_next != NULL)
markLoop(p->tsi.tsi_next, p);
}
}
static inline /*@observer@*/ const char * identifyDepend(int_32 f)
{
if (isLegacyPreReq(f))
return "PreReq:";
f = _notpre(f);
if (f & RPMSENSE_SCRIPT_PRE)
return "Requires(pre):";
if (f & RPMSENSE_SCRIPT_POST)
return "Requires(post):";
if (f & RPMSENSE_SCRIPT_PREUN)
return "Requires(preun):";
if (f & RPMSENSE_SCRIPT_POSTUN)
return "Requires(postun):";
if (f & RPMSENSE_SCRIPT_VERIFY)
return "Requires(verify):";
return "Requires:";
}
/**
* Find (and eliminate co-requisites) "q <- p" relation in dependency loop.
* Search all successors of q for instance of p. Format the specific relation,
* (e.g. p contains "Requires: q"). Unlink and free co-requisite (i.e.
* pure Requires: dependencies) successor node(s).
* @param q sucessor (i.e. package required by p)
* @param p predecessor (i.e. package that "Requires: q")
* @param zap max. no. of co-requisites to remove (-1 is all)?
* @retval nzaps address of no. of relations removed
* @return (possibly NULL) formatted "q <- p" releation (malloc'ed)
*/
static /*@owned@*/ /*@null@*/ const char *
zapRelation(struct availablePackage * q, struct availablePackage * p,
int zap, /*@in@*/ /*@out@*/ int * nzaps)
/*@modifies q, p, *nzaps @*/
{
tsortInfo tsi_prev;
tsortInfo tsi;
const char *dp = NULL;
for (tsi_prev = &q->tsi, tsi = q->tsi.tsi_next;
tsi != NULL;
/* XXX Note: the loop traverses "not found", break on "found". */
/*@-nullderef@*/
tsi_prev = tsi, tsi = tsi->tsi_next)
/*@=nullderef@*/
{
int j;
if (tsi->tsi_suc != p)
continue;
if (p->requires == NULL) continue; /* XXX can't happen */
if (p->requireFlags == NULL) continue; /* XXX can't happen */
if (p->requiresEVR == NULL) continue; /* XXX can't happen */
j = tsi->tsi_reqx;
dp = printDepend( identifyDepend(p->requireFlags[j]),
p->requires[j], p->requiresEVR[j], p->requireFlags[j]);
/*
* Attempt to unravel a dependency loop by eliminating Requires's.
*/
if (zap && !(p->requireFlags[j] & RPMSENSE_PREREQ)) {
/* The above RPMSENSE_PREREQ test works properly only because,
* on one hand, rpmbuild marks all scriptlet-like dependencies
* with RPMSENSE_PREREQ (see rpmsenseFlags_e in rpmlib.h);
* and, on the other, since only %pre/%post dependencies are added
* for installed packages, but not %preun/%postun (see below). */
rpmMessage(RPMMESS_DEBUG,
_("removing %s-%s-%s \"%s\" from tsort relations.\n"),
p->name, p->version, p->release, dp);
p->tsi.tsi_count--;
if (tsi_prev) tsi_prev->tsi_next = tsi->tsi_next;
tsi->tsi_next = NULL;
tsi->tsi_suc = NULL;
tsi = _free(tsi);
if (nzaps)
(*nzaps)++;
if (zap)
zap--;
}
/* XXX Note: the loop traverses "not found", get out now! */
break;
}
return dp;
}
/**
* Record next "q <- p" relation (i.e. "p" requires "q").
* @param ts transaction set
* @param p predecessor (i.e. package that "Requires: q")
* @param selected boolean package selected array
* @param j relation index
* @return 0 always
*/
static inline int addRelation( const rpmTransactionSet ts,
struct availablePackage * p, unsigned char * selected, int j)
/*@modifies p->tsi.tsi_u.count, p->depth, *selected @*/
{
struct availablePackage * q;
tsortInfo tsi;
int matchNum;
if (!p->requires || !p->requiresEVR || !p->requireFlags)
return 0;
q = alSatisfiesDepend(&ts->addedPackages,
p->requires[j], p->requiresEVR[j], p->requireFlags[j]);
/* Ordering depends only on added package relations. */
if (q == NULL)
return 0;
/* Avoid rpmlib feature dependencies. */
if (!strncmp(p->requires[j], "rpmlib(", sizeof("rpmlib(")-1))
return 0;
/* Avoid redundant relations. */
/* XXX TODO: add control bit. */
matchNum = q - ts->addedPackages.list;
if (selected[matchNum] != 0)
return 0;
selected[matchNum] = 1;
/* T3. Record next "q <- p" relation (i.e. "p" requires "q"). */
p->tsi.tsi_count++; /* bump p predecessor count */
if (p->depth <= q->depth) /* Save max. depth in dependency tree */
p->depth = q->depth + 1;
tsi = xmalloc(sizeof(*tsi));
tsi->tsi_suc = p;
tsi->tsi_reqx = j;
tsi->tsi_next = q->tsi.tsi_next;
q->tsi.tsi_next = tsi;
q->tsi.tsi_qcnt++; /* bump q successor count */
return 0;
}
/**
* Add element to list sorting by initial successor count.
* @param p new element
* @retval qp address of first element
* @retval rp address of last element
*/
static void addQ(struct availablePackage * p,
/*@in@*/ /*@out@*/ struct availablePackage ** qp,
/*@in@*/ /*@out@*/ struct availablePackage ** rp)
/*@modifies p->tsi, *qp, *rp @*/
{
struct availablePackage *q, *qprev;
/* Mark the package as queued. */
p->tsi.tsi_reqx = 1;
if ((*rp) == NULL) { /* 1st element */
(*rp) = (*qp) = p;
return;
}
for (qprev = NULL, q = (*qp); q != NULL; qprev = q, q = q->tsi.tsi_suc) {
if (q->tsi.tsi_qcnt <= p->tsi.tsi_qcnt)
break;
}
if (qprev == NULL) { /* insert at beginning of list */
p->tsi.tsi_suc = q;
(*qp) = p; /* new head */
} else if (q == NULL) { /* insert at end of list */
qprev->tsi.tsi_suc = p;
(*rp) = p; /* new tail */
} else { /* insert between qprev and q */
p->tsi.tsi_suc = q;
qprev->tsi.tsi_suc = p;
}
}
struct orderListIndex {
int alIndex;
int orIndex;
};
/**
* Compare ordered list entries by index (qsort/bsearch).
* @param one 1st ordered list entry
* @param two 2nd ordered list entry
* @return result of comparison
*/
static int orderListIndexCmp(const void * one, const void * two) /*@*/
{
int a = ((const struct orderListIndex *)one)->alIndex;
int b = ((const struct orderListIndex *)two)->alIndex;
return (a - b);
}
int rpmdepOrder(rpmTransactionSet ts)
{
int npkgs = ts->addedPackages.size;
int anaconda = 0;
struct availablePackage * p;
struct availablePackage * q;
struct availablePackage * r;
tsortInfo tsi;
tsortInfo tsi_next;
int * ordering = alloca(sizeof(*ordering) * (npkgs + 1));
int orderingCount = 0;
unsigned char * selected = alloca(sizeof(*selected) * (npkgs + 1));
int loopcheck;
transactionElement newOrder;
int newOrderCount = 0;
struct orderListIndex * orderList;
int _printed = 0;
int treex;
int depth;
int qlen;
int i, j;
/* T1. Initialize. */
loopcheck = npkgs;
/* Record all relations. */
rpmMessage(RPMMESS_DEBUG, _("========== recording tsort relations\n"));
if ((p = ts->addedPackages.list) != NULL)
for (i = 0; i < npkgs; i++, p++) {
int matchNum;
if (p->requiresCount <= 0)
continue;
memset(selected, 0, sizeof(*selected) * npkgs);
/* Avoid narcisstic relations. */
matchNum = p - ts->addedPackages.list;
selected[matchNum] = 1;
/* T2. Next "q <- p" relation. */
/* First, do pre-requisites.
* This is required for selected[] optimization to work properly. */
for (j = 0; j < p->requiresCount; j++) {
if (p->requireFlags == NULL) continue; /* XXX can't happen */
/* Skip if not %pre/%post requires or legacy prereq. */
if (!( isInstallPreReq(p->requireFlags[j]) ||
isLegacyPreReq(p->requireFlags[j]) ))
continue;
/* T3. Record next "q <- p" relation (i.e. "p" requires "q"). */
(void) addRelation(ts, p, selected, j);
}
/* Then do co-requisites. */
for (j = 0; j < p->requiresCount; j++) {
if (p->requireFlags == NULL) continue; /* XXX can't happen */
/* Skip if %pre/%post/%preun/%postun requires or legacy prereq. */
if (isErasePreReq(p->requireFlags[j]) ||
( isInstallPreReq(p->requireFlags[j]) ||
isLegacyPreReq(p->requireFlags[j]) ))
continue;
/* T3. Record next "q <- p" relation (i.e. "p" requires "q"). */
(void) addRelation(ts, p, selected, j);
}
}
/* Save predecessor count and mark tree roots. */
treex = 0;
if ((p = ts->addedPackages.list) != NULL)
for (i = 0; i < npkgs; i++, p++) {
p->npreds = p->tsi.tsi_count;
p->tree = (p->npreds == 0 ? treex++ : -1);
}
/* T4. Scan for zeroes. */
rpmMessage(RPMMESS_DEBUG, _("========== tsorting packages (order, #predecessors, #succesors, tree, depth)\n"));
rescan:
q = r = NULL;
qlen = 0;
if ((p = ts->addedPackages.list) != NULL)
for (i = 0; i < npkgs; i++, p++) {
/* Prefer packages in chainsaw or anaconda presentation order. */
if (anaconda)
p->tsi.tsi_qcnt = (npkgs - i);
if (p->tsi.tsi_count != 0)
continue;
p->tsi.tsi_suc = NULL;
addQ(p, &q, &r);
qlen++;
}
/* T5. Output front of queue (T7. Remove from queue.) */
for (; q != NULL; q = q->tsi.tsi_suc) {
/* Mark the package as unqueued. */
q->tsi.tsi_reqx = 0;
rpmMessage(RPMMESS_DEBUG, "%5d%5d%5d%5d%5d %*s %s-%s-%s\n",
orderingCount, q->npreds, q->tsi.tsi_qcnt,
q->tree, q->depth,
2*q->depth, "",
q->name, q->version, q->release);
treex = q->tree;
depth = q->depth;
q->degree = 0;
ordering[orderingCount++] = q - ts->addedPackages.list;
qlen--;
loopcheck--;
/* T6. Erase relations. */
tsi_next = q->tsi.tsi_next;
q->tsi.tsi_next = NULL;
while ((tsi = tsi_next) != NULL) {
tsi_next = tsi->tsi_next;
tsi->tsi_next = NULL;
p = tsi->tsi_suc;
if (p && (--p->tsi.tsi_count) <= 0) {
p->tree = treex;
p->depth = depth + 1;
p->parent = q;
q->degree++;
/* XXX TODO: add control bit. */
p->tsi.tsi_suc = NULL;
/*@-nullstate@*/ /* FIX: q->tsi.tsi_u.suc may be NULL */
addQ(p, &q->tsi.tsi_suc, &r);
/*@=nullstate@*/
qlen++;
}
tsi = _free(tsi);
}
if (!_printed && loopcheck == qlen && q->tsi.tsi_suc != NULL) {
_printed++;
rpmMessage(RPMMESS_DEBUG,
_("========== successors only (presentation order)\n"));
/* Relink the queue in presentation order. */
tsi = &q->tsi;
if ((p = ts->addedPackages.list) != NULL)
for (i = 0; i < npkgs; i++, p++) {
/* Is this element in the queue? */
if (p->tsi.tsi_reqx == 0)
/*@innercontinue@*/ continue;
tsi->tsi_suc = p;
tsi = &p->tsi;
}
tsi->tsi_suc = NULL;
}
}
/* T8. End of process. Check for loops. */
if (loopcheck != 0) {
int nzaps;
/* T9. Initialize predecessor chain. */
nzaps = 0;
if ((q = ts->addedPackages.list) != NULL)
for (i = 0; i < npkgs; i++, q++) {
q->tsi.tsi_pkg = NULL;
q->tsi.tsi_reqx = 0;
/* Mark packages already sorted. */
if (q->tsi.tsi_count == 0)
q->tsi.tsi_count = -1;
}
/* T10. Mark all packages with their predecessors. */
if ((q = ts->addedPackages.list) != NULL)
for (i = 0; i < npkgs; i++, q++) {
if ((tsi = q->tsi.tsi_next) == NULL)
continue;
q->tsi.tsi_next = NULL;
markLoop(tsi, q);
q->tsi.tsi_next = tsi;
}
/* T11. Print all dependency loops. */
if ((r = ts->addedPackages.list) != NULL)
for (i = 0; i < npkgs; i++, r++) {
int printed;
printed = 0;
/* T12. Mark predecessor chain, looking for start of loop. */
for (q = r->tsi.tsi_pkg; q != NULL; q = q->tsi.tsi_pkg) {
if (q->tsi.tsi_reqx)
/*@innerbreak@*/ break;
q->tsi.tsi_reqx = 1;
}
/* T13. Print predecessor chain from start of loop. */
while ((p = q) != NULL && (q = p->tsi.tsi_pkg) != NULL) {
const char * dp;
char buf[4096];
/* Unchain predecessor loop. */
p->tsi.tsi_pkg = NULL;
if (!printed) {
rpmMessage(RPMMESS_DEBUG, _("LOOP:\n"));
printed = 1;
}
/* Find (and destroy if co-requisite) "q <- p" relation. */
dp = zapRelation(q, p, 1, &nzaps);
/* Print next member of loop. */
sprintf(buf, "%s-%s-%s", p->name, p->version, p->release);
rpmMessage(RPMMESS_DEBUG, " %-40s %s\n", buf,
(dp ? dp : "not found!?!"));
dp = _free(dp);
}
/* Walk (and erase) linear part of predecessor chain as well. */
for (p = r, q = r->tsi.tsi_pkg;
q != NULL;
p = q, q = q->tsi.tsi_pkg)
{
/* Unchain linear part of predecessor loop. */
p->tsi.tsi_pkg = NULL;
p->tsi.tsi_reqx = 0;
}
}
/* If a relation was eliminated, then continue sorting. */
/* XXX TODO: add control bit. */
if (nzaps) {
rpmMessage(RPMMESS_DEBUG, _("========== continuing tsort ...\n"));
goto rescan;
}
rpmMessage(RPMMESS_ERROR, _("ordering failed, %d elements remain\n"), loopcheck);
return 1;
}
/*
* The order ends up as installed packages followed by removed packages,
* with removes for upgrades immediately following the installation of
* the new package. This would be easier if we could sort the
* addedPackages array, but we store indexes into it in various places.
*/
orderList = xmalloc(npkgs * sizeof(*orderList));
for (i = 0, j = 0; i < ts->orderCount; i++) {
if (ts->order[i].type == TR_ADDED) {
orderList[j].alIndex = ts->order[i].u.addedIndex;
orderList[j].orIndex = i;
j++;
}
}
assert(j <= npkgs);
qsort(orderList, npkgs, sizeof(*orderList), orderListIndexCmp);
newOrder = xmalloc(ts->orderCount * sizeof(*newOrder));
for (i = 0, newOrderCount = 0; i < orderingCount; i++) {
struct orderListIndex * needle, key;
key.alIndex = ordering[i];
needle = bsearch(&key, orderList, npkgs, sizeof(key),orderListIndexCmp);
/* bsearch should never, ever fail */
if (needle == NULL) continue;
newOrder[newOrderCount++] = ts->order[needle->orIndex];
for (j = needle->orIndex + 1; j < ts->orderCount; j++) {
if (ts->order[j].type == TR_REMOVED &&
ts->order[j].u.removed.dependsOnIndex == needle->alIndex) {
newOrder[newOrderCount++] = ts->order[j];
} else
/*@innerbreak@*/ break;
}
}
for (i = 0; i < ts->orderCount; i++) {
if (ts->order[i].type == TR_REMOVED &&
ts->order[i].u.removed.dependsOnIndex == -1) {
newOrder[newOrderCount++] = ts->order[i];
}
}
assert(newOrderCount == ts->orderCount);
ts->order = _free(ts->order);
ts->order = newOrder;
orderList = _free(orderList);
return 0;
}