# 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>
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( "JEND", "%i %i %i\n", counts->made, counts->failed, counts->skipped );
#endif
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 );
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
if( PARSABLE_OUTPUT )
pbx_printf( "JUPD", "%i\n", counts->total );
#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 )
{
#ifdef APPLE_EXTENSIONS
if( PARSABLE_OUTPUT )
{
unsigned i = 0;
LIST *l;
pbx_printf( ( cmd->rule->flags & RULE_QUIETLY ) ? "rl00" : "RL00", "%s|", cmd->rule->name );
while ( ( l = lol_get( &cmd->args, i++ ) ) != NULL )
printf( "%s|", l->string );
printf( "\n" );
}
#endif
if( DEBUG_MAKE )
if( DEBUG_MAKEQ || ! ( cmd->rule->flags & RULE_QUIETLY ) )
{
printf( "%s ", cmd->rule->name );
list_print( lol_get( &cmd->args, 0 ) );
printf( "\n" );
}
if( DEBUG_EXEC )
printf( "%s\n", cmd->buf );
#ifdef APPLE_EXTENSIONS
if( PARSABLE_OUTPUT )
{
pbx_printf( "CB00", "%s\n", cmd->buf );
pbx_printf( "CO00", "\n" );
}
#endif
if( globs.noexec )
{
make1d( t, EXEC_CMD_OK );
}
else
{
fflush( stdout );
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( PARSABLE_OUTPUT )
pbx_printf( "CE00", "\n" );
#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 )
printf( "%s\n", 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 )
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 );
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 );
}