summaryrefslogtreecommitdiff
path: root/usr.bin/make
diff options
context:
space:
mode:
authorMarc Espie <espie@cvs.openbsd.org>2007-11-02 17:27:25 +0000
committerMarc Espie <espie@cvs.openbsd.org>2007-11-02 17:27:25 +0000
commit38cee2c92fe49e02148edcf072b2408b46a3707b (patch)
tree5107d5347ee96fbe9db13fd6f733edce4cc09ba0 /usr.bin/make
parente1edad895abd8b9b6c138c98651079a93441f006 (diff)
Work done at p2k7.
This is a really big step towards getting parallel make to work. Note that this is not yet complete. There are still a few `details' to fix before this works 100%. Specifically: sequential make (compat) and parallel make don't use the same engine, and the parallel engine still has a few limitations. For instance, some known issues: - parallel make does not deal with .phony targets correctly all the time. - some errors are deadly in parallel make mode. - parallel make NEEDS way more sturdy correspondance of file system paths and target names, since it often needs to match dependencies to targets before the corresponding files exist. - some local variables like $* get set in a bogus way in some cases. - suffix handling has issues, especially related to the NULL suffix. So, if you find stuff that does NOT yet work with parallel make, don't go blindly try to fix the Makefile. It's very likely you might have stumbled into a make bug. (unless you really, really, understand Makefiles, DON'T GO CHANGING THEM YET). Tested by lots of people, thanks go to miod@, and robert@ among other people. Quick summary of what this does: - remove `saving commands' extension (it's not really usable, nor used) - move compat job runner and parallel interrupt handling into engine.c - tweak the code so that both compat and parallel mode use the same job runner and the same interrupt handling. Remove the other one. - optimize job runner so that, in parallel mode, the last command does not fork if we can avoid it (as it's already running in a sub shell). - scrape all the code that dealt with creating shell scripts from commands. - scrape all the code that dealt with recognizing special sequences in command output to print/not print output. - fix the parallel job pipe to not keep around file descriptors that are not needed. - replace the parallel job buffering with a nicer one, that deals with non-blocking descriptors to try to agregate as much output from one job in one go (greed) to unconfuse the users. - create two pipes per job, so that stdout and stderr stay separate. - make job token printing a debug option. - always use the parallel job-runner to `execute' commands, even if we just print them out. - store list of errors encountered during parallel make running, and print them on exit, so that we know what went wrong. - add a dirty hack to targ.c to deal with paths produced by gccmakedep.
Diffstat (limited to 'usr.bin/make')
-rw-r--r--usr.bin/make/PSD.doc/tutorial.ms52
-rw-r--r--usr.bin/make/arch.c22
-rw-r--r--usr.bin/make/compat.c285
-rw-r--r--usr.bin/make/defines.h3
-rw-r--r--usr.bin/make/engine.c363
-rw-r--r--usr.bin/make/engine.h12
-rw-r--r--usr.bin/make/error.c16
-rw-r--r--usr.bin/make/gnode.h4
-rw-r--r--usr.bin/make/job.c1035
-rw-r--r--usr.bin/make/job.h3
-rw-r--r--usr.bin/make/main.c6
-rw-r--r--usr.bin/make/make.110
-rw-r--r--usr.bin/make/make.c11
-rw-r--r--usr.bin/make/suff.c3
-rw-r--r--usr.bin/make/targ.c48
-rw-r--r--usr.bin/make/targ.h4
16 files changed, 744 insertions, 1133 deletions
diff --git a/usr.bin/make/PSD.doc/tutorial.ms b/usr.bin/make/PSD.doc/tutorial.ms
index 7f3a7c0ab2e..e24750daf33 100644
--- a/usr.bin/make/PSD.doc/tutorial.ms
+++ b/usr.bin/make/PSD.doc/tutorial.ms
@@ -1,4 +1,4 @@
-.\" $OpenBSD: tutorial.ms,v 1.12 2007/09/23 09:49:18 espie Exp $
+.\" $OpenBSD: tutorial.ms,v 1.13 2007/11/02 17:27:24 espie Exp $
.\" $NetBSD: tutorial.ms,v 1.3 1996/03/06 00:15:31 christos Exp $
.\" Copyright (c) 1988, 1989 by Adam de Boor
.\" Copyright (c) 1989 by Berkeley Softworks
@@ -1765,56 +1765,6 @@ SYSTEM = <command.mk>
#include $(SYSTEM)
.DE
won't work.
-.xH 2 Saving Commands
-.No
-.LP
-.Ix 0 def ...
-There may come a time when you will want to save certain commands to
-be executed when everything else is done. For instance: you're
-making several different libraries at one time and you want to create the
-members in parallel. Problem is,
-.CW ranlib
-is another one of those programs that can't be run more than once in
-the same directory at the same time (each one creates a file called
-.CW __.SYMDEF
-into which it stuffs information for the linker to use. Two of them
-running at once will overwrite each other's file and the result will
-be garbage for both parties). You might want a way to save the ranlib
-commands til the end so they can be run one after the other, thus
-keeping them from trashing each other's file. Make allows you to do
-this by inserting an ellipsis (``.\|.\|.'') as a command between
-commands to be run at once and those to be run later.
-.LP
-So for the
-.CW ranlib
-case above, you might do this:
-.Rd 5
-.DS
-lib1.a : $(LIB1OBJS)
- rm -f $(.TARGET)
- ar cr $(.TARGET) $(.ALLSRC)
- ...
- ranlib $(.TARGET)
-
-lib2.a : $(LIB2OBJS)
- rm -f $(.TARGET)
- ar cr $(.TARGET) $(.ALLSRC)
- ...
- ranlib $(.TARGET)
-.DE
-.Ix 0 ref variable local .TARGET
-.Ix 0 ref variable local .ALLSRC
-This would save both
-.DS
-ranlib $(.TARGET)
-.DE
-commands until the end, when they would run one after the other
-(using the correct value for the
-.CW .TARGET
-variable, of course).
-.LP
-Commands saved in this manner are only executed if Make manages to
-re-create everything without an error.
.xH 2 Target Attributes
.LP
Make allows you to give attributes to targets by means of special
diff --git a/usr.bin/make/arch.c b/usr.bin/make/arch.c
index 7306b691df5..8a5b60e9c0e 100644
--- a/usr.bin/make/arch.c
+++ b/usr.bin/make/arch.c
@@ -1,5 +1,5 @@
/* $OpenPackages$ */
-/* $OpenBSD: arch.c,v 1.71 2007/09/17 12:50:59 espie Exp $ */
+/* $OpenBSD: arch.c,v 1.72 2007/11/02 17:27:24 espie Exp $ */
/* $NetBSD: arch.c,v 1.17 1996/11/06 17:58:59 christos Exp $ */
/*
@@ -934,26 +934,10 @@ Arch_MemMTime(GNode *gn)
return gn->mtime;
}
-/* If the system can handle the -L flag when linking (or we cannot find
- * the library), we assume that the user has placed the .LIBRARIES variable
- * in the final linking command (or the linker will know where to find it)
- * and set the TARGET variable for this node to be the node's name. Otherwise,
- * we set the TARGET variable to be the full path of the library,
- * as returned by Dir_FindFile.
- */
+/* we assume the system knows how to find libraries */
void
-Arch_FindLib(GNode *gn, Lst path)
+Arch_FindLib(GNode *gn, Lst path UNUSED)
{
- char *libName; /* file name for archive */
- size_t length = strlen(gn->name) + 6 - 2;
-
- libName = emalloc(length);
- snprintf(libName, length, "lib%s.a", &gn->name[2]);
-
- gn->path = Dir_FindFile(libName, path);
-
- free(libName);
-
Varq_Set(TARGET_INDEX, gn->name, gn);
}
diff --git a/usr.bin/make/compat.c b/usr.bin/make/compat.c
index bd5c6d841a1..8fc82f13a6f 100644
--- a/usr.bin/make/compat.c
+++ b/usr.bin/make/compat.c
@@ -1,5 +1,5 @@
/* $OpenPackages$ */
-/* $OpenBSD: compat.c,v 1.62 2007/10/27 08:44:12 espie Exp $ */
+/* $OpenBSD: compat.c,v 1.63 2007/11/02 17:27:24 espie Exp $ */
/* $NetBSD: compat.c,v 1.14 1996/11/06 17:59:01 christos Exp $ */
/*
@@ -36,18 +36,10 @@
* SUCH DAMAGE.
*/
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <ctype.h>
-#include <errno.h>
#include <limits.h>
#include <signal.h>
-#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
#include "config.h"
#include "defines.h"
#include "dir.h"
@@ -57,272 +49,12 @@
#include "var.h"
#include "targ.h"
#include "error.h"
-#include "str.h"
#include "extern.h"
-#include "memory.h"
#include "gnode.h"
-#include "make.h"
#include "timestamp.h"
#include "lst.h"
-#include "pathnames.h"
-static void CompatInterrupt(int);
-static int CompatRunCommand(LstNode, void *);
static void CompatMake(void *, void *);
-static int run_gnode(GNode *);
-static void setup_meta(void);
-static char **recheck_command_for_shell(char **);
-static void run_command(const char *, bool);
-
-static volatile sig_atomic_t interrupted;
-
-static void
-CompatInterrupt(int signo)
-{
- if (interrupted != SIGINT)
- interrupted = signo;
-}
-
-/* The following array is used to make a fast determination of which
- * characters are interpreted specially by the shell. If a command
- * contains any of these characters, it is executed by the shell, not
- * directly by us. */
-static char meta[256];
-
-static void
-setup_meta(void)
-{
- char *p;
-
- for (p = "#=|^(){};&<>*?[]:$`\\\n"; *p != '\0'; p++)
- meta[(unsigned char) *p] = 1;
- /* The null character serves as a sentinel in the string. */
- meta[0] = 1;
-}
-
-static char **
-recheck_command_for_shell(char **av)
-{
- char *runsh[] = {
- "alias", "cd", "eval", "exit", "read", "set", "ulimit",
- "unalias", "unset", "wait", "umask", NULL
- };
-
- char **p;
-
- /* optimization: if exec cmd, we avoid the intermediate shell */
- if (strcmp(av[0], "exec") == 0)
- av++;
-
- for (p = runsh; *p; p++)
- if (strcmp(av[0], *p) == 0)
- return NULL;
-
- return av;
-}
-
-static void
-run_command(const char *cmd, bool errCheck)
-{
- const char *p;
- char *shargv[4];
- char **todo;
-
- shargv[0] = _PATH_BSHELL;
-
- shargv[1] = errCheck ? "-ec" : "-c";
- shargv[2] = (char *)cmd;
- shargv[3] = NULL;
-
- todo = shargv;
-
-
- /* Search for meta characters in the command. If there are no meta
- * characters, there's no need to execute a shell to execute the
- * command. */
- for (p = cmd; !meta[(unsigned char)*p]; p++)
- continue;
- if (*p == '\0') {
- char *bp;
- char **av;
- int argc;
- /* No meta-characters, so probably no need to exec a shell.
- * Break the command into words to form an argument vector
- * we can execute. */
- av = brk_string(cmd, &argc, &bp);
- av = recheck_command_for_shell(av);
- if (av != NULL)
- todo = av;
- }
- execvp(todo[0], todo);
-
- if (errno == ENOENT)
- fprintf(stderr, "%s: not found\n", todo[0]);
- else
- perror(todo[0]);
- _exit(1);
-}
-
-/*-
- *-----------------------------------------------------------------------
- * CompatRunCommand --
- * Execute the next command for a target. If the command returns an
- * error, the node's made field is set to ERROR and creation stops.
- *
- * Results:
- * 0 in case of error, 1 if ok.
- *
- * Side Effects:
- * The node's 'made' field may be set to ERROR.
- *-----------------------------------------------------------------------
- */
-static int
-CompatRunCommand(LstNode cmdNode, /* Command to execute */
- void *gnp) /* Node from which the command came */
-{
- char *cmdStart; /* Start of expanded command */
- 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 */
- char *cmd = (char *)Lst_Datum(cmdNode);
- GNode *gn = (GNode *)gnp;
-
- silent = gn->type & OP_SILENT;
- errCheck = !(gn->type & OP_IGNORE);
- doExecute = !noExecute;
-
- cmdStart = Var_Subst(cmd, &gn->context, false);
-
- /* 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 (*cmdStart == '\0') {
- free(cmdStart);
- Error("%s expands to empty string", cmd);
- return 1;
- } else
- cmd = cmdStart;
-
- if ((gn->type & OP_SAVE_CMDS) && (gn != end_node)) {
- Lst_AtEnd(&end_node->commands, cmdStart);
- end_node->type &= ~OP_DUMMY;
- return 1;
- } else if (strcmp(cmdStart, "...") == 0) {
- gn->type |= OP_SAVE_CMDS;
- 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;
-
- /* 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;
- }
- free(cmdStart);
-
- /* The child is off and running. Now all we can do is wait... */
- while (1) {
-
- while ((stat = wait(&reason)) != cpid) {
- if (stat == -1 && errno != EINTR)
- break;
- }
-
- if (interrupted)
- break;
-
- if (stat != -1) {
- if (WIFSTOPPED(reason))
- status = WSTOPSIG(reason); /* stopped */
- else 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->made = 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: %d", stat);
- /*NOTREACHED*/
- }
-
- /* This is reached only if interrupted */
- if (!Targ_Precious(gn)) {
- char *file = Varq_Value(TARGET_INDEX, gn);
-
- if (!noExecute && eunlink(file) != -1)
- Error("*** %s removed\n", file);
- }
- if (interrupted == SIGINT) {
- signal(SIGINT, SIG_IGN);
- signal(SIGTERM, SIG_IGN);
- signal(SIGHUP, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- interrupted = 0;
- run_gnode(interrupt_node);
- exit(SIGINT);
- }
- exit(interrupted);
-}
-
-static int
-run_gnode(GNode *gn)
-{
- if (gn != NULL && (gn->type & OP_DUMMY) == 0) {
- Lst_ForEachNodeWhile(&gn->commands, CompatRunCommand, gn);
- return gn->made;
- } else
- return NOSUCHNODE;
-}
/*-
*-----------------------------------------------------------------------
@@ -340,6 +72,8 @@ CompatMake(void *gnp, /* The node to make */
GNode *gn = (GNode *)gnp;
GNode *pgn = (GNode *)pgnp;
+ look_harder_for_target(gn);
+
if (pgn->type & OP_MADE) {
(void)Dir_MTime(gn);
gn->made = UPTODATE;
@@ -406,7 +140,7 @@ CompatMake(void *gnp, /* The node to make */
/* Our commands are ok, but we still have to worry
* about the -t flag... */
if (!touchFlag)
- run_gnode(gn);
+ run_gnode(gn, 0);
else
Job_Touch(gn, gn->type & OP_SILENT);
} else
@@ -495,16 +229,11 @@ 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 */
- signal(SIGINT, CompatInterrupt);
- signal(SIGTERM, CompatInterrupt);
- signal(SIGHUP, CompatInterrupt);
- signal(SIGQUIT, CompatInterrupt);
-
- setup_meta();
+ setup_engine();
/* If the user has defined a .BEGIN target, execute the commands
* attached to it. */
if (!queryFlag) {
- if (run_gnode(begin_node) == ERROR) {
+ if (run_gnode(begin_node, 0) == ERROR) {
printf("\n\nStop.\n");
exit(1);
}
@@ -535,5 +264,5 @@ Compat_Run(Lst targs) /* List of target nodes to re-create */
/* If the user has defined a .END target, run its commands. */
if (errors == 0)
- run_gnode(end_node);
+ run_gnode(end_node, 0);
}
diff --git a/usr.bin/make/defines.h b/usr.bin/make/defines.h
index 2a926e71236..e2cb2181ac3 100644
--- a/usr.bin/make/defines.h
+++ b/usr.bin/make/defines.h
@@ -2,7 +2,7 @@
#define DEFINES_H
/* $OpenPackages$ */
-/* $OpenBSD: defines.h,v 1.3 2007/07/08 17:44:20 espie Exp $ */
+/* $OpenBSD: defines.h,v 1.4 2007/11/02 17:27:24 espie Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
@@ -89,6 +89,7 @@ extern int debug;
#define DEBUG_VAR 0x0200
#define DEBUG_FOR 0x0400
#define DEBUG_LOUD 0x0800
+#define DEBUG_JOBTOKEN 0x1000
#define CONCAT(a,b) a##b
diff --git a/usr.bin/make/engine.c b/usr.bin/make/engine.c
index 4dba6633604..f49d9845937 100644
--- a/usr.bin/make/engine.c
+++ b/usr.bin/make/engine.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: engine.c,v 1.9 2007/09/17 12:42:09 espie Exp $ */
+/* $OpenBSD: engine.c,v 1.10 2007/11/02 17:27:24 espie Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
* Copyright (c) 1988, 1989 by Adam de Boor
@@ -33,12 +33,17 @@
* SUCH DAMAGE.
*/
+#include <sys/types.h>
+#include <sys/wait.h>
#include <limits.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
+#include <signal.h>
#include "config.h"
#include "defines.h"
#include "dir.h"
@@ -51,10 +56,22 @@
#include "lst.h"
#include "timestamp.h"
#include "make.h"
+#include "pathnames.h"
+#include "error.h"
+#include "str.h"
+#include "memory.h"
static void MakeTimeStamp(void *, void *);
static void MakeAddAllSrc(void *, void *);
static int rewrite_time(const char *);
+static void setup_signal(int);
+static void setup_all_signals(void);
+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 void handle_compat_interrupts(GNode *);
bool
Job_CheckCommands(GNode *gn, void (*abortProc)(char *, ...))
@@ -381,3 +398,347 @@ Make_OODate(GNode *gn)
return oodate;
}
+volatile sig_atomic_t got_signal;
+
+volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT,
+ got_SIGTERM, got_SIGTSTP, got_SIGTTOU, got_SIGTTIN, got_SIGWINCH;
+
+static void
+setup_signal(int sig)
+{
+ if (signal(sig, SIG_IGN) != SIG_IGN) {
+ (void)signal(sig, SigHandler);
+ }
+}
+
+void
+setup_all_signals()
+{
+ /*
+ * 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);
+ /*
+ * There are additional signals that need to be caught and passed if
+ * either the export system wants to be told directly of signals or if
+ * we're giving each job its own process group (since then it won't get
+ * signals from the terminal driver as we own the terminal)
+ */
+#if defined(USE_PGRP)
+ setup_signal(SIGTSTP);
+ setup_signal(SIGTTOU);
+ setup_signal(SIGTTIN);
+ setup_signal(SIGWINCH);
+#endif
+}
+
+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;
+#ifdef USE_PGRP
+ 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;
+#endif
+ }
+}
+
+/* The following array is used to make a fast determination of which
+ * characters are interpreted specially by the shell. If a command
+ * contains any of these characters, it is executed by the shell, not
+ * directly by us. */
+static char meta[256];
+
+void
+setup_meta(void)
+{
+ char *p;
+
+ for (p = "#=|^(){};&<>*?[]:$`\\\n"; *p != '\0'; p++)
+ meta[(unsigned char) *p] = 1;
+ /* The null character serves as a sentinel in the string. */
+ meta[0] = 1;
+}
+
+static char **
+recheck_command_for_shell(char **av)
+{
+ char *runsh[] = {
+ "alias", "cd", "eval", "exit", "read", "set", "ulimit",
+ "unalias", "unset", "wait", "umask", NULL
+ };
+
+ char **p;
+
+ /* optimization: if exec cmd, we avoid the intermediate shell */
+ if (strcmp(av[0], "exec") == 0)
+ av++;
+
+ for (p = runsh; *p; p++)
+ if (strcmp(av[0], *p) == 0)
+ return NULL;
+
+ return av;
+}
+
+static void
+run_command(const char *cmd, bool errCheck)
+{
+ const char *p;
+ char *shargv[4];
+ char **todo;
+
+ shargv[0] = _PATH_BSHELL;
+
+ shargv[1] = errCheck ? "-ec" : "-c";
+ shargv[2] = (char *)cmd;
+ shargv[3] = NULL;
+
+ todo = shargv;
+
+
+ /* Search for meta characters in the command. If there are no meta
+ * characters, there's no need to execute a shell to execute the
+ * command. */
+ for (p = cmd; !meta[(unsigned char)*p]; p++)
+ continue;
+ if (*p == '\0') {
+ char *bp;
+ char **av;
+ int argc;
+ /* No meta-characters, so probably no need to exec a shell.
+ * Break the command into words to form an argument vector
+ * we can execute. */
+ av = brk_string(cmd, &argc, &bp);
+ av = recheck_command_for_shell(av);
+ if (av != NULL)
+ todo = av;
+ }
+ execvp(todo[0], todo);
+
+ if (errno == ENOENT)
+ fprintf(stderr, "%s: not found\n", todo[0]);
+ else
+ perror(todo[0]);
+ _exit(1);
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * setup_and_run_command --
+ * Execute the next command for a target. If the command returns an
+ * error, the node's made field is set to ERROR and creation stops.
+ *
+ * Results:
+ * 0 in case of error, 1 if ok.
+ *
+ * Side Effects:
+ * The node's 'made' field may be set to ERROR.
+ *-----------------------------------------------------------------------
+ */
+static int
+setup_and_run_command(char *cmd, GNode *gn, int dont_fork)
+{
+ char *cmdStart; /* Start of expanded command */
+ 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;
+
+ cmdStart = Var_Subst(cmd, &gn->context, false);
+
+ /* 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 (*cmdStart == '\0') {
+ free(cmdStart);
+ Error("%s expands to empty string", cmd);
+ return 1;
+ } else
+ cmd = cmdStart;
+
+ 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;
+ }
+ free(cmdStart);
+
+ /* The child is off and running. Now all we can do is wait... */
+ while (1) {
+
+ while ((stat = wait(&reason)) != cpid) {
+ if (stat == -1 && errno != EINTR)
+ break;
+ }
+
+ if (got_signal)
+ break;
+
+ if (stat != -1) {
+ if (WIFSTOPPED(reason))
+ status = WSTOPSIG(reason); /* stopped */
+ else 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->made = 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: %d", stat);
+ /*NOTREACHED*/
+ }
+ return 0;
+}
+
+static void
+handle_compat_interrupts(GNode *gn)
+{
+ if (!Targ_Precious(gn)) {
+ char *file = Varq_Value(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, 0);
+ exit(255);
+ }
+ exit(255);
+}
+
+int
+run_gnode(GNode *gn, int parallel)
+{
+ LstNode ln, nln;
+
+ if (gn != NULL && (gn->type & OP_DUMMY) == 0) {
+ gn->made = MADE;
+ for (ln = Lst_First(&gn->commands); ln != NULL; ln = nln) {
+ nln = Lst_Adv(ln);
+ if (setup_and_run_command(Lst_Datum(ln), gn,
+ parallel && nln == NULL) == 0)
+ break;
+ }
+ if (got_signal && !parallel)
+ handle_compat_interrupts(gn);
+ return gn->made;
+ } else
+ return NOSUCHNODE;
+}
+
+void
+setup_engine()
+{
+ static int already_setup = 0;
+
+ if (!already_setup) {
+ setup_meta();
+ setup_all_signals();
+ already_setup = 1;
+ }
+}
diff --git a/usr.bin/make/engine.h b/usr.bin/make/engine.h
index 969d244dac4..68fc47aa7a2 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.2 2007/09/17 09:28:36 espie Exp $ */
+/* $OpenBSD: engine.h,v 1.3 2007/11/02 17:27:24 espie Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -65,4 +65,14 @@ 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;
+
+extern void SigHandler(int);
+extern int run_gnode(GNode *, int);
+
+extern void setup_engine(void);
+
#endif
diff --git a/usr.bin/make/error.c b/usr.bin/make/error.c
index d8cab775d30..314b6d7a04e 100644
--- a/usr.bin/make/error.c
+++ b/usr.bin/make/error.c
@@ -1,5 +1,5 @@
/* $OpenPackages$ */
-/* $OpenBSD: error.c,v 1.13 2007/09/17 09:28:36 espie Exp $ */
+/* $OpenBSD: error.c,v 1.14 2007/11/02 17:27:24 espie Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
@@ -29,12 +29,15 @@
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
+#include <sys/types.h>
+#include <unistd.h>
#include "config.h"
#include "defines.h"
#include "error.h"
#include "job.h"
#include "targ.h"
+#include "var.h"
#include "lowparse.h"
@@ -132,7 +135,16 @@ DieHorribly(void)
void
Finish(int errors) /* number of errors encountered in Make_Make */
{
- Fatal("%d error%s", errors, errors == 1 ? "" : "s");
+ Job_Wait();
+ if (errors != 0) {
+ Error("make pid #%ld: %d error%s in directory %s:", (long)getpid(),
+ errors, errors == 1 ? "" : "s",
+ Var_Value(".CURDIR"));
+ }
+ print_errors();
+ if (DEBUG(GRAPH2))
+ Targ_PrintGraph(2);
+ exit(2); /* Not 1 so -q can distinguish error */
}
diff --git a/usr.bin/make/gnode.h b/usr.bin/make/gnode.h
index 795a87842db..8317182d34e 100644
--- a/usr.bin/make/gnode.h
+++ b/usr.bin/make/gnode.h
@@ -1,7 +1,7 @@
#ifndef GNODE_H
#define GNODE_H
/* $OpenPackages$ */
-/* $OpenBSD: gnode.h,v 1.6 2007/09/23 09:47:56 espie Exp $ */
+/* $OpenBSD: gnode.h,v 1.7 2007/11/02 17:27:24 espie Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
@@ -196,8 +196,8 @@ struct GNode_ {
#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should.
* Used when parsing to catch multiple
* commands for a target */
-#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */
#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */
+#define OP_RESOLVED 0x01000000 /* We looked harder already */
/*
* OP_NOP will return true if the node with the given type was not the
diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c
index 41112050ecb..24d6c710aa2 100644
--- a/usr.bin/make/job.c
+++ b/usr.bin/make/job.c
@@ -1,5 +1,5 @@
/* $OpenPackages$ */
-/* $OpenBSD: job.c,v 1.100 2007/10/14 09:02:10 espie Exp $ */
+/* $OpenBSD: job.c,v 1.101 2007/11/02 17:27:24 espie Exp $ */
/* $NetBSD: job.c,v 1.16 1996/11/06 17:59:08 christos Exp $ */
/*
@@ -142,61 +142,34 @@
* 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 */
- LstNode tailCmds; /* The node of the first command to be
- * saved when the job has been run */
- FILE *cmdFILE; /* When creating the shell script, this is
- * where the commands go */
- int rmtID; /* ID returned from Rmt module */
short flags; /* Flags to control treatment of job */
#define JOB_IGNERR 0x001 /* Ignore non-zero exits */
#define JOB_SILENT 0x002 /* no output */
#define JOB_SPECIAL 0x004 /* Target is a special one. */
-#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing
- * commands */
#define JOB_RESTART 0x080 /* Job needs to be completely restarted */
#define JOB_RESUME 0x100 /* Job needs to be resumed b/c it stopped,
* for some reason */
#define JOB_CONTINUING 0x200 /* We are in the process of resuming this job.
* Used to avoid infinite recursion between
* JobFinish and JobRestart */
- int inPipe; /* Input side of pipe associated
- * with job's output channel */
- int outPipe; /* Output side of pipe associated with
- * job's output channel */
- char outBuf[JOB_BUFSIZE + 1];
- /* Buffer for storing the output of the
- * job, line by line */
- int curPos; /* Current position in op_outBuf */
+ struct job_pipe in[2];
} Job;
-/*
- * error handling variables
- */
-static int errors = 0; /* number of errors reported */
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 */
#define ABORT_WAIT 3 /* Waiting for jobs to finish */
-static int numCommands; /* The number of commands actually printed
- * for a target. Should this number be
- * 0, no shell will be executed. */
-
-#define SHELL_ECHO_OFF "set -"
-#define SHELL_ECHO_ON "set -v"
-#define SHELL_ERROR_ON "set -e"
-#define SHELL_ERROR_OFF "set +e"
-#define SHELL_ECHO_FLAG "v"
-#define SHELL_ERROR_FLAG "e"
-
-static const char *shellPath = _PATH_BSHELL;
-static const char *shellName = "sh";
-
-
static int maxJobs; /* The most children we can run at once */
static int nJobs; /* The number of children currently running */
static LIST runningJobs; /* The structures that describe them */
@@ -207,14 +180,6 @@ static fd_set *outputsp; /* Set of descriptors of pipes connected to
static int outputsn;
static GNode *lastNode; /* The node for which output was most recently
* produced. */
-static char *targFmt; /* Format string to use to head output from a
- * job when it's not the most-recent job heard
- * from */
-
-# define TARG_FMT "--- %s ---\n" /* Default format */
-# define MESSAGE(fp, gn) \
- (void)fprintf(fp, targFmt, gn->name);
-
/*
* When JobStart attempts to run a job but isn't allowed to,
* the job is placed on the queuedJobs queue to be run
@@ -222,6 +187,13 @@ static char *targFmt; /* Format string to use to head output from a
*/
static LIST stoppedJobs;
static LIST queuedJobs;
+static LIST errorsList;
+static int errors;
+struct error_info {
+ int status;
+ char *name;
+};
+
#if defined(USE_PGRP) && defined(SYSV)
@@ -255,93 +227,68 @@ static LIST queuedJobs;
static void pass_signal_to_job(void *, void *);
-static void SigHandler(int);
static void handle_all_signals(void);
static void handle_signal(int);
static int JobCmpPid(void *, void *);
-static int JobPrintCommand(LstNode, void *);
-static void JobSaveCommand(void *, void *);
static void JobClose(Job *);
-static void JobFinish(Job *, int *);
-static void JobExec(Job *, char **);
-static void JobMakeArgv(Job *, char **);
+static void JobFinish(Job *, int);
+static void JobExec(Job *);
static void JobRestart(Job *);
static void JobStart(GNode *, int);
-static char *JobOutput(Job *, char *, char *, int);
-static void JobDoOutput(Job *, bool);
static void JobInterrupt(int, int);
static void JobRestartJobs(void);
-static void DBPRINTF(Job *, const char *, ...);
static void debug_printf(const char *, ...);
-static FILE *new_command_file(void);
-static void setup_signal(int);
-static void setup_all_signals(void);
static Job *prepare_job(GNode *, int);
static void start_queued_job(Job *);
+static void token(Job *, FILE *);
+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 prepare_pipe(struct job_pipe *, int *);
+static void handle_job_output(Job *, int, bool);
+static void register_error(int, Job *);
-static volatile sig_atomic_t got_signal;
-
-static volatile sig_atomic_t got_SIGINT, got_SIGHUP, got_SIGQUIT,
- got_SIGTERM, got_SIGTSTP, got_SIGTTOU, got_SIGTTIN, got_SIGWINCH;
-
-
-#define TMPPAT "/tmp/makeXXXXXXXXXX"
-
-static FILE *
-new_command_file()
+static void
+register_error(int status, Job *job)
{
- int fd;
- FILE *f;
- char tmp[] = TMPPAT;
+ struct error_info *p;
+
+ errors++;
+ p = emalloc(sizeof(struct error_info));
+ p->status = status;
+ p->name = job->node->name;
+ if (p)
+ Lst_AtEnd(&errorsList, p);
+}
- fd = mkstemp(tmp);
- if (fd == -1)
- return NULL;
- f = fdopen(fd, "w");
- if (f == NULL)
- close(fd);
- eunlink(tmp);
- return f;
+void
+print_errors()
+{
+ LstNode ln;
+ struct error_info *p;
+
+ for (ln = Lst_First(&errorsList); ln != NULL; ln = Lst_Adv(ln)) {
+ p = (struct error_info *)Lst_Datum(ln);
+ if (WIFEXITED(p->status)) {
+ Error("\tExit status %d in target %s",
+ WEXITSTATUS(p->status), p->name);
+ } else if (WIFSIGNALED(p->status)) {
+ Error("\tReceived signal %d in target s",
+ WTERMSIG(p->status), p->name);
+ } else {
+ Error("\tStatus %d in target %s", p->status, p->name);
+ }
+ }
}
static void
-SigHandler(int sig)
+token(Job *job, FILE *out)
{
- 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;
-#ifdef USE_PGRP
- 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;
-#endif
+ if (job->node != lastNode) {
+ if (DEBUG(JOBTOKEN))
+ (void)fprintf(out, "--- %s ---\n", job->node->name);
+ lastNode = job->node;
}
}
@@ -506,19 +453,6 @@ JobCmpPid(void *job, /* job to examine */
}
static void
-DBPRINTF(Job *job, const char *fmt, ...)
-{
- va_list va;
- va_start(va, fmt);
- if (DEBUG(JOB)) {
- (void)vfprintf(stdout, fmt, va);
- fflush(stdout);
- }
- vfprintf(job->cmdFILE, fmt, va);
- va_end(va);
-}
-
-static void
debug_printf(const char *fmt, ...)
{
if (DEBUG(JOB)) {
@@ -533,156 +467,6 @@ debug_printf(const char *fmt, ...)
/*-
*-----------------------------------------------------------------------
- * JobPrintCommand --
- * Put out another command for the given job. If the command starts
- * with an @ or a - we process it specially. In the former case,
- * so long as the -s and -n flags weren't given to make, we stick
- * a shell-specific echoOff command in the script. In the latter,
- * we ignore errors for the entire job, unless the shell has error
- * control.
- * If the command is just "..." we take all future commands for this
- * job to be commands to be executed once the entire graph has been
- * made and return non-zero to signal that the end of the commands
- * was reached. These commands are later attached to the end_node
- * node and executed by Job_End when all things are done.
- * This function is called from JobStart via Lst_Find
- *
- * Results:
- * Always 1, unless the command was "..."
- *
- * Side Effects:
- * If the command begins with a '-' and the shell has no error control,
- * the JOB_IGNERR flag is set in the job descriptor.
- * If the command is "..." and we're not ignoring such things,
- * tailCmds is set to the successor node of the cmd.
- * numCommands is incremented if the command is actually printed.
- *-----------------------------------------------------------------------
- */
-static int
-JobPrintCommand(LstNode cmdNode, /* command string to print */
- void *jobp) /* job for which to print it */
-{
- bool noSpecials; /* true if we shouldn't worry about
- * inserting special commands into
- * the input stream. */
- bool shutUp = false; /* true if we put a no echo command
- * into the command file */
- bool errOff = false; /* true if we turned error checking
- * off before printing the command
- * and need to turn it back on */
- char *cmdTemplate; /* Template to use when printing the
- * command */
- char *cmdStart; /* Start of expanded command */
- char *cmd = (char *)Lst_Datum(cmdNode);
- Job *job = (Job *)jobp;
-
- noSpecials = (noExecute && !(job->node->type & OP_MAKE));
-
- if (strcmp(cmd, "...") == 0) {
- job->node->type |= OP_SAVE_CMDS;
- if ((job->flags & JOB_IGNDOTS) == 0) {
- job->tailCmds = Lst_Succ(cmdNode);
- return 0;
- }
- return 1;
- }
-
-
- numCommands++;
-
- /* For debugging, we replace each command with the result of expanding
- * the variables in the command. */
- cmdStart = cmd = Var_Subst(cmd, &job->node->context, false);
-
- cmdTemplate = "%s\n";
-
- /*
- * Check for leading @' and -'s to control echoing and error checking.
- */
- for (;; cmd++) {
- if (*cmd == '@')
- shutUp = DEBUG(LOUD) ? false : true;
- else if (*cmd == '-')
- errOff = true;
- else if (*cmd != '+')
- break;
- }
-
- while (isspace(*cmd))
- cmd++;
-
- if (shutUp) {
- if (!(job->flags & JOB_SILENT) && !noSpecials) {
- DBPRINTF(job, "%s\n", SHELL_ECHO_OFF);
- } else {
- shutUp = false;
- }
- }
-
- if (errOff) {
- if ( !(job->flags & JOB_IGNERR) && !noSpecials) {
- /*
- * we don't want the error-control commands showing
- * up either, so we turn off echoing while executing
- * them. We could put another field in the shell
- * structure to tell JobDoOutput to look for this
- * string too, but why make it any more complex than
- * it already is?
- */
- if (!(job->flags & JOB_SILENT) && !shutUp) {
- DBPRINTF(job, "%s; %s; %s\n", SHELL_ECHO_OFF,
- SHELL_ERROR_OFF, SHELL_ECHO_ON);
- } else {
- DBPRINTF(job, "%s\n", SHELL_ERROR_OFF);
- }
- } else {
- errOff = false;
- }
- }
-
- DBPRINTF(job, cmdTemplate, cmd);
-
- if (errOff) {
- /*
- * If echoing is already off, there's no point in issuing the
- * echoOff command. Otherwise we issue it and pretend it was on
- * for the whole command...
- */
- if (!shutUp && !(job->flags & JOB_SILENT)) {
- DBPRINTF(job, "%s\n", SHELL_ECHO_OFF);
- shutUp = true;
- }
- DBPRINTF(job, "%s\n", SHELL_ERROR_ON);
- }
- if (shutUp) {
- DBPRINTF(job, "%s\n", SHELL_ECHO_ON);
- }
- return 1;
-}
-
-/*-
- *-----------------------------------------------------------------------
- * JobSaveCommand --
- * Save a command to be executed when everything else is done.
- * Callback function for JobFinish...
- *
- * Side Effects:
- * The command is tacked onto the end of end_node's commands list.
- *-----------------------------------------------------------------------
- */
-static void
-JobSaveCommand(void *cmd, void *gn)
-{
- GNode *g = (GNode *)gn;
- char *result;
-
- result = Var_Subst((char *)cmd, &g->context, false);
- Lst_AtEnd(&end_node->commands, result);
-}
-
-
-/*-
- *-----------------------------------------------------------------------
* JobClose --
* Called to close both input and output pipes when a job is finished.
*
@@ -693,12 +477,13 @@ JobSaveCommand(void *cmd, void *gn)
static void
JobClose(Job *job)
{
- FD_CLR(job->inPipe, outputsp);
- if (job->outPipe != job->inPipe) {
- (void)close(job->outPipe);
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ FD_CLR(job->in[i].fd, outputsp);
+ handle_job_output(job, i, true);
+ (void)close(job->in[i].fd);
}
- JobDoOutput(job, true);
- (void)close(job->inPipe);
}
/*-
@@ -717,20 +502,19 @@ JobClose(Job *job)
*
* If we got an error and are aborting (aborting == ABORT_ERROR) and
* the job list is now empty, we are done for the day.
- * If we recognized an error (errors !=0), we set the aborting flag
+ * If we recognized an error we set the aborting flag
* to ABORT_ERROR so no more jobs will be started.
*-----------------------------------------------------------------------
*/
/*ARGSUSED*/
static void
-JobFinish(Job *job, /* job to finish */
- int *status) /* sub-why job went away */
+JobFinish(Job *job, int status)
{
bool done;
- if ((WIFEXITED(*status) &&
- WEXITSTATUS(*status) != 0 && !(job->flags & JOB_IGNERR)) ||
- (WIFSIGNALED(*status) && WTERMSIG(*status) != SIGCONT)) {
+ if ((WIFEXITED(status) &&
+ WEXITSTATUS(status) != 0 && !(job->flags & JOB_IGNERR)) ||
+ (WIFSIGNALED(status) && WTERMSIG(status) != SIGCONT)) {
/*
* If it exited non-zero and either we're doing things our
* way or we're not ignoring errors, the job is finished.
@@ -740,11 +524,8 @@ JobFinish(Job *job, /* job to finish */
* status...
*/
JobClose(job);
- if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
- (void)fclose(job->cmdFILE);
- }
done = true;
- } else if (WIFEXITED(*status)) {
+ } else if (WIFEXITED(status)) {
/*
* Deal with ignored errors in -B mode. We need to print a
* message telling of the ignored error as well as setting
@@ -752,7 +533,7 @@ JobFinish(Job *job, /* job to finish */
* this, we set done to be true if in -B mode and the job
* exited non-zero.
*/
- done = WEXITSTATUS(*status) != 0;
+ done = WEXITSTATUS(status) != 0;
/*
* Old comment said: "Note we don't want to close down any of
* the streams until we know we're at the end." But we do.
@@ -767,49 +548,36 @@ JobFinish(Job *job, /* job to finish */
}
if (done ||
- WIFSTOPPED(*status) ||
- (WIFSIGNALED(*status) && WTERMSIG(*status) == SIGCONT) ||
+ WIFSTOPPED(status) ||
+ (WIFSIGNALED(status) && WTERMSIG(status) == SIGCONT) ||
DEBUG(JOB)) {
- FILE *out;
-
- out = stdout;
-
- if (WIFEXITED(*status)) {
+ if (WIFEXITED(status)) {
debug_printf("Process %ld exited.\n", (long)job->pid);
- if (WEXITSTATUS(*status) != 0) {
- if (job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- (void)fprintf(out, "*** Error code %d%s\n",
- WEXITSTATUS(*status),
+ if (WEXITSTATUS(status) != 0) {
+ token(job, stdout);
+ (void)fprintf(stdout, "*** Error code %d%s\n",
+ WEXITSTATUS(status),
(job->flags & JOB_IGNERR) ? "(ignored)" :
"");
if (job->flags & JOB_IGNERR) {
- *status = 0;
+ status = 0;
}
} else if (DEBUG(JOB)) {
- if (job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- (void)fprintf(out,
+ token(job, stdout);
+ (void)fprintf(stdout,
"*** Completed successfully\n");
}
- } else if (WIFSTOPPED(*status)) {
+ } else if (WIFSTOPPED(status)) {
debug_printf("Process %ld stopped.\n", (long)job->pid);
- if (job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- (void)fprintf(out, "*** Stopped -- signal %d\n",
- WSTOPSIG(*status));
+ token(job, stdout);
+ (void)fprintf(stdout, "*** Stopped -- signal %d\n",
+ WSTOPSIG(status));
job->flags |= JOB_RESUME;
Lst_AtEnd(&stoppedJobs, job);
- (void)fflush(out);
+ (void)fflush(stdout);
return;
- } else if (WTERMSIG(*status) == SIGCONT) {
+ } else if (WTERMSIG(status) == SIGCONT) {
/*
* If the beastie has continued, shift the Job from the
* stopped list to the running one (or re-stop it if
@@ -817,11 +585,8 @@ JobFinish(Job *job, /* job to finish */
* child.
*/
if (job->flags & (JOB_RESUME|JOB_RESTART)) {
- if (job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- (void)fprintf(out, "*** Continued\n");
+ token(job, stdout);
+ (void)fprintf(stdout, "*** Continued\n");
}
if (!(job->flags & JOB_CONTINUING)) {
debug_printf(
@@ -848,18 +613,15 @@ JobFinish(Job *job, /* job to finish */
jobFull = true;
debug_printf("Job queue is full.\n");
}
- (void)fflush(out);
+ (void)fflush(stdout);
return;
} else {
- if (job->node != lastNode) {
- MESSAGE(out, job->node);
- lastNode = job->node;
- }
- (void)fprintf(out, "*** Signal %d\n",
- WTERMSIG(*status));
+ token(job, stdout);
+ (void)fprintf(stdout, "*** Signal %d\n",
+ WTERMSIG(status));
}
- (void)fflush(out);
+ (void)fflush(stdout);
}
done = true;
@@ -867,17 +629,15 @@ JobFinish(Job *job, /* job to finish */
if (done &&
aborting != ABORT_ERROR &&
aborting != ABORT_INTERRUPT &&
- *status == 0) {
+ status == 0) {
/* 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. In addition, any saved
- * commands for the node are placed on the .END target. */
- Lst_ForEachFrom(job->tailCmds, JobSaveCommand, job->node);
+ * Make_Update to update the parents. */
job->node->made = MADE;
Make_Update(job->node);
free(job);
- } else if (*status != 0) {
- errors++;
+ } else if (status != 0) {
+ register_error(status, job);
free(job);
}
@@ -886,7 +646,8 @@ JobFinish(Job *job, /* job to finish */
/*
* Set aborting if any error.
*/
- if (errors && !keepgoing && aborting != ABORT_INTERRUPT) {
+ if (errors && !keepgoing &&
+ aborting != ABORT_INTERRUPT) {
/*
* If we found any errors in this batch of children and the -k
* flag wasn't given, we set the aborting flag so no more jobs
@@ -903,6 +664,33 @@ JobFinish(Job *job, /* job to finish */
}
}
+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 (outputsp == NULL || p->fd > outputsn) {
+ int fdn, ofdn;
+ fd_set *tmp;
+
+ fdn = howmany(p->fd+1, NFDBITS);
+ ofdn = outputsn ? howmany(outputsn+1, NFDBITS) : 0;
+
+ if (fdn != ofdn) {
+ tmp = recalloc(outputsp, fdn, sizeof(fd_mask));
+ if (tmp == NULL)
+ return;
+ outputsp = tmp;
+ }
+ outputsn = p->fd;
+ }
+ fcntl(p->fd, F_SETFL, O_NONBLOCK);
+ FD_SET(p->fd, outputsp);
+}
+
/*-
*-----------------------------------------------------------------------
* JobExec --
@@ -915,20 +703,17 @@ JobFinish(Job *job, /* job to finish */
*-----------------------------------------------------------------------
*/
static void
-JobExec(Job *job, char **argv)
+JobExec(Job *job)
{
pid_t cpid; /* ID of new child */
- static int signals_caught = 0;
+ int fds[4];
+ int *fdout = fds;
+ int *fderr = fds+2;
+ int result;
+ int i;
if (DEBUG(JOB)) {
- int i;
-
(void)fprintf(stdout, "Running %s\n", job->node->name);
- (void)fprintf(stdout, "\tCommand: ");
- for (i = 0; argv[i] != NULL; i++) {
- (void)fprintf(stdout, "%s ", argv[i]);
- }
- (void)fprintf(stdout, "\n");
(void)fflush(stdout);
}
@@ -938,46 +723,33 @@ JobExec(Job *job, char **argv)
* banner with their name in it never appears). This is an attempt to
* provide that feedback, even if nothing follows it.
*/
- if (lastNode != job->node && !(job->flags & JOB_SILENT)) {
- MESSAGE(stdout, job->node);
- lastNode = job->node;
- }
+ token(job, stdout);
- if (!signals_caught) {
- signals_caught = 1;
- setup_all_signals();
- }
+ setup_engine();
+
+ /* 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));
if ((cpid = fork()) == -1) {
Punt("Cannot fork");
} else if (cpid == 0) {
- /*
- * Must duplicate the input stream down to the child's input
- * and reset it to the beginning (again). Since the stream was
- * marked close-on-exec, we must clear that bit in the new
- * input.
- */
- if (dup2(fileno(job->cmdFILE), 0) == -1)
- Punt("Cannot dup2(job->cmdFile): %s", strerror(errno));
- (void)fcntl(0, F_SETFD, 0);
- (void)lseek(0, 0, SEEK_SET);
-
- /*
- * Set up the child's output to be routed through the pipe
- * we've created for it.
- */
- if (dup2(job->outPipe, 1) == -1)
- Punt("Cannot dup2(job->outPipe): %s", strerror(errno));
- /*
- * The output channels are marked close on exec. This bit was
- * duplicated by the dup2 (on some systems), so we have to
- * clear it before routing the shell's error output to the same
- * place as its standard output.
- */
- (void)fcntl(1, F_SETFD, 0);
- if (dup2(1, 2) == -1)
- Punt("Cannot dup2(stdout): %s", strerror(errno));
+ /* 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]);
#ifdef USE_PGRP
/*
@@ -992,43 +764,26 @@ JobExec(Job *job, char **argv)
# endif
#endif /* USE_PGRP */
- (void)execv(shellPath, argv);
-
- (void)write(STDERR_FILENO, "Could not execute shell\n",
- sizeof("Could not execute shell"));
- _exit(1);
+ /* most cases won't return, but will exit directly */
+ result = run_gnode(job->node, 1);
+ switch(result) {
+ case MADE:
+ exit(0);
+ case ERROR:
+ exit(1);
+ default:
+ fprintf(stderr,
+ "Could not run gnode, returned %d\n", result);
+ exit(1);
+ }
} else {
job->pid = cpid;
- /* we set the current position in the buffer to the beginning
+ /* we set the current position in the buffers to the beginning
* and mark another stream to watch in the outputs mask
*/
- job->curPos = 0;
-
- if (outputsp == NULL || job->inPipe > outputsn) {
- int fdn, ofdn;
- fd_set *tmp;
-
- fdn = howmany(job->inPipe+1, NFDBITS);
- ofdn = outputsn ? howmany(outputsn+1, NFDBITS) : 0;
-
- if (fdn != ofdn) {
- tmp = recalloc(outputsp, fdn, sizeof(fd_mask));
- if (tmp == NULL)
- return;
- outputsp = tmp;
- }
- outputsn = job->inPipe;
- }
- FD_SET(job->inPipe, outputsp);
-
- /*
- * XXX: Used to not happen if REMOTE. Why?
- */
- if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
- (void)fclose(job->cmdFILE);
- job->cmdFILE = NULL;
- }
+ for (i = 0; i < 2; i++)
+ prepare_pipe(&job->in[i], fds+2*i);
}
/*
@@ -1041,48 +796,9 @@ JobExec(Job *job, char **argv)
}
}
-/*-
- *-----------------------------------------------------------------------
- * JobMakeArgv --
- * Create the argv needed to execute the shell for a given job.
- *-----------------------------------------------------------------------
- */
-static void
-JobMakeArgv(Job *job, char **argv)
-{
- int argc;
- static char args[10]; /* For merged arguments */
-
- argv[0] = (char *)shellName;
- argc = 1;
-
- (void)snprintf(args, sizeof(args), "-%s%s",
- (job->flags & JOB_IGNERR) ? "" : SHELL_ERROR_FLAG,
- (job->flags & JOB_SILENT) ? "" : SHELL_ECHO_FLAG);
-
- if (args[1]) {
- argv[argc] = args;
- argc++;
- }
- argv[argc] = NULL;
-}
-
static void
start_queued_job(Job *job)
{
- /*
- * Set up the control arguments to the shell. This is based on
- * the flags set earlier for this job. If the JOB_IGNERR flag
- * is clear, the 'exit' flag of the commandShell is used to
- * cause it to exit upon receiving an error. If the JOB_SILENT
- * flag is clear, the 'echo' flag of the commandShell is used
- * to get it to start echoing as soon as it starts processing
- * commands.
- */
- char *argv[4];
-
- JobMakeArgv(job, argv);
-
if (DEBUG(JOB)) {
(void)fprintf(stdout, "Restarting %s...",
job->node->name);
@@ -1105,7 +821,7 @@ start_queued_job(Job *job)
*/
debug_printf("running locally\n");
}
- JobExec(job, argv);
+ JobExec(job);
}
/*-
@@ -1135,7 +851,7 @@ JobRestart(Job *job)
* (or maxJobs is 0), it's ok to resume the job.
*/
bool error;
- int status;
+ int status = 0;
error = KILL(job->pid, SIGCONT) != 0;
@@ -1147,16 +863,15 @@ JobRestart(Job *job)
*/
job->flags |= JOB_CONTINUING;
W_SETTERMSIG(&status, SIGCONT);
- JobFinish(job, &status);
+ JobFinish(job, status);
job->flags &= ~(JOB_RESUME|JOB_CONTINUING);
debug_printf("done\n");
} else {
Error("couldn't resume %s: %s",
job->node->name, strerror(errno));
- status = 0;
W_SETEXITSTATUS(&status, 1);
- JobFinish(job, &status);
+ JobFinish(job, status);
}
} else {
/*
@@ -1184,7 +899,6 @@ prepare_job(GNode *gn, int flags)
}
job->node = gn;
- job->tailCmds = NULL;
/*
* Set the initial value of the flags for this job based on the global
@@ -1220,66 +934,23 @@ prepare_job(GNode *gn, int flags)
DieHorribly();
}
- job->cmdFILE = new_command_file();
- if (job->cmdFILE == NULL) {
- Punt("Error creating command file");
- }
- (void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC);
- /*
- * Send the commands to the command file, flush all its buffers
- * then rewind and remove the thing.
- */
- noExec = false;
-
- /*
- * We can do all the commands at once. hooray for
- * sanity
- */
- numCommands = 0;
- Lst_ForEachNodeWhile(&gn->commands, JobPrintCommand,
- job);
-
- /*
- * If we didn't print out any commands to the shell
- * script, there's not much point in executing the
- * shell, is there?
- */
- if (numCommands == 0) {
+ if (Lst_IsEmpty(&gn->commands))
noExec = true;
- }
+ else
+ noExec = false;
+
} else if (noExecute) {
- /*
- * Not executing anything -- just print all the commands to
- * stdout in one fell swoop. This will still set up
- * job->tailCmds correctly.
- */
- if (lastNode != gn) {
- MESSAGE(stdout, gn);
- lastNode = gn;
- }
- job->cmdFILE = stdout;
- /*
- * Only print the commands if they're ok, but don't die if
- * they're not -- just let the user know they're bad and keep
- * going. It doesn't do any harm in this case and may do some
- * good.
- */
- if (cmdsOK) {
- Lst_ForEachNodeWhile(&gn->commands, JobPrintCommand,
- job);
- }
- /*
- * Don't execute the shell, thank you.
- */
- noExec = true;
+ if (!cmdsOK || Lst_IsEmpty(&gn->commands))
+ noExec = true;
+ else
+ noExec = false;
} else {
/*
* Just touch the target and note that no shell should be
- * executed. Set cmdFILE to stdout to make life easier. Check
+ * 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->cmdFILE = stdout;
Job_Touch(gn, job->flags & JOB_SILENT);
noExec = true;
}
@@ -1289,30 +960,17 @@ prepare_job(GNode *gn, int flags)
*/
if (noExec) {
/*
- * Unlink and close the command file if we opened one
- */
- if (job->cmdFILE != stdout) {
- if (job->cmdFILE != NULL)
- (void)fclose(job->cmdFILE);
- } else {
- (void)fflush(stdout);
- }
-
- /*
* 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) {
if (aborting == 0) {
- Lst_ForEachFrom(job->tailCmds, JobSaveCommand,
- job->node);
Make_Update(job->node);
}
}
free(job);
return NULL;
} else {
- (void)fflush(job->cmdFILE);
return job;
}
}
@@ -1334,26 +992,9 @@ JobStart(GNode *gn, /* target to create */
* e.g. JOB_SPECIAL */
{
Job *job;
- char *argv[4]; /* Argument vector to shell */
- int fd[2];
job = prepare_job(gn, flags);
if (!job)
return;
- /* Create the pipe by which we'll get the shell's output.
- */
- if (pipe(fd) == -1)
- Punt("Cannot create pipe: %s", strerror(errno));
- job->inPipe = fd[0];
- job->outPipe = fd[1];
- (void)fcntl(job->inPipe, F_SETFD, FD_CLOEXEC);
- (void)fcntl(job->outPipe, F_SETFD, FD_CLOEXEC);
-
- /*
- * Set up the control arguments to the shell. This is based on the flags
- * set earlier for this job.
- */
- JobMakeArgv(job, argv);
-
if (nJobs >= maxJobs && !(job->flags & JOB_SPECIAL) &&
maxJobs != 0) {
/*
@@ -1379,201 +1020,112 @@ JobStart(GNode *gn, /* target to create */
jobFull = true;
debug_printf("Local job queue is full.\n");
}
- JobExec(job, argv);
+ JobExec(job);
}
}
-static char *
-JobOutput(Job *job, char *cp, char *endp, int msg)
+/* Helper functions for JobDoOutput */
+
+
+/* output debugging token 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;
+
+ token(job, out);
+ for (i = 0; i < endPos; i++)
+ putc(p->buffer[i], out);
+}
+
+/* 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)
{
- char *ecp;
-
- ecp = strstr(cp, SHELL_ECHO_OFF);
- while (ecp != NULL) {
- if (cp != ecp) {
- *ecp = '\0';
- if (msg && job->node != lastNode) {
- MESSAGE(stdout, job->node);
- lastNode = job->node;
- }
- /*
- * The only way there wouldn't be a newline after
- * this line is if it were the last in the buffer.
- * however, since the non-printable comes after it,
- * there must be a newline, so we don't print one.
- */
- (void)fprintf(stdout, "%s", cp);
- (void)fflush(stdout);
- }
- cp = ecp + strlen(SHELL_ECHO_OFF);
- if (cp != endp) {
- /*
- * Still more to print, look again after skipping
- * the whitespace following the non-printable
- * command....
- */
- cp++;
- while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
- cp++;
- }
- ecp = strstr(cp, SHELL_ECHO_OFF);
- } else {
- return cp;
+ 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)
+{
+ 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;
}
}
- return cp;
+ return false;
}
-
/*-
*-----------------------------------------------------------------------
- * JobDoOutput --
+ * 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 a line, we print it tagged by the job's
+ * job's outBuf. If this makes up lines, we print it tagged by the job's
* identifier, as necessary.
- * We also keep our figurative eye out for the
- * 'noPrint' line for the shell from which the output came. If
- * we recognize a line, we don't print it. If the command is not
- * alone on the line (the character after it is not \0 or \n), we
- * do print whatever follows it.
*
* Side Effects:
* curPos may be shifted as may the contents of outBuf.
*-----------------------------------------------------------------------
*/
static void
-JobDoOutput(Job *job, /* the job whose output needs printing */
- bool finish) /* true if this is the last time we'll be
- * called for this job */
+handle_pipe(struct job_pipe *p,
+ Job *job, FILE *out, bool finish)
{
- bool gotNL = false; /* true if got a newline */
- bool fbuf; /* true if our buffer filled up */
int nr; /* number of bytes read */
- int i; /* auxiliary index into outBuf */
- int max; /* limit for i (end of current data) */
- int nRead; /* (Temporary) number of bytes read */
-
- /*
- * Read as many bytes as will fit in the buffer.
- */
-end_loop:
- gotNL = false;
- fbuf = false;
-
- nRead = read(job->inPipe, &job->outBuf[job->curPos],
- JOB_BUFSIZE - job->curPos);
- if (nRead == -1) {
- if (DEBUG(JOB)) {
- perror("JobDoOutput(piperead)");
- }
- nr = 0;
- } else {
- nr = nRead;
- }
-
- /*
- * If we hit the end-of-file (the job is dead), we must flush its
- * remaining output, so pretend we read a newline if there's any
- * output remaining in the buffer.
- * Also clear the 'finish' flag so we stop looping.
- */
- if (nr == 0 && job->curPos != 0) {
- job->outBuf[job->curPos] = '\n';
- nr = 1;
- finish = false;
- } else if (nr == 0) {
- finish = false;
- }
-
- /*
- * Look for the last newline in the bytes we just got. If there is
- * one, break out of the loop with 'i' as its index and gotNL set
- * true.
- */
- max = job->curPos + nr;
- for (i = job->curPos + nr - 1; i >= job->curPos; i--) {
- if (job->outBuf[i] == '\n') {
- gotNL = true;
- break;
- } else if (job->outBuf[i] == '\0') {
- /*
- * To be fixed: don't use printf, it stops at NUL bytes.
- */
- job->outBuf[i] = ' ';
- }
- }
-
- if (!gotNL) {
- job->curPos += nr;
- if (job->curPos == JOB_BUFSIZE) {
- /*
- * If we've run out of buffer space, we have no choice
- * but to print the stuff. sigh.
- */
- fbuf = true;
- i = job->curPos;
- }
- }
- if (gotNL || fbuf) {
- /*
- * Need to send the output to the screen. Null terminate it
- * first, overwriting the newline character if there was one.
- * So long as the line isn't one we should filter (according
- * to the shell description), we print the line, preceded
- * by a target banner if this target isn't the same as the
- * one for which we last printed something.
- * The rest of the data in the buffer are then shifted down
- * to the start of the buffer and curPos is set accordingly.
- */
- job->outBuf[i] = '\0';
- if (i >= job->curPos) {
- char *cp;
-
- cp = JobOutput(job, job->outBuf, &job->outBuf[i],
- false);
-
- /*
- * There's still more in that thar buffer. This time,
- * though, we know there's no newline at the end, so we
- * add one of our own free will.
- */
- if (*cp != '\0') {
- if (job->node != lastNode) {
- MESSAGE(stdout, job->node);
- lastNode = job->node;
- }
- (void)fprintf(stdout, "%s%s", cp,
- gotNL ? "\n" : "");
- (void)fflush(stdout);
+ 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)");
}
}
- if (i < max - 1) {
- /* shift the remaining characters down */
- (void)memcpy(job->outBuf, &job->outBuf[i + 1],
- max - (i + 1));
- job->curPos = max - (i + 1);
+ 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);
- } else {
- /*
- * We have written everything out, so we just start over
- * from the start of the buffer. No copying. No nothing.
- */
- job->curPos = 0;
- }
- }
- if (finish) {
- /*
- * If the finish flag is true, we must loop until we hit
- * end-of-file on the pipe. This is guaranteed to happen
- * eventually since the other end of the pipe is now closed
- * (we closed it explicitly and the child has exited). When
- * we do get an EOF, finish will be set false and we'll fall
- * through and out.
- */
- goto end_loop;
+ /* 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)
+{
+ handle_pipe(&job->in[i], job, i == 0 ? stdout : stderr, finish);
+}
+
/*-
*-----------------------------------------------------------------------
* Job_CatchChildren --
@@ -1633,7 +1185,7 @@ Job_CatchChildren()
jobFull = false;
}
- JobFinish(job, &status);
+ JobFinish(job, status);
}
}
@@ -1657,6 +1209,7 @@ Job_CatchOutput(void)
struct timeval timeout;
LstNode ln;
Job *job;
+ int i;
int count = howmany(outputsn+1, NFDBITS) * sizeof(fd_mask);
fd_set *readfdsp = malloc(count);
@@ -1675,9 +1228,11 @@ Job_CatchOutput(void)
for (ln = Lst_First(&runningJobs); nfds && ln != NULL;
ln = Lst_Adv(ln)) {
job = (Job *)Lst_Datum(ln);
- if (FD_ISSET(job->inPipe, readfdsp)) {
- JobDoOutput(job, false);
- nfds--;
+ for (i = 0; i < 2; i++) {
+ if (FD_ISSET(job->in[i].fd, readfdsp)) {
+ handle_job_output(job, i, false);
+ nfds--;
+ }
}
}
}
@@ -1700,39 +1255,6 @@ Job_Make(GNode *gn)
(void)JobStart(gn, 0);
}
-static void
-setup_signal(int sig)
-{
- if (signal(sig, SIG_IGN) != SIG_IGN) {
- (void)signal(sig, SigHandler);
- }
-}
-
-static void
-setup_all_signals()
-{
- /*
- * 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);
- /*
- * There are additional signals that need to be caught and passed if
- * either the export system wants to be told directly of signals or if
- * we're giving each job its own process group (since then it won't get
- * signals from the terminal driver as we own the terminal)
- */
-#if defined(USE_PGRP)
- setup_signal(SIGTSTP);
- setup_signal(SIGTTOU);
- setup_signal(SIGTTIN);
- setup_signal(SIGWINCH);
-#endif
-}
-
/*-
*-----------------------------------------------------------------------
* Job_Init --
@@ -1748,25 +1270,16 @@ Job_Init(int maxproc)
Static_Lst_Init(&runningJobs);
Static_Lst_Init(&stoppedJobs);
Static_Lst_Init(&queuedJobs);
+ Static_Lst_Init(&errorsList);
maxJobs = maxproc;
nJobs = 0;
jobFull = false;
+ errors = 0;
aborting = 0;
- errors = 0;
lastNode = NULL;
- if (maxJobs == 1) {
- /*
- * If only one job can run at a time, there's no need for a
- * banner, no is there?
- */
- targFmt = "";
- } else {
- targFmt = TARG_FMT;
- }
-
if ((begin_node->type & OP_DUMMY) == 0) {
JobStart(begin_node, JOB_SPECIAL);
while (nJobs) {
@@ -1867,7 +1380,7 @@ JobInterrupt(int runINTERRUPT, /* Non-zero if commands for the .INTERRUPT
if ((interrupt_node->type & OP_DUMMY) == 0) {
ignoreErrors = false;
- JobStart(interrupt_node, JOB_IGNDOTS);
+ JobStart(interrupt_node, 0);
while (nJobs) {
Job_CatchOutput();
Job_CatchChildren();
@@ -1895,7 +1408,7 @@ Job_Finish(void)
if (errors) {
Error("Errors reported so .END ignored");
} else {
- JobStart(end_node, JOB_SPECIAL | JOB_IGNDOTS);
+ JobStart(end_node, JOB_SPECIAL);
while (nJobs) {
Job_CatchOutput();
diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h
index 24a3f1ad7a1..d93199d0d44 100644
--- a/usr.bin/make/job.h
+++ b/usr.bin/make/job.h
@@ -2,7 +2,7 @@
#define _JOB_H_
/* $OpenPackages$ */
-/* $OpenBSD: job.h,v 1.20 2007/09/23 09:44:39 espie Exp $ */
+/* $OpenBSD: job.h,v 1.21 2007/11/02 17:27:24 espie Exp $ */
/* $NetBSD: job.h,v 1.5 1996/11/06 17:59:10 christos Exp $ */
/*
@@ -60,5 +60,6 @@ extern void Job_End(void);
#endif
extern void Job_Wait(void);
extern void Job_AbortAll(void);
+extern void print_errors(void);
#endif /* _JOB_H_ */
diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c
index 7b9c1538def..ef2ef8dde6b 100644
--- a/usr.bin/make/main.c
+++ b/usr.bin/make/main.c
@@ -1,5 +1,5 @@
/* $OpenPackages$ */
-/* $OpenBSD: main.c,v 1.86 2007/10/24 13:19:24 espie Exp $ */
+/* $OpenBSD: main.c,v 1.87 2007/11/02 17:27:24 espie Exp $ */
/* $NetBSD: main.c,v 1.34 1997/03/24 20:56:36 gwr Exp $ */
/*
@@ -258,6 +258,9 @@ MainParseArgs(int argc, char **argv)
case 'j':
debug |= DEBUG_JOB;
break;
+ case 'J':
+ debug |= DEBUG_JOBTOKEN;
+ break;
case 'l':
debug |= DEBUG_LOUD;
break;
@@ -678,6 +681,7 @@ main(int argc, char **argv)
Dir_AddDir(defaultPath, d.current);
Var_Set(".CURDIR", d.current);
Var_Set(".OBJDIR", d.object);
+ Targ_setdirs(d.current, d.object);
/*
* Initialize various variables.
diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1
index 70a814a60fa..e17a6551446 100644
--- a/usr.bin/make/make.1
+++ b/usr.bin/make/make.1
@@ -1,4 +1,4 @@
-.\" $OpenBSD: make.1,v 1.70 2007/07/09 14:47:29 jmc Exp $
+.\" $OpenBSD: make.1,v 1.71 2007/11/02 17:27:24 espie Exp $
.\" $OpenPackages$
.\" $NetBSD: make.1,v 1.18 1997/03/10 21:19:53 christos Exp $
.\"
@@ -31,7 +31,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd $Mdocdate: July 9 2007 $
+.Dd $Mdocdate: November 2 2007 $
.Dt MAKE 1
.Os
.Sh NAME
@@ -165,6 +165,8 @@ Print the input graph after making everything, or before exiting
on error.
.It Ar j
Print debugging information about running multiple shells.
+.It Ar J
+Print job tokens showing which output corresponds to what job.
.It Ar l
Print commands in Makefile targets regardless of whether or not they are
prefixed by @.
@@ -1409,7 +1411,3 @@ A+=$I
.Pp
.Sq A
will evaluate to a b c d after the loop, not z b c d.
-.Pp
-The
-.Ql +
-command modificator is ignored in parallel make mode.
diff --git a/usr.bin/make/make.c b/usr.bin/make/make.c
index a2ee3b138d9..417a0996278 100644
--- a/usr.bin/make/make.c
+++ b/usr.bin/make/make.c
@@ -1,5 +1,5 @@
/* $OpenPackages$ */
-/* $OpenBSD: make.c,v 1.42 2007/09/17 12:01:17 espie Exp $ */
+/* $OpenBSD: make.c,v 1.43 2007/11/02 17:27:24 espie Exp $ */
/* $NetBSD: make.c,v 1.10 1996/11/06 17:59:15 christos Exp $ */
/*
@@ -59,6 +59,7 @@
#include <limits.h>
#include <stdio.h>
+#include <signal.h>
#include "config.h"
#include "defines.h"
#include "dir.h"
@@ -72,6 +73,7 @@
#include "timestamp.h"
#include "engine.h"
#include "lst.h"
+#include "targ.h"
static LIST toBeMade; /* The current fringe of the graph. These
* are nodes which await examination by
@@ -166,12 +168,8 @@ Make_Update(GNode *cgn) /* the child node */
* the Dir_MTime occurs, thus leading us to believe that the
* file is unchanged, wreaking havoc with files that depend
* on this one.
- * XXX If we are saving commands pretend that
- * the target is made now. Otherwise archives with ... rules
- * don't work!
*/
- if (noExecute || (cgn->type & OP_SAVE_CMDS) ||
- is_out_of_date(Dir_MTime(cgn)))
+ if (noExecute || is_out_of_date(Dir_MTime(cgn)))
cgn->mtime = now;
if (DEBUG(MAKE))
printf("update time: %s\n", time_to_string(cgn->mtime));
@@ -409,6 +407,7 @@ Make_Run(Lst targs) /* the initial list of targets */
gn->make = true;
numNodes++;
+ look_harder_for_target(gn);
/*
* Apply any .USE rules before looking for implicit
* dependencies to make sure everything that should have
diff --git a/usr.bin/make/suff.c b/usr.bin/make/suff.c
index a42bf8f004b..2a3e33ecda8 100644
--- a/usr.bin/make/suff.c
+++ b/usr.bin/make/suff.c
@@ -1,5 +1,5 @@
/* $OpenPackages$ */
-/* $OpenBSD: suff.c,v 1.72 2007/09/18 09:15:04 espie Exp $ */
+/* $OpenBSD: suff.c,v 1.73 2007/11/02 17:27:24 espie Exp $ */
/* $NetBSD: suff.c,v 1.13 1996/11/06 17:59:25 christos Exp $ */
/*
@@ -79,6 +79,7 @@
#include <stdint.h>
#include <stddef.h>
#include <string.h>
+#include <signal.h>
#include <ohash.h>
#include "config.h"
#include "defines.h"
diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c
index fdc86106f3c..27e678bef8a 100644
--- a/usr.bin/make/targ.c
+++ b/usr.bin/make/targ.c
@@ -1,5 +1,5 @@
/* $OpenPackages$ */
-/* $OpenBSD: targ.c,v 1.48 2007/09/18 08:51:23 espie Exp $ */
+/* $OpenBSD: targ.c,v 1.49 2007/11/02 17:27:24 espie Exp $ */
/* $NetBSD: targ.c,v 1.11 1997/02/20 16:51:50 christos Exp $ */
/*
@@ -443,3 +443,49 @@ Targ_PrintGraph(int pass) /* Which pass this is. 1 => no processing
#endif
Suff_PrintAll();
}
+
+static char *curdir, *objdir;
+static size_t curdir_len, objdir_len;
+
+void
+Targ_setdirs(const char *c, const char *o)
+{
+ curdir_len = strlen(c);
+ curdir = emalloc(curdir_len+2);
+ memcpy(curdir, c, curdir_len);
+ curdir[curdir_len++] = '/';
+ curdir[curdir_len] = 0;
+
+ objdir_len = strlen(o);
+ objdir = emalloc(objdir_len+2);
+ memcpy(objdir, o, objdir_len);
+ objdir[objdir_len++] = '/';
+ objdir[objdir_len] = 0;
+}
+
+void
+look_harder_for_target(GNode *gn)
+{
+ GNode *extra, *cgn;
+ LstNode ln;
+
+ if (gn->type & OP_RESOLVED)
+ return;
+ gn->type |= OP_RESOLVED;
+ if (strncmp(gn->name, objdir, objdir_len) == 0) {
+ extra = Targ_FindNode(gn->name + objdir_len, TARG_NOCREATE);
+ if (extra != NULL) {
+ if (Lst_IsEmpty(&gn->commands))
+ Lst_Concat(&gn->commands, &extra->commands);
+ for (ln = Lst_First(&extra->children); ln != NULL;
+ ln = Lst_Adv(ln)) {
+ cgn = (GNode *)Lst_Datum(ln);
+
+ if (Lst_AddNew(&gn->children, cgn)) {
+ Lst_AtEnd(&cgn->parents, gn);
+ gn->unmade++;
+ }
+ }
+ }
+ }
+}
diff --git a/usr.bin/make/targ.h b/usr.bin/make/targ.h
index db9080165f1..8af62308438 100644
--- a/usr.bin/make/targ.h
+++ b/usr.bin/make/targ.h
@@ -1,7 +1,7 @@
#ifndef TARG_H
#define TARG_H
/* $OpenPackages$ */
-/* $OpenBSD: targ.h,v 1.4 2007/09/18 08:51:23 espie Exp $ */
+/* $OpenBSD: targ.h,v 1.5 2007/11/02 17:27:24 espie Exp $ */
/*
* Copyright (c) 2001 Marc Espie.
@@ -77,4 +77,6 @@ struct ohash_info;
extern struct ohash_info gnode_info;
+extern void look_harder_for_target(GNode *);
+extern void Targ_setdirs(const char *, const char *);
#endif