diff --git a/v2/engine/make.c b/v2/engine/make.c index dadb67d03..bd4fc4706 100644 --- a/v2/engine/make.c +++ b/v2/engine/make.c @@ -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. */ diff --git a/v2/engine/make1.c b/v2/engine/make1.c index 7b5354870..451ef6983 100644 --- a/v2/engine/make1.c +++ b/v2/engine/make1.c @@ -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; -} diff --git a/v2/engine/rules.c b/v2/engine/rules.c index b9ff6191f..fd00346f7 100644 --- a/v2/engine/rules.c +++ b/v2/engine/rules.c @@ -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. diff --git a/v2/engine/rules.h b/v2/engine/rules.h index 3a3db8c94..758e53644 100644 --- a/v2/engine/rules.h +++ b/v2/engine/rules.h @@ -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(); diff --git a/v2/test/rescan_header.py b/v2/test/rescan_header.py index 16fd8b104..5d4dc3c06 100755 --- a/v2/test/rescan_header.py +++ b/v2/test/rescan_header.py @@ -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