Here is the "next" incarnation of that diff.
So, the pipe stuff is gone for good, it's no longer really useful.
Thinking some more about it, "old make" kind-of required it: it did spawn
a new process for each job, process was responsible for handling the job's
commands (which includes displaying command lines and running them).
Since that middleman is gone, it's make proper that does most of the display
now (apart from commands that output stuff), so keeping things from being
on top of each other is ways less important.
With this patch, job running is now more or less the same in compatmake and
make-j. Specifically, compatmake has a single static job, and uses the same
functions to run commands/handle post-mortem (the job creation part still
has slightly different semantics, I intend to address that soon).
That job is permanently entered as a runningJob/errorJob,
so status function/final error display is now EXACTLY the same.
Error location handling is now more accurate: the parser distinguishes between
a target start (last line where we see the target right before it acquires
commands) and the command being run. If a target acquires .USE commands,
those locations may even be in different files !
In case of non-fatal errors, the display is still about the same, I figure
that saying
*** Error code 1 in target foo (ignored)
is slightly better than
*** Error code 1 (ignored)
but that's about all.
oh, and if a target with a silent command leads to a fatal error, then we
display THAT command, so that variable expansion issues may be solved more
quickly.
Todo list for this specific work (finally shrinking):
- zap/unify more code in the error handling path (some of the jobInterrupt
and the job_failure code)
- make sure +/.MAKE stuff is handled identically in compat and makej mode.
Next line of work:
- fix remaining problems with makej.
Index: compat.c
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/compat.c,v
retrieving revision 1.76
diff -u -p -r1.76 compat.c
--- compat.c 22 Mar 2012 13:47:12 -0000 1.76
+++ compat.c 9 Sep 2012 10:36:16 -0000
@@ -229,14 +229,9 @@ CompatMake(void *gnp, /* The node to mak
pgn->must_make = false;
else {
- if (gn->origin.lineno)
- printf("\n\nStop in %s (line %lu of %s).\n",
- Var_Value(".CURDIR"),
- (unsigned long)gn->origin.lineno,
- gn->origin.fname);
- else
- printf("\n\nStop in %s.\n",
- Var_Value(".CURDIR"));
+ printf("Stop in %s:\n",
+ Var_Value(".CURDIR"));
+ print_errors();
exit(1);
}
} else if (gn->built_status == ERROR)
Index: engine.c
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/engine.c,v
retrieving revision 1.30
diff -u -p -r1.30 engine.c
--- engine.c 25 Aug 2012 08:12:56 -0000 1.30
+++ engine.c 9 Sep 2012 10:53:07 -0000
@@ -66,13 +66,14 @@
static void MakeTimeStamp(void *, void *);
static int rewrite_time(const char *);
+typedef void (*psighandler)(int);
static void setup_signal(int, psighandler);
+static void setup_all_signals(void);
+static void notice_signal(int);
static void setup_meta(void);
static char **recheck_command_for_shell(char **);
-static int setup_and_run_command(char *, GNode *, int);
-static void run_command(const char *, bool);
-static int run_prepared_gnode(GNode *);
+static int setup_and_run_command(char *, GNode *);
static void handle_compat_interrupts(GNode *);
bool
@@ -453,9 +454,10 @@ Make_OODate(GNode *gn)
return oodate;
}
-volatile sig_atomic_t got_signal;
+volatile sig_atomic_t got_fatal;
-volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT, got_SIGTERM;
+volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT, got_SIGTERM,
+ got_SIGINFO;
static void
setup_signal(int sig, psighandler h)
@@ -464,48 +466,47 @@ setup_signal(int sig, psighandler h)
(void)signal(sig, h);
}
-void
-setup_all_signals(psighandler interrupt, psighandler jc)
-{
- /*
- * Catch the four signals that POSIX specifies if they aren't ignored.
- * handle_signal will take care of calling JobInterrupt if appropriate.
- */
- setup_signal(SIGINT, interrupt);
- setup_signal(SIGHUP, interrupt);
- setup_signal(SIGQUIT, interrupt);
- setup_signal(SIGTERM, interrupt);
- setup_signal(SIGTSTP, jc);
- setup_signal(SIGTTOU, jc);
- setup_signal(SIGTTIN, jc);
- setup_signal(SIGWINCH, jc);
- setup_signal(SIGCONT, jc);
- got_signal = 0;
-}
-
-void
-SigHandler(int sig)
+static void
+notice_signal(int sig)
{
switch(sig) {
case SIGINT:
got_SIGINT++;
- got_signal = 1;
+ got_fatal = 1;
break;
case SIGHUP:
got_SIGHUP++;
- got_signal = 1;
+ got_fatal = 1;
break;
case SIGQUIT:
got_SIGQUIT++;
- got_signal = 1;
+ got_fatal = 1;
break;
case SIGTERM:
got_SIGTERM++;
- got_signal = 1;
+ got_fatal = 1;
+ break;
+ case SIGINFO:
+ got_SIGINFO++;
break;
}
}
+void
+setup_all_signals(void)
+{
+ /*
+ * Catch the four signals that POSIX specifies if they aren't ignored.
+ * handle_signal will take care of calling JobInterrupt if appropriate.
+ */
+ setup_signal(SIGINT, notice_signal);
+ setup_signal(SIGHUP, notice_signal);
+ setup_signal(SIGQUIT, notice_signal);
+ setup_signal(SIGTERM, notice_signal);
+ setup_signal(SIGINFO, notice_signal);
+ got_fatal = 0;
+}
+
/* 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
@@ -544,7 +545,7 @@ recheck_command_for_shell(char **av)
return av;
}
-static void
+void
run_command(const char *cmd, bool errCheck)
{
const char *p;
@@ -586,145 +587,25 @@ run_command(const char *cmd, bool errChe
_exit(1);
}
-/*-
- *-----------------------------------------------------------------------
- * setup_and_run_command --
- * Execute the next command for a target. If the command returns an
- * error, the node's built_status field is set to ERROR and creation stops.
- *
- * Results:
- * 0 in case of error, 1 if ok.
- *
- * Side Effects:
- * The node's 'built_status' field may be set to ERROR.
- *-----------------------------------------------------------------------
- */
-static int
-setup_and_run_command(char *cmd, GNode *gn, int dont_fork)
-{
- bool silent; /* Don't print command */
- bool doExecute; /* Execute the command */
- bool errCheck; /* Check errors */
- int reason; /* Reason for child's death */
- int status; /* Description of child's death */
- pid_t cpid; /* Child actually found */
- pid_t stat; /* Status of fork */
-
- silent = gn->type & OP_SILENT;
- errCheck = !(gn->type & OP_IGNORE);
- 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') {
- Error("%s expands to empty string", cmd);
- return 1;
- }
-
- 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 echoing if we're not supposed to be quiet
- * for this one. We also print the command if -n given. */
- if (!silent || noExecute) {
- printf("%s\n", cmd);
- fflush(stdout);
- }
- /* If we're not supposed to execute any commands, this is as far as
- * we go... */
- if (!doExecute)
- return 1;
-
- /* if we're running in parallel mode, we try not to fork the last
- * command, since it's exit status will be just fine... unless
- * errCheck is not set, in which case we must deal with the
- * status ourselves.
- */
- if (dont_fork && errCheck)
- run_command(cmd, errCheck);
- /*NOTREACHED*/
-
- /* Fork and execute the single command. If the fork fails, we abort. */
- switch (cpid = fork()) {
- case -1:
- Fatal("Could not fork");
- /*NOTREACHED*/
- case 0:
- run_command(cmd, errCheck);
- /*NOTREACHED*/
- default:
- break;
- }
-
- /* The child is off and running. Now all we can do is wait... */
- while (1) {
-
- while ((stat = waitpid(cpid, &reason, 0)) != cpid) {
- if (stat == -1 && errno != EINTR)
- break;
- }
-
- if (got_signal)
- break;
-
- if (stat != -1) {
- if (WIFEXITED(reason)) {
- status = WEXITSTATUS(reason); /* exited */
- if (status != 0)
- printf("*** Error code %d", status);
- } else {
- status = WTERMSIG(reason); /* signaled */
- printf("*** Signal %d", status);
- }
-
-
- if (!WIFEXITED(reason) || status != 0) {
- if (errCheck) {
- gn->built_status = ERROR;
- if (keepgoing)
- /* 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");
- status = 0;
- }
- }
- return !status;
- } else
- Fatal("error in wait: %s", strerror(errno));
- /*NOTREACHED*/
- }
- return 0;
-}
-
static void
handle_compat_interrupts(GNode *gn)
{
+ if (got_SIGINFO)
+ handle_siginfo();
+ if (!got_fatal)
+ return;
if (!Targ_Precious(gn)) {
char *file = Var(TARGET_INDEX, gn);
if (!noExecute && eunlink(file) != -1)
- Error("*** %s removed\n", file);
+ Error("*** %s removed", file);
}
if (got_SIGINT) {
signal(SIGINT, SIG_IGN);
signal(SIGTERM, SIG_IGN);
signal(SIGHUP, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
- got_signal = 0;
+ got_fatal = 0;
got_SIGINT = 0;
run_gnode(interrupt_node);
exit(255);
@@ -732,87 +613,195 @@ handle_compat_interrupts(GNode *gn)
exit(255);
}
+static Job myjob;
+
void
-expand_commands(GNode *gn)
+job_attach_node(Job *job, GNode *node)
{
- LstNode ln;
- char *cmd;
-
- Parse_SetLocation(&gn->origin);
- for (ln = Lst_First(&gn->commands); ln != NULL; ln = Lst_Adv(ln)) {
- cmd = Var_Subst(Lst_Datum(ln), &gn->context, false);
- Lst_AtEnd(&gn->expanded, cmd);
- }
+ job->node = node;
+ job->next_cmd = Lst_First(&node->commands);
+ job->exit_type = JOB_EXIT_OKAY;
+ job->location = NULL;
+ job->flags = 0; /* redundant ? */
}
-int
-run_gnode(GNode *gn)
+void
+job_handle_status(Job *job, int status)
{
- if (gn != NULL && (gn->type & OP_DUMMY) == 0) {
- expand_commands(gn);
- if (fatal_errors)
- exit(1);
- return run_prepared_gnode(gn);
+ if (WIFEXITED(status)) {
+ job->code = WEXITSTATUS(status);/* exited */
+ if (status != 0) {
+ printf("*** Error code %d", job->code);
+ job->exit_type = JOB_EXIT_BAD;
+ } else
+ job->exit_type = JOB_EXIT_OKAY;
} else {
- return NOSUCHNODE;
+ job->exit_type = JOB_SIGNALED;
+ job->code = WTERMSIG(status); /* signaled */
+ printf("*** Signal %d", job->code);
+ }
+ if (job->exit_type != JOB_EXIT_OKAY) {
+ printf(" in target %s", job->node->name);
+ if (job->flags & JOB_ERRCHECK) {
+ job->node->built_status = ERROR;
+ printf(", line %lu of %s", job->location->lineno,
+ job->location->fname);
+ if (job->flags & JOB_SILENT)
+ printf(": %s", job->cmd);
+ if (keepgoing)
+ /* Abort the current target,
+ * but let others continue. */
+ printf(" (continuing)\n");
+ else
+ printf("\n");
+ } else {
+ /* Continue executing commands for
+ * this target. If we return 0,
+ * this will happen... */
+ printf(" (ignored)\n");
+ job->exit_type = JOB_EXIT_OKAY;
+ }
}
}
-static int
-run_prepared_gnode(GNode *gn)
+int
+run_gnode(GNode *gn)
{
- char *cmd;
+ bool finished;
+ int stat;
+ int status;
+
+ if (!gn || (gn->type & OP_DUMMY))
+ return NOSUCHNODE;
gn->built_status = MADE;
- while ((cmd = Lst_DeQueue(&gn->expanded)) != NULL) {
- if (setup_and_run_command(cmd, gn, 0) == 0)
+
+ job_attach_node(&myjob, gn);
+ while (myjob.exit_type == JOB_EXIT_OKAY) {
+ finished = job_run_next(&myjob);
+ if (finished)
break;
- free(cmd);
+ while ((stat = waitpid(myjob.pid, &status, 0)) != myjob.pid) {
+ if (stat == -1 && errno == EINTR) {
+ handle_compat_interrupts(gn);
+ continue;
+ }
+ if (stat == -1)
+ Fatal("error in wait: %s", strerror(errno));
+ }
+ job_handle_status(&myjob, status);
}
- free(cmd);
- if (got_signal)
- handle_compat_interrupts(gn);
+
return gn->built_status;
}
+
void
-run_gnode_parallel(GNode *gn)
+setup_engine(int parallel)
{
- char *cmd;
+ static int already_setup = 0;
- gn->built_status = MADE;
- /* XXX don't bother freeing cmd, we're dead anyways ! */
- while ((cmd = Lst_DeQueue(&gn->expanded)) != NULL) {
- if (setup_and_run_command(cmd, gn,
- Lst_IsEmpty(&gn->expanded)) == 0)
+ if (!already_setup) {
+ setup_meta();
+ setup_all_signals();
+ already_setup = 1;
+ /* so that stuff in the job module knows about us */
+ if (!parallel) {
+ myjob.cmd = NULL;
+ register_job(&myjob);
+ }
+ }
+}
+
+static bool
+do_run_command(Job *job, const char *cmd)
+{
+ bool silent; /* Don't print command */
+ bool doExecute; /* Execute the command */
+ bool errCheck; /* Check errors */
+ pid_t cpid; /* Child pid */
+
+ silent = job->node->type & OP_SILENT;
+ errCheck = !(job->node->type & OP_IGNORE);
+ 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') {
+ Error("%s expands to empty string", cmd);
+ return false;
+ }
+
+ for (;; cmd++) {
+ if (*cmd == '@')
+ silent = DEBUG(LOUD) ? false : true;
+ else if (*cmd == '-')
+ errCheck = false;
+ else if (*cmd == '+')
+ doExecute = true;
+ else
break;
}
- /* Normally, we don't reach this point, unless the last command
- * ignores error, in which case we interpret the status ourselves.
- */
- switch(gn->built_status) {
- case MADE:
- exit(0);
- case ERROR:
- exit(1);
+ 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);
+
+ /* Fork and execute the single command. If the fork fails, we abort. */
+ switch (cpid = fork()) {
+ case -1:
+ Punt("Could not fork");
+ /*NOTREACHED*/
+ case 0:
+ run_command(cmd, errCheck);
+ /*NOTREACHED*/
default:
- fprintf(stderr, "Could not run gnode, returned %d\n",
- gn->built_status);
- exit(1);
+ job->pid = cpid;
+ if (errCheck)
+ job->flags |= JOB_ERRCHECK;
+ else
+ job->flags &= ~JOB_ERRCHECK;
+ return true;
}
}
-void
-setup_engine(int parallel)
+bool
+job_run_next(Job *job)
{
- static int already_setup = 0;
+ char *cmd;
+ bool started;
+ GNode *gn = job->node;
- if (!already_setup) {
- setup_meta();
- if (parallel)
- setup_all_signals(parallel_handler, parallel_handler);
- else
- setup_all_signals(SigHandler, SIG_DFL);
- already_setup = 1;
+ setup_engine(1);
+ while (job->next_cmd != NULL) {
+ struct command *command = Lst_Datum(job->next_cmd);
+ job->location = &command->location;
+ Parse_SetLocation(job->location);
+ cmd = Var_Subst(command->string, &gn->context, false);
+ job->next_cmd = Lst_Adv(job->next_cmd);
+ if (fatal_errors)
+ Punt(NULL);
+ started = do_run_command(job, cmd);
+ if (started) {
+ job->cmd = cmd;
+ return false;
+ } else
+ free(cmd);
}
+ job->exit_type = JOB_EXIT_OKAY;
+ return true;
}
+
Index: engine.h
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/engine.h,v
retrieving revision 1.9
diff -u -p -r1.9 engine.h
--- engine.h 19 Jul 2010 19:30:37 -0000 1.9
+++ engine.h 9 Sep 2012 10:50:18 -0000
@@ -66,19 +66,56 @@ extern bool Make_OODate(GNode *);
* fill all dynamic variables for a node.
*/
extern void Make_DoAllVar(GNode *);
-extern volatile sig_atomic_t got_signal;
+extern volatile sig_atomic_t got_fatal;
extern volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT,
- got_SIGTERM, got_SIGTSTP, got_SIGTTOU, got_SIGTTIN, got_SIGWINCH,
- got_SIGCONT;
+ got_SIGTERM, got_SIGINFO;
extern void SigHandler(int);
extern int run_gnode(GNode *);
-extern void run_gnode_parallel(GNode *);
-extern void expand_commands(GNode *);
extern void setup_engine(int);
-typedef void (*psighandler)(int);
-extern void setup_all_signals(psighandler, psighandler);
+extern void run_command(const char *, bool);
+
+/*-
+ * Job Table definitions.
+ *
+ * Each job has several things associated with it:
+ * 1) The process id of the child shell
+ * 2) The graph node describing the target being made by this job
+ * 3) State associated to latest command run
+ * 5) A word of flags which determine how the module handles errors,
+ * echoing, etc. for the job
+ *
+ * The job "table" is kept as a linked Lst in 'jobs', with the number of
+ * active jobs maintained in the 'nJobs' variable. At no time will this
+ * exceed the value of 'maxJobs', initialized by the Job_Init function.
+ *
+ * When a job is finished, the Make_Update function is called on each of the
+ * parents of the node which was just remade. This takes care of the upward
+ * traversal of the dependency graph.
+ */
+typedef struct Job_ {
+ Location *location;
+ pid_t pid; /* Current command process id */
+ int exit_type; /* last child exit or signal */
+#define JOB_EXIT_OKAY 0
+#define JOB_EXIT_BAD 1
+#define JOB_SIGNALED 2
+ int code; /* exit status or signal code */
+ LstNode next_cmd; /* Next command to run */
+ char *cmd; /* Last command run */
+ GNode *node; /* Target of this job */
+ unsigned short flags;
+#define JOB_SILENT 0x001 /* Command was silent */
+#define JOB_IS_EXPENSIVE 0x002
+#define JOB_IS_SPECIAL 0x004 /* Target is a special one. */
+#define JOB_ERRCHECK 0x008 /* command wants errcheck */
+} Job;
+
+extern bool job_run_next(Job *);
+
+extern void job_attach_node(Job *, GNode *);
+extern void job_handle_status(Job *, int);
#endif
Index: error.c
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/error.c,v
retrieving revision 1.21
diff -u -p -r1.21 error.c
--- error.c 22 Mar 2012 13:50:30 -0000 1.21
+++ error.c 4 Sep 2012 16:43:54 -0000
@@ -44,7 +44,6 @@
#include "lowparse.h"
int fatal_errors = 0;
-bool supervise_jobs = false;
static void ParseVErrorInternal(const Location *, int, const char *, va_list);
/*-
@@ -77,8 +76,7 @@ Fatal(char *fmt, ...)
{
va_list ap;
- if (supervise_jobs)
- Job_Wait();
+ Job_Wait();
va_start(ap, fmt);
(void)vfprintf(stderr, fmt, ap);
@@ -102,13 +100,15 @@ Fatal(char *fmt, ...)
void
Punt(char *fmt, ...)
{
- va_list ap;
+ if (fmt) {
+ va_list ap;
- va_start(ap, fmt);
- (void)fprintf(stderr, "make: ");
- (void)vfprintf(stderr, fmt, ap);
- va_end(ap);
- (void)fprintf(stderr, "\n");
+ va_start(ap, fmt);
+ (void)fprintf(stderr, "make: ");
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ (void)fprintf(stderr, "\n");
+ }
Job_AbortAll();
if (DEBUG(GRAPH2))
@@ -118,19 +118,16 @@ Punt(char *fmt, ...)
/*
* Finish --
- * Called when aborting due to errors in child shell to signal
- * abnormal exit.
+ * Called when aborting due to errors in command or fatal signal
*
* Side Effects:
* The program exits
*/
void
-Finish(int errors) /* number of errors encountered in Make_Make */
+Finish()
{
Job_Wait();
- if (errors != 0) {
- Error("Stop in %s:", Var_Value(".CURDIR"));
- }
+ Error("Stop in %s:", Var_Value(".CURDIR"));
print_errors();
if (DEBUG(GRAPH2))
Targ_PrintGraph(2);
Index: error.h
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/error.h,v
retrieving revision 1.11
diff -u -p -r1.11 error.h
--- error.h 19 Jul 2010 19:46:44 -0000 1.11
+++ error.h 4 Sep 2012 16:43:54 -0000
@@ -44,7 +44,7 @@
extern void Error(char *, ...);
extern void Fatal(char *, ...);
extern void Punt(char *, ...);
-extern void Finish(int);
+extern void Finish();
/*
* Error levels for parsing. PARSE_FATAL means the process cannot continue
Index: gnode.h
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/gnode.h,v
retrieving revision 1.19
diff -u -p -r1.19 gnode.h
--- gnode.h 11 Apr 2012 18:27:30 -0000 1.19
+++ gnode.h 4 Sep 2012 16:43:54 -0000
@@ -132,7 +132,6 @@ struct GNode_ {
SymTable context; /* The local variables */
Location origin; /* First line number and file name of commands. */
LIST commands; /* Creation commands */
- LIST expanded; /* Expanded commands */
struct Suff_ *suffix;/* Suffix for the node (determined by
* Suff_FindDeps and opaque to everyone
* but the Suff module) */
@@ -141,6 +140,12 @@ struct GNode_ {
char *basename; /* pointer to name stripped of path */
struct GNode_ *next;
char name[1]; /* The target's name */
+};
+
+struct command
+{
+ Location location;
+ char string[1];
};
#define has_been_built(gn) \
Index: job.c
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/job.c,v
retrieving revision 1.123
diff -u -p -r1.123 job.c
--- job.c 25 Aug 2012 08:12:56 -0000 1.123
+++ job.c 9 Sep 2012 11:06:53 -0000
@@ -103,52 +103,6 @@
#define SEL_USEC 500000
-/*-
- * Job Table definitions.
- *
- * Each job has several things associated with it:
- * 1) The process id of the child shell
- * 2) The graph node describing the target being made by this job
- * 3) An FILE* for writing out the commands. This is only
- * used before the job is actually started.
- * 4) Things used for handling the shell's output.
- * the output is being caught via a pipe and
- * the descriptors of our pipe, an array in which output is line
- * buffered and the current position in that buffer are all
- * maintained for each job.
- * 5) A word of flags which determine how the module handles errors,
- * echoing, etc. for the job
- *
- * The job "table" is kept as a linked Lst in 'jobs', with the number of
- * active jobs maintained in the 'nJobs' variable. At no time will this
- * exceed the value of 'maxJobs', initialized by the Job_Init function.
- *
- * When a job is finished, the Make_Update function is called on each of the
- * parents of the node which was just remade. This takes care of the upward
- * traversal of the dependency graph.
- */
-#define JOB_BUFSIZE 1024
-struct job_pipe {
- int fd;
- char buffer[JOB_BUFSIZE];
- size_t pos;
-};
-
-typedef struct Job_ {
- pid_t pid; /* The child's process ID */
- GNode *node; /* The target the child is making */
- short flags; /* Flags to control treatment of job */
- LstNode p;
-#define JOB_DIDOUTPUT 0x001
-#define JOB_IS_SPECIAL 0x004 /* Target is a special one. */
-#define JOB_IS_EXPENSIVE 0x002
- struct job_pipe in[2];
-} Job;
-
-struct job_pid {
- pid_t pid;
-};
-
static int aborting = 0; /* why is the make aborting? */
#define ABORT_ERROR 1 /* Because of an error */
#define ABORT_INTERRUPT 2 /* Because it was interrupted */
@@ -156,123 +110,81 @@ static int aborting = 0; /* why is t
static int maxJobs; /* The most children we can run at once */
static int nJobs; /* The number of current children */
-static bool expensive_job;
-static LIST runningJobs; /* The structures that describe them */
+static bool expensive_job; /* Mark recursive shit so we shouldn't start
+ * something else at the same time
+ */
+static LIST runningJobs;
+static LIST heldJobs; /* Jobs not running yet because of expensive */
+static LIST errorJobs; /* Jobs in error at end */
static GNode *lastNode; /* The node for which output was most recently
* produced. */
-static LIST job_pids; /* a simple list that doesn't move that much */
-
-/* data structure linked to job handling through select */
-static fd_set *output_mask = NULL; /* File descriptors to look for */
-
-static fd_set *actual_mask = NULL; /* actual select argument */
-static int largest_fd = -1;
-static size_t mask_size = 0;
-
-/* wait possibilities */
-#define JOB_EXITED 0
-#define JOB_SIGNALED 1
-#define JOB_UNKNOWN 4
-
-static LIST errorsList;
-static int errors;
-struct error_info {
- int reason;
- int code;
- GNode *n;
-};
-
-/* for blocking/unblocking */
-static sigset_t oset, set;
-static void block_signals(void);
-static void unblock_signals(void);
static void handle_all_signals(void);
static void handle_signal(int);
static int JobCmpPid(void *, void *);
-static void process_job_status(Job *, int);
-static void JobExec(Job *);
+static void process_job_status(Job *, bool);
static void JobStart(GNode *, int);
static void JobInterrupt(bool, int);
static void debug_printf(const char *, ...);
static Job *prepare_job(GNode *, int);
static void banner(Job *, FILE *);
static bool Job_Full(void);
+static void remove_job(Job *, bool);
+static bool handle_job_child(Job *);
+static void continue_job(Job *);
+static void may_remove_job(Job *);
+static bool advance_job(Job *);
-/***
- *** Input/output from jobs
- ***/
-
-/* prepare_pipe(jp, &fd):
- * set up pipe data structure (buffer and pos) corresponding to
- * pointed fd, and prepare to watch for it.
- */
-static void prepare_pipe(struct job_pipe *, int *);
-
-/* close_job_pipes(j):
- * handle final output from job, and close pipes properly
- */
-static void close_job_pipes(Job *);
-
-
-static void handle_all_jobs_output(void);
-
-/* handle_job_output(job, n, finish):
- * n = 0 or 1 (stdout/stderr), set finish to retrieve everything.
- */
-static void handle_job_output(Job *, int, bool);
-
-static void print_partial_buffer(struct job_pipe *, Job *, FILE *, size_t);
-static void print_partial_buffer_and_shift(struct job_pipe *, Job *, FILE *,
- size_t);
-static bool print_complete_lines(struct job_pipe *, Job *, FILE *, size_t);
-
-
-static void register_error(int, int, Job *);
static void loop_handle_running_jobs(void);
-static void Job_CatchChildren(void);
-
-static void
-register_error(int reason, int code, Job *job)
-{
- struct error_info *p;
-
- errors++;
- p = emalloc(sizeof(struct error_info));
- p->reason = reason;
- p->code = code;
- p->n = job->node;
- Lst_AtEnd(&errorsList, p);
-}
void
print_errors()
{
LstNode ln;
- struct error_info *p;
- const char *type;
- for (ln = Lst_First(&errorsList); ln != NULL; ln = Lst_Adv(ln)) {
- p = (struct error_info *)Lst_Datum(ln);
- switch(p->reason) {
- case JOB_EXITED:
+ for (ln = Lst_First(&errorJobs); ln != NULL; ln = Lst_Adv(ln)) {
+ int code;
+ const char *type;
+
+ Job *j = (Job *)Lst_Datum(ln);
+ if (j->exit_type == JOB_EXIT_BAD)
type = "Exit status";
- break;
- case JOB_SIGNALED:
+ else if (j->exit_type = JOB_SIGNALED)
type = "Received signal";
- break;
- default:
+ else
type = "Should not happen";
- break;
- }
- if (p->n->origin.lineno)
- Error(" %s %d (%s, line %lu of %s)",
- type, p->code, p->n->name, p->n->origin.lineno,
p->n->origin.fname);
- else
- Error(" %s %d (%s)", type, p->code, p->n->name);
+ fprintf(stderr, " %s %d (", type, j->code);
+ fprintf(stderr, "line %lu of %s, ",
+ j->location->lineno, j->location->fname);
+ fprintf(stderr, "target %s", j->node->name);
+ fprintf(stderr, ", line %lu", j->node->origin.lineno);
+ if (j->node->origin.fname != j->location->fname)
+ fprintf(stderr, " of %s", j->node->origin.fname);
+ if (j->flags & JOB_SILENT)
+ fprintf(stderr, ": %s", j->cmd);
+ fprintf(stderr, ")\n");
}
}
+void
+handle_siginfo()
+{
+ LstNode ln;
+ Job *job;
+ bool first = true;
+
+ got_SIGINFO = 0;
+ fprintf(stderr, "%s in %s: ", Var_Value("MAKE"), Var_Value(".CURDIR"));
+ for (ln = Lst_First(&runningJobs); ln != NULL; ln = Lst_Adv(ln)) {
+ job = (Job *)Lst_Datum(ln);
+ if (!first)
+ fprintf(stderr, ",");
+ first = false;
+ fprintf(stderr, job->node->name);
+ }
+ fprintf(stderr, first ? "nothing running\n" : "\n");
+}
+
static void
banner(Job *job, FILE *out)
{
@@ -283,13 +195,13 @@ banner(Job *job, FILE *out)
}
}
-volatile sig_atomic_t got_SIGTSTP, got_SIGTTOU, got_SIGTTIN, got_SIGWINCH,
- got_SIGCONT;
static void
handle_all_signals()
{
- while (got_signal) {
- got_signal = 0;
+ if (got_SIGINFO)
+ handle_siginfo();
+ while (got_fatal) {
+ got_fatal = 0;
if (got_SIGINT) {
got_SIGINT=0;
@@ -307,89 +219,13 @@ handle_all_signals()
got_SIGTERM=0;
handle_signal(SIGTERM);
}
- if (got_SIGTSTP) {
- got_SIGTSTP=0;
- signal(SIGTSTP, parallel_handler);
- }
- if (got_SIGTTOU) {
- got_SIGTTOU=0;
- signal(SIGTTOU, parallel_handler);
- }
- if (got_SIGTTIN) {
- got_SIGTTIN=0;
- signal(SIGTTIN, parallel_handler);
- }
- if (got_SIGWINCH) {
- got_SIGWINCH=0;
- signal(SIGWINCH, parallel_handler);
- }
- if (got_SIGCONT) {
- got_SIGCONT = 0;
- signal(SIGCONT, parallel_handler);
- }
}
}
-/* this is safe from interrupts, actually */
-void
-parallel_handler(int signo)
-{
- int save_errno = errno;
- LstNode ln;
- for (ln = Lst_First(&job_pids); ln != NULL; ln = Lst_Adv(ln)) {
- struct job_pid *p = Lst_Datum(ln);
- killpg(p->pid, signo);
- }
- errno = save_errno;
-
- switch(signo) {
- case SIGINT:
- got_SIGINT++;
- got_signal = 1;
- return;
- case SIGHUP:
- got_SIGHUP++;
- got_signal = 1;
- return;
- case SIGQUIT:
- got_SIGQUIT++;
- got_signal = 1;
- return;
- case SIGTERM:
- got_SIGTERM++;
- got_signal = 1;
- return;
- case SIGTSTP:
- got_SIGTSTP++;
- got_signal = 1;
- break;
- case SIGTTOU:
- got_SIGTTOU++;
- got_signal = 1;
- break;
- case SIGTTIN:
- got_SIGTTIN++;
- got_signal = 1;
- break;
- case SIGWINCH:
- got_SIGWINCH++;
- got_signal = 1;
- break;
- case SIGCONT:
- got_SIGCONT++;
- got_signal = 1;
- break;
- }
- (void)killpg(getpid(), signo);
-
- (void)signal(signo, SIG_DFL);
- errno = save_errno;
-}
-
/*-
*-----------------------------------------------------------------------
* handle_signal --
- * handle a signal for ourselves
+ * handle a signal in the main loop
*
*-----------------------------------------------------------------------
*/
@@ -413,7 +249,7 @@ handle_signal(int signo)
JobInterrupt(false, signo);
if (signo == SIGQUIT)
- Finish(0);
+ Finish();
}
/*-
@@ -447,18 +283,6 @@ debug_printf(const char *fmt, ...)
}
}
-static void
-close_job_pipes(Job *job)
-{
- int i;
-
- for (i = 1; i >= 0; i--) {
- FD_CLR(job->in[i].fd, output_mask);
- handle_job_output(job, i, true);
- (void)close(job->in[i].fd);
- }
-}
-
/*-
*-----------------------------------------------------------------------
* process_job_status --
@@ -478,236 +302,27 @@ close_job_pipes(Job *job)
/*ARGSUSED*/
static void
-process_job_status(Job *job, int status)
+process_job_status(Job *job, bool okay)
{
- int reason, code;
- bool done;
-
- debug_printf("Process %ld (%s) exited with status %d.\n",
- (long)job->pid, job->node->name, status);
- /* parse status */
- if (WIFEXITED(status)) {
- reason = JOB_EXITED;
- code = WEXITSTATUS(status);
- } else if (WIFSIGNALED(status)) {
- reason = JOB_SIGNALED;
- code = WTERMSIG(status);
- } else {
- /* can't happen, set things to be bad. */
- reason = UNKNOWN;
- code = status;
- }
-
- if ((reason == JOB_EXITED &&
- code != 0 && !(job->node->type & OP_IGNORE)) ||
- reason == JOB_SIGNALED) {
- /*
- * If it exited non-zero and either we're doing things our
- * way or we're not ignoring errors, the job is finished.
- * Similarly, if the shell died because of a signal
- * the job is also finished. In these
- * cases, finish out the job's output before printing the exit
- * status...
- */
- close_job_pipes(job);
- done = true;
- } else if (reason == JOB_EXITED) {
- /*
- * Deal with ignored errors. We need to print a message telling
- * of the ignored error as well as setting status.w_status to 0
- * so the next command gets run. To do this, we set done to be
- * true and the job exited non-zero.
- */
- done = code != 0;
- close_job_pipes(job);
- } else {
- /*
- * No need to close things down or anything.
- */
- done = false;
- }
-
- if (done || DEBUG(JOB)) {
- if (reason == JOB_EXITED) {
- debug_printf("Process %ld (%s) exited.\n",
- (long)job->pid, job->node->name);
- if (code != 0) {
- banner(job, stdout);
- (void)fprintf(stdout, "*** Error code %d %s\n",
- code,
- (job->node->type & OP_IGNORE) ?
- "(ignored)" : "");
-
- if (job->node->type & OP_IGNORE) {
- reason = JOB_EXITED;
- code = 0;
- }
- } else if (DEBUG(JOB)) {
- (void)fprintf(stdout,
- "*** %ld (%s) Completed successfully\n",
- (long)job->pid, job->node->name);
- }
- } else {
- banner(job, stdout);
- (void)fprintf(stdout, "*** Signal %d\n", code);
- }
-
- (void)fflush(stdout);
- }
-
- done = true;
-
- if (done &&
+ if (okay &&
aborting != ABORT_ERROR &&
- aborting != ABORT_INTERRUPT &&
- reason == JOB_EXITED && code == 0) {
+ aborting != ABORT_INTERRUPT) {
/* 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 the parents. */
job->node->built_status = MADE;
Make_Update(job->node);
- } else if (!(reason == JOB_EXITED && code == 0)) {
- register_error(reason, code, job);
+ free(job);
+ } else if (!okay) {
+ Lst_AtEnd(&errorJobs, job);
}
- free(job);
- if (errors && !keepgoing &&
+ if (!Lst_IsEmpty(&errorJobs) && !keepgoing &&
aborting != ABORT_INTERRUPT)
aborting = ABORT_ERROR;
if (aborting == ABORT_ERROR && Job_Empty())
- Finish(errors);
-}
-
-static void
-prepare_pipe(struct job_pipe *p, int *fd)
-{
- p->pos = 0;
- (void)fcntl(fd[0], F_SETFD, FD_CLOEXEC);
- p->fd = fd[0];
- close(fd[1]);
-
- if (output_mask == NULL || p->fd > largest_fd) {
- int fdn, ofdn;
-
- fdn = howmany(p->fd+1, NFDBITS);
- ofdn = howmany(largest_fd+1, NFDBITS);
-
- if (fdn != ofdn) {
- output_mask = emult_realloc(output_mask, fdn,
- sizeof(fd_mask));
- memset(((char *)output_mask) + ofdn * sizeof(fd_mask),
- 0, (fdn-ofdn) * sizeof(fd_mask));
- actual_mask = emult_realloc(actual_mask, fdn,
- sizeof(fd_mask));
- mask_size = fdn * sizeof(fd_mask);
- }
- largest_fd = p->fd;
- }
- fcntl(p->fd, F_SETFL, O_NONBLOCK);
- FD_SET(p->fd, output_mask);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * JobExec --
- * Execute the shell for the given job. Called from JobStart
- *
- * Side Effects:
- * A shell is executed, outputs is altered and the Job structure added
- * to the job table.
- *-----------------------------------------------------------------------
- */
-static void
-JobExec(Job *job)
-{
- pid_t cpid; /* ID of new child */
- struct job_pid *p;
- int fds[4];
- int *fdout = fds;
- int *fderr = fds+2;
- int i;
-
- banner(job, stdout);
-
- setup_engine(1);
-
- /* Create the pipe by which we'll get the shell's output.
- */
- if (pipe(fdout) == -1)
- Punt("Cannot create pipe: %s", strerror(errno));
-
- if (pipe(fderr) == -1)
- Punt("Cannot create pipe: %s", strerror(errno));
-
- block_signals();
- if ((cpid = fork()) == -1) {
- Punt("Cannot fork");
- unblock_signals();
- } else if (cpid == 0) {
- supervise_jobs = false;
- /* standard pipe code to route stdout and stderr */
- close(fdout[0]);
- if (dup2(fdout[1], 1) == -1)
- Punt("Cannot dup2(outPipe): %s", strerror(errno));
- if (fdout[1] != 1)
- close(fdout[1]);
- close(fderr[0]);
- if (dup2(fderr[1], 2) == -1)
- Punt("Cannot dup2(errPipe): %s", strerror(errno));
- if (fderr[1] != 2)
- close(fderr[1]);
-
- /*
- * We want to switch the child into a different process family
- * so we can kill it and all its descendants in one fell swoop,
- * by killing its process family, but not commit suicide.
- */
- (void)setpgid(0, getpid());
-
- if (random_delay)
- if (!(nJobs == 1 && no_jobs_left()))
- usleep(random() % random_delay);
-
- setup_all_signals(SigHandler, SIG_DFL);
- unblock_signals();
- /* this exits directly */
- run_gnode_parallel(job->node);
- /*NOTREACHED*/
- } else {
- supervise_jobs = true;
- job->pid = cpid;
-
- /* we set the current position in the buffers to the beginning
- * and mark another stream to watch in the outputs mask
- */
- for (i = 0; i < 2; i++)
- prepare_pipe(&job->in[i], fds+2*i);
- }
- /*
- * Now the job is actually running, add it to the table.
- */
- nJobs++;
- Lst_AtEnd(&runningJobs, job);
- if (job->flags & JOB_IS_EXPENSIVE)
- expensive_job = true;
- p = emalloc(sizeof(struct job_pid));
- p->pid = cpid;
- Lst_AtEnd(&job_pids, p);
- job->p = Lst_Last(&job_pids);
-
- unblock_signals();
- if (DEBUG(JOB)) {
- LstNode ln;
-
- (void)fprintf(stdout, "Running %ld (%s)\n", (long)cpid,
- job->node->name);
- for (ln = Lst_First(&job->node->commands); ln != NULL ;
- ln = Lst_Adv(ln))
- fprintf(stdout, "\t%s\n", (char *)Lst_Datum(ln));
- (void)fflush(stdout);
- }
-
+ Finish();
}
static bool
@@ -757,16 +372,6 @@ expensive_command(const char *s)
return false;
}
-static bool
-expensive_commands(Lst l)
-{
- LstNode ln;
- for (ln = Lst_First(l); ln != NULL; ln = Lst_Adv(ln))
- if (expensive_command(Lst_Datum(ln)))
- return true;
- return false;
-}
-
static Job *
prepare_job(GNode *gn, int flags)
{
@@ -778,9 +383,6 @@ prepare_job(GNode *gn, int flags)
* to migrate to the node
*/
cmdsOK = Job_CheckCommands(gn);
- expand_commands(gn);
- if (fatal_errors)
- Punt("can't continue");
if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) {
/*
@@ -830,8 +432,7 @@ prepare_job(GNode *gn, int flags)
if (job == NULL)
Punt("JobStart out of memory");
- job->node = gn;
-
+ job_attach_node(job, gn);
/*
* Set the initial value of the flags for this job based on the
* global ones and the node's attributes... Any flags supplied
@@ -839,16 +440,31 @@ prepare_job(GNode *gn, int flags)
*/
job->flags = flags;
- if (gn->type & OP_CHEAP)
- return job;
- if ((gn->type & OP_EXPENSIVE) ||
- expensive_commands(&gn->expanded))
- job->flags |= JOB_IS_EXPENSIVE;
-
return job;
}
}
+static bool
+advance_job(Job *job)
+{
+ bool finished;
+
+ banner(job, stdout);
+ finished = job_run_next(job);
+ if (!finished) {
+ if (job->node->type & OP_CHEAP)
+ ;
+ else
+ if ((job->node->type & OP_EXPENSIVE) ||
expensive_command(job->cmd)) {
+ job->flags |= JOB_IS_EXPENSIVE;
+ expensive_job = true;
+ }
+ Lst_AtEnd(&runningJobs, job);
+ }
+
+ return finished;
+}
+
/*-
*-----------------------------------------------------------------------
* JobStart --
@@ -866,227 +482,137 @@ JobStart(GNode *gn, /* target to
* e.g. JOB_IS_SPECIAL */
{
Job *job;
+ bool finished;
+
job = prepare_job(gn, flags);
if (!job)
return;
- JobExec(job);
-}
-
-/* Helper functions for JobDoOutput */
-
-
-/* output debugging banner and print characters from 0 to endpos */
-static void
-print_partial_buffer(struct job_pipe *p, Job *job, FILE *out, size_t endPos)
-{
- size_t i;
+ nJobs++;
+ if (expensive_job) {
+ Lst_AtEnd(&heldJobs, job);
+ return;
+ }
+ finished = advance_job(job);
+ if (finished) {
+ remove_job(job, true);
+ } else if (DEBUG(JOB)) {
+ LstNode ln;
- banner(job, out);
- job->flags |= JOB_DIDOUTPUT;
- for (i = 0; i < endPos; i++)
- putc(p->buffer[i], out);
+ (void)fprintf(stdout, "Running %ld (%s)\n",
+ (long)job->pid, job->node->name);
+ for (ln = Lst_First(&job->node->commands); ln != NULL ;
+ ln = Lst_Adv(ln)) {
+ struct command *cmd = Lst_Datum(ln);
+ fprintf(stdout, "\t%s\n", cmd->string);
+ }
+ (void)fflush(stdout);
+ }
}
-/* print partial buffer and shift remaining contents */
static void
-print_partial_buffer_and_shift(struct job_pipe *p, Job *job, FILE *out,
- size_t endPos)
+continue_job(Job *job)
{
- size_t i;
+ bool finished;
- print_partial_buffer(p, job, out, endPos);
-
- for (i = endPos; i < p->pos; i++)
- p->buffer[i-endPos] = p->buffer[i];
- p->pos -= endPos;
+ finished = advance_job(job);
+ if (finished) {
+ remove_job(job, true);
+ }
}
-/* print complete lines, looking back to the limit position
- * (stuff before limit was already scanned).
- * returns true if something was printed.
- */
-static bool
-print_complete_lines(struct job_pipe *p, Job *job, FILE *out, size_t limit)
-{
- size_t i;
+/* Helper functions for JobDoOutput */
- for (i = p->pos; i > limit; i--) {
- if (p->buffer[i-1] == '\n') {
- print_partial_buffer_and_shift(p, job, out, i);
- return true;
- }
- }
- return false;
-}
-/*-
- *-----------------------------------------------------------------------
- * handle_pipe --
- * This functions is called whenever there is something to read on the
- * pipe. We collect more output from the given job and store it in the
- * job's outBuf. If this makes up lines, we print it tagged by the job's
- * identifier, as necessary.
- *
- * Side Effects:
- * curPos may be shifted as may the contents of outBuf.
- *-----------------------------------------------------------------------
- */
static void
-handle_pipe(struct job_pipe *p,
- Job *job, FILE *out, bool finish)
+may_remove_job(Job *job)
{
- int nr; /* number of bytes read */
- int oldpos; /* optimization */
-
- /* want to get everything ? -> we block */
- if (finish)
- fcntl(p->fd, F_SETFL, 0);
-
- do {
- nr = read(p->fd, &p->buffer[p->pos],
- JOB_BUFSIZE - p->pos);
- if (nr == -1) {
- if (errno == EAGAIN)
- break;
- if (DEBUG(JOB)) {
- perror("JobDoOutput(piperead)");
- }
- }
- oldpos = p->pos;
- p->pos += nr;
- if (!print_complete_lines(p, job, out, oldpos))
- if (p->pos == JOB_BUFSIZE) {
- print_partial_buffer(p, job, out, p->pos);
- p->pos = 0;
- }
- } while (nr != 0);
+ bool okay;
+ if (job->flags & JOB_IS_EXPENSIVE) {
+ expensive_job = false;
+ job->flags &= ~JOB_IS_EXPENSIVE;
+ }
- /* at end of file, we print whatever is left */
- if (nr == 0) {
- print_partial_buffer(p, job, out, p->pos);
- if (p->pos > 0 && p->buffer[p->pos - 1] != '\n')
- putchar('\n');
- p->pos = 0;
+ okay = handle_job_child(job);
+ if (!okay || job->next_cmd == NULL) {
+ remove_job(job, okay);
+ } else {
+ if (expensive_job)
+ Lst_AtEnd(&heldJobs, job);
+ else
+ continue_job(job);
}
}
static void
-handle_job_output(Job *job, int i, bool finish)
+remove_job(Job *job, bool okay)
{
- handle_pipe(&job->in[i], job, i == 0 ? stdout : stderr, finish);
+ nJobs--;
+ process_job_status(job, okay);
+ while (!expensive_job) {
+ if ((job = Lst_DeQueue(&heldJobs)) != NULL)
+ continue_job(job);
+ else
+ break;
+ }
}
-static void
-remove_job(LstNode ln, int status)
+static bool
+handle_job_child(Job *job)
{
- Job *job;
-
- job = (Job *)Lst_Datum(ln);
- Lst_Remove(&runningJobs, ln);
- block_signals();
- free(Lst_Datum(job->p));
- Lst_Remove(&job_pids, job->p);
- unblock_signals();
- nJobs--;
- if (job->flags & JOB_IS_EXPENSIVE)
- expensive_job = false;
- process_job_status(job, status);
+ free(job->cmd);
+ return job->exit_type == JOB_EXIT_OKAY;
}
-
/*-
*-----------------------------------------------------------------------
- * Job_CatchChildren --
- * Handle the exit of a child. Called by handle_running_jobs
+ * handle_running_jobs(wait) --
+ * Handle the exit of children
+ * only wait for the first job, unless wait is true
*
* Side Effects:
- * The job descriptor is removed from the list of children.
+ * Jobs may be removed from the list
*
- * Notes:
- * We do waits, blocking or not, according to the wisdom of our
- * caller, until there are no more children to report. For each
- * job, call process_job_status to finish things off.
*-----------------------------------------------------------------------
*/
void
-Job_CatchChildren()
+handle_running_jobs(bool wait)
{
pid_t pid; /* pid of dead child */
LstNode jnode; /* list element for finding job */
int status; /* Exit/termination status */
+ int wflag = 0;
- /*
- * Don't even bother if we know there's no one around.
- */
- if (nJobs == 0)
- return;
- while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
+ while (1) {
+ if (nJobs == 0)
+ return;
+ pid = waitpid(WAIT_ANY, &status, wflag);
handle_all_signals();
+ if (pid <= 0)
+ break;
+ if (!wait)
+ wflag = WNOHANG;
jnode = Lst_Find(&runningJobs, JobCmpPid, &pid);
if (jnode == NULL) {
- Error("Child (%ld) not in table?", (long)pid);
+ Punt("Child (%ld) not in table?", (long)pid);
} else {
- remove_job(jnode, status);
- }
- }
-}
-
-void
-handle_all_jobs_output(void)
-{
- int nfds;
- struct timeval timeout;
- LstNode ln, ln2;
- Job *job;
- int i;
- int status;
-
- /* no jobs */
- if (Lst_IsEmpty(&runningJobs))
- return;
-
- (void)fflush(stdout);
-
- memcpy(actual_mask, output_mask, mask_size);
- timeout.tv_sec = SEL_SEC;
- timeout.tv_usec = SEL_USEC;
-
- nfds = select(largest_fd+1, actual_mask, NULL, NULL, &timeout);
- handle_all_signals();
- for (ln = Lst_First(&runningJobs); nfds && ln != NULL; ln = ln2) {
- ln2 = Lst_Adv(ln);
- job = (Job *)Lst_Datum(ln);
- job->flags &= ~JOB_DIDOUTPUT;
- for (i = 1; i >= 0; i--) {
- if (FD_ISSET(job->in[i].fd, actual_mask)) {
- nfds--;
- handle_job_output(job, i, false);
- }
- }
- if (job->flags & JOB_DIDOUTPUT) {
- if (waitpid(job->pid, &status, WNOHANG) == job->pid) {
- remove_job(ln, status);
- } else {
- Lst_Requeue(&runningJobs, ln);
- }
+ Job *job = (Job *)Lst_Datum(jnode);
+ job_handle_status(job, status);
+ /* XXX may_remove_job should know whether we
+ * actually need to remove it
+ */
+ Lst_Remove(&runningJobs, jnode);
+ may_remove_job(job);
}
}
}
-void
-handle_running_jobs()
-{
- handle_all_jobs_output();
- Job_CatchChildren();
-}
-
static void
loop_handle_running_jobs()
{
while (nJobs)
- handle_running_jobs();
+ handle_running_jobs(true);
}
/*-
*-----------------------------------------------------------------------
@@ -1105,18 +631,6 @@ Job_Make(GNode *gn)
}
-static void
-block_signals()
-{
- sigprocmask(SIG_BLOCK, &set, &oset);
-}
-
-static void
-unblock_signals()
-{
- sigprocmask(SIG_SETMASK, &oset, NULL);
-}
-
/*-
*-----------------------------------------------------------------------
* Job_Init --
@@ -1130,18 +644,11 @@ void
Job_Init(int maxproc)
{
Static_Lst_Init(&runningJobs);
- Static_Lst_Init(&errorsList);
+ Static_Lst_Init(&heldJobs);
+ Static_Lst_Init(&errorJobs);
maxJobs = maxproc;
+
nJobs = 0;
- errors = 0;
- sigemptyset(&set);
- sigaddset(&set, SIGINT);
- sigaddset(&set, SIGHUP);
- sigaddset(&set, SIGQUIT);
- sigaddset(&set, SIGTERM);
- sigaddset(&set, SIGTSTP);
- sigaddset(&set, SIGTTOU);
- sigaddset(&set, SIGTTIN);
aborting = 0;
@@ -1153,6 +660,15 @@ Job_Init(int maxproc)
}
}
+void
+register_job(Job *job)
+{
+ Static_Lst_Init(&runningJobs);
+ Lst_AtEnd(&runningJobs, job);
+ /* XXX: if we get to run print_errors, then it errored out */
+ Lst_AtEnd(&errorJobs, job);
+}
+
static bool
Job_Full()
{
@@ -1172,7 +688,7 @@ Job_Full()
bool
can_start_job(void)
{
- if (Job_Full() || expensive_job)
+ if (Job_Full())
return false;
else
return true;
@@ -1229,7 +745,7 @@ JobInterrupt(bool runINTERRUPT, /* true
if (job->pid) {
debug_printf("JobInterrupt passing signal to "
"child %ld.\n", (long)job->pid);
- killpg(job->pid, signo);
+ kill(job->pid, signo);
}
}
@@ -1251,13 +767,15 @@ JobInterrupt(bool runINTERRUPT, /* true
* attached to the .END target.
*
* Results:
- * Number of errors reported.
+ * Errors have been reported.
*
*-----------------------------------------------------------------------
*/
-int
+bool
Job_Finish(void)
{
+ bool errors = !Lst_IsEmpty(&errorJobs);
+
if ((end_node->type & OP_DUMMY) == 0) {
if (errors) {
Error("Errors reported so .END ignored");
Index: job.h
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/job.h,v
retrieving revision 1.25
diff -u -p -r1.25 job.h
--- job.h 19 Jul 2010 19:46:44 -0000 1.25
+++ job.h 9 Sep 2012 10:52:49 -0000
@@ -58,7 +58,11 @@ extern void Job_End(void);
extern void Job_Wait(void);
extern void Job_AbortAll(void);
extern void print_errors(void);
-extern void handle_running_jobs(void);
+extern void handle_running_jobs(bool);
extern void parallel_handler(int);
+
+struct Job_;
+extern void register_job(struct Job_ *);
+extern void handle_siginfo();
#endif /* _JOB_H_ */
Index: make.c
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/make.c,v
retrieving revision 1.63
diff -u -p -r1.63 make.c
--- make.c 21 Apr 2012 04:35:32 -0000 1.63
+++ make.c 4 Sep 2012 16:43:54 -0000
@@ -558,7 +558,7 @@ Make_Run(Lst targs) /* the initial list
* the keepgoing flag was given.
*/
while (!Job_Empty()) {
- handle_running_jobs();
+ handle_running_jobs(false);
(void)MakeStartJobs();
}
Index: parse.c
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/parse.c,v
retrieving revision 1.104
diff -u -p -r1.104 parse.c
--- parse.c 20 Apr 2012 13:28:11 -0000 1.104
+++ parse.c 9 Sep 2012 10:25:00 -0000
@@ -547,8 +547,14 @@ add_target_node(const char *line, const
gn->type &= ~OP_DUMMY;
}
- if (gn != NULL)
- Array_AtEnd(>argets, gn);
+ /* try to find a proper location for a target in a file, by
+ * filling it repeatedly until the target has commands..
+ * This is not perfect for .USE targets...
+ */
+ if ((gn->type & OP_HAS_COMMANDS) == 0)
+ Parse_FillLocation(&gn->origin);
+
+ Array_AtEnd(>argets, gn);
}
static void
@@ -1040,11 +1046,8 @@ ParseAddCmd(void *gnp, void *cmd)
{
GNode *gn = (GNode *)gnp;
/* if target already supplied, ignore commands */
- if (!(gn->type & OP_HAS_COMMANDS)) {
+ if (!(gn->type & OP_HAS_COMMANDS))
Lst_AtEnd(&gn->commands, cmd);
- if (!gn->origin.lineno)
- Parse_FillLocation(&gn->origin);
- }
}
/*-
@@ -1441,7 +1444,14 @@ parse_commands(struct growableArray *tar
{
/* add the command to the list of
* commands of all targets in the dependency spec */
- char *cmd = estrdup(line);
+
+ struct command *cmd;
+ size_t len = strlen(line);
+
+ cmd = emalloc(sizeof(struct command) + len);
+ memcpy(&cmd->string, line, len+1);
+ Parse_FillLocation(&cmd->location);
+
Array_ForEach(targets, ParseAddCmd, cmd);
#ifdef CLEANUP
@@ -1486,13 +1496,13 @@ parse_target_line(struct growableArray *
size_t pos;
char *end;
char *cp;
- char *dep;
+ char *cmd;
/* let's start a new set of commands */
Array_Reset(targets);
/* XXX this is a dirty heuristic to handle target: dep ; commands */
- dep = NULL;
+ cmd = NULL;
/* First we need to find eventual dependencies */
pos = strcspn(stripped, ":!");
/* go over :!, and find ; */
@@ -1501,9 +1511,9 @@ parse_target_line(struct growableArray *
if (line != stripped)
/* find matching ; in original... The
* original might be slightly longer. */
- dep = strchr(line+(end-stripped), ';');
+ cmd = strchr(line+(end-stripped), ';');
else
- dep = end;
+ cmd = end;
/* kill end of line. */
*end = '\0';
}
@@ -1514,13 +1524,13 @@ parse_target_line(struct growableArray *
ParseDoDependency(cp);
free(cp);
- /* Parse dependency if it's not empty. */
- if (dep != NULL) {
+ /* Parse command if it's not empty. */
+ if (cmd != NULL) {
do {
- dep++;
- } while (isspace(*dep));
- if (*dep != '\0')
- parse_commands(targets, dep);
+ cmd++;
+ } while (isspace(*cmd));
+ if (*cmd != '\0')
+ parse_commands(targets, cmd);
}
}
Index: targ.c
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/targ.c,v
retrieving revision 1.65
diff -u -p -r1.65 targ.c
--- targ.c 1 Sep 2012 16:44:25 -0000 1.65
+++ targ.c 4 Sep 2012 16:43:54 -0000
@@ -195,7 +195,6 @@ Targ_NewGNi(const char *name, const char
gn->origin.fname = NULL;
gn->impliedsrc = NULL;
Lst_Init(&gn->commands);
- Lst_Init(&gn->expanded);
gn->suffix = NULL;
gn->next = NULL;
gn->basename = NULL;
Index: varmodifiers.c
===================================================================
RCS file: /home/openbsd/cvs/src/usr.bin/make/varmodifiers.c,v
retrieving revision 1.30
diff -u -p -r1.30 varmodifiers.c
--- varmodifiers.c 16 Aug 2011 14:18:25 -0000 1.30
+++ varmodifiers.c 5 Sep 2012 10:36:05 -0000
@@ -1489,7 +1489,7 @@ VarModifiers_Apply(char *str, const stru
if (mod->freearg != NULL)
mod->freearg(arg);
} else {
- Error("Bad modifier: %s\n", tstr);
+ Error("Bad modifier: %s", tstr);
/* Try skipping to end of var... */
for (tstr++; *tstr != endc && *tstr != '\0';)
tstr++;