Exemple #1
0
LIST *order( PARSE *parse, FRAME *frame )
{
    LIST* arg = lol_get( frame->args, 0 );  
    LIST* tmp;
    LIST* result = 0;
    int src, dst;

    /* We need to create a graph of order dependencies between
       the passed objects. We assume that there are no duplicates
       passed to 'add_pair'.
    */
    int length = list_length(arg);
    int** graph = (int**)calloc(length, sizeof(int*));
    int* order = (int*)malloc((length+1)*sizeof(int));
    if ( DEBUG_PROFILE )
        profile_memory( length*sizeof(int*) + (length+1)*sizeof(int) );
   
    for(tmp = arg, src = 0; tmp; tmp = tmp->next, ++src) {
        /* For all object this one depend upon, add elements
           to 'graph' */
        LIST* dependencies = var_get(tmp->string);
        int index = 0;

        graph[src] = (int*)calloc(list_length(dependencies)+1, sizeof(int));
        if ( DEBUG_PROFILE )
            profile_memory( (list_length(dependencies)+1)*sizeof(int) );
        for(; dependencies; dependencies = dependencies->next) {          
            int dst = list_index(arg, dependencies->string);
            if (dst != -1)
                graph[src][index++] = dst;
        }
        graph[src][index] = -1;               
    }

    topological_sort(graph, length, order);

    {
        int index = length-1;
        for(; index >= 0; --index) {
            int i;
            tmp = arg;
            for (i = 0; i < order[index]; ++i, tmp = tmp->next);
            result = list_new(result, tmp->string);
        }
    }

    /* Clean up */
    {
        int i;
        for(i = 0; i < length; ++i)
            free(graph[i]);
        free(graph);
        free(order);
    }

    return result;
}
Exemple #2
0
/*
 * args_new() - make a new reference-counted argument list
 */
argument_list* args_new()
{
    argument_list* r = (argument_list*)malloc( sizeof(argument_list) );
    if ( DEBUG_PROFILE )
        profile_memory( sizeof(argument_list) );
    r->reference_count = 0;
    lol_init(r->data);
    return r;
}
Exemple #3
0
static rule_actions* actions_new( char* command, LIST* bindlist, int flags )
{
    rule_actions* result = (rule_actions*)malloc(sizeof(rule_actions));
    if ( DEBUG_PROFILE )
        profile_memory( sizeof(rule_actions) );
    result->command = copystr( command );
    result->bindlist = bindlist;
    result->flags = flags;
    result->reference_count = 0;
    return result;
}
Exemple #4
0
SETTINGS *
addsettings(
    SETTINGS *head,
    int	append,
    char	*symbol,
    LIST	*value )
{
    SETTINGS *v;

    /* Look for previous setting */

    for( v = head; v; v = v->next )
        if( !strcmp( v->symbol, symbol ) )
            break;

    /* If not previously set, alloc a new. */
    /* If appending, do so. */
    /* Else free old and set new. */

    if( !v )
    {
        v = settings_freelist;

        if ( v )
            settings_freelist = v->next;
        else
        {
            v = (SETTINGS *)malloc( sizeof( *v ) );
            if ( DEBUG_PROFILE )
                profile_memory( sizeof( *v ) );
        }

        v->symbol = newstr( symbol );
        v->value = value;
        v->next = head;
        head = v;
    }
    else if( append )
    {
        v->value = list_append( v->value, value );
    }
    else
    {
        list_free( v->value );
        v->value = value;
    }

    /* Return (new) head of list. */

    return head;
}
Exemple #5
0
void topological_sort(int** graph, int num_vertices, int* result)
{
    int i;
    int* colors = (int*)calloc(num_vertices, sizeof(int));
    if ( DEBUG_PROFILE )
        profile_memory( num_vertices*sizeof(int) );
    for (i = 0; i < num_vertices; ++i)
        colors[i] = white;

    for(i = 0; i < num_vertices; ++i)
        if (colors[i] == white)
            do_ts(graph, i, colors, &result);

    free(colors);
}
Exemple #6
0
TARGET *
copytarget( const TARGET *ot )
{
    TARGET *t;

    t = (TARGET *)malloc( sizeof( *t ) );
    if ( DEBUG_PROFILE )
        profile_memory( sizeof( *t ) );
    memset( (char *)t, '\0', sizeof( *t ) );
    t->name = copystr( ot->name );
    t->boundname = t->name;

    t->flags |= T_FLAG_NOTFILE | T_FLAG_INTERNAL;

    return t;
}
Exemple #7
0
ACTIONS *
actionlist(
    ACTIONS	*chain,
    ACTION	*action )
{
    ACTIONS *actions = (ACTIONS *)malloc( sizeof( ACTIONS ) );
    if ( DEBUG_PROFILE )
        profile_memory( sizeof( ACTIONS ) );

    actions->action = action;

    if( !chain ) chain = actions;
    else chain->tail->next = actions;
    chain->tail = actions;
    actions->next = 0;

    return chain;
}
Exemple #8
0
static state *alloc_state()
{
	if(state_freelist != NULL)
	{
		state *pState;

		pState = state_freelist;
		state_freelist = pState->prev;
		memset(pState, 0, sizeof(state));
		return pState;
	}
	else
	{
        if ( DEBUG_PROFILE )
            profile_memory( sizeof(state) );
		return (state *)malloc(sizeof(state));
	}
}
Exemple #9
0
TARGETS *
targetentry(
    TARGETS	*chain,
    TARGET	*target )
{
    TARGETS *c;

    c = (TARGETS *)malloc( sizeof( TARGETS ) );
    if ( DEBUG_PROFILE )
        profile_memory( sizeof( TARGETS ) );
    c->target = target;

    if( !chain ) chain = c;
    else chain->tail->next = c;
    chain->tail = c;
    c->next = 0;

    return chain;
}
Exemple #10
0
void
yyfparse( char *s )
{
	struct include *i = (struct include *)malloc( sizeof( *i ) );
    if ( DEBUG_PROFILE )
        profile_memory( sizeof( *i ) );

	/* Push this onto the incp chain. */

	i->string = "";
	i->strings = 0;
	i->file = 0;
	i->fname = copystr( s );
	i->line = 0;
	i->next = incp;
	incp = i;

	/* If the filename is "+", it means use the internal jambase. */

	if( !strcmp( s, "+" ) )
	    i->strings = jambase;
}
Exemple #11
0
CMD *
cmd_new(
	RULE	*rule,
	LIST	*targets,
	LIST	*sources,
	LIST	*shell )
{
    CMD *cmd = (CMD *)malloc( sizeof( CMD ) );
    /* lift line-length limitation entirely when JAMSHELL is just "%" */
    int no_limit = ( shell && !strcmp(shell->string,"%") && !list_next(shell) );
    int max_line = MAXLINE;
    int allocated = -1;

    if ( DEBUG_PROFILE )
        profile_memory( sizeof( CMD ) );

    cmd->rule = rule;
    cmd->shell = shell;
    cmd->next = 0;

    lol_init( &cmd->args );
    lol_add( &cmd->args, targets );
    lol_add( &cmd->args, sources );
    cmd->buf = 0;

    do
    {
        free(cmd->buf); /* free any buffer from previous iteration */
        
        cmd->buf = (char*)malloc(max_line + 1);

        if ( DEBUG_PROFILE )
            profile_memory( max_line + 1 );
        
        if (cmd->buf == 0)
            break;
        
        allocated = var_string( rule->actions->command, cmd->buf, max_line, &cmd->args );
        
        max_line = max_line * 2;
    }
    while( allocated < 0 && max_line < INT_MAX / 2 );

    if ( !no_limit )
    {
        /* Bail if the result won't fit in MAXLINE */
        char *s = cmd->buf;
        while ( *s )
        {
            size_t l = strcspn( s, "\n" );
            
            if ( l > MAXLINE )
            {
                /* We don't free targets/sources/shell if bailing. */
                cmd_free( cmd );
                return 0;
            }
            
            s += l;
            if ( *s )
                ++s;
        }
    }

    return cmd;
}
Exemple #12
0
void
execcmd( 
	char *string,
	void (*func)( void *closure, int status, timing_info* ),
	void *closure,
	LIST *shell )
{
    int pid;
    int slot;
    int raw_cmd = 0 ;
    char *argv_static[ MAXARGC + 1 ];	/* +1 for NULL */
    char **argv = argv_static;
    char *p;

    /* Check to see if we need to hack around the line-length limitation. */
    /* Look for a JAMSHELL setting of "%", indicating that the command
     * should be invoked directly */
    if ( shell && !strcmp(shell->string,"%") && !list_next(shell) )
    {
        raw_cmd = 1;
        shell = 0;
    }

    if ( !is_win95_defined )
        set_is_win95();
          
    /* Find a slot in the running commands table for this one. */
    if ( is_win95 )
    {
        /* only synchronous spans are supported on Windows 95/98 */
        slot = 0;
    }
    else
    {
        for( slot = 0; slot < MAXJOBS; slot++ )
            if( !cmdtab[ slot ].pid )
                break;
    }
    if( slot == MAXJOBS )
    {
        printf( "no slots for child!\n" );
        exit( EXITBAD );
    }
  
    if( !cmdtab[ slot ].tempfile )
    {
        const char *tempdir = path_tmpdir();
        DWORD procID = GetCurrentProcessId();
  
        /* SVA - allocate 64 other just to be safe */
        cmdtab[ slot ].tempfile = malloc( strlen( tempdir ) + 64 );
        if ( DEBUG_PROFILE )
            profile_memory( strlen( tempdir ) + 64 );
  
        sprintf( cmdtab[ slot ].tempfile, "%s\\jam%d-%02d.bat", 
                 tempdir, procID, slot );		
    }

    /* Trim leading, ending white space */

    while( isspace( *string ) )
        ++string;

    /* Write to .BAT file unless the line would be too long and it
     * meets the other spawnability criteria.
     */
    if( raw_cmd && can_spawn( string ) >= MAXLINE )
    {
        if( DEBUG_EXECCMD )
            printf("Executing raw command directly\n");        
    }
    else
    {
        FILE *f = 0;
        int tries = 0;
        raw_cmd = 0;
        
        /* Write command to bat file. For some reason this open can
           fails intermitently. But doing some retries works. Most likely
           this is due to a previously existing file of the same name that
           happens to be opened by an active virus scanner. Pointed out,
           and fix by Bronek Kozicki. */
        for (; !f && tries < 4; ++tries)
        {
            f = fopen( cmdtab[ slot ].tempfile, "w" );
            if ( !f && tries < 4 ) Sleep( 250 );
        }
        if (!f)
        {
            printf( "failed to write command file!\n" );
            exit( EXITBAD );
        }
        fputs( string, f );
        fclose( f );

        string = cmdtab[ slot ].tempfile;
        
        if( DEBUG_EXECCMD )
        {
            if (shell)
                printf("using user-specified shell: %s", shell->string);
            else
                printf("Executing through .bat file\n");
        }
    }

    /* Forumulate argv */
    /* If shell was defined, be prepared for % and ! subs. */
    /* Otherwise, use stock /bin/sh (on unix) or cmd.exe (on NT). */

    if( shell )
    {
        int i;
        char jobno[4];
        int gotpercent = 0;

        sprintf( jobno, "%d", slot + 1 );

        for( i = 0; shell && i < MAXARGC; i++, shell = list_next( shell ) )
        {
            switch( shell->string[0] )
            {
            case '%':	argv[i] = string; gotpercent++; break;
            case '!':	argv[i] = jobno; break;
            default:	argv[i] = shell->string;
            }
            if( DEBUG_EXECCMD )
                printf( "argv[%d] = '%s'\n", i, argv[i] );
        }

        if( !gotpercent )
            argv[i++] = string;

        argv[i] = 0;
    }
    else if (raw_cmd)
    {
        argv = string_to_args(string);
    }
    else
    {
        /* don't worry, this is ignored on Win95/98, see later.. */
        argv[0] = "cmd.exe";
        argv[1] = "/Q/C";		/* anything more is non-portable */
        argv[2] = string;
        argv[3] = 0;
    }

    /* Catch interrupts whenever commands are running. */

    if( !cmdsrunning++ )
        istat = signal( SIGINT, onintr );

    /* Start the command */

    /* on Win95, we only do a synchronous call */
    if ( is_win95 )
    {
        static const char* hard_coded[] =
            {
                "del", "erase", "copy", "mkdir", "rmdir", "cls", "dir",
                "ren", "rename", "move", 0
            };
          
        const char**  keyword;
        int           len, spawn = 1;
        int           result;
        timing_info time = {0,0};
          
        for ( keyword = hard_coded; keyword[0]; keyword++ )
        {
            len = strlen( keyword[0] );
            if ( strnicmp( string, keyword[0], len ) == 0 &&
                 !isalnum(string[len]) )
            {
                /* this is one of the hard coded symbols, use 'system' to run */
                /* them.. except for "del"/"erase"                            */
                if ( keyword - hard_coded < 2 )
                    result = process_del( string );
                else
                    result = system( string );

                spawn  = 0;
                break;
            }
        }
          
        if (spawn)
        {
            char**  args;
            
            /* convert the string into an array of arguments */
            /* we need to take care of double quotes !!      */
            args = string_to_args( string );
            if ( args )
            {
#if 0
                char** arg;
                fprintf( stderr, "%s: ", args[0] );
                arg = args+1;
                while ( arg[0] )
                {
                    fprintf( stderr, " {%s}", arg[0] );
                    arg++;
                }
                fprintf( stderr, "\n" );
#endif              
                result = spawnvp( P_WAIT, args[0], args );
                record_times(result, &time);
                free_argv( args );
            }
            else
                result = 1;
        }
        func( closure, result ? EXEC_CMD_FAIL : EXEC_CMD_OK, &time );
        return;
    }

    if( DEBUG_EXECCMD )
    {
        char **argp = argv;

        printf("Executing command");
        while(*argp != 0)
        {
            printf(" [%s]", *argp);
            argp++;
        }
        printf("\n");
    }

    /* the rest is for Windows NT only */
    /* spawn doesn't like quotes around the command name */
    if ( argv[0][0] == '"')
    {
        int l = strlen(argv[0]);

        /* Clobber any closing quote, shortening the string by one
         * element */
        if (argv[0][l-1] == '"')
            argv[0][l-1] = '\0';
        
        /* Move everything *including* the original terminating zero
         * back one place in memory, covering up the opening quote */
        memmove(argv[0],argv[0]+1,l);
    }
    if( ( pid = spawnvp( P_NOWAIT, argv[0], argv ) ) == -1 )
    {
        perror( "spawn" );
        exit( EXITBAD );
    }
    /* Save the operation for execwait() to find. */

    cmdtab[ slot ].pid = pid;
    cmdtab[ slot ].func = func;
    cmdtab[ slot ].closure = closure;

    /* Wait until we're under the limit of concurrent commands. */
    /* Don't trust globs.jobs alone.                            */

    while( cmdsrunning >= MAXJOBS || cmdsrunning >= globs.jobs )
        if( !execwait() )
            break;
    
    if (argv != argv_static)
    {
        free_argv(argv);
    }
}
Exemple #13
0
/* process a "del" or "erase" command under Windows 95/98 */
static int
process_del( char*  command )
{
  char** arg;
  char*  p = command, *q;
  int    wildcard = 0, result = 0;

  /* first of all, skip the command itself */
  if ( p[0] == 'd' )
    p += 3; /* assumes "del..;" */
  else if ( p[0] == 'e' )
    p += 5; /* assumes "erase.." */
  else
    return 1; /* invalid command */

  /* process all targets independently */
  for (;;)
  {
    /* skip leading spaces */
    while ( *p && isspace(*p) )
      p++;
      
    /* exit if we encounter an end of string */
    if (!*p)
      return 0;
      
    /* ignore toggles/flags */
    while (*p == '/')
    {
      p++;
      while ( *p && isalnum(*p) )
          p++;
      while (*p && isspace(*p) )
          ++p;
    }

    
    {
      int  in_quote = 0;
      int  wildcard = 0;
      int  go_on    = 1;
      
      q = p;
      while (go_on)
      {
        switch (*p)
        {
          case '"':
            in_quote = !in_quote;
            break;
          
          case '?':
          case '*':
            if (!in_quote)
              wildcard = 1;
            break;
            
          case '\0':
            if (in_quote)
              return 1;
            /* fall-through */
              
          case ' ':
          case '\t':
            if (!in_quote)
            {
              int    len = p - q;
              int    result;
              char*  line;
              
              /* q..p-1 contains the delete argument */
              if ( len <= 0 )
                return 1;
  
              line = (char*)malloc( len+4+1 );
              if (!line)
                return 1;
              if ( DEBUG_PROFILE )
                  profile_memory( len+4+1 );
                
              strncpy( line, "del ", 4 );
              strncpy( line+4, q, len );
              line[len+4] = '\0';
              
              if ( wildcard )
                result = system( line );
              else
                result = !DeleteFile( line+4 );
  
              free( line );
              if (result)
                return 1;
                
              go_on = 0;
            }
            
          default:
            ;
        }
        p++;
      } /* while (go_on) */
    }
  }
}
Exemple #14
0
int  main( int argc, char **argv, char **arg_environ )
{
    int		n;
    char		*s;
    struct option	optv[N_OPTS];
    const char	*all = "all";
    int		anyhow = 0;
    int		status;
    int arg_c = argc;
    char ** arg_v = argv;
    const char *progname = argv[0];

# ifdef OS_MAC
    InitGraf(&qd.thePort);
# endif

    argc--, argv++;

	if( getoptions( argc, argv, "-:l:d:j:f:gs:t:ano:qv", optv ) < 0 )
    {
        printf( "\nusage: %s [ options ] targets...\n\n", progname );

        printf( "-a      Build all targets, even if they are current.\n" );
        printf( "-dx     Set the debug level to x (0-9).\n" );
        printf( "-fx     Read x instead of Jambase.\n" );
	    /* printf( "-g      Build from newest sources first.\n" ); */
        printf( "-jx     Run up to x shell commands concurrently.\n" );
        printf( "-lx     Limit actions to x number of seconds after which they are stopped.\n" );
        printf( "-n      Don't actually execute the updating actions.\n" );
        printf( "-ox     Write the updating actions to file x.\n" );
		printf( "-q      Quit quickly as soon as a target fails.\n" );
        printf( "-sx=y   Set variable x=y, overriding environment.\n" );
        printf( "-tx     Rebuild x, even if it is up-to-date.\n" );
        printf( "-v      Print the version of jam and exit.\n" );
        printf( "--x     Option is ignored.\n\n" );

        exit( EXITBAD );
    }

    /* Version info. */

    if( ( s = getoptval( optv, 'v', 0 ) ) )
    {
        printf( "Boost.Jam  " );
        printf( "Version %s. %s.\n", VERSION, OSMINOR );
	   printf( "   Copyright 1993-2002 Christopher Seiwald and Perforce Software, Inc.  \n" );
        printf( "   Copyright 2001 David Turner.\n" );
        printf( "   Copyright 2001-2004 David Abrahams.\n" );
        printf( "   Copyright 2002-2005 Rene Rivera.\n" );
        printf( "   Copyright 2003-2005 Vladimir Prus.\n" );

        return EXITOK;
    }

    /* Pick up interesting options */

    if( ( s = getoptval( optv, 'n', 0 ) ) )
        globs.noexec++, globs.debug[2] = 1;

	if( ( s = getoptval( optv, 'q', 0 ) ) )
 	    globs.quitquick = 1;
    if( ( s = getoptval( optv, 'a', 0 ) ) )
        anyhow++;

    if( ( s = getoptval( optv, 'j', 0 ) ) )
        globs.jobs = atoi( s );

	if( ( s = getoptval( optv, 'g', 0 ) ) )
	    globs.newestfirst = 1;

    if( ( s = getoptval( optv, 'l', 0 ) ) )
        globs.timeout = atoi( s );

    /* Turn on/off debugging */

    for( n = 0; s = getoptval( optv, 'd', n ); n++ )
    {
        int i;

        /* First -d, turn off defaults. */

        if( !n )
            for( i = 0; i < DEBUG_MAX; i++ )
                globs.debug[i] = 0;

        i = atoi( s );

        if( i < 0 || i >= DEBUG_MAX )
        {
            printf( "Invalid debug level '%s'.\n", s );
            continue;
        }

        /* n turns on levels 1-n */
        /* +n turns on level n */

        if( *s == '+' )
            globs.debug[i] = 1;
        else while( i )
            globs.debug[i--] = 1;
    }

    { PROFILE_ENTER(MAIN);

    #ifdef HAVE_PYTHON
    {
        PROFILE_ENTER(MAIN_PYTHON);
        Py_Initialize();
    
        {
            static PyMethodDef BjamMethods[] = {
                {"call", bjam_call, METH_VARARGS,
                 "Call the specified bjam rule."},
                {"import_rule", bjam_import_rule, METH_VARARGS,
                 "Imports Python callable to bjam."},
                {NULL, NULL, 0, NULL}
            };
    
            Py_InitModule("bjam", BjamMethods);
        }
        PROFILE_EXIT(MAIN_PYTHON);
    }
    #endif
    
#ifndef NDEBUG
    run_unit_tests();
#endif
#if YYDEBUG != 0
    if ( DEBUG_PARSE )
        yydebug = 1;
#endif

    /* Set JAMDATE first */

    {
        char *date;
        time_t clock;
        time( &clock );
        date = newstr( ctime( &clock ) );

        /* Trim newline from date */

        if( strlen( date ) == 25 )
            date[ 24 ] = 0;

        var_set( "JAMDATE", list_new( L0, newstr( date ) ), VAR_SET );
    }

 
    var_set( "JAM_VERSION",
             list_new( list_new( list_new( L0, newstr( VERSION_MAJOR_SYM ) ), 
                                 newstr( VERSION_MINOR_SYM ) ), 
                       newstr( VERSION_PATCH_SYM ) ),
             VAR_SET );

    /* And JAMUNAME */
# ifdef unix
    {
        struct utsname u;

        if( uname( &u ) >= 0 )
        {
            var_set( "JAMUNAME", 
                     list_new( 
                         list_new(
                             list_new(
                                 list_new(
                                     list_new( L0, 
                                               newstr( u.sysname ) ),
                                     newstr( u.nodename ) ),
                                 newstr( u.release ) ),
                             newstr( u.version ) ),
                         newstr( u.machine ) ), VAR_SET );
        }
    }
# endif /* unix */

    /* load up environment variables */

    /* first into global module, with splitting, for backward compatibility */
    var_defines( use_environ, 1 );
    
    /* then into .ENVIRON, without splitting */
    enter_module( bindmodule(".ENVIRON") );
    var_defines( use_environ, 0 );
    exit_module( bindmodule(".ENVIRON") );

	/*
	 * Jam defined variables OS, OSPLAT
     * We load them after environment, so that
     * setting OS in environment does not 
     * change Jam notion of the current platform.
	 */

    var_defines( othersyms, 1 );


    /* Load up variables set on command line. */

    for( n = 0; s = getoptval( optv, 's', n ); n++ )
    {
        char *symv[2];
        symv[0] = s;
        symv[1] = 0;
        var_defines( symv, 1 );
    }

    /* Set the ARGV to reflect the complete list of arguments of invocation. */

    for ( n = 0; n < arg_c; ++n )
    {
        var_set( "ARGV", list_new( L0, newstr( arg_v[n] ) ), VAR_APPEND );
    }

	/* Initialize built-in rules */

	load_builtins();

    /* Add the targets in the command line to update list */

    for ( n = 1; n < arg_c; ++n )
    {
        if ( arg_v[n][0] == '-' )
        {
            char *f = "-:l:d:j:f:gs:t:ano:qv";
            for( ; *f; f++ ) if( *f == arg_v[n][1] ) break;
            if ( f[1] == ':' && arg_v[n][2] == '\0' ) { ++n; }
        }
        else
        {
            mark_target_for_updating(arg_v[n]);
        }
    }

    /* Parse ruleset */

    {
        FRAME frame[1];
        frame_init( frame );
	for( n = 0; s = getoptval( optv, 'f', n ); n++ )
	    parse_file( s, frame );

	if( !n )
	    parse_file( "+", frame );
    }

    status = yyanyerrors();

    /* Manually touch -t targets */

    for( n = 0; s = getoptval( optv, 't', n ); n++ )
        touchtarget( s );

    /* If an output file is specified, set globs.cmdout to that */

    if( s = getoptval( optv, 'o', 0 ) )
    {
        if( !( globs.cmdout = fopen( s, "w" ) ) )
        {
            printf( "Failed to write to '%s'\n", s );
            exit( EXITBAD );
        }
        globs.noexec++;
    }

    /* Now make target */

    {
        PROFILE_ENTER(MAIN_MAKE);
        
        LIST* targets = targets_to_update();
        if ( !targets )
        {
            status |= make( 1, &all, anyhow );
        }
        else 
        {
            int targets_count = list_length(targets);
            const char **targets2 = (const char **)malloc(targets_count * sizeof(char *));
            int n = 0;
            if ( DEBUG_PROFILE )
                profile_memory( targets_count * sizeof(char *) );
            for ( ; targets; targets = list_next(targets) )
            {
                targets2[n++] = targets->string;
            }
            status |= make( targets_count, targets2, anyhow );       
            free(targets);
        }
        
        PROFILE_EXIT(MAIN_MAKE);
    }


    PROFILE_EXIT(MAIN); }
    
    if ( DEBUG_PROFILE )
        profile_dump();

    /* Widely scattered cleanup */

    var_done();
    file_done();
    donerules();
    donestamps();
    donestr();

    /* close cmdout */

    if( globs.cmdout )
        fclose( globs.cmdout );

#ifdef HAVE_PYTHON
    Py_Finalize();
#endif


    return status ? EXITBAD : EXITOK;
}