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:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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\
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
@@ -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 )
|
||||
{
|
||||
|
||||
1384
src/engine/execnt.c
1384
src/engine/execnt.c
File diff suppressed because it is too large
Load Diff
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 );
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
72
src/engine/output.c
Normal 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
18
src/engine/output.h
Normal 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
|
||||
Reference in New Issue
Block a user