2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-14 00:32:11 +00:00

Implement running action commands through pipes (-p option) to fix jumbled output when using parallel execution with -j option. This is implemented for Unix variants, and Windows (Win32/NT). [ticket #994]

[SVN r38009]
This commit is contained in:
Rene Rivera
2007-06-16 20:52:36 +00:00
parent 187a4d7fa0
commit b3128a55bd
13 changed files with 1139 additions and 910 deletions

View File

@@ -350,7 +350,7 @@ set MKJAMBASE_SOURCES=mkjambase.c
set BJAM_SOURCES=
set BJAM_SOURCES=%BJAM_SOURCES% command.c compile.c debug.c execnt.c expand.c filent.c glob.c hash.c
set BJAM_SOURCES=%BJAM_SOURCES% hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c
set BJAM_SOURCES=%BJAM_SOURCES% newstr.c option.c parse.c pathunix.c regexp.c
set BJAM_SOURCES=%BJAM_SOURCES% newstr.c option.c output.c parse.c pathunix.c regexp.c
set BJAM_SOURCES=%BJAM_SOURCES% rules.c scan.c search.c subst.c timestamp.c variable.c modules.c
set BJAM_SOURCES=%BJAM_SOURCES% strings.c filesys.c builtins.c pwd.c class.c w32_getreg.c native.c
set BJAM_SOURCES=%BJAM_SOURCES% modules/set.c modules/path.c modules/regex.c

View File

@@ -256,20 +256,6 @@ toolset qcc qcc : "-o " : -D
[ opt --debug : -g -O0 -Wc,-fno-inline ]
-I$(--python-include) -I$(--extra-include)
: -L$(--python-lib[1]) -l$(--python-lib[2]) ;
## Qlogic Pathscale 2.4
toolset pathscale pathcc : "-o " : -D
:
[ opt --release : -s -Ofast -O3 ]
[ opt --debug : -g ]
-I$(--python-include) -I$(--extra-include)
: -L$(--python-lib[1]) -l$(--python-lib[2]) ;
## Portland Group Pgi 6.2
toolset pgi pgcc : "-o " : -D
:
[ opt --release : -s -O3 ]
[ opt --debug : -g ]
-I$(--python-include) -I$(--extra-include)
: -L$(--python-lib[1]) -l$(--python-lib[2]) ;
## Sun Workshop 6 C++
toolset sunpro cc : "-o " : -D
:
@@ -413,7 +399,7 @@ jam.source =
hash.c hcache.c headers.c hdrmacro.c
jam.c jambase.c jamgram.c
lists.c make.c make1.c mem.c newstr.c
option.c parse.c regexp.c rules.c
option.c output.c parse.c regexp.c rules.c
scan.c search.c subst.c w32_getreg.c
timestamp.c variable.c modules.c strings.c filesys.c
builtins.c pwd.c class.c native.c modules/set.c

View File

@@ -30,7 +30,7 @@ error_exit ()
echo "###"
echo "### Toolsets supported by this script are:"
echo "### acc, como, darwin, gcc, intel-linux, kcc, kylix, mipspro,"
echo "### mingw(msys), pathscale, pgi, qcc, sunpro, tru64cxx, vacpp"
echo "### mingw(msys), qcc, sunpro, tru64cxx, vacpp"
echo "###"
echo "### A special toolset; cc, is available which is used as a fallback"
echo "### when a more specific toolset is not found and the cc command is"
@@ -86,8 +86,6 @@ Guess_Toolset ()
elif test -r /opt/intel/compiler50/ia32/bin/iccvars.sh ; then
BOOST_JAM_TOOLSET=intel-linux
BOOST_JAM_TOOLSET_ROOT=/opt/intel/compiler50/ia32/
elif test_path pgcc ; then BOOST_JAM_TOOLSET=pgi
elif test_path pathcc ; then BOOST_JAM_TOOLSET=pathscale
elif test_path xlc ; then BOOST_JAM_TOOLSET=vacpp
elif test_path como ; then BOOST_JAM_TOOLSET=como
elif test_path KCC ; then BOOST_JAM_TOOLSET=kcc
@@ -185,14 +183,6 @@ case $BOOST_JAM_TOOLSET in
BOOST_JAM_CC=cc
;;
pathscale)
BOOST_JAM_CC=pathcc
;;
pgi)
BOOST_JAM_CC=pgcc
;;
sunpro)
if test -z "${BOOST_JAM_TOOLSET_ROOT}" -a -r /opt/SUNWspro/bin/cc ; then
BOOST_JAM_TOOLSET_ROOT=/opt/SUNWspro/
@@ -238,7 +228,7 @@ MKJAMBASE_SOURCES="mkjambase.c"
BJAM_SOURCES="\
command.c compile.c debug.c execunix.c expand.c fileunix.c glob.c hash.c\
hdrmacro.c headers.c jam.c jambase.c jamgram.c lists.c make.c make1.c\
newstr.c option.c parse.c pathunix.c pathvms.c regexp.c\
newstr.c option.c output.c parse.c pathunix.c pathvms.c regexp.c\
rules.c scan.c search.c subst.c timestamp.c variable.c modules.c\
strings.c filesys.c builtins.c pwd.c class.c native.c w32_getreg.c\
modules/set.c modules/path.c modules/regex.c modules/property-set.c\

