mirror of
https://github.com/boostorg/build.git
synced 2026-02-16 01:12:13 +00:00
jam_src/
builtins.{c,h}
Support for the REBUILDS rule
remove unused variable
rules.h
make.c
Support for the REBUILDS rule
make1.c
Support for the REBUILDS rule
Support for recording timing information
Restructured a case statement because it was masking a bug I
introduced.
execcmd.h
execnt.c
execunix.c
Support for recording timing information
Also removed NT-specific stuff from execunix
expand.c
Removed tabs from critical comment
Added tab-width variable setting comment for emacs.
v2/test
rebuilds.py, timedata.py, test_all.py
Tests for REBUILDS and timing.
[SVN r27334]
This commit is contained in:
@@ -113,6 +113,12 @@ load_builtins()
|
||||
bind_builtin( "INCLUDES" ,
|
||||
builtin_depends, 1, 0 ) );
|
||||
|
||||
{
|
||||
char * args[] = { "targets", "*", ":", "targets-to-rebuild", "*", 0 };
|
||||
bind_builtin( "REBUILDS" ,
|
||||
builtin_rebuilds, 0, args );
|
||||
}
|
||||
|
||||
duplicate_rule( "Leaves" ,
|
||||
bind_builtin( "LEAVES" ,
|
||||
builtin_flags, T_FLAG_LEAVES, 0 ) );
|
||||
@@ -376,7 +382,6 @@ builtin_depends(
|
||||
{
|
||||
LIST *targets = lol_get( frame->args, 0 );
|
||||
LIST *sources = lol_get( frame->args, 1 );
|
||||
int which = parse->num;
|
||||
LIST *l;
|
||||
|
||||
for( l = targets; l; l = list_next( l ) )
|
||||
@@ -399,6 +404,39 @@ builtin_depends(
|
||||
t->depends = targetlist( t->depends, sources );
|
||||
}
|
||||
|
||||
/* Enter reverse links */
|
||||
for( l = sources; l; l = list_next( l ) )
|
||||
{
|
||||
TARGET *s = bindtarget( l->string );
|
||||
s->dependents = targetlist( s->dependents, targets );
|
||||
}
|
||||
|
||||
return L0;
|
||||
}
|
||||
|
||||
/*
|
||||
* builtin_rebuilds() - REBUILDS rule
|
||||
*
|
||||
* The REBUILDS builtin rule appends each of the listed
|
||||
* rebuild-targets in its 2nd argument on the rebuilds list of each of
|
||||
* the listed targets in its first argument.
|
||||
*/
|
||||
|
||||
LIST *
|
||||
builtin_rebuilds(
|
||||
PARSE *parse,
|
||||
FRAME *frame )
|
||||
{
|
||||
LIST *targets = lol_get( frame->args, 0 );
|
||||
LIST *rebuilds = lol_get( frame->args, 1 );
|
||||
LIST *l;
|
||||
|
||||
for( l = targets; l; l = list_next( l ) )
|
||||
{
|
||||
TARGET *t = bindtarget( l->string );
|
||||
t->rebuilds = targetlist( t->rebuilds, rebuilds );
|
||||
}
|
||||
|
||||
return L0;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ void load_builtins();
|
||||
|
||||
LIST *builtin_calc( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_depends( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_rebuilds( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_echo( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_exit( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_flags( PARSE *parse, FRAME *args );
|
||||
|
||||
@@ -10,9 +10,16 @@
|
||||
* 05/04/94 (seiwald) - async multiprocess interface
|
||||
*/
|
||||
|
||||
typedef struct timing_info
|
||||
{
|
||||
/* double elapsed; */ /* We don't know how to get this number on Unix */
|
||||
double system;
|
||||
double user;
|
||||
} timing_info;
|
||||
|
||||
void execcmd(
|
||||
char *string,
|
||||
void (*func)( void *closure, int status ),
|
||||
void (*func)( void *closure, int status, timing_info* ),
|
||||
void *closure,
|
||||
LIST *shell );
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
# include <errno.h>
|
||||
# include <assert.h>
|
||||
# include <ctype.h>
|
||||
# include <time.h>
|
||||
|
||||
# ifdef USE_EXECNT
|
||||
|
||||
@@ -71,7 +72,7 @@ static int is_win95_defined = 0;
|
||||
static struct
|
||||
{
|
||||
int pid; /* on win32, a real process handle */
|
||||
void (*func)( void *closure, int status );
|
||||
void (*func)( void *closure, int status, timing_info* );
|
||||
void *closure;
|
||||
char *tempfile;
|
||||
|
||||
@@ -446,6 +447,65 @@ static const char *getTempDir(void)
|
||||
return pTempPath;
|
||||
}
|
||||
|
||||
/* 64-bit arithmetic helpers */
|
||||
|
||||
/* Compute the carry bit from the addition of two 32-bit unsigned numbers */
|
||||
#define add_carry_bit(a, b) ( (((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1 )
|
||||
|
||||
/* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1 and h2l2 */
|
||||
#define add_64_hi(h1, l1, h2, l2) ((h1) + (h2) + add_carry_bit(l1, l2))
|
||||
|
||||
/* Add two 64-bit unsigned numbers, h1l1 and h2l2 */
|
||||
static FILETIME add_64(
|
||||
unsigned long h1, unsigned long l1,
|
||||
unsigned long h2, unsigned long l2)
|
||||
{
|
||||
FILETIME result;
|
||||
result.dwLowDateTime = l1 + l2;
|
||||
result.dwHighDateTime = add_64_hi(h1, l1, h2, l2);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static FILETIME add_FILETIME(FILETIME t1, FILETIME t2)
|
||||
{
|
||||
return add_64(
|
||||
t1.dwHighDateTime, t1.dwLowDateTime
|
||||
, t2.dwHighDateTime, t2.dwLowDateTime);
|
||||
}
|
||||
static FILETIME negate_FILETIME(FILETIME t)
|
||||
{
|
||||
/* 2s complement negation */
|
||||
return add_64(~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1);
|
||||
}
|
||||
|
||||
/* COnvert a FILETIME to a number of seconds */
|
||||
static double filetime_seconds(FILETIME t)
|
||||
{
|
||||
return t.dwHighDateTime * (double)(1UL << 31) * 2 + t.dwLowDateTime * 1.0e-7;
|
||||
}
|
||||
|
||||
static void
|
||||
record_times(int pid, timing_info* time)
|
||||
{
|
||||
FILETIME creation, exit, kernel, user;
|
||||
if (GetProcessTimes((HANDLE)pid, &creation, &exit, &kernel, &user))
|
||||
{
|
||||
/* Compute the elapsed time */
|
||||
#if 0 /* We don't know how to get this number this on Unix */
|
||||
time->elapsed = filetime_seconds(
|
||||
add_FILETIME( exit, negate_FILETIME(creation) )
|
||||
);
|
||||
#endif
|
||||
|
||||
time->system = filetime_seconds(kernel);
|
||||
time->user = filetime_seconds(user);
|
||||
}
|
||||
|
||||
CloseHandle((HANDLE)pid);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* execcmd() - launch an async command execution
|
||||
*/
|
||||
@@ -453,7 +513,7 @@ static const char *getTempDir(void)
|
||||
void
|
||||
execcmd(
|
||||
char *string,
|
||||
void (*func)( void *closure, int status ),
|
||||
void (*func)( void *closure, int status, timing_info* ),
|
||||
void *closure,
|
||||
LIST *shell )
|
||||
{
|
||||
@@ -613,6 +673,7 @@ execcmd(
|
||||
const char** keyword;
|
||||
int len, spawn = 1;
|
||||
int result;
|
||||
timing_info time = {0,0};
|
||||
|
||||
for ( keyword = hard_coded; keyword[0]; keyword++ )
|
||||
{
|
||||
@@ -654,12 +715,13 @@ execcmd(
|
||||
fprintf( stderr, "\n" );
|
||||
#endif
|
||||
result = spawnvp( P_WAIT, args[0], args );
|
||||
record_times(result, &time);
|
||||
free_args( args );
|
||||
}
|
||||
else
|
||||
result = 1;
|
||||
}
|
||||
func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK );
|
||||
func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK, &time );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -718,19 +780,20 @@ execwait()
|
||||
int i;
|
||||
int status, w;
|
||||
int rstat;
|
||||
timing_info time;
|
||||
|
||||
/* Handle naive make1() which doesn't know if cmds are running. */
|
||||
|
||||
if( !cmdsrunning )
|
||||
return 0;
|
||||
|
||||
if ( is_win95 )
|
||||
return 0;
|
||||
if ( is_win95 )
|
||||
return 0;
|
||||
|
||||
/* Pick up process pid and status */
|
||||
|
||||
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
|
||||
;
|
||||
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
|
||||
;
|
||||
|
||||
if( w == -1 )
|
||||
{
|
||||
@@ -751,6 +814,8 @@ execwait()
|
||||
exit( EXITBAD );
|
||||
}
|
||||
|
||||
record_times(cmdtab[i].pid, &time);
|
||||
|
||||
/* Clear the temp file */
|
||||
if ( cmdtab[i].tempfile )
|
||||
unlink( cmdtab[ i ].tempfile );
|
||||
@@ -774,62 +839,107 @@ execwait()
|
||||
free(cmdtab[i].tempfile);
|
||||
cmdtab[i].tempfile = NULL;
|
||||
}
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat );
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
# if !defined( __BORLANDC__ )
|
||||
|
||||
/* The possible result codes from check_process_exit, below */
|
||||
typedef enum { process_error, process_active, process_finished } process_state;
|
||||
|
||||
/* Helper for my_wait() below. Checks to see whether the process has
|
||||
* exited and if so, records timing information.
|
||||
*/
|
||||
static process_state
|
||||
check_process_exit(
|
||||
HANDLE process /* The process we're looking at */
|
||||
|
||||
, int* status /* Storage for the finished process' exit
|
||||
* code. If the process is still active
|
||||
* this location is left untouched. */
|
||||
|
||||
, HANDLE* active_handles /* Storage for the process handle if it is
|
||||
* found to be still active, or NULL. The
|
||||
* process is treated as though it is
|
||||
* complete. */
|
||||
|
||||
, int* num_active /* The current length of active_handles */
|
||||
)
|
||||
{
|
||||
DWORD exitcode;
|
||||
process_state result;
|
||||
|
||||
/* Try to get the process exit code */
|
||||
if (!GetExitCodeProcess(process, &exitcode))
|
||||
{
|
||||
result = process_error; /* signal an error */
|
||||
}
|
||||
else if (
|
||||
exitcode == STILL_ACTIVE /* If the process is still active */
|
||||
&& active_handles != 0 /* and we've been passed a place to buffer it */
|
||||
)
|
||||
{
|
||||
active_handles[(*num_active)++] = process; /* push it onto the active stack */
|
||||
result = process_active;
|
||||
}
|
||||
else
|
||||
{
|
||||
*status = (int)((exitcode & 0xff) << 8);
|
||||
result = process_finished;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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) );
|
||||
HANDLE active_handles[MAXJOBS];
|
||||
|
||||
/* 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;
|
||||
for ( i = 0; i < globs.jobs; i++ )
|
||||
{
|
||||
int pid = cmdtab[i].pid;
|
||||
|
||||
if ( pid )
|
||||
{
|
||||
process_state state
|
||||
= check_process_exit((HANDLE)pid, status, active_handles, &num_active);
|
||||
|
||||
if ( state == process_error )
|
||||
goto FAILED;
|
||||
else if ( state == process_finished )
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
/* if a child exists, wait for it to die */
|
||||
if ( !num_active ) {
|
||||
if ( !num_active )
|
||||
{
|
||||
errno = ECHILD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
waitcode = WaitForMultipleObjects( num_active,
|
||||
active_handles,
|
||||
FALSE,
|
||||
INFINITE );
|
||||
if ( waitcode != WAIT_FAILED ) {
|
||||
active_handles,
|
||||
FALSE,
|
||||
INFINITE );
|
||||
if ( waitcode != WAIT_FAILED )
|
||||
{
|
||||
if ( waitcode >= WAIT_ABANDONED_0
|
||||
&& waitcode < WAIT_ABANDONED_0 + num_active )
|
||||
i = 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];
|
||||
}
|
||||
i = waitcode - WAIT_OBJECT_0;
|
||||
|
||||
if ( check_process_exit(active_handles[i], status, 0, 0) == process_finished )
|
||||
return (int)active_handles[i];
|
||||
}
|
||||
|
||||
FAILED:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
# include "lists.h"
|
||||
# include "execcmd.h"
|
||||
# include <errno.h>
|
||||
# include <time.h>
|
||||
|
||||
#if defined(sun) || defined(__sun)
|
||||
#include <unistd.h> /* need to include unistd.h on sun for the vfork prototype*/
|
||||
@@ -15,24 +16,12 @@
|
||||
#endif
|
||||
|
||||
# ifdef USE_EXECUNIX
|
||||
# include <sys/times.h>
|
||||
|
||||
# ifdef NO_VFORK
|
||||
# define vfork() fork()
|
||||
# endif
|
||||
|
||||
# if defined( OS_NT ) || defined( OS_OS2 )
|
||||
|
||||
# define USE_EXECNT
|
||||
|
||||
# include <process.h>
|
||||
|
||||
# if !defined( __BORLANDC__ ) && !defined( OS_OS2 )
|
||||
# define wait my_wait
|
||||
static int my_wait( int *status );
|
||||
# endif
|
||||
|
||||
# endif
|
||||
|
||||
/*
|
||||
* execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS
|
||||
*
|
||||
@@ -71,13 +60,8 @@ static void (*istat)( int );
|
||||
static struct
|
||||
{
|
||||
int pid; /* on win32, a real process handle */
|
||||
void (*func)( void *closure, int status );
|
||||
void (*func)( void *closure, int status, timing_info* );
|
||||
void *closure;
|
||||
|
||||
# ifdef USE_EXECNT
|
||||
char *tempfile;
|
||||
# endif
|
||||
|
||||
} cmdtab[ MAXJOBS ] = {{0}};
|
||||
|
||||
/*
|
||||
@@ -98,7 +82,7 @@ onintr( int disp )
|
||||
void
|
||||
execcmd(
|
||||
char *string,
|
||||
void (*func)( void *closure, int status ),
|
||||
void (*func)( void *closure, int status, timing_info* ),
|
||||
void *closure,
|
||||
LIST *shell )
|
||||
{
|
||||
@@ -106,10 +90,6 @@ execcmd(
|
||||
int slot;
|
||||
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++ )
|
||||
@@ -122,49 +102,6 @@ execcmd(
|
||||
exit( EXITBAD );
|
||||
}
|
||||
|
||||
# ifdef USE_EXECNT
|
||||
if( !cmdtab[ slot ].tempfile )
|
||||
{
|
||||
char *tempdir;
|
||||
|
||||
if( !( tempdir = getenv( "TEMP" ) ) &&
|
||||
!( tempdir = getenv( "TMP" ) ) )
|
||||
tempdir = "\\temp";
|
||||
|
||||
cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 );
|
||||
|
||||
sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat",
|
||||
tempdir, slot );
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
/* Write command to bat file. */
|
||||
|
||||
f = fopen( cmdtab[ slot ].tempfile, "w" );
|
||||
fputs( string, f );
|
||||
fclose( f );
|
||||
|
||||
string = cmdtab[ slot ].tempfile;
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Forumulate argv */
|
||||
/* If shell was defined, be prepared for % and ! subs. */
|
||||
@@ -197,13 +134,8 @@ execcmd(
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -215,13 +147,6 @@ execcmd(
|
||||
|
||||
/* Start the command */
|
||||
|
||||
# ifdef USE_EXECNT
|
||||
if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
|
||||
{
|
||||
perror( "spawn" );
|
||||
exit( EXITBAD );
|
||||
}
|
||||
# else
|
||||
if ((pid = vfork()) == 0)
|
||||
{
|
||||
execvp( argv[0], argv );
|
||||
@@ -233,7 +158,7 @@ execcmd(
|
||||
perror( "vfork" );
|
||||
exit( EXITBAD );
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Save the operation for execwait() to find. */
|
||||
|
||||
cmdtab[ slot ].pid = pid;
|
||||
@@ -258,14 +183,17 @@ execwait()
|
||||
int i;
|
||||
int status, w;
|
||||
int rstat;
|
||||
|
||||
timing_info time;
|
||||
struct tms old_time, new_time;
|
||||
|
||||
/* Handle naive make1() which doesn't know if cmds are running. */
|
||||
|
||||
if( !cmdsrunning )
|
||||
return 0;
|
||||
|
||||
/* Pick up process pid and status */
|
||||
times(&old_time);
|
||||
|
||||
/* Pick up process pid and status */
|
||||
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
|
||||
;
|
||||
|
||||
@@ -276,6 +204,11 @@ execwait()
|
||||
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++ )
|
||||
@@ -288,6 +221,7 @@ execwait()
|
||||
exit( EXITBAD );
|
||||
}
|
||||
|
||||
|
||||
/* Drive the completion */
|
||||
|
||||
if( !--cmdsrunning )
|
||||
@@ -302,7 +236,7 @@ execwait()
|
||||
|
||||
cmdtab[ i ].pid = 0;
|
||||
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat );
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -136,14 +136,14 @@ expand:
|
||||
/*
|
||||
* Input so far (ignore blanks):
|
||||
*
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^
|
||||
* in end
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^
|
||||
* in end
|
||||
* Output so far:
|
||||
*
|
||||
* stuff-in-outbuf $
|
||||
* ^ ^
|
||||
* out_buf out
|
||||
* stuff-in-outbuf $
|
||||
* ^ ^
|
||||
* out_buf out
|
||||
*
|
||||
*
|
||||
* We just copied the $ of $(...), so back up one on the output.
|
||||
@@ -169,9 +169,9 @@ expand:
|
||||
/*
|
||||
* Input so far (ignore blanks):
|
||||
*
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^ ^
|
||||
* inp in end
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^ ^
|
||||
* inp in end
|
||||
*/
|
||||
prefix_length = buf->size;
|
||||
string_append_range( buf, inp, in - 1 );
|
||||
@@ -190,14 +190,14 @@ expand:
|
||||
/*
|
||||
* Input so far (ignore blanks):
|
||||
*
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^
|
||||
* in end
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^
|
||||
* in end
|
||||
* Output so far:
|
||||
*
|
||||
* stuff-in-outbuf variable
|
||||
* ^ ^ ^
|
||||
* out_buf out ov
|
||||
* stuff-in-outbuf variable
|
||||
* ^ ^ ^
|
||||
* out_buf out ov
|
||||
*
|
||||
* Later we will overwrite 'variable' in out_buf, but we'll be
|
||||
* done with it by then. 'variable' may be a multi-element list,
|
||||
@@ -714,3 +714,9 @@ void var_expand_unit_test()
|
||||
lol_free(lol);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
tab-width: 8
|
||||
End:
|
||||
*/
|
||||
|
||||
@@ -82,6 +82,7 @@ static const char *target_fate[] =
|
||||
"newer", /* T_FATE_NEWER */
|
||||
"temp", /* T_FATE_ISTMP */
|
||||
"touched", /* T_FATE_TOUCHED */
|
||||
"rebuild", /* T_FATE_REBUILD */
|
||||
"missing", /* T_FATE_MISSING */
|
||||
"needtmp", /* T_FATE_NEEDTMP */
|
||||
"old", /* T_FATE_OUTDATED */
|
||||
@@ -174,6 +175,63 @@ make(
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Force any dependents of t that have already at least begun being
|
||||
* visited by make0 to be updated.
|
||||
*/
|
||||
static void update_dependents(TARGET* t)
|
||||
{
|
||||
TARGETS *q;
|
||||
|
||||
for (q = t->dependents; q; q = q->next)
|
||||
{
|
||||
TARGET* p = q->target;
|
||||
char fate0 = p->fate;
|
||||
|
||||
/* If we've already at least begun visiting it and
|
||||
* we're not already rebuilding it for other reasons
|
||||
*/
|
||||
if (fate0 != T_FATE_INIT && fate0 < T_FATE_BUILD)
|
||||
{
|
||||
p->fate = T_FATE_UPDATE;
|
||||
|
||||
if (DEBUG_FATE)
|
||||
{
|
||||
printf( "fate change %s from %s to %s (as dependent of %s)\n",
|
||||
p->name, target_fate[fate0], target_fate[p->fate], t->name);
|
||||
}
|
||||
|
||||
/* If we're done visiting it, go back and make sure its
|
||||
* dependents get rebuilt.
|
||||
*/
|
||||
if (fate0 > T_FATE_MAKING)
|
||||
update_dependents(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure that all of t's rebuilds get rebuilt */
|
||||
static void force_rebuilds(TARGET* t)
|
||||
{
|
||||
TARGETS* d;
|
||||
for (d = t->rebuilds; d; d = d->next)
|
||||
{
|
||||
TARGET* r = d->target;
|
||||
|
||||
/* If it's not already being rebuilt for other reasons */
|
||||
if (r->fate < T_FATE_BUILD)
|
||||
{
|
||||
if (DEBUG_FATE)
|
||||
printf( "fate change %s from %s to %s (by rebuild)\n",
|
||||
r->name, target_fate[r->fate], target_fate[T_FATE_REBUILD]);
|
||||
|
||||
/* Force rebuild it */
|
||||
r->fate = T_FATE_REBUILD;
|
||||
|
||||
/* And make sure its dependents are updated too */
|
||||
update_dependents(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* make0() - bind and scan everything to make a TARGET
|
||||
*
|
||||
@@ -362,7 +420,7 @@ make0(
|
||||
|
||||
last = 0;
|
||||
leaf = 0;
|
||||
fate = T_FATE_STABLE;
|
||||
fate = T_FATE_STABLE;
|
||||
|
||||
for( c = t->depends; c; c = c->next )
|
||||
{
|
||||
@@ -515,7 +573,7 @@ make0(
|
||||
t->name, target_fate[fate],
|
||||
oldTimeStamp ? " (by timestamp)" : "" );
|
||||
else
|
||||
printf( "fate change %s adjusted from %s to %s%s\n",
|
||||
printf( "fate change %s from %s to %s%s\n",
|
||||
t->name, target_fate[savedFate], target_fate[fate],
|
||||
oldTimeStamp ? " (by timestamp)" : "" );
|
||||
#endif
|
||||
@@ -551,8 +609,19 @@ make0(
|
||||
|
||||
t->time = max( t->time, last );
|
||||
t->leaf = leaf ? leaf : t->time ;
|
||||
t->fate = fate;
|
||||
/* This target's fate may have been updated by virtue of following
|
||||
* some target's rebuilds list, so only allow it to be increased
|
||||
* to the fate we've calculated. Otherwise, grab its new fate.
|
||||
*/
|
||||
if (fate > t->fate)
|
||||
t->fate = fate;
|
||||
else
|
||||
fate = t->fate;
|
||||
|
||||
/* Step 4g: if this target needs to be built, force rebuild
|
||||
* everything in this target's rebuilds list */
|
||||
if (fate >= T_FATE_BUILD && fate < T_FATE_BROKEN)
|
||||
force_rebuilds(t);
|
||||
/*
|
||||
* Step 5: sort dependents by their update time.
|
||||
*/
|
||||
@@ -674,6 +743,9 @@ dependGraphOutput( TARGET *t, int depth )
|
||||
case T_FATE_OUTDATED:
|
||||
printf( " %s : Outdated, updating it\n", spaces(depth) );
|
||||
break;
|
||||
case T_FATE_REBUILD:
|
||||
printf( " %s : Rebuild, Updating it\n", spaces(depth) );
|
||||
break;
|
||||
case T_FATE_UPDATE:
|
||||
printf( " %s : Updating it\n", spaces(depth) );
|
||||
break;
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
# include "command.h"
|
||||
# include "execcmd.h"
|
||||
|
||||
# include <stdlib.h>
|
||||
|
||||
#if defined(sun) || defined(__sun)
|
||||
#include <unistd.h> /* for unlink */
|
||||
#endif
|
||||
@@ -101,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);
|
||||
static void make_closure(void *closure, int status, timing_info*);
|
||||
|
||||
typedef struct _stack
|
||||
{
|
||||
@@ -410,9 +412,10 @@ make1b( state *pState )
|
||||
if( pState->t->status == EXEC_CMD_OK )
|
||||
switch( pState->t->fate )
|
||||
{
|
||||
/* These are handled by the default case below now
|
||||
case T_FATE_INIT:
|
||||
case T_FATE_MAKING:
|
||||
/* shouldn't happen */
|
||||
*/
|
||||
|
||||
case T_FATE_STABLE:
|
||||
case T_FATE_NEWER:
|
||||
@@ -433,6 +436,7 @@ make1b( state *pState )
|
||||
case T_FATE_NEEDTMP:
|
||||
case T_FATE_OUTDATED:
|
||||
case T_FATE_UPDATE:
|
||||
case T_FATE_REBUILD:
|
||||
|
||||
/* Set "on target" vars, build actions, unset vars */
|
||||
/* Set "progress" so that make1c() counts this target among */
|
||||
@@ -449,6 +453,11 @@ make1b( state *pState )
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
/* All possible fates should have been accounted for by now */
|
||||
default:
|
||||
printf("ERROR: %s has bad fate %d", pState->t->name, pState->t->fate);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Call make1c() to begin the execution of the chain of commands */
|
||||
@@ -655,9 +664,60 @@ make1c( state *pState )
|
||||
}
|
||||
}
|
||||
|
||||
static void make_closure(void *closure, int status)
|
||||
/* To l, append a 1-element list containing the string representation
|
||||
* of x
|
||||
*/
|
||||
static void append_double_string( LOL *l, double x )
|
||||
{
|
||||
push_state(&state_stack, (TARGET *)closure, NULL, T_STATE_MAKE1D)->status = status;
|
||||
char buffer[50];
|
||||
sprintf(buffer, "%f", x);
|
||||
lol_add( l, list_new( L0, newstr( buffer ) ) );
|
||||
}
|
||||
|
||||
/* Look up the __TIMING_RULE__ variable on the given target, and if
|
||||
* non-empty, invoke the rule it names, passing the given
|
||||
* timing_info
|
||||
*/
|
||||
static void call_timing_rule(TARGET* target, timing_info* time)
|
||||
{
|
||||
LIST* timing_rule;
|
||||
|
||||
pushsettings(target->settings);
|
||||
timing_rule = var_get( "__TIMING_RULE__" );
|
||||
popsettings(target->settings);
|
||||
|
||||
if (timing_rule)
|
||||
{
|
||||
/* We'll prepend $(__TIMING_RULE__[2-]) to the first argument */
|
||||
LIST* initial_args = list_copy( L0, timing_rule->next );
|
||||
|
||||
/* Prepare the argument list */
|
||||
FRAME frame[1];
|
||||
frame_init( frame );
|
||||
|
||||
/* First argument is the name of the timed target */
|
||||
lol_add( frame->args, list_new( initial_args, target->name ) );
|
||||
append_double_string(frame->args, time->user);
|
||||
append_double_string(frame->args, time->system);
|
||||
|
||||
if( lol_get( frame->args, 2 ) )
|
||||
evaluate_rule( timing_rule->string, frame );
|
||||
|
||||
/* Clean up */
|
||||
frame_free( frame );
|
||||
}
|
||||
}
|
||||
|
||||
static void make_closure(
|
||||
void *closure, int status, timing_info* time)
|
||||
{
|
||||
TARGET* built = (TARGET*)closure;
|
||||
|
||||
call_timing_rule(built, time);
|
||||
if (DEBUG_EXECCMD)
|
||||
printf("%f sec system; %f sec user\n", time->system, time->user);
|
||||
|
||||
push_state(&state_stack, built, NULL, T_STATE_MAKE1D)->status = status;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -178,6 +178,8 @@ struct _target {
|
||||
# define T_BIND_EXISTS 3 /* real file, timestamp valid */
|
||||
|
||||
TARGETS *depends; /* dependencies */
|
||||
TARGETS *dependents;/* the inverse of dependencies */
|
||||
TARGETS *rebuilds; /* targets that should be force-rebuilt whenever this one is */
|
||||
TARGET *includes; /* includes */
|
||||
TARGET *original_target; /* original_target->includes = this */
|
||||
char rescanned;
|
||||
@@ -198,14 +200,15 @@ struct _target {
|
||||
|
||||
# define T_FATE_BUILD 5 /* >= BUILD rebuilds target */
|
||||
# define T_FATE_TOUCHED 5 /* manually touched with -t */
|
||||
# define T_FATE_MISSING 6 /* is missing, needs updating */
|
||||
# define T_FATE_NEEDTMP 7 /* missing temp that must be rebuild */
|
||||
# define T_FATE_OUTDATED 8 /* is out of date, needs updating */
|
||||
# define T_FATE_UPDATE 9 /* deps updated, needs updating */
|
||||
# define T_FATE_REBUILD 6
|
||||
# define T_FATE_MISSING 7 /* is missing, needs updating */
|
||||
# define T_FATE_NEEDTMP 8 /* missing temp that must be rebuild */
|
||||
# define T_FATE_OUTDATED 9 /* is out of date, needs updating */
|
||||
# define T_FATE_UPDATE 10 /* deps updated, needs updating */
|
||||
|
||||
# define T_FATE_BROKEN 10 /* >= BROKEN ruins parents */
|
||||
# define T_FATE_CANTFIND 10 /* no rules to make missing target */
|
||||
# define T_FATE_CANTMAKE 11 /* can't find dependents */
|
||||
# define T_FATE_BROKEN 11 /* >= BROKEN ruins parents */
|
||||
# define T_FATE_CANTFIND 11 /* no rules to make missing target */
|
||||
# define T_FATE_CANTMAKE 12 /* can't find dependents */
|
||||
|
||||
char progress; /* tracks make1() progress */
|
||||
|
||||
|
||||
@@ -113,6 +113,12 @@ load_builtins()
|
||||
bind_builtin( "INCLUDES" ,
|
||||
builtin_depends, 1, 0 ) );
|
||||
|
||||
{
|
||||
char * args[] = { "targets", "*", ":", "targets-to-rebuild", "*", 0 };
|
||||
bind_builtin( "REBUILDS" ,
|
||||
builtin_rebuilds, 0, args );
|
||||
}
|
||||
|
||||
duplicate_rule( "Leaves" ,
|
||||
bind_builtin( "LEAVES" ,
|
||||
builtin_flags, T_FLAG_LEAVES, 0 ) );
|
||||
@@ -376,7 +382,6 @@ builtin_depends(
|
||||
{
|
||||
LIST *targets = lol_get( frame->args, 0 );
|
||||
LIST *sources = lol_get( frame->args, 1 );
|
||||
int which = parse->num;
|
||||
LIST *l;
|
||||
|
||||
for( l = targets; l; l = list_next( l ) )
|
||||
@@ -399,6 +404,39 @@ builtin_depends(
|
||||
t->depends = targetlist( t->depends, sources );
|
||||
}
|
||||
|
||||
/* Enter reverse links */
|
||||
for( l = sources; l; l = list_next( l ) )
|
||||
{
|
||||
TARGET *s = bindtarget( l->string );
|
||||
s->dependents = targetlist( s->dependents, targets );
|
||||
}
|
||||
|
||||
return L0;
|
||||
}
|
||||
|
||||
/*
|
||||
* builtin_rebuilds() - REBUILDS rule
|
||||
*
|
||||
* The REBUILDS builtin rule appends each of the listed
|
||||
* rebuild-targets in its 2nd argument on the rebuilds list of each of
|
||||
* the listed targets in its first argument.
|
||||
*/
|
||||
|
||||
LIST *
|
||||
builtin_rebuilds(
|
||||
PARSE *parse,
|
||||
FRAME *frame )
|
||||
{
|
||||
LIST *targets = lol_get( frame->args, 0 );
|
||||
LIST *rebuilds = lol_get( frame->args, 1 );
|
||||
LIST *l;
|
||||
|
||||
for( l = targets; l; l = list_next( l ) )
|
||||
{
|
||||
TARGET *t = bindtarget( l->string );
|
||||
t->rebuilds = targetlist( t->rebuilds, rebuilds );
|
||||
}
|
||||
|
||||
return L0;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ void load_builtins();
|
||||
|
||||
LIST *builtin_calc( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_depends( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_rebuilds( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_echo( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_exit( PARSE *parse, FRAME *args );
|
||||
LIST *builtin_flags( PARSE *parse, FRAME *args );
|
||||
|
||||
@@ -10,9 +10,16 @@
|
||||
* 05/04/94 (seiwald) - async multiprocess interface
|
||||
*/
|
||||
|
||||
typedef struct timing_info
|
||||
{
|
||||
/* double elapsed; */ /* We don't know how to get this number on Unix */
|
||||
double system;
|
||||
double user;
|
||||
} timing_info;
|
||||
|
||||
void execcmd(
|
||||
char *string,
|
||||
void (*func)( void *closure, int status ),
|
||||
void (*func)( void *closure, int status, timing_info* ),
|
||||
void *closure,
|
||||
LIST *shell );
|
||||
|
||||
|
||||
186
jam_src/execnt.c
186
jam_src/execnt.c
@@ -16,6 +16,7 @@
|
||||
# include <errno.h>
|
||||
# include <assert.h>
|
||||
# include <ctype.h>
|
||||
# include <time.h>
|
||||
|
||||
# ifdef USE_EXECNT
|
||||
|
||||
@@ -71,7 +72,7 @@ static int is_win95_defined = 0;
|
||||
static struct
|
||||
{
|
||||
int pid; /* on win32, a real process handle */
|
||||
void (*func)( void *closure, int status );
|
||||
void (*func)( void *closure, int status, timing_info* );
|
||||
void *closure;
|
||||
char *tempfile;
|
||||
|
||||
@@ -446,6 +447,65 @@ static const char *getTempDir(void)
|
||||
return pTempPath;
|
||||
}
|
||||
|
||||
/* 64-bit arithmetic helpers */
|
||||
|
||||
/* Compute the carry bit from the addition of two 32-bit unsigned numbers */
|
||||
#define add_carry_bit(a, b) ( (((a) | (b)) >> 31) & (~((a) + (b)) >> 31) & 0x1 )
|
||||
|
||||
/* Compute the high 32 bits of the addition of two 64-bit unsigned numbers, h1l1 and h2l2 */
|
||||
#define add_64_hi(h1, l1, h2, l2) ((h1) + (h2) + add_carry_bit(l1, l2))
|
||||
|
||||
/* Add two 64-bit unsigned numbers, h1l1 and h2l2 */
|
||||
static FILETIME add_64(
|
||||
unsigned long h1, unsigned long l1,
|
||||
unsigned long h2, unsigned long l2)
|
||||
{
|
||||
FILETIME result;
|
||||
result.dwLowDateTime = l1 + l2;
|
||||
result.dwHighDateTime = add_64_hi(h1, l1, h2, l2);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static FILETIME add_FILETIME(FILETIME t1, FILETIME t2)
|
||||
{
|
||||
return add_64(
|
||||
t1.dwHighDateTime, t1.dwLowDateTime
|
||||
, t2.dwHighDateTime, t2.dwLowDateTime);
|
||||
}
|
||||
static FILETIME negate_FILETIME(FILETIME t)
|
||||
{
|
||||
/* 2s complement negation */
|
||||
return add_64(~t.dwHighDateTime, ~t.dwLowDateTime, 0, 1);
|
||||
}
|
||||
|
||||
/* COnvert a FILETIME to a number of seconds */
|
||||
static double filetime_seconds(FILETIME t)
|
||||
{
|
||||
return t.dwHighDateTime * (double)(1UL << 31) * 2 + t.dwLowDateTime * 1.0e-7;
|
||||
}
|
||||
|
||||
static void
|
||||
record_times(int pid, timing_info* time)
|
||||
{
|
||||
FILETIME creation, exit, kernel, user;
|
||||
if (GetProcessTimes((HANDLE)pid, &creation, &exit, &kernel, &user))
|
||||
{
|
||||
/* Compute the elapsed time */
|
||||
#if 0 /* We don't know how to get this number this on Unix */
|
||||
time->elapsed = filetime_seconds(
|
||||
add_FILETIME( exit, negate_FILETIME(creation) )
|
||||
);
|
||||
#endif
|
||||
|
||||
time->system = filetime_seconds(kernel);
|
||||
time->user = filetime_seconds(user);
|
||||
}
|
||||
|
||||
CloseHandle((HANDLE)pid);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* execcmd() - launch an async command execution
|
||||
*/
|
||||
@@ -453,7 +513,7 @@ static const char *getTempDir(void)
|
||||
void
|
||||
execcmd(
|
||||
char *string,
|
||||
void (*func)( void *closure, int status ),
|
||||
void (*func)( void *closure, int status, timing_info* ),
|
||||
void *closure,
|
||||
LIST *shell )
|
||||
{
|
||||
@@ -613,6 +673,7 @@ execcmd(
|
||||
const char** keyword;
|
||||
int len, spawn = 1;
|
||||
int result;
|
||||
timing_info time = {0,0};
|
||||
|
||||
for ( keyword = hard_coded; keyword[0]; keyword++ )
|
||||
{
|
||||
@@ -654,12 +715,13 @@ execcmd(
|
||||
fprintf( stderr, "\n" );
|
||||
#endif
|
||||
result = spawnvp( P_WAIT, args[0], args );
|
||||
record_times(result, &time);
|
||||
free_args( args );
|
||||
}
|
||||
else
|
||||
result = 1;
|
||||
}
|
||||
func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK );
|
||||
func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK, &time );
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -718,19 +780,20 @@ execwait()
|
||||
int i;
|
||||
int status, w;
|
||||
int rstat;
|
||||
timing_info time;
|
||||
|
||||
/* Handle naive make1() which doesn't know if cmds are running. */
|
||||
|
||||
if( !cmdsrunning )
|
||||
return 0;
|
||||
|
||||
if ( is_win95 )
|
||||
return 0;
|
||||
if ( is_win95 )
|
||||
return 0;
|
||||
|
||||
/* Pick up process pid and status */
|
||||
|
||||
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
|
||||
;
|
||||
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
|
||||
;
|
||||
|
||||
if( w == -1 )
|
||||
{
|
||||
@@ -751,6 +814,8 @@ execwait()
|
||||
exit( EXITBAD );
|
||||
}
|
||||
|
||||
record_times(cmdtab[i].pid, &time);
|
||||
|
||||
/* Clear the temp file */
|
||||
if ( cmdtab[i].tempfile )
|
||||
unlink( cmdtab[ i ].tempfile );
|
||||
@@ -774,62 +839,107 @@ execwait()
|
||||
free(cmdtab[i].tempfile);
|
||||
cmdtab[i].tempfile = NULL;
|
||||
}
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat );
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
# if !defined( __BORLANDC__ )
|
||||
|
||||
/* The possible result codes from check_process_exit, below */
|
||||
typedef enum { process_error, process_active, process_finished } process_state;
|
||||
|
||||
/* Helper for my_wait() below. Checks to see whether the process has
|
||||
* exited and if so, records timing information.
|
||||
*/
|
||||
static process_state
|
||||
check_process_exit(
|
||||
HANDLE process /* The process we're looking at */
|
||||
|
||||
, int* status /* Storage for the finished process' exit
|
||||
* code. If the process is still active
|
||||
* this location is left untouched. */
|
||||
|
||||
, HANDLE* active_handles /* Storage for the process handle if it is
|
||||
* found to be still active, or NULL. The
|
||||
* process is treated as though it is
|
||||
* complete. */
|
||||
|
||||
, int* num_active /* The current length of active_handles */
|
||||
)
|
||||
{
|
||||
DWORD exitcode;
|
||||
process_state result;
|
||||
|
||||
/* Try to get the process exit code */
|
||||
if (!GetExitCodeProcess(process, &exitcode))
|
||||
{
|
||||
result = process_error; /* signal an error */
|
||||
}
|
||||
else if (
|
||||
exitcode == STILL_ACTIVE /* If the process is still active */
|
||||
&& active_handles != 0 /* and we've been passed a place to buffer it */
|
||||
)
|
||||
{
|
||||
active_handles[(*num_active)++] = process; /* push it onto the active stack */
|
||||
result = process_active;
|
||||
}
|
||||
else
|
||||
{
|
||||
*status = (int)((exitcode & 0xff) << 8);
|
||||
result = process_finished;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
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) );
|
||||
HANDLE active_handles[MAXJOBS];
|
||||
|
||||
/* 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;
|
||||
for ( i = 0; i < globs.jobs; i++ )
|
||||
{
|
||||
int pid = cmdtab[i].pid;
|
||||
|
||||
if ( pid )
|
||||
{
|
||||
process_state state
|
||||
= check_process_exit((HANDLE)pid, status, active_handles, &num_active);
|
||||
|
||||
if ( state == process_error )
|
||||
goto FAILED;
|
||||
else if ( state == process_finished )
|
||||
return pid;
|
||||
}
|
||||
}
|
||||
|
||||
/* if a child exists, wait for it to die */
|
||||
if ( !num_active ) {
|
||||
if ( !num_active )
|
||||
{
|
||||
errno = ECHILD;
|
||||
return -1;
|
||||
}
|
||||
|
||||
waitcode = WaitForMultipleObjects( num_active,
|
||||
active_handles,
|
||||
FALSE,
|
||||
INFINITE );
|
||||
if ( waitcode != WAIT_FAILED ) {
|
||||
active_handles,
|
||||
FALSE,
|
||||
INFINITE );
|
||||
if ( waitcode != WAIT_FAILED )
|
||||
{
|
||||
if ( waitcode >= WAIT_ABANDONED_0
|
||||
&& waitcode < WAIT_ABANDONED_0 + num_active )
|
||||
i = 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];
|
||||
}
|
||||
i = waitcode - WAIT_OBJECT_0;
|
||||
|
||||
if ( check_process_exit(active_handles[i], status, 0, 0) == process_finished )
|
||||
return (int)active_handles[i];
|
||||
}
|
||||
|
||||
FAILED:
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
# include "lists.h"
|
||||
# include "execcmd.h"
|
||||
# include <errno.h>
|
||||
# include <time.h>
|
||||
|
||||
#if defined(sun) || defined(__sun)
|
||||
#include <unistd.h> /* need to include unistd.h on sun for the vfork prototype*/
|
||||
@@ -15,24 +16,12 @@
|
||||
#endif
|
||||
|
||||
# ifdef USE_EXECUNIX
|
||||
# include <sys/times.h>
|
||||
|
||||
# ifdef NO_VFORK
|
||||
# define vfork() fork()
|
||||
# endif
|
||||
|
||||
# if defined( OS_NT ) || defined( OS_OS2 )
|
||||
|
||||
# define USE_EXECNT
|
||||
|
||||
# include <process.h>
|
||||
|
||||
# if !defined( __BORLANDC__ ) && !defined( OS_OS2 )
|
||||
# define wait my_wait
|
||||
static int my_wait( int *status );
|
||||
# endif
|
||||
|
||||
# endif
|
||||
|
||||
/*
|
||||
* execunix.c - execute a shell script on UNIX/WinNT/OS2/AmigaOS
|
||||
*
|
||||
@@ -71,13 +60,8 @@ static void (*istat)( int );
|
||||
static struct
|
||||
{
|
||||
int pid; /* on win32, a real process handle */
|
||||
void (*func)( void *closure, int status );
|
||||
void (*func)( void *closure, int status, timing_info* );
|
||||
void *closure;
|
||||
|
||||
# ifdef USE_EXECNT
|
||||
char *tempfile;
|
||||
# endif
|
||||
|
||||
} cmdtab[ MAXJOBS ] = {{0}};
|
||||
|
||||
/*
|
||||
@@ -98,7 +82,7 @@ onintr( int disp )
|
||||
void
|
||||
execcmd(
|
||||
char *string,
|
||||
void (*func)( void *closure, int status ),
|
||||
void (*func)( void *closure, int status, timing_info* ),
|
||||
void *closure,
|
||||
LIST *shell )
|
||||
{
|
||||
@@ -106,10 +90,6 @@ execcmd(
|
||||
int slot;
|
||||
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++ )
|
||||
@@ -122,49 +102,6 @@ execcmd(
|
||||
exit( EXITBAD );
|
||||
}
|
||||
|
||||
# ifdef USE_EXECNT
|
||||
if( !cmdtab[ slot ].tempfile )
|
||||
{
|
||||
char *tempdir;
|
||||
|
||||
if( !( tempdir = getenv( "TEMP" ) ) &&
|
||||
!( tempdir = getenv( "TMP" ) ) )
|
||||
tempdir = "\\temp";
|
||||
|
||||
cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 14 );
|
||||
|
||||
sprintf( cmdtab[ slot ].tempfile, "%s\\jamtmp%02d.bat",
|
||||
tempdir, slot );
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
FILE *f;
|
||||
|
||||
/* Write command to bat file. */
|
||||
|
||||
f = fopen( cmdtab[ slot ].tempfile, "w" );
|
||||
fputs( string, f );
|
||||
fclose( f );
|
||||
|
||||
string = cmdtab[ slot ].tempfile;
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Forumulate argv */
|
||||
/* If shell was defined, be prepared for % and ! subs. */
|
||||
@@ -197,13 +134,8 @@ execcmd(
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -215,13 +147,6 @@ execcmd(
|
||||
|
||||
/* Start the command */
|
||||
|
||||
# ifdef USE_EXECNT
|
||||
if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
|
||||
{
|
||||
perror( "spawn" );
|
||||
exit( EXITBAD );
|
||||
}
|
||||
# else
|
||||
if ((pid = vfork()) == 0)
|
||||
{
|
||||
execvp( argv[0], argv );
|
||||
@@ -233,7 +158,7 @@ execcmd(
|
||||
perror( "vfork" );
|
||||
exit( EXITBAD );
|
||||
}
|
||||
# endif
|
||||
|
||||
/* Save the operation for execwait() to find. */
|
||||
|
||||
cmdtab[ slot ].pid = pid;
|
||||
@@ -258,14 +183,17 @@ execwait()
|
||||
int i;
|
||||
int status, w;
|
||||
int rstat;
|
||||
|
||||
timing_info time;
|
||||
struct tms old_time, new_time;
|
||||
|
||||
/* Handle naive make1() which doesn't know if cmds are running. */
|
||||
|
||||
if( !cmdsrunning )
|
||||
return 0;
|
||||
|
||||
/* Pick up process pid and status */
|
||||
times(&old_time);
|
||||
|
||||
/* Pick up process pid and status */
|
||||
while( ( w = wait( &status ) ) == -1 && errno == EINTR )
|
||||
;
|
||||
|
||||
@@ -276,6 +204,11 @@ execwait()
|
||||
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++ )
|
||||
@@ -288,6 +221,7 @@ execwait()
|
||||
exit( EXITBAD );
|
||||
}
|
||||
|
||||
|
||||
/* Drive the completion */
|
||||
|
||||
if( !--cmdsrunning )
|
||||
@@ -302,7 +236,7 @@ execwait()
|
||||
|
||||
cmdtab[ i ].pid = 0;
|
||||
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat );
|
||||
(*cmdtab[ i ].func)( cmdtab[ i ].closure, rstat, &time );
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -136,14 +136,14 @@ expand:
|
||||
/*
|
||||
* Input so far (ignore blanks):
|
||||
*
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^
|
||||
* in end
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^
|
||||
* in end
|
||||
* Output so far:
|
||||
*
|
||||
* stuff-in-outbuf $
|
||||
* ^ ^
|
||||
* out_buf out
|
||||
* stuff-in-outbuf $
|
||||
* ^ ^
|
||||
* out_buf out
|
||||
*
|
||||
*
|
||||
* We just copied the $ of $(...), so back up one on the output.
|
||||
@@ -169,9 +169,9 @@ expand:
|
||||
/*
|
||||
* Input so far (ignore blanks):
|
||||
*
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^ ^
|
||||
* inp in end
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^ ^
|
||||
* inp in end
|
||||
*/
|
||||
prefix_length = buf->size;
|
||||
string_append_range( buf, inp, in - 1 );
|
||||
@@ -190,14 +190,14 @@ expand:
|
||||
/*
|
||||
* Input so far (ignore blanks):
|
||||
*
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^
|
||||
* in end
|
||||
* stuff-in-outbuf $(variable) remainder
|
||||
* ^ ^
|
||||
* in end
|
||||
* Output so far:
|
||||
*
|
||||
* stuff-in-outbuf variable
|
||||
* ^ ^ ^
|
||||
* out_buf out ov
|
||||
* stuff-in-outbuf variable
|
||||
* ^ ^ ^
|
||||
* out_buf out ov
|
||||
*
|
||||
* Later we will overwrite 'variable' in out_buf, but we'll be
|
||||
* done with it by then. 'variable' may be a multi-element list,
|
||||
@@ -714,3 +714,9 @@ void var_expand_unit_test()
|
||||
lol_free(lol);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
Local Variables:
|
||||
tab-width: 8
|
||||
End:
|
||||
*/
|
||||
|
||||
@@ -82,6 +82,7 @@ static const char *target_fate[] =
|
||||
"newer", /* T_FATE_NEWER */
|
||||
"temp", /* T_FATE_ISTMP */
|
||||
"touched", /* T_FATE_TOUCHED */
|
||||
"rebuild", /* T_FATE_REBUILD */
|
||||
"missing", /* T_FATE_MISSING */
|
||||
"needtmp", /* T_FATE_NEEDTMP */
|
||||
"old", /* T_FATE_OUTDATED */
|
||||
@@ -174,6 +175,63 @@ make(
|
||||
return status;
|
||||
}
|
||||
|
||||
/* Force any dependents of t that have already at least begun being
|
||||
* visited by make0 to be updated.
|
||||
*/
|
||||
static void update_dependents(TARGET* t)
|
||||
{
|
||||
TARGETS *q;
|
||||
|
||||
for (q = t->dependents; q; q = q->next)
|
||||
{
|
||||
TARGET* p = q->target;
|
||||
char fate0 = p->fate;
|
||||
|
||||
/* If we've already at least begun visiting it and
|
||||
* we're not already rebuilding it for other reasons
|
||||
*/
|
||||
if (fate0 != T_FATE_INIT && fate0 < T_FATE_BUILD)
|
||||
{
|
||||
p->fate = T_FATE_UPDATE;
|
||||
|
||||
if (DEBUG_FATE)
|
||||
{
|
||||
printf( "fate change %s from %s to %s (as dependent of %s)\n",
|
||||
p->name, target_fate[fate0], target_fate[p->fate], t->name);
|
||||
}
|
||||
|
||||
/* If we're done visiting it, go back and make sure its
|
||||
* dependents get rebuilt.
|
||||
*/
|
||||
if (fate0 > T_FATE_MAKING)
|
||||
update_dependents(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Make sure that all of t's rebuilds get rebuilt */
|
||||
static void force_rebuilds(TARGET* t)
|
||||
{
|
||||
TARGETS* d;
|
||||
for (d = t->rebuilds; d; d = d->next)
|
||||
{
|
||||
TARGET* r = d->target;
|
||||
|
||||
/* If it's not already being rebuilt for other reasons */
|
||||
if (r->fate < T_FATE_BUILD)
|
||||
{
|
||||
if (DEBUG_FATE)
|
||||
printf( "fate change %s from %s to %s (by rebuild)\n",
|
||||
r->name, target_fate[r->fate], target_fate[T_FATE_REBUILD]);
|
||||
|
||||
/* Force rebuild it */
|
||||
r->fate = T_FATE_REBUILD;
|
||||
|
||||
/* And make sure its dependents are updated too */
|
||||
update_dependents(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
* make0() - bind and scan everything to make a TARGET
|
||||
*
|
||||
@@ -362,7 +420,7 @@ make0(
|
||||
|
||||
last = 0;
|
||||
leaf = 0;
|
||||
fate = T_FATE_STABLE;
|
||||
fate = T_FATE_STABLE;
|
||||
|
||||
for( c = t->depends; c; c = c->next )
|
||||
{
|
||||
@@ -515,7 +573,7 @@ make0(
|
||||
t->name, target_fate[fate],
|
||||
oldTimeStamp ? " (by timestamp)" : "" );
|
||||
else
|
||||
printf( "fate change %s adjusted from %s to %s%s\n",
|
||||
printf( "fate change %s from %s to %s%s\n",
|
||||
t->name, target_fate[savedFate], target_fate[fate],
|
||||
oldTimeStamp ? " (by timestamp)" : "" );
|
||||
#endif
|
||||
@@ -551,8 +609,19 @@ make0(
|
||||
|
||||
t->time = max( t->time, last );
|
||||
t->leaf = leaf ? leaf : t->time ;
|
||||
t->fate = fate;
|
||||
/* This target's fate may have been updated by virtue of following
|
||||
* some target's rebuilds list, so only allow it to be increased
|
||||
* to the fate we've calculated. Otherwise, grab its new fate.
|
||||
*/
|
||||
if (fate > t->fate)
|
||||
t->fate = fate;
|
||||
else
|
||||
fate = t->fate;
|
||||
|
||||
/* Step 4g: if this target needs to be built, force rebuild
|
||||
* everything in this target's rebuilds list */
|
||||
if (fate >= T_FATE_BUILD && fate < T_FATE_BROKEN)
|
||||
force_rebuilds(t);
|
||||
/*
|
||||
* Step 5: sort dependents by their update time.
|
||||
*/
|
||||
@@ -674,6 +743,9 @@ dependGraphOutput( TARGET *t, int depth )
|
||||
case T_FATE_OUTDATED:
|
||||
printf( " %s : Outdated, updating it\n", spaces(depth) );
|
||||
break;
|
||||
case T_FATE_REBUILD:
|
||||
printf( " %s : Rebuild, Updating it\n", spaces(depth) );
|
||||
break;
|
||||
case T_FATE_UPDATE:
|
||||
printf( " %s : Updating it\n", spaces(depth) );
|
||||
break;
|
||||
|
||||
@@ -61,6 +61,8 @@
|
||||
# include "command.h"
|
||||
# include "execcmd.h"
|
||||
|
||||
# include <stdlib.h>
|
||||
|
||||
#if defined(sun) || defined(__sun)
|
||||
#include <unistd.h> /* for unlink */
|
||||
#endif
|
||||
@@ -101,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);
|
||||
static void make_closure(void *closure, int status, timing_info*);
|
||||
|
||||
typedef struct _stack
|
||||
{
|
||||
@@ -410,9 +412,10 @@ make1b( state *pState )
|
||||
if( pState->t->status == EXEC_CMD_OK )
|
||||
switch( pState->t->fate )
|
||||
{
|
||||
/* These are handled by the default case below now
|
||||
case T_FATE_INIT:
|
||||
case T_FATE_MAKING:
|
||||
/* shouldn't happen */
|
||||
*/
|
||||
|
||||
case T_FATE_STABLE:
|
||||
case T_FATE_NEWER:
|
||||
@@ -433,6 +436,7 @@ make1b( state *pState )
|
||||
case T_FATE_NEEDTMP:
|
||||
case T_FATE_OUTDATED:
|
||||
case T_FATE_UPDATE:
|
||||
case T_FATE_REBUILD:
|
||||
|
||||
/* Set "on target" vars, build actions, unset vars */
|
||||
/* Set "progress" so that make1c() counts this target among */
|
||||
@@ -449,6 +453,11 @@ make1b( state *pState )
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
/* All possible fates should have been accounted for by now */
|
||||
default:
|
||||
printf("ERROR: %s has bad fate %d", pState->t->name, pState->t->fate);
|
||||
abort();
|
||||
}
|
||||
|
||||
/* Call make1c() to begin the execution of the chain of commands */
|
||||
@@ -655,9 +664,60 @@ make1c( state *pState )
|
||||
}
|
||||
}
|
||||
|
||||
static void make_closure(void *closure, int status)
|
||||
/* To l, append a 1-element list containing the string representation
|
||||
* of x
|
||||
*/
|
||||
static void append_double_string( LOL *l, double x )
|
||||
{
|
||||
push_state(&state_stack, (TARGET *)closure, NULL, T_STATE_MAKE1D)->status = status;
|
||||
char buffer[50];
|
||||
sprintf(buffer, "%f", x);
|
||||
lol_add( l, list_new( L0, newstr( buffer ) ) );
|
||||
}
|
||||
|
||||
/* Look up the __TIMING_RULE__ variable on the given target, and if
|
||||
* non-empty, invoke the rule it names, passing the given
|
||||
* timing_info
|
||||
*/
|
||||
static void call_timing_rule(TARGET* target, timing_info* time)
|
||||
{
|
||||
LIST* timing_rule;
|
||||
|
||||
pushsettings(target->settings);
|
||||
timing_rule = var_get( "__TIMING_RULE__" );
|
||||
popsettings(target->settings);
|
||||
|
||||
if (timing_rule)
|
||||
{
|
||||
/* We'll prepend $(__TIMING_RULE__[2-]) to the first argument */
|
||||
LIST* initial_args = list_copy( L0, timing_rule->next );
|
||||
|
||||
/* Prepare the argument list */
|
||||
FRAME frame[1];
|
||||
frame_init( frame );
|
||||
|
||||
/* First argument is the name of the timed target */
|
||||
lol_add( frame->args, list_new( initial_args, target->name ) );
|
||||
append_double_string(frame->args, time->user);
|
||||
append_double_string(frame->args, time->system);
|
||||
|
||||
if( lol_get( frame->args, 2 ) )
|
||||
evaluate_rule( timing_rule->string, frame );
|
||||
|
||||
/* Clean up */
|
||||
frame_free( frame );
|
||||
}
|
||||
}
|
||||
|
||||
static void make_closure(
|
||||
void *closure, int status, timing_info* time)
|
||||
{
|
||||
TARGET* built = (TARGET*)closure;
|
||||
|
||||
call_timing_rule(built, time);
|
||||
if (DEBUG_EXECCMD)
|
||||
printf("%f sec system; %f sec user\n", time->system, time->user);
|
||||
|
||||
push_state(&state_stack, built, NULL, T_STATE_MAKE1D)->status = status;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -178,6 +178,8 @@ struct _target {
|
||||
# define T_BIND_EXISTS 3 /* real file, timestamp valid */
|
||||
|
||||
TARGETS *depends; /* dependencies */
|
||||
TARGETS *dependents;/* the inverse of dependencies */
|
||||
TARGETS *rebuilds; /* targets that should be force-rebuilt whenever this one is */
|
||||
TARGET *includes; /* includes */
|
||||
TARGET *original_target; /* original_target->includes = this */
|
||||
char rescanned;
|
||||
@@ -198,14 +200,15 @@ struct _target {
|
||||
|
||||
# define T_FATE_BUILD 5 /* >= BUILD rebuilds target */
|
||||
# define T_FATE_TOUCHED 5 /* manually touched with -t */
|
||||
# define T_FATE_MISSING 6 /* is missing, needs updating */
|
||||
# define T_FATE_NEEDTMP 7 /* missing temp that must be rebuild */
|
||||
# define T_FATE_OUTDATED 8 /* is out of date, needs updating */
|
||||
# define T_FATE_UPDATE 9 /* deps updated, needs updating */
|
||||
# define T_FATE_REBUILD 6
|
||||
# define T_FATE_MISSING 7 /* is missing, needs updating */
|
||||
# define T_FATE_NEEDTMP 8 /* missing temp that must be rebuild */
|
||||
# define T_FATE_OUTDATED 9 /* is out of date, needs updating */
|
||||
# define T_FATE_UPDATE 10 /* deps updated, needs updating */
|
||||
|
||||
# define T_FATE_BROKEN 10 /* >= BROKEN ruins parents */
|
||||
# define T_FATE_CANTFIND 10 /* no rules to make missing target */
|
||||
# define T_FATE_CANTMAKE 11 /* can't find dependents */
|
||||
# define T_FATE_BROKEN 11 /* >= BROKEN ruins parents */
|
||||
# define T_FATE_CANTFIND 11 /* no rules to make missing target */
|
||||
# define T_FATE_CANTMAKE 12 /* can't find dependents */
|
||||
|
||||
char progress; /* tracks make1() progress */
|
||||
|
||||
|
||||
48
v2/test/rebuilds.py
Normal file
48
v2/test/rebuilds.py
Normal file
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This tests the typechecking facilities.
|
||||
|
||||
import BoostBuild
|
||||
|
||||
t = BoostBuild.Tester(pass_toolset=0)
|
||||
|
||||
t.write('file.jam', '''
|
||||
rule make
|
||||
{
|
||||
DEPENDS $(<) : $(>) ;
|
||||
DEPENDS all : $(<) ;
|
||||
}
|
||||
actions make
|
||||
{
|
||||
echo "******" making $(<) from $(>) "******"
|
||||
echo made from $(>) >> $(<)
|
||||
}
|
||||
|
||||
make aux1 : bar ;
|
||||
make foo : bar ;
|
||||
REBUILDS foo : bar ;
|
||||
make bar : baz ;
|
||||
make aux2 : bar ;
|
||||
''')
|
||||
|
||||
t.write('baz', 'nothing\n')
|
||||
|
||||
t.run_build_system('-ffile.jam bar')
|
||||
t.expect_addition('bar')
|
||||
t.expect_nothing_more()
|
||||
|
||||
t.run_build_system('-ffile.jam foo')
|
||||
t.expect_touch('bar')
|
||||
t.expect_addition('foo')
|
||||
t.expect_nothing_more()
|
||||
|
||||
t.run_build_system('-ffile.jam')
|
||||
t.expect_addition(['aux1', 'aux2'])
|
||||
t.expect_nothing_more()
|
||||
|
||||
t.touch('bar')
|
||||
t.run_build_system('-ffile.jam')
|
||||
t.expect_touch(['foo', 'aux1', 'aux2', 'bar'])
|
||||
t.expect_nothing_more()
|
||||
|
||||
t.cleanup()
|
||||
@@ -73,7 +73,9 @@ critical_tests = ["unit_tests", "module_actions", "startup_v1", "startup_v2"]
|
||||
critical_tests += ["core_d12", "core_typecheck", "core_delete_module",
|
||||
"core_varnames", "core_import_module"]
|
||||
|
||||
tests = [ "project_test3",
|
||||
tests = [ "rebuilds",
|
||||
"timedata",
|
||||
"project_test3",
|
||||
"project_test4",
|
||||
"generators_test",
|
||||
"dependency_test",
|
||||
|
||||
54
v2/test/timedata.py
Normal file
54
v2/test/timedata.py
Normal file
@@ -0,0 +1,54 @@
|
||||
#!/usr/bin/python
|
||||
|
||||
# This tests the typechecking facilities.
|
||||
|
||||
import BoostBuild
|
||||
|
||||
t = BoostBuild.Tester(pass_toolset=0)
|
||||
|
||||
t.write('file.jam', '''
|
||||
rule time
|
||||
{
|
||||
DEPENDS $(<) : $(>) ;
|
||||
__TIMING_RULE__ on $(>) = record_time $(<) ;
|
||||
DEPENDS all : $(<) ;
|
||||
}
|
||||
actions time
|
||||
{
|
||||
echo $(>) user: $(__USER_TIME__) system: $(__SYSTEM_TIME__)
|
||||
echo timed from $(>) >> $(<)
|
||||
}
|
||||
|
||||
rule record_time ( target source : user : system )
|
||||
{
|
||||
ECHO record_time called: $(target) / $(source) / $(user) / $(system) ;
|
||||
__USER_TIME__ on $(target) = $(user) ;
|
||||
__SYSTEM_TIME__ on $(target) = $(system) ;
|
||||
}
|
||||
|
||||
rule make
|
||||
{
|
||||
DEPENDS $(<) : $(>) ;
|
||||
}
|
||||
actions make
|
||||
{
|
||||
echo made from $(>) >> $(<)
|
||||
}
|
||||
|
||||
|
||||
time foo : bar ;
|
||||
make bar : baz ;
|
||||
''')
|
||||
|
||||
import re
|
||||
t.write('baz', 'nothing\n')
|
||||
t.run_build_system(
|
||||
'-ffile.jam',
|
||||
stdout=r'bar +user: [0-9\.]+ +system: +[0-9\.]+ *$',
|
||||
match = lambda actual,expected: re.search(expected,actual,re.DOTALL)
|
||||
)
|
||||
t.expect_addition('foo')
|
||||
t.expect_addition('bar')
|
||||
t.expect_nothing_more()
|
||||
|
||||
t.cleanup()
|
||||
Reference in New Issue
Block a user