# include "jam.h"
# include "lists.h"
# include "parse.h"
# include "variable.h"
# include "rules.h"
# include "search.h"
# include "newstr.h"
# include "make.h"
# include "command.h"
# include "execcmd.h"
# include <unistd.h>
#ifdef APPLE_EXTENSIONS
# include <sys/time.h>
#endif
static void make1a();
static void make1b();
static void make1c();
static void make1d();
static CMD *make1cmds();
static int make1chunk();
static LIST *make1list();
static SETTINGS *make1settings();
static void make1bind();
static struct {
int failed;
int skipped;
int total;
int made;
} counts[1] ;
static int intr = 0;
int
make1( t )
TARGET *t;
{
memset( (char *)counts, 0, sizeof( *counts ) );
make1a( t, (TARGET *)0 );
while( execwait() )
;
#ifdef APPLE_EXTENSIONS
if( PARSABLE_OUTPUT ) {
pbx_printf( "STAT", "100.000\n");
pbx_printf( "JEND", "%i %i %i\n", counts->made, counts->failed, counts->skipped );
}
fflush(stdout);
#endif
#ifndef APPLE_EXTENSIONS
if( DEBUG_MAKE && counts->failed )
printf( "...failed updating %d target(s)...\n", counts->failed );
if( DEBUG_MAKE && counts->skipped )
printf( "...skipped %d target(s)...\n", counts->skipped );
if( DEBUG_MAKE && counts->made )
printf( "...updated %d target(s)...\n", counts->made );
#endif
return counts->total != counts->made;
}
static void
make1a( t, parent )
TARGET *t;
TARGET *parent;
{
TARGETS *c;
int i;
if( parent )
switch( t->progress )
{
case T_MAKE_INIT:
case T_MAKE_ACTIVE:
case T_MAKE_RUNNING:
t->parents = targetentry( t->parents, parent );
parent->asynccnt++;
}
if( t->progress != T_MAKE_INIT )
return;
t->asynccnt = 1;
t->progress = T_MAKE_ONSTACK;
for( i = T_DEPS_DEPENDS; i <= T_DEPS_INCLUDES; i++ )
for( c = t->deps[i]; c && !intr; c = c->next )
make1a( c->target, t );
t->progress = T_MAKE_ACTIVE;
make1b( t );
}
static void
make1b( t )
TARGET *t;
{
TARGETS *c;
int i;
char *failed = "dependents";
if( --t->asynccnt )
return;
for( i = T_DEPS_DEPENDS; i <= T_DEPS_INCLUDES; i++ )
for( c = t->deps[i]; c; c = c->next )
if( c->target->status > t->status )
{
failed = c->target->name;
t->status = c->target->status;
}
if( t->status == EXEC_CMD_FAIL && t->actions )
{
++counts->skipped;
printf( "...skipped %s for lack of %s...\n", t->name, failed );
}
if( t->status == EXEC_CMD_OK )
switch( t->fate )
{
case T_FATE_INIT:
case T_FATE_MAKING:
case T_FATE_STABLE:
case T_FATE_NEWER:
break;
case T_FATE_CANTFIND:
case T_FATE_CANTMAKE:
t->status = EXEC_CMD_FAIL;
break;
case T_FATE_ISTMP:
if( DEBUG_MAKE )
printf( "...using %s...\n", t->name );
break;
case T_FATE_TOUCHED:
case T_FATE_MISSING:
case T_FATE_OUTDATED:
case T_FATE_UPDATE:
if( t->actions )
{
counts->total++;
#ifdef APPLE_EXTENSIONS
#else
if( DEBUG_MAKE && !( counts->total % 100 ) )
printf( "...on %dth target...\n", counts->total );
#endif
pushsettings( t->settings );
t->cmds = (char *)make1cmds( t->actions );
popsettings( t->settings );
t->progress = T_MAKE_RUNNING;
}
break;
}
make1c( t );
}
static void
make1c( t )
TARGET *t;
{
CMD *cmd = (CMD *)t->cmds;
if( cmd && t->status == EXEC_CMD_OK )
{
if( DEBUG_MAKE )
if( DEBUG_MAKEQ || ! ( cmd->rule->flags & RULE_QUIETLY ) || PARSABLE_OUTPUT )
{
printf( "\n%s ", cmd->rule->name );
list_print( lol_get( &cmd->args, 0 ) );
printf( "\n" );
}
if( DEBUG_EXEC && !PARSABLE_OUTPUT)
printf( "%s", cmd->buf );
#ifdef APPLE_EXTENSIONS
if (PARSABLE_OUTPUT) {
unsigned i = 0;
LIST * l;
char prefix_string[] = "RLxx";
int next_cmd_slot;
if (cmd->rule->flags & RULE_QUIETLY) {
prefix_string[0] = 'r';
prefix_string[1] = 'l';
}
#define UNICHAR_INVISIBLE_SEPARATOR_STRING "\342\201\243"
#define UNICHAR_INVISIBLE_TIMES_STRING "\342\201\242"
next_cmd_slot = next_available_cmd_slot();
prefix_string[2] = '0' + next_cmd_slot / 10;
prefix_string[3] = '0' + next_cmd_slot % 10;
pbx_printf(prefix_string, "%s", cmd->rule->name);
while ((l = lol_get(&cmd->args, i++)) != NULL) {
LIST * l2 = l;
for( ; l2; l2 = list_next(l2)) {
if (ASCII_OUTPUT_ANNOTATION) {
printf( "\036" "%s", l2->string); }
else {
printf(UNICHAR_INVISIBLE_SEPARATOR_STRING "%s", l2->string);
}
}
}
unsigned cmd_buf_len = strlen(cmd->buf);
unsigned char * flattened_cmd_buf = malloc(cmd_buf_len * 3 + 1);
unsigned j = 0;
for (i = 0; i < cmd_buf_len; i++) {
unsigned char ch = cmd->buf[i];
if (ch == '\r' || ch == '\n') {
if (i < cmd_buf_len-1) {
if (ASCII_OUTPUT_ANNOTATION) {
flattened_cmd_buf[j++] = '\037';
}
else {
flattened_cmd_buf[j++] = '\342';
flattened_cmd_buf[j++] = '\201';
flattened_cmd_buf[j++] = '\242';
}
}
}
else {
flattened_cmd_buf[j++] = ch;
}
if (ch == '\r' && cmd->buf[i+1] == '\n') {
i++;
}
}
flattened_cmd_buf[j++] = '\0';
if (ASCII_OUTPUT_ANNOTATION) {
printf( "\036" "%s\n", flattened_cmd_buf);
}
else {
printf(UNICHAR_INVISIBLE_SEPARATOR_STRING "%s\n", flattened_cmd_buf);
}
free(flattened_cmd_buf);
}
if( globs.enable_timings ) {
cmd->timing_entry = create_timing_entry();
}
#endif
if( globs.noexec )
{
make1d( t, EXEC_CMD_OK );
}
else
{
fflush( stdout );
cmd->slot = next_available_cmd_slot();
execcmd( cmd->buf, make1d, t, cmd->shell, cmd->exportvars );
}
}
else
{
TARGETS *c;
ACTIONS *actions;
for( actions = t->actions; actions; actions = actions->next )
if( actions->action->status > t->status )
t->status = actions->action->status;
for( actions = t->actions; actions; actions = actions->next )
if( t->status > actions->action->status )
actions->action->status = t->status;
if( t->progress == T_MAKE_RUNNING )
switch( t->status )
{
case EXEC_CMD_OK:
++counts->made;
break;
case EXEC_CMD_FAIL:
++counts->failed;
break;
}
t->progress = T_MAKE_DONE;
for( c = t->parents; c; c = c->next )
make1b( c->target );
}
}
static void
make1d( t, status )
TARGET *t;
int status;
{
CMD *cmd = (CMD *)t->cmds;
#ifdef APPLE_EXTENSIONS
if( globs.enable_timings ) {
LIST *source_file_list, *obj_file_list;
char *source_file, *obj_file;
source_file_list = lol_get( &cmd->args, 1 );
source_file = source_file_list ? source_file_list->string : NULL;
obj_file_list = lol_get( &cmd->args, 0 );
obj_file = obj_file_list ? obj_file_list->string : NULL;
append_timing_entry( cmd->timing_entry, cmd->slot,
cmd->rule->name, source_file, obj_file );
}
#endif
#ifdef APPLE_EXTENSIONS
if (PARSABLE_OUTPUT) {
char prefix_string[] = "RExx";
prefix_string[2] = '0' + cmd->slot / 10;
prefix_string[3] = '0' + cmd->slot % 10;
pbx_printf(prefix_string, "%u %u %.3f %.3f %.3f 0 0\n", (status == 0), 0, 0.0, 0.0, 0.0, 0, 0);
}
#endif
#ifdef APPLE_EXTENSIONS
if (PARSABLE_OUTPUT) {
pbx_printf("STAT", "%.3f\n", counts->total * 100.0 / globs.num_targets_to_update);
}
#endif
if( status == EXEC_CMD_FAIL && ( cmd->rule->flags & RULE_IGNORE ) )
status = EXEC_CMD_OK;
if( status == EXEC_CMD_INTR )
++intr;
if( status == EXEC_CMD_FAIL && DEBUG_MAKE )
{
if( !DEBUG_EXEC && !PARSABLE_OUTPUT )
printf( "%s", cmd->buf );
printf( "...failed %s ", cmd->rule->name );
list_print( lol_get( &cmd->args, 0 ) );
printf( "...\n" );
}
if( status != EXEC_CMD_OK && !( cmd->rule->flags & RULE_TOGETHER ) )
{
LIST *targets = lol_get( &cmd->args, 0 );
for( ; targets; targets = list_next( targets ) )
if( !unlink( targets->string ) )
printf( "...removing %s\n", targets->string );
}
if( status != EXEC_CMD_OK && !globs.ignore ) {
#ifdef APPLE_EXTENSIONS
if( PARSABLE_OUTPUT )
pbx_printf( "JEND", "%i %i %i\n", counts->made, counts->failed, counts->skipped );
fflush(stdout);
#endif
exit(EXEC_CMD_FAIL);
}
t->status = status;
t->cmds = (char *)cmd_next( cmd );
cmd_free( cmd );
make1c( t );
}
static CMD *
make1cmds( a0 )
ACTIONS *a0;
{
CMD *cmds = 0;
LIST *shell = var_get( "JAMSHELL" );
for( ; a0; a0 = a0->next )
{
RULE *rule = a0->action->rule;
SETTINGS *boundvars;
int chunk = 0;
LIST *nt, *ns;
ACTIONS *a1;
if( !rule->actions || a0->action->running )
continue;
a0->action->running = 1;
nt = make1list( L0, a0->action->targets, 0 );
ns = make1list( L0, a0->action->sources, rule->flags );
if( rule->flags & RULE_TOGETHER )
for( a1 = a0->next; a1; a1 = a1->next )
if( a1->action->rule == rule && !a1->action->running )
{
ns = make1list( ns, a1->action->sources, rule->flags );
a1->action->running = 1;
}
if( !ns && ( rule->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) )
{
list_free( nt );
continue;
}
boundvars = make1settings( rule->bindlist );
pushsettings( boundvars );
if( rule->flags & RULE_PIECEMEAL )
chunk = make1chunk( rule->actions, nt, ns );
if( chunk < 0 )
{
printf( "fatal error: %s command line too long (max %d)\n",
rule->name, MAXLINE );
#ifdef APPLE_EXTENSIONS
if( PARSABLE_OUTPUT )
pbx_printf( "JEND", "%i %i %i\n", counts->made, counts->failed, counts->skipped );
fflush(stdout);
#endif
exit( EXITBAD );
}
if( chunk )
{
int start;
LIST *somes;
if( DEBUG_EXECCMD )
printf( "%s: %d args per exec\n", rule->name, chunk );
for( start = 0;
(somes = list_sublist( ns, start, chunk )) != NULL;
start += chunk )
{
cmds = cmd_new( cmds, rule,
list_copy( L0, nt ), somes,
list_copy( L0, shell ), (rule-> flags & RULE_EXPORTVARS) ? 1 : 0 );
}
list_free( nt );
list_free( ns );
}
else
{
cmds = cmd_new( cmds, rule, nt, ns, list_copy( L0, shell ), (rule-> flags & RULE_EXPORTVARS) ? 1 : 0 );
}
popsettings( boundvars );
freesettings( boundvars );
}
return cmds;
}
static int
make1chunk( cmd, targets, sources )
char *cmd;
LIST *targets;
LIST *sources;
{
int onesize;
int twosize;
int chunk = 0;
char buf[ MAXLINE ];
LOL lol;
lol_init( &lol );
lol.count = 2;
lol.list[0] = targets;
lol.list[1] = list_sublist( sources, 0, 1 );
onesize = var_string( cmd, buf, MAXLINE, &lol );
list_free( lol.list[1] );
if( onesize < 0 )
return -1;
lol.list[1] = list_sublist( sources, 0, 2 );
twosize = var_string( cmd, buf, MAXLINE, &lol );
list_free( lol.list[1] );
if( twosize < 0 )
return -1;
if( twosize > onesize )
chunk = 3 * ( MAXLINE - onesize ) / 5 / ( twosize - onesize ) + 1;
return chunk;
}
static LIST *
make1list( l, targets, flags )
LIST *l;
TARGETS *targets;
int flags;
{
for( ; targets; targets = targets->next )
{
TARGET *t = targets->target;
if( t->binding == T_BIND_UNBOUND )
make1bind( t, !( flags & RULE_EXISTING ) );
if( ( flags & RULE_EXISTING ) && t->binding != T_BIND_EXISTS )
continue;
if( ( flags & RULE_NEWSRCS ) && t->fate <= T_FATE_STABLE )
continue;
if( flags & RULE_TOGETHER )
{
LIST *m;
for( m = l; m; m = m->next )
if( !strcmp( m->string, t->boundname ) )
break;
if( m )
continue;
}
l = list_new( l, copystr( t->boundname ) );
}
return l;
}
static SETTINGS *
make1settings( vars )
LIST *vars;
{
SETTINGS *settings = 0;
for( ; vars; vars = list_next( vars ) )
{
LIST *l = var_get( vars->string );
LIST *nl = 0;
for( ; l; l = list_next( l ) )
{
TARGET *t = bindtarget( l->string );
if( t->binding == T_BIND_UNBOUND )
make1bind( t, 1 );
nl = list_new( nl, copystr( t->boundname ) );
}
settings = addsettings( settings, 0, vars->string, nl );
}
return settings;
}
static void
make1bind( t, warn )
TARGET *t;
int warn;
{
if( t->flags & T_FLAG_NOTFILE )
return;
if( warn )
printf( "warning: using independent target %s\n", t->name );
pushsettings( t->settings );
t->boundname = search( t->name, &t->time );
t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
popsettings( t->settings );
}