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 ); } @@ -676,12 +680,36 @@ else fate = t->fate; - /* Step 4g: If this target needs to be built, force rebuild everything in + /* + * Step 4g: If this target needs to be built, make0 all targets + * that are updated by the same actions used to update this target. + * These have already been marked as REBUILDS, and make1 has + * special handling for them. We just need to make sure that + * they get make0ed. + */ + if ( ( fate >= T_FATE_BUILD ) && ( fate < T_FATE_BROKEN ) ) + { + ACTIONS * a; + TARGETS * c; + for ( a = t->actions; a; a = a->next ) + { + for ( c = a->action->targets; c; c = c->next ) + { + if ( c->target->fate == T_FATE_INIT ) + { + make0( c->target, ptime, depth + 1, counts, anyhow, rescanning ); + } + } + } + } + + /* Step 4h: If this target needs to be built, force rebuild everything in * its rebuilds list. */ if ( ( fate >= T_FATE_BUILD ) && ( fate < T_FATE_BROKEN ) ) force_rebuilds( t ); + /* * Step 5: Sort dependencies by their update time. */ Index: engine/make1.c =================================================================== --- engine/make1.c (revision 86425) +++ engine/make1.c (working copy) @@ -63,6 +63,7 @@ static void make1bind ( TARGET * ); static TARGET * make1findcycle ( TARGET * ); static void make1breakcycle( TARGET *, TARGET * cycle_root ); +static void push_cmds( CMDLIST * cmds, int status ); /* Ugly static - it is too hard to carry it through the callbacks. */ @@ -372,12 +373,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 +396,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 +514,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 +536,7 @@ TARGET * const t = pState->t; CMD * const cmd = (CMD *)t->cmds; - if ( cmd && t->status == EXEC_CMD_OK ) + if ( cmd ) { /* 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 @@ -543,6 +545,14 @@ */ pop_state( &state_stack ); + if ( cmd->status != EXEC_CMD_OK ) + { + t->cmds = NULL; + push_cmds( cmd->next, cmd->status ); + cmd_free( cmd ); + return; + } + /* Increment the jobs running counter. */ ++cmdsrunning; @@ -575,14 +585,6 @@ { ACTIONS * actions; - /* Collect status from actions, and distribute it as well. */ - for ( actions = t->actions; actions; actions = actions->next ) - if ( actions->action->status > t->status ) - t->status = actions->action->status; - for ( actions = t->actions; actions; actions = actions->next ) - if ( t->status > actions->action->status ) - actions->action->status = t->status; - /* Tally success/failure for those we tried to update. */ if ( t->progress == T_MAKE_RUNNING ) switch ( t->status ) @@ -948,12 +950,47 @@ /* 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; + push_cmds( cmd->next, t->status ); cmd_free( cmd ); - push_state( &state_stack, t, NULL, T_STATE_MAKE1C ); } +/* push the next MAKE1C state after a command is run. */ +static void push_cmds( CMDLIST * cmds, int status ) +{ + CMDLIST * cmd_iter; + for( cmd_iter = cmds; 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 < status ) + next_cmd->status = 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 < status ) + updated_target->status = status; + updated_target->cmds = NULL; + push_state( &state_stack, updated_target, NULL, T_STATE_MAKE1C ); + } + } +} + /* * swap_settings() - replace the settings from the current module and target * with those from the new module and target @@ -995,7 +1032,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 +1054,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 +1137,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 +1201,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 +1235,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 +1259,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/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) @@ -92,8 +92,13 @@ #define A_INIT 0 #define A_RUNNING_NOEXEC 1 #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. */