525 lines
14 KiB
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;
|
|
}
|