"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "job.c" between
bmake-20201101.tar.gz and bmake-20201117.tar.gz

About: bmake a BSD make tool (derived from NetBSD’s make).

job.c  (bmake-20201101):job.c  (bmake-20201117)
/* $NetBSD: job.c,v 1.302 2020/11/01 18:45:49 rillig Exp $ */ /* $NetBSD: job.c,v 1.326 2020/11/16 18:28:27 rillig Exp $ */
/* /*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
* All rights reserved. * All rights reserved.
* *
* This code is derived from software contributed to Berkeley by * This code is derived from software contributed to Berkeley by
* Adam de Boor. * Adam de Boor.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions * modification, are permitted provided that the following conditions
skipping to change at line 159 skipping to change at line 159
# include <sys/socket.h> # include <sys/socket.h>
#endif #endif
#include "make.h" #include "make.h"
#include "dir.h" #include "dir.h"
#include "job.h" #include "job.h"
#include "pathnames.h" #include "pathnames.h"
#include "trace.h" #include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */ /* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
MAKE_RCSID("$NetBSD: job.c,v 1.302 2020/11/01 18:45:49 rillig Exp $"); MAKE_RCSID("$NetBSD: job.c,v 1.326 2020/11/16 18:28:27 rillig Exp $");
/* A shell defines how the commands are run. All commands for a target are /* A shell defines how the commands are run. All commands for a target are
* written into a single file, which is then given to the shell to execute * written into a single file, which is then given to the shell to execute
* the commands from it. The commands are written to the file using a few * the commands from it. The commands are written to the file using a few
* templates for echo control and error control. * templates for echo control and error control.
* *
* The name of the shell is the basename for the predefined shells, such as * The name of the shell is the basename for the predefined shells, such as
* "sh", "csh", "bash". For custom shells, it is the full pathname, and its * "sh", "csh", "bash". For custom shells, it is the full pathname, and its
* basename is used to select the type of shell; the longest match wins. * basename is used to select the type of shell; the longest match wins.
* So /usr/pkg/bin/bash has type sh, /usr/local/bin/tcsh has type csh. * So /usr/pkg/bin/bash has type sh, /usr/local/bin/tcsh has type csh.
skipping to change at line 185 skipping to change at line 185
* *
* The error checking for individual commands is controlled using hasErrCtl, * The error checking for individual commands is controlled using hasErrCtl,
* errOnOrEcho, errOffOrExecIgnore and errExit. * errOnOrEcho, errOffOrExecIgnore and errExit.
* *
* If a shell doesn't have error control, errOnOrEcho becomes a printf template * If a shell doesn't have error control, errOnOrEcho becomes a printf template
* for echoing the command, should echoing be on; errOffOrExecIgnore becomes * for echoing the command, should echoing be on; errOffOrExecIgnore becomes
* another printf template for executing the command while ignoring the return * another printf template for executing the command while ignoring the return
* status. Finally errExit is a printf template for running the command and * status. Finally errExit is a printf template for running the command and
* causing the shell to exit on error. If any of these strings are empty when * causing the shell to exit on error. If any of these strings are empty when
* hasErrCtl is FALSE, the command will be executed anyway as is, and if it * hasErrCtl is FALSE, the command will be executed anyway as is, and if it
* causes an error, so be it. Any templates setup to echo the command will * causes an error, so be it. Any templates set up to echo the command will
* escape any '$ ` \ "' characters in the command string to avoid common * escape any '$ ` \ "' characters in the command string to avoid common
* problems with echo "%s\n" as a template. * problems with echo "%s\n" as a template.
* *
* The command-line flags "echo" and "exit" also control the behavior. The * The command-line flags "echo" and "exit" also control the behavior. The
* "echo" flag causes the shell to start echoing commands right away. The * "echo" flag causes the shell to start echoing commands right away. The
* "exit" flag causes the shell to exit when an error is detected in one of * "exit" flag causes the shell to exit when an error is detected in one of
* the commands. * the commands.
*/ */
typedef struct Shell { typedef struct Shell {
skipping to change at line 390 skipping to change at line 390
* It is set by the Job_ParseShell function. */ * It is set by the Job_ParseShell function. */
static Shell *commandShell = &shells[DEFSHELL_INDEX]; static Shell *commandShell = &shells[DEFSHELL_INDEX];
const char *shellPath = NULL; /* full pathname of executable image */ const char *shellPath = NULL; /* full pathname of executable image */
const char *shellName = NULL; /* last component of shellPath */ const char *shellName = NULL; /* last component of shellPath */
char *shellErrFlag = NULL; char *shellErrFlag = NULL;
static char *shellArgv = NULL; /* Custom shell args */ static char *shellArgv = NULL; /* Custom shell args */
static Job *job_table; /* The structures that describe them */ static Job *job_table; /* The structures that describe them */
static Job *job_table_end; /* job_table + maxJobs */ static Job *job_table_end; /* job_table + maxJobs */
static unsigned int wantToken; /* we want a token */ static unsigned int wantToken; /* we want a token */
static int lurking_children = 0; static Boolean lurking_children = FALSE;
static int make_suspended = 0; /* non-zero if we've seen a SIGTSTP (etc) */ static Boolean make_suspended = FALSE; /* Whether we've seen a SIGTSTP (etc) */
/* /*
* Set of descriptors of pipes connected to * Set of descriptors of pipes connected to
* the output channels of children * the output channels of children
*/ */
static struct pollfd *fds = NULL; static struct pollfd *fds = NULL;
static Job **jobfds = NULL; static Job **jobfds = NULL;
static nfds_t nfds = 0; static nfds_t nfds = 0;
static void watchfd(Job *); static void watchfd(Job *);
static void clearfd(Job *); static void clearfd(Job *);
skipping to change at line 445 skipping to change at line 445
} }
static void static void
job_table_dump(const char *where) job_table_dump(const char *where)
{ {
Job *job; Job *job;
debug_printf("job table @ %s\n", where); debug_printf("job table @ %s\n", where);
for (job = job_table; job < job_table_end; job++) { for (job = job_table; job < job_table_end; job++) {
debug_printf("job %d, status %d, flags %d, pid %d\n", debug_printf("job %d, status %d, flags %d, pid %d\n",
(int)(job - job_table), job->job_state, job->flags, job->pid); (int)(job - job_table), job->status, job->flags, job->pid);
} }
} }
/* /*
* Delete the target of a failed, interrupted, or otherwise * Delete the target of a failed, interrupted, or otherwise
* unsuccessful job unless inhibited by .PRECIOUS. * unsuccessful job unless inhibited by .PRECIOUS.
*/ */
static void static void
JobDeleteTarget(GNode *gn) JobDeleteTarget(GNode *gn)
{ {
skipping to change at line 541 skipping to change at line 541
/* Pass the signal to each running job. */ /* Pass the signal to each running job. */
static void static void
JobCondPassSig(int signo) JobCondPassSig(int signo)
{ {
Job *job; Job *job;
DEBUG1(JOB, "JobCondPassSig(%d) called.\n", signo); DEBUG1(JOB, "JobCondPassSig(%d) called.\n", signo);
for (job = job_table; job < job_table_end; job++) { for (job = job_table; job < job_table_end; job++) {
if (job->job_state != JOB_ST_RUNNING) if (job->status != JOB_ST_RUNNING)
continue; continue;
DEBUG2(JOB, "JobCondPassSig passing signal %d to child %d.\n", DEBUG2(JOB, "JobCondPassSig passing signal %d to child %d.\n",
signo, job->pid); signo, job->pid);
KILLPG(job->pid, signo); KILLPG(job->pid, signo);
} }
} }
/* SIGCHLD handler. /* SIGCHLD handler.
* *
* Sends a token on the child exit pipe to wake us up from select()/poll(). */ * Sends a token on the child exit pipe to wake us up from select()/poll(). */
skipping to change at line 597 skipping to change at line 597
JobInterrupt(FALSE, signo); JobInterrupt(FALSE, signo);
} }
static void static void
JobPassSig_suspend(int signo) JobPassSig_suspend(int signo)
{ {
sigset_t nmask, omask; sigset_t nmask, omask;
struct sigaction act; struct sigaction act;
/* Suppress job started/continued messages */ /* Suppress job started/continued messages */
make_suspended = 1; make_suspended = TRUE;
/* Pass the signal onto every job */ /* Pass the signal onto every job */
JobCondPassSig(signo); JobCondPassSig(signo);
/* /*
* Send ourselves the signal now we've given the message to everyone else. * Send ourselves the signal now we've given the message to everyone else.
* Note we block everything else possible while we're getting the signal. * Note we block everything else possible while we're getting the signal.
* This ensures that all our jobs get continued when we wake up before * This ensures that all our jobs get continued when we wake up before
* we take any other signal. * we take any other signal.
*/ */
skipping to change at line 647 skipping to change at line 647
* In any case nothing else is needed here. * In any case nothing else is needed here.
*/ */
/* Restore handler and signal mask */ /* Restore handler and signal mask */
act.sa_handler = JobPassSig_suspend; act.sa_handler = JobPassSig_suspend;
(void)sigaction(signo, &act, NULL); (void)sigaction(signo, &act, NULL);
(void)sigprocmask(SIG_SETMASK, &omask, NULL); (void)sigprocmask(SIG_SETMASK, &omask, NULL);
} }
static Job * static Job *
JobFindPid(int pid, JobState status, Boolean isJobs) JobFindPid(int pid, JobStatus status, Boolean isJobs)
{ {
Job *job; Job *job;
for (job = job_table; job < job_table_end; job++) { for (job = job_table; job < job_table_end; job++) {
if ((job->job_state == status) && job->pid == pid) if (job->status == status && job->pid == pid)
return job; return job;
} }
if (DEBUG(JOB) && isJobs) if (DEBUG(JOB) && isJobs)
job_table_dump("no pid"); job_table_dump("no pid");
return NULL; return NULL;
} }
/* Parse leading '@', '-' and '+', which control the exact execution mode. */ /* Parse leading '@', '-' and '+', which control the exact execution mode. */
static void static void
ParseRunOptions( ParseRunOptions(
skipping to change at line 706 skipping to change at line 706
for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) { for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) {
if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || cmd[i] == '"') if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || cmd[i] == '"')
esc[j++] = '\\'; esc[j++] = '\\';
esc[j] = cmd[i]; esc[j] = cmd[i];
} }
esc[j] = '\0'; esc[j] = '\0';
return esc; return esc;
} }
static void
JobPrintf(Job *job, const char *fmt, const char *arg)
{
if (DEBUG(JOB))
debug_printf(fmt, arg);
(void)fprintf(job->cmdFILE, fmt, arg);
(void)fflush(job->cmdFILE);
}
static void
JobPrintln(Job *job, const char *line)
{
JobPrintf(job, "%s\n", line);
}
/*- /*-
*----------------------------------------------------------------------- *-----------------------------------------------------------------------
* JobPrintCommand -- * JobPrintCommand --
* Put out another command for the given job. If the command starts * Put out another command for the given job. If the command starts
* with an @ or a - we process it specially. In the former case, * with an @ or a - we process it specially. In the former case,
* so long as the -s and -n flags weren't given to make, we stick * so long as the -s and -n flags weren't given to make, we stick
* a shell-specific echoOff command in the script. In the latter, * a shell-specific echoOff command in the script. In the latter,
* we ignore errors for the entire job, unless the shell has error * we ignore errors for the entire job, unless the shell has error
* control. * control.
* If the command is just "..." we take all future commands for this * If the command is just "..." we take all future commands for this
* job to be commands to be executed once the entire graph has been * job to be commands to be executed once the entire graph has been
* made and return non-zero to signal that the end of the commands * made and return non-zero to signal that the end of the commands
* was reached. These commands are later attached to the postCommands * was reached. These commands are later attached to the .END
* node and executed by Job_End when all things are done. * node and executed by Job_End when all things are done.
* *
* Side Effects: * Side Effects:
* If the command begins with a '-' and the shell has no error control, * If the command begins with a '-' and the shell has no error control,
* the JOB_IGNERR flag is set in the job descriptor. * the JOB_IGNERR flag is set in the job descriptor.
* numCommands is incremented if the command is actually printed. * numCommands is incremented if the command is actually printed.
*----------------------------------------------------------------------- *-----------------------------------------------------------------------
*/ */
static void static void
JobPrintCommand(Job *job, char *cmd) JobPrintCommand(Job *job, char *cmd)
skipping to change at line 747 skipping to change at line 763
* off before printing the command * off before printing the command
* and need to turn it back on */ * and need to turn it back on */
Boolean runAlways; Boolean runAlways;
const char *cmdTemplate; /* Template to use when printing the const char *cmdTemplate; /* Template to use when printing the
* command */ * command */
char *cmdStart; /* Start of expanded command */ char *cmdStart; /* Start of expanded command */
char *escCmd = NULL; /* Command with quotes/backticks escaped */ char *escCmd = NULL; /* Command with quotes/backticks escaped */
noSpecials = !GNode_ShouldExecute(job->node); noSpecials = !GNode_ShouldExecute(job->node);
#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) { \
debug_printf(fmt, arg); \
} \
(void)fprintf(job->cmdFILE, fmt, arg); \
(void)fflush(job->cmdFILE);
numCommands++; numCommands++;
Var_Subst(cmd, job->node, VARE_WANTRES, &cmd); Var_Subst(cmd, job->node, VARE_WANTRES, &cmd);
/* TODO: handle errors */ /* TODO: handle errors */
cmdStart = cmd; cmdStart = cmd;
cmdTemplate = "%s\n"; cmdTemplate = "%s\n";
ParseRunOptions(&cmd, &shutUp, &errOff, &runAlways); ParseRunOptions(&cmd, &shutUp, &errOff, &runAlways);
skipping to change at line 784 skipping to change at line 794
* If the shell doesn't have error control the alternate echo'ing will * If the shell doesn't have error control the alternate echo'ing will
* be done (to avoid showing additional error checking code) * be done (to avoid showing additional error checking code)
* and this will need the characters '$ ` \ "' escaped * and this will need the characters '$ ` \ "' escaped
*/ */
if (!commandShell->hasErrCtl) if (!commandShell->hasErrCtl)
escCmd = EscapeShellDblQuot(cmd); escCmd = EscapeShellDblQuot(cmd);
if (shutUp) { if (shutUp) {
if (!(job->flags & JOB_SILENT) && !noSpecials && if (!(job->flags & JOB_SILENT) && !noSpecials &&
commandShell->hasEchoCtl) { (commandShell->hasEchoCtl)) {
DBPRINTF("%s\n", commandShell->echoOff); JobPrintln(job, commandShell->echoOff);
} else { } else {
if (commandShell->hasErrCtl) if (commandShell->hasErrCtl)
shutUp = FALSE; shutUp = FALSE;
} }
} }
if (errOff) { if (errOff) {
if (!noSpecials) { if (!noSpecials) {
if (commandShell->hasErrCtl) { if (commandShell->hasErrCtl) {
/* /*
* we don't want the error-control commands showing * we don't want the error-control commands showing
* up either, so we turn off echoing while executing * up either, so we turn off echoing while executing
* them. We could put another field in the shell * them. We could put another field in the shell
* structure to tell JobDoOutput to look for this * structure to tell JobDoOutput to look for this
* string too, but why make it any more complex than * string too, but why make it any more complex than
* it already is? * it already is?
*/ */
if (!(job->flags & JOB_SILENT) && !shutUp && if (!(job->flags & JOB_SILENT) && !shutUp &&
commandShell->hasEchoCtl) { (commandShell->hasEchoCtl)) {
DBPRINTF("%s\n", commandShell->echoOff); JobPrintln(job, commandShell->echoOff);
DBPRINTF("%s\n", commandShell->errOffOrExecIgnore); JobPrintln(job, commandShell->errOffOrExecIgnore);
DBPRINTF("%s\n", commandShell->echoOn); JobPrintln(job, commandShell->echoOn);
} else { } else {
DBPRINTF("%s\n", commandShell->errOffOrExecIgnore); JobPrintln(job, commandShell->errOffOrExecIgnore);
} }
} else if (commandShell->errOffOrExecIgnore && } else if (commandShell->errOffOrExecIgnore &&
commandShell->errOffOrExecIgnore[0] != '\0') commandShell->errOffOrExecIgnore[0] != '\0') {
{
/* /*
* The shell has no error control, so we need to be * The shell has no error control, so we need to be
* weird to get it to ignore any errors from the command. * weird to get it to ignore any errors from the command.
* If echoing is turned on, we turn it off and use the * If echoing is turned on, we turn it off and use the
* errOnOrEcho template to echo the command. Leave echoing * errOnOrEcho template to echo the command. Leave echoing
* off so the user doesn't see the weirdness we go through * off so the user doesn't see the weirdness we go through
* to ignore errors. Set cmdTemplate to use the weirdness * to ignore errors. Set cmdTemplate to use the weirdness
* instead of the simple "%s\n" template. * instead of the simple "%s\n" template.
*/ */
job->flags |= JOB_IGNERR; job->flags |= JOB_IGNERR;
if (!(job->flags & JOB_SILENT) && !shutUp) { if (!(job->flags & JOB_SILENT) && !shutUp) {
if (commandShell->hasEchoCtl) { if (commandShell->hasEchoCtl) {
DBPRINTF("%s\n", commandShell->echoOff); JobPrintln(job, commandShell->echoOff);
} }
DBPRINTF(commandShell->errOnOrEcho, escCmd); JobPrintf(job, commandShell->errOnOrEcho, escCmd);
shutUp = TRUE; shutUp = TRUE;
} else { } else {
if (!shutUp) { if (!shutUp)
DBPRINTF(commandShell->errOnOrEcho, escCmd); JobPrintf(job, commandShell->errOnOrEcho, escCmd);
}
} }
cmdTemplate = commandShell->errOffOrExecIgnore; cmdTemplate = commandShell->errOffOrExecIgnore;
/* /*
* The error ignoration (hee hee) is already taken care * The error ignoration (hee hee) is already taken care
* of by the errOffOrExecIgnore template, so pretend error * of by the errOffOrExecIgnore template, so pretend error
* checking is still on. * checking is still on.
*/ */
errOff = FALSE; errOff = FALSE;
} else { } else {
errOff = FALSE; errOff = FALSE;
} }
} else { } else {
errOff = FALSE; errOff = FALSE;
} }
} else { } else {
/* /*
* If errors are being checked and the shell doesn't have error control * If errors are being checked and the shell doesn't have error control
* but does supply an errExit template, then setup commands to run * but does supply an errExit template, then set up commands to run
* through it. * through it.
*/ */
if (!commandShell->hasErrCtl && commandShell->errExit && if (!commandShell->hasErrCtl && commandShell->errExit &&
commandShell->errExit[0] != '\0') { commandShell->errExit[0] != '\0') {
if (!(job->flags & JOB_SILENT) && !shutUp) { if (!(job->flags & JOB_SILENT) && !shutUp) {
if (commandShell->hasEchoCtl) { if (commandShell->hasEchoCtl)
DBPRINTF("%s\n", commandShell->echoOff); JobPrintln(job, commandShell->echoOff);
} JobPrintf(job, commandShell->errOnOrEcho, escCmd);
DBPRINTF(commandShell->errOnOrEcho, escCmd); shutUp = TRUE;
shutUp = TRUE; }
} /* If it's a comment line or blank, treat as an ignored error */
/* If it's a comment line or blank, treat as an ignored error */ if (escCmd[0] == commandShell->commentChar ||
if ((escCmd[0] == commandShell->commentChar) || (escCmd[0] == '\0'))
(escCmd[0] == 0)) cmdTemplate = commandShell->errOffOrExecIgnore;
cmdTemplate = commandShell->errOffOrExecIgnore; else
else cmdTemplate = commandShell->errExit;
cmdTemplate = commandShell->errExit; errOff = FALSE;
errOff = FALSE;
} }
} }
if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0 && if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0 &&
(job->flags & JOB_TRACED) == 0) { !(job->flags & JOB_TRACED)) {
DBPRINTF("set -%s\n", "x"); JobPrintln(job, "set -x");
job->flags |= JOB_TRACED; job->flags |= JOB_TRACED;
} }
DBPRINTF(cmdTemplate, cmd); JobPrintf(job, cmdTemplate, cmd);
free(cmdStart); free(cmdStart);
free(escCmd); free(escCmd);
if (errOff) { if (errOff) {
/* /*
* If echoing is already off, there's no point in issuing the * If echoing is already off, there's no point in issuing the
* echoOff command. Otherwise we issue it and pretend it was on * echoOff command. Otherwise we issue it and pretend it was on
* for the whole command... * for the whole command...
*/ */
if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl) {
DBPRINTF("%s\n", commandShell->echoOff); JobPrintln(job, commandShell->echoOff);
shutUp = TRUE; shutUp = TRUE;
} }
DBPRINTF("%s\n", commandShell->errOnOrEcho); JobPrintln(job, commandShell->errOnOrEcho);
}
if (shutUp && commandShell->hasEchoCtl) {
DBPRINTF("%s\n", commandShell->echoOn);
} }
if (shutUp && commandShell->hasEchoCtl)
JobPrintln(job, commandShell->echoOn);
} }
/* Print all commands to the shell file that is later executed. /* Print all commands to the shell file that is later executed.
* *
* The special command "..." stops printing and saves the remaining commands * The special command "..." stops printing and saves the remaining commands
* to be executed later. */ * to be executed later. */
static void static void
JobPrintCommands(Job *job) JobPrintCommands(Job *job)
{ {
StringListNode *ln; StringListNode *ln;
for (ln = job->node->commands->first; ln != NULL; ln = ln->next) { for (ln = job->node->commands->first; ln != NULL; ln = ln->next) {
const char *cmd = ln->datum; const char *cmd = ln->datum;
if (strcmp(cmd, "...") == 0) { if (strcmp(cmd, "...") == 0) {
job->node->type |= OP_SAVE_CMDS; job->node->type |= OP_SAVE_CMDS;
if ((job->flags & JOB_IGNDOTS) == 0) { job->tailCmds = ln->next;
job->tailCmds = ln->next; break;
break; }
}
} else JobPrintCommand(job, ln->datum);
JobPrintCommand(job, ln->datum);
} }
} }
/* Save the delayed commands, to be executed when everything else is done. */ /* Save the delayed commands, to be executed when everything else is done. */
static void static void
JobSaveCommands(Job *job) JobSaveCommands(Job *job)
{ {
StringListNode *node; StringListNode *node;
for (node = job->tailCmds; node != NULL; node = node->next) { for (node = job->tailCmds; node != NULL; node = node->next) {
skipping to change at line 944 skipping to change at line 949
* variables such as .TARGET, .IMPSRC. It is not intended to * variables such as .TARGET, .IMPSRC. It is not intended to
* expand the other variables as well; see deptgt-end.mk. */ * expand the other variables as well; see deptgt-end.mk. */
(void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd); (void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd);
/* TODO: handle errors */ /* TODO: handle errors */
Lst_Append(Targ_GetEndNode()->commands, expanded_cmd); Lst_Append(Targ_GetEndNode()->commands, expanded_cmd);
} }
} }
/* Called to close both input and output pipes when a job is finished. */ /* Called to close both input and output pipes when a job is finished. */
static void static void
JobClose(Job *job) JobClosePipes(Job *job)
{ {
clearfd(job); clearfd(job);
(void)close(job->outPipe); (void)close(job->outPipe);
job->outPipe = -1; job->outPipe = -1;
JobDoOutput(job, TRUE); JobDoOutput(job, TRUE);
(void)close(job->inPipe); (void)close(job->inPipe);
job->inPipe = -1; job->inPipe = -1;
} }
/*- /* Do final processing for the given job including updating parent nodes and
*----------------------------------------------------------------------- * starting new jobs as available/necessary.
* JobFinish -- *
* Do final processing for the given job including updating * Deferred commands for the job are placed on the .END node.
* parents and starting new jobs as available/necessary. Note *
* that we pay no attention to the JOB_IGNERR flag here. * If there was a serious error (errors != 0; not an ignored one), no more
* This is because when we're called because of a noexecute flag * jobs will be started.
* or something, jstat.w_status is 0 and when called from
* Job_CatchChildren, the status is zeroed if it s/b ignored.
* *
* Input: * Input:
* job job to finish * job job to finish
* status sub-why job went away * status sub-why job went away
*
* Side Effects:
* Final commands for the job are placed on postCommands.
*
* If we got an error and are aborting (aborting == ABORT_ERROR) and
* the job list is now empty, we are done for the day.
* If we recognized an error (errors !=0), we set the aborting flag
* to ABORT_ERROR so no more jobs will be started.
*-----------------------------------------------------------------------
*/ */
static void static void
JobFinish (Job *job, WAIT_T status) JobFinish (Job *job, WAIT_T status)
{ {
Boolean done, return_job_token; Boolean done, return_job_token;
DEBUG3(JOB, "JobFinish: %d [%s], status %d\n", DEBUG3(JOB, "JobFinish: %d [%s], status %d\n",
job->pid, job->node->name, status); job->pid, job->node->name, status);
if ((WIFEXITED(status) && if ((WIFEXITED(status) &&
(((WEXITSTATUS(status) != 0) && !(job->flags & JOB_IGNERR)))) || ((WEXITSTATUS(status) != 0 && !(job->flags & JOB_IGNERR)))) ||
WIFSIGNALED(status)) WIFSIGNALED(status))
{ {
/* /*
* If it exited non-zero and either we're doing things our * If it exited non-zero and either we're doing things our
* way or we're not ignoring errors, the job is finished. * way or we're not ignoring errors, the job is finished.
* Similarly, if the shell died because of a signal * Similarly, if the shell died because of a signal
* the job is also finished. In these * the job is also finished. In these
* cases, finish out the job's output before printing the exit * cases, finish out the job's output before printing the exit
* status... * status...
*/ */
JobClose(job); JobClosePipes(job);
if (job->cmdFILE != NULL && job->cmdFILE != stdout) { if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
(void)fclose(job->cmdFILE); (void)fclose(job->cmdFILE);
job->cmdFILE = NULL; job->cmdFILE = NULL;
} }
done = TRUE; done = TRUE;
} else if (WIFEXITED(status)) { } else if (WIFEXITED(status)) {
/* /*
* Deal with ignored errors in -B mode. We need to print a message * Deal with ignored errors in -B mode. We need to print a message
* telling of the ignored error as well as setting status.w_status * telling of the ignored error as well as to run the next command.
* to 0 so the next command gets run. To do this, we set done to be *
* TRUE if in -B mode and the job exited non-zero.
*/ */
done = WEXITSTATUS(status) != 0; done = WEXITSTATUS(status) != 0;
/* JobClosePipes(job);
* Old comment said: "Note we don't
* want to close down any of the streams until we know we're at the
* end."
* But we do. Otherwise when are we going to print the rest of the
* stuff?
*/
JobClose(job);
} else { } else {
/* /*
* No need to close things down or anything. * No need to close things down or anything.
*/ */
done = FALSE; done = FALSE;
} }
if (done) { if (done) {
if (WIFEXITED(status)) { if (WIFEXITED(status)) {
DEBUG2(JOB, "Process %d [%s] exited.\n", DEBUG2(JOB, "Process %d [%s] exited.\n",
skipping to change at line 1041 skipping to change at line 1027
if (WEXITSTATUS(status) != 0) { if (WEXITSTATUS(status) != 0) {
if (job->node != lastNode) { if (job->node != lastNode) {
MESSAGE(stdout, job->node); MESSAGE(stdout, job->node);
lastNode = job->node; lastNode = job->node;
} }
#ifdef USE_META #ifdef USE_META
if (useMeta) { if (useMeta) {
meta_job_error(job, job->node, job->flags, WEXITSTATUS(status )); meta_job_error(job, job->node, job->flags, WEXITSTATUS(status ));
} }
#endif #endif
if (!dieQuietly(job->node, -1)) if (!shouldDieQuietly(job->node, -1))
(void)printf("*** [%s] Error code %d%s\n", (void)printf("*** [%s] Error code %d%s\n",
job->node->name, job->node->name,
WEXITSTATUS(status), WEXITSTATUS(status),
(job->flags & JOB_IGNERR) ? " (ignored)" : ""); (job->flags & JOB_IGNERR) ? " (ignored)" : "");
if (job->flags & JOB_IGNERR) { if (job->flags & JOB_IGNERR) {
WAIT_STATUS(status) = 0; WAIT_STATUS(status) = 0;
} else { } else {
if (deleteOnError) { if (deleteOnError) {
JobDeleteTarget(job->node); JobDeleteTarget(job->node);
} }
skipping to change at line 1078 skipping to change at line 1064
job->node->name, WTERMSIG(status)); job->node->name, WTERMSIG(status));
if (deleteOnError) { if (deleteOnError) {
JobDeleteTarget(job->node); JobDeleteTarget(job->node);
} }
} }
(void)fflush(stdout); (void)fflush(stdout);
} }
#ifdef USE_META #ifdef USE_META
if (useMeta) { if (useMeta) {
int x; int meta_status = meta_job_finish(job);
if (meta_status != 0 && status == 0)
if ((x = meta_job_finish(job)) != 0 && status == 0) { status = meta_status;
status = x;
}
} }
#endif #endif
return_job_token = FALSE; return_job_token = FALSE;
Trace_Log(JOBEND, job); Trace_Log(JOBEND, job);
if (!(job->flags & JOB_SPECIAL)) { if (!(job->flags & JOB_SPECIAL)) {
if ((WAIT_STATUS(status) != 0) || if (WAIT_STATUS(status) != 0 ||
(aborting == ABORT_ERROR) || (aborting == ABORT_ERROR) || aborting == ABORT_INTERRUPT)
(aborting == ABORT_INTERRUPT))
return_job_token = TRUE; return_job_token = TRUE;
} }
if ((aborting != ABORT_ERROR) && (aborting != ABORT_INTERRUPT) && if (aborting != ABORT_ERROR && aborting != ABORT_INTERRUPT &&
(WAIT_STATUS(status) == 0)) { (WAIT_STATUS(status) == 0)) {
/* /*
* As long as we aren't aborting and the job didn't return a non-zero * As long as we aren't aborting and the job didn't return a non-zero
* status that we shouldn't ignore, we call Make_Update to update * status that we shouldn't ignore, we call Make_Update to update
* the parents. * the parents.
*/ */
JobSaveCommands(job); JobSaveCommands(job);
job->node->made = MADE; job->node->made = MADE;
if (!(job->flags & JOB_SPECIAL)) if (!(job->flags & JOB_SPECIAL))
return_job_token = TRUE; return_job_token = TRUE;
Make_Update(job->node); Make_Update(job->node);
job->job_state = JOB_ST_FREE; job->status = JOB_ST_FREE;
} else if (WAIT_STATUS(status)) { } else if (WAIT_STATUS(status)) {
errors++; errors++;
job->job_state = JOB_ST_FREE; job->status = JOB_ST_FREE;
} }
/* if (errors > 0 && !opts.keepgoing && aborting != ABORT_INTERRUPT)
* Set aborting if any error. aborting = ABORT_ERROR; /* Prevent more jobs from getting started. */
*/
if (errors && !opts.keepgoing && (aborting != ABORT_INTERRUPT)) {
/*
* If we found any errors in this batch of children and the -k flag
* wasn't given, we set the aborting flag so no more jobs get
* started.
*/
aborting = ABORT_ERROR;
}
if (return_job_token) if (return_job_token)
Job_TokenReturn(); Job_TokenReturn();
if (aborting == ABORT_ERROR && jobTokensRunning == 0) { if (aborting == ABORT_ERROR && jobTokensRunning == 0)
/*
* If we are aborting and the job table is now empty, we finish.
*/
Finish(errors); Finish(errors);
}
static void
TouchRegular(GNode *gn)
{
const char *file = GNode_Path(gn);
struct utimbuf times = { now, now };
int fd;
char c;
if (utime(file, &times) >= 0)
return;
fd = open(file, O_RDWR | O_CREAT, 0666);
if (fd < 0) {
(void)fprintf(stderr, "*** couldn't touch %s: %s\n",
file, strerror(errno));
(void)fflush(stderr);
return; /* XXX: What about propagating the error? */
}
/* Last resort: update the file's time stamps in the traditional way.
* XXX: This doesn't work for empty files, which are sometimes used
* as marker files. */
if (read(fd, &c, 1) == 1) {
(void)lseek(fd, 0, SEEK_SET);
while (write(fd, &c, 1) == -1 && errno == EAGAIN)
continue;
} }
(void)close(fd); /* XXX: What about propagating the error? */
} }
/* Touch the given target. Called by JobStart when the -t flag was given. /* Touch the given target. Called by JobStart when the -t flag was given.
* *
* The modification date of the file is changed. * The modification date of the file is changed.
* If the file did not exist, it is created. */ * If the file did not exist, it is created. */
void void
Job_Touch(GNode *gn, Boolean silent) Job_Touch(GNode *gn, Boolean silent)
{ {
int streamID; /* ID of stream opened to do the touch */
struct utimbuf times; /* Times for utime() call */
if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL| if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL|
OP_SPECIAL|OP_PHONY)) { OP_SPECIAL|OP_PHONY)) {
/* /* These are "virtual" targets and should not really be created. */
* .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets
* and, as such, shouldn't really be created.
*/
return; return;
} }
if (!silent || !GNode_ShouldExecute(gn)) { if (!silent || !GNode_ShouldExecute(gn)) {
(void)fprintf(stdout, "touch %s\n", gn->name); (void)fprintf(stdout, "touch %s\n", gn->name);
(void)fflush(stdout); (void)fflush(stdout);
} }
if (!GNode_ShouldExecute(gn)) { if (!GNode_ShouldExecute(gn))
return; return;
}
if (gn->type & OP_ARCHV) { if (gn->type & OP_ARCHV) {
Arch_Touch(gn); Arch_Touch(gn);
} else if (gn->type & OP_LIB) { return;
Arch_TouchLib(gn); }
} else {
const char *file = GNode_Path(gn);
times.actime = times.modtime = now;
if (utime(file, &times) < 0){
streamID = open(file, O_RDWR | O_CREAT, 0666);
if (streamID >= 0) {
char c;
/*
* Read and write a byte to the file to change the
* modification time, then close the file.
*/
if (read(streamID, &c, 1) == 1) {
(void)lseek(streamID, (off_t)0, SEEK_SET);
while (write(streamID, &c, 1) == -1 && errno == EAGAIN)
continue;
}
(void)close(streamID); if (gn->type & OP_LIB) {
} else { Arch_TouchLib(gn);
(void)fprintf(stdout, "*** couldn't touch %s: %s", return;
file, strerror(errno));
(void)fflush(stdout);
}
}
} }
TouchRegular(gn);
} }
/* Make sure the given node has all the commands it needs. /* Make sure the given node has all the commands it needs.
* *
* The node will have commands from the .DEFAULT rule added to it if it * The node will have commands from the .DEFAULT rule added to it if it
* needs them. * needs them.
* *
* Input: * Input:
* gn The target whose commands need verifying * gn The target whose commands need verifying
* abortProc Function to abort with message * abortProc Function to abort with message
skipping to change at line 1223 skipping to change at line 1195
{ {
if (GNode_IsTarget(gn)) if (GNode_IsTarget(gn))
return TRUE; return TRUE;
if (!Lst_IsEmpty(gn->commands)) if (!Lst_IsEmpty(gn->commands))
return TRUE; return TRUE;
if ((gn->type & OP_LIB) && !Lst_IsEmpty(gn->children)) if ((gn->type & OP_LIB) && !Lst_IsEmpty(gn->children))
return TRUE; return TRUE;
/* /*
* No commands. Look for .DEFAULT rule from which we might infer * No commands. Look for .DEFAULT rule from which we might infer
* commands * commands.
*/ */
if ((DEFAULT != NULL) && !Lst_IsEmpty(DEFAULT->commands) && if (defaultNode != NULL && !Lst_IsEmpty(defaultNode->commands) &&
(gn->type & OP_SPECIAL) == 0) { !(gn->type & OP_SPECIAL)) {
/* /*
* Make only looks for a .DEFAULT if the node was never the * The traditional Make only looks for a .DEFAULT if the node was
* target of an operator, so that's what we do too. If * never the target of an operator, so that's what we do too.
* 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
* The DEFAULT node acts like a transformation rule, in that
* gn also inherits any attributes or sources attached to * gn also inherits any attributes or sources attached to
* .DEFAULT itself. * .DEFAULT itself.
*/ */
Make_HandleUse(DEFAULT, gn); Make_HandleUse(defaultNode, gn);
Var_Set(IMPSRC, GNode_VarTarget(gn), gn); Var_Set(IMPSRC, GNode_VarTarget(gn), gn);
return TRUE; return TRUE;
} }
if (Dir_MTime(gn, 0) != 0 || (gn->type & OP_SPECIAL)) Dir_UpdateMTime(gn, FALSE);
if (gn->mtime != 0 || (gn->type & OP_SPECIAL))
return TRUE; return TRUE;
/* /*
* The node wasn't the target of an operator. We have no .DEFAULT * 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 * rule to go on and the target doesn't already exist. There's
* nothing more we can do for this branch. If the -k flag wasn't * nothing more we can do for this branch. If the -k flag wasn't
* given, we stop in our tracks, otherwise we just don't update * given, we stop in our tracks, otherwise we just don't update
* this node's parents so they never get examined. * this node's parents so they never get examined.
*/ */
skipping to change at line 1280 skipping to change at line 1252
(void)fflush(stdout); (void)fflush(stdout);
return FALSE; return FALSE;
} }
abortProc("%s: don't know how to make %s. Stop", progname, gn->name); abortProc("%s: don't know how to make %s. Stop", progname, gn->name);
return FALSE; return FALSE;
} }
/* Execute the shell for the given job. /* Execute the shell for the given job.
* *
* A shell is executed, its output is altered and the Job structure added * See Job_CatchOutput for handling the output of the shell. */
* to the job table.
*/
static void static void
JobExec(Job *job, char **argv) JobExec(Job *job, char **argv)
{ {
int cpid; /* ID of new child */ int cpid; /* ID of new child */
sigset_t mask; sigset_t mask;
job->flags &= ~JOB_TRACED; job->flags &= ~JOB_TRACED;
if (DEBUG(JOB)) { if (DEBUG(JOB)) {
int i; int i;
debug_printf("Running %s %sly\n", job->node->name, "local"); debug_printf("Running %s\n", job->node->name);
debug_printf("\tCommand: "); debug_printf("\tCommand: ");
for (i = 0; argv[i] != NULL; i++) { for (i = 0; argv[i] != NULL; i++) {
debug_printf("%s ", argv[i]); debug_printf("%s ", argv[i]);
} }
debug_printf("\n"); debug_printf("\n");
} }
/* /*
* Some jobs produce no output and it's disconcerting to have * Some jobs produce no output and it's disconcerting to have
* no feedback of their running (since they produce no output, the * no feedback of their running (since they produce no output, the
skipping to change at line 1317 skipping to change at line 1287
*/ */
if ((lastNode != job->node) && !(job->flags & JOB_SILENT)) { if ((lastNode != job->node) && !(job->flags & JOB_SILENT)) {
MESSAGE(stdout, job->node); MESSAGE(stdout, job->node);
lastNode = job->node; lastNode = job->node;
} }
/* No interruptions until this job is on the `jobs' list */ /* No interruptions until this job is on the `jobs' list */
JobSigLock(&mask); JobSigLock(&mask);
/* Pre-emptively mark job running, pid still zero though */ /* Pre-emptively mark job running, pid still zero though */
job->job_state = JOB_ST_RUNNING; job->status = JOB_ST_RUNNING;
cpid = vFork(); cpid = vFork();
if (cpid == -1) if (cpid == -1)
Punt("Cannot vfork: %s", strerror(errno)); Punt("Cannot vfork: %s", strerror(errno));
if (cpid == 0) { if (cpid == 0) {
/* Child */ /* Child */
sigset_t tmask; sigset_t tmask;
#ifdef USE_META #ifdef USE_META
skipping to change at line 1351 skipping to change at line 1321
/* /*
* Must duplicate the input stream down to the child's input and * Must duplicate the input stream down to the child's input and
* reset it to the beginning (again). Since the stream was marked * reset it to the beginning (again). Since the stream was marked
* close-on-exec, we must clear that bit in the new input. * close-on-exec, we must clear that bit in the new input.
*/ */
if (dup2(fileno(job->cmdFILE), 0) == -1) if (dup2(fileno(job->cmdFILE), 0) == -1)
execDie("dup2", "job->cmdFILE"); execDie("dup2", "job->cmdFILE");
if (fcntl(0, F_SETFD, 0) == -1) if (fcntl(0, F_SETFD, 0) == -1)
execDie("fcntl clear close-on-exec", "stdin"); execDie("fcntl clear close-on-exec", "stdin");
if (lseek(0, (off_t)0, SEEK_SET) == -1) if (lseek(0, 0, SEEK_SET) == -1)
execDie("lseek to 0", "stdin"); execDie("lseek to 0", "stdin");
if (job->node->type & (OP_MAKE | OP_SUBMAKE)) { if (job->node->type & (OP_MAKE | OP_SUBMAKE)) {
/* /*
* Pass job token pipe to submakes. * Pass job token pipe to submakes.
*/ */
if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1) if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1)
execDie("clear close-on-exec", "tokenWaitJob.inPipe"); execDie("clear close-on-exec", "tokenWaitJob.inPipe");
if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1) if (fcntl(tokenWaitJob.outPipe, F_SETFD, 0) == -1)
execDie("clear close-on-exec", "tokenWaitJob.outPipe"); execDie("clear close-on-exec", "tokenWaitJob.outPipe");
} }
/* /*
* Set up the child's output to be routed through the pipe * Set up the child's output to be routed through the pipe
* we've created for it. * we've created for it.
*/ */
if (dup2(job->outPipe, 1) == -1) if (dup2(job->outPipe, 1) == -1)
execDie("dup2", "job->outPipe"); execDie("dup2", "job->outPipe");
/* /*
skipping to change at line 1458 skipping to change at line 1428
if ((commandShell->exit && commandShell->exit[0] != '-') || if ((commandShell->exit && commandShell->exit[0] != '-') ||
(commandShell->echo && commandShell->echo[0] != '-')) (commandShell->echo && commandShell->echo[0] != '-'))
{ {
/* /*
* At least one of the flags doesn't have a minus before it, so * At least one of the flags doesn't have a minus before it, so
* merge them together. Have to do this because the *(&(@*#*&#$# * merge them together. Have to do this because the *(&(@*#*&#$#
* Bourne shell thinks its second argument is a file to source. * Bourne shell thinks its second argument is a file to source.
* Grrrr. Note the ten-character limitation on the combined arguments. * Grrrr. Note the ten-character limitation on the combined arguments.
*/ */
(void)snprintf(args, sizeof(args), "-%s%s", (void)snprintf(args, sizeof args, "-%s%s",
((job->flags & JOB_IGNERR) ? "" : ((job->flags & JOB_IGNERR) ? "" :
(commandShell->exit ? commandShell->exit : "")), (commandShell->exit ? commandShell->exit : "")),
((job->flags & JOB_SILENT) ? "" : ((job->flags & JOB_SILENT) ? "" :
(commandShell->echo ? commandShell->echo : ""))); (commandShell->echo ? commandShell->echo : "")));
if (args[1]) { if (args[1]) {
argv[argc] = args; argv[argc] = args;
argc++; argc++;
} }
} else { } else {
skipping to change at line 1490 skipping to change at line 1460
/*- /*-
*----------------------------------------------------------------------- *-----------------------------------------------------------------------
* JobStart -- * JobStart --
* Start a target-creation process going for the target described * Start a target-creation process going for the target described
* by the graph node gn. * by the graph node gn.
* *
* Input: * Input:
* gn target to create * gn target to create
* flags flags for the job to override normal ones. * flags flags for the job to override normal ones.
* e.g. JOB_SPECIAL or JOB_IGNDOTS
* previous The previous Job structure for this node, if any. * previous The previous Job structure for this node, if any.
* *
* Results: * Results:
* JOB_ERROR if there was an error in the commands, JOB_FINISHED * JOB_ERROR if there was an error in the commands, JOB_FINISHED
* if there isn't actually anything left to do for the job and * if there isn't actually anything left to do for the job and
* JOB_RUNNING if the job has been started. * JOB_RUNNING if the job has been started.
* *
* Side Effects: * Side Effects:
* A new Job node is created and added to the list of running * A new Job node is created and added to the list of running
* jobs. PMake is forked and a child shell created. * jobs. PMake is forked and a child shell created.
* *
* NB: I'm fairly sure that this code is never called with JOB_SPECIAL set * NB: The return value is ignored by everyone.
* JOB_IGNDOTS is never set (dsl)
* Also the return value is ignored by everyone.
*----------------------------------------------------------------------- *-----------------------------------------------------------------------
*/ */
static JobStartResult static JobStartResult
JobStart(GNode *gn, int flags) JobStart(GNode *gn, JobFlags flags)
{ {
Job *job; /* new job descriptor */ Job *job; /* new job descriptor */
char *argv[10]; /* Argument vector to shell */ char *argv[10]; /* Argument vector to shell */
Boolean cmdsOK; /* true if the nodes commands were all right */ Boolean cmdsOK; /* true if the nodes commands were all right */
Boolean noExec; /* Set true if we decide not to run the job */ Boolean noExec; /* Set true if we decide not to run the job */
int tfd; /* File descriptor to the temp file */ int tfd; /* File descriptor to the temp file */
for (job = job_table; job < job_table_end; job++) { for (job = job_table; job < job_table_end; job++) {
if (job->job_state == JOB_ST_FREE) if (job->status == JOB_ST_FREE)
break; break;
} }
if (job >= job_table_end) if (job >= job_table_end)
Punt("JobStart no job slots vacant"); Punt("JobStart no job slots vacant");
memset(job, 0, sizeof *job); memset(job, 0, sizeof *job);
job->job_state = JOB_ST_SETUP;
if (gn->type & OP_SPECIAL)
flags |= JOB_SPECIAL;
job->node = gn; job->node = gn;
job->tailCmds = NULL; job->tailCmds = NULL;
job->status = JOB_ST_SET_UP;
/* if (gn->type & OP_SPECIAL)
* Set the initial value of the flags for this job based on the global flags |= JOB_SPECIAL;
* ones and the node's attributes... Any flags supplied by the caller if (Targ_Ignore(gn))
* are also added to the field. flags |= JOB_IGNERR;
*/ if (Targ_Silent(gn))
job->flags = 0; flags |= JOB_SILENT;
if (Targ_Ignore(gn)) { job->flags = flags;
job->flags |= JOB_IGNERR;
}
if (Targ_Silent(gn)) {
job->flags |= JOB_SILENT;
}
job->flags |= flags;
/* /*
* Check the commands now so any attributes from .DEFAULT have a chance * Check the commands now so any attributes from .DEFAULT have a chance
* to migrate to the node * to migrate to the node
*/ */
cmdsOK = Job_CheckCommands(gn, Error); cmdsOK = Job_CheckCommands(gn, Error);
job->inPollfd = NULL; job->inPollfd = NULL;
/* /*
* If the -n flag wasn't given, we open up OUR (not the child's) * If the -n flag wasn't given, we open up OUR (not the child's)
* temporary file to stuff commands in it. The thing is rd/wr so we don't * temporary file to stuff commands in it. The thing is rd/wr so we don't
* need to reopen it to feed it to the shell. If the -n flag *was* given, * need to reopen it to feed it to the shell. If the -n flag *was* given,
* we just set the file to be stdout. Cute, huh? * we just set the file to be stdout. Cute, huh?
*/ */
if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) || if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) ||
(!opts.noExecute && !opts.touchFlag)) { (!opts.noExecute && !opts.touchFlag)) {
/* /*
* tfile is the name of a file into which all shell commands are * tfile is the name of a file into which all shell commands are
* put. It is removed before the child shell is executed, unless * put. It is removed before the child shell is executed, unless
* DEBUG(SCRIPT) is set. * DEBUG(SCRIPT) is set.
*/ */
char *tfile; char *tfile;
sigset_t mask; sigset_t mask;
/* /*
* We're serious here, but if the commands were bogus, we're * We're serious here, but if the commands were bogus, we're
* also dead... * also dead...
*/ */
if (!cmdsOK) { if (!cmdsOK) {
PrintOnError(gn, NULL); /* provide some clue */ PrintOnError(gn, NULL); /* provide some clue */
DieHorribly(); DieHorribly();
} }
JobSigLock(&mask); JobSigLock(&mask);
tfd = mkTempFile(TMPPAT, &tfile); tfd = mkTempFile(TMPPAT, &tfile);
if (!DEBUG(SCRIPT)) if (!DEBUG(SCRIPT))
(void)eunlink(tfile); (void)eunlink(tfile);
JobSigUnlock(&mask); JobSigUnlock(&mask);
job->cmdFILE = fdopen(tfd, "w+"); job->cmdFILE = fdopen(tfd, "w+");
if (job->cmdFILE == NULL) { if (job->cmdFILE == NULL)
Punt("Could not fdopen %s", tfile); Punt("Could not fdopen %s", tfile);
}
(void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC); (void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC);
/* /*
* Send the commands to the command file, flush all its buffers then * Send the commands to the command file, flush all its buffers then
* rewind and remove the thing. * rewind and remove the thing.
*/ */
noExec = FALSE; noExec = FALSE;
#ifdef USE_META #ifdef USE_META
if (useMeta) { if (useMeta) {
meta_job_start(job, gn); meta_job_start(job, gn);
if (Targ_Silent(gn)) { /* might have changed */ if (Targ_Silent(gn)) /* might have changed */
job->flags |= JOB_SILENT; job->flags |= JOB_SILENT;
}
} }
#endif #endif
/* /*
* We can do all the commands at once. hooray for sanity * We can do all the commands at once. hooray for sanity
*/ */
numCommands = 0; numCommands = 0;
JobPrintCommands(job); JobPrintCommands(job);
/* /*
* If we didn't print out any commands to the shell script, * If we didn't print out any commands to the shell script,
skipping to change at line 1645 skipping to change at line 1602
*/ */
noExec = TRUE; noExec = TRUE;
} else { } else {
/* /*
* Just touch the target and note that no shell should be executed. * Just touch the target and note that no shell should be executed.
* Set cmdFILE to stdout to make life easier. Check the commands, too, * Set cmdFILE to stdout to make life easier. Check the commands, too,
* but don't die if they're no good -- it does no harm to keep working * but don't die if they're no good -- it does no harm to keep working
* up the graph. * up the graph.
*/ */
job->cmdFILE = stdout; job->cmdFILE = stdout;
Job_Touch(gn, job->flags&JOB_SILENT); Job_Touch(gn, job->flags & JOB_SILENT);
noExec = TRUE; noExec = TRUE;
} }
/* Just in case it isn't already... */ /* Just in case it isn't already... */
(void)fflush(job->cmdFILE); (void)fflush(job->cmdFILE);
/* /*
* If we're not supposed to execute a shell, don't. * If we're not supposed to execute a shell, don't.
*/ */
if (noExec) { if (noExec) {
if (!(job->flags & JOB_SPECIAL)) if (!(job->flags & JOB_SPECIAL))
Job_TokenReturn(); Job_TokenReturn();
/* /*
* Unlink and close the command file if we opened one * Unlink and close the command file if we opened one
*/ */
if (job->cmdFILE != stdout) { if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
if (job->cmdFILE != NULL) { (void)fclose(job->cmdFILE);
(void)fclose(job->cmdFILE); job->cmdFILE = NULL;
job->cmdFILE = NULL;
}
} }
/* /*
* We only want to work our way up the graph if we aren't here because * We only want to work our way up the graph if we aren't here because
* the commands for the job were no good. * the commands for the job were no good.
*/ */
if (cmdsOK && aborting == ABORT_NONE) { if (cmdsOK && aborting == ABORT_NONE) {
JobSaveCommands(job); JobSaveCommands(job);
job->node->made = MADE; job->node->made = MADE;
Make_Update(job->node); Make_Update(job->node);
} }
job->job_state = JOB_ST_FREE; job->status = JOB_ST_FREE;
return cmdsOK ? JOB_FINISHED : JOB_ERROR; return cmdsOK ? JOB_FINISHED : JOB_ERROR;
} }
/* /*
* Set up the control arguments to the shell. This is based on the flags * Set up the control arguments to the shell. This is based on the flags
* set earlier for this job. * set earlier for this job.
*/ */
JobMakeArgv(job, argv); JobMakeArgv(job, argv);
/* Create the pipe by which we'll get the shell's output. */ /* Create the pipe by which we'll get the shell's output. */
JobCreatePipe(job, 3); JobCreatePipe(job, 3);
JobExec(job, argv); JobExec(job, argv);
return JOB_RUNNING; return JOB_RUNNING;
} }
/* Print the output of the shell command, skipping the noPrint command of
* the shell, if any. */
static char * static char *
JobOutput(Job *job, char *cp, char *endp) JobOutput(Job *job, char *cp, char *endp)
{ {
char *ecp; char *ecp;
if (commandShell->noPrint && commandShell->noPrint[0] != '\0') { if (commandShell->noPrint == NULL || commandShell->noPrint[0] == '\0')
while ((ecp = strstr(cp, commandShell->noPrint)) != NULL) { return cp;
if (cp != ecp) {
*ecp = '\0'; while ((ecp = strstr(cp, commandShell->noPrint)) != NULL) {
/* if (ecp != cp) {
* The only way there wouldn't be a newline after *ecp = '\0';
* this line is if it were the last in the buffer. /*
* however, since the non-printable comes after it, * The only way there wouldn't be a newline after
* there must be a newline, so we don't print one. * this line is if it were the last in the buffer.
*/ * however, since the non-printable comes after it,
(void)fprintf(stdout, "%s", cp); * there must be a newline, so we don't print one.
(void)fflush(stdout); */
} (void)fprintf(stdout, "%s", cp);
cp = ecp + commandShell->noPrintLen; (void)fflush(stdout);
if (cp != endp) { }
/* cp = ecp + commandShell->noPrintLen;
* Still more to print, look again after skipping if (cp != endp) {
* the whitespace following the non-printable /*
* command.... * Still more to print, look again after skipping
*/ * the whitespace following the non-printable
cp++; * command....
while (*cp == ' ' || *cp == '\t' || *cp == '\n') { */
cp++; cp++;
} pp_skip_whitespace(&cp);
} else { } else {
return cp; return cp;
}
} }
} }
return cp; return cp;
} }
/*- /*
*----------------------------------------------------------------------- * This function is called whenever there is something to read on the pipe.
* JobDoOutput -- * We collect more output from the given job and store it in the job's
* This function is called at different times depending on * outBuf. If this makes up a line, we print it tagged by the job's
* whether the user has specified that output is to be collected * identifier, as necessary.
* via pipes or temporary files. In the former case, we are called *
* whenever there is something to read on the pipe. We collect more * In the output of the shell, the 'noPrint' lines are removed. If the
* output from the given job and store it in the job's outBuf. If * command is not alone on the line (the character after it is not \0 or
* this makes up a line, we print it tagged by the job's identifier, * \n), we do print whatever follows it.
* as necessary.
* If output has been collected in a temporary file, we open the
* file and read it line by line, transferring it to our own
* output channel until the file is empty. At which point we
* remove the temporary file.
* In both cases, however, we keep our figurative eye out for the
* 'noPrint' line for the shell from which the output came. If
* we recognize a line, we don't print it. If the command is not
* alone on the line (the character after it is not \0 or \n), we
* do print whatever follows it.
* *
* Input: * Input:
* job the job whose output needs printing * job the job whose output needs printing
* finish TRUE if this is the last time we'll be called * finish TRUE if this is the last time we'll be called
* for this job * for this job
*
* Side Effects:
* curPos may be shifted as may the contents of outBuf.
*-----------------------------------------------------------------------
*/ */
static void static void
JobDoOutput(Job *job, Boolean finish) JobDoOutput(Job *job, Boolean finish)
{ {
Boolean gotNL = FALSE; /* true if got a newline */ Boolean gotNL = FALSE; /* true if got a newline */
Boolean fbuf; /* true if our buffer filled up */ Boolean fbuf; /* true if our buffer filled up */
size_t nr; /* number of bytes read */ size_t nr; /* number of bytes read */
size_t i; /* auxiliary index into outBuf */ size_t i; /* auxiliary index into outBuf */
size_t max; /* limit for i (end of current data) */ size_t max; /* limit for i (end of current data) */
ssize_t nRead; /* (Temporary) number of bytes read */ ssize_t nRead; /* (Temporary) number of bytes read */
/* /*
* Read as many bytes as will fit in the buffer. * Read as many bytes as will fit in the buffer.
*/ */
end_loop: again:
gotNL = FALSE; gotNL = FALSE;
fbuf = FALSE; fbuf = FALSE;
nRead = read(job->inPipe, &job->outBuf[job->curPos], nRead = read(job->inPipe, &job->outBuf[job->curPos],
JOB_BUFSIZE - job->curPos); JOB_BUFSIZE - job->curPos);
if (nRead < 0) { if (nRead < 0) {
if (errno == EAGAIN) if (errno == EAGAIN)
return; return;
if (DEBUG(JOB)) { if (DEBUG(JOB)) {
perror("JobDoOutput(piperead)"); perror("JobDoOutput(piperead)");
} }
nr = 0; nr = 0;
} else { } else {
nr = (size_t)nRead; nr = (size_t)nRead;
} }
/* /*
* If we hit the end-of-file (the job is dead), we must flush its * If we hit the end-of-file (the job is dead), we must flush its
* remaining output, so pretend we read a newline if there's any * remaining output, so pretend we read a newline if there's any
* output remaining in the buffer. * output remaining in the buffer.
* Also clear the 'finish' flag so we stop looping. * Also clear the 'finish' flag so we stop looping.
*/ */
if ((nr == 0) && (job->curPos != 0)) { if (nr == 0 && job->curPos != 0) {
job->outBuf[job->curPos] = '\n'; job->outBuf[job->curPos] = '\n';
nr = 1; nr = 1;
finish = FALSE; finish = FALSE;
} else if (nr == 0) { } else if (nr == 0) {
finish = FALSE; finish = FALSE;
} }
/* /*
* Look for the last newline in the bytes we just got. If there is * Look for the last newline in the bytes we just got. If there is
* one, break out of the loop with 'i' as its index and gotNL set * one, break out of the loop with 'i' as its index and gotNL set
skipping to change at line 1890 skipping to change at line 1832
} }
if (finish) { if (finish) {
/* /*
* If the finish flag is true, we must loop until we hit * If the finish flag is true, we must loop until we hit
* end-of-file on the pipe. This is guaranteed to happen * end-of-file on the pipe. This is guaranteed to happen
* eventually since the other end of the pipe is now closed * eventually since the other end of the pipe is now closed
* (we closed it explicitly and the child has exited). When * (we closed it explicitly and the child has exited). When
* we do get an EOF, finish will be set FALSE and we'll fall * we do get an EOF, finish will be set FALSE and we'll fall
* through and out. * through and out.
*/ */
goto end_loop; goto again;
} }
} }
static void static void
JobRun(GNode *targ) JobRun(GNode *targ)
{ {
#if 0 #if 0
/* /*
* Unfortunately it is too complicated to run .BEGIN, .END, and * Unfortunately it is too complicated to run .BEGIN, .END, and
* .INTERRUPT job in the parallel job module. As of 2020-09-25, * .INTERRUPT job in the parallel job module. As of 2020-09-25,
skipping to change at line 1988 skipping to change at line 1930
case SIGTSTP: case SIGTSTP:
(void)printf("*** [%s] Suspended\n", job->node->name); (void)printf("*** [%s] Suspended\n", job->node->name);
break; break;
case SIGSTOP: case SIGSTOP:
(void)printf("*** [%s] Stopped\n", job->node->name); (void)printf("*** [%s] Stopped\n", job->node->name);
break; break;
default: default:
(void)printf("*** [%s] Stopped -- signal %d\n", (void)printf("*** [%s] Stopped -- signal %d\n",
job->node->name, WSTOPSIG(status)); job->node->name, WSTOPSIG(status));
} }
job->job_suspended = 1; job->suspended = TRUE;
} }
(void)fflush(stdout); (void)fflush(stdout);
return; return;
} }
job->job_state = JOB_ST_FINISHED; job->status = JOB_ST_FINISHED;
job->exit_status = WAIT_STATUS(status); job->exit_status = WAIT_STATUS(status);
JobFinish(job, status); JobFinish(job, status);
} }
/* Catch the output from our children, if we're using pipes do so. Otherwise /* Catch the output from our children, if we're using pipes do so. Otherwise
* just block time until we get a signal(most likely a SIGCHLD) since there's * just block time until we get a signal(most likely a SIGCHLD) since there's
* no point in just spinning when there's nothing to do and the reaping of a * no point in just spinning when there's nothing to do and the reaping of a
* child can wait for a while. */ * child can wait for a while. */
void void
skipping to change at line 2038 skipping to change at line 1980
case -1: case -1:
Punt("token pipe read: %s", strerror(errno)); Punt("token pipe read: %s", strerror(errno));
case 1: case 1:
if (token == DO_JOB_RESUME[0]) if (token == DO_JOB_RESUME[0])
/* Complete relay requested from our SIGCONT handler */ /* Complete relay requested from our SIGCONT handler */
JobRestartJobs(); JobRestartJobs();
break; break;
default: default:
abort(); abort();
} }
--nready; nready--;
} }
Job_CatchChildren(); Job_CatchChildren();
if (nready == 0) if (nready == 0)
return; return;
for (i = npseudojobs * nfds_per_job(); i < nfds; i++) { for (i = npseudojobs * nfds_per_job(); i < nfds; i++) {
if (!fds[i].revents) if (!fds[i].revents)
continue; continue;
job = jobfds[i]; job = jobfds[i];
if (job->job_state == JOB_ST_RUNNING) if (job->status == JOB_ST_RUNNING)
JobDoOutput(job, FALSE); JobDoOutput(job, FALSE);
#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV)
/* /*
* With meta mode, we may have activity on the job's filemon * With meta mode, we may have activity on the job's filemon
* descriptor too, which at the moment is any pollfd other than * descriptor too, which at the moment is any pollfd other than
* job->inPollfd. * job->inPollfd.
*/ */
if (useMeta && job->inPollfd != &fds[i]) { if (useMeta && job->inPollfd != &fds[i]) {
if (meta_job_event(job) <= 0) { if (meta_job_event(job) <= 0) {
fds[i].events = 0; /* never mind */ fds[i].events = 0; /* never mind */
skipping to change at line 2073 skipping to change at line 2015
if (--nready == 0) if (--nready == 0)
return; return;
} }
} }
/* Start the creation of a target. Basically a front-end for JobStart used by /* Start the creation of a target. Basically a front-end for JobStart used by
* the Make module. */ * the Make module. */
void void
Job_Make(GNode *gn) Job_Make(GNode *gn)
{ {
(void)JobStart(gn, 0); (void)JobStart(gn, JOB_NONE);
} }
void void
Shell_Init(void) Shell_Init(void)
{ {
if (shellPath == NULL) { if (shellPath == NULL) {
/* /*
* We are using the default shell, which may be an absolute * We are using the default shell, which may be an absolute
* path if DEFSHELL_CUSTOM is defined. * path if DEFSHELL_CUSTOM is defined.
*/ */
shellName = commandShell->name; shellName = commandShell->name;
#ifdef DEFSHELL_CUSTOM #ifdef DEFSHELL_CUSTOM
if (*shellName == '/') { if (*shellName == '/') {
shellPath = shellName; shellPath = shellName;
shellName = strrchr(shellPath, '/'); shellName = strrchr(shellPath, '/');
shellName++; shellName++;
} else } else
#endif #endif
shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName); shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName);
} }
Var_Set_with_flags(".SHELL", shellPath, VAR_CMDLINE, VAR_SET_READONLY); Var_SetWithFlags(".SHELL", shellPath, VAR_CMDLINE, VAR_SET_READONLY);
if (commandShell->exit == NULL) { if (commandShell->exit == NULL) {
commandShell->exit = ""; commandShell->exit = "";
} }
if (commandShell->echo == NULL) { if (commandShell->echo == NULL) {
commandShell->echo = ""; commandShell->echo = "";
} }
if (commandShell->hasErrCtl && commandShell->exit[0] != '\0') { if (commandShell->hasErrCtl && commandShell->exit[0] != '\0') {
if (shellErrFlag && if (shellErrFlag &&
strcmp(commandShell->exit, &shellErrFlag[1]) != 0) { strcmp(commandShell->exit, &shellErrFlag[1]) != 0) {
free(shellErrFlag); free(shellErrFlag);
skipping to change at line 2172 skipping to change at line 2114
* Since their termination causes a 'Child (pid) not in table' message, * Since their termination causes a 'Child (pid) not in table' message,
* Collect the status of any that are already dead, and suppress the * Collect the status of any that are already dead, and suppress the
* error message if there are any undead ones. * error message if there are any undead ones.
*/ */
for (;;) { for (;;) {
int rval, status; int rval, status;
rval = waitpid((pid_t) -1, &status, WNOHANG); rval = waitpid((pid_t) -1, &status, WNOHANG);
if (rval > 0) if (rval > 0)
continue; continue;
if (rval == 0) if (rval == 0)
lurking_children = 1; lurking_children = TRUE;
break; break;
} }
Shell_Init(); Shell_Init();
JobCreatePipe(&childExitJob, 3); JobCreatePipe(&childExitJob, 3);
/* Preallocate enough for the maximum number of jobs. */ /* Preallocate enough for the maximum number of jobs. */
fds = bmake_malloc(sizeof(*fds) * fds = bmake_malloc(sizeof *fds *
(npseudojobs + (size_t)opts.maxJobs) * nfds_per_job()); (npseudojobs + (size_t)opts.maxJobs) * nfds_per_job());
jobfds = bmake_malloc(sizeof(*jobfds) * jobfds = bmake_malloc(sizeof *jobfds *
(npseudojobs + (size_t)opts.maxJobs) * nfds_per_job()); (npseudojobs + (size_t)opts.maxJobs) * nfds_per_job());
/* These are permanent entries and take slots 0 and 1 */ /* These are permanent entries and take slots 0 and 1 */
watchfd(&tokenWaitJob); watchfd(&tokenWaitJob);
watchfd(&childExitJob); watchfd(&childExitJob);
sigemptyset(&caught_signals); sigemptyset(&caught_signals);
/* /*
* Install a SIGCHLD handler. * Install a SIGCHLD handler.
*/ */
skipping to change at line 2328 skipping to change at line 2270
size_t argc; size_t argc;
char *path; char *path;
Shell newShell; Shell newShell;
Boolean fullSpec = FALSE; Boolean fullSpec = FALSE;
Shell *sh; Shell *sh;
pp_skip_whitespace(&line); pp_skip_whitespace(&line);
free(shellArgv); free(shellArgv);
memset(&newShell, 0, sizeof(newShell)); memset(&newShell, 0, sizeof newShell);
/* /*
* Parse the specification by keyword * Parse the specification by keyword
*/ */
wordsList = Str_Words(line, TRUE); wordsList = Str_Words(line, TRUE);
words = wordsList.words; words = wordsList.words;
argc = wordsList.len; argc = wordsList.len;
path = wordsList.freeIt; path = wordsList.freeIt;
if (words == NULL) { if (words == NULL) {
Error("Unterminated quoted string [%s]", line); Error("Unterminated quoted string [%s]", line);
skipping to change at line 2440 skipping to change at line 2382
} }
if (!fullSpec) { if (!fullSpec) {
if ((sh = FindShellByName(shellName)) == NULL) { if ((sh = FindShellByName(shellName)) == NULL) {
Parse_Error(PARSE_WARNING, "%s: No matching shell", Parse_Error(PARSE_WARNING, "%s: No matching shell",
shellName); shellName);
free(words); free(words);
return FALSE; return FALSE;
} }
commandShell = sh; commandShell = sh;
} else { } else {
commandShell = bmake_malloc(sizeof(Shell)); commandShell = bmake_malloc(sizeof *commandShell);
*commandShell = newShell; *commandShell = newShell;
} }
/* this will take care of shellErrFlag */ /* this will take care of shellErrFlag */
Shell_Init(); Shell_Init();
} }
if (commandShell->echoOn && commandShell->echoOff) { if (commandShell->echoOn && commandShell->echoOff) {
commandShell->hasEchoCtl = TRUE; commandShell->hasEchoCtl = TRUE;
} }
skipping to change at line 2491 skipping to change at line 2433
Job *job; /* job descriptor in that element */ Job *job; /* job descriptor in that element */
GNode *interrupt; /* the node describing the .INTERRUPT target */ GNode *interrupt; /* the node describing the .INTERRUPT target */
sigset_t mask; sigset_t mask;
GNode *gn; GNode *gn;
aborting = ABORT_INTERRUPT; aborting = ABORT_INTERRUPT;
JobSigLock(&mask); JobSigLock(&mask);
for (job = job_table; job < job_table_end; job++) { for (job = job_table; job < job_table_end; job++) {
if (job->job_state != JOB_ST_RUNNING) if (job->status != JOB_ST_RUNNING)
continue; continue;
gn = job->node; gn = job->node;
JobDeleteTarget(gn); JobDeleteTarget(gn);
if (job->pid) { if (job->pid) {
DEBUG2(JOB, "JobInterrupt passing signal %d to child %d.\n", DEBUG2(JOB, "JobInterrupt passing signal %d to child %d.\n",
signo, job->pid); signo, job->pid);
KILLPG(job->pid, signo); KILLPG(job->pid, signo);
} }
skipping to change at line 2513 skipping to change at line 2455
JobSigUnlock(&mask); JobSigUnlock(&mask);
if (runINTERRUPT && !opts.touchFlag) { if (runINTERRUPT && !opts.touchFlag) {
interrupt = Targ_FindNode(".INTERRUPT"); interrupt = Targ_FindNode(".INTERRUPT");
if (interrupt != NULL) { if (interrupt != NULL) {
opts.ignoreErrors = FALSE; opts.ignoreErrors = FALSE;
JobRun(interrupt); JobRun(interrupt);
} }
} }
Trace_Log(MAKEINTR, 0); Trace_Log(MAKEINTR, NULL);
exit(signo); exit(signo);
} }
/* Do the final processing, i.e. run the commands attached to the .END target. /* Do the final processing, i.e. run the commands attached to the .END target.
* *
* Return the number of errors reported. */ * Return the number of errors reported. */
int int
Job_Finish(void) Job_Finish(void)
{ {
GNode *endNode = Targ_GetEndNode(); GNode *endNode = Targ_GetEndNode();
skipping to change at line 2570 skipping to change at line 2512
void void
Job_AbortAll(void) Job_AbortAll(void)
{ {
Job *job; /* the job descriptor in that element */ Job *job; /* the job descriptor in that element */
WAIT_T foo; WAIT_T foo;
aborting = ABORT_ERROR; aborting = ABORT_ERROR;
if (jobTokensRunning) { if (jobTokensRunning) {
for (job = job_table; job < job_table_end; job++) { for (job = job_table; job < job_table_end; job++) {
if (job->job_state != JOB_ST_RUNNING) if (job->status != JOB_ST_RUNNING)
continue; continue;
/* /*
* kill the child process with increasingly drastic signals to make * kill the child process with increasingly drastic signals to make
* darn sure it's dead. * darn sure it's dead.
*/ */
KILLPG(job->pid, SIGINT); KILLPG(job->pid, SIGINT);
KILLPG(job->pid, SIGKILL); KILLPG(job->pid, SIGKILL);
} }
} }
skipping to change at line 2596 skipping to change at line 2538
} }
/* Tries to restart stopped jobs if there are slots available. /* Tries to restart stopped jobs if there are slots available.
* Called in process context in response to a SIGCONT. */ * Called in process context in response to a SIGCONT. */
static void static void
JobRestartJobs(void) JobRestartJobs(void)
{ {
Job *job; Job *job;
for (job = job_table; job < job_table_end; job++) { for (job = job_table; job < job_table_end; job++) {
if (job->job_state == JOB_ST_RUNNING && if (job->status == JOB_ST_RUNNING &&
(make_suspended || job->job_suspended)) { (make_suspended || job->suspended)) {
DEBUG1(JOB, "Restarting stopped job pid %d.\n", job->pid); DEBUG1(JOB, "Restarting stopped job pid %d.\n", job->pid);
if (job->job_suspended) { if (job->suspended) {
(void)printf("*** [%s] Continued\n", job->node->name); (void)printf("*** [%s] Continued\n", job->node->name);
(void)fflush(stdout); (void)fflush(stdout);
} }
job->job_suspended = 0; job->suspended = FALSE;
if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) { if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) {
debug_printf("Failed to send SIGCONT to %d\n", job->pid); debug_printf("Failed to send SIGCONT to %d\n", job->pid);
} }
} }
if (job->job_state == JOB_ST_FINISHED) if (job->status == JOB_ST_FINISHED)
/* Job exit deferred after calling waitpid() in a signal handler */ /* Job exit deferred after calling waitpid() in a signal handler */
JobFinish(job, job->exit_status); JobFinish(job, job->exit_status);
} }
make_suspended = 0; make_suspended = FALSE;
} }
static void static void
watchfd(Job *job) watchfd(Job *job)
{ {
if (job->inPollfd != NULL) if (job->inPollfd != NULL)
Punt("Watching watched job"); Punt("Watching watched job");
fds[nfds].fd = job->inPipe; fds[nfds].fd = job->inPipe;
fds[nfds].events = POLLIN; fds[nfds].events = POLLIN;
skipping to change at line 2716 skipping to change at line 2658
/* Pipe passed in from parent */ /* Pipe passed in from parent */
tokenWaitJob.inPipe = jp_0; tokenWaitJob.inPipe = jp_0;
tokenWaitJob.outPipe = jp_1; tokenWaitJob.outPipe = jp_1;
(void)fcntl(jp_0, F_SETFD, FD_CLOEXEC); (void)fcntl(jp_0, F_SETFD, FD_CLOEXEC);
(void)fcntl(jp_1, F_SETFD, FD_CLOEXEC); (void)fcntl(jp_1, F_SETFD, FD_CLOEXEC);
return; return;
} }
JobCreatePipe(&tokenWaitJob, 15); JobCreatePipe(&tokenWaitJob, 15);
snprintf(jobarg, sizeof(jobarg), "%d,%d", snprintf(jobarg, sizeof jobarg, "%d,%d",
tokenWaitJob.inPipe, tokenWaitJob.outPipe); tokenWaitJob.inPipe, tokenWaitJob.outPipe);
Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL);
Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL); Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL);
/* /*
* Preload the job pipe with one token per job, save the one * Preload the job pipe with one token per job, save the one
* "extra" token for the primary job. * "extra" token for the primary job.
* *
* XXX should clip maxJobs against PIPE_BUF -- if max_tokens is * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is
skipping to change at line 2784 skipping to change at line 2726
} }
if (count == 1 && tok != '+') { if (count == 1 && tok != '+') {
/* make being abvorted - remove any other job tokens */ /* make being abvorted - remove any other job tokens */
DEBUG2(JOB, "(%d) aborted by token %c\n", getpid(), tok); DEBUG2(JOB, "(%d) aborted by token %c\n", getpid(), tok);
while (read(tokenWaitJob.inPipe, &tok1, 1) == 1) while (read(tokenWaitJob.inPipe, &tok1, 1) == 1)
continue; continue;
/* And put the stopper back */ /* And put the stopper back */
while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
continue; continue;
if (dieQuietly(NULL, 1)) if (shouldDieQuietly(NULL, 1))
exit(2); exit(2);
Fatal("A failure has been detected in another branch of the parallel make "); Fatal("A failure has been detected in another branch of the parallel make ");
} }
if (count == 1 && jobTokensRunning == 0) if (count == 1 && jobTokensRunning == 0)
/* We didn't want the token really */ /* We didn't want the token really */
while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN)
continue; continue;
jobTokensRunning++; jobTokensRunning++;
skipping to change at line 2860 skipping to change at line 2802
if (timeout < 0) { if (timeout < 0) {
tvp = NULL; tvp = NULL;
} else { } else {
usecs = timeout * 1000; usecs = timeout * 1000;
tv.tv_sec = usecs / 1000000; tv.tv_sec = usecs / 1000000;
tv.tv_usec = usecs % 1000000; tv.tv_usec = usecs % 1000000;
tvp = &tv; tvp = &tv;
} }
nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp); nselect = select(maxfd + 1, &rfds, &wfds, NULL, tvp);
if (nselect <= 0) if (nselect <= 0)
return nselect; return nselect;
npoll = 0; npoll = 0;
for (i = 0; i < nfd; i++) { for (i = 0; i < nfd; i++) {
if (FD_ISSET(fd[i].fd, &rfds)) if (FD_ISSET(fd[i].fd, &rfds))
fd[i].revents |= POLLIN; fd[i].revents |= POLLIN;
if (FD_ISSET(fd[i].fd, &wfds)) if (FD_ISSET(fd[i].fd, &wfds))
 End of changes. 108 change blocks. 
299 lines changed or deleted 241 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)