diff options
Diffstat (limited to 'jam-files/engine/make1.c')
-rw-r--r-- | jam-files/engine/make1.c | 1145 |
1 files changed, 0 insertions, 1145 deletions
diff --git a/jam-files/engine/make1.c b/jam-files/engine/make1.c deleted file mode 100644 index 8001f333..00000000 --- a/jam-files/engine/make1.c +++ /dev/null @@ -1,1145 +0,0 @@ -/* - * Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc. - * - * This file is part of Jam - see jam.c for Copyright information. - */ - -/* This file is ALSO: - * Copyright 2001-2004 David Abrahams. - * Distributed under the Boost Software License, Version 1.0. - * (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) - */ - -/* - * make1.c - execute command to bring targets up to date - * - * This module contains make1(), the entry point called by make() to - * recursively decend the dependency graph executing update actions as - * marked by make0(). - * - * External routines: - * - * make1() - execute commands to update a TARGET and all of its dependencies. - * - * Internal routines, the recursive/asynchronous command executors: - * - * make1a() - recursively traverse dependency target tree, calling make1b(). - * make1atail() - started processing all dependencies so go on to make1b(). - * make1b() - when dependencies are up to date, build target with make1c(). - * make1c() - launch target's next command, call parents' make1b() if none. - * make1d() - handle command execution completion and call back make1c(). - * - * Internal support routines: - * - * make1cmds() - turn ACTIONS into CMDs, grouping, splitting, etc. - * make1list() - turn a list of targets into a LIST, for $(<) and $(>). - * make1settings() - for vars that get bound values, build up replacement lists. - * make1bind() - bind targets that weren't bound in dependency analysis. - * - * 04/16/94 (seiwald) - Split from make.c. - * 04/21/94 (seiwald) - Handle empty "updated" actions. - * 05/04/94 (seiwald) - async multiprocess (-j) support. - * 06/01/94 (seiwald) - new 'actions existing' does existing sources. - * 12/20/94 (seiwald) - NOTIME renamed NOTFILE. - * 01/19/95 (seiwald) - distinguish between CANTFIND/CANTMAKE targets. - * 01/22/94 (seiwald) - pass per-target JAMSHELL down to exec_cmd(). - * 02/28/95 (seiwald) - Handle empty "existing" actions. - * 03/10/95 (seiwald) - Fancy counts. - */ - -#include "jam.h" - -#include "lists.h" -#include "parse.h" -#include "assert.h" -#include "variable.h" -#include "rules.h" -#include "headers.h" - -#include "search.h" -#include "newstr.h" -#include "make.h" -#include "command.h" -#include "execcmd.h" -#include "compile.h" -#include "output.h" - -#include <stdlib.h> - -#if ! defined(NT) || defined(__GNUC__) - #include <unistd.h> /* for unlink */ -#endif - -static CMD * make1cmds ( TARGET * ); -static LIST * make1list ( LIST *, TARGETS *, int flags ); -static SETTINGS * make1settings( LIST * vars ); -static void make1bind ( TARGET * ); - -/* Ugly static - it is too hard to carry it through the callbacks. */ - -static struct -{ - int failed; - int skipped; - int total; - int made; -} counts[ 1 ] ; - -/* Target state - remove recursive calls by just keeping track of state target - * is in. - */ -typedef struct _state -{ - struct _state * prev; /* previous state on stack */ - TARGET * t; /* current target */ - TARGET * parent; /* parent argument necessary for make1a() */ -#define T_STATE_MAKE1A 0 /* make1a() should be called */ -#define T_STATE_MAKE1ATAIL 1 /* make1atail() should be called */ -#define T_STATE_MAKE1B 2 /* make1b() should be called */ -#define T_STATE_MAKE1C 3 /* make1c() should be called */ -#define T_STATE_MAKE1D 4 /* make1d() should be called */ - int curstate; /* current state */ - int status; -} state; - -static void make1a ( state * ); -static void make1atail ( state * ); -static void make1b ( state * ); -static void make1c ( state * ); -static void make1d ( state * ); -static void make_closure( void * closure, int status, timing_info *, char *, char * ); - -typedef struct _stack -{ - state * stack; -} stack; - -static stack state_stack = { NULL }; - -static state * state_freelist = NULL; - - -static state * alloc_state() -{ - if ( state_freelist != NULL ) - { - state * pState = state_freelist; - state_freelist = pState->prev; - memset( pState, 0, sizeof( state ) ); - return pState; - } - - return (state *)BJAM_MALLOC( sizeof( state ) ); -} - - -static void free_state( state * pState ) -{ - pState->prev = state_freelist; - state_freelist = pState; -} - - -static void clear_state_freelist() -{ - while ( state_freelist != NULL ) - { - state * pState = state_freelist; - state_freelist = state_freelist->prev; - BJAM_FREE( pState ); - } -} - - -static state * current_state( stack * pStack ) -{ - return pStack->stack; -} - - -static void pop_state( stack * pStack ) -{ - if ( pStack->stack != NULL ) - { - state * pState = pStack->stack->prev; - free_state( pStack->stack ); - pStack->stack = pState; - } -} - - -static state * push_state( stack * pStack, TARGET * t, TARGET * parent, int curstate ) -{ - state * pState = alloc_state(); - - pState->t = t; - pState->parent = parent; - pState->prev = pStack->stack; - pState->curstate = curstate; - - pStack->stack = pState; - - return pStack->stack; -} - - -/* - * Pushes a stack onto another stack, effectively reversing the order. - */ - -static void push_stack_on_stack( stack * pDest, stack * pSrc ) -{ - while ( pSrc->stack != NULL ) - { - state * pState = pSrc->stack; - pSrc->stack = pSrc->stack->prev; - pState->prev = pDest->stack; - pDest->stack = pState; - } -} - - -/* - * make1() - execute commands to update a TARGET and all of its dependencies. - */ - -static int intr = 0; - -int make1( TARGET * t ) -{ - state * pState; - - memset( (char *)counts, 0, sizeof( *counts ) ); - - /* Recursively make the target and its dependencies. */ - push_state( &state_stack, t, NULL, T_STATE_MAKE1A ); - - do - { - while ( ( pState = current_state( &state_stack ) ) != NULL ) - { - if ( intr ) - pop_state( &state_stack ); - - switch ( pState->curstate ) - { - case T_STATE_MAKE1A : make1a ( pState ); break; - case T_STATE_MAKE1ATAIL: make1atail( pState ); break; - case T_STATE_MAKE1B : make1b ( pState ); break; - case T_STATE_MAKE1C : make1c ( pState ); break; - case T_STATE_MAKE1D : make1d ( pState ); break; - } - } - } - /* Wait for any outstanding commands to finish running. */ - while ( exec_wait() ); - - clear_state_freelist(); - - /* Talk about it. */ - if ( counts->failed ) - printf( "...failed updating %d target%s...\n", counts->failed, - counts->failed > 1 ? "s" : "" ); - if ( DEBUG_MAKE && counts->skipped ) - printf( "...skipped %d target%s...\n", counts->skipped, - counts->skipped > 1 ? "s" : "" ); - if ( DEBUG_MAKE && counts->made ) - printf( "...updated %d target%s...\n", counts->made, - counts->made > 1 ? "s" : "" ); - - return counts->total != counts->made; -} - - -/* - * make1a() - recursively traverse target tree, calling make1b(). - * - * Called to start processing a specified target. Does nothing if the target is - * already being processed or otherwise starts processing all of its - * dependencies. Once all of its dependencies have started being processed goes - * on and calls make1b() (actually does that indirectly via a helper - * make1atail() state). - */ - -static void make1a( state * pState ) -{ - TARGET * t = pState->t; - TARGETS * c; - - /* If the parent is the first to try to build this target or this target is - * in the make1c() quagmire, arrange for the parent to be notified when this - * target is built. - */ - if ( pState->parent ) - switch ( pState->t->progress ) - { - case T_MAKE_INIT: - case T_MAKE_ACTIVE: - case T_MAKE_RUNNING: - pState->t->parents = targetentry( pState->t->parents, - pState->parent ); - ++pState->parent->asynccnt; - } - - /* If this target is already being processed then do nothing. There is no - * need to start processing the same target all over again. - */ - if ( pState->t->progress != T_MAKE_INIT ) - { - pop_state( &state_stack ); - return; - } - - /* Asynccnt counts the dependencies preventing this target from proceeding - * to make1b() for actual building. We start off with a count of 1 to - * prevent anything from happening until we can notify all dependencies that - * they are needed. This 1 is accounted for when we call make1b() ourselves, - * below. Without this if a a dependency gets built before we finish - * processing all of our other dependencies our build might be triggerred - * prematurely. - */ - pState->t->asynccnt = 1; - - /* Add header nodes created during the building process. */ - { - TARGETS * inc = 0; - for ( c = t->depends; c; c = c->next ) - if ( c->target->rescanned && c->target->includes ) - inc = targetentry( inc, c->target->includes ); - t->depends = targetchain( t->depends, inc ); - } - - /* Guard against circular dependencies. */ - pState->t->progress = T_MAKE_ONSTACK; - - { - stack temp_stack = { NULL }; - for ( c = t->depends; c && !intr; c = c->next ) - push_state( &temp_stack, c->target, pState->t, T_STATE_MAKE1A ); - - /* Using stacks reverses the order of execution. Reverse it back. */ - push_stack_on_stack( &state_stack, &temp_stack ); - } - - pState->curstate = T_STATE_MAKE1ATAIL; -} - - -/* - * make1atail() - started processing all dependencies so go on to make1b(). - */ - -static void make1atail( state * pState ) -{ - pState->t->progress = T_MAKE_ACTIVE; - /* Now that all of our dependencies have bumped up our asynccnt we can - * remove our own internal bump added to prevent this target from being - * built before all of its dependencies start getting processed. - */ - pState->curstate = T_STATE_MAKE1B; -} - - -/* - * make1b() - when dependencies are up to date, build target with make1c(). - * - * Called after all dependencies have started being processed and after each of - * them finishes its processing. The target actually goes on to getting built in - * make1c() only after all of its dependencies have finished their processing. - */ - -static void make1b( state * pState ) -{ - TARGET * t = pState->t; - TARGETS * c; - TARGET * failed = 0; - char * failed_name = "dependencies"; - - /* If any dependencies are still outstanding, wait until they call make1b() - * to signal their completion. - */ - if ( --pState->t->asynccnt ) - { - pop_state( &state_stack ); - return; - } - - /* Try to aquire a semaphore. If it is locked, wait until the target that - * locked it is built and signal completition. - */ -#ifdef OPT_SEMAPHORE - if ( t->semaphore && t->semaphore->asynccnt ) - { - /* Append 't' to the list of targets waiting on semaphore. */ - t->semaphore->parents = targetentry( t->semaphore->parents, t ); - t->asynccnt++; - - if ( DEBUG_EXECCMD ) - printf( "SEM: %s is busy, delaying launch of %s\n", - t->semaphore->name, t->name ); - pop_state( &state_stack ); - return; - } -#endif - - /* Now ready to build target 't', if dependencies built OK. */ - - /* Collect status from dependencies. */ - for ( c = t->depends; c; c = c->next ) - if ( c->target->status > t->status && !( c->target->flags & T_FLAG_NOCARE ) ) - { - failed = c->target; - pState->t->status = c->target->status; - } - /* If an internal header node failed to build, we want to output the target - * that it failed on. - */ - if ( failed ) - { - failed_name = failed->flags & T_FLAG_INTERNAL - ? failed->failed - : failed->name; - } - t->failed = failed_name; - - /* If actions for building any of the dependencies have failed, bail. - * Otherwise, execute all actions to make the current target. - */ - if ( ( pState->t->status == EXEC_CMD_FAIL ) && pState->t->actions ) - { - ++counts->skipped; - if ( ( pState->t->flags & ( T_FLAG_RMOLD | T_FLAG_NOTFILE ) ) == T_FLAG_RMOLD ) - { - if ( !unlink( pState->t->boundname ) ) - printf( "...removing outdated %s\n", pState->t->boundname ); - } - else - printf( "...skipped %s for lack of %s...\n", pState->t->name, failed_name ); - } - - 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: - */ - - case T_FATE_STABLE: - case T_FATE_NEWER: - break; - - case T_FATE_CANTFIND: - case T_FATE_CANTMAKE: - pState->t->status = EXEC_CMD_FAIL; - break; - - case T_FATE_ISTMP: - if ( DEBUG_MAKE ) - printf( "...using %s...\n", pState->t->name ); - break; - - case T_FATE_TOUCHED: - case T_FATE_MISSING: - case T_FATE_NEEDTMP: - case T_FATE_OUTDATED: - case T_FATE_UPDATE: - case T_FATE_REBUILD: - /* Prepare commands for executing actions scheduled for this target - * and then schedule transfer to make1c() state to proceed with - * executing the prepared commands. Commands have their embedded - * variables automatically expanded, including making use of any "on - * target" variables. - */ - if ( pState->t->actions ) - { - ++counts->total; - if ( DEBUG_MAKE && !( counts->total % 100 ) ) - printf( "...on %dth target...\n", counts->total ); - - pState->t->cmds = (char *)make1cmds( pState->t ); - /* Set the target's "progress" so that make1c() counts it among - * its successes/failures. - */ - pState->t->progress = T_MAKE_RUNNING; - } - 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 needed to - * build the target. If we are not going to build the target (due of - * dependency failures or no commands needing to be run) the chain will be - * empty and make1c() will directly signal the target's completion. - */ - -#ifdef OPT_SEMAPHORE - /* If there is a semaphore, indicate that it is in use. */ - if ( pState->t->semaphore ) - { - ++pState->t->semaphore->asynccnt; - if ( DEBUG_EXECCMD ) - printf( "SEM: %s now used by %s\n", pState->t->semaphore->name, - pState->t->name ); - } -#endif - - pState->curstate = T_STATE_MAKE1C; -} - - -/* - * make1c() - launch target's next command, call parents' make1b() if none. - * - * If there are (more) commands to run to build this target (and we have not hit - * an error running earlier comands) we launch the command using exec_cmd(). If - * there are no more commands to run, we collect the status from all the actions - * and report our completion to all the parents. - */ - -static void make1c( state * pState ) -{ - CMD * cmd = (CMD *)pState->t->cmds; - - if ( cmd && ( pState->t->status == EXEC_CMD_OK ) ) - { - char * rule_name = 0; - char * target = 0; - - if ( DEBUG_MAKEQ || - ( !( cmd->rule->actions->flags & RULE_QUIETLY ) && DEBUG_MAKE ) ) - { - rule_name = cmd->rule->name; - target = lol_get( &cmd->args, 0 )->string; - if ( globs.noexec ) - out_action( rule_name, target, cmd->buf, "", "", EXIT_OK ); - } - - if ( globs.noexec ) - { - pState->curstate = T_STATE_MAKE1D; - pState->status = EXEC_CMD_OK; - } - else - { - /* Pop state first because exec_cmd() could push state. */ - pop_state( &state_stack ); - exec_cmd( cmd->buf, make_closure, pState->t, cmd->shell, rule_name, - target ); - } - } - 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 their dependency has been built. */ - { - stack temp_stack = { NULL }; - TARGET * t = pState->t; - TARGET * additional_includes = NULL; - - t->progress = T_MAKE_DONE; - - /* Target has been updated so rescan it for 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. */ - 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 have already been processed, but they - * have not seen the internal node, because it was just - * created. We need to make the calls to make1a() that would - * have been made by the 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 would be considered - * built before this make1a() processing has 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, it is 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 is no - * point in notifying waiting targets, since they will be - * notified 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 ); - BJAM_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 ); - } - } -} - - -/* - * call_timing_rule() - 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 ) - { - /* rule timing-rule ( args * : target : start end user system ) */ - - /* Prepare the argument list. */ - FRAME frame[ 1 ]; - frame_init( frame ); - - /* args * :: $(__TIMING_RULE__[2-]) */ - lol_add( frame->args, list_copy( L0, timing_rule->next ) ); - - /* target :: the name of the target */ - lol_add( frame->args, list_new( L0, target->name ) ); - - /* start end user system :: info about the action command */ - lol_add( frame->args, list_new( list_new( list_new( list_new( L0, - outf_time ( time->start ) ), - outf_time ( time->end ) ), - outf_double( time->user ) ), - outf_double( time->system ) ) ); - - /* Call the rule. */ - evaluate_rule( timing_rule->string, frame ); - - /* Clean up. */ - frame_free( frame ); - } -} - - -/* - * call_action_rule() - Look up the __ACTION_RULE__ variable on the given - * target, and if non-empty, invoke the rule it names, passing the given info, - * timing_info, executed command and command output. - */ - -static void call_action_rule -( - TARGET * target, - int status, - timing_info * time, - char * executed_command, - char * command_output -) -{ - LIST * action_rule; - - pushsettings( target->settings ); - action_rule = var_get( "__ACTION_RULE__" ); - popsettings( target->settings ); - - if ( action_rule ) - { - /* rule action-rule ( - args * : - target : - command status start end user system : - output ? ) */ - - /* Prepare the argument list. */ - FRAME frame[ 1 ]; - frame_init( frame ); - - /* args * :: $(__ACTION_RULE__[2-]) */ - lol_add( frame->args, list_copy( L0, action_rule->next ) ); - - /* target :: the name of the target */ - lol_add( frame->args, list_new( L0, target->name ) ); - - /* command status start end user system :: info about the action command */ - lol_add( frame->args, - list_new( list_new( list_new( list_new( list_new( list_new( L0, - newstr( executed_command ) ), - outf_int( status ) ), - outf_time( time->start ) ), - outf_time( time->end ) ), - outf_double( time->user ) ), - outf_double( time->system ) ) ); - - /* output ? :: the output of the action command */ - if ( command_output ) - lol_add( frame->args, list_new( L0, newstr( command_output ) ) ); - else - lol_add( frame->args, L0 ); - - /* Call the rule. */ - evaluate_rule( action_rule->string, frame ); - - /* Clean up. */ - frame_free( frame ); - } -} - - -/* - * make_closure() - internal function passed as a notification callback for when - * commands finish getting executed by the OS. - */ - -static void make_closure -( - void * closure, - int status, - timing_info * time, - char * executed_command, - char * command_output -) -{ - TARGET * built = (TARGET *)closure; - - call_timing_rule( built, time ); - if ( DEBUG_EXECCMD ) - printf( "%f sec system; %f sec user\n", time->system, time->user ); - - call_action_rule( built, status, time, executed_command, command_output ); - - push_state( &state_stack, built, NULL, T_STATE_MAKE1D )->status = status; -} - - -/* - * make1d() - handle command execution completion and call back make1c(). - * - * exec_cmd() has completed and now all we need to do is fiddle with the status - * and call back to make1c() so it can run the next command scheduled for - * building this target or close up the target's build process in case there are - * no more commands scheduled for it. On interrupts, we bail heavily. - */ - -static void make1d( state * pState ) -{ - TARGET * t = pState->t; - CMD * cmd = (CMD *)t->cmds; - int status = pState->status; - - if ( t->flags & T_FLAG_FAIL_EXPECTED ) - { - /* Invert execution result when FAIL_EXPECTED has been applied. */ - switch ( status ) - { - case EXEC_CMD_FAIL: status = EXEC_CMD_OK ; break; - case EXEC_CMD_OK: status = EXEC_CMD_FAIL; break; - } - } - - 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; - - /* Print command text on failure. */ - 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" ); - } - - /* Treat failed commands as interrupts in case we were asked to stop the - * build in case of any errors. - */ - if ( ( status == EXEC_CMD_FAIL ) && globs.quitquick ) - ++intr; - - /* If the command was interrupted or failed and the target is not - * "precious", remove the targets. - */ - if (status != EXEC_CMD_OK) - { - LIST * targets = lol_get( &cmd->args, 0 ); - for ( ; targets; targets = list_next( targets ) ) - { - int need_unlink = 1; - TARGET* t = bindtarget ( targets->string ); - if (t->flags & T_FLAG_PRECIOUS) - { - need_unlink = 0; - } - if (need_unlink && !unlink( targets->string ) ) - printf( "...removing %s\n", targets->string ); - } - } - - /* Free this command and call make1c() to move onto the next one scheduled - * for building this same target. - */ - 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 using exec_cmd(). - */ - -static CMD * make1cmds( TARGET * t ) -{ - CMD * cmds = 0; - LIST * shell = 0; - module_t * settings_module = 0; - TARGET * settings_target = 0; - ACTIONS * a0; - - /* Step through actions. Actions may be shared with other targets or grouped - * using RULE_TOGETHER, so actions already seen are skipped. - */ - for ( a0 = t->actions ; a0; a0 = a0->next ) - { - RULE * rule = a0->action->rule; - rule_actions * actions = rule->actions; - SETTINGS * boundvars; - LIST * nt; - LIST * ns; - ACTIONS * a1; - int start; - int chunk; - int 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 is because the resulting command length is - * > MAXLINE. In this case, we will 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 is worth it to get as close to MAXLINE as possible, because - * launching the commands we are 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( "%" ) ) ); - fputs( cmd->buf, stdout ); - 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 a new list. */ - nl = list_new( nl, copystr( t->boundname ) ); - } - - /* Add to settings chain. */ - settings = addsettings( settings, VAR_SET, vars->string, nl ); - } - - return settings; -} - - -/* - * make1bind() - bind targets that were not bound during dependency analysis - * - * Spot the kludge! If a target is not in the dependency tree, it did not 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->flags & T_FLAG_ISFILE ) ); - t->binding = t->time ? T_BIND_EXISTS : T_BIND_MISSING; - popsettings( t->settings ); -} |