/* * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. * * This file is part of Jam - see jam.c for Copyright information. */ /* This file is ALSO: * Copyright 2001-2004 David Abrahams. * Distributed under the Boost Software License, Version 1.0. * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) */ /* * make1.c - execute commands to bring targets up to date * * This module contains make1(), the entry point called by make() to recursively * descend the dependency graph executing update actions as marked by make0(). * * External routines: * make1() - execute commands to update a TARGET and all of its dependencies * * Internal routines, the recursive/asynchronous command executors: * make1a() - recursively schedules dependency builds and then goes to * MAKE1B * make1b() - if nothing is blocking this target's build, proceed to * MAKE1C * make1c() - launch target's next command, or go to parents' MAKE1B * if none * make1c_closure() - handle command execution completion and go to MAKE1C * * Internal support routines: * make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc. * make1list() - turn a list of targets into a LIST, for $(<) and $(>) * make1settings() - for vars with bound values, build up replacement lists * make1bind() - bind targets that weren't bound in dependency analysis */ #include "jam.h" #include "make.h" #include "command.h" #include "compile.h" #include "execcmd.h" #include "headers.h" #include "lists.h" #include "object.h" #include "output.h" #include "parse.h" #include "rules.h" #include "search.h" #include "variable.h" #include #include #if !defined( NT ) || defined( __GNUC__ ) #include /* for unlink */ #endif static CMD * make1cmds ( TARGET * ); static LIST * make1list ( LIST *, TARGETS *, int flags ); static SETTINGS * make1settings ( struct module_t *, LIST * vars ); static void make1bind ( TARGET * ); static TARGET * make1findcycle ( TARGET * ); static void make1breakcycle( TARGET *, TARGET * cycle_root ); static void push_cmds( CMDLIST * cmds, int status ); static int cmd_sem_lock( TARGET * t ); static void cmd_sem_unlock( TARGET * t ); static int targets_contains( TARGETS * l, TARGET * t ); static int targets_equal( TARGETS * l1, TARGETS * l2 ); /* Ugly static - it is too hard to carry it through the callbacks. */ static struct { int failed; int skipped; int total; int made; } counts[ 1 ]; /* Target state. */ #define T_STATE_MAKE1A 0 /* make1a() should be called */ #define T_STATE_MAKE1B 1 /* make1b() should be called */ #define T_STATE_MAKE1C 2 /* make1c() should be called */ typedef struct _state state; struct _state { state * prev; /* previous state on stack */ TARGET * t; /* current target */ TARGET * parent; /* parent argument necessary for MAKE1A */ int curstate; /* current state */ }; static void make1a( state * const ); static void make1b( state * const ); static void make1c( state const * const ); static void make1c_closure( void * const closure, int status, timing_info const * const, char const * const cmd_stdout, char const * const cmd_stderr, int const cmd_exit_reason ); typedef struct _stack { state * stack; } stack; static stack state_stack = { NULL }; static state * state_freelist = NULL; /* Currently running command counter. */ static int cmdsrunning; static state * alloc_state() { if ( state_freelist ) { state * const pState = state_freelist; state_freelist = pState->prev; memset( pState, 0, sizeof( state ) ); return pState; } return (state *)BJAM_MALLOC( sizeof( state ) ); } static void free_state( state * const pState ) { pState->prev = state_freelist; state_freelist = pState; } static void clear_state_freelist() { while ( state_freelist ) { state * const pState = state_freelist; state_freelist = state_freelist->prev; BJAM_FREE( pState ); } } static state * current_state( stack * const pStack ) { return pStack->stack; } static void pop_state( stack * const pStack ) { if ( pStack->stack ) { state * const pState = pStack->stack->prev; free_state( pStack->stack ); pStack->stack = pState; } } static state * push_state( stack * const pStack, TARGET * const t, TARGET * const parent, int const curstate ) { state * const pState = alloc_state(); pState->t = t; pState->parent = parent; pState->prev = pStack->stack; pState->curstate = curstate; return pStack->stack = pState; } /* * Pushes a stack onto another stack, effectively reversing the order. */ static void push_stack_on_stack( stack * const pDest, stack * const pSrc ) { while ( pSrc->stack ) { state * const pState = pSrc->stack; pSrc->stack = pState->prev; pState->prev = pDest->stack; pDest->stack = pState; } } /* * make1() - execute commands to update a list of targets and all of their dependencies */ static int intr = 0; static int quit = 0; int make1( LIST * targets ) { state * pState; int status = 0; memset( (char *)counts, 0, sizeof( *counts ) ); { LISTITER iter, end; stack temp_stack = { NULL }; for ( iter = list_begin( targets ), end = list_end( targets ); iter != end; iter = list_next( iter ) ) push_state( &temp_stack, bindtarget( list_item( iter ) ), NULL, T_STATE_MAKE1A ); push_stack_on_stack( &state_stack, &temp_stack ); } /* Clear any state left over from the past */ quit = 0; /* Recursively make the target and its dependencies. */ while ( 1 ) { while ( ( pState = current_state( &state_stack ) ) ) { if ( quit ) pop_state( &state_stack ); switch ( pState->curstate ) { case T_STATE_MAKE1A: make1a( pState ); break; case T_STATE_MAKE1B: make1b( pState ); break; case T_STATE_MAKE1C: make1c( pState ); break; default: assert( !"make1(): Invalid state detected." ); } } if ( !cmdsrunning ) break; /* Wait for outstanding commands to finish running. */ exec_wait(); } clear_state_freelist(); /* Talk about it. */ if ( counts->failed ) printf( "...failed updating %d target%s...\n", counts->failed, counts->failed > 1 ? "s" : "" ); if ( DEBUG_MAKE && counts->skipped ) printf( "...skipped %d target%s...\n", counts->skipped, counts->skipped > 1 ? "s" : "" ); if ( DEBUG_MAKE && counts->made ) printf( "...updated %d target%s...\n", counts->made, counts->made > 1 ? "s" : "" ); /* If we were interrupted, exit now that all child processes have finished. */ if ( intr ) exit( 1 ); { LISTITER iter, end; for ( iter = list_begin( targets ), end = list_end( targets ); iter != end; iter = list_next( iter ) ) { /* Check that the target was updated and that the update succeeded. */ TARGET * t = bindtarget( list_item( iter ) ); if (t->progress == T_MAKE_DONE) { if (t->status != EXEC_CMD_OK) status = 1; } else if ( ! ( t->progress == T_MAKE_NOEXEC_DONE && globs.noexec ) ) { status = 1; } } } return status; } /* * make1a() - recursively schedules dependency builds and then goes to MAKE1B * * Called to start processing a specified target. Does nothing if the target is * already being processed or otherwise starts processing all of its * dependencies. */ static void make1a( state * const pState ) { TARGET * t = pState->t; TARGET * const scc_root = target_scc( t ); if ( !pState->parent || target_scc( pState->parent ) != scc_root ) pState->t = t = scc_root; /* 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 has been built. */ if ( pState->parent && t->progress <= T_MAKE_RUNNING ) { TARGET * const parent_scc = target_scc( pState->parent ); if ( t != parent_scc ) { t->parents = targetentry( t->parents, parent_scc ); ++parent_scc->asynccnt; } } /* If the target has been previously updated with -n in effect, and we are * now ignoring -n, update it for real. E.g. if the UPDATE_NOW rule was * called for it twice - first with the -n option and then without. */ if ( !globs.noexec && t->progress == T_MAKE_NOEXEC_DONE ) 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 ( t->progress != T_MAKE_INIT ) { pop_state( &state_stack ); return; } /* Guard against circular dependencies. */ t->progress = T_MAKE_ONSTACK; /* 'asynccnt' counts the dependencies preventing this target from proceeding * to MAKE1C for actual building. We start off with a count of 1 to prevent * anything from happening until we can notify all dependencies that they * are needed. This 1 is then accounted for when we enter MAKE1B ourselves, * below. Without this if a dependency gets built before we finish * processing all of our other dependencies our build might be triggerred * prematurely. */ t->asynccnt = 1; /* Push dependency build requests (to be executed in the natural order). */ { stack temp_stack = { NULL }; TARGETS * c; for ( c = t->depends; c && !quit; c = c->next ) push_state( &temp_stack, c->target, t, T_STATE_MAKE1A ); push_stack_on_stack( &state_stack, &temp_stack ); } t->progress = T_MAKE_ACTIVE; /* Once all of our dependencies have started getting processed we can move * onto MAKE1B. */ /* Implementation note: * In theory this would be done by popping this state before pushing * dependency target build requests but as a slight optimization we simply * modify our current state and leave it on the stack instead. */ pState->curstate = T_STATE_MAKE1B; } /* * make1b() - if nothing is blocking this target's build, proceed to MAKE1C * * Called after something stops blocking this target's build, e.g. that all of * its dependencies have started being processed, one of its dependencies has * been built or a semaphore this target has been waiting for is free again. */ static void make1b( state * const pState ) { TARGET * const t = pState->t; 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 ) { return; } /* Now ready to build target 't', if dependencies built OK. */ /* Collect status from dependencies. If -n was passed then act as though all * dependencies built correctly (the only way they can fail is if UPDATE_NOW * was called). If the dependencies can not be found or we got an interrupt, * we can not get here. */ if ( !globs.noexec ) { TARGETS * c; for ( c = t->depends; c; c = c->next ) if ( c->target->status > t->status && !( c->target->flags & T_FLAG_NOCARE ) ) { failed = c->target; t->status = c->target->status; } } /* If an internal header node failed to build, we want to output the target * that it failed on. */ if ( failed ) failed_name = failed->flags & T_FLAG_INTERNAL ? failed->failed : object_str( failed->name ); t->failed = failed_name; /* If actions for building any of the dependencies have failed, bail. * Otherwise, execute all actions to make the current target. */ if ( ( t->status == EXEC_CMD_FAIL ) && t->actions ) { ++counts->skipped; if ( ( t->flags & ( T_FLAG_RMOLD | T_FLAG_NOTFILE ) ) == T_FLAG_RMOLD ) { if ( !unlink( object_str( t->boundname ) ) ) printf( "...removing outdated %s\n", object_str( t->boundname ) ); } else printf( "...skipped %s for lack of %s...\n", object_str( t->name ), failed_name ); } if ( t->status == EXEC_CMD_OK ) switch ( t->fate ) { case T_FATE_STABLE: case T_FATE_NEWER: break; case T_FATE_CANTFIND: case T_FATE_CANTMAKE: t->status = EXEC_CMD_FAIL; break; case T_FATE_ISTMP: if ( DEBUG_MAKE ) printf( "...using %s...\n", object_str( t->name ) ); break; case T_FATE_TOUCHED: case T_FATE_MISSING: case T_FATE_NEEDTMP: case T_FATE_OUTDATED: case T_FATE_UPDATE: case T_FATE_REBUILD: /* Prepare commands for executing actions scheduled for this target. * Commands have their embedded variables automatically expanded, * including making use of any "on target" variables. */ if ( t->actions ) { ++counts->total; if ( DEBUG_MAKE && !( counts->total % 100 ) ) printf( "...on %dth target...\n", counts->total ); t->cmds = (char *)make1cmds( t ); /* Update the target's "progress" so MAKE1C processing counts it * among its successes/failures. */ t->progress = T_MAKE_RUNNING; } break; /* All valid fates should have been accounted for by now. */ default: printf( "ERROR: %s has bad fate %d", object_str( t->name ), t->fate ); abort(); } /* Proceed to MAKE1C to begin executing the chain of commands prepared for * building the target. If we are not going to build the target (e.g. due to * dependency failures or no commands needing to be run) the chain will be * empty and MAKE1C processing will directly signal the target's completion. */ if ( t->cmds == NULL || --( ( CMD * )t->cmds )->asynccnt == 0 ) push_state( &state_stack, t, NULL, T_STATE_MAKE1C ); else if ( DEBUG_EXECCMD ) { CMD * cmd = ( CMD * )t->cmds; printf( "Delaying %s %s: %d targets not ready\n", object_str( cmd->rule->name ), object_str( t->boundname ), cmd->asynccnt ); } } /* * make1c() - launch target's next command, or go to parents' MAKE1B if none * * If there are (more) commands to run to build this target (and we have not hit * an error running earlier comands) we launch the command using exec_cmd(). * Command execution signals its completion in exec_wait() by calling our * make1c_closure() callback. * * If there are no more commands to run, we collect the status from all the * actions and report our completion to all the parents. */ static void make1c( state const * const pState ) { TARGET * const t = pState->t; CMD * const cmd = (CMD *)t->cmds; 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 * the popped state data after this as the same stack node might have * been reused internally for some newly pushed state. */ pop_state( &state_stack ); if ( cmd->status != EXEC_CMD_OK ) { t->cmds = NULL; push_cmds( cmd->next, cmd->status ); cmd_free( cmd ); return; } #ifdef OPT_SEMAPHORE if ( ! cmd_sem_lock( t ) ) { return; } #endif /* Increment the jobs running counter. */ ++cmdsrunning; /* Execute the actual build command or fake it if no-op. */ if ( globs.noexec || cmd->noop ) { timing_info time_info = { 0 }; timestamp_current( &time_info.start ); timestamp_copy( &time_info.end, &time_info.start ); make1c_closure( t, EXEC_CMD_OK, &time_info, "", "", EXIT_OK ); } else { exec_cmd( cmd->buf, make1c_closure, t, cmd->shell ); /* Wait until under the concurrent command count limit. */ /* FIXME: This wait could be skipped here and moved to just before * trying to execute a command that would cross the command count * limit. Note though that this might affect the order in which * unrelated targets get built and would thus require that all * affected Boost Build tests be updated. */ assert( 0 < globs.jobs ); assert( globs.jobs <= MAXJOBS ); while ( cmdsrunning >= globs.jobs ) exec_wait(); } } else { ACTIONS * actions; /* Tally success/failure for those we tried to update. */ if ( t->progress == T_MAKE_RUNNING ) switch ( t->status ) { case EXEC_CMD_OK: ++counts->made; break; case EXEC_CMD_FAIL: ++counts->failed; break; } /* Tell parents their dependency has been built. */ { TARGETS * c; stack temp_stack = { NULL }; TARGET * additional_includes = NULL; t->progress = globs.noexec ? T_MAKE_NOEXEC_DONE : T_MAKE_DONE; /* Target has been updated so rescan it for dependencies. */ if ( t->fate >= T_FATE_MISSING && t->status == EXEC_CMD_OK && !( t->flags & T_FLAG_INTERNAL ) ) { TARGET * saved_includes; SETTINGS * s; /* Clean current includes. */ saved_includes = t->includes; t->includes = 0; s = copysettings( t->settings ); pushsettings( root_module(), s ); headers( t ); popsettings( root_module(), s ); freesettings( s ); if ( t->includes ) { /* Tricky. The parents have already been processed, but they * have not seen the internal node, because it was just * created. We need to: * - push MAKE1A states that would have been pushed by the * parents here * - make sure all unprocessed parents will pick up the * new includes * - make sure processing the additional MAKE1A states is * done before processing the MAKE1B state for our * current target (which would mean this target has * already been built), otherwise the parent would be * considered built before the additional MAKE1A state * processing even got a chance to start. */ 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->dependants; c; c = c->next ) c->target->depends = targetentry( c->target->depends, t->includes ); /* Will be processed below. */ additional_includes = t->includes; } else { t->includes = saved_includes; } } if ( additional_includes ) for ( c = t->parents; c; c = c->next ) push_state( &temp_stack, additional_includes, c->target, T_STATE_MAKE1A ); if ( t->scc_root ) { TARGET * const 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 ) push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B ); else scc_root->parents = targetentry( scc_root->parents, c->target ); } } else { for ( c = t->parents; c; c = c->next ) push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B ); } /* Must pop state before pushing any more. */ pop_state( &state_stack ); /* Using stacks reverses the order of execution. Reverse it back. */ push_stack_on_stack( &state_stack, &temp_stack ); } } } /* * call_timing_rule() - Look up the __TIMING_RULE__ variable on the given * target, and if non-empty, invoke the rule it names, passing the given * timing_info. */ static void call_timing_rule( TARGET * target, timing_info const * const time ) { LIST * timing_rule; pushsettings( root_module(), target->settings ); timing_rule = var_get( root_module(), constant_TIMING_RULE ); popsettings( root_module(), target->settings ); if ( !list_empty( timing_rule ) ) { /* rule timing-rule ( args * : target : start end user system ) */ /* Prepare the argument list. */ FRAME frame[ 1 ]; OBJECT * rulename = list_front( timing_rule ); frame_init( frame ); /* args * :: $(__TIMING_RULE__[2-]) */ lol_add( frame->args, list_copy_range( timing_rule, list_next( list_begin( timing_rule ) ), list_end( timing_rule ) ) ); /* target :: the name of the target */ lol_add( frame->args, list_new( object_copy( target->name ) ) ); /* start end user system :: info about the action command */ lol_add( frame->args, list_push_back( list_push_back( list_push_back( list_new( outf_time( &time->start ) ), outf_time( &time->end ) ), outf_double( time->user ) ), outf_double( time->system ) ) ); /* Call the rule. */ evaluate_rule( bindrule( rulename , root_module() ), rulename, frame ); /* Clean up. */ frame_free( frame ); } } /* * call_action_rule() - Look up the __ACTION_RULE__ variable on the given * target, and if non-empty, invoke the rule it names, passing the given info, * timing_info, executed command and command output. */ static void call_action_rule ( TARGET * target, int status, timing_info const * time, char const * executed_command, char const * command_output ) { LIST * action_rule; pushsettings( root_module(), target->settings ); action_rule = var_get( root_module(), constant_ACTION_RULE ); popsettings( root_module(), target->settings ); if ( !list_empty( action_rule ) ) { /* rule action-rule ( args * : target : command status start end user system : output ? ) */ /* Prepare the argument list. */ FRAME frame[ 1 ]; OBJECT * rulename = list_front( action_rule ); frame_init( frame ); /* args * :: $(__ACTION_RULE__[2-]) */ lol_add( frame->args, list_copy_range( action_rule, list_next( list_begin( action_rule ) ), list_end( action_rule ) ) ); /* target :: the name of the target */ lol_add( frame->args, list_new( object_copy( target->name ) ) ); /* command status start end user system :: info about the action command */ lol_add( frame->args, list_push_back( list_push_back( list_push_back( list_push_back( list_push_back( list_new( object_new( executed_command ) ), outf_int( status ) ), outf_time( &time->start ) ), outf_time( &time->end ) ), outf_double( time->user ) ), outf_double( time->system ) ) ); /* output ? :: the output of the action command */ if ( command_output ) lol_add( frame->args, list_new( object_new( command_output ) ) ); else lol_add( frame->args, L0 ); /* Call the rule. */ evaluate_rule( bindrule( rulename, root_module() ), rulename, frame ); /* Clean up. */ frame_free( frame ); } } /* * make1c_closure() - handle command execution completion and go to MAKE1C. * * Internal function passed as a notification callback for when a command * finishes getting executed by the OS or called directly when faking that a * command had been executed by the OS. * * Now all we need to do is fiddle with the command exit status and push a new * MAKE1C state to execute the next command scheduled for building this target * or close up the target's build process in case there are no more commands * scheduled for it. On interrupts, we bail heavily. */ static void make1c_closure ( void * const closure, int status_orig, timing_info const * const time, char const * const cmd_stdout, char const * const cmd_stderr, int const cmd_exit_reason ) { TARGET * const t = (TARGET *)closure; CMD * const cmd = (CMD *)t->cmds; char const * rule_name = 0; char const * target_name = 0; assert( cmd ); --cmdsrunning; /* Calculate the target's status from the cmd execution result. */ { /* Store the target's status. */ t->status = status_orig; /* Invert OK/FAIL target status when FAIL_EXPECTED has been applied. */ if ( t->flags & T_FLAG_FAIL_EXPECTED && !globs.noexec ) { switch ( t->status ) { case EXEC_CMD_FAIL: t->status = EXEC_CMD_OK; break; case EXEC_CMD_OK: t->status = EXEC_CMD_FAIL; break; } } /* Ignore failures for actions marked as 'ignore'. */ if ( t->status == EXEC_CMD_FAIL && cmd->rule->actions->flags & RULE_IGNORE ) t->status = EXEC_CMD_OK; } if ( DEBUG_MAKEQ || ( DEBUG_MAKE && !( cmd->rule->actions->flags & RULE_QUIETLY ) ) ) { rule_name = object_str( cmd->rule->name ); target_name = object_str( list_front( lol_get( (LOL *)&cmd->args, 0 ) ) ); } out_action( rule_name, target_name, cmd->buf->value, cmd_stdout, cmd_stderr, cmd_exit_reason ); if ( !globs.noexec ) { call_timing_rule( t, time ); if ( DEBUG_EXECCMD ) printf( "%f sec system; %f sec user\n", time->system, time->user ); /* Assume -p0 is in effect, i.e. cmd_stdout contains merged output. */ call_action_rule( t, status_orig, time, cmd->buf->value, cmd_stdout ); } /* Print command text on failure. */ if ( t->status == EXEC_CMD_FAIL && DEBUG_MAKE ) { if ( !DEBUG_EXEC ) printf( "%s\n", cmd->buf->value ); printf( "...failed %s ", object_str( cmd->rule->name ) ); list_print( lol_get( (LOL *)&cmd->args, 0 ) ); printf( "...\n" ); } /* On interrupt, set quit so _everything_ fails. Do the same for failed * commands if we were asked to stop the build in case of any errors. */ if ( t->status == EXEC_CMD_INTR ) { ++intr; ++quit; } if ( t->status == EXEC_CMD_FAIL && globs.quitquick ) ++quit; /* If the command was not successful remove all of its targets not marked as * "precious". */ if ( t->status != EXEC_CMD_OK ) { LIST * const targets = lol_get( (LOL *)&cmd->args, 0 ); LISTITER iter = list_begin( targets ); LISTITER const end = list_end( targets ); for ( ; iter != end; iter = list_next( iter ) ) { char const * const filename = object_str( list_item( iter ) ); TARGET const * const t = bindtarget( list_item( iter ) ); if ( !( t->flags & T_FLAG_PRECIOUS ) && !unlink( filename ) ) printf( "...removing %s\n", filename ); } } #ifdef OPT_SEMAPHORE /* Release any semaphores used by this action. */ cmd_sem_unlock( t ); #endif /* Free this command and push the MAKE1C state to execute the next one * scheduled for building this same target. */ t->cmds = NULL; push_cmds( cmd->next, t->status ); cmd_free( cmd ); } /* 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->impl.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 if ( DEBUG_EXECCMD ) { TARGET * first_target = bindtarget( list_front( lol_get( &next_cmd->args, 0 ) ) ); printf( "Delaying %s %s: %d targets not ready\n", object_str( next_cmd->rule->name ), object_str( first_target->boundname ), next_cmd->asynccnt ); } } else { /* This is a target that we're finished updating */ TARGET * updated_target = cmd_iter->impl.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 */ static void swap_settings ( module_t * * current_module, TARGET * * current_target, module_t * new_module, TARGET * new_target ) { if ( ( new_target == *current_target ) && ( new_module == *current_module ) ) return; if ( *current_target ) popsettings( *current_module, (*current_target)->settings ); if ( new_target ) pushsettings( new_module, new_target->settings ); *current_module = new_module; *current_target = new_target; } /* * make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc. * * Essentially copies a chain of ACTIONs to a chain of CMDs, grouping * RULE_TOGETHER actions, splitting RULE_PIECEMEAL actions, and handling * RULE_NEWSRCS actions. The result is a chain of CMDs which has already had all * of its embedded variable references expanded and can now be executed using * exec_cmd(). */ static CMD * make1cmds( TARGET * t ) { CMD * cmds = 0; CMD * last_cmd; LIST * shell = L0; module_t * settings_module = 0; TARGET * settings_target = 0; ACTIONS * a0; int const running_flag = globs.noexec ? A_RUNNING_NOEXEC : A_RUNNING; /* Step through actions. */ for ( a0 = t->actions; a0; a0 = a0->next ) { RULE * rule = a0->action->rule; rule_actions * actions = rule->actions; SETTINGS * boundvars; LIST * nt; LIST * ns; ACTIONS * a1; /* Only do rules with commands to execute. */ if ( !actions ) continue; if ( a0->action->running >= running_flag ) { CMD * first; /* If this action was skipped either because it was * combined with another action by RULE_TOGETHER, or * because all of its sources were filtered out, * then we don't have anything to do here. */ if ( a0->action->first_cmd == NULL ) 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 * specified for this rule, tack on sources from each instance of this * rule for this target. */ nt = make1list( L0, a0->action->targets, 0 ); ns = make1list( L0, a0->action->sources, actions->flags ); if ( actions->flags & RULE_TOGETHER ) for ( a1 = a0->next; a1; a1 = a1->next ) if ( a1->action->rule == rule && a1->action->running < running_flag && targets_equal( a0->action->targets, a1->action->targets ) ) { ns = make1list( ns, a1->action->sources, actions->flags ); a1->action->running = running_flag; } /* If doing only updated (or existing) sources, but none have been * updated (or exist), skip this action. */ if ( list_empty( ns ) && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) ) { list_free( nt ); continue; } swap_settings( &settings_module, &settings_target, rule->module, t ); if ( list_empty( shell ) ) { /* shell is per-target */ shell = var_get( rule->module, constant_JAMSHELL ); } /* If we had 'actions xxx bind vars' we bind the vars now. */ boundvars = make1settings( rule->module, actions->bindlist ); pushsettings( rule->module, boundvars ); /* * Build command, starting with all source args. * * For actions that allow PIECEMEAL commands, if the constructed command * string is too long, we retry constructing it with a reduced number of * source arguments presented. * * While reducing slowly takes a bit of compute time to get things just * right, it is worth it to get as close to maximum allowed command * string length as possible, because launching the commands we are * executing is likely to be much more compute intensive. * * Note that we loop through at least once, for sourceless actions. */ { 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 * semaphores = NULL; TARGETS * targets_iter; int unique_targets; do { CMD * cmd; int cmd_check_result; int cmd_error_length; int cmd_error_max_length; int retry = 0; int accept_command = 0; /* Build cmd: cmd_new() takes ownership of its lists. */ if ( list_empty( cmd_targets ) ) cmd_targets = list_copy( nt ); if ( list_empty( cmd_shell ) ) cmd_shell = list_copy( shell ); cmd = cmd_new( rule, cmd_targets, list_sublist( ns, start, chunk ), cmd_shell ); cmd_check_result = exec_check( cmd->buf, &cmd->shell, &cmd_error_length, &cmd_error_max_length ); if ( cmd_check_result == EXEC_CHECK_OK ) { accept_command = 1; } else if ( cmd_check_result == EXEC_CHECK_NOOP ) { accept_command = 1; cmd->noop = 1; } else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) ) { /* Too long but splittable. Reduce chunk size slowly and * retry. */ assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); chunk = chunk * 9 / 10; retry = 1; } else { /* Too long and not splittable. */ char const * const error_message = cmd_check_result == EXEC_CHECK_TOO_LONG ? "is too long" : "contains a line that is too long"; assert( cmd_check_result == EXEC_CHECK_TOO_LONG || cmd_check_result == EXEC_CHECK_LINE_TOO_LONG ); printf( "%s action %s (%d, max %d):\n", object_str( rule->name ), error_message, cmd_error_length, cmd_error_max_length ); /* Tell the user what did not fit. */ fputs( cmd->buf->value, stdout ); exit( EXITBAD ); } assert( !retry || !accept_command ); if ( accept_command ) { /* Chain it up. */ 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. */ cmd_targets = L0; cmd_shell = L0; } else { /* We can reuse targets & shell lists for the next command * if we do not let them die with this cmd object. */ cmd_release_targets_and_shell( cmd ); cmd_free( cmd ); } if ( !retry ) start += chunk; } while ( start < length ); /* Record the end of the actions cmds */ a0->action->last_cmd = last_cmd; unique_targets = 0; for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next ) { if ( targets_contains( targets_iter->next, targets_iter->target ) ) continue; /* Add all targets produced by the action to the update list. */ push_state( &state_stack, targets_iter->target, NULL, T_STATE_MAKE1A ); ++unique_targets; } /* We need to wait until all the targets agree that * it's okay to run this action. */ ( ( CMD * )a0->action->first_cmd )->asynccnt = unique_targets; #if OPT_SEMAPHORE /* Collect semaphores */ for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next ) { TARGET * sem = targets_iter->target->semaphore; if ( sem ) { TARGETS * semiter; if ( ! targets_contains( semaphores, sem ) ) semaphores = targetentry( semaphores, sem ); } } ( ( CMD * )a0->action->first_cmd )->lock = semaphores; ( ( CMD * )a0->action->last_cmd )->unlock = semaphores; #endif } /* These were always copied when used. */ list_free( nt ); list_free( ns ); /* Free variables with values bound by 'actions xxx bind vars'. */ popsettings( rule->module, boundvars ); freesettings( boundvars ); } if ( cmds ) { last_cmd->next = cmdlist_append_target( last_cmd->next, t ); } swap_settings( &settings_module, &settings_target, 0, 0 ); return cmds; } /* * make1list() - turn a list of targets into a LIST, for $(<) and $(>) */ static LIST * make1list( LIST * l, TARGETS * targets, int flags ) { for ( ; targets; targets = targets->next ) { TARGET * t = targets->target; if ( t->binding == T_BIND_UNBOUND ) make1bind( t ); if ( ( flags & RULE_EXISTING ) && ( flags & RULE_NEWSRCS ) ) { if ( ( t->binding != T_BIND_EXISTS ) && ( t->fate <= T_FATE_STABLE ) ) continue; } else if ( flags & RULE_EXISTING ) { if ( t->binding != T_BIND_EXISTS ) continue; } else if ( flags & RULE_NEWSRCS ) { if ( t->fate <= T_FATE_STABLE ) continue; } /* Prohibit duplicates for RULE_TOGETHER. */ if ( flags & RULE_TOGETHER ) { LISTITER iter = list_begin( l ); LISTITER const end = list_end( l ); for ( ; iter != end; iter = list_next( iter ) ) if ( object_equal( list_item( iter ), t->boundname ) ) break; if ( iter != end ) continue; } /* Build new list. */ l = list_push_back( l, object_copy( t->boundname ) ); } return l; } /* * make1settings() - for vars with bound values, build up replacement lists */ static SETTINGS * make1settings( struct module_t * module, LIST * vars ) { SETTINGS * settings = 0; LISTITER vars_iter = list_begin( vars ); LISTITER const vars_end = list_end( vars ); for ( ; vars_iter != vars_end; vars_iter = list_next( vars_iter ) ) { LIST * const l = var_get( module, list_item( vars_iter ) ); LIST * nl = L0; LISTITER iter = list_begin( l ); LISTITER const end = list_end( l ); for ( ; iter != end; iter = list_next( iter ) ) { TARGET * const t = bindtarget( list_item( iter ) ); /* Make sure the target is bound. */ if ( t->binding == T_BIND_UNBOUND ) make1bind( t ); /* Build a new list. */ nl = list_push_back( nl, object_copy( t->boundname ) ); } /* Add to settings chain. */ settings = addsettings( settings, VAR_SET, list_item( vars_iter ), nl ); } return settings; } /* * make1bind() - bind targets that were not bound during dependency analysis * * Spot the kludge! If a target is not in the dependency tree, it did not get * bound by make0(), so we have to do it here. Ugly. */ static void make1bind( TARGET * t ) { if ( t->flags & T_FLAG_NOTFILE ) return; pushsettings( root_module(), t->settings ); object_free( t->boundname ); t->boundname = search( t->name, &t->time, 0, t->flags & T_FLAG_ISFILE ); t->binding = timestamp_empty( &t->time ) ? T_BIND_MISSING : T_BIND_EXISTS; popsettings( root_module(), t->settings ); } static int targets_contains( TARGETS * l, TARGET * t ) { for ( ; l; l = l->next ) { if ( t == l->target ) { return 1; } } return 0; } static int targets_equal( TARGETS * l1, TARGETS * l2 ) { for ( ; l1 && l2; l1 = l1->next, l2 = l2->next ) { if ( l1->target != l2->target ) return 0; } return !l1 && !l2; } #ifdef OPT_SEMAPHORE static int cmd_sem_lock( TARGET * t ) { CMD * cmd = (CMD *)t->cmds; TARGETS * iter; /* Check whether all the semaphores required for updating * this target are free. */ for ( iter = cmd->lock; iter; iter = iter->next ) { if ( iter->target->asynccnt > 0 ) { if ( DEBUG_EXECCMD ) printf( "SEM: %s is busy, delaying launch of %s\n", object_str( iter->target->name ), object_str( t->name ) ); iter->target->parents = targetentry( iter->target->parents, t ); return 0; } } /* Lock the semaphores. */ for ( iter = cmd->lock; iter; iter = iter->next ) { ++iter->target->asynccnt; if ( DEBUG_EXECCMD ) printf( "SEM: %s now used by %s\n", object_str( iter->target->name ), object_str( t->name ) ); } /* A cmd only needs to be locked around its execution. * clearing cmd->lock here makes it safe to call cmd_sem_lock * twice. */ cmd->lock = NULL; return 1; } static void cmd_sem_unlock( TARGET * t ) { CMD * cmd = ( CMD * )t->cmds; TARGETS * iter; /* Release the semaphores. */ for ( iter = cmd->unlock; iter; iter = iter->next ) { if ( DEBUG_EXECCMD ) printf( "SEM: %s is now free\n", object_str( iter->target->name ) ); --iter->target->asynccnt; assert( iter->target->asynccnt <= 0 ); } for ( iter = cmd->unlock; iter; iter = iter->next ) { /* Find a waiting target that's ready */ while ( iter->target->parents ) { TARGETS * first = iter->target->parents; TARGET * t1 = first->target; /* Pop the first waiting CMD */ if ( first->next ) first->next->tail = first->tail; iter->target->parents = first->next; BJAM_FREE( first ); if ( cmd_sem_lock( t1 ) ) { push_state( &state_stack, t1, NULL, T_STATE_MAKE1C ); break; } } } } #endif