mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 01:12:13 +00:00
Handle cycles when determining target fate in make0.
[SVN r78259]
This commit is contained in:
@@ -280,6 +280,7 @@ void make0
|
||||
printf( "make\t--\t%s%s\n", spaces( depth ), object_str( t->name ) );
|
||||
|
||||
t->fate = T_FATE_MAKING;
|
||||
t->depth = depth;
|
||||
|
||||
/*
|
||||
* Step 2: under the influence of "on target" variables,
|
||||
@@ -397,6 +398,25 @@ void make0
|
||||
t->depends = targetchain( t->depends, incs );
|
||||
}
|
||||
|
||||
/* Step 3d: detect cycles. */
|
||||
{
|
||||
int cycle_depth = depth;
|
||||
for ( c = t->depends; c; c = c->next )
|
||||
{
|
||||
TARGET * scc_root = target_scc( c->target );
|
||||
if ( scc_root->fate == T_FATE_MAKING &&
|
||||
( !scc_root->includes ||
|
||||
scc_root->includes->fate != T_FATE_MAKING ) )
|
||||
{
|
||||
if ( scc_root->depth < cycle_depth )
|
||||
{
|
||||
cycle_depth = scc_root->depth;
|
||||
t->scc_root = scc_root;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Step 4: compute time & fate
|
||||
*/
|
||||
@@ -407,6 +427,20 @@ void make0
|
||||
fate = T_FATE_STABLE;
|
||||
for ( c = t->depends; c; c = c->next )
|
||||
{
|
||||
/* If we're in a different strongly connected component,
|
||||
* pull timestamps from the root.
|
||||
*/
|
||||
if ( c->target->scc_root )
|
||||
{
|
||||
TARGET * scc_root = target_scc( c->target );
|
||||
if ( scc_root != t->scc_root )
|
||||
{
|
||||
c->target->leaf = max( c->target->leaf, scc_root->leaf );
|
||||
c->target->time = max( c->target->time, scc_root->time );
|
||||
c->target->fate = max( c->target->fate, scc_root->fate );
|
||||
}
|
||||
}
|
||||
|
||||
/* If LEAVES has been applied, we only heed the timestamps of the leaf
|
||||
* source nodes.
|
||||
*/
|
||||
|
||||
@@ -76,7 +76,6 @@ static SETTINGS * make1settings( struct module_t * module, LIST * vars );
|
||||
static void make1bind ( TARGET * );
|
||||
static TARGET * make1findcycle( TARGET * t );
|
||||
static void make1breakcycle( TARGET * t, TARGET * cycle_root );
|
||||
static TARGET * make1scc ( TARGET * );
|
||||
|
||||
/* Ugly static - it is too hard to carry it through the callbacks. */
|
||||
|
||||
@@ -271,31 +270,37 @@ int make1( TARGET * t )
|
||||
|
||||
static void make1a( state * pState )
|
||||
{
|
||||
TARGET * t = pState->t;
|
||||
TARGET * t = target_scc( pState->t );
|
||||
TARGETS * c;
|
||||
|
||||
if ( pState->parent == NULL || target_scc( pState->parent ) != t )
|
||||
pState->t = t;
|
||||
else
|
||||
t = pState->t;
|
||||
|
||||
/* If the parent is the first to try to build this target or this target is
|
||||
* in the make1c() quagmire, arrange for the parent to be notified when this
|
||||
* target is built.
|
||||
*/
|
||||
if ( pState->parent )
|
||||
{
|
||||
TARGET * dependency = make1scc( t );
|
||||
TARGET * cycle_root;
|
||||
switch ( dependency->progress )
|
||||
switch ( t->progress )
|
||||
{
|
||||
case T_MAKE_ONSTACK:
|
||||
make1breakcycle( pState->parent, dependency ); break;
|
||||
if ( target_scc( pState->parent ) != t )
|
||||
make1breakcycle( pState->parent, t );
|
||||
break;
|
||||
case T_MAKE_ACTIVE:
|
||||
if ( handling_rescan && ( cycle_root = make1findcycle( dependency ) ) )
|
||||
if ( handling_rescan && ( cycle_root = make1findcycle( t ) ) )
|
||||
{
|
||||
make1breakcycle( pState->parent, cycle_root ); break;
|
||||
}
|
||||
case T_MAKE_INIT:
|
||||
case T_MAKE_RUNNING:
|
||||
if( dependency != pState->parent )
|
||||
if( t != pState->parent )
|
||||
{
|
||||
dependency->parents = targetentry( dependency->parents,
|
||||
t->parents = targetentry( t->parents,
|
||||
pState->parent );
|
||||
++pState->parent->asynccnt;
|
||||
}
|
||||
@@ -306,15 +311,15 @@ static void make1a( state * pState )
|
||||
* If the target has been previously updated with -n in
|
||||
* effect, and we're ignoring -n, update it for real.
|
||||
*/
|
||||
if ( !globs.noexec && pState->t->progress == T_MAKE_NOEXEC_DONE )
|
||||
if ( !globs.noexec && t->progress == T_MAKE_NOEXEC_DONE )
|
||||
{
|
||||
pState->t->progress = T_MAKE_INIT;
|
||||
t->progress = T_MAKE_INIT;
|
||||
}
|
||||
|
||||
/* If this target is already being processed then do nothing. There is no
|
||||
* need to start processing the same target all over again.
|
||||
*/
|
||||
if ( pState->t->progress != T_MAKE_INIT )
|
||||
if ( t->progress != T_MAKE_INIT )
|
||||
{
|
||||
pop_state( &state_stack );
|
||||
return;
|
||||
@@ -328,7 +333,7 @@ static void make1a( state * pState )
|
||||
* processing all of our other dependencies our build might be triggerred
|
||||
* prematurely.
|
||||
*/
|
||||
pState->t->asynccnt = 1;
|
||||
t->asynccnt = 1;
|
||||
|
||||
/* Add header nodes created during the building process. */
|
||||
{
|
||||
@@ -340,12 +345,12 @@ static void make1a( state * pState )
|
||||
}
|
||||
|
||||
/* Guard against circular dependencies. */
|
||||
pState->t->progress = T_MAKE_ONSTACK;
|
||||
t->progress = T_MAKE_ONSTACK;
|
||||
|
||||
{
|
||||
stack temp_stack = { NULL };
|
||||
for ( c = t->depends; c && !intr; c = c->next )
|
||||
push_state( &temp_stack, c->target, pState->t, T_STATE_MAKE1A );
|
||||
push_state( &temp_stack, c->target, t, T_STATE_MAKE1A );
|
||||
|
||||
/* Using stacks reverses the order of execution. Reverse it back. */
|
||||
push_stack_on_stack( &state_stack, &temp_stack );
|
||||
@@ -662,10 +667,10 @@ static void make1c( state * pState )
|
||||
|
||||
if ( t->scc_root )
|
||||
{
|
||||
TARGET * scc_root = make1scc( t );
|
||||
TARGET * scc_root = target_scc( t );
|
||||
for ( c = t->parents; c; c = c->next )
|
||||
{
|
||||
if ( make1scc( c->target ) == scc_root )
|
||||
if ( target_scc( c->target ) == scc_root )
|
||||
push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B );
|
||||
else
|
||||
scc_root->parents = targetentry( scc_root->parents, c->target );
|
||||
@@ -1215,7 +1220,7 @@ 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 = make1scc( t );
|
||||
TARGET * other_root = target_scc( t );
|
||||
if ( other_root != scc_root )
|
||||
{
|
||||
other_root->scc_root = scc_root;
|
||||
@@ -1271,25 +1276,10 @@ static TARGET * make1findcycle( TARGET * t )
|
||||
|
||||
static void make1breakcycle( TARGET * t, TARGET * cycle_root )
|
||||
{
|
||||
TARGET * scc_root = make1scc( cycle_root );
|
||||
TARGET * scc_root = target_scc( cycle_root );
|
||||
while ( t != cycle_root )
|
||||
{
|
||||
make1cyclenode( t, scc_root );
|
||||
t = t->parents->target;
|
||||
}
|
||||
}
|
||||
|
||||
static TARGET * make1scc( TARGET * t )
|
||||
{
|
||||
TARGET * result = t;
|
||||
TARGET * tmp;
|
||||
while ( result->scc_root )
|
||||
result = result->scc_root;
|
||||
while ( t->scc_root )
|
||||
{
|
||||
tmp = t->scc_root;
|
||||
t->scc_root = result;
|
||||
t = tmp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -214,6 +214,26 @@ void touch_target( OBJECT * t )
|
||||
bindtarget( t )->flags |= T_FLAG_TOUCHED;
|
||||
}
|
||||
|
||||
/*
|
||||
* target_scc() - returns the root of the strongly
|
||||
* connected component that this target
|
||||
* is a part of.
|
||||
*/
|
||||
TARGET * target_scc( TARGET * t )
|
||||
{
|
||||
TARGET * result = t;
|
||||
TARGET * tmp;
|
||||
while ( result->scc_root )
|
||||
result = result->scc_root;
|
||||
while ( t->scc_root )
|
||||
{
|
||||
tmp = t->scc_root;
|
||||
t->scc_root = result;
|
||||
t = tmp;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* targetlist() - turn list of target names into a TARGET chain.
|
||||
|
||||
@@ -228,6 +228,7 @@ struct _target
|
||||
int asynccnt; /* child deps outstanding */
|
||||
TARGETS * parents; /* used by make1() for completion */
|
||||
TARGET * scc_root; /* used by make1 to resolve cyclic includes */
|
||||
int depth; /* The depth of the target in the make0 stack. */
|
||||
char * cmds; /* type-punned command list */
|
||||
|
||||
const char * failed;
|
||||
@@ -265,6 +266,7 @@ void target_include ( TARGET * including, TARGET * included
|
||||
TARGETS * targetlist ( TARGETS * chain, LIST * target_names );
|
||||
void touch_target ( OBJECT * t );
|
||||
void clear_includes ( TARGET * );
|
||||
TARGET * target_scc ( TARGET * t );
|
||||
|
||||
/* Final module cleanup. */
|
||||
void rules_done();
|
||||
|
||||
@@ -220,6 +220,14 @@ t.expect_addition("bin/$toolset/debug/test2.obj")
|
||||
t.expect_addition("bin/$toolset/debug/test.exe")
|
||||
t.expect_nothing_more()
|
||||
|
||||
t.touch("header3.in")
|
||||
t.run_build_system("-j2 test")
|
||||
t.expect_touch("bin/$toolset/debug/header3.h")
|
||||
t.expect_touch("bin/$toolset/debug/test1.obj")
|
||||
t.expect_touch("bin/$toolset/debug/test2.obj")
|
||||
t.expect_touch("bin/$toolset/debug/test.exe")
|
||||
t.expect_nothing_more()
|
||||
|
||||
t.rm(".")
|
||||
|
||||
# Test a loop that includes a generated header
|
||||
|
||||
Reference in New Issue
Block a user