make1.c
来自「C++的一个好库。。。现在很流行」· C语言 代码 · 共 1,092 行 · 第 1/2 页
C
1,092 行
/* 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);
}
}
}
/* To l, append a 1-element list containing the string representation
* of x
*/
static void append_double_string( LOL *l, double x )
{
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;
}
/*
* 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;
if( t->binding == T_BIND_UNBOUND )
make1bind( t );
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 */
if( t->binding == T_BIND_UNBOUND )
make1bind( t );
/* 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 )
{
if( t->flags & T_FLAG_NOTFILE )
return;
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 + =
减小字号Ctrl + -
显示快捷键?