diff options
author | Marc Espie <espie@cvs.openbsd.org> | 2012-09-21 07:55:21 +0000 |
---|---|---|
committer | Marc Espie <espie@cvs.openbsd.org> | 2012-09-21 07:55:21 +0000 |
commit | 10f160f479b38433f4047a2f25d5aa8f07bc60e6 (patch) | |
tree | 3c0207b7741e4887482742726e4bc1238f03de6b /usr.bin | |
parent | 844d86d53b6754d7a21150eb94823f41ce76becc (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/Makefile | 5 | ||||
-rw-r--r-- | usr.bin/make/buf.c | 21 | ||||
-rw-r--r-- | usr.bin/make/buf.h | 5 | ||||
-rw-r--r-- | usr.bin/make/compat.c | 27 | ||||
-rw-r--r-- | usr.bin/make/defines.h | 12 | ||||
-rw-r--r-- | usr.bin/make/dump.c | 36 | ||||
-rw-r--r-- | usr.bin/make/dump.h | 34 | ||||
-rw-r--r-- | usr.bin/make/engine.c | 449 | ||||
-rw-r--r-- | usr.bin/make/engine.h | 62 | ||||
-rw-r--r-- | usr.bin/make/error.c | 30 | ||||
-rw-r--r-- | usr.bin/make/error.h | 4 | ||||
-rw-r--r-- | usr.bin/make/gnode.h | 9 | ||||
-rw-r--r-- | usr.bin/make/job.c | 1367 | ||||
-rw-r--r-- | usr.bin/make/job.h | 10 | ||||
-rw-r--r-- | usr.bin/make/lowparse.c | 24 | ||||
-rw-r--r-- | usr.bin/make/lowparse.h | 4 | ||||
-rw-r--r-- | usr.bin/make/main.c | 55 | ||||
-rw-r--r-- | usr.bin/make/make.1 | 25 | ||||
-rw-r--r-- | usr.bin/make/parse.c | 46 | ||||
-rw-r--r-- | usr.bin/make/targ.c | 8 | ||||
-rw-r--r-- | usr.bin/make/varmodifiers.c | 4 |
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(>argets, gn); + /* try to find a proper location for a target in a file, by + * filling it repeatedly until the target has commands.. + * This is not perfect for .USE targets... + */ + if ((gn->type & OP_HAS_COMMANDS) == 0) + Parse_FillLocation(&gn->origin); + + Array_AtEnd(>argets, gn); } static void @@ -1040,11 +1046,8 @@ ParseAddCmd(void *gnp, void *cmd) { GNode *gn = (GNode *)gnp; /* if target already supplied, ignore commands */ - if (!(gn->type & OP_HAS_COMMANDS)) { + if (!(gn->type & OP_HAS_COMMANDS)) Lst_AtEnd(&gn->commands, cmd); - if (!gn->origin.lineno) - Parse_FillLocation(&gn->origin); - } } /*- @@ -1441,7 +1444,14 @@ parse_commands(struct growableArray *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++; |