View File

@@ -19,9 +19,11 @@ typedef struct timing_info
void execcmd(
char *string,
void (*func)( void *closure, int status, timing_info* ),
void (*func)( void *closure, int status, timing_info*, char *, char * ),
void *closure,
LIST *shell );
LIST *shell,
char *action,
char *target);
int execwait();

View File

@@ -47,7 +47,7 @@
void
execcmd(
char *string,
void (*func)( void *closure, int status ),
void (*func)( void *closure, int status, timing_info*, char *, char * ),
void *closure,
LIST *shell )
{

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,6 @@
/*
* Copyright 1993, 1995 Christopher Seiwald.
* Copyright 2007 Noel Belcourt.
*
* This file is part of Jam - see jam.c for Copyright information.
*/
@@ -8,6 +9,8 @@
# include "lists.h"
# include "execcmd.h"
# include <errno.h>
# include <signal.h>
# include <stdio.h>
# include <time.h>
# include <unistd.h> /* needed for vfork(), _exit() prototypes */
@@ -57,11 +60,22 @@ static int intr = 0;
static int cmdsrunning = 0;
static void (*istat)( int );
#define OUT 0
#define ERR 1
static struct
{
int pid; /* on win32, a real process handle */
void (*func)( void *closure, int status, timing_info* );
void *closure;
int pid; /* on win32, a real process handle */
int fd[2]; /* file descriptors for stdout and stderr */
FILE *stream[2]; /* child's stdout (0) and stderr (1) file stream */
int action_length; /* length of action string */
int target_length; /* length of target string */
char *action; /* buffer to hold action and target invoked */
char *target; /* buffer to hold action and target invoked */
char *command; /* buffer to hold command being invoked */
char *buffer[2]; /* buffer to hold stdout and stderr, if any */
void (*func)( void *closure, int status, timing_info*, char *, char * );
void *closure;
} cmdtab[ MAXJOBS ] = {{0}};
/*
@@ -82,13 +96,16 @@ onintr( int disp )
void
execcmd(
char *string,
void (*func)( void *closure, int status, timing_info* ),
void (*func)( void *closure, int status, timing_info*, char *, char * ),
void *closure,
LIST *shell )
LIST *shell,
char *action,
char *target )
{
int pid;
int slot;
int out[2], err[2];
int slot, len;
char *argv[ MAXARGC + 1 ]; /* +1 for NULL */
FILE *stream;
/* Find a slot in the running commands table for this one. */
@@ -102,7 +119,6 @@ execcmd(
exit( EXITBAD );
}
/* Forumulate argv */
/* If shell was defined, be prepared for % and ! subs. */
/* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */
@@ -140,37 +156,172 @@ execcmd(
argv[3] = 0;
}
/* Catch interrupts whenever commands are running. */
/* increment jobs running */
++cmdsrunning;
if( !cmdsrunning++ )
istat = signal( SIGINT, onintr );
/* save off actual command string */
cmdtab[ slot ].command = BJAM_MALLOC_ATOMIC(strlen(string)+1);
strcpy(cmdtab[slot].command, string);
/* create pipe from child to parent */
if (pipe(out) < 0)
exit(EXITBAD);
fcntl(out[0], F_SETFL, O_NONBLOCK);
fcntl(out[1], F_SETFL, O_NONBLOCK);
if (pipe(err) < 0)
exit(EXITBAD);
fcntl(err[0], F_SETFL, O_NONBLOCK);
fcntl(err[1], F_SETFL, O_NONBLOCK);
/* Start the command */
if ((pid = vfork()) == 0)
if ((cmdtab[slot].pid = vfork()) == 0)
{
execvp( argv[0], argv );
_exit(127);
}
close(out[0]);
close(err[0]);
if( pid == -1 )
dup2(out[1], STDOUT_FILENO);
if (globs.pipe_action == 0)
{
dup2(out[1], STDERR_FILENO);
close(err[1]);
}
else
dup2(err[1], STDERR_FILENO);
execvp( argv[0], argv );
_exit(127);
}
else if( cmdtab[slot].pid == -1 )
{
perror( "vfork" );
exit( EXITBAD );
}
/* close write end of pipes */
close(out[1]);
close(err[1]);
/* child writes stdout to out[1], parent reads from out[0] */
cmdtab[slot].fd[OUT] = out[0];
cmdtab[slot].stream[OUT] = fdopen(cmdtab[slot].fd[OUT], "rb");
if (cmdtab[slot].stream[OUT] == NULL) {
perror( "fdopen" );
exit( EXITBAD );
}
/* child writes stderr to err[1], parent reads from err[0] */
if (globs.pipe_action == 0)
{
close(err[0]);
}
else
{
cmdtab[slot].fd[ERR] = err[0];
cmdtab[slot].stream[ERR] = fdopen(cmdtab[slot].fd[ERR], "rb");
if (cmdtab[slot].stream[ERR] == NULL) {
perror( "fdopen" );
exit( EXITBAD );
}
}
/* ensure enough room for rule and target name */
if (action && target)
{
len = strlen(action) + 1;
if (cmdtab[slot].action_length < len)
{
BJAM_FREE(cmdtab[ slot ].action);
cmdtab[ slot ].action = BJAM_MALLOC_ATOMIC(len);
cmdtab[ slot ].action_length = len;
}
strcpy(cmdtab[ slot ].action, action);
len = strlen(target) + 1;
if (cmdtab[slot].target_length < len)
{
BJAM_FREE(cmdtab[ slot ].target);
cmdtab[ slot ].target = BJAM_MALLOC_ATOMIC(len);
cmdtab[ slot ].target_length = len;
}
strcpy(cmdtab[ slot ].target, target);
}
else
{
BJAM_FREE(cmdtab[ slot ].action);
BJAM_FREE(cmdtab[ slot ].target);
cmdtab[ slot ].action = 0;
cmdtab[ slot ].target = 0;
cmdtab[ slot ].action_length = 0;
cmdtab[ slot ].target_length = 0;
}
/* Save the operation for execwait() to find. */
cmdtab[ slot ].pid = pid;
cmdtab[ slot ].func = func;
cmdtab[ slot ].closure = closure;
/* Wait until we're under the limit of concurrent commands. */
/* Don't trust globs.jobs alone. */
while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
if( !execwait() )
break;
while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
if( !execwait() )
break;
}
/* returns 1 if file is closed, 0 if descriptor is still live
*
* i is index into cmdtab
*
* s (stream) indexes
*
* cmdtab[i].stream[s]
* cmdtab[i].buffer[s] and
* cmdtab[i].fd[s]
*/
int read_descriptor(int i, int s)
{
int done, ret, len;
char buffer[BUFSIZ];
while (0 < (ret = fread(buffer, sizeof(char), BUFSIZ-1, cmdtab[i].stream[s])))
{
buffer[ret] = 0;
if (!cmdtab[i].buffer[s])
{
/* never been allocated */
cmdtab[i].buffer[s] = (char*)BJAM_MALLOC_ATOMIC(ret+1);
memcpy(cmdtab[i].buffer[s], buffer, ret+1);
}
else
{
/* previously allocated */
char *tmp = cmdtab[i].buffer[s];
len = strlen(tmp);
cmdtab[i].buffer[s] = (char*)BJAM_MALLOC_ATOMIC(len+ret+1);
memcpy(cmdtab[i].buffer[s], tmp, len);
memcpy(cmdtab[i].buffer[s]+len, buffer, ret+1);
BJAM_FREE(tmp);
}
}
return feof(cmdtab[i].stream[s]);
}
void close_streams(int i, int s)
{
/* close the stream and pipe descriptor */
fclose(cmdtab[i].stream[s]);
cmdtab[i].stream[s] = 0;
close(cmdtab[i].fd[s]);
cmdtab[i].fd[s] = 0;
}
/*
@@ -180,132 +331,123 @@ execcmd(
int
execwait()
{
int i;
int status, w;
int rstat;
int i, j, len, ret, fd_max;
int pid, status, w, finished;
int rstat;
timing_info time;
fd_set fds;
struct tms old_time, new_time;
/* Handle naive make1() which doesn't know if cmds are running. */
char *tmp;
char buffer[BUFSIZ];
if( !cmdsrunning )
return 0;
/* Handle naive make1() which doesn't know if cmds are running. */
times(&old_time);
/* Pick up process pid and status */
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
;
if( !cmdsrunning )
return 0;
if( w == -1 )
{
printf( "child process(es) lost!\n" );
perror("wait");
exit( EXITBAD );
}
times(&new_time);
time.system = (double)(new_time.tms_cstime - old_time.tms_cstime) / CLOCKS_PER_SEC;
time.user = (double)(new_time.tms_cutime - old_time.tms_cutime) / CLOCKS_PER_SEC;
/* Find the process in the cmdtab. */
for( i = 0; i < MAXJOBS; i++ )
if( w == cmdtab[ i ].pid )
break;
if( i == MAXJOBS )
{
printf( "waif child found!\n" );
exit( EXITBAD );
}
/* Drive the completion */
if( !--cmdsrunning )
signal( SIGINT, istat );
if( intr )
rstat = EXEC_CMD_INTR;
else if( w == -1 || status != 0 )
rstat = EXEC_CMD_FAIL;
else
rstat = EXEC_CMD_OK;
cmdtab[ i ].pid = 0;
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time );
return 1;
}
# if defined( OS_NT ) && !defined( __BORLANDC__ )
# define WIN32_LEAN_AND_MEAN
# include <windows.h> /* do the ugly deed */
static int
my_wait( int *status )
{
int i, num_active = 0;
DWORD exitcode, waitcode;
static HANDLE *active_handles = 0;
if (!active_handles)
/* process children that signaled */
finished = 0;
while (!finished && cmdsrunning)
{
active_handles = (HANDLE *)BJAM_MALLOC(globs.jobs * sizeof(HANDLE) );
/* compute max read file descriptor for use in select */
fd_max = 0;
FD_ZERO(&fds);
for (i=0; i<globs.jobs; ++i)
{
if (0 < cmdtab[i].fd[OUT])
{
fd_max = fd_max < cmdtab[i].fd[OUT] ? cmdtab[i].fd[OUT] : fd_max;
FD_SET(cmdtab[i].fd[OUT], &fds);
}
if (globs.pipe_action != 0)
{
if (0 < cmdtab[i].fd[ERR])
{
fd_max = fd_max < cmdtab[i].fd[ERR] ? cmdtab[i].fd[ERR] : fd_max;
FD_SET(cmdtab[i].fd[ERR], &fds);
}
}
}
/* select will wait until io on a descriptor or a signal */
ret = select(fd_max+1, &fds, 0, 0, 0);
if (0 < ret)
{
for (i=0; i<globs.jobs; ++i)
{
int out = 0, err = 0;
if (FD_ISSET(cmdtab[i].fd[OUT], &fds))
out = read_descriptor(i, OUT);
if (FD_ISSET(cmdtab[i].fd[ERR], &fds))
err = read_descriptor(i, ERR);
/* if feof on either descriptor, then we're done */
if (out || err)
{
/* close the stream and pipe descriptors */
close_streams(i, OUT);
if (globs.pipe_action != 0)
close_streams(i, ERR);
/* reap the child and release resources */
pid = waitpid(cmdtab[i].pid, &status, 0);
if (pid == cmdtab[i].pid)
{
finished = 1;
pid = 0;
cmdtab[i].pid = 0;
times(&old_time);
/* print out the rule and target name */
out_action(cmdtab[i].action, cmdtab[i].target,
cmdtab[i].command, cmdtab[i].buffer[OUT], cmdtab[i].buffer[ERR]);
times(&new_time);
time.system = (double)(new_time.tms_cstime - old_time.tms_cstime) / CLOCKS_PER_SEC;
time.user = (double)(new_time.tms_cutime - old_time.tms_cutime) / CLOCKS_PER_SEC;
/* Drive the completion */
--cmdsrunning;
if( intr )
rstat = EXEC_CMD_INTR;
else if( w == -1 || status != 0 )
rstat = EXEC_CMD_FAIL;
else
rstat = EXEC_CMD_OK;
/* assume -p0 in effect so only pass buffer[0] containing merged output */
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time, cmdtab[i].command, cmdtab[i].buffer[0] );
BJAM_FREE(cmdtab[i].buffer[OUT]);
cmdtab[i].buffer[OUT] = 0;
BJAM_FREE(cmdtab[i].buffer[ERR]);
cmdtab[i].buffer[ERR] = 0;
BJAM_FREE(cmdtab[i].command);
cmdtab[i].command = 0;
cmdtab[i].func = 0;
cmdtab[i].closure = 0;
}
else
{
printf("unknown pid %d with errno = %d\n", pid, errno);
exit(EXITBAD);
}
}
}
}
}
/* first see if any non-waited-for processes are dead,
* and return if so.
*/
for ( i = 0; i < globs.jobs; i++ ) {
if ( cmdtab[i].pid ) {
if ( GetExitCodeProcess((HANDLE)cmdtab[i].pid, &exitcode) ) {
if ( exitcode == STILL_ACTIVE )
active_handles[num_active++] = (HANDLE)cmdtab[i].pid;
else {
CloseHandle((HANDLE)cmdtab[i].pid);
*status = (int)((exitcode & 0xff) << 8);
return cmdtab[i].pid;
}
}
else
goto FAILED;
}
}
/* if a child exists, wait for it to die */
if ( !num_active ) {
errno = ECHILD;
return -1;
}
waitcode = WaitForMultipleObjects( num_active,
active_handles,
FALSE,
INFINITE );
if ( waitcode != WAIT_FAILED ) {
if ( waitcode >= WAIT_ABANDONED_0
&& waitcode < WAIT_ABANDONED_0 + num_active )
i = waitcode - WAIT_ABANDONED_0;
else
i = waitcode - WAIT_OBJECT_0;
if ( GetExitCodeProcess(active_handles[i], &exitcode) ) {
CloseHandle(active_handles[i]);
*status = (int)((exitcode & 0xff) << 8);
return (int)active_handles[i];
}
}
FAILED:
errno = GetLastError();
return -1;
return 1;
}
# endif /* NT && !__BORLANDC__ */
# endif /* USE_EXECUNIX */

