2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-16 01:12:13 +00:00
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:
Dave Abrahams
2005-02-12 02:30:18 +00:00
parent 0360896b09
commit 5539d6e2b9
21 changed files with 871 additions and 305 deletions

View File

@@ -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;
}

View File

@@ -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 );

View File

@@ -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 );

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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:
*/

View File

@@ -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;

View File

@@ -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;
}
/*

View File

@@ -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 */

View File

@@ -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;
}

View File

@@ -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 );

View File

@@ -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 );

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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:
*/

View File

@@ -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;

View File

@@ -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;
}
/*

View File

@@ -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
View 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()

View File

@@ -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
View 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()