summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>2012-09-21 07:55:21 +0000
committerMarc Espie <espie@cvs.openbsd.org>2012-09-21 07:55:21 +0000
commit10f160f479b38433f4047a2f25d5aa8f07bc60e6 (patch)
tree3c0207b7741e4887482742726e4bc1238f03de6b /usr.bin
parent844d86d53b6754d7a21150eb94823f41ce76becc (diff)
major overhaul of the way make handle jobs, inspired by dpb:
instead of forking a "job" per target, and having that job further fork separate commands, have make maintain a list of jobs, indexed by pid of currently running commands, and handle process termination continuation-style. This has lots of benefits: - make is responsible for most printing, so we no longer need pipes nor job control: make -j jobs see the tty. - no more special-casing for jobs that don't really execute anything. - unify code for make -jn and make -B, including signal handlers and job waiting. So make -n, make -q, +cmd now run commands in the same way in all cases. - unified more accurate error-reporting, as make knows precisely which command failed. Commands are tagged with their lines, and we display failing commands in silent mode. - fine-grained "expensive" command handling (recursion limiter). Do it per-command instead of per-target. Moreover, signal response is now simpler, as we just block the signals in a small critical sections, test for events, and sigpause (thanks a lot to guenther@ and millert@), so running make is now almost always paused without any busy-waiting. Thanks to everyone who tested and gave input.
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/make/Makefile5
-rw-r--r--usr.bin/make/buf.c21
-rw-r--r--usr.bin/make/buf.h5
-rw-r--r--usr.bin/make/compat.c27
-rw-r--r--usr.bin/make/defines.h12
-rw-r--r--usr.bin/make/dump.c36
-rw-r--r--usr.bin/make/dump.h34
-rw-r--r--usr.bin/make/engine.c449
-rw-r--r--usr.bin/make/engine.h62
-rw-r--r--usr.bin/make/error.c30
-rw-r--r--usr.bin/make/error.h4
-rw-r--r--usr.bin/make/gnode.h9
-rw-r--r--usr.bin/make/job.c1367
-rw-r--r--usr.bin/make/job.h10
-rw-r--r--usr.bin/make/lowparse.c24
-rw-r--r--usr.bin/make/lowparse.h4
-rw-r--r--usr.bin/make/main.c55
-rw-r--r--usr.bin/make/make.125
-rw-r--r--usr.bin/make/parse.c46
-rw-r--r--usr.bin/make/targ.c8
-rw-r--r--usr.bin/make/varmodifiers.c4
21 files changed, 918 insertions, 1319 deletions
diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile
index 019dfa3d2c3..7a289ba4029 100644
--- a/usr.bin/make/Makefile
+++ b/usr.bin/make/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.52 2010/10/17 22:54:37 schwarze Exp $
+# $OpenBSD: Makefile,v 1.53 2012/09/21 07:55:20 espie Exp $
PROG= make
CFLAGS+= -I${.OBJDIR} -I${.CURDIR}
@@ -15,7 +15,8 @@ CDEFS+=-DHAS_EXTENDED_GETCWD
CFLAGS+=${CDEFS}
HOSTCFLAGS+=${CDEFS}
-SRCS= arch.c buf.c cmd_exec.c compat.c cond.c dir.c direxpand.c engine.c \
+SRCS= arch.c buf.c cmd_exec.c compat.c cond.c dir.c direxpand.c dump.c \
+ engine.c \
error.c for.c init.c job.c lowparse.c main.c make.c memory.c parse.c \
parsevar.c str.c stats.c suff.c targ.c targequiv.c timestamp.c \
var.c varmodifiers.c varname.c
diff --git a/usr.bin/make/buf.c b/usr.bin/make/buf.c
index 92a9451caf9..e31b13e0068 100644
--- a/usr.bin/make/buf.c
+++ b/usr.bin/make/buf.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: buf.c,v 1.23 2010/07/19 19:46:43 espie Exp $ */
+/* $OpenBSD: buf.c,v 1.24 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: buf.c,v 1.9 1996/12/31 17:53:21 christos Exp $ */
/*
@@ -69,6 +69,8 @@
#include <ctype.h>
#include <stddef.h>
#include <string.h>
+#include <stdio.h>
+#include <stdarg.h>
#include "config.h"
#include "defines.h"
#include "buf.h"
@@ -126,6 +128,23 @@ Buf_AddChars(Buffer bp, size_t numBytes, const char *bytesPtr)
bp->inPtr += numBytes;
}
+void
+Buf_printf(Buffer bp, const char *fmt, ...)
+{
+ va_list va;
+ int n;
+ va_start(va, fmt);
+ n = vsnprintf(bp->inPtr, bp->endPtr - bp->inPtr, fmt, va);
+ va_end(va);
+ if (n > bp->endPtr - bp->inPtr) {
+ va_list vb;
+ BufExpand(bp, n);
+ va_start(vb, fmt);
+ (void)vsnprintf(bp->inPtr, bp->endPtr - bp->inPtr, fmt, vb);
+ va_end(vb);
+ }
+ bp->inPtr += n;
+}
void
Buf_Init(Buffer bp, size_t size)
diff --git a/usr.bin/make/buf.h b/usr.bin/make/buf.h
index 980d15a9546..4806b434b51 100644
--- a/usr.bin/make/buf.h
+++ b/usr.bin/make/buf.h
@@ -1,7 +1,7 @@
#ifndef _BUF_H
#define _BUF_H
-/* $OpenBSD: buf.h,v 1.19 2010/07/19 19:46:43 espie Exp $ */
+/* $OpenBSD: buf.h,v 1.20 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: buf.h,v 1.7 1996/12/31 17:53:22 christos Exp $ */
/*
@@ -133,4 +133,7 @@ do { \
* Removes non-backslashed spaces at the end of a buffer. */
extern void Buf_KillTrailingSpaces(Buffer);
+extern void Buf_printf(Buffer, const char *, ...);
+#define Buf_puts(b, s) Buf_AddChars((b), strlen(s), (s))
+
#endif /* _BUF_H */
diff --git a/usr.bin/make/compat.c b/usr.bin/make/compat.c
index 80f3f5641c4..4814e32b2c1 100644
--- a/usr.bin/make/compat.c
+++ b/usr.bin/make/compat.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: compat.c,v 1.76 2012/03/22 13:47:12 espie Exp $ */
+/* $OpenBSD: compat.c,v 1.77 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: compat.c,v 1.14 1996/11/06 17:59:01 christos Exp $ */
/*
@@ -43,6 +43,7 @@
#include "defines.h"
#include "dir.h"
#include "engine.h"
+#include "job.h"
#include "compat.h"
#include "suff.h"
#include "var.h"
@@ -161,7 +162,7 @@ CompatMake(void *gnp, /* The node to make */
* the $> variable using Make_DoAllVar().
*/
Make_DoAllVar(sib);
- cmdsOk = Job_CheckCommands(sib);
+ cmdsOk = node_find_valid_commands(sib);
if (cmdsOk || (gn->type & OP_OPTIONAL))
break;
@@ -179,7 +180,7 @@ CompatMake(void *gnp, /* The node to make */
Job_Touch(gn);
}
} else {
- job_failure(gn, Fatal);
+ node_failure(gn);
sib->built_status = ERROR;
}
@@ -228,15 +229,7 @@ CompatMake(void *gnp, /* The node to make */
} else if (keepgoing)
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"));
+ print_errors();
exit(1);
}
} else if (gn->built_status == ERROR)
@@ -272,16 +265,6 @@ Compat_Run(Lst targs) /* List of target nodes to re-create */
GNode *gn = NULL; /* Current root target */
int errors; /* Number of targets not remade due to errors */
- setup_engine(0);
- /* If the user has defined a .BEGIN target, execute the commands
- * attached to it. */
- if (!queryFlag) {
- if (run_gnode(begin_node) == ERROR) {
- printf("\n\nStop.\n");
- exit(1);
- }
- }
-
/* For each entry in the list of targets to create, call CompatMake on
* it to create the thing. CompatMake will leave the 'built_status'
* field of gn in one of several states:
diff --git a/usr.bin/make/defines.h b/usr.bin/make/defines.h
index 267860fc1aa..a0b20860d48 100644
--- a/usr.bin/make/defines.h
+++ b/usr.bin/make/defines.h
@@ -1,7 +1,7 @@
#ifndef DEFINES_H
#define DEFINES_H
-/* $OpenBSD: defines.h,v 1.10 2012/03/22 13:47:12 espie Exp $ */
+/* $OpenBSD: defines.h,v 1.11 2012/09/21 07:55:20 espie Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
@@ -57,6 +57,9 @@ struct Name;
struct ListNode_;
typedef struct ListNode_ *LstNode;
+struct Job_;
+typedef struct Job_ Job;
+
/* some useful defines for gcc */
#ifdef __GNUC__
@@ -91,9 +94,10 @@ extern int debug;
#define DEBUG_VAR 0x0200
#define DEBUG_FOR 0x0400
#define DEBUG_LOUD 0x0800
-#define DEBUG_JOBBANNER 0x1000
-#define DEBUG_PARALLEL 0x2000
-#define DEBUG_NAME_MATCHING 0x4000
+#define DEBUG_PARALLEL 0x1000
+#define DEBUG_NAME_MATCHING 0x2000
+#define DEBUG_QUICKDEATH 0x4000
+#define DEBUG_EXPENSIVE 0x8000
#define CONCAT(a,b) a##b
diff --git a/usr.bin/make/dump.c b/usr.bin/make/dump.c
new file mode 100644
index 00000000000..84a07452e5c
--- /dev/null
+++ b/usr.bin/make/dump.c
@@ -0,0 +1,36 @@
+/* $OpenBSD: dump.c,v 1.1 2012/09/21 07:55:20 espie Exp $ */
+/*
+ * Copyright (c) 2012 Marc Espie.
+ *
+ * Extensive code modifications for the OpenBSD project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#include "defines.h"
+#include "dump.h"
+#include "targ.h"
+
+void
+dump_data(void)
+{
+ Targ_PrintGraph(1);
+}
diff --git a/usr.bin/make/dump.h b/usr.bin/make/dump.h
new file mode 100644
index 00000000000..78531abcd3b
--- /dev/null
+++ b/usr.bin/make/dump.h
@@ -0,0 +1,34 @@
+/* $OpenBSD: dump.h,v 1.1 2012/09/21 07:55:20 espie Exp $ */
+#ifndef _DUMP_H_
+#define _DUMP_H_
+
+/*
+ * Copyright (c) 2012 Marc Espie.
+ *
+ * Extensive code modifications for the OpenBSD project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/* implementation of -p option */
+extern void dump_data(void);
+
+#endif
diff --git a/usr.bin/make/engine.c b/usr.bin/make/engine.c
index 99d8924f91e..2a8a37b20fb 100644
--- a/usr.bin/make/engine.c
+++ b/usr.bin/make/engine.c
@@ -1,4 +1,30 @@
-/* $OpenBSD: engine.c,v 1.32 2012/09/14 14:18:50 espie Exp $ */
+/* $OpenBSD: engine.c,v 1.33 2012/09/21 07:55:20 espie Exp $ */
+/*
+ * Copyright (c) 2012 Marc Espie.
+ *
+ * Extensive code modifications for the OpenBSD project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
* Copyright (c) 1988, 1989 by Adam de Boor
@@ -34,6 +60,7 @@
*/
#include <sys/types.h>
+#include <sys/time.h>
#include <sys/wait.h>
#include <assert.h>
#include <limits.h>
@@ -63,20 +90,16 @@
#include "memory.h"
#include "buf.h"
#include "job.h"
+#include "lowparse.h"
static void MakeTimeStamp(void *, void *);
static int rewrite_time(const char *);
-static void setup_signal(int, psighandler);
static void setup_meta(void);
+static void setup_engine(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 void handle_compat_interrupts(GNode *);
-
bool
-Job_CheckCommands(GNode *gn)
+node_find_valid_commands(GNode *gn)
{
/* Alter our type to tell if errors should be ignored or things
* should not be printed so setup_and_run_command knows what to do.
@@ -120,7 +143,7 @@ Job_CheckCommands(GNode *gn)
}
void
-job_failure(GNode *gn, void (*abortProc)(char *, ...))
+node_failure(GNode *gn)
{
/*
If the -k flag wasn't given, we stop in
@@ -131,18 +154,17 @@ job_failure(GNode *gn, void (*abortProc)(char *, ...))
"make: don't know how to make";
if (gn->type & OP_OPTIONAL) {
- (void)fprintf(stdout, "%s %s(ignored)\n", msg,
- gn->name);
- (void)fflush(stdout);
+ printf("%s %s(ignored)\n", msg, gn->name);
} else if (keepgoing) {
- (void)fprintf(stdout, "%s %s(continuing)\n",
- msg, gn->name);
- (void)fflush(stdout);
+ (void)printf("%s %s(continuing)\n", msg, gn->name);
} else {
- (*abortProc)("%s %s. Stop in %s.", msg,
- gn->name, Var_Value(".CURDIR"));
+ fprintf(stderr, "%s %s\n", msg, gn->name);
+ print_errors();
+ Punt(NULL);
}
+ fflush(stdout);
}
+
/* touch files the hard way, by writing stuff to them */
static int
rewrite_time(const char *name)
@@ -169,6 +191,7 @@ rewrite_time(const char *name)
void
Job_Touch(GNode *gn)
{
+ handle_all_signals();
if (gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_OPTIONAL|OP_PHONY)) {
/*
* .JOIN, .USE, and .OPTIONAL targets are "virtual" targets
@@ -453,59 +476,6 @@ Make_OODate(GNode *gn)
return oodate;
}
-volatile sig_atomic_t got_signal;
-
-volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT, got_SIGTERM;
-
-static void
-setup_signal(int sig, psighandler h)
-{
- if (signal(sig, SIG_IGN) != SIG_IGN)
- (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)
-{
- switch(sig) {
- case SIGINT:
- got_SIGINT++;
- got_signal = 1;
- break;
- case SIGHUP:
- got_SIGHUP++;
- got_signal = 1;
- break;
- case SIGQUIT:
- got_SIGQUIT++;
- got_signal = 1;
- break;
- case SIGTERM:
- got_SIGTERM++;
- got_signal = 1;
- break;
- }
-}
-
/* 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 +514,7 @@ recheck_command_for_shell(char **av)
return av;
}
-static void
+void
run_command(const char *cmd, bool errCheck)
{
const char *p;
@@ -586,39 +556,124 @@ run_command(const char *cmd, bool errCheck)
_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)
+static Job myjob;
+
+void
+job_attach_node(Job *job, GNode *node)
+{
+ job->node = node;
+ job->next_cmd = Lst_First(&node->commands);
+ job->exit_type = JOB_EXIT_OKAY;
+ job->location = NULL;
+ job->flags = 0;
+}
+
+void
+job_handle_status(Job *job, int status)
+{
+ debug_job_printf("Process %ld (%s) exited with status %d.\n",
+ (long)job->pid, job->node->name, status);
+
+ /* classify status */
+ if (WIFEXITED(status)) {
+ job->code = WEXITSTATUS(status);/* exited */
+ if (status != 0) {
+ printf("*** Error code %d", job->code);
+ job->exit_type = JOB_EXIT_BAD;
+ } else
+ job->exit_type = JOB_EXIT_OKAY;
+ } else {
+ job->exit_type = JOB_SIGNALED;
+ job->code = WTERMSIG(status); /* signaled */
+ printf("*** Signal %d", job->code);
+ }
+
+ /* if there is a problem, what's going on ? */
+ if (job->exit_type != JOB_EXIT_OKAY) {
+ printf(" in target %s", job->node->name);
+ if (job->flags & JOB_ERRCHECK) {
+ job->node->built_status = ERROR;
+ /* compute expensive status if we really want it */
+ if ((job->flags & JOB_SILENT) && job == &myjob)
+ determine_expensive_job(job);
+ if (!keepgoing) {
+ printf("\n");
+ job->next = errorJobs;
+ errorJobs = job;
+ /* XXX don't free the command */
+ return;
+ }
+ printf(", line %lu of %s", job->location->lineno,
+ job->location->fname);
+ if ((job->flags & (JOB_SILENT | JOB_IS_EXPENSIVE))
+ == JOB_SILENT)
+ printf(": %s", job->cmd);
+ /* Abort the current target,
+ * but let others continue. */
+ printf(" (continuing)\n");
+ } else {
+ /* Continue executing commands for
+ * this target. If we return 0,
+ * this will happen... */
+ printf(" (ignored)\n");
+ job->exit_type = JOB_EXIT_OKAY;
+ }
+ }
+ free(job->cmd);
+}
+
+int
+run_gnode(GNode *gn)
+{
+ if (!gn || (gn->type & OP_DUMMY))
+ return NOSUCHNODE;
+
+ gn->built_status = MADE;
+
+ job_attach_node(&myjob, gn);
+ while (myjob.exit_type == JOB_EXIT_OKAY) {
+ bool finished = job_run_next(&myjob);
+ if (finished)
+ break;
+ handle_one_job(&myjob);
+ }
+
+ return gn->built_status;
+}
+
+
+static void
+setup_engine(void)
+{
+ static int already_setup = 0;
+
+ if (!already_setup) {
+ setup_meta();
+ already_setup = 1;
+ }
+}
+
+static bool
+do_run_command(Job *job)
{
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 */
+ pid_t cpid; /* Child pid */
- silent = gn->type & OP_SILENT;
- errCheck = !(gn->type & OP_IGNORE);
- doExecute = !noExecute;
+ const char *cmd = job->cmd;
+ silent = job->node->type & OP_SILENT;
+ errCheck = !(job->node->type & OP_IGNORE);
+ if (job->node->type & OP_MAKE)
+ doExecute = true;
+ else
+ doExecute = !noExecute;
/* How can we execute a null command ? we warn the user that the
* command expanded to nothing (is this the right thing to do?). */
if (*cmd == '\0') {
Error("%s expands to empty string", cmd);
- return 1;
+ return false;
}
for (;; cmd++) {
@@ -633,186 +688,68 @@ setup_and_run_command(char *cmd, GNode *gn, int dont_fork)
}
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) {
+ /* Print the command before fork if make -n or !silent*/
+ if ( noExecute || !silent)
printf("%s\n", cmd);
- fflush(stdout);
- }
+
+ 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 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*/
+ 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:
- Fatal("Could not fork");
+ Punt("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 (!Targ_Precious(gn)) {
- char *file = Var(TARGET_INDEX, gn);
-
- if (!noExecute && eunlink(file) != -1)
- Error("*** %s removed\n", file);
- }
- if (got_SIGINT) {
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- got_signal = 0;
- got_SIGINT = 0;
- run_gnode(interrupt_node);
- exit(255);
- }
- exit(255);
-}
-
-void
-expand_commands(GNode *gn)
-{
- 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->pid = cpid;
+ job->next = runningJobs;
+ runningJobs = job;
+ if (errCheck)
+ job->flags |= JOB_ERRCHECK;
+ else
+ job->flags &= ~JOB_ERRCHECK;
+ debug_job_printf("Running %ld (%s) %s\n", (long)job->pid,
+ job->node->name, (noExecute || !silent) ? "" : cmd);
+ return true;
}
}
-int
-run_gnode(GNode *gn)
+bool
+job_run_next(Job *job)
{
- if (gn != NULL && (gn->type & OP_DUMMY) == 0) {
- expand_commands(gn);
+ bool started;
+ GNode *gn = job->node;
+
+ setup_engine();
+ while (job->next_cmd != NULL) {
+ struct command *command = Lst_Datum(job->next_cmd);
+
+ handle_all_signals();
+ job->location = &command->location;
+ Parse_SetLocation(job->location);
+ job->cmd = Var_Subst(command->string, &gn->context, false);
+ job->next_cmd = Lst_Adv(job->next_cmd);
if (fatal_errors)
- exit(1);
- return run_prepared_gnode(gn);
- } else {
- return NOSUCHNODE;
- }
-}
-
-static int
-run_prepared_gnode(GNode *gn)
-{
- char *cmd;
-
- gn->built_status = MADE;
- while ((cmd = Lst_DeQueue(&gn->expanded)) != NULL) {
- if (setup_and_run_command(cmd, gn, 0) == 0)
- break;
- free(cmd);
- }
- free(cmd);
- if (got_signal)
- handle_compat_interrupts(gn);
- return gn->built_status;
-}
-
-void
-run_gnode_parallel(GNode *gn)
-{
- char *cmd;
-
- 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)
- 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);
- default:
- fprintf(stderr, "Could not run gnode, returned %d\n",
- gn->built_status);
- exit(1);
- }
-}
-
-void
-setup_engine(int parallel)
-{
- static int already_setup = 0;
-
- if (!already_setup) {
- setup_meta();
- if (parallel)
- setup_all_signals(parallel_handler, parallel_handler);
+ Punt(NULL);
+ started = do_run_command(job);
+ if (started)
+ return false;
else
- setup_all_signals(SigHandler, SIG_DFL);
- already_setup = 1;
+ free(job->cmd);
}
+ job->exit_type = JOB_EXIT_OKAY;
+ return true;
}
+
diff --git a/usr.bin/make/engine.h b/usr.bin/make/engine.h
index 046dd34e5b7..74fa2ed482a 100644
--- a/usr.bin/make/engine.h
+++ b/usr.bin/make/engine.h
@@ -1,6 +1,6 @@
#ifndef ENGINE_H
#define ENGINE_H
-/* $OpenBSD: engine.h,v 1.9 2010/07/19 19:30:37 espie Exp $ */
+/* $OpenBSD: engine.h,v 1.10 2012/09/21 07:55:20 espie Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -38,12 +38,12 @@
* from: @(#)job.h 8.1 (Berkeley) 6/6/93
*/
-/* ok = Job_CheckCommands(node);
+/* ok = node_find_valid_commands(node);
* verify the integrity of a node's commands, pulling stuff off
* .DEFAULT and other places if necessary.
*/
-extern bool Job_CheckCommands(GNode *);
-extern void job_failure(GNode *, void (*abortProc)(char *, ...));
+extern bool node_find_valid_commands(GNode *);
+extern void node_failure(GNode *);
/* Job_Touch(node);
* touch the path corresponding to a node or update the corresponding
* archive object.
@@ -66,19 +66,51 @@ 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_SIGINT, got_SIGHUP, got_SIGQUIT,
- got_SIGTERM, got_SIGTSTP, got_SIGTTOU, got_SIGTTIN, got_SIGWINCH,
- got_SIGCONT;
-
-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.
+ */
+struct Job_ {
+ struct Job_ *next; /* singly linked list */
+ pid_t pid; /* Current command process id */
+ Location *location;
+ 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_ERRCHECK 0x008 /* command wants errcheck */
+#define JOB_MINE 0x010 /* XXX special job run by compat */
+};
+
+extern bool job_run_next(Job *);
+
+extern void job_attach_node(Job *, GNode *);
+extern void job_handle_status(Job *, int);
#endif
diff --git a/usr.bin/make/error.c b/usr.bin/make/error.c
index 38f264eba3e..224379fc33a 100644
--- a/usr.bin/make/error.c
+++ b/usr.bin/make/error.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: error.c,v 1.21 2012/03/22 13:50:30 espie Exp $ */
+/* $OpenBSD: error.c,v 1.22 2012/09/21 07:55:20 espie Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
@@ -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;
-
- va_start(ap, fmt);
- (void)fprintf(stderr, "make: ");
- (void)vfprintf(stderr, fmt, ap);
- va_end(ap);
- (void)fprintf(stderr, "\n");
+ 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");
+ }
Job_AbortAll();
if (DEBUG(GRAPH2))
@@ -118,19 +118,15 @@ 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"));
- }
print_errors();
if (DEBUG(GRAPH2))
Targ_PrintGraph(2);
diff --git a/usr.bin/make/error.h b/usr.bin/make/error.h
index f81a7ec253d..ebcaf038c84 100644
--- a/usr.bin/make/error.h
+++ b/usr.bin/make/error.h
@@ -1,6 +1,6 @@
#ifndef ERROR_H
#define ERROR_H
-/* $OpenBSD: error.h,v 1.11 2010/07/19 19:46:44 espie Exp $ */
+/* $OpenBSD: error.h,v 1.12 2012/09/21 07:55:20 espie Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
@@ -44,7 +44,7 @@
extern void Error(char *, ...);
extern void Fatal(char *, ...);
extern void Punt(char *, ...);
-extern void Finish(int);
+extern void Finish(void);
/*
* Error levels for parsing. PARSE_FATAL means the process cannot continue
diff --git a/usr.bin/make/gnode.h b/usr.bin/make/gnode.h
index 25f5032a2ef..047e753e8f9 100644
--- a/usr.bin/make/gnode.h
+++ b/usr.bin/make/gnode.h
@@ -1,6 +1,6 @@
#ifndef GNODE_H
#define GNODE_H
-/* $OpenBSD: gnode.h,v 1.19 2012/04/11 18:27:30 espie Exp $ */
+/* $OpenBSD: gnode.h,v 1.20 2012/09/21 07:55:20 espie Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
@@ -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) */
@@ -143,6 +142,12 @@ struct GNode_ {
char name[1]; /* The target's name */
};
+struct command
+{
+ Location location;
+ char string[1];
+};
+
#define has_been_built(gn) \
((gn)->built_status == MADE || (gn)->built_status == UPTODATE)
#define should_have_file(gn) \
diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c
index 8fc16a82564..96081b75db8 100644
--- a/usr.bin/make/job.c
+++ b/usr.bin/make/job.c
@@ -1,7 +1,33 @@
-/* $OpenBSD: job.c,v 1.123 2012/08/25 08:12:56 espie Exp $ */
+/* $OpenBSD: job.c,v 1.124 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: job.c,v 1.16 1996/11/06 17:59:08 christos Exp $ */
/*
+ * Copyright (c) 2012 Marc Espie.
+ *
+ * Extensive code modifications for the OpenBSD project.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD
+ * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
* Copyright (c) 1988, 1989 by Adam de Boor
* Copyright (c) 1989 by Berkeley Softworks
@@ -42,13 +68,12 @@
* Interface:
* Job_Make Start the creation of the given target.
*
- * Job_Init Called to initialize this module. in addition,
- * any commands attached to the .BEGIN target
- * are executed before this function returns.
- * Hence, the makefile must have been parsed
- * before this function is called.
+ * Job_Init Called to initialize this module.
+ *
+ * Job_Begin execute commands attached to the .BEGIN target
+ * if any.
*
- * Job_End Cleanup any memory used.
+ * Job_End Should cleanup any memory used.
*
* can_start_job Return true if we can start job
*
@@ -58,13 +83,11 @@
* Job_Finish Perform any final processing which needs doing.
* This includes the execution of any commands
* which have been/were attached to the .END
- * target. It should only be called when the
- * job table is empty.
+ * target.
*
* Job_AbortAll Abort all current jobs. It doesn't
* handle output or do anything for the jobs,
- * just kills them. It should only be called in
- * an emergency, as it were.
+ * just kills them.
*
* Job_Wait Wait for all running jobs to finish.
*/
@@ -88,66 +111,12 @@
#include "var.h"
#include "targ.h"
#include "error.h"
-#include "lst.h"
#include "extern.h"
+#include "lst.h"
#include "gnode.h"
#include "memory.h"
#include "make.h"
-
-/*
- * The SEL_ constants determine the maximum amount of time spent in select
- * before coming out to see if a child has finished. SEL_SEC is the number of
- * seconds and SEL_USEC is the number of micro-seconds
- */
-#define SEL_SEC 0
-#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;
-};
+#include "buf.h"
static int aborting = 0; /* why is the make aborting? */
#define ABORT_ERROR 1 /* Because of an error */
@@ -155,320 +124,208 @@ static int aborting = 0; /* why is the make aborting? */
#define ABORT_WAIT 3 /* Waiting for jobs to finish */
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 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 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);
-
-/***
- *** 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 int nJobs; /* Number of jobs already allocated */
+static bool no_new_jobs; /* Mark recursive shit so we shouldn't start
+ * something else at the same time
+ */
+Job *runningJobs; /* Jobs currently running a process */
+Job *errorJobs; /* Jobs in error at end */
+static Job *heldJobs; /* Jobs not running yet because of expensive */
+static pid_t mypid;
+static volatile sig_atomic_t got_fatal;
-static void handle_all_jobs_output(void);
+static volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT, got_SIGTERM,
+ got_SIGINFO;
-/* 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 sigset_t sigset, emptyset;
+static void handle_signal(int);
+static void handle_siginfo(void);
+static void postprocess_job(Job *, bool);
+static Job *prepare_job(GNode *);
+static void determine_job_next_step(Job *);
+static void remove_job(Job *, bool);
+static void may_continue_job(Job *);
+static void continue_job(Job *);
+static Job *reap_finished_job(pid_t);
+static bool reap_jobs(void);
-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);
-}
+static bool expensive_job(Job *);
+static bool expensive_command(const char *);
+static void setup_signal(int);
+static void notice_signal(int);
+static void setup_all_signals(void);
+static void really_kill(int, pid_t);
void
-print_errors()
+print_errors(void)
{
- 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:
+ Job *j;
+
+ fprintf(stderr, "\nStop in %s%c\n", Var_Value(".CURDIR"),
+ errorJobs ? ':' : '.');
+ const char *previous = NULL;
+
+ for (j = errorJobs; j != NULL; j = j->next) {
+ const char *type;
+
+ 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",
+ j->location->lineno);
+ if (j->location->fname == previous)
+ fputs(",", stderr);
+ else
+ fprintf(stderr, " of %s,", j->location->fname);
+ previous = j->location->fname;
+ if ((j->flags & (JOB_SILENT | JOB_IS_EXPENSIVE)) == JOB_SILENT)
+ fprintf(stderr, "\n target %s: %s", j->node->name, j->cmd);
+ else
+ fprintf(stderr, " target %s", j->node->name);
+ fputs(")\n", stderr);
+ free(j->cmd);
}
}
static void
-banner(Job *job, FILE *out)
+setup_signal(int sig)
{
- if (job->node != lastNode) {
- if (DEBUG(JOBBANNER))
- (void)fprintf(out, "--- %s ---\n", job->node->name);
- lastNode = job->node;
+ if (signal(sig, SIG_IGN) != SIG_IGN) {
+ (void)signal(sig, notice_signal);
+ sigaddset(&sigset, sig);
}
}
-volatile sig_atomic_t got_SIGTSTP, got_SIGTTOU, got_SIGTTIN, got_SIGWINCH,
- got_SIGCONT;
static void
-handle_all_signals()
+notice_signal(int sig)
{
- while (got_signal) {
- got_signal = 0;
-
- if (got_SIGINT) {
- got_SIGINT=0;
- handle_signal(SIGINT);
- }
- if (got_SIGHUP) {
- got_SIGHUP=0;
- handle_signal(SIGHUP);
- }
- if (got_SIGQUIT) {
- got_SIGQUIT=0;
- handle_signal(SIGQUIT);
- }
- if (got_SIGTERM) {
- 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) {
+ switch(sig) {
case SIGINT:
got_SIGINT++;
- got_signal = 1;
- return;
+ got_fatal = 1;
+ break;
case SIGHUP:
got_SIGHUP++;
- got_signal = 1;
- return;
+ got_fatal = 1;
+ break;
case SIGQUIT:
got_SIGQUIT++;
- got_signal = 1;
- return;
+ got_fatal = 1;
+ break;
case SIGTERM:
got_SIGTERM++;
- got_signal = 1;
- return;
- case SIGTSTP:
- got_SIGTSTP++;
- got_signal = 1;
+ got_fatal = 1;
break;
- case SIGTTOU:
- got_SIGTTOU++;
- got_signal = 1;
+ case SIGINFO:
+ got_SIGINFO++;
break;
- case SIGTTIN:
- got_SIGTTIN++;
- got_signal = 1;
- break;
- case SIGWINCH:
- got_SIGWINCH++;
- got_signal = 1;
- break;
- case SIGCONT:
- got_SIGCONT++;
- got_signal = 1;
+ case SIGCHLD:
break;
}
- (void)killpg(getpid(), signo);
+}
- (void)signal(signo, SIG_DFL);
- errno = save_errno;
+void
+setup_all_signals(void)
+{
+ sigemptyset(&sigset);
+ sigemptyset(&emptyset);
+ /*
+ * 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);
+ setup_signal(SIGHUP);
+ setup_signal(SIGQUIT);
+ setup_signal(SIGTERM);
+ /* Display running jobs on SIGINFO */
+ setup_signal(SIGINFO);
+ /* Have to see SIGCHLD */
+ setup_signal(SIGCHLD);
+ got_fatal = 0;
}
-/*-
- *-----------------------------------------------------------------------
- * handle_signal --
- * handle a signal for ourselves
- *
- *-----------------------------------------------------------------------
- */
-static void
-handle_signal(int signo)
+static void
+handle_siginfo(void)
{
- if (DEBUG(JOB)) {
- (void)fprintf(stdout, "handle_signal(%d) called.\n", signo);
- (void)fflush(stdout);
- }
+ Job *job;
+ BUFFER buf;
+ bool first = true;
- /*
- * Deal with proper cleanup based on the signal received. We only run
- * the .INTERRUPT target if the signal was in fact an interrupt. The
- * other three termination signals are more of a "get out *now*"
- * command.
+ got_SIGINFO = 0;
+ /* we have to store the info in a buffer, because status from all
+ * makes running would get intermixed otherwise
*/
- if (signo == SIGINT)
- JobInterrupt(true, signo);
- else if (signo == SIGHUP || signo == SIGTERM || signo == SIGQUIT)
- JobInterrupt(false, signo);
+ Buf_Init(&buf, 0);
+
+ Buf_printf(&buf, "%s in %s: ", Var_Value("MAKE"), Var_Value(".CURDIR"));
- if (signo == SIGQUIT)
- Finish(0);
+ for (job = runningJobs; job != NULL ; job = job->next) {
+ if (!first)
+ Buf_puts(&buf, ", ");
+ first = false;
+ Buf_puts(&buf, job->node->name);
+ }
+ Buf_puts(&buf, first ? "nothing running\n" : "\n");
+
+ fputs(Buf_Retrieve(&buf), stderr);
+ Buf_Destroy(&buf);
}
-/*-
- *-----------------------------------------------------------------------
- * JobCmpPid --
- * Compare the pid of the job with the given pid and return 0 if they
- * are equal. This function is called from Job_CatchChildren via
- * Lst_Find to find the job descriptor of the finished job.
- *
- * Results:
- * 0 if the pid's match
- *-----------------------------------------------------------------------
- */
-static int
-JobCmpPid(void *job, /* job to examine */
- void *pid) /* process id desired */
+void
+handle_all_signals(void)
{
- return *(pid_t *)pid - ((Job *)job)->pid;
+ if (got_SIGINFO)
+ handle_siginfo();
+ while (got_fatal) {
+ got_fatal = 0;
+ aborting = ABORT_INTERRUPT;
+
+ if (got_SIGINT) {
+ got_SIGINT=0;
+ handle_signal(SIGINT);
+ }
+ if (got_SIGHUP) {
+ got_SIGHUP=0;
+ handle_signal(SIGHUP);
+ }
+ if (got_SIGQUIT) {
+ got_SIGQUIT=0;
+ handle_signal(SIGQUIT);
+ }
+ if (got_SIGTERM) {
+ got_SIGTERM=0;
+ handle_signal(SIGTERM);
+ }
+ }
}
-static void
-debug_printf(const char *fmt, ...)
+void
+debug_job_printf(const char *fmt, ...)
{
if (DEBUG(JOB)) {
va_list va;
-
+ (void)printf("[%ld] ", (long)mypid);
va_start(va, fmt);
- (void)vfprintf(stdout, fmt, va);
+ (void)vprintf(fmt, va);
fflush(stdout);
va_end(va);
}
}
-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 --
- * Do processing for the given job including updating
+ * postprocess_job --
+ * Do final processing for the given job including updating
* parents and starting new jobs as available/necessary.
*
* Side Effects:
- * Some nodes may be put on the toBeMade queue.
- * Final commands for the job are placed on end_node.
- *
* 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 we set the aborting flag
@@ -478,236 +335,69 @@ close_job_pipes(Job *job)
/*ARGSUSED*/
static void
-process_job_status(Job *job, int status)
+postprocess_job(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);
}
- free(job);
- if (errors && !keepgoing &&
+ if (errorJobs != NULL && !keepgoing &&
aborting != ABORT_INTERRUPT)
aborting = ABORT_ERROR;
+ if (aborting == ABORT_ERROR && DEBUG(QUICKDEATH))
+ handle_signal(SIGINT);
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);
+ Finish();
+}
+
+/* expensive jobs handling: in order to avoid forking an exponential number
+ * of jobs, make tries to figure out "recursive make" configurations.
+ * It may err on the side of caution.
+ * Basically, a command is "expensive" if it's likely to fork an extra
+ * level of make: either by looking at the command proper, or if it has
+ * some specific qualities ('+cmd' are likely to be recursive, as are
+ * .MAKE: commands). It's possible to explicitly say some targets are
+ * expensive or cheap with .EXPENSIVE or .CHEAP.
+ *
+ * While an expensive command is running, no_new_jobs
+ * is set, so jobs that would fork new processes are accumulated in the
+ * heldJobs list instead.
+ *
+ * This heuristics is also used on error exit: we display silent commands
+ * that failed, unless those ARE expensive commands: expensive commands
+ * are likely to not be failing by themselves, but to be the result of
+ * a cascade of failures in descendant makes.
+ */
+void
+determine_expensive_job(Job *job)
+{
+ if (expensive_job(job)) {
+ job->flags |= JOB_IS_EXPENSIVE;
+ no_new_jobs = true;
+ } else
+ job->flags &= ~JOB_IS_EXPENSIVE;
+ if (DEBUG(EXPENSIVE))
+ fprintf(stderr, "[%ld] Target %s running %.50s: %s\n",
+ (long)mypid, job->node->name, job->cmd,
+ job->flags & JOB_IS_EXPENSIVE ? "expensive" : "cheap");
}
-/*-
- *-----------------------------------------------------------------------
- * 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)
+static bool
+expensive_job(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);
- }
-
+ if (job->node->type & OP_CHEAP)
+ return false;
+ if (job->node->type & (OP_EXPENSIVE | OP_MAKE))
+ return true;
+ return expensive_command(job->cmd);
}
static bool
@@ -720,6 +410,9 @@ expensive_command(const char *s)
/* okay, comments are cheap, always */
if (*s == '#')
return false;
+ /* and commands we always execute are expensive */
+ if (*s == '+')
+ return true;
for (p = s; *p != '\0'; p++) {
if (*p == ' ' || *p == '\t') {
@@ -745,7 +438,7 @@ expensive_command(const char *s)
p++;
expensive = true;
while (p[1] != '\0' && p[1] != ' ' && p[1] != '\t') {
- if (p[1] == '.') {
+ if (p[1] == '.' || p[1] == '/') {
expensive = false;
break;
}
@@ -757,491 +450,309 @@ 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)
+prepare_job(GNode *gn)
{
- bool cmdsOK; /* true if the nodes commands were all right */
- bool noExec; /* Set true if we decide not to run the job */
-
- /*
- * Check the commands now so any attributes from .DEFAULT have a chance
- * to migrate to the node
+ /* a new job is prepared unless its commands are bogus (we don't
+ * have anything for it), or if we're in touch mode.
+ *
+ * Note that even in noexec mode, some commands may still run
+ * thanks to the +cmd construct.
*/
- cmdsOK = Job_CheckCommands(gn);
- expand_commands(gn);
- if (fatal_errors)
- Punt("can't continue");
-
- if ((gn->type & OP_MAKE) || (!noExecute && !touchFlag)) {
- /*
- * We're serious here, but if the commands were bogus, we're
- * also dead...
- */
- if (!cmdsOK)
- job_failure(gn, Punt);
-
- if (Lst_IsEmpty(&gn->commands))
- noExec = true;
- else
- noExec = false;
+ if (node_find_valid_commands(gn)) {
+ if (touchFlag) {
+ Job_Touch(gn);
+ return NULL;
+ } else {
+ Job *job;
- } else if (noExecute) {
- if (!cmdsOK || Lst_IsEmpty(&gn->commands))
- noExec = true;
- else
- noExec = false;
- } else {
- /*
- * Just touch the target and note that no shell should be
- * executed. Check
- * the commands, too, but don't die if they're no good -- it
- * does no harm to keep working up the graph.
- */
- Job_Touch(gn);
- noExec = true;
- }
+ job = emalloc(sizeof(Job));
+ if (job == NULL)
+ Punt("can't create job: out of memory");
- /*
- * If we're not supposed to execute a shell, don't.
- */
- if (noExec) {
- /*
- * We only want to work our way up the graph if we aren't here
- * because the commands for the job were no good.
- */
- if (cmdsOK && !aborting) {
- gn->built_status = MADE;
- Make_Update(gn);
+ job_attach_node(job, gn);
+ return job;
}
- return NULL;
} else {
- Job *job; /* new job descriptor */
- job = emalloc(sizeof(Job));
- if (job == NULL)
- Punt("JobStart out of memory");
-
- job->node = gn;
-
- /*
- * Set the initial value of the flags for this job based on the
- * global ones and the node's attributes... Any flags supplied
- * by the caller are also added to the field.
- */
- job->flags = flags;
+ node_failure(gn);
+ return NULL;
+ }
+}
- if (gn->type & OP_CHEAP)
- return job;
- if ((gn->type & OP_EXPENSIVE) ||
- expensive_commands(&gn->expanded))
- job->flags |= JOB_IS_EXPENSIVE;
+static void
+may_continue_job(Job *job)
+{
+ if (no_new_jobs) {
+ if (DEBUG(EXPENSIVE))
+ fprintf(stderr, "[%ld] expensive -> hold %s\n",
+ (long)mypid, job->node->name);
+ job->next = heldJobs;
+ heldJobs = job;
+ } else
+ continue_job(job);
+}
- return job;
- }
+static void
+continue_job(Job *job)
+{
+ bool finished = job_run_next(job);
+ if (finished)
+ remove_job(job, true);
+ else
+ determine_expensive_job(job);
}
/*-
*-----------------------------------------------------------------------
- * JobStart --
+ * Job_Make --
* Start a target-creation process going for the target described
* by the graph node gn.
*
* Side Effects:
- * A new Job node is created and added to the list of running
- * jobs. Make is forked and a child shell created.
+ * A new Job node is created and its commands continued, which
+ * may fork the first command of that job.
*-----------------------------------------------------------------------
*/
-static void
-JobStart(GNode *gn, /* target to create */
- int flags) /* flags for the job to override normal ones.
- * e.g. JOB_IS_SPECIAL */
+void
+Job_Make(GNode *gn)
{
Job *job;
- job = prepare_job(gn, flags);
+ bool finished;
+
+ job = prepare_job(gn);
if (!job)
return;
- JobExec(job);
+ nJobs++;
+ may_continue_job(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;
+determine_job_next_step(Job *job)
+{
+ bool okay;
+ if (job->flags & JOB_IS_EXPENSIVE) {
+ no_new_jobs = false;
+ if (DEBUG(EXPENSIVE))
+ fprintf(stderr, "[%ld] "
+ "Returning from expensive target %s, "
+ "allowing new jobs\n", (long)mypid,
+ job->node->name);
+ }
- banner(job, out);
- job->flags |= JOB_DIDOUTPUT;
- for (i = 0; i < endPos; i++)
- putc(p->buffer[i], out);
+ okay = job->exit_type == JOB_EXIT_OKAY;
+ if (!okay || job->next_cmd == NULL)
+ remove_job(job, okay);
+ else
+ may_continue_job(job);
}
-/* 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)
-{
- size_t i;
-
- print_partial_buffer(p, job, out, endPos);
-
- for (i = endPos; i < p->pos; i++)
- p->buffer[i-endPos] = p->buffer[i];
- p->pos -= endPos;
-}
-
-/* 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)
+remove_job(Job *job, bool okay)
{
- size_t i;
-
- for (i = p->pos; i > limit; i--) {
- if (p->buffer[i-1] == '\n') {
- print_partial_buffer_and_shift(p, job, out, i);
- return true;
- }
+ nJobs--;
+ postprocess_job(job, okay);
+ while (!no_new_jobs) {
+ if (heldJobs != NULL) {
+ job = heldJobs;
+ heldJobs = heldJobs->next;
+ if (DEBUG(EXPENSIVE))
+ fprintf(stderr, "[%ld] cheap -> release %s\n",
+ (long)mypid, job->node->name);
+ continue_job(job);
+ } else
+ break;
}
- 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.
+/*
+ * job = reap_finished_job(pid):
+ * retrieve and remove a job from runningJobs, based on its pid
*
- * Side Effects:
- * curPos may be shifted as may the contents of outBuf.
- *-----------------------------------------------------------------------
+ * Note that we remove it right away, so that handle_signals()
+ * is accurate.
*/
-static void
-handle_pipe(struct job_pipe *p,
- Job *job, FILE *out, bool finish)
-{
- 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);
-
- /* 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;
- }
-}
-
-static void
-handle_job_output(Job *job, int i, bool finish)
+static Job *
+reap_finished_job(pid_t pid)
{
- handle_pipe(&job->in[i], job, i == 0 ? stdout : stderr, finish);
-}
+ Job **j, *job;
-static void
-remove_job(LstNode ln, int status)
-{
- Job *job;
+ for (j = &runningJobs; *j != NULL; j = &((*j)->next))
+ if ((*j)->pid == pid) {
+ job = *j;
+ *j = job->next;
+ return 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);
+ return NULL;
}
-/*-
- *-----------------------------------------------------------------------
- * Job_CatchChildren --
- * Handle the exit of a child. Called by handle_running_jobs
- *
- * Side Effects:
- * The job descriptor is removed from the list of children.
- *
- * 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.
- *-----------------------------------------------------------------------
+/*
+ * classic waitpid handler: retrieve as many dead children as possible.
+ * returns true if succesful
*/
-void
-Job_CatchChildren()
+static bool
+reap_jobs(void)
{
- pid_t pid; /* pid of dead child */
- LstNode jnode; /* list element for finding job */
- int status; /* Exit/termination status */
-
- /*
- * Don't even bother if we know there's no one around.
- */
- if (nJobs == 0)
- return;
+ pid_t pid; /* pid of dead child */
+ int status; /* Exit/termination status */
+ bool reaped = false;
+ Job *job;
while ((pid = waitpid(WAIT_ANY, &status, WNOHANG)) > 0) {
- handle_all_signals();
-
- jnode = Lst_Find(&runningJobs, JobCmpPid, &pid);
+ reaped = true;
+ job = reap_finished_job(pid);
- if (jnode == NULL) {
- Error("Child (%ld) not in table?", (long)pid);
+ if (job == NULL) {
+ Punt("Child (%ld) not in table?", (long)pid);
} else {
- remove_job(jnode, status);
+ job_handle_status(job, status);
+ determine_job_next_step(job);
}
}
+ /* sanity check, should not happen */
+ if (pid == -1 && errno == ECHILD && runningJobs != NULL)
+ Punt("Process has no children, but runningJobs is not empty ?");
+ return reaped;
}
void
-handle_all_jobs_output(void)
+handle_running_jobs(void)
{
- int nfds;
- struct timeval timeout;
- LstNode ln, ln2;
- Job *job;
- int i;
- int status;
+ sigset_t old;
- /* no jobs */
- if (Lst_IsEmpty(&runningJobs))
- return;
+ /* reaping children in the presence of caught signals */
- (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);
- }
- }
+ /* first, we make sure to hold on new signals, to synchronize
+ * reception of new stuff on sigsuspend
+ */
+ sigprocmask(SIG_BLOCK, &sigset, &old);
+ while (runningJobs != NULL) {
+ /* did we already have pending stuff that advances things ?
+ * then handle_all_signals() will not return
+ * or reap_jobs() will reap_jobs()
+ */
+ handle_all_signals();
+ if (reap_jobs())
+ break;
+ /* okay, so it's safe to suspend, we have nothing to do but
+ * wait...
+ */
+ sigsuspend(&emptyset);
}
+ sigprocmask(SIG_SETMASK, &old, NULL);
}
void
-handle_running_jobs()
+handle_one_job(Job *job)
{
- handle_all_jobs_output();
- Job_CatchChildren();
+ int stat;
+ int status;
+ sigset_t old;
+
+ sigprocmask(SIG_BLOCK, &sigset, &old);
+ while (1) {
+ handle_all_signals();
+ stat = waitpid(job->pid, &status, WNOHANG);
+ if (stat == job->pid)
+ break;
+ sigsuspend(&emptyset);
+ }
+ runningJobs = NULL;
+ job_handle_status(job, status);
+ sigprocmask(SIG_SETMASK, &old, NULL);
}
static void
loop_handle_running_jobs()
{
- while (nJobs)
+ while (runningJobs != NULL)
handle_running_jobs();
}
-/*-
- *-----------------------------------------------------------------------
- * Job_Make --
- * Start the creation of a target. Basically a front-end for
- * JobStart used by the Make module.
- *
- * Side Effects:
- * Another job is started.
- *-----------------------------------------------------------------------
- */
-void
-Job_Make(GNode *gn)
-{
- (void)JobStart(gn, 0);
-}
-
-static void
-block_signals()
-{
- sigprocmask(SIG_BLOCK, &set, &oset);
-}
-
-static void
-unblock_signals()
-{
- sigprocmask(SIG_SETMASK, &oset, NULL);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * Job_Init --
- * Initialize the process module
- *
- * Side Effects:
- * lists and counters are initialized
- *-----------------------------------------------------------------------
- */
void
Job_Init(int maxproc)
{
- Static_Lst_Init(&runningJobs);
- Static_Lst_Init(&errorsList);
+ runningJobs = NULL;
+ heldJobs = NULL;
+ errorJobs = NULL;
maxJobs = maxproc;
+ mypid = getpid();
+
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;
-
- lastNode = NULL;
-
- if ((begin_node->type & OP_DUMMY) == 0) {
- JobStart(begin_node, JOB_IS_SPECIAL);
- loop_handle_running_jobs();
- }
+ setup_all_signals();
}
-static bool
-Job_Full()
-{
- return aborting || (nJobs >= maxJobs);
-}
-/*-
- *-----------------------------------------------------------------------
- * Job_Full --
- * See if the job table is full. It is considered full
- * if we are in the process of aborting OR if we have
- * reached/exceeded our quota.
- *
- * Results:
- * true if the job table is full, false otherwise
- *-----------------------------------------------------------------------
- */
bool
can_start_job(void)
{
- if (Job_Full() || expensive_job)
+ if (aborting || nJobs >= maxJobs)
return false;
else
return true;
}
-/*-
- *-----------------------------------------------------------------------
- * Job_Empty --
- * See if the job table is empty.
- *
- * Results:
- * true if it is. false if it ain't.
- * -----------------------------------------------------------------------
- */
bool
Job_Empty(void)
{
- if (nJobs == 0)
- return true;
- else
- return false;
+ return runningJobs == NULL;
}
+static void
+really_kill(pid_t pid, int sig)
+{
+ killpg(pid, sig);
+ if (killpg(pid, sig) == - 1 && errno == ESRCH)
+ kill(pid, sig);
+}
/*-
*-----------------------------------------------------------------------
- * JobInterrupt --
+ * handle_signal --
* Handle the receipt of an interrupt.
*
* Side Effects:
- * All children are killed. Another job will be started if the
+ * All children are killed. Another job may be started if the
* .INTERRUPT target was given.
*-----------------------------------------------------------------------
*/
static void
-JobInterrupt(bool runINTERRUPT, /* true if commands for the .INTERRUPT
- * target should be executed */
- int signo) /* signal received */
+handle_signal(int signo)
{
- LstNode ln; /* element in job table */
- Job *job; /* job descriptor in that element */
+ Job *job;
- aborting = ABORT_INTERRUPT;
+ debug_job_printf("handle_signal(%d) called.\n", signo);
- for (ln = Lst_First(&runningJobs); ln != NULL; ln = Lst_Adv(ln)) {
- job = (Job *)Lst_Datum(ln);
+ for (job = runningJobs; job != NULL; job = job->next) {
if (!Targ_Precious(job->node)) {
- const char *file = job->node->path == NULL ?
- job->node->name : job->node->path;
- if (!noExecute && eunlink(file) != -1) {
+ const char *file = Var(TARGET_INDEX, job->node);
+
+ if (!noExecute && eunlink(file) != -1)
Error("*** %s removed", file);
- }
- }
- if (job->pid) {
- debug_printf("JobInterrupt passing signal to "
- "child %ld.\n", (long)job->pid);
- killpg(job->pid, signo);
}
+ debug_job_printf("handle_signal passing signal to "
+ "child %ld running %s.\n", (long)job->pid,
+ job->node->name);
+ really_kill(job->pid, signo);
}
- if (runINTERRUPT && !touchFlag) {
+ if (signo == SIGINT && !touchFlag) {
if ((interrupt_node->type & OP_DUMMY) == 0) {
ignoreErrors = false;
- JobStart(interrupt_node, 0);
- loop_handle_running_jobs();
+ Job_Make(interrupt_node);
}
}
- exit(signo);
+ loop_handle_running_jobs();
+ print_errors();
+
+ /* die by that signal */
+ sigprocmask(SIG_BLOCK, &sigset, NULL);
+ signal(signo, SIG_DFL);
+ really_kill(getpid(), signo);
+ sigprocmask(SIG_SETMASK, &emptyset, NULL);
+ /*NOTREACHED*/
}
/*
@@ -1250,25 +761,34 @@ JobInterrupt(bool runINTERRUPT, /* true if commands for the .INTERRUPT
* Do final processing such as the running of the commands
* attached to the .END target.
*
- * Results:
- * Number of errors reported.
- *
+ * return true if fatal errors have happened.
*-----------------------------------------------------------------------
*/
-int
+bool
Job_Finish(void)
{
+ bool errors = errorJobs != NULL;
+
if ((end_node->type & OP_DUMMY) == 0) {
if (errors) {
Error("Errors reported so .END ignored");
} else {
- JobStart(end_node, JOB_IS_SPECIAL);
+ Job_Make(end_node);
loop_handle_running_jobs();
}
}
return errors;
}
+void
+Job_Begin(void)
+{
+ if ((begin_node->type & OP_DUMMY) == 0) {
+ Job_Make(begin_node);
+ loop_handle_running_jobs();
+ }
+}
+
#ifdef CLEANUP
void
Job_End(void)
@@ -1300,33 +820,23 @@ Job_Wait(void)
* Job_AbortAll --
* Abort all currently running jobs without handling output or anything.
* This function is to be called only in the event of a major
- * error. Most definitely NOT to be called from JobInterrupt.
+ * error.
*
* Side Effects:
- * All children are killed, not just the firstborn
+ * All children are killed
*-----------------------------------------------------------------------
*/
void
Job_AbortAll(void)
{
- LstNode ln; /* element in job table */
Job *job; /* the job descriptor in that element */
int foo;
aborting = ABORT_ERROR;
- if (nJobs) {
- for (ln = Lst_First(&runningJobs); ln != NULL;
- ln = Lst_Adv(ln)) {
- job = (Job *)Lst_Datum(ln);
-
- /*
- * kill the child process with increasingly drastic
- * signals to make darn sure it's dead.
- */
- killpg(job->pid, SIGINT);
- killpg(job->pid, SIGKILL);
- }
+ for (job = runningJobs; job != NULL; job = job->next) {
+ really_kill(job->pid, SIGINT);
+ really_kill(job->pid, SIGKILL);
}
/*
@@ -1335,4 +845,3 @@ Job_AbortAll(void)
while (waitpid(WAIT_ANY, &foo, WNOHANG) > 0)
continue;
}
-
diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h
index 89689195ea2..0b3e909ab7e 100644
--- a/usr.bin/make/job.h
+++ b/usr.bin/make/job.h
@@ -1,7 +1,7 @@
#ifndef _JOB_H_
#define _JOB_H_
-/* $OpenBSD: job.h,v 1.25 2010/07/19 19:46:44 espie Exp $ */
+/* $OpenBSD: job.h,v 1.26 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: job.h,v 1.5 1996/11/06 17:59:10 christos Exp $ */
/*
@@ -50,15 +50,21 @@ extern void Job_Init(int);
extern bool can_start_job(void);
extern bool Job_Empty(void);
extern int Job_Finish(void);
+extern void Job_Begin(void);
#ifdef CLEANUP
extern void Job_End(void);
#else
#define Job_End()
#endif
+
extern void Job_Wait(void);
extern void Job_AbortAll(void);
extern void print_errors(void);
extern void handle_running_jobs(void);
-extern void parallel_handler(int);
+extern void handle_all_signals(void);
+extern void determine_expensive_job(Job *);
+extern Job *runningJobs, *errorJobs;
+extern void debug_job_printf(const char *, ...);
+extern void handle_one_job(Job *);
#endif /* _JOB_H_ */
diff --git a/usr.bin/make/lowparse.c b/usr.bin/make/lowparse.c
index af712903acd..d2c83e45fad 100644
--- a/usr.bin/make/lowparse.c
+++ b/usr.bin/make/lowparse.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: lowparse.c,v 1.28 2012/08/25 08:12:56 espie Exp $ */
+/* $OpenBSD: lowparse.c,v 1.29 2012/09/21 07:55:20 espie Exp $ */
/* low-level parsing functions. */
@@ -116,6 +116,26 @@ static void read_logical_line(Buffer, int);
* (e.g., not a backslash or a space. */
static int skip_empty_lines_and_read_char(Buffer);
+const char *curdir;
+size_t curdir_len;
+
+void
+Parse_setcurdir(const char *dir)
+{
+ curdir = dir;
+ curdir_len = strlen(dir);
+}
+
+static const char *
+simplify(const char *filename)
+{
+ if (strncmp(curdir, filename, curdir_len) == 0 &&
+ filename[curdir_len] == '/')
+ return filename + curdir_len + 1;
+ else
+ return filename;
+}
+
static struct input_stream *
new_input_file(const char *name, FILE *stream)
{
@@ -125,7 +145,7 @@ new_input_file(const char *name, FILE *stream)
#endif
istream = emalloc(sizeof(*istream));
- istream->origin.fname = name;
+ istream->origin.fname = simplify(name);
istream->str = NULL;
/* Naturally enough, we start reading at line 0. */
istream->origin.lineno = 0;
diff --git a/usr.bin/make/lowparse.h b/usr.bin/make/lowparse.h
index 2bbdd831742..acacc608ebb 100644
--- a/usr.bin/make/lowparse.h
+++ b/usr.bin/make/lowparse.h
@@ -1,7 +1,7 @@
#ifndef LOWPARSE_H
#define LOWPARSE_H
-/* $OpenBSD: lowparse.h,v 1.9 2012/08/25 08:12:56 espie Exp $ */
+/* $OpenBSD: lowparse.h,v 1.10 2012/09/21 07:55:20 espie Exp $ */
/*
* Copyright (c) 1999 Marc Espie.
@@ -114,4 +114,6 @@ extern char *Parse_ReadUnparsedLine(Buffer, const char *);
* At end of parsing, report on fatal errors.
*/
extern void Parse_ReportErrors(void);
+
+extern void Parse_setcurdir(const char *);
#endif
diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c
index 646e9b82ad9..5c61c5d5a1b 100644
--- a/usr.bin/make/main.c
+++ b/usr.bin/make/main.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: main.c,v 1.95 2010/07/19 19:46:44 espie Exp $ */
+/* $OpenBSD: main.c,v 1.96 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: main.c,v 1.34 1997/03/24 20:56:36 gwr Exp $ */
/*
@@ -49,6 +49,7 @@
#include "config.h"
#include "defines.h"
#include "var.h"
+#include "lowparse.h"
#include "parse.h"
#include "parsevar.h"
#include "dir.h"
@@ -65,6 +66,7 @@
#include "lst.h"
#include "memory.h"
#include "make.h"
+#include "dump.h"
#ifndef PATH_MAX
# ifdef MAXPATHLEN
@@ -94,6 +96,7 @@ bool queryFlag; /* -q flag */
bool touchFlag; /* -t flag */
bool ignoreErrors; /* -i flag */
bool beSilent; /* -s flag */
+bool dumpData; /* -p flag */
struct dirs {
char *current;
@@ -109,7 +112,6 @@ static void record_option(int, const char *);
static char *figure_out_MACHINE(void);
static char *figure_out_MACHINE_ARCH(void);
static char *figure_out_MACHINE_CPU(void);
-static void no_fd_limits(void);
static char *chdir_verify_path(const char *, struct dirs *);
static char *concat_verify(const char *, const char *, char, struct dirs *);
@@ -122,7 +124,6 @@ static void read_all_make_rules(bool, bool, Lst, struct dirs *);
static void read_makefile_list(Lst, struct dirs *);
static int ReadMakefile(void *, void *);
-
static void record_option(int c, const char *arg)
{
char opt[3];
@@ -159,6 +160,9 @@ posixParseOptLetter(int c)
case 'n':
noExecute = true;
break;
+ case 'p':
+ dumpData = true;
+ break;
case 'q':
queryFlag = true;
/* Kind of nonsensical, wot? */
@@ -195,8 +199,8 @@ MainParseArgs(int argc, char **argv)
{
int c, optend;
-#define OPTFLAGS "BD:I:PSV:d:ef:ij:km:nqrst"
-#define OPTLETTERS "BPSiknqrst"
+#define OPTFLAGS "BD:I:PSV:d:ef:ij:km:npqrst"
+#define OPTLETTERS "BPSiknpqrst"
optind = 1; /* since we're called more than once */
optreset = 1;
@@ -242,6 +246,9 @@ MainParseArgs(int argc, char **argv)
case 'd':
debug |= DEBUG_DIR;
break;
+ case 'e':
+ debug |= DEBUG_EXPENSIVE;
+ break;
case 'f':
debug |= DEBUG_FOR;
break;
@@ -259,7 +266,7 @@ MainParseArgs(int argc, char **argv)
debug |= DEBUG_JOB;
break;
case 'J':
- debug |= DEBUG_JOBBANNER;
+ /* ignore */
break;
case 'l':
debug |= DEBUG_LOUD;
@@ -273,6 +280,9 @@ MainParseArgs(int argc, char **argv)
case 'p':
debug |= DEBUG_PARALLEL;
break;
+ case 'q':
+ debug |= DEBUG_QUICKDEATH;
+ break;
case 's':
debug |= DEBUG_SUFF;
break;
@@ -460,20 +470,6 @@ figure_out_MACHINE_CPU()
return r;
}
-/* get rid of resource limit on file descriptors */
-static void
-no_fd_limits()
-{
-#ifdef RLIMIT_NOFILE
- struct rlimit rl;
- if (getrlimit(RLIMIT_NOFILE, &rl) != -1 &&
- rl.rlim_cur != rl.rlim_max) {
- rl.rlim_cur = rl.rlim_max;
- (void)setrlimit(RLIMIT_NOFILE, &rl);
- }
-#endif
-}
-
static char *
figure_out_CURDIR()
{
@@ -676,7 +672,6 @@ main(int argc, char **argv)
static struct dirs d;
bool read_depend = true;/* false if we don't want to read .depend */
- no_fd_limits();
setup_CURDIR_OBJDIR(&d, machine);
esetenv("PWD", d.object);
@@ -710,6 +705,7 @@ main(int argc, char **argv)
Dir_AddDir(defaultPath, d.current);
Var_Set(".CURDIR", d.current);
Var_Set(".OBJDIR", d.object);
+ Parse_setcurdir(d.current);
Targ_setdirs(d.current, d.object);
/*
@@ -788,6 +784,10 @@ main(int argc, char **argv)
if (DEBUG(GRAPH1))
Targ_PrintGraph(1);
+ if (dumpData) {
+ dump_data();
+ exit(0);
+ }
/* Print the values of any variables requested by the user. */
if (!Lst_IsEmpty(&varstoprint)) {
LstNode ln;
@@ -808,19 +808,16 @@ main(int argc, char **argv)
else
Targ_FindList(&targs, create);
+ Job_Init(maxJobs);
+ /* If the user has defined a .BEGIN target, execute the commands
+ * attached to it. */
+ if (!queryFlag)
+ Job_Begin();
if (compatMake)
/* Compat_Init will take care of creating all the
* targets as well as initializing the module. */
Compat_Run(&targs);
else {
- /* Initialize job module before traversing the graph,
- * now that any .BEGIN and .END targets have been
- * read. This is done only if the -q flag wasn't given
- * (to prevent the .BEGIN from being executed should
- * it exist). */
- if (!queryFlag)
- Job_Init(maxJobs);
-
/* Traverse the graph, checking on all the targets. */
outOfDate = Make_Run(&targs);
}
diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1
index 5b031fce343..73ac08138c1 100644
--- a/usr.bin/make/make.1
+++ b/usr.bin/make/make.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: make.1,v 1.93 2012/04/11 18:27:30 espie Exp $
+.\" $OpenBSD: make.1,v 1.94 2012/09/21 07:55:20 espie Exp $
.\" $NetBSD: make.1,v 1.18 1997/03/10 21:19:53 christos Exp $
.\"
.\" Copyright (c) 1990, 1993
@@ -30,7 +30,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd $Mdocdate: April 11 2012 $
+.Dd $Mdocdate: September 21 2012 $
.Dt MAKE 1
.Os
.Sh NAME
@@ -155,6 +155,8 @@ Print debugging information about archive searching and caching.
Print debugging information about conditional evaluation.
.It Ar d
Print debugging information about directory searching and caching.
+.It Ar e
+Print debugging information about expensive jobs heuristics.
.It Ar f
Print debugging information about the expansion of for loops.
.It Ar "g1"
@@ -162,10 +164,8 @@ Print the input graph before making anything.
.It Ar "g2"
Print the input graph after making everything, or before exiting
on error.
-.It Ar J
-Print job tokens showing which output corresponds to what job.
.It Ar j
-Print debugging information about running multiple shells.
+Print debugging information about external jobs forked.
.It Ar l
Print commands in Makefile targets regardless of whether or not they are
prefixed by @.
@@ -190,6 +190,10 @@ will wait between 0 and ${RANDOM_DELAY} seconds at the start of each job.
A given random seed can be forced by setting
.Va RANDOM_SEED ,
but this does not guarantee reproductibility.
+.It Ar q
+.Sq quick death
+option: after a fatal error, instead of waiting for other jobs to die,
+kill them right away.
.It Ar s
Print debugging information about suffix-transformation rules.
.It Ar t
@@ -1134,8 +1138,10 @@ Execute the commands associated with this target even if the
or
.Fl t
options were specified.
-Normally used to mark recursive
-.Nm make Ns 's .
+Can be used to mark recursive
+.Nm make Ns 's ,
+prefer standard
+.Sq Ic + Ns Ar cmd .
.It Ic .NOTMAIN
Normally
.Nm
@@ -1193,11 +1199,10 @@ behavior apply.
.It Ic .DEFAULT
This is sort of a
.Ic .USE
-rule for any target (that was used only as a
-source) that
+rule for any target (that was used only as a source) that
.Nm
can't figure out any other way to create.
-Only the shell script is used.
+Uses only the commands.
The
.Ic .IMPSRC
variable of a target that inherits
diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c
index ff2954dbd59..5afede25cd0 100644
--- a/usr.bin/make/parse.c
+++ b/usr.bin/make/parse.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: parse.c,v 1.104 2012/04/20 13:28:11 espie Exp $ */
+/* $OpenBSD: parse.c,v 1.105 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: parse.c,v 1.29 1997/03/10 21:20:04 christos Exp $ */
/*
@@ -547,8 +547,14 @@ add_target_node(const char *line, const char *end)
gn->type &= ~OP_DUMMY;
}
- if (gn != NULL)
- Array_AtEnd(&gtargets, 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(&gtargets, 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 *targets, const char *line)
{
/* 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 *targets, const char *line,
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 *targets, const char *line,
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 *targets, const char *line,
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);
}
}
diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c
index 6a2f61334ee..9827efee8a5 100644
--- a/usr.bin/make/targ.c
+++ b/usr.bin/make/targ.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: targ.c,v 1.65 2012/09/01 16:44:25 espie Exp $ */
+/* $OpenBSD: targ.c,v 1.66 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: targ.c,v 1.11 1997/02/20 16:51:50 christos Exp $ */
/*
@@ -195,7 +195,6 @@ Targ_NewGNi(const char *name, const char *ename)
gn->origin.fname = NULL;
gn->impliedsrc = NULL;
Lst_Init(&gn->commands);
- Lst_Init(&gn->expanded);
gn->suffix = NULL;
gn->next = NULL;
gn->basename = NULL;
@@ -312,9 +311,10 @@ TargPrintName(void *gnp)
void
-Targ_PrintCmd(void *cmd)
+Targ_PrintCmd(void *p)
{
- printf("\t%s\n", (char *)cmd);
+ struct command *cmd = p;
+ printf("\t%s\n", cmd->string);
}
void
diff --git a/usr.bin/make/varmodifiers.c b/usr.bin/make/varmodifiers.c
index a878a478950..bade403c7bf 100644
--- a/usr.bin/make/varmodifiers.c
+++ b/usr.bin/make/varmodifiers.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: varmodifiers.c,v 1.30 2011/08/16 14:18:25 espie Exp $ */
+/* $OpenBSD: varmodifiers.c,v 1.31 2012/09/21 07:55:20 espie Exp $ */
/* $NetBSD: var.c,v 1.18 1997/03/18 19:24:46 christos Exp $ */
/*
@@ -1489,7 +1489,7 @@ VarModifiers_Apply(char *str, const struct Name *name, SymTable *ctxt,
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++;