mirror of
https://github.com/boostorg/build.git
synced 2026-02-13 12:22:17 +00:00
Replace ad hoc (incorrect) cycle detection code with a variation of Tarjan's algorithm.
[SVN r78287]
This commit is contained in:
@@ -509,7 +509,14 @@ LIST * builtin_depends( FRAME * frame, int flags )
|
||||
for ( ; iter != end; iter = list_next( iter ) )
|
||||
{
|
||||
TARGET * s = bindtarget( list_item( iter ) );
|
||||
s->dependants = targetlist( s->dependants, targets );
|
||||
if ( flags )
|
||||
{
|
||||
LISTITER t_iter = list_begin( targets ), t_end = list_end( targets );
|
||||
for ( ; t_iter != t_end; t_iter = list_next( t_iter ) )
|
||||
s->dependants = targetentry( s->dependants, bindtarget( list_item( t_iter ) )->includes );
|
||||
}
|
||||
else
|
||||
s->dependants = targetlist( s->dependants, targets );
|
||||
}
|
||||
|
||||
return L0;
|
||||
|
||||
@@ -129,7 +129,7 @@ int make( LIST * targets, int anyhow )
|
||||
{
|
||||
TARGET * t = bindtarget( list_item( iter ) );
|
||||
if ( t->fate == T_FATE_INIT )
|
||||
make0( t, 0, 0, counts, anyhow );
|
||||
make0( t, 0, 0, counts, anyhow, 0 );
|
||||
}
|
||||
PROFILE_EXIT( MAKE_MAKE0 );
|
||||
}
|
||||
@@ -240,6 +240,48 @@ static void force_rebuilds( TARGET * t )
|
||||
}
|
||||
|
||||
|
||||
int make0rescan( TARGET * t, TARGET * rescanning )
|
||||
{
|
||||
int result = 0;
|
||||
TARGETS * c;
|
||||
/* Check whether we've already found a cycle. */
|
||||
if( target_scc( t ) == rescanning )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
/* If we've already visited this node, ignore it. */
|
||||
if ( t->rescanning == rescanning )
|
||||
return 0;
|
||||
|
||||
/* If t is already updated, ignore it. */
|
||||
if ( t->scc_root == NULL &&
|
||||
t->progress != T_MAKE_INIT &&
|
||||
t->progress != T_MAKE_ONSTACK &&
|
||||
t->progress != T_MAKE_ACTIVE )
|
||||
return 0;
|
||||
|
||||
t->rescanning = rescanning;
|
||||
for ( c = t->depends; c; c = c->next )
|
||||
{
|
||||
TARGET * dependency = c->target;
|
||||
/* Always start at the root of each new strongly connected component. */
|
||||
if ( target_scc( dependency ) != target_scc( t ) )
|
||||
dependency = target_scc( dependency );
|
||||
result |= make0rescan( dependency, rescanning );
|
||||
|
||||
/* Make sure that we pick up the new include node. */
|
||||
if ( c->target->includes == rescanning )
|
||||
result = 1;
|
||||
}
|
||||
if ( result && t->scc_root == NULL )
|
||||
{
|
||||
t->scc_root = rescanning;
|
||||
rescanning->depends = targetentry( rescanning->depends, t );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make0() - bind and scan everything to make a TARGET.
|
||||
*
|
||||
@@ -253,7 +295,8 @@ void make0
|
||||
TARGET * p, /* parent */
|
||||
int depth, /* for display purposes */
|
||||
COUNTS * counts, /* for reporting */
|
||||
int anyhow
|
||||
int anyhow,
|
||||
TARGET * rescanning
|
||||
) /* forcibly touch all (real) targets */
|
||||
{
|
||||
TARGETS * c;
|
||||
@@ -303,7 +346,11 @@ void make0
|
||||
* depending on us will depend on that other target as well.
|
||||
*/
|
||||
if ( another_target )
|
||||
t->depends = targetentry( t->depends, bindtarget( another_target ) );
|
||||
{
|
||||
TARGET * other = bindtarget( another_target );
|
||||
t->depends = targetentry( t->depends, other );
|
||||
other->dependants = targetentry( other->dependants, t );
|
||||
}
|
||||
|
||||
t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
|
||||
}
|
||||
@@ -380,14 +427,18 @@ void make0
|
||||
* other alot.
|
||||
*/
|
||||
if ( c->target->fate == T_FATE_INIT )
|
||||
make0( c->target, ptime, depth + 1, counts, anyhow );
|
||||
make0( c->target, ptime, depth + 1, counts, anyhow, rescanning );
|
||||
else if ( c->target->fate == T_FATE_MAKING && !internal )
|
||||
printf( "warning: %s depends on itself\n", object_str( c->target->name ) );
|
||||
else if ( c->target->fate != T_FATE_MAKING && rescanning )
|
||||
make0rescan( c->target, rescanning );
|
||||
if ( rescanning && c->target->includes && c->target->includes->fate != T_FATE_MAKING )
|
||||
make0rescan( target_scc( c->target->includes ), rescanning );
|
||||
}
|
||||
|
||||
/* Step 3b: recursively make0() internal includes node. */
|
||||
if ( t->includes )
|
||||
make0( t->includes, p, depth + 1, counts, anyhow );
|
||||
make0( t->includes, p, depth + 1, counts, anyhow, rescanning );
|
||||
|
||||
/* Step 3c: add dependencies' includes to our direct dependencies. */
|
||||
{
|
||||
|
||||
@@ -28,7 +28,7 @@ typedef struct {
|
||||
|
||||
|
||||
void make0( TARGET *t, TARGET *p, int depth,
|
||||
COUNTS *counts, int anyhow );
|
||||
COUNTS *counts, int anyhow, TARGET * rescanning );
|
||||
|
||||
|
||||
/*
|
||||
|
||||
@@ -87,8 +87,6 @@ static struct
|
||||
int made;
|
||||
} counts[ 1 ] ;
|
||||
|
||||
int handling_rescan;
|
||||
|
||||
/* Target state - remove recursive calls by just keeping track of state target
|
||||
* is in.
|
||||
*/
|
||||
@@ -101,8 +99,7 @@ typedef struct _state
|
||||
#define T_STATE_MAKE1ATAIL 1 /* make1atail() should be called */
|
||||
#define T_STATE_MAKE1B 2 /* make1b() should be called */
|
||||
#define T_STATE_MAKE1C 3 /* make1c() should be called */
|
||||
#define T_STATE_MAKE1CTAIL 4 /* make1ctail() should be called */
|
||||
#define T_STATE_MAKE1D 5 /* make1d() should be called */
|
||||
#define T_STATE_MAKE1D 4 /* make1d() should be called */
|
||||
int curstate; /* current state */
|
||||
int status;
|
||||
} state;
|
||||
@@ -111,7 +108,6 @@ static void make1a ( state * );
|
||||
static void make1atail ( state * );
|
||||
static void make1b ( state * );
|
||||
static void make1c ( state * );
|
||||
static void make1ctail ( state * );
|
||||
static void make1d ( state * );
|
||||
static void make_closure( void * closure, int status, timing_info *, const char *, const char * );
|
||||
|
||||
@@ -233,7 +229,6 @@ int make1( TARGET * t )
|
||||
case T_STATE_MAKE1ATAIL: make1atail( pState ); break;
|
||||
case T_STATE_MAKE1B : make1b ( pState ); break;
|
||||
case T_STATE_MAKE1C : make1c ( pState ); break;
|
||||
case T_STATE_MAKE1CTAIL: make1ctail( pState ); break;
|
||||
case T_STATE_MAKE1D : make1d ( pState ); break;
|
||||
}
|
||||
}
|
||||
@@ -284,28 +279,19 @@ static void make1a( state * pState )
|
||||
*/
|
||||
if ( pState->parent )
|
||||
{
|
||||
TARGET * cycle_root;
|
||||
switch ( t->progress )
|
||||
{
|
||||
case T_MAKE_ONSTACK:
|
||||
if ( t->depth == handling_rescan )
|
||||
{
|
||||
if ( target_scc( pState->parent ) != scc_root )
|
||||
make1breakcycle( pState->parent, t );
|
||||
break;
|
||||
}
|
||||
case T_MAKE_ACTIVE:
|
||||
if ( handling_rescan && ( cycle_root = make1findcycle( t ) ) )
|
||||
{
|
||||
make1breakcycle( pState->parent, cycle_root ); break;
|
||||
}
|
||||
case T_MAKE_INIT:
|
||||
case T_MAKE_RUNNING:
|
||||
if( t != pState->parent )
|
||||
{
|
||||
t->parents = targetentry( t->parents,
|
||||
pState->parent );
|
||||
++pState->parent->asynccnt;
|
||||
TARGET * parent_scc = target_scc( pState->parent );
|
||||
if( t != parent_scc )
|
||||
{
|
||||
t->parents = targetentry( t->parents, parent_scc );
|
||||
++parent_scc->asynccnt;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,18 +324,8 @@ static void make1a( state * pState )
|
||||
*/
|
||||
t->asynccnt = 1;
|
||||
|
||||
/* Add header nodes created during the building process. */
|
||||
{
|
||||
TARGETS * inc = 0;
|
||||
for ( c = t->depends; c; c = c->next )
|
||||
if ( c->target->rescanned && c->target->includes )
|
||||
inc = targetentry( inc, c->target->includes );
|
||||
t->depends = targetchain( t->depends, inc );
|
||||
}
|
||||
|
||||
/* Guard against circular dependencies. */
|
||||
t->progress = T_MAKE_ONSTACK;
|
||||
t->depth = handling_rescan;
|
||||
|
||||
{
|
||||
stack temp_stack = { NULL };
|
||||
@@ -644,12 +620,12 @@ static void make1c( state * pState )
|
||||
* target is built, otherwise the parent would be considered
|
||||
* built before this make1a() processing has even started.
|
||||
*/
|
||||
make0( t->includes, t->parents->target, 0, 0, 0 );
|
||||
make0( t->includes, t->parents->target, 0, 0, 0, t->includes );
|
||||
/* Link the old includes on to make sure that it gets
|
||||
* cleaned up correctly.
|
||||
*/
|
||||
t->includes->includes = saved_includes;
|
||||
for ( c = t->parents; c; c = c->next )
|
||||
for ( c = t->dependants; c; c = c->next )
|
||||
c->target->depends = targetentry( c->target->depends,
|
||||
t->includes );
|
||||
/* Will be processed below. */
|
||||
@@ -662,16 +638,13 @@ static void make1c( state * pState )
|
||||
}
|
||||
|
||||
if ( additional_includes )
|
||||
{
|
||||
++handling_rescan;
|
||||
for ( c = t->parents; c; c = c->next )
|
||||
push_state( &temp_stack, additional_includes, c->target, T_STATE_MAKE1A );
|
||||
push_state( &temp_stack, additional_includes, NULL, T_STATE_MAKE1CTAIL );
|
||||
}
|
||||
|
||||
if ( t->scc_root )
|
||||
{
|
||||
TARGET * scc_root = target_scc( t );
|
||||
assert( scc_root->progress < T_MAKE_DONE );
|
||||
for ( c = t->parents; c; c = c->next )
|
||||
{
|
||||
if ( target_scc( c->target ) == scc_root )
|
||||
@@ -724,12 +697,6 @@ static void make1c( state * pState )
|
||||
}
|
||||
}
|
||||
|
||||
static void make1ctail( state * pState )
|
||||
{
|
||||
--handling_rescan;
|
||||
pop_state( &state_stack );
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* call_timing_rule() - Look up the __TIMING_RULE__ variable on the given
|
||||
@@ -1217,80 +1184,3 @@ static void make1bind( TARGET * t )
|
||||
t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
|
||||
popsettings( root_module(), t->settings );
|
||||
}
|
||||
|
||||
|
||||
static void make1cyclenode( TARGET * t, TARGET * scc_root )
|
||||
{
|
||||
/* if we intersect with another cycle we need to merge the two */
|
||||
if ( t->scc_root )
|
||||
{
|
||||
TARGET * other_root = target_scc( t );
|
||||
if ( other_root != scc_root )
|
||||
{
|
||||
other_root->scc_root = scc_root;
|
||||
other_root->parents = targetentry( other_root->parents, scc_root );
|
||||
++scc_root->asynccnt;
|
||||
}
|
||||
}
|
||||
if ( t != scc_root )
|
||||
t->scc_root = scc_root;
|
||||
}
|
||||
|
||||
|
||||
static TARGET * make1findcycle_impl( TARGET * t, TARGET * scc_root )
|
||||
{
|
||||
TARGETS * c;
|
||||
TARGET * result;
|
||||
|
||||
if ( t->progress == T_MAKE_ONSTACK )
|
||||
if ( t->depth == handling_rescan )
|
||||
return t;
|
||||
else
|
||||
t->progress = T_MAKE_FINDCYCLE_ONSTACK;
|
||||
else if ( t->progress == T_MAKE_ACTIVE )
|
||||
t->progress = T_MAKE_FINDCYCLE_ACTIVE;
|
||||
else
|
||||
return 0;
|
||||
|
||||
|
||||
for ( c = t->depends; c; c = c->next )
|
||||
if ( ( result = make1findcycle_impl( c->target, scc_root ) ) )
|
||||
{
|
||||
make1cyclenode( c->target, scc_root );
|
||||
return result;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void make1findcycle_cleanup( TARGET * t )
|
||||
{
|
||||
TARGETS * c;
|
||||
|
||||
if ( t->progress == T_MAKE_FINDCYCLE_ACTIVE )
|
||||
t->progress = T_MAKE_ACTIVE;
|
||||
else if ( t->progress == T_MAKE_FINDCYCLE_ONSTACK )
|
||||
t->progress = T_MAKE_ONSTACK;
|
||||
else
|
||||
return;
|
||||
|
||||
for ( c = t->depends; c; c = c->next )
|
||||
make1findcycle_cleanup( c->target );
|
||||
}
|
||||
|
||||
static TARGET * make1findcycle( TARGET * t )
|
||||
{
|
||||
TARGET * result = make1findcycle_impl( t, target_scc( t ) );
|
||||
make1findcycle_cleanup( t );
|
||||
return result;
|
||||
}
|
||||
|
||||
static void make1breakcycle( TARGET * t, TARGET * cycle_root )
|
||||
{
|
||||
TARGET * scc_root = target_scc( cycle_root );
|
||||
while ( t != cycle_root )
|
||||
{
|
||||
make1cyclenode( t, scc_root );
|
||||
t = t->parents->target;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -212,8 +212,6 @@ struct _target
|
||||
#define T_MAKE_RUNNING 3 /* make1(target) running commands */
|
||||
#define T_MAKE_DONE 4 /* make1(target) done */
|
||||
#define T_MAKE_NOEXEC_DONE 5 /* make1(target) done with -n in effect */
|
||||
#define T_MAKE_FINDCYCLE_ONSTACK 6 /* make1(target) searching for cyclic includes after */
|
||||
#define T_MAKE_FINDCYCLE_ACTIVE 7 /* rescanning a generated file. */
|
||||
|
||||
#ifdef OPT_SEMAPHORE
|
||||
#define T_MAKE_SEMAPHORE 5 /* Special target type for semaphores */
|
||||
@@ -227,7 +225,8 @@ struct _target
|
||||
|
||||
int asynccnt; /* child deps outstanding */
|
||||
TARGETS * parents; /* used by make1() for completion */
|
||||
TARGET * scc_root; /* used by make1 to resolve cyclic includes */
|
||||
TARGET * scc_root; /* used by make to resolve cyclic includes */
|
||||
TARGET * rescanning; /* used by make0 to mark visited targets when rescanning */
|
||||
int depth; /* The depth of the target in the make0 stack. */
|
||||
char * cmds; /* type-punned command list */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user