/* * Copyright 1993, 1995 Christopher Seiwald. * * This file is part of Jam - see jam.c for Copyright information. */ /* * execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS * * If $(JAMSHELL) is defined, uses that to formulate execvp()/spawnvp(). * The default is: * * /bin/sh -c % [ on UNIX/AmigaOS ] * cmd.exe /c % [ on OS2/WinNT ] * * Each word must be an individual element in a jam variable value. * * In $(JAMSHELL), % expands to the command string and ! expands to * the slot number (starting at 1) for multiprocess (-j) invocations. * If $(JAMSHELL) doesn't include a %, it is tacked on as the last * argument. * * Don't just set JAMSHELL to /bin/sh or cmd.exe - it won't work! * * External routines: * execcmd() - launch an async command execution * execwait() - wait and drive at most one execution completion * * Internal routines: * onintr() - bump intr to note command interruption * * 04/08/94 (seiwald) - Coherent/386 support added. * 05/04/94 (seiwald) - async multiprocess interface * 01/22/95 (seiwald) - $(JAMSHELL) support * 06/02/97 (gsar) - full async multiprocess support for Win32 * 01/20/00 (seiwald) - Upgraded from K&R to ANSI C * 11/04/02 (seiwald) - const-ing for string literals * 12/27/02 (seiwald) - grist .bat file with pid for system uniqueness */ # include "jam.h" # include "lists.h" # include "execcmd.h" # include <errno.h> # ifdef USE_EXECUNIX # ifdef OS_OS2 # define USE_EXECNT # include <process.h> # endif # ifdef OS_NT # define USE_EXECNT # include <process.h> # define WIN32_LEAN_AND_MEAN # include <windows.h> /* do the ugly deed */ # define USE_MYWAIT # if !defined( __BORLANDC__ ) # define wait my_wait static int my_wait( int *status ); # endif # endif static int intr = 0; static int cmdsrunning = 0; static void (*istat)( int ); static struct { int pid; /* on win32, a real process handle */ /* CWM - outputname for serial output. 0 disables */ void (*func)( void *closure, int status, char *outputname ); void *closure; /* # ifdef USE_EXECNT *//* CWM - need on unix too */ char *tempfile; /* # endif *//* CWM */ # ifdef OPT_SERIAL_OUTPUT_EXT char *outputFilename; # endif } cmdtab[ MAXJOBS ] = {{0}}; /* * onintr() - bump intr to note command interruption */ void onintr( int disp ) { intr++; printf( "...interrupted\n" ); } /* CWM. Added pre/post exec functions */ void exec_init() { # ifdef OPT_SERIAL_OUTPUT_EXT char *tempdir; int i; # ifdef USE_EXECNT tempdir = "\\temp"; # else tempdir = "/tmp"; # endif if( getenv( "TEMP" ) ) tempdir = getenv( "TEMP" ); else if( getenv( "TMP" ) ) tempdir = getenv( "TMP" ); for( i = 0; i < globs.jobs; ++i ) { cmdtab[ i ].outputFilename = malloc( strlen( tempdir ) + 32 ); sprintf( cmdtab[ i ].outputFilename, "%s%cjam%dout%d", tempdir, PATH_DELIM, getpid(), i ); } # endif } void exec_done() { int i; for( i = 0; i < globs.jobs; ++i) { # ifdef OPT_SERIAL_OUTPUT_EXT unlink( cmdtab[ i ].outputFilename ); # endif /* # ifdef USE_EXECNT *//* CWM - need on unix too */ if ( cmdtab[ i ].tempfile ) unlink( cmdtab[ i ].tempfile ); /* # endif *//* CWM */ } } /* CWM */ /* * execcmd() - launch an async command execution */ void execcmd( char *string, /* CWM - outputname for serial output. 0 disables */ void (*func)( void *closure, int status, char *outputname ), void *closure, LIST *shell ) { int pid; int slot; const char *argv[ MAXARGC + 1 ]; /* +1 for NULL */ # ifdef USE_EXECNT char *p; # endif /* Find a slot in the running commands table for this one. */ for( slot = 0; slot < MAXJOBS; slot++ ) if( !cmdtab[ slot ].pid ) break; if( slot == MAXJOBS ) { printf( "no slots for child!\n" ); exit( EXITBAD ); } # ifdef OPT_JOB_SLOT_EXT /* Now that we know the slot we are going to run in, replace any */ /* occurrences of '!!' with the slot number. */ { char *c = strchr( string, '!' ); while( c ) { if( c[1] == '!' ) { char num[3]; sprintf( num, "%02d", slot ); c[0] = num[0]; c[1] = num[1]; c += 2; } else ++c; c = strchr( c, '!' ); } } # endif /* # ifdef USE_EXECNT *//* CWM */ if( !cmdtab[ slot ].tempfile ) { char *tempdir; if( !( tempdir = getenv( "TEMP" ) ) && !( tempdir = getenv( "TMP" ) ) ) # ifdef USE_EXECNT /* CWM */ tempdir = "\\temp"; # else tempdir = "/tmp"; # endif /* +32 is room for \jamXXXXXcmdSS.bat (at least) */ cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 32 ); sprintf( cmdtab[ slot ].tempfile, "%s%cjam%dcmd%d.bat", tempdir, PATH_DELIM, getpid(), slot ); } # ifdef USE_EXECNT /* Trim leading, ending white space */ while( isspace( *string ) ) ++string; p = strchr( string, '\n' ); while( p && isspace( *p ) ) ++p; /* If multi line, or too long, or JAMSHELL is set, write to bat file. */ /* Otherwise, exec directly. */ /* Frankly, if it is a single long line I don't think the */ /* command interpreter will do any better -- it will fail. */ if( p && *p || strlen( string ) > MAXLINE || shell ) # else if( shell ) # endif { FILE *f; /* Write command to bat file. */ f = fopen( cmdtab[ slot ].tempfile, "w" ); /* CWM - avoid a core dump */ if (!f) { fprintf (stderr, "Jam: Couldn't open temp file: %s\n", cmdtab[slot].tempfile); exit (1); } /* CWM */ fputs( string, f ); fclose( f ); string = cmdtab[ slot ].tempfile; } /* # endif *//* CWM */ /* Forumulate argv */ /* If shell was defined, be prepared for % and ! subs. */ /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */ if( shell ) { int i; char jobno[4]; int gotpercent = 0; sprintf( jobno, "%d", slot + 1 ); for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) ) { switch( shell->string[0] ) { case '%': argv[i] = string; gotpercent++; break; case '!': argv[i] = jobno; break; default: argv[i] = shell->string; } if( DEBUG_EXECCMD ) printf( "argv[%d] = '%s'\n", i, argv[i] ); } if( !gotpercent ) argv[i++] = string; argv[i] = 0; } else { # ifdef USE_EXECNT argv[0] = "cmd.exe"; argv[1] = "/Q/C"; /* anything more is non-portable */ # else argv[0] = "/bin/sh"; argv[1] = "-c"; # endif argv[2] = string; argv[3] = 0; } /* Catch interrupts whenever commands are running. */ if( !cmdsrunning++ ) istat = signal( SIGINT, onintr ); /* Start the command */ # ifdef USE_EXECNT # ifdef OPT_SERIAL_OUTPUT_EXT { int out, err, fd, bad_spawn = 0, spawn_err; out = _dup (1); err = _dup (2); fd = open( cmdtab[ slot ].outputFilename, O_WRONLY | O_TRUNC | O_CREAT, 0644 ); _dup2 (fd, 1); _dup2 (fd, 2); close (fd); if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 ) { bad_spawn = 1; spawn_err = errno; } _dup2 (out, 1); _dup2 (err, 2); close (out); close (err); if( bad_spawn ) { errno = spawn_err; printf( "Jam: Error invoking spawn() for %s\n", argv[0] ); perror( "spawn" ); exit( EXITBAD ); } } # else /* Not serial */ if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 ) { perror( "spawn" ); exit( EXITBAD ); } # endif # else # ifdef NO_VFORK if ((pid = fork()) == 0) # else if ((pid = vfork()) == 0) # endif { # ifdef OPT_SERIAL_OUTPUT_EXT int fd; close( 1 ); close( 2 ); fd = open( cmdtab[ slot ].outputFilename, O_WRONLY | O_TRUNC | O_CREAT, 0644 ); dup( fd ); dup( fd ); # endif execvp( argv[0], argv ); /* CWM */ printf( "Jam: Error invoking execvp() for %s\n", argv[0] ); perror( "execvp" ); _exit(127); } if( pid == -1 ) { perror( "vfork" ); exit( EXITBAD ); } # endif /* 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; } /* * execwait() - wait and drive at most one execution completion */ int execwait() { int i; int status, w; int rstat; /* Handle naive make1() which doesn't know if cmds are running. */ if( !cmdsrunning ) return 0; /* Pick up process pid and status */ while( ( w = wait( &status ) ) == -1 && errno == EINTR ) ; if( w == -1 ) { printf( "child process(es) lost!\n" ); perror("wait"); exit( EXITBAD ); } /* 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 ); } # ifdef USE_EXECNT /* Clear the temp file */ unlink( cmdtab[ i ].tempfile ); # endif /* 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; # ifdef OPT_SERIAL_OUTPUT_EXT (*cmdtab[ i ].func) (cmdtab[ i ].closure, rstat, cmdtab[ i ].outputFilename ); # else (*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, 0 ); # endif return 1; } # ifdef USE_MYWAIT static int my_wait( int *status ) { int i, num_active = 0; DWORD exitcode, waitcode; static HANDLE *active_handles = 0; if (!active_handles) active_handles = (HANDLE *)malloc(globs.jobs * sizeof(HANDLE) ); /* 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; } # endif /* USE_MYWAIT */ # endif /* USE_EXECUNIX */
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#9 | 3183 | Craig Mcpheeters | Integration of my jam mainline branch into my work branch. | ||
#8 | 3182 | Craig Mcpheeters | Update of my work branch. | ||
#7 | 1605 | Craig Mcpheeters |
Updated with respect to my copy at work. A few minor updates/enhancements |
||
#6 | 1477 | Craig Mcpheeters |
Integration from my main jam branch. This is 2.4-dev with my extensions. |
||
#5 | 1188 | Craig Mcpheeters |
The latest updates from my A|W work copy. Added a -d11 debug mode, which outputs information on fate changes Create jam variables to show job (-jn) information, such as the number of jobs requested, whether or not there is more than one job, and a list of the job slot numbers. These can be used in a variety of ways in supporting multi-job builds Added jobs slot expansion to the action blocks, the sequence !! is replaced by the job slot the action is running in. Modified the special shell handling to include unix. It used to be that on Unix, you could create an action block which exceeded the size that execvp() could handle (perhaps only when the DynamicCommandSize option is enabled.) On NT, all non-default shells are invoked through a intermediate file. This modification does the same for unix shells. Improved the -d+10 debug level, the dependency graph. It now shows the flags on the node as well (NOUPDATE, NOTFILE, etc.) Only issue the '...skipped' messages for nodes which would have been built |
||
#4 | 1085 | Craig Mcpheeters |
Returned some changes to bring this copy in line with our internal version * Added some double quotes in the Jambase so we can compile files with spaces in the names * Updated our Jambase.aw from the Jambase * Added some debug/optim code to the Jamfile * Fixed a bug in command.c In my dynamic command size extension, it no longer took into account if a target was marked as piecemeal. Now it does * Fixed a bug in execunix.c In jam 2.3 the logic for when to invoke the interpreter directly, and when to go through an intermediate .bat file was changed. This was failing for us, as on NT, cmd.exe does not handle large lines. rc.exe was failing when it went through a .bat file. Reverted it to the 2.2 logic |
||
#3 | 785 | Craig Mcpheeters |
Integration from //guest/craig_mcpheeters/jam/src/... This work area now contains both the Alias|Wavefront changes, and the latest integrations from the mainline. There are some changes in this area which shouldn't be merged back into the mainline. As I merge this branch back into my jam/src/... branch, I'll leave out a few of the changes. |
||
#2 | 782 | Craig Mcpheeters |
Initial return of the Alias|Wavefront mods to jam 2.2. I made a stab at a configuration system - see the file Jamfile.config. Most of the mods are now enclosed within #ifdef blocks, which kind of pollutes the code, but may make it easier to accept or reject some of these changes. Some of these #ifdefs could disappear completely if they are accepted into the mainline This return implements the following extensions: * header cache * dynamic command block size, allowing for *large* commands * slightly improved warnings and errors * serial output from jam - nice when working with multiple jobs * an extension to variable modifiers: $(>:/) and $(>:\\) * ability to ignore header dependencies for a build (jam -p) * new debug level, -d+10, which outputs the dependency graph * added no-care support to internal nodes. if they fail, dependents are built anyway * time stamps on output * a few minor output modifications * a fix for nt batch file names conflicing when more than one jam runs at a time Each of the above can be enabled/disabled on the command line. For example, to turn on the HeaderCache code: jam -sHeaderCache=1 that is, build jam first, then use that jam to build a new one with the options you want. Some of these changes may have been made in the mainline already, my next step will be to integrate the mainline changes into these ones This return isn't yet ready for prime-time |
||
#1 | 777 | Craig Mcpheeters |
Branch of my jam/src/... area. This new area establishes the integration base for the copy of Jam which has all the changes. |
||
//guest/craig_mcpheeters/jam/src/execunix.c | |||||
#2 | 617 | Craig Mcpheeters | Integration from mainline as of @3 | ||
#1 | 616 | Craig Mcpheeters | Integration from Jam mainline, as of @2 |