Index: engine/make1.c =================================================================== --- engine/make1.c (revision 86425) +++ engine/make1.c (working copy) @@ -372,12 +372,13 @@ TARGET * failed = 0; char const * failed_name = "dependencies"; + pop_state( &state_stack ); + /* If any dependencies are still outstanding, wait until they signal their * completion by pushing this same state for their parent targets. */ if ( --t->asynccnt ) { - pop_state( &state_stack ); return; } @@ -394,7 +395,6 @@ if ( DEBUG_EXECCMD ) printf( "SEM: %s is busy, delaying launch of %s\n", object_str( t->semaphore->name ), object_str( t->name ) ); - pop_state( &state_stack ); return; } #endif @@ -513,7 +513,8 @@ * pushing a new one is a slight optimization with no side-effects since we * pushed no other states while processing this one. */ - pState->curstate = T_STATE_MAKE1C; + if ( t->cmds == NULL || --( ( CMD * )t->cmds )->asynccnt == 0 ) + push_state( &state_stack, t, NULL, T_STATE_MAKE1C ); } @@ -534,7 +535,7 @@ TARGET * const t = pState->t; CMD * const cmd = (CMD *)t->cmds; - if ( cmd && t->status == EXEC_CMD_OK ) + if ( cmd && cmd->status == EXEC_CMD_OK ) { /* Pop state first in case something below (e.g. exec_cmd(), exec_wait() * or make1c_closure()) pushes a new state. Note that we must not access @@ -859,6 +860,7 @@ CMD * const cmd = (CMD *)t->cmds; char const * rule_name = 0; char const * target_name = 0; + CMDLIST * cmd_iter; assert( cmd ); @@ -948,9 +950,37 @@ /* Free this command and push the MAKE1C state to execute the next one * scheduled for building this same target. */ - t->cmds = (char *)cmd_next( cmd ); + t->cmds = NULL; + for( cmd_iter = cmd->next; cmd_iter; cmd_iter = cmd_iter->next ) + { + if ( cmd_iter->iscmd ) + { + CMD * next_cmd = cmd_iter->cmd; + /* Propagate the command status. */ + if ( next_cmd->status < t->status ) + next_cmd->status = t->status; + if ( --next_cmd->asynccnt == 0 ) + { + /* Select the first target associated with the action. + * This is safe because sibling CMDs cannot have targets + * in common. + */ + TARGET * first_target = bindtarget( list_front( lol_get( &next_cmd->args, 0 ) ) ); + first_target->cmds = (char *)next_cmd; + push_state( &state_stack, first_target, NULL, T_STATE_MAKE1C ); + } + } + else + { + /* This is a target that we're finished updating */ + TARGET * updated_target = cmd_iter->t; + if ( updated_target->status < t->status ) + updated_target->status = t->status; + updated_target->cmds = NULL; + push_state( &state_stack, updated_target, NULL, T_STATE_MAKE1C ); + } + } cmd_free( cmd ); - push_state( &state_stack, t, NULL, T_STATE_MAKE1C ); } @@ -995,7 +1025,7 @@ static CMD * make1cmds( TARGET * t ) { CMD * cmds = 0; - CMD * * cmds_next = &cmds; + CMD * last_cmd; LIST * shell = L0; module_t * settings_module = 0; TARGET * settings_target = 0; @@ -1017,9 +1047,33 @@ /* Only do rules with commands to execute. If this action has already * been executed, use saved status. */ - if ( !actions || a0->action->running >= running_flag ) + if ( !actions ) continue; + if ( a0->action->running >= running_flag ) + { + CMD * first; + /* FIXME: Forbid multiple targets with RULE_TOGETHER, or + * figure out exactly what it should mean. + */ + if ( actions->flags & RULE_TOGETHER ) + continue; + /* This action has already been processed for another target. + * Just set up the dependency graph correctly and move on. + */ + first = a0->action->first_cmd; + if( cmds ) + { + last_cmd->next = cmdlist_append_cmd( last_cmd->next, first ); + } + else + { + cmds = first; + } + last_cmd = a0->action->last_cmd; + continue; + } + a0->action->running = running_flag; /* Make LISTS of targets and sources. If `execute together` has been @@ -1076,8 +1130,10 @@ int const length = list_length( ns ); int start = 0; int chunk = length; + int cmd_count = 0; LIST * cmd_targets = L0; LIST * cmd_shell = L0; + TARGETS * targets_iter; do { CMD * cmd; @@ -1138,9 +1194,21 @@ if ( accept_command ) { /* Chain it up. */ - *cmds_next = cmd; - cmds_next = &cmd->next; + if ( cmds ) + { + last_cmd->next = cmdlist_append_cmd( last_cmd->next, cmd ); + last_cmd = cmd; + } + else + { + cmds = last_cmd = cmd; + } + if ( cmd_count++ == 0 ) + { + a0->action->first_cmd = cmd; + } + /* Mark lists we need recreated for the next command since * they got consumed by the cmd object. */ @@ -1160,6 +1228,19 @@ start += chunk; } while ( start < length ); + + /* Record the end of the actions cmds */ + a0->action->last_cmd = last_cmd; + /* We need to wait until all the targets agree that + * it's okay to run this action. + */ + ( ( CMD * )a0->action->first_cmd )->asynccnt = list_length( nt ); + + /* Add all targets produced by the action to the update list. */ + for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next ) + { + push_state( &state_stack, targets_iter->target, NULL, T_STATE_MAKE1A ); + } } /* These were always copied when used. */ @@ -1171,6 +1252,11 @@ freesettings( boundvars ); } + if ( cmds ) + { + last_cmd->next = cmdlist_append_target( last_cmd->next, t ); + } + swap_settings( &settings_module, &settings_target, 0, 0 ); return cmds; } Index: engine/command.c =================================================================== --- engine/command.c (revision 86425) +++ engine/command.c (working copy) @@ -24,6 +24,37 @@ /* + * cmdlist_append_cmd + */ +CMDLIST * cmdlist_append_cmd( CMDLIST * l, CMD * cmd ) +{ + CMDLIST * result = (CMDLIST *)BJAM_MALLOC( sizeof( CMDLIST ) ); + result->iscmd = 1; + result->next = l; + result->cmd = cmd; + return result; +} + +CMDLIST * cmdlist_append_target( CMDLIST * l, TARGET * t ) +{ + CMDLIST * result = (CMDLIST *)BJAM_MALLOC( sizeof( CMDLIST ) ); + result->iscmd = 0; + result->next = l; + result->t = t; + return result; +} + +void cmdlist_free( CMDLIST * l ) +{ + while ( l ) + { + CMDLIST * tmp = l->next; + BJAM_FREE( l ); + l = tmp; + } +} + +/* * cmd_new() - return a new CMD. */ @@ -37,6 +68,8 @@ cmd->shell = shell; cmd->next = 0; cmd->noop = 0; + cmd->asynccnt = 1; + cmd->status = 0; lol_init( &cmd->args ); lol_add( &cmd->args, targets ); @@ -62,6 +95,7 @@ void cmd_free( CMD * cmd ) { + cmdlist_free( cmd->next ); lol_free( &cmd->args ); list_free( cmd->shell ); string_free( cmd->buf ); Index: engine/command.h =================================================================== --- engine/command.h (revision 86425) +++ engine/command.h (working copy) @@ -44,16 +44,40 @@ #include "rules.h" #include "strings.h" +typedef struct _cmd CMD; -typedef struct _cmd CMD; +/* + * A list whose elements are either TARGETS or CMDS. + * CMDLIST is used only by CMD. A TARGET means that + * the CMD is the last updating action required to + * build the target. A CMD is the next CMD required + * to build the same target. (Note that a single action + * can update more than one target, so the CMDs form + * a DAG, not a straight linear list.) + */ +typedef struct _cmdlist { + struct _cmdlist * next; + union { + CMD * cmd; + TARGET * t; + }; + char iscmd; +} CMDLIST; + +CMDLIST * cmdlist_append_cmd( CMDLIST *, CMD * ); +CMDLIST * cmdlist_append_target( CMDLIST *, TARGET * ); +void cmd_list_free( CMDLIST * ); + struct _cmd { - CMD * next; + CMDLIST * next; RULE * rule; /* rule->actions contains shell script */ LIST * shell; /* $(JAMSHELL) value */ LOL args; /* LISTs for $(<), $(>) */ string buf[ 1 ]; /* actual commands */ int noop; /* no-op commands should be faked instead of executed */ + int asynccnt; /* number of outstanding dependencies */ + char status; /* the command status */ }; CMD * cmd_new Index: engine/make.c =================================================================== --- engine/make.c (revision 86425) +++ engine/make.c (working copy) @@ -161,6 +161,8 @@ * make0() to be updated. */ +static void force_rebuilds( TARGET * t ); + static void update_dependants( TARGET * t ) { TARGETS * q; @@ -190,6 +192,8 @@ update_dependants( p ); } } + /* Make sure that rebuilds can be chained. */ + force_rebuilds( t ); } Index: engine/compile.c =================================================================== --- engine/compile.c (revision 86425) +++ engine/compile.c (working copy) @@ -165,8 +165,8 @@ TARGET * const t0 = action->targets->target; for ( t = action->targets->next; t; t = t->next ) { - target_include( t->target, t0 ); - target_include( t0, t->target ); + t->target->rebuilds = targetentry( t->target->rebuilds, t0 ); + t0->rebuilds = targetentry( t0->rebuilds, t->target ); } } Index: engine/rules.h =================================================================== --- engine/rules.h (revision 86425) +++ engine/rules.h (working copy) @@ -94,6 +94,12 @@ #define A_RUNNING 2 char status; /* see TARGET status */ int refs; + + /* WARNING: These variables are used to pass state required by make1cmds and + * are not valid anywhere else. + */ + void * first_cmd; /* Pointer to the first CMD created by this action */ + void * last_cmd; /* Pointer to the last CMD created by this action */ }; /* SETTINGS - variables to set when executing a TARGET's ACTIONS. */