Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/moses-smt/mosesdecoder.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'jam-files/engine/make1.c')
-rw-r--r--jam-files/engine/make1.c1021
1 files changed, 567 insertions, 454 deletions
diff --git a/jam-files/engine/make1.c b/jam-files/engine/make1.c
index 47132419a..71eee1231 100644
--- a/jam-files/engine/make1.c
+++ b/jam-files/engine/make1.c
@@ -11,69 +11,58 @@
*/
/*
- * make1.c - execute command to bring targets up to date
+ * make1.c - execute commands to bring targets up to date
*
- * This module contains make1(), the entry point called by make() to
- * recursively decend the dependency graph executing update actions as
- * marked by make0().
+ * 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.
+ * make1() - execute commands to update a TARGET and all of its dependencies
*
* Internal routines, the recursive/asynchronous command executors:
- *
- * make1a() - recursively traverse dependency target tree, calling make1b().
- * make1atail() - started processing all dependencies so go on to make1b().
- * make1b() - when dependencies are up to date, build target with make1c().
- * make1c() - launch target's next command, call parents' make1b() if none.
- * make1d() - handle command execution completion and call back make1c().
+ * 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 that get bound values, build up replacement lists.
- * make1bind() - bind targets that weren't bound in dependency analysis.
- *
- * 04/16/94 (seiwald) - Split from make.c.
- * 04/21/94 (seiwald) - Handle empty "updated" actions.
- * 05/04/94 (seiwald) - async multiprocess (-j) support.
- * 06/01/94 (seiwald) - new 'actions existing' does existing sources.
- * 12/20/94 (seiwald) - NOTIME renamed NOTFILE.
- * 01/19/95 (seiwald) - distinguish between CANTFIND/CANTMAKE targets.
- * 01/22/94 (seiwald) - pass per-target JAMSHELL down to exec_cmd().
- * 02/28/95 (seiwald) - Handle empty "existing" actions.
- * 03/10/95 (seiwald) - Fancy counts.
+ * 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 "assert.h"
-#include "variable.h"
#include "rules.h"
-#include "headers.h"
-
#include "search.h"
-#include "object.h"
-#include "make.h"
-#include "command.h"
-#include "execcmd.h"
-#include "compile.h"
-#include "output.h"
+#include "variable.h"
+#include <assert.h>
#include <stdlib.h>
-#if ! defined(NT) || defined(__GNUC__)
+#if !defined( NT ) || defined( __GNUC__ )
#include <unistd.h> /* for unlink */
#endif
-static CMD * make1cmds ( TARGET * );
-static LIST * make1list ( LIST *, TARGETS *, int flags );
-static SETTINGS * make1settings( struct module_t * module, LIST * vars );
-static void make1bind ( TARGET * );
+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 );
/* Ugly static - it is too hard to carry it through the callbacks. */
@@ -83,31 +72,29 @@ static struct
int skipped;
int total;
int made;
-} counts[ 1 ] ;
+} counts[ 1 ];
-/* Target state - remove recursive calls by just keeping track of state target
- * is in.
- */
-typedef struct _state
+/* 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
{
- struct _state * prev; /* previous state on stack */
- TARGET * t; /* current target */
- TARGET * parent; /* parent argument necessary for make1a() */
-#define T_STATE_MAKE1A 0 /* make1a() should be called */
-#define T_STATE_MAKE1ATAIL 1 /* make1atail() should be called */
-#define T_STATE_MAKE1B 2 /* make1b() should be called */
-#define T_STATE_MAKE1C 3 /* make1c() should be called */
-#define T_STATE_MAKE1D 4 /* make1d() should be called */
- int curstate; /* current state */
- int status;
-} state;
-
-static void make1a ( state * );
-static void make1atail ( state * );
-static void make1b ( state * );
-static void make1c ( state * );
-static void make1d ( state * );
-static void make_closure( void * closure, int status, timing_info *, const char *, const char * );
+ 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
{
@@ -118,22 +105,24 @@ static stack state_stack = { NULL };
static state * state_freelist = NULL;
+/* Currently running command counter. */
+static int cmdsrunning;
+
static state * alloc_state()
{
- if ( state_freelist != NULL )
+ if ( state_freelist )
{
- state * pState = 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 * pState )
+static void free_state( state * const pState )
{
pState->prev = state_freelist;
state_freelist = pState;
@@ -142,44 +131,41 @@ static void free_state( state * pState )
static void clear_state_freelist()
{
- while ( state_freelist != NULL )
+ while ( state_freelist )
{
- state * pState = state_freelist;
+ state * const pState = state_freelist;
state_freelist = state_freelist->prev;
BJAM_FREE( pState );
}
}
-static state * current_state( stack * pStack )
+static state * current_state( stack * const pStack )
{
return pStack->stack;
}
-static void pop_state( stack * pStack )
+static void pop_state( stack * const pStack )
{
- if ( pStack->stack != NULL )
+ if ( pStack->stack )
{
- state * pState = pStack->stack->prev;
+ state * const pState = pStack->stack->prev;
free_state( pStack->stack );
pStack->stack = pState;
}
}
-static state * push_state( stack * pStack, TARGET * t, TARGET * parent, int curstate )
+static state * push_state( stack * const pStack, TARGET * const t,
+ TARGET * const parent, int const curstate )
{
- state * pState = alloc_state();
-
+ state * const pState = alloc_state();
pState->t = t;
pState->parent = parent;
pState->prev = pStack->stack;
pState->curstate = curstate;
-
- pStack->stack = pState;
-
- return pStack->stack;
+ return pStack->stack = pState;
}
@@ -187,12 +173,12 @@ static state * push_state( stack * pStack, TARGET * t, TARGET * parent, int curs
* Pushes a stack onto another stack, effectively reversing the order.
*/
-static void push_stack_on_stack( stack * pDest, stack * pSrc )
+static void push_stack_on_stack( stack * const pDest, stack * const pSrc )
{
- while ( pSrc->stack != NULL )
+ while ( pSrc->stack )
{
- state * pState = pSrc->stack;
- pSrc->stack = pSrc->stack->prev;
+ state * const pState = pSrc->stack;
+ pSrc->stack = pState->prev;
pState->prev = pDest->stack;
pDest->stack = pState;
}
@@ -200,181 +186,203 @@ static void push_stack_on_stack( stack * pDest, stack * pSrc )
/*
- * make1() - execute commands to update a TARGET and all of its dependencies.
+ * make1() - execute commands to update a list of targets and all of their dependencies
*/
static int intr = 0;
+static int quit = 0;
-int make1( TARGET * t )
+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. */
- push_state( &state_stack, t, NULL, T_STATE_MAKE1A );
- do
+ while ( 1 )
{
- while ( ( pState = current_state( &state_stack ) ) != NULL )
+ while ( ( pState = current_state( &state_stack ) ) )
{
- if ( intr )
+ if ( quit )
pop_state( &state_stack );
switch ( pState->curstate )
{
- case T_STATE_MAKE1A : make1a ( pState ); break;
- case T_STATE_MAKE1ATAIL: make1atail( pState ); break;
- case T_STATE_MAKE1B : make1b ( pState ); break;
- case T_STATE_MAKE1C : make1c ( pState ); break;
- case T_STATE_MAKE1D : make1d ( pState ); break;
+ 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();
}
- /* Wait for any outstanding commands to finish running. */
- while ( exec_wait() );
clear_state_freelist();
/* Talk about it. */
if ( counts->failed )
printf( "...failed updating %d target%s...\n", counts->failed,
- counts->failed > 1 ? "s" : "" );
+ counts->failed > 1 ? "s" : "" );
if ( DEBUG_MAKE && counts->skipped )
printf( "...skipped %d target%s...\n", counts->skipped,
- counts->skipped > 1 ? "s" : "" );
+ counts->skipped > 1 ? "s" : "" );
if ( DEBUG_MAKE && counts->made )
printf( "...updated %d target%s...\n", counts->made,
- counts->made > 1 ? "s" : "" );
+ counts->made > 1 ? "s" : "" );
- return counts->total != counts->made;
+ /* 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 traverse target tree, calling make1b().
+ * 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. Once all of its dependencies have started being processed goes
- * on and calls make1b() (actually does that indirectly via a helper
- * make1atail() state).
+ * dependencies.
*/
-static void make1a( state * pState )
+static void make1a( state * const pState )
{
TARGET * t = pState->t;
- TARGETS * c;
+ 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 is built.
+ * in the MAKE1C quagmire, arrange for the parent to be notified when this
+ * target has been built.
*/
- if ( pState->parent )
- switch ( pState->t->progress )
+ if ( pState->parent && t->progress <= T_MAKE_RUNNING )
+ {
+ TARGET * const parent_scc = target_scc( pState->parent );
+ if ( t != parent_scc )
{
- case T_MAKE_INIT:
- case T_MAKE_ACTIVE:
- case T_MAKE_RUNNING:
- pState->t->parents = targetentry( pState->t->parents,
- pState->parent );
- ++pState->parent->asynccnt;
+ t->parents = targetentry( t->parents, parent_scc );
+ ++parent_scc->asynccnt;
}
+ }
- /*
- * If the target has been previously updated with -n in
- * effect, and we're ignoring -n, update it for real.
+ /* 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 && pState->t->progress == T_MAKE_NOEXEC_DONE )
- {
- pState->t->progress = T_MAKE_INIT;
- }
+ 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 ( pState->t->progress != T_MAKE_INIT )
+ if ( t->progress != T_MAKE_INIT )
{
pop_state( &state_stack );
return;
}
- /* Asynccnt counts the dependencies preventing this target from proceeding
- * to make1b() 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 accounted for when we call make1b() ourselves,
- * below. Without this if a a dependency gets built before we finish
+ /* 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.
*/
- pState->t->asynccnt = 1;
-
- /* Add header nodes created during the building process. */
- {
- TARGETS * inc = 0;
- for ( c = t->depends; c; c = c->next )
- if ( c->target->rescanned && c->target->includes )
- inc = targetentry( inc, c->target->includes );
- t->depends = targetchain( t->depends, inc );
- }
-
- /* Guard against circular dependencies. */
- pState->t->progress = T_MAKE_ONSTACK;
+ t->asynccnt = 1;
+ /* Push dependency build requests (to be executed in the natural order). */
{
stack temp_stack = { NULL };
- for ( c = t->depends; c && !intr; c = c->next )
- push_state( &temp_stack, c->target, pState->t, T_STATE_MAKE1A );
-
- /* Using stacks reverses the order of execution. Reverse it back. */
+ 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 );
}
- pState->curstate = T_STATE_MAKE1ATAIL;
-}
-
-
-/*
- * make1atail() - started processing all dependencies so go on to make1b().
- */
+ t->progress = T_MAKE_ACTIVE;
-static void make1atail( state * pState )
-{
- pState->t->progress = T_MAKE_ACTIVE;
- /* Now that all of our dependencies have bumped up our asynccnt we can
- * remove our own internal bump added to prevent this target from being
- * built before all of its dependencies start getting processed.
+ /* 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() - when dependencies are up to date, build target with make1c().
+ * make1b() - if nothing is blocking this target's build, proceed to MAKE1C
*
- * Called after all dependencies have started being processed and after each of
- * them finishes its processing. The target actually goes on to getting built in
- * make1c() only after all of its dependencies have finished their processing.
+ * 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 * pState )
+static void make1b( state * const pState )
{
- TARGET * t = pState->t;
- TARGETS * c;
- TARGET * failed = 0;
- const char * failed_name = "dependencies";
+ TARGET * const t = pState->t;
+ TARGET * failed = 0;
+ char const * failed_name = "dependencies";
- /* If any dependencies are still outstanding, wait until they call make1b()
- * to signal their completion.
+ /* If any dependencies are still outstanding, wait until they signal their
+ * completion by pushing this same state for their parent targets.
*/
- if ( --pState->t->asynccnt )
+ if ( --t->asynccnt )
{
pop_state( &state_stack );
return;
}
/* Try to aquire a semaphore. If it is locked, wait until the target that
- * locked it is built and signal completition.
+ * locked it is built and signals completition.
*/
#ifdef OPT_SEMAPHORE
if ( t->semaphore && t->semaphore->asynccnt )
@@ -393,67 +401,64 @@ static void make1b( state * pState )
/* 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't be found or we got an interrupt,
- * we can't get here.
+ /* 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 ) )
+ if ( c->target->status > t->status && !( c->target->flags &
+ T_FLAG_NOCARE ) )
{
failed = c->target;
- pState->t->status = c->target->status;
+ 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 ( ( pState->t->status == EXEC_CMD_FAIL ) && pState->t->actions )
+ if ( ( t->status == EXEC_CMD_FAIL ) && t->actions )
{
++counts->skipped;
- if ( ( pState->t->flags & ( T_FLAG_RMOLD | T_FLAG_NOTFILE ) ) == T_FLAG_RMOLD )
+ if ( ( t->flags & ( T_FLAG_RMOLD | T_FLAG_NOTFILE ) ) == T_FLAG_RMOLD )
{
- if ( !unlink( object_str( pState->t->boundname ) ) )
- printf( "...removing outdated %s\n", object_str( pState->t->boundname ) );
+ 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( pState->t->name ), failed_name );
+ printf( "...skipped %s for lack of %s...\n", object_str( t->name ),
+ failed_name );
}
- if ( pState->t->status == EXEC_CMD_OK )
- switch ( pState->t->fate )
+ if ( t->status == EXEC_CMD_OK )
+ switch ( t->fate )
{
- /* These are handled by the default case below now
- case T_FATE_INIT:
- case T_FATE_MAKING:
- */
-
case T_FATE_STABLE:
case T_FATE_NEWER:
break;
case T_FATE_CANTFIND:
case T_FATE_CANTMAKE:
- pState->t->status = EXEC_CMD_FAIL;
+ t->status = EXEC_CMD_FAIL;
break;
case T_FATE_ISTMP:
if ( DEBUG_MAKE )
- printf( "...using %s...\n", object_str( pState->t->name ) );
+ printf( "...using %s...\n", object_str( t->name ) );
break;
case T_FATE_TOUCHED:
@@ -462,186 +467,215 @@ static void make1b( state * pState )
case T_FATE_OUTDATED:
case T_FATE_UPDATE:
case T_FATE_REBUILD:
- /* Prepare commands for executing actions scheduled for this target
- * and then schedule transfer to make1c() state to proceed with
- * executing the prepared commands. Commands have their embedded
- * variables automatically expanded, including making use of any "on
- * target" variables.
+ /* 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 ( pState->t->actions )
+ if ( t->actions )
{
++counts->total;
if ( DEBUG_MAKE && !( counts->total % 100 ) )
printf( "...on %dth target...\n", counts->total );
- pState->t->cmds = (char *)make1cmds( pState->t );
- /* Set the target's "progress" so that make1c() counts it among
- * its successes/failures.
+ t->cmds = (char *)make1cmds( t );
+ /* Update the target's "progress" so MAKE1C processing counts it
+ * among its successes/failures.
*/
- pState->t->progress = T_MAKE_RUNNING;
+ t->progress = T_MAKE_RUNNING;
}
break;
- /* All possible fates should have been accounted for by now. */
+ /* All valid fates should have been accounted for by now. */
default:
- printf( "ERROR: %s has bad fate %d", object_str( pState->t->name ),
- pState->t->fate );
+ printf( "ERROR: %s has bad fate %d", object_str( t->name ),
+ t->fate );
abort();
}
- /* Call make1c() to begin the execution of the chain of commands needed to
- * build the target. If we are not going to build the target (due of
- * dependency failures or no commands needing to be run) the chain will be
- * empty and make1c() will directly signal the target's completion.
- */
-
#ifdef OPT_SEMAPHORE
/* If there is a semaphore, indicate that it is in use. */
- if ( pState->t->semaphore )
+ if ( t->semaphore )
{
- ++pState->t->semaphore->asynccnt;
+ ++t->semaphore->asynccnt;
if ( DEBUG_EXECCMD )
- printf( "SEM: %s now used by %s\n", object_str( pState->t->semaphore->name ),
- object_str( pState->t->name ) );
+ printf( "SEM: %s now used by %s\n", object_str( t->semaphore->name
+ ), object_str( t->name ) );
}
#endif
+ /* 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.
+ */
+ /* Implementation note:
+ * Morfing the current state on the stack instead of popping it and
+ * 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;
}
/*
- * make1c() - launch target's next command, call parents' make1b() if none.
+ * 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(). If
- * there are no more commands to run, we collect the status from all the actions
- * and report our completion to all the parents.
+ * 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 * pState )
+static void make1c( state const * const pState )
{
- CMD * cmd = (CMD *)pState->t->cmds;
+ TARGET * const t = pState->t;
+ CMD * const cmd = (CMD *)t->cmds;
- if ( cmd && ( pState->t->status == EXEC_CMD_OK ) )
+ if ( cmd && t->status == EXEC_CMD_OK )
{
- const char * rule_name = 0;
- const char * target = 0;
+ /* 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 ( DEBUG_MAKEQ ||
- ( !( cmd->rule->actions->flags & RULE_QUIETLY ) && DEBUG_MAKE ) )
- {
- rule_name = object_str( cmd->rule->name );
- target = object_str( list_front( lol_get( &cmd->args, 0 ) ) );
- if ( globs.noexec )
- out_action( rule_name, target, cmd->buf->value, "", "", EXIT_OK );
- }
+ /* Increment the jobs running counter. */
+ ++cmdsrunning;
- if ( globs.noexec )
+ /* Execute the actual build command or fake it if no-op. */
+ if ( globs.noexec || cmd->noop )
{
- pState->curstate = T_STATE_MAKE1D;
- pState->status = EXEC_CMD_OK;
+ 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
{
- /* Pop state first because exec_cmd() could push state. */
- pop_state( &state_stack );
- exec_cmd( cmd->buf->value, make_closure, pState->t, cmd->shell, rule_name,
- target );
+ 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
{
- TARGETS * c;
ACTIONS * actions;
/* Collect status from actions, and distribute it as well. */
- for ( actions = pState->t->actions; actions; actions = actions->next )
- if ( actions->action->status > pState->t->status )
- pState->t->status = actions->action->status;
- for ( actions = pState->t->actions; actions; actions = actions->next )
- if ( pState->t->status > actions->action->status )
- actions->action->status = pState->t->status;
+ 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 ( pState->t->progress == T_MAKE_RUNNING )
- switch ( pState->t->status )
+ if ( t->progress == T_MAKE_RUNNING )
+ switch ( t->status )
{
- case EXEC_CMD_OK : ++counts->made ; break;
+ 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 * t = pState->t;
TARGET * additional_includes = NULL;
- if ( globs.noexec )
- t->progress = T_MAKE_NOEXEC_DONE;
- else
- t->progress = T_MAKE_DONE;
+ 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->rescanned )
+ if ( t->fate >= T_FATE_MISSING && t->status == EXEC_CMD_OK &&
+ !( t->flags & T_FLAG_INTERNAL ) )
{
TARGET * saved_includes;
- TARGET * target_to_rescan = t;
SETTINGS * s;
- target_to_rescan->rescanned = 1;
-
- if ( target_to_rescan->flags & T_FLAG_INTERNAL )
- target_to_rescan = t->original_target;
+ t->rescanned = 1;
/* Clean current includes. */
- saved_includes = target_to_rescan->includes;
- target_to_rescan->includes = 0;
+ saved_includes = t->includes;
+ t->includes = 0;
- s = copysettings( target_to_rescan->settings );
+ s = copysettings( t->settings );
pushsettings( root_module(), s );
- headers( target_to_rescan );
+ headers( t );
popsettings( root_module(), s );
freesettings( s );
- if ( target_to_rescan->includes )
+ if ( t->includes )
{
- /* Link the old includes on to make sure that it gets
- * cleaned up correctly.
- */
- target_to_rescan->includes->includes = saved_includes;
- target_to_rescan->includes->rescanned = 1;
/* Tricky. The parents have already been processed, but they
* have not seen the internal node, because it was just
- * created. We need to make the calls to make1a() that would
- * have been made by the parents here, and also make sure
- * all unprocessed parents will pick up the includes. We
- * must make sure processing of the additional make1a()
- * invocations is done before make1b() which means this
- * target is built, otherwise the parent would be considered
- * built before this make1a() processing has even started.
+ * 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( target_to_rescan->includes, target_to_rescan->parents->target, 0, 0, 0 );
- for ( c = target_to_rescan->parents; c; c = c->next )
+ 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,
- target_to_rescan->includes );
+ t->includes );
/* Will be processed below. */
- additional_includes = target_to_rescan->includes;
+ additional_includes = t->includes;
}
else
{
- target_to_rescan->includes = saved_includes;
+ 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 );
+ push_state( &temp_stack, additional_includes, c->target,
+ T_STATE_MAKE1A );
- for ( c = t->parents; c; c = c->next )
- push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B );
+ 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 );
+ }
#ifdef OPT_SEMAPHORE
/* If there is a semaphore, it is now free. */
@@ -651,22 +685,25 @@ static void make1c( state * pState )
--t->semaphore->asynccnt;
if ( DEBUG_EXECCMD )
- printf( "SEM: %s is now free\n", object_str( t->semaphore->name ) );
+ printf( "SEM: %s is now free\n", object_str(
+ t->semaphore->name ) );
/* If anything is waiting, notify the next target. There is no
- * point in notifying waiting targets, since they will be
+ * point in notifying all waiting targets, since they will be
* notified again.
*/
if ( t->semaphore->parents )
{
TARGETS * first = t->semaphore->parents;
+ t->semaphore->parents = first->next;
if ( first->next )
first->next->tail = first->tail;
- t->semaphore->parents = first->next;
if ( DEBUG_EXECCMD )
- printf( "SEM: placing %s on stack\n", object_str( first->target->name ) );
- push_state( &temp_stack, first->target, NULL, T_STATE_MAKE1B );
+ printf( "SEM: placing %s on stack\n", object_str(
+ first->target->name ) );
+ push_state( &temp_stack, first->target, NULL, T_STATE_MAKE1B
+ );
BJAM_FREE( first );
}
}
@@ -688,7 +725,7 @@ static void make1c( state * pState )
* timing_info.
*/
-static void call_timing_rule( TARGET * target, timing_info * time )
+static void call_timing_rule( TARGET * target, timing_info const * const time )
{
LIST * timing_rule;
@@ -702,23 +739,25 @@ static void call_timing_rule( TARGET * target, timing_info * time )
/* 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 ) ) );
+ 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_time( &time->start ) ),
+ outf_time( &time->end ) ),
+ outf_double( time->user ) ),
outf_double( time->system ) ) );
/* Call the rule. */
- evaluate_rule( list_front( timing_rule ), frame );
+ evaluate_rule( bindrule( rulename , root_module() ), rulename, frame );
/* Clean up. */
frame_free( frame );
@@ -734,14 +773,14 @@ static void call_timing_rule( TARGET * target, timing_info * time )
static void call_action_rule
(
- TARGET * target,
- int status,
- timing_info * time,
- const char * executed_command,
- const char * command_output
+ TARGET * target,
+ int status,
+ timing_info const * time,
+ char const * executed_command,
+ char const * command_output
)
{
- LIST * action_rule;
+ LIST * action_rule;
pushsettings( root_module(), target->settings );
action_rule = var_get( root_module(), constant_ACTION_RULE );
@@ -757,21 +796,24 @@ static void call_action_rule
/* 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 ) ) );
+ 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 */
+ /* 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_time( &time->start ) ),
+ outf_time( &time->end ) ),
outf_double( time->user ) ),
outf_double( time->system ) ) );
@@ -782,7 +824,7 @@ static void call_action_rule
lol_add( frame->args, L0 );
/* Call the rule. */
- evaluate_rule( list_front( action_rule ), frame );
+ evaluate_rule( bindrule( rulename, root_module() ), rulename, frame );
/* Clean up. */
frame_free( frame );
@@ -791,114 +833,130 @@ static void call_action_rule
/*
- * make_closure() - internal function passed as a notification callback for when
- * commands finish getting executed by the OS.
+ * 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 make_closure
+static void make1c_closure
(
- void * closure,
- int status,
- timing_info * time,
- const char * executed_command,
- const char * command_output
+ 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 * built = (TARGET *)closure;
+ TARGET * const t = (TARGET *)closure;
+ CMD * const cmd = (CMD *)t->cmds;
+ char const * rule_name = 0;
+ char const * target_name = 0;
- call_timing_rule( built, time );
- if ( DEBUG_EXECCMD )
- printf( "%f sec system; %f sec user\n", time->system, time->user );
+ assert( cmd );
- call_action_rule( built, status, time, executed_command, command_output );
-
- push_state( &state_stack, built, NULL, T_STATE_MAKE1D )->status = status;
-}
+ --cmdsrunning;
+ /* Calculate the target's status from the cmd execution result. */
+ {
+ /* Store the target's status. */
+ t->status = status_orig;
-/*
- * make1d() - handle command execution completion and call back make1c().
- *
- * exec_cmd() has completed and now all we need to do is fiddle with the status
- * and call back to make1c() so it can run 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.
- */
+ /* 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;
+ }
+ }
-static void make1d( state * pState )
-{
- TARGET * t = pState->t;
- CMD * cmd = (CMD *)t->cmds;
- int status = pState->status;
+ /* 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 ( t->flags & T_FLAG_FAIL_EXPECTED && !globs.noexec )
+ if ( DEBUG_MAKEQ ||
+ ( DEBUG_MAKE && !( cmd->rule->actions->flags & RULE_QUIETLY ) ) )
{
- /* Invert execution result when FAIL_EXPECTED has been applied. */
- switch ( status )
- {
- case EXEC_CMD_FAIL: status = EXEC_CMD_OK ; break;
- case EXEC_CMD_OK: status = EXEC_CMD_FAIL; break;
- }
+ rule_name = object_str( cmd->rule->name );
+ target_name = object_str( list_front( lol_get( (LOL *)&cmd->args, 0 ) )
+ );
}
- if ( ( status == EXEC_CMD_FAIL ) &&
- ( cmd->rule->actions->flags & RULE_IGNORE ) )
- status = EXEC_CMD_OK;
+ out_action( rule_name, target_name, cmd->buf->value, cmd_stdout, cmd_stderr,
+ cmd_exit_reason );
- /* On interrupt, set intr so _everything_ fails. */
- if ( status == EXEC_CMD_INTR )
- ++intr;
+ 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 ( ( status == EXEC_CMD_FAIL ) && DEBUG_MAKE )
+ 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( &cmd->args, 0 ) );
+ list_print( lol_get( (LOL *)&cmd->args, 0 ) );
printf( "...\n" );
}
- /* Treat failed commands as interrupts in case we were asked to stop the
- * build in case of any errors.
+ /* 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 ( ( status == EXEC_CMD_FAIL ) && globs.quitquick )
+ if ( t->status == EXEC_CMD_INTR )
+ {
++intr;
+ ++quit;
+ }
+ if ( t->status == EXEC_CMD_FAIL && globs.quitquick )
+ ++quit;
- /* If the command was interrupted or failed and the target is not
- * "precious", remove the targets.
+ /* If the command was not successful remove all of its targets not marked as
+ * "precious".
*/
- if (status != EXEC_CMD_OK)
+ if ( t->status != EXEC_CMD_OK )
{
- LIST * targets = lol_get( &cmd->args, 0 );
- LISTITER iter = list_begin( targets ), end = list_end( targets );
+ 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 ) )
{
- int need_unlink = 1;
- TARGET* t = bindtarget ( list_item( iter ) );
- if (t->flags & T_FLAG_PRECIOUS)
- {
- need_unlink = 0;
- }
- if (need_unlink && !unlink( object_str( list_item( iter ) ) ) )
- printf( "...removing %s\n", object_str( list_item( 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 );
}
}
- /* Free this command and call make1c() to move onto the next one scheduled
- * for building this same target.
+ /* Free this command and push the MAKE1C state to execute the next one
+ * scheduled for building this same target.
*/
- t->status = status;
t->cmds = (char *)cmd_next( cmd );
cmd_free( cmd );
- pState->curstate = T_STATE_MAKE1C;
+ push_state( &state_stack, t, NULL, T_STATE_MAKE1C );
}
/*
* swap_settings() - replace the settings from the current module and target
- * with those from the new module and target
+ * with those from the new module and target
*/
static void swap_settings
@@ -909,7 +967,8 @@ static void swap_settings
TARGET * new_target
)
{
- if ( ( new_target == *current_target ) && ( new_module == *current_module ) )
+ if ( ( new_target == *current_target ) &&
+ ( new_module == *current_module ) )
return;
if ( *current_target )
@@ -928,23 +987,25 @@ static void swap_settings
*
* 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 can be expanded by
- * var_string() and executed using exec_cmd().
+ * 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;
- LIST * shell = L0;
+ CMD * cmds = 0;
+ CMD * * cmds_next = &cmds;
+ LIST * shell = L0;
module_t * settings_module = 0;
- TARGET * settings_target = 0;
- ACTIONS * a0;
- int running_flag = globs.noexec ? A_RUNNING_NOEXEC : A_RUNNING;
+ TARGET * settings_target = 0;
+ ACTIONS * a0;
+ int const running_flag = globs.noexec ? A_RUNNING_NOEXEC : A_RUNNING;
/* Step through actions. Actions may be shared with other targets or grouped
* using RULE_TOGETHER, so actions already seen are skipped.
*/
- for ( a0 = t->actions ; a0; a0 = a0->next )
+ for ( a0 = t->actions; a0; a0 = a0->next )
{
RULE * rule = a0->action->rule;
rule_actions * actions = rule->actions;
@@ -952,9 +1013,6 @@ static CMD * make1cmds( TARGET * t )
LIST * nt;
LIST * ns;
ACTIONS * a1;
- int start;
- int chunk;
- int length;
/* Only do rules with commands to execute. If this action has already
* been executed, use saved status.
@@ -972,7 +1030,8 @@ static CMD * make1cmds( TARGET * t )
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 )
+ if ( a1->action->rule == rule &&
+ a1->action->running < running_flag )
{
ns = make1list( ns, a1->action->sources, actions->flags );
a1->action->running = running_flag;
@@ -981,7 +1040,8 @@ static CMD * make1cmds( TARGET * t )
/* 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 ) ) )
+ if ( list_empty( ns ) &&
+ ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) )
{
list_free( nt );
continue;
@@ -990,7 +1050,8 @@ static CMD * make1cmds( TARGET * t )
swap_settings( &settings_module, &settings_target, rule->module, t );
if ( list_empty( shell ) )
{
- shell = var_get( rule->module, constant_JAMSHELL ); /* shell is per-target */
+ /* shell is per-target */
+ shell = var_get( rule->module, constant_JAMSHELL );
}
/* If we had 'actions xxx bind vars' we bind the vars now. */
@@ -1000,66 +1061,112 @@ static CMD * make1cmds( TARGET * t )
/*
* Build command, starting with all source args.
*
- * If cmd_new returns 0, it is because the resulting command length is
- * > MAXLINE. In this case, we will slowly reduce the number of source
- * arguments presented until it does fit. This only applies to actions
- * that allow PIECEMEAL commands.
+ * 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 MAXLINE as possible, because
- * launching the commands we are executing is likely to be much more
- * compute intensive.
+ * 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 we loop through at least once, for sourceless actions.
+ * Note that we loop through at least once, for sourceless actions.
*/
+ {
+ int const length = list_length( ns );
+ int start = 0;
+ int chunk = length;
+ LIST * cmd_targets = L0;
+ LIST * cmd_shell = L0;
+ 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 );
+ }
- start = 0;
- chunk = length = list_length( ns );
+ assert( !retry || !accept_command );
- do
- {
- /* Build cmd: cmd_new consumes its lists. */
- CMD * cmd = cmd_new( rule,
- list_copy( nt ),
- list_sublist( ns, start, chunk ),
- list_copy( shell ) );
+ if ( accept_command )
+ {
+ /* Chain it up. */
+ *cmds_next = cmd;
+ cmds_next = &cmd->next;
- if ( cmd )
- {
- /* It fit: chain it up. */
- if ( !cmds ) cmds = cmd;
- else cmds->tail->next = cmd;
- cmds->tail = cmd;
- start += chunk;
- }
- else if ( ( actions->flags & RULE_PIECEMEAL ) && ( chunk > 1 ) )
- {
- /* Reduce chunk size slowly. */
- chunk = chunk * 9 / 10;
- }
- else
- {
- /* Too long and not splittable. */
- printf( "%s actions too long (max %d):\n", object_str( rule->name ), MAXLINE
- );
+ /* 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 );
+ }
- /* Tell the user what didn't fit. */
- cmd = cmd_new( rule, list_copy( nt ),
- list_sublist( ns, start, chunk ),
- list_new( object_copy( constant_percent ) ) );
- fputs( cmd->buf->value, stdout );
- exit( EXITBAD );
+ if ( !retry )
+ start += chunk;
}
+ while ( start < length );
}
- while ( start < length );
/* These were always copied when used. */
list_free( nt );
list_free( ns );
- /* Free the variables whose values were bound by 'actions xxx bind
- * vars'.
- */
+ /* Free variables with values bound by 'actions xxx bind vars'. */
popsettings( rule->module, boundvars );
freesettings( boundvars );
}
@@ -1070,7 +1177,7 @@ static CMD * make1cmds( TARGET * t )
/*
- * make1list() - turn a list of targets into a LIST, for $(<) and $(>).
+ * make1list() - turn a list of targets into a LIST, for $(<) and $(>)
*/
static LIST * make1list( LIST * l, TARGETS * targets, int flags )
@@ -1084,22 +1191,26 @@ static LIST * make1list( LIST * l, TARGETS * targets, int flags )
if ( ( flags & RULE_EXISTING ) && ( flags & RULE_NEWSRCS ) )
{
- if ( ( t->binding != T_BIND_EXISTS ) && ( t->fate <= T_FATE_STABLE ) )
+ if ( ( t->binding != T_BIND_EXISTS ) &&
+ ( t->fate <= T_FATE_STABLE ) )
continue;
}
- else
+ else if ( flags & RULE_EXISTING )
{
- if ( ( flags & RULE_EXISTING ) && ( t->binding != T_BIND_EXISTS ) )
+ if ( t->binding != T_BIND_EXISTS )
continue;
-
- if ( ( flags & RULE_NEWSRCS ) && ( t->fate <= T_FATE_STABLE ) )
+ }
+ 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 ), end = list_end( l );
+ 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;
@@ -1116,23 +1227,25 @@ static LIST * make1list( LIST * l, TARGETS * targets, int flags )
/*
- * make1settings() - for vars that get bound values, build up replacement lists.
+ * 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 ), vars_end = list_end( vars );
+ 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 * l = var_get( module, list_item( vars_iter ) );
+ LIST * const l = var_get( module, list_item( vars_iter ) );
LIST * nl = L0;
- LISTITER iter = list_begin( l ), end = list_end( l );
+ LISTITER iter = list_begin( l );
+ LISTITER const end = list_end( l );
for ( ; iter != end; iter = list_next( iter ) )
{
- TARGET * t = bindtarget( list_item( iter ) );
+ TARGET * const t = bindtarget( list_item( iter ) );
/* Make sure the target is bound. */
if ( t->binding == T_BIND_UNBOUND )
@@ -1164,7 +1277,7 @@ static void make1bind( TARGET * t )
pushsettings( root_module(), t->settings );
object_free( t->boundname );
- t->boundname = search( t->name, &t->time, 0, ( t->flags & T_FLAG_ISFILE ) );
- t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
+ 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 );
}