📄 make1.c
字号:
else
{
TARGETS *c;
ACTIONS *actions;
/* Collect status from actions, and distribute it as well */
for( actions = pState->t->actions; actions; actions = actions->next )
if( actions->action->status > pState->t->status )
pState->t->status = actions->action->status;
for( actions = pState->t->actions; actions; actions = actions->next )
if( pState->t->status > actions->action->status )
actions->action->status = pState->t->status;
/* Tally success/failure for those we tried to update. */
if( pState->t->progress == T_MAKE_RUNNING )
switch( pState->t->status )
{
case EXEC_CMD_OK:
++counts->made;
break;
case EXEC_CMD_FAIL:
++counts->failed;
break;
}
/* Tell parents dependent has been built */
{
stack temp_stack = { NULL };
TARGET *t = pState->t;
TARGET* additional_includes = NULL;
t->progress = T_MAKE_DONE;
/* Target was updated. Rescan dependencies. */
if (t->fate >= T_FATE_MISSING &&
t->status == EXEC_CMD_OK &&
!t->rescanned) {
TARGET *target_to_rescan = t;
SETTINGS *s;
target_to_rescan->rescanned = 1;
if (target_to_rescan->flags & T_FLAG_INTERNAL) {
target_to_rescan = t->original_target;
}
/* Clean current includes */
if (target_to_rescan->includes) {
target_to_rescan->includes = 0;
}
s = copysettings( target_to_rescan->settings );
pushsettings( s );
headers(target_to_rescan);
popsettings( s );
freesettings( s );
if (target_to_rescan->includes) {
target_to_rescan->includes->rescanned = 1;
/* Tricky. The parents were already processed, but they
did not seen the internal node, because it was just
created. We need to make the calls to make1a that would
have been done by parents here, and also make sure all
unprocessed parents will pick up the includes. We must
make sure processing of the additional make1a invocations
is done before make1b which means this target is built,
otherwise the parent will be considered built before this
make1a processing is even started.
*/
make0(target_to_rescan->includes, target_to_rescan->parents->target, 0, 0, 0);
for( c = target_to_rescan->parents; c; c = c->next) {
c->target->depends = targetentry( c->target->depends,
target_to_rescan->includes );
}
/* Will be processed below. */
additional_includes = target_to_rescan->includes;
}
}
if (additional_includes)
for ( c = t->parents; c; c = c->next ) {
push_state(&temp_stack, additional_includes, c->target, T_STATE_MAKE1A);
}
for( c = t->parents; c; c = c->next ) {
push_state(&temp_stack, c->target, NULL, T_STATE_MAKE1B);
}
#ifdef OPT_SEMAPHORE
/* If there is a semaphore, its now free */
if( t->semaphore )
{
assert( t->semaphore->asynccnt == 1 );
--(t->semaphore->asynccnt);
if( DEBUG_EXECCMD )
printf( "SEM: %s is now free\n", t->semaphore->name);
/* If anything is waiting, notify the next target. There's no
point in notifying all waiting targets, since they'll be
serialized again. */
if( t->semaphore->parents )
{
TARGETS *first = t->semaphore->parents;
if( first->next )
first->next->tail = first->tail;
t->semaphore->parents = first->next;
if( DEBUG_EXECCMD )
printf( "SEM: placing %s on stack\n", first->target->name);
push_state(&temp_stack, first->target, NULL, T_STATE_MAKE1B);
free( first );
}
}
#endif
/* must pop state before pushing any more */
pop_state(&state_stack);
/* using stacks reverses the order of execution. Reverse it back */
push_stack_on_stack(&state_stack, &temp_stack);
}
}
}
static void make_closure(void *closure, int status)
{
push_state(&state_stack, (TARGET *)closure, NULL, T_STATE_MAKE1D)->status = status;
}
/*
* make1d() - handle command execution completion and call back make1c()
*/
static void
make1d(state *pState)
{
TARGET *t = pState->t;
CMD *cmd = (CMD *)t->cmds;
int status = pState->status;
/* Execcmd() has completed. All we need to do is fiddle with the */
/* status and signal our completion so make1c() can run the next */
/* command. On interrupts, we bail heavily. */
if ( t->flags & T_FLAG_FAIL_EXPECTED )
{
/* invert execution result when FAIL_EXPECTED was applied */
switch (status)
{
case EXEC_CMD_FAIL: status = EXEC_CMD_OK; break;
case EXEC_CMD_OK: status = EXEC_CMD_FAIL; break;
default:
;
}
}
if( status == EXEC_CMD_FAIL && ( cmd->rule->actions->flags & RULE_IGNORE ) )
status = EXEC_CMD_OK;
/* On interrupt, set intr so _everything_ fails */
if( status == EXEC_CMD_INTR )
++intr;
if( status == EXEC_CMD_FAIL && DEBUG_MAKE )
{
/* Print command text on failure */
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_FAIL)
if( globs.quitquick ) ++intr;
/* If the command was interrupted or failed and the target */
/* is not "precious", remove the targets */
if( status != EXEC_CMD_OK && !( cmd->rule->actions->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 );
}
/* Free this command and call make1c() to move onto next command. */
t->status = status;
t->cmds = (char *)cmd_next( cmd );
cmd_free( cmd );
pState->curstate = T_STATE_MAKE1C;
}
/*
* swap_settings() - replace the settings from the current module and
* target with those from the new module and target
*/
static void swap_settings(
module_t** current_module
, TARGET** current_target
, module_t* new_module
, TARGET* new_target)
{
if (new_module == root_module())
new_module = 0;
if (new_target == *current_target && new_module == *current_module)
return;
if (*current_target)
popsettings( (*current_target)->settings );
if (new_module != *current_module)
{
if (*current_module)
exit_module( *current_module );
*current_module = new_module;
if (new_module)
enter_module( new_module );
}
*current_target = new_target;
if (new_target)
pushsettings( new_target->settings );
}
/*
* make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc
*
* Essentially copies a chain of ACTIONs to a chain of CMDs,
* grouping RULE_TOGETHER actions, splitting RULE_PIECEMEAL actions,
* and handling RULE_NEWSRCS actions. The result is a chain of
* CMDs which can be expanded by var_string() and executed with
* execcmd().
*/
static CMD *
make1cmds( TARGET *t )
{
CMD *cmds = 0;
LIST *shell = 0;
module_t *settings_module = 0;
TARGET *settings_target = 0;
/* Step through actions */
/* Actions may be shared with other targets or grouped with */
/* RULE_TOGETHER, so actions already seen are skipped. */
ACTIONS* a0;
for(a0 = t->actions ; a0; a0 = a0->next )
{
RULE *rule = a0->action->rule;
rule_actions *actions = rule->actions;
SETTINGS *boundvars;
LIST *nt, *ns;
ACTIONS *a1;
int start, chunk, length;
/* Only do rules with commands to execute. */
/* If this action has already been executed, use saved status */
if( !actions || a0->action->running )
continue;
a0->action->running = 1;
/* Make LISTS of targets and sources */
/* If `execute together` has been specified for this rule, tack */
/* on sources from each instance of this rule for this target. */
nt = make1list( L0, a0->action->targets, 0 );
ns = make1list( L0, a0->action->sources, actions->flags );
if( actions->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, actions->flags );
a1->action->running = 1;
}
/* If doing only updated (or existing) sources, but none have */
/* been updated (or exist), skip this action. */
if( !ns && ( actions->flags & ( RULE_NEWSRCS | RULE_EXISTING ) ) )
{
list_free( nt );
continue;
}
swap_settings( &settings_module, &settings_target, rule->module, t );
if (!shell)
shell = var_get( "JAMSHELL" ); /* shell is per-target */
/* If we had 'actions xxx bind vars' we bind the vars now */
boundvars = make1settings( actions->bindlist );
pushsettings( boundvars );
/*
* Build command, starting with all source args.
*
* If cmd_new returns 0, it's because the resulting command
* length is > MAXLINE. In this case, we'll slowly reduce
* the number of source arguments presented until it does
* fit. This only applies to actions that allow PIECEMEAL
* commands.
*
* While reducing slowly takes a bit of compute time to get
* things just right, it's worth it to get as close to MAXLINE
* as possible, because launching the commands we're executing
* is likely to be much more compute intensive!
*
* Note we loop through at least once, for sourceless actions.
*/
start = 0;
chunk = length = list_length( ns );
do
{
/* Build cmd: cmd_new consumes its lists. */
CMD *cmd = cmd_new( rule,
list_copy( L0, nt ),
list_sublist( ns, start, chunk ),
list_copy( L0, shell ) );
if( cmd )
{
/* It fit: chain it up. */
if( !cmds ) cmds = cmd;
else cmds->tail->next = cmd;
cmds->tail = cmd;
start += chunk;
}
else if( ( actions->flags & RULE_PIECEMEAL ) && chunk > 1 )
{
/* Reduce chunk size slowly. */
chunk = chunk * 9 / 10;
}
else
{
/* Too long and not splittable. */
printf( "%s actions too long (max %d):\n",
rule->name, MAXLINE );
/* Tell the user what didn't fit */
cmd = cmd_new(
rule, list_copy( L0, nt ),
list_sublist( ns, start, chunk ),
list_new( L0, newstr( "%" ) ) );
printf( cmd->buf );
exit( EXITBAD );
}
}
while( start < length );
/* These were always copied when used. */
list_free( nt );
list_free( ns );
/* Free the variables whose values were bound by */
/* 'actions xxx bind vars' */
popsettings( boundvars );
freesettings( boundvars );
}
swap_settings( &settings_module, &settings_target, 0, 0 );
return cmds;
}
/*
* make1list() - turn a list of targets into a LIST, for $(<) and $(>)
*/
static LIST *
make1list(
LIST *l,
TARGETS *targets,
int flags )
{
for( ; targets; targets = targets->next )
{
TARGET *t = targets->target;
/* Sources to 'actions existing' are never in the dependency */
/* graph (if they were, they'd get built and 'existing' would */
/* be superfluous, so throttle warning message about independent */
/* targets. */
if( t->binding == T_BIND_UNBOUND )
make1bind( t, !( flags & RULE_EXISTING ) );
if ( ( flags & RULE_EXISTING ) && ( flags & RULE_NEWSRCS ) )
{
if ( t->binding != T_BIND_EXISTS && t->fate <= T_FATE_STABLE)
continue;
}
else
{
if( ( flags & RULE_EXISTING ) && t->binding != T_BIND_EXISTS )
continue;
if( ( flags & RULE_NEWSRCS ) && t->fate <= T_FATE_STABLE )
continue;
}
/* Prohibit duplicates for RULE_TOGETHER */
if( flags & RULE_TOGETHER )
{
LIST *m;
for( m = l; m; m = m->next )
if( !strcmp( m->string, t->boundname ) )
break;
if( m )
continue;
}
/* Build new list */
l = list_new( l, copystr( t->boundname ) );
}
return l;
}
/*
* make1settings() - for vars that get bound values, build up replacement lists
*/
static SETTINGS *
make1settings( 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 );
/* Make sure the target is bound, warning if it is not in the */
/* dependency graph. */
if( t->binding == T_BIND_UNBOUND )
make1bind( t, 1 );
/* Build new list */
nl = list_new( nl, copystr( t->boundname ) );
}
/* Add to settings chain */
settings = addsettings( settings, 0, vars->string, nl );
}
return settings;
}
/*
* make1bind() - bind targets that weren't bound in dependency analysis
*
* Spot the kludge! If a target is not in the dependency tree, it didn't
* get bound by make0(), so we have to do it here. Ugly.
*/
static void
make1bind(
TARGET *t,
int warn )
{
if( t->flags & T_FLAG_NOTFILE )
return;
/* Sources to 'actions existing' are never in the dependency */
/* graph (if they were, they'd get built and 'existing' would */
/* be superfluous, so throttle warning message about independent */
/* targets. */
if( warn )
printf( "warning: using independent target %s\n", t->name );
pushsettings( t->settings );
t->boundname = search( t->name, &t->time, 0 );
t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING;
popsettings( t->settings );
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -