2009-10-04 16:30:36 +04:00
# 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 ) ) {
2009-10-05 20:36:03 +04:00
/* 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 ) . */
2009-10-04 16:30:36 +04:00
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 1 st ordered list entry
* @ param two 2 nd 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. */
2009-10-05 20:36:03 +04:00
/* First, do pre-requisites.
* This is required for selected [ ] optimization to work properly . */
2009-10-04 16:30:36 +04:00
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 ;
}
2010-03-16 15:05:18 +03:00
rpmMessage ( RPMMESS_ERROR , _ ( " ordering failed, %d elements remain \n " ) , loopcheck ) ;
2009-10-04 16:30:36 +04:00
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 ;
}