View File

@@ -49,9 +49,11 @@ char tempnambuf[ L_tmpnam + 1 + 4 ] = {0};
void
execcmd(
char *string,
void (*func)( void *closure, int status ),
void (*func)( void *closure, int status, timing_info*, char *, char * ),
void *closure,
LIST *shell )
LIST *shell,
char *rule_name,
char *target )
{
char *s, *e, *p;
int rstat = EXEC_CMD_OK;

View File

@@ -133,6 +133,7 @@
# ifdef unix
# include <sys/utsname.h>
# include <signal.h>
# endif
struct globs globs = {
@@ -140,6 +141,7 @@ struct globs globs = {
1, /* jobs */
0, /* quitquick */
0, /* newestfirst */
0, /* pipes action stdout and stderr merged to action output */
# ifdef OS_MAC
{ 0, 0 }, /* debug - suppress tracing output */
# else
@@ -217,7 +219,7 @@ int main( int argc, char **argv, char **arg_environ )
int arg_c = argc;
char ** arg_v = argv;
const char *progname = argv[0];
BJAM_MEM_INIT();
# ifdef OS_MAC
@@ -226,19 +228,21 @@ int main( int argc, char **argv, char **arg_environ )
argc--, argv++;
if( getoptions( argc, argv, "-:l:d:j:f:gs:t:ano:qv", optv ) < 0 )
if( getoptions( argc, argv, "-:l:d:j:p:f:gs:t:ano:qv", optv ) < 0 )
{
printf( "\nusage: %s [ options ] targets...\n\n", progname );
printf( "-a Build all targets, even if they are current.\n" );
printf( "-dx Set the debug level to x (0-9).\n" );
printf( "-fx Read x instead of Jambase.\n" );
/* printf( "-g Build from newest sources first.\n" ); */
/* printf( "-g Build from newest sources first.\n" ); */
printf( "-jx Run up to x shell commands concurrently.\n" );
printf( "-lx Limit actions to x number of seconds after which they are stopped.\n" );
printf( "-n Don't actually execute the updating actions.\n" );
printf( "-ox Write the updating actions to file x.\n" );
printf( "-q Quit quickly as soon as a target fails.\n" );
printf( "-px x=0, pipes action stdout and stderr merged into action output.\n" );
printf( "-q Quit quickly as soon as a target fails.\n" );
printf( "-r Enable Dart results.\n" );
printf( "-sx=y Set variable x=y, overriding environment.\n" );
printf( "-tx Rebuild x, even if it is up-to-date.\n" );
printf( "-v Print the version of jam and exit.\n" );
@@ -253,7 +257,7 @@ int main( int argc, char **argv, char **arg_environ )
{
printf( "Boost.Jam " );
printf( "Version %s. %s.\n", VERSION, OSMINOR );
printf( " Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. \n" );
printf( " Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. \n" );
printf( " Copyright 2001 David Turner.\n" );
printf( " Copyright 2001-2004 David Abrahams.\n" );
printf( " Copyright 2002-2005 Rene Rivera.\n" );
@@ -267,16 +271,28 @@ int main( int argc, char **argv, char **arg_environ )
if( ( s = getoptval( optv, 'n', 0 ) ) )
globs.noexec++, globs.debug[2] = 1;
if( ( s = getoptval( optv, 'q', 0 ) ) )
globs.quitquick = 1;
if( ( s = getoptval( optv, 'p', 0 ) ) )
{
/* undocumented -p3 (acts like both -p1 -p2) means separate pipe action stdout and stderr */
globs.pipe_action = atoi(s);
if (3 < globs.pipe_action || globs.pipe_action < 0)
{
printf( "Invalid pipe descriptor '%d', valid values are -p[0..3].\n", globs.pipe_action);
exit(EXITBAD);
}
}
if( ( s = getoptval( optv, 'q', 0 ) ) )
globs.quitquick = 1;
if( ( s = getoptval( optv, 'a', 0 ) ) )
anyhow++;
if( ( s = getoptval( optv, 'j', 0 ) ) )
globs.jobs = atoi( s );
if( ( s = getoptval( optv, 'g', 0 ) ) )
globs.newestfirst = 1;
if( ( s = getoptval( optv, 'g', 0 ) ) )
globs.newestfirst = 1;
if( ( s = getoptval( optv, 'l', 0 ) ) )
globs.timeout = atoi( s );

View File

@@ -522,9 +522,11 @@ struct globs {
int jobs;
int quitquick;
int newestfirst; /* build newest sources first */
int pipe_action;
char debug[DEBUG_MAX];
FILE *cmdout; /* print cmds, not run them */
long timeout; /* number of seconds to limit actions to, default 0 for no limit. */
long timeout; /* number of seconds to limit actions to, default 0 for no limit. */
int dart; /* output build and test results formatted for Dart */
} ;
extern struct globs globs;

View File

@@ -103,7 +103,7 @@ static void make1atail(state *pState);
static void make1b( state *pState );
static void make1c( state *pState );
static void make1d( state *pState );
static void make_closure(void *closure, int status, timing_info*);
static void make_closure(void *closure, int status, timing_info*, char *, char *);
typedef struct _stack
{
@@ -502,20 +502,15 @@ make1c( state *pState )
if( cmd && pState->t->status == EXEC_CMD_OK )
{
char *rule_name = 0;
char *target = 0;
if( DEBUG_MAKEQ ||
! ( cmd->rule->actions->flags & RULE_QUIETLY ) && DEBUG_MAKE)
{
printf( "%s ", cmd->rule->name );
list_print( lol_get( &cmd->args, 0 ) );
printf( "\n" );
rule_name = cmd->rule->name;
target = lol_get(&cmd->args, 0)->string;
}
if( DEBUG_EXEC )
printf( "%s\n", cmd->buf );
if( globs.cmdout )
fprintf( globs.cmdout, "%s", cmd->buf );
if( globs.noexec )
{
pState->curstate = T_STATE_MAKE1D;
@@ -524,10 +519,9 @@ make1c( state *pState )
else
{
TARGET *t = pState->t;
fflush( stdout );
pop_state(&state_stack); /* pop state first because execcmd could push state */
execcmd( cmd->buf, make_closure, t, cmd->shell );
execcmd( cmd->buf, make_closure, t, cmd->shell, rule_name, target );
}
}
else
@@ -708,14 +702,67 @@ static void call_timing_rule(TARGET* target, timing_info* time)
}
}
static void append_int_string(LOL *l, int x)
{
char buffer[50];
sprintf(buffer, "%i", x);
lol_add(l, list_new(L0, newstr(buffer)));
}
/* 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* time,
char *executed_command, char *command_output)
{
LIST* action_rule;
pushsettings(target->settings);
action_rule = var_get( "__ACTION_RULE__" );
popsettings(target->settings);
if (action_rule)
{
/* We'll prepend $(__ACTION_RULE__[2-]) to the first argument */
LIST* initial_args = list_copy( L0, action_rule->next );
/* Prepare the argument list */
FRAME frame[1];
frame_init( frame );
/* First argument is the name of the target */
lol_add( frame->args, list_new( initial_args, target->name ) );
append_int_string(frame->args, status);
append_double_string(frame->args, time->user);
append_double_string(frame->args, time->system);
lol_add(frame->args, list_new(L0, newstr(executed_command)));
if (command_output)
lol_add(frame->args, list_new(L0, newstr(command_output)));
else
lol_add(frame->args, L0);
if( lol_get( frame->args, 2 ) )
evaluate_rule( action_rule->string, frame );
/* Clean up */
frame_free( frame );
}
}
static void make_closure(
void *closure, int status, timing_info* time)
void *closure, int status, timing_info* time, char *executed_command,
char *command_output)
{
TARGET* built = (TARGET*)closure;
call_timing_rule(built, time);
if (DEBUG_EXECCMD)
printf("%f sec system; %f sec user\n", time->system, time->user);
call_action_rule(built, status, time, executed_command, command_output);
push_state(&state_stack, built, NULL, T_STATE_MAKE1D)->status = status;
}

72
src/engine/output.c Normal file
View File

@@ -0,0 +1,72 @@
/*
Copyright 2007 Rene Rivera
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)
*/
#include "jam.h"
#include "output.h"
#include <stdio.h>
#define bjam_out (stdout)
#define bjam_err (stderr)
static void out_(
const char * data,
FILE * io
)
{
while ( *data )
{
size_t len = strcspn(data,"\r");
fwrite(data,len,1,io);
data += len;
if ( *data == '\r' ) ++data;
}
}
void out_action(
const char * action,
const char * target,
const char * command,
const char * out_data,
const char * err_data
)
{
/* print out the action+target line, if the action is quite
the action should be null. */
if ( action )
{
fprintf(bjam_out,"%s %s\n",action,target);
}
/* print out the command executed if given -d+2 */
if ( DEBUG_EXEC )
{
fputs(command,bjam_out);
fputc('\n',bjam_out);
}
/* print out the command executed to the command stream */
if ( globs.cmdout )
{
fputs(command,globs.cmdout);
}
/* print out the command output, if requested */
if (0 != out_data &&
( globs.pipe_action & 1 /* STDOUT_FILENO */ ||
globs.pipe_action == 0))
{
out_(out_data,bjam_out);
}
if (0 != err_data &&
globs.pipe_action & 2 /* STDERR_FILENO */)
{
out_(err_data,bjam_err);
}
fflush(bjam_out);
fflush(bjam_err);
fflush(globs.cmdout);
}

18
src/engine/output.h Normal file
View File

@@ -0,0 +1,18 @@
/*
Copyright 2007 Rene Rivera
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)
*/
#ifndef BJAM_OUTPUT_H
#define BJAM_OUTPUT_H
void out_action(
const char * action,
const char * target,
const char * command,
const char * out_data,
const char * err_data
);
#endif