Edit

IABSD.fr/src/usr.bin/make/engine.c

Branch :

  • Show log

    Commit

  • Author : kn
    Date : 2021-10-25 19:54:29
    Hash : dcbed439
    Message : Zap unused variables/functions under /usr/src/*bin/ OK deraadt

  • usr.bin/make/engine.c
  • /*	$OpenBSD: engine.c,v 1.70 2021/10/25 19:54:29 kn Exp $ */
    /*
     * Copyright (c) 2012 Marc Espie.
     *
     * Extensive code modifications for the OpenBSD project.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     *
     * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
     * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
     * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     */
    /*
     * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
     * Copyright (c) 1988, 1989 by Adam de Boor
     * Copyright (c) 1989 by Berkeley Softworks
     * All rights reserved.
     *
     * This code is derived from software contributed to Berkeley by
     * Adam de Boor.
     *
     * Redistribution and use in source and binary forms, with or without
     * modification, are permitted provided that the following conditions
     * are met:
     * 1. Redistributions of source code must retain the above copyright
     *    notice, this list of conditions and the following disclaimer.
     * 2. Redistributions in binary form must reproduce the above copyright
     *    notice, this list of conditions and the following disclaimer in the
     *    documentation and/or other materials provided with the distribution.
     * 3. Neither the name of the University nor the names of its contributors
     *    may be used to endorse or promote products derived from this software
     *    without specific prior written permission.
     *
     * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     * SUCH DAMAGE.
     */
    
    #include <sys/types.h>
    #include <sys/time.h>
    #include <sys/wait.h>
    #include <assert.h>
    #include <ctype.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <signal.h>
    #include <stdint.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include "config.h"
    #include "defines.h"
    #include "dir.h"
    #include "engine.h"
    #include "arch.h"
    #include "gnode.h"
    #include "targ.h"
    #include "var.h"
    #include "extern.h"
    #include "lst.h"
    #include "timestamp.h"
    #include "make.h"
    #include "pathnames.h"
    #include "error.h"
    #include "str.h"
    #include "memory.h"
    #include "buf.h"
    #include "job.h"
    #include "lowparse.h"
    
    static void MakeTimeStamp(void *, void *);
    static int rewrite_time(const char *);
    static void setup_meta(void);
    static void setup_engine(void);
    static char **recheck_command_for_shell(char **);
    static void list_parents(GNode *, FILE *);
    
    /* XXX due to a bug in make's logic, targets looking like *.a or -l*
     * have been silently dropped when make couldn't figure them out.
     * Now, we warn about them until all Makefile bugs have been fixed.
     */
    static bool
    drop_silently(const char *s)
    {
    	size_t len;
    
    	if (s[0] == '-' && s[1] == 'l')
    		return true;
    
    	len = strlen(s);
    	if (len >=2 && s[len-2] == '.' && s[len-1] == 'a')
    		return true;
    	return false;
    }
    
    bool
    node_find_valid_commands(GNode *gn)
    {
    	if (DEBUG(DOUBLE) && (gn->type & OP_DOUBLE))
    		fprintf(stderr, "Warning: target %s had >1 lists of "
    		    "shell commands (ignoring later ones)\n", gn->name);
    	if (OP_NOP(gn->type) && Lst_IsEmpty(&gn->commands)) {
    		if (drop_silently(gn->name)) {
    			printf("Warning: target %s", gn->name);
    			list_parents(gn, stdout);
    			printf(" does not have any command (BUG)\n");
    			return true;
    		}
    		/*
    		 * No commands. Look for .DEFAULT rule from which we might infer
    		 * commands
    		 */
    		if ((gn->type & OP_NODEFAULT) == 0 &&
    		    (DEFAULT->type & OP_DUMMY) == 0 &&
    		    !Lst_IsEmpty(&DEFAULT->commands)) {
    			/*
    			 * Make only looks for a .DEFAULT if the node was never
    			 * the target of an operator, so that's what we do too.
    			 * If a .DEFAULT was given, we substitute its commands
    			 * for gn's commands and set the IMPSRC variable to be
    			 * the target's name The DEFAULT node acts like a
    			 * transformation rule, in that gn also inherits any
    			 * attributes or sources attached to .DEFAULT itself.
    			 */
    			Make_HandleUse(DEFAULT, gn);
    			Var(IMPSRC_INDEX, gn) = Var(TARGET_INDEX, gn);
    		} else if (is_out_of_date(Dir_MTime(gn))) {
    			/*
    			 * The node wasn't the target of an operator we have no
    			 * .DEFAULT rule to go on and the target doesn't
    			 * already exist. There's nothing more we can do for
    			 * this branch.
    			 */
    			 return false;
    	    	}
    	}
    	return true;
    }
    
    static void
    list_parents(GNode *gn, FILE *out)
    {
    	LstNode ln;
    	bool first = true;
    
    	for (ln = Lst_First(&gn->parents); ln != NULL; ln = Lst_Adv(ln)) {
    		GNode *p = Lst_Datum(ln);
    		if (!p->must_make)
    			continue;
    		if (first) {
    			fprintf(out, " (prerequisite of:");
    			first = false;
    		}
    		fprintf(out, " %s", p->name);
    	}
    	if (!first)
    		fprintf(out, ")");
    }
    
    void
    node_failure(GNode *gn)
    {
    	/*
    	 If the -k flag wasn't given, we stop in
    	 * our tracks, otherwise we just don't update this
    	 * node's parents so they never get examined.
    	 */
    	const char *diag;
    	FILE *out;
    
    	if (gn->type & OP_OPTIONAL) {
    		out = stdout;
    		diag = "(ignored)";
    	} else if (keepgoing) {
    		out = stdout;
    		diag = "(continuing)";
    	} else {
    		out = stderr;
    		diag = "";
    	}
    	fprintf(out, "make: don't know how to make %s", gn->name);
    	list_parents(gn, out);
    	fprintf(out, "%s\n", diag);
    	if (out == stdout)
    		fflush(stdout);
    	else {
    		print_errors();
    		Punt(NULL);
    	}
    }
    
    /* touch files the hard way, by writing stuff to them */
    static int
    rewrite_time(const char *name)
    {
    	int fd;
    	char c;
    
    	fd = open(name, O_RDWR | O_CREAT, 0666);
    	if (fd < 0)
    		return -1;
    	/*
    	 * Read and write a byte to the file to change
    	 * the modification time.
    	 */
    	if (read(fd, &c, 1) == 1) {
    		(void)lseek(fd, 0, SEEK_SET);
    		(void)write(fd, &c, 1);
    	}
    
    	(void)close(fd);
    	return 0;
    }
    
    void
    Job_Touch(GNode *gn)
    {
    	handle_all_signals();
    	if (gn->type & (OP_USE|OP_OPTIONAL|OP_PHONY)) {
    		/*
    		 * .JOIN, .USE, and .OPTIONAL targets are "virtual" targets
    		 * and, as such, shouldn't really be created.
    		 * Likewise, .PHONY targets are not really files
    		 */
    		return;
    	}
    
    	if (!Targ_Silent(gn)) {
    		(void)fprintf(stdout, "touch %s\n", gn->name);
    		(void)fflush(stdout);
    	}
    
    	if (noExecute) {
    		return;
    	}
    
    	if (gn->type & OP_ARCHV) {
    		Arch_Touch(gn);
    	} else {
    		const char *file = gn->path != NULL ? gn->path : gn->name;
    
    		if (set_times(file) == -1){
    			if (rewrite_time(file) == -1) {
    				(void)fprintf(stderr,
    				    "*** couldn't touch %s: %s", file,
    				    strerror(errno));
    		    	}
    		}
    	}
    }
    
    void
    Make_TimeStamp(GNode *parent, GNode *child)
    {
    	if (is_strictly_before(parent->youngest->mtime, child->mtime)) {
     		parent->youngest = child;
    	}
    }
    
    void
    Make_HandleUse(GNode	*cgn,	/* The .USE node */
        GNode	*pgn)	/* The target of the .USE node */
    {
    	GNode	*gn;	/* A child of the .USE node */
    	LstNode	ln;	/* An element in the children list */
    
    	assert(cgn->type & (OP_USE|OP_TRANSFORM));
    
    	if (pgn == NULL)
    		Fatal("Trying to apply .USE to '%s' without a parent",
    		    cgn->name);
    
    	if ((cgn->type & OP_USE) || Lst_IsEmpty(&pgn->commands)) {
    		/* .USE or transformation and target has no commands
    		 * -- append the child's commands to the parent.  */
    		Lst_Concat(&pgn->commands, &cgn->commands);
    	}
    
    	for (ln = Lst_First(&cgn->children); ln != NULL;
    	    ln = Lst_Adv(ln)) {
    		gn = Lst_Datum(ln);
    
    		if (Lst_AddNew(&pgn->children, gn)) {
    			Lst_AtEnd(&gn->parents, pgn);
    			pgn->children_left++;
    		}
    	}
    
    	if (DEBUG(DOUBLE) && (cgn->type & OP_DOUBLE))
    		fprintf(stderr, 
    		    "Warning: .USE %s expanded in %s had >1 lists of "
    		    "shell commands (ignoring later ones)\n", 
    		    cgn->name, pgn->name);
    	pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_TRANSFORM|OP_DOUBLE);
    
    	/*
    	 * This child node is now built, so we decrement the count of
    	 * not yet built children in the parent... We also remove the child
    	 * from the parent's list to accurately reflect the number of
    	 * remaining children the parent has. This is used by Make_Run to
    	 * decide whether to queue the parent or examine its children...
    	 */
    	if (cgn->type & OP_USE)
    		pgn->children_left--;
    }
    
    void
    Make_DoAllVar(GNode *gn)
    {
    	GNode *child;
    	LstNode ln;
    	BUFFER allsrc, oodate;
    	char *target;
    	bool do_oodate;
    	int oodate_count, allsrc_count = 0;
    
    	oodate_count = 0;
    	allsrc_count = 0;
    
    	Var(OODATE_INDEX, gn) = "";
    	Var(ALLSRC_INDEX, gn) = "";
    
    	for (ln = Lst_First(&gn->children); ln != NULL; ln = Lst_Adv(ln)) {
    		child = Lst_Datum(ln);
    		if ((child->type & (OP_USE|OP_INVISIBLE)) != 0)
    			continue;
    		if (OP_NOP(child->type) ||
    		    (target = Var(TARGET_INDEX, child)) == NULL) {
    			/*
    			 * this node is only source; use the specific pathname
    			 * for it
    			 */
    			target = child->path != NULL ? child->path :
    			    child->name;
    		}
    
    		/*
    		 * It goes in the OODATE variable if the parent is younger than
    		 * the child or if the child has been modified more recently
    		 * than the start of the make.  This is to keep make from
    		 * getting confused if something else updates the parent after
    		 * the make starts (shouldn't happen, I know, but sometimes it
    		 * does). In such a case, if we've updated the kid, the parent
    		 * is likely to have a modification time later than that of the
    		 * kid and anything that relies on the OODATE variable will be
    		 * hosed.
    		 */
    		do_oodate = false;
    		if (is_strictly_before(gn->mtime, child->mtime) ||
    		   (!is_strictly_before(child->mtime, starttime) &&
    		   child->built_status == REBUILT))
    		   	do_oodate = true;
    		if (do_oodate) {
    			oodate_count++;
    			if (oodate_count == 1)
    				Var(OODATE_INDEX, gn) = target;
    			else {
    				if (oodate_count == 2) {
    					Buf_Init(&oodate, 0);
    					Buf_AddString(&oodate,
    					    Var(OODATE_INDEX, gn));
    				}
    				Buf_AddSpace(&oodate);
    				Buf_AddString(&oodate, target);
    			}
    		}
    		allsrc_count++;
    		if (allsrc_count == 1)
    			Var(ALLSRC_INDEX, gn) = target;
    		else {
    			if (allsrc_count == 2) {
    				Buf_Init(&allsrc, 0);
    				Buf_AddString(&allsrc,
    				    Var(ALLSRC_INDEX, gn));
    			}
    			Buf_AddSpace(&allsrc);
    			Buf_AddString(&allsrc, target);
    		}
    	}
    
    	if (allsrc_count > 1)
    		Var(ALLSRC_INDEX, gn) = Buf_Retrieve(&allsrc);
    	if (oodate_count > 1)
    		Var(OODATE_INDEX, gn) = Buf_Retrieve(&oodate);
    
    	if (gn->impliedsrc)
    		Var(IMPSRC_INDEX, gn) = Var(TARGET_INDEX, gn->impliedsrc);
    }
    
    /* Wrapper to call Make_TimeStamp from a forEach loop.	*/
    static void
    MakeTimeStamp(void *parent, void *child)
    {
        Make_TimeStamp(parent, child);
    }
    
    bool
    Make_OODate(GNode *gn)
    {
    	bool	    oodate;
    
    	/*
    	 * Certain types of targets needn't even be sought as their datedness
    	 * doesn't depend on their modification time...
    	 */
    	if ((gn->type & (OP_USE|OP_PHONY)) == 0) {
    		(void)Dir_MTime(gn);
    		if (DEBUG(MAKE)) {
    			if (!is_out_of_date(gn->mtime))
    				printf("modified %s...",
    				    time_to_string(&gn->mtime));
    			else
    				printf("non-existent...");
    		}
    	}
    
    	/*
    	 * A target is rebuilt in one of the following circumstances:
    	 * - its modification time is smaller than that of its youngest child
    	 *   and it would actually be run (has commands or type OP_NOP)
    	 * - it's the object of a force operator
    	 * - it has no children, was on the lhs of an operator and doesn't
    	 *   exist already.
    	 *
    	 */
    	if (gn->type & OP_USE) {
    		/*
    		 * If the node is a USE node it is *never* out of date
    		 * no matter *what*.
    		 */
    		if (DEBUG(MAKE))
    			printf(".USE node...");
    		oodate = false;
    	} else if (gn->type & (OP_FORCE|OP_PHONY)) {
    		/*
    		 * A node which is the object of the force (!) operator or which
    		 * has the .EXEC attribute is always considered out-of-date.
    		 */
    		if (DEBUG(MAKE)) {
    			if (gn->type & OP_FORCE)
    				printf("! operator...");
    			else if (gn->type & OP_PHONY)
    				printf(".PHONY node...");
    			else
    				printf(".EXEC node...");
    		}
    		oodate = true;
    	} else if (is_strictly_before(gn->mtime, gn->youngest->mtime) ||
    	   (gn == gn->youngest &&
    	    (is_out_of_date(gn->mtime) || (gn->type & OP_DOUBLEDEP)))) {
    		/*
    		 * A node whose modification time is less than that of its
    		 * youngest child or that has no children (gn->youngest == gn)
    		 * and either doesn't exist (mtime == OUT_OF_DATE)
    		 * or was the object of a :: operator is out-of-date.
    		 */
    		if (DEBUG(MAKE)) {
    			if (is_strictly_before(gn->mtime, gn->youngest->mtime))
    				printf("modified before source(%s)...",
    				    gn->youngest->name);
    			else if (is_out_of_date(gn->mtime))
    				printf("non-existent and no sources...");
    			else
    				printf(":: operator and no sources...");
    		}
    		oodate = true;
    	} else {
    		oodate = false;
    	}
    
    	/*
    	 * If the target isn't out-of-date, the parents need to know its
    	 * modification time. Note that targets that appear to be out-of-date
    	 * but aren't, because they have no commands and aren't of type OP_NOP,
    	 * have their mtime stay below their children's mtime to keep parents
    	 * from thinking they're out-of-date.
    	 */
    	if (!oodate)
    		Lst_ForEach(&gn->parents, MakeTimeStamp, gn);
    
    	return oodate;
    }
    
    /* The following array is used to make a fast determination of which
     * characters are interpreted specially by the shell.  If a command
     * contains any of these characters, it is executed by the shell, not
     * directly by us.  */
    static char	    meta[256];
    
    void
    setup_meta(void)
    {
    	char *p;
    
    	for (p = "#=|^(){};&<>*?[]:$`\\\n~"; *p != '\0'; p++)
    		meta[(unsigned char) *p] = 1;
    	/* The null character serves as a sentinel in the string.  */
    	meta[0] = 1;
    }
    
    static char **
    recheck_command_for_shell(char **av)
    {
    	char *runsh[] = {
    		"!", "alias", "cd", "eval", "exit", "read", "set", "ulimit",
    		"unalias", "unset", "wait", "umask", NULL
    	};
    
    	char **p;
    
    	/* optimization: if exec cmd, we avoid the intermediate shell */
    	if (strcmp(av[0], "exec") == 0)
    		av++;
    
    	if (!av[0])
    		return NULL;
    
    	for (p = runsh; *p; p++)
    		if (strcmp(av[0], *p) == 0)
    			return NULL;
    
    	return av;
    }
    
    static void
    run_command(const char *cmd, bool errCheck)
    {
    	const char *p;
    	char *shargv[4];
    	char **todo;
    
    	shargv[0] = _PATH_BSHELL;
    
    	shargv[1] = errCheck ? "-ec" : "-c";
    	shargv[2] = (char *)cmd;
    	shargv[3] = NULL;
    
    	todo = shargv;
    
    
    	/* Search for meta characters in the command. If there are no meta
    	 * characters, there's no need to execute a shell to execute the
    	 * command.  */
    	for (p = cmd; !meta[(unsigned char)*p]; p++)
    		continue;
    	if (*p == '\0') {
    		char *bp;
    		char **av;
    		int argc;
    		/* No meta-characters, so probably no need to exec a shell.
    		 * Break the command into words to form an argument vector
    		 * we can execute.  */
    		av = brk_string(cmd, &argc, &bp);
    		av = recheck_command_for_shell(av);
    		if (av != NULL)
    			todo = av;
    	}
    	execvp(todo[0], todo);
    
    	if (errno == ENOENT)
    		fprintf(stderr, "%s: not found\n", todo[0]);
    	else
    		perror(todo[0]);
    	_exit(1);
    }
    
    void
    job_attach_node(Job *job, GNode *node)
    {
    	job->node = node;
    	job->node->built_status = BUILDING;
    	job->next_cmd = Lst_First(&node->commands);
    	job->exit_type = JOB_EXIT_OKAY;
    	job->location = NULL;
    	job->flags = 0;
    }
    
    void
    handle_job_status(Job *job, int status)
    {
    	bool silent;
    	int dying;
    
    	/* if there's one job running and we don't keep going, no need 
    	 * to report right now.
    	 */
    	if ((job->flags & JOB_ERRCHECK) && !keepgoing && runningJobs == NULL) 
    		silent = !DEBUG(JOB);
    	else
    		silent = false;
    
    	debug_job_printf("Process %ld (%s) exited with status %d.\n",
    	    (long)job->pid, job->node->name, status);
    
    	/* classify status */
    	if (WIFEXITED(status)) {
    		job->code = WEXITSTATUS(status);/* exited */
    		if (job->code != 0) {
    			/* if we're already dying from that signal, be silent */
    			if (!silent && job->code > 128 
    			    && job->code <= 128 + _NSIG) {
    				dying = check_dying_signal();
    				silent = dying && job->code == dying + 128;
    			}
    			if (!silent)
    				printf("*** Error %d", job->code);
    			job->exit_type = JOB_EXIT_BAD;
    		} else 
    			job->exit_type = JOB_EXIT_OKAY;
    	} else {
    		job->exit_type = JOB_SIGNALED;
    		job->code = WTERMSIG(status);	/* signaled */
    		/* if we're already dying from that signal, be silent */
    		if (!silent) {
    			dying = check_dying_signal();
    			silent = dying && job->code == dying;
    		}
    		if (!silent)
    			printf("*** Signal %d", job->code);
    	}
    
    	/* if there is a problem, what's going on ? */
    	if (job->exit_type != JOB_EXIT_OKAY) {
    		if (!silent)
    			printf(" in target '%s'", job->node->name);
    		if (job->flags & JOB_ERRCHECK) {
    			job->node->built_status = ERROR;
    			if (!keepgoing) {
    				if (!silent)
    					printf("\n");
    				job->flags |= JOB_KEEPERROR;
    				/* XXX don't free the command */
    				return;
    			}
    			printf(", line %lu of %s", job->location->lineno, 
    			    job->location->fname);
    			/* Parallel make already determined whether
    			 * JOB_IS_EXPENSIVE, perform the computation for
    			 * sequential make to figure out whether to display the
    			 * command or not.  */
    			if ((job->flags & JOB_SILENT) && sequential)
    				determine_expensive_job(job);
    			if ((job->flags & (JOB_SILENT | JOB_IS_EXPENSIVE)) 
    			    == JOB_SILENT)
    				printf(": %s", job->cmd);
    			/* Abort the current target,
    			 * but let others continue.  */
    			printf(" (continuing)\n");
    		} else {
    			/* Continue executing commands for
    			 * this target.  If we return 0,
    			 * this will happen...  */
    			printf(" (ignored)\n");
    			job->exit_type = JOB_EXIT_OKAY;
    		}
    	}
    	free(job->cmd);
    }
    
    int
    run_gnode(GNode *gn)
    {
    	if (!gn || (gn->type & OP_DUMMY))
    		return NOSUCHNODE;
    
    	Job_Make(gn);
    	loop_handle_running_jobs();
    	return gn->built_status;
    }
    
    
    static void
    setup_engine(void)
    {
    	static int already_setup = 0;
    
    	if (!already_setup) {
    		setup_meta();
    		already_setup = 1;
    	}
    }
    
    static bool
    do_run_command(Job *job, const char *pre)
    {
    	bool silent;	/* Don't print command */
    	bool doExecute;	/* Execute the command */
    	bool errCheck;	/* Check errors */
    	pid_t cpid; 	/* Child pid */
    
    	const char *cmd = job->cmd;
    	silent = Targ_Silent(job->node);
    	errCheck = !Targ_Ignore(job->node);
    	if (job->node->type & OP_MAKE)
    		doExecute = true;
    	else
    		doExecute = !noExecute;
    
    	/* How can we execute a null command ? we warn the user that the
    	 * command expanded to nothing (is this the right thing to do?).  */
    	if (*cmd == '\0') {
    		Parse_Error(PARSE_WARNING, 
    		    "'%s' expands to '' while building %s", 
    		    pre, job->node->name);
    		return false;
    	}
    
    	for (;; cmd++) {
    		if (*cmd == '@')
    			silent = DEBUG(LOUD) ? false : true;
    		else if (*cmd == '-')
    			errCheck = false;
    		else if (*cmd == '+')
    			doExecute = true;
    		else
    			break;
    	}
    	while (ISSPACE(*cmd))
    		cmd++;
    	/* Print the command before fork if make -n or !silent*/
    	if ( noExecute || !silent)
    		printf("%s\n", cmd);
    	
    	if (silent)
    		job->flags |= JOB_SILENT;
    	else
    		job->flags &= ~JOB_SILENT;
    
    	/* If we're not supposed to execute any commands, this is as far as
    	 * we go...  */
    	if (!doExecute)
    		return false;
    	/* always flush for other stuff */
    	fflush(stdout);
    
    	/* Optimization: bypass comments entirely */
    	if (*cmd == '#')
    		return false;
    
    	/* Fork and execute the single command. If the fork fails, we abort.  */
    	switch (cpid = fork()) {
    	case -1:
    		Punt("Could not fork");
    		/*NOTREACHED*/
    	case 0:
    		reset_signal_mask();
    		/* put a random delay unless we're the only job running
    		 * and there's nothing left to do.
    		 */
    		if (random_delay)
    			if (!(runningJobs == NULL && nothing_left_to_build()))
    				usleep(arc4random_uniform(random_delay));
    		run_command(cmd, errCheck);
    		/*NOTREACHED*/
    	default:
    		job->pid = cpid;
    		job->next = runningJobs;
    		runningJobs = job;
    		if (errCheck)
    			job->flags |= JOB_ERRCHECK;
    		else
    			job->flags &= ~JOB_ERRCHECK;
    		debug_job_printf("Running %ld (%s) %s\n", (long)job->pid, 
    		    job->node->name, (noExecute || !silent) ? "" : cmd);
    		return true;
    	}
    }
    
    bool
    job_run_next(Job *job)
    {
    	bool started;
    	GNode *gn = job->node;
    
    	setup_engine();
    	while (job->next_cmd != NULL) {
    		struct command *command = Lst_Datum(job->next_cmd);
    
    		handle_all_signals();
    		job->location = &command->location;
    		Parse_SetLocation(job->location);
    		job->cmd = Var_Subst(command->string, &gn->localvars, false);
    		job->next_cmd = Lst_Adv(job->next_cmd);
    		if (fatal_errors)
    			Punt(NULL);
    		started = do_run_command(job, command->string);
    		if (started)
    			return false;
    		else
    			free(job->cmd);
    	}
    	job->exit_type = JOB_EXIT_OKAY;
    	return true;
    }