Date: Thu, 11 Feb 2021 06:04:27 GMT From: "Simon J. Gerraty" <sjg@FreeBSD.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org Subject: git: dba7b0ef928a - main - Merge bmake-20210206 Message-ID: <202102110604.11B64RFg078214@gitrepo.freebsd.org>
next in thread | raw e-mail | index | archive | help
The branch main has been updated by sjg: URL: https://cgit.FreeBSD.org/src/commit/?id=dba7b0ef928af88caa38728a73657b837aeeac93 commit dba7b0ef928af88caa38728a73657b837aeeac93 Merge: 2a7d4d954504 8b6f73e37baf Author: Simon J. Gerraty <sjg@FreeBSD.org> AuthorDate: 2021-02-11 06:01:59 +0000 Commit: Simon J. Gerraty <sjg@FreeBSD.org> CommitDate: 2021-02-11 06:03:22 +0000 Merge bmake-20210206 Changes of interest o unit-tests: use private TMPDIR to avoid errors from other users o avoid strdup in mkTempFile o always use vfork o job.c: do not create empty shell files in jobs mode reduce unnecessary calls to waitpid o cond.c: fix debug output for comparison operators in conditionals contrib/bmake/ChangeLog | 73 +++ contrib/bmake/FILES | 6 + contrib/bmake/VERSION | 2 +- contrib/bmake/arch.c | 24 +- contrib/bmake/bmake.1 | 6 +- contrib/bmake/bmake.cat1 | 4 +- contrib/bmake/buf.c | 59 +- contrib/bmake/buf.h | 25 +- contrib/bmake/compat.c | 18 +- contrib/bmake/cond.c | 655 ++++++++++--------- contrib/bmake/config.h.in | 3 + contrib/bmake/configure | 36 +- contrib/bmake/configure.in | 15 +- contrib/bmake/dir.c | 454 +++++++------ contrib/bmake/dir.h | 10 +- contrib/bmake/enum.c | 16 +- contrib/bmake/enum.h | 33 +- contrib/bmake/filemon/filemon.h | 4 +- contrib/bmake/filemon/filemon_dev.c | 6 +- contrib/bmake/filemon/filemon_ktrace.c | 13 +- contrib/bmake/for.c | 253 ++++---- contrib/bmake/hash.c | 13 +- contrib/bmake/job.c | 260 ++++---- contrib/bmake/job.h | 3 +- contrib/bmake/lst.c | 17 +- contrib/bmake/lst.h | 4 +- contrib/bmake/main.c | 316 ++++----- contrib/bmake/make.1 | 6 +- contrib/bmake/make.c | 154 +++-- contrib/bmake/make.h | 104 +-- contrib/bmake/make_malloc.c | 6 +- contrib/bmake/make_malloc.h | 4 +- contrib/bmake/meta.c | 87 +-- contrib/bmake/metachar.c | 7 +- contrib/bmake/metachar.h | 6 +- contrib/bmake/mk/ChangeLog | 9 + contrib/bmake/mk/dirdeps.mk | 10 +- contrib/bmake/mk/install-mk | 4 +- contrib/bmake/mk/meta.stage.mk | 7 +- contrib/bmake/nonints.h | 58 +- contrib/bmake/parse.c | 306 ++++----- contrib/bmake/str.c | 22 +- contrib/bmake/suff.c | 66 +- contrib/bmake/targ.c | 30 +- contrib/bmake/trace.c | 10 +- contrib/bmake/trace.h | 6 +- contrib/bmake/unit-tests/Makefile | 80 ++- .../bmake/unit-tests/cmdline-redirect-stdin.exp | 1 + contrib/bmake/unit-tests/cmdline-redirect-stdin.mk | 34 + contrib/bmake/unit-tests/cmdline.exp | 4 +- contrib/bmake/unit-tests/cmdline.mk | 4 +- contrib/bmake/unit-tests/cond-cmp-numeric-eq.exp | 1 - contrib/bmake/unit-tests/cond-cmp-numeric.exp | 8 +- contrib/bmake/unit-tests/cond-cmp-string.exp | 5 +- contrib/bmake/unit-tests/cond-cmp-string.mk | 30 +- contrib/bmake/unit-tests/cond-func-defined.exp | 6 +- contrib/bmake/unit-tests/cond-func.exp | 9 +- contrib/bmake/unit-tests/cond-op-not.exp | 5 +- contrib/bmake/unit-tests/cond-op-not.mk | 9 +- contrib/bmake/unit-tests/cond-op-parentheses.exp | 6 +- contrib/bmake/unit-tests/cond-op-parentheses.mk | 19 +- contrib/bmake/unit-tests/cond-op.exp | 4 + contrib/bmake/unit-tests/cond-op.mk | 40 +- contrib/bmake/unit-tests/cond-token-plain.exp | 31 +- contrib/bmake/unit-tests/cond-token-plain.mk | 97 ++- contrib/bmake/unit-tests/cond-token-string.exp | 20 +- contrib/bmake/unit-tests/cond-token-string.mk | 47 +- contrib/bmake/unit-tests/cond1.exp | 2 +- contrib/bmake/unit-tests/dir.mk | 4 +- contrib/bmake/unit-tests/directive-error.exp | 5 +- contrib/bmake/unit-tests/directive-error.mk | 13 +- contrib/bmake/unit-tests/directive-export-impl.exp | 8 +- contrib/bmake/unit-tests/directive-for-escape.exp | 43 +- contrib/bmake/unit-tests/directive-for-escape.mk | 28 +- contrib/bmake/unit-tests/directive-ifdef.exp | 2 + contrib/bmake/unit-tests/directive-ifdef.mk | 17 +- .../bmake/unit-tests/directive-include-fatal.mk | 4 +- contrib/bmake/unit-tests/envfirst.mk | 4 +- contrib/bmake/unit-tests/export.exp | 1 + contrib/bmake/unit-tests/gnode-submake.exp | 16 +- contrib/bmake/unit-tests/include-main.exp | 9 +- contrib/bmake/unit-tests/include-main.mk | 6 +- contrib/bmake/unit-tests/include-subsub.mk | 4 +- contrib/bmake/unit-tests/jobs-empty-commands.exp | 2 + contrib/bmake/unit-tests/jobs-empty-commands.mk | 18 + contrib/bmake/unit-tests/lint.mk | 13 +- contrib/bmake/unit-tests/objdir-writable.exp | 6 +- contrib/bmake/unit-tests/opt-debug-graph1.exp | 16 +- contrib/bmake/unit-tests/opt-debug-graph2.exp | 92 ++- contrib/bmake/unit-tests/opt-debug-graph2.mk | 22 +- contrib/bmake/unit-tests/opt-debug-graph3.exp | 92 ++- contrib/bmake/unit-tests/opt-debug-graph3.mk | 22 +- contrib/bmake/unit-tests/opt-no-action-touch.exp | 11 + contrib/bmake/unit-tests/opt-no-action-touch.mk | 48 ++ contrib/bmake/unit-tests/opt-touch-jobs.mk | 8 +- .../bmake/unit-tests/opt-warnings-as-errors.exp | 4 +- contrib/bmake/unit-tests/opt-warnings-as-errors.mk | 7 +- contrib/bmake/unit-tests/suff-incomplete.exp | 4 +- contrib/bmake/unit-tests/suff-main-several.exp | 16 +- contrib/bmake/unit-tests/suff-transform-debug.exp | 2 +- contrib/bmake/unit-tests/var-op-append.exp | 6 +- contrib/bmake/unit-tests/var-op-append.mk | 14 +- contrib/bmake/unit-tests/var-op-assign.exp | 2 +- contrib/bmake/unit-tests/var-op-shell.exp | 4 +- contrib/bmake/unit-tests/var-op-shell.mk | 13 +- contrib/bmake/unit-tests/vardebug.exp | 36 +- contrib/bmake/unit-tests/vardebug.mk | 4 +- contrib/bmake/unit-tests/varmisc.mk | 10 +- contrib/bmake/unit-tests/varmod-assign.mk | 3 +- contrib/bmake/unit-tests/varmod-gmtime.exp | 20 +- contrib/bmake/unit-tests/varmod-gmtime.mk | 19 +- contrib/bmake/unit-tests/varmod-ifelse.mk | 4 +- contrib/bmake/unit-tests/varmod-indirect.exp | 32 +- contrib/bmake/unit-tests/varmod-localtime.exp | 20 +- contrib/bmake/unit-tests/varmod-localtime.mk | 21 +- contrib/bmake/unit-tests/varmod-loop.mk | 6 +- contrib/bmake/unit-tests/varmod-match-escape.exp | 16 +- contrib/bmake/unit-tests/varmod-match-escape.mk | 20 +- contrib/bmake/unit-tests/varname-dot-shell.mk | 6 +- contrib/bmake/unit-tests/varname-empty.exp | 38 +- contrib/bmake/unit-tests/varname-empty.mk | 9 +- .../varname-make_print_var_on_error-jobs.mk | 4 +- .../unit-tests/varname-make_print_var_on_error.mk | 4 +- contrib/bmake/unit-tests/varname.exp | 10 +- contrib/bmake/unit-tests/varparse-dynamic.mk | 6 +- contrib/bmake/util.c | 10 +- contrib/bmake/var.c | 713 ++++++++++++--------- usr.bin/bmake/Makefile.config | 2 +- usr.bin/bmake/config.h | 7 +- usr.bin/bmake/unit-tests/Makefile | 80 ++- 130 files changed, 3225 insertions(+), 2153 deletions(-) diff --cc contrib/bmake/job.c index d43761ca80ff,000000000000..eb5454cde574 mode 100644,000000..100644 --- a/contrib/bmake/job.c +++ b/contrib/bmake/job.c @@@ -1,3016 -1,0 +1,3012 @@@ - /* $NetBSD: job.c,v 1.397 2021/01/10 23:59:53 rillig Exp $ */ ++/* $NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + - /*- ++/* + * job.c -- + * handle the creation etc. of our child processes. + * + * Interface: + * Job_Init Called to initialize this module. In addition, - * any commands attached to the .BEGIN target - * are executed before this function returns. ++ * the .BEGIN target is made including all of its ++ * dependencies before this function returns. + * Hence, the makefiles must have been parsed + * before this function is called. + * + * Job_End Clean up any memory used. + * + * Job_Make Start the creation of the given target. + * + * Job_CatchChildren + * Check for and handle the termination of any + * children. This must be called reasonably + * frequently to keep the whole make going at + * a decent clip, since job table entries aren't + * removed until their process is caught this way. + * + * Job_CatchOutput + * Print any output our children have produced. + * Should also be called fairly frequently to + * keep the user informed of what's going on. + * If no output is waiting, it will block for + * a time given by the SEL_* constants, below, + * or until output is ready. + * - * Job_ParseShell Given the line following a .SHELL target, parse - * the line as a shell specification. Returns - * FALSE if the spec was incorrect. ++ * Job_ParseShell Given a special dependency line with target '.SHELL', ++ * define the shell that is used for the creation ++ * commands in jobs mode. + * + * 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. + * - * Job_AbortAll Abort all currently running jobs. It doesn't - * handle output or do anything for the jobs, - * just kills them. It should only be called in - * an emergency. ++ * Job_AbortAll Abort all currently running jobs. Do not handle ++ * output or do anything for the jobs, just kill them. ++ * Should only be called in an emergency. + * + * Job_CheckCommands + * Verify that the commands for a target are + * ok. Provide them if necessary and possible. + * + * Job_Touch Update a target without really updating it. + * + * Job_Wait Wait for all currently-running jobs to finish. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include "wait.h" + +#include <errno.h> +#if !defined(USE_SELECT) && defined(HAVE_POLL_H) +#include <poll.h> +#else +#ifndef USE_SELECT /* no poll.h */ +# define USE_SELECT +#endif +#if defined(HAVE_SYS_SELECT_H) +# include <sys/select.h> +#endif +#endif +#include <signal.h> +#include <utime.h> +#if defined(HAVE_SYS_SOCKET_H) +# include <sys/socket.h> +#endif + +#include "make.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" + +/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */ - MAKE_RCSID("$NetBSD: job.c,v 1.397 2021/01/10 23:59:53 rillig Exp $"); ++MAKE_RCSID("$NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $"); + +/* + * A shell defines how the commands are run. All commands for a target are + * written into a single file, which is then given to the shell to execute + * the commands from it. The commands are written to the file using a few + * templates for echo control and error control. + * + * The name of the shell is the basename for the predefined shells, such as + * "sh", "csh", "bash". For custom shells, it is the full pathname, and its + * basename is used to select the type of shell; the longest match wins. + * So /usr/pkg/bin/bash has type sh, /usr/local/bin/tcsh has type csh. + * + * The echoing of command lines is controlled using hasEchoCtl, echoOff, + * echoOn, noPrint and noPrintLen. When echoOff is executed by the shell, it + * still outputs something, but this something is not interesting, therefore + * it is filtered out using noPrint and noPrintLen. + * + * The error checking for individual commands is controlled using hasErrCtl, + * errOn, errOff and runChkTmpl. + * - * If a shell doesn't have error control, echoTmpl becomes a printf template - * for echoing the command, should echoing be on; runIgnTmpl becomes - * another printf template for executing the command while ignoring the return ++ * In case a shell doesn't have error control, echoTmpl is a printf template ++ * for echoing the command, should echoing be on; runIgnTmpl is another ++ * printf template for executing the command while ignoring the return + * status. Finally runChkTmpl is a printf template for running the command and + * causing the shell to exit on error. If any of these strings are empty when + * hasErrCtl is FALSE, the command will be executed anyway as is, and if it + * causes an error, so be it. Any templates set up to echo the command will - * escape any '$ ` \ "' characters in the command string to avoid common - * problems with echo "%s\n" as a template. ++ * escape any '$ ` \ "' characters in the command string to avoid unwanted ++ * shell code injection, the escaped command is safe to use in double quotes. + * + * The command-line flags "echo" and "exit" also control the behavior. The + * "echo" flag causes the shell to start echoing commands right away. The + * "exit" flag causes the shell to exit when an error is detected in one of + * the commands. + */ +typedef struct Shell { + + /* + * The name of the shell. For Bourne and C shells, this is used only + * to find the shell description when used as the single source of a + * .SHELL target. For user-defined shells, this is the full path of + * the shell. + */ + const char *name; + + Boolean hasEchoCtl; /* whether both echoOff and echoOn are there */ + const char *echoOff; /* command to turn echoing off */ + const char *echoOn; /* command to turn echoing back on */ + const char *noPrint; /* text to skip when printing output from the + * shell. This is usually the same as echoOff */ + size_t noPrintLen; /* length of noPrint command */ + + Boolean hasErrCtl; /* whether error checking can be controlled + * for individual commands */ + const char *errOn; /* command to turn on error checking */ + const char *errOff; /* command to turn off error checking */ + + const char *echoTmpl; /* template to echo a command */ + const char *runIgnTmpl; /* template to run a command + * without error checking */ + const char *runChkTmpl; /* template to run a command + * with error checking */ + + /* string literal that results in a newline character when it appears + * outside of any 'quote' or "quote" characters */ + const char *newline; + char commentChar; /* character used by shell for comment lines */ + + const char *echoFlag; /* shell flag to echo commands */ + const char *errFlag; /* shell flag to exit on error */ +} Shell; + +typedef struct CommandFlags { - /* Whether to echo the command before running it. */ ++ /* Whether to echo the command before or instead of running it. */ + Boolean echo; + + /* Run the command even in -n or -N mode. */ + Boolean always; + + /* + * true if we turned error checking off before printing the command + * and need to turn it back on + */ + Boolean ignerr; +} CommandFlags; + +/* + * Write shell commands to a file. + * + * TODO: keep track of whether commands are echoed. + * TODO: keep track of whether error checking is active. + */ +typedef struct ShellWriter { + FILE *f; + + /* we've sent 'set -x' */ + Boolean xtraced; + +} ShellWriter; + +/* + * FreeBSD: traditionally .MAKE is not required to + * pass jobs queue to sub-makes. + * Use .MAKE.ALWAYS_PASS_JOB_QUEUE=no to disable. + */ +#define MAKE_ALWAYS_PASS_JOB_QUEUE ".MAKE.ALWAYS_PASS_JOB_QUEUE" +static int Always_pass_job_queue = TRUE; +/* + * FreeBSD: aborting entire parallel make isn't always + * desired. When doing tinderbox for example, failure of + * one architecture should not stop all. + * We still want to bail on interrupt though. + */ +#define MAKE_JOB_ERROR_TOKEN "MAKE_JOB_ERROR_TOKEN" +static int Job_error_token = TRUE; + +/* + * error handling variables + */ +static int job_errors = 0; /* number of errors reported */ +typedef enum AbortReason { /* why is the make aborting? */ + ABORT_NONE, + ABORT_ERROR, /* Because of an error */ + ABORT_INTERRUPT, /* Because it was interrupted */ + ABORT_WAIT /* Waiting for jobs to finish */ ++ /* XXX: "WAIT" is not a _reason_ for aborting, it's rather a status. */ +} AbortReason; +static AbortReason aborting = ABORT_NONE; +#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */ + +/* + * this tracks the number of tokens currently "out" to build jobs. + */ +int jobTokensRunning = 0; + +typedef enum JobStartResult { + JOB_RUNNING, /* Job is running */ + JOB_ERROR, /* Error in starting the job */ + JOB_FINISHED /* The job is already finished */ +} JobStartResult; + +/* + * Descriptions for various shells. + * + * The build environment may set DEFSHELL_INDEX to one of + * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to + * select one of the predefined shells as the default shell. + * + * Alternatively, the build environment may set DEFSHELL_CUSTOM to the + * name or the full path of a sh-compatible shell, which will be used as + * the default shell. + * + * ".SHELL" lines in Makefiles can choose the default shell from the + * set defined here, or add additional shells. + */ + +#ifdef DEFSHELL_CUSTOM +#define DEFSHELL_INDEX_CUSTOM 0 +#define DEFSHELL_INDEX_SH 1 +#define DEFSHELL_INDEX_KSH 2 +#define DEFSHELL_INDEX_CSH 3 +#else /* !DEFSHELL_CUSTOM */ +#define DEFSHELL_INDEX_SH 0 +#define DEFSHELL_INDEX_KSH 1 +#define DEFSHELL_INDEX_CSH 2 +#endif /* !DEFSHELL_CUSTOM */ + +#ifndef DEFSHELL_INDEX +#define DEFSHELL_INDEX 0 /* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */ +#endif /* !DEFSHELL_INDEX */ + +static Shell shells[] = { +#ifdef DEFSHELL_CUSTOM + /* + * An sh-compatible shell with a non-standard name. + * + * Keep this in sync with the "sh" description below, but avoid + * non-portable features that might not be supplied by all + * sh-compatible shells. + */ + { + DEFSHELL_CUSTOM, /* .name */ + FALSE, /* .hasEchoCtl */ + "", /* .echoOff */ + "", /* .echoOn */ + "", /* .noPrint */ + 0, /* .noPrintLen */ + FALSE, /* .hasErrCtl */ + "", /* .errOn */ + "", /* .errOff */ + "echo \"%s\"\n", /* .echoTmpl */ + "%s\n", /* .runIgnTmpl */ + "{ %s \n} || exit $?\n", /* .runChkTmpl */ + "'\n'", /* .newline */ + '#', /* .commentChar */ + "", /* .echoFlag */ + "", /* .errFlag */ + }, +#endif /* DEFSHELL_CUSTOM */ + /* + * SH description. Echo control is also possible and, under + * sun UNIX anyway, one can even control error checking. + */ + { + "sh", /* .name */ + FALSE, /* .hasEchoCtl */ + "", /* .echoOff */ + "", /* .echoOn */ + "", /* .noPrint */ + 0, /* .noPrintLen */ + FALSE, /* .hasErrCtl */ + "", /* .errOn */ + "", /* .errOff */ + "echo \"%s\"\n", /* .echoTmpl */ + "%s\n", /* .runIgnTmpl */ + "{ %s \n} || exit $?\n", /* .runChkTmpl */ + "'\n'", /* .newline */ + '#', /* .commentChar*/ +#if defined(MAKE_NATIVE) && defined(__NetBSD__) + /* XXX: -q is not really echoFlag, it's more like noEchoInSysFlag. */ + "q", /* .echoFlag */ +#else + "", /* .echoFlag */ +#endif + "", /* .errFlag */ + }, + /* + * KSH description. + */ + { + "ksh", /* .name */ + TRUE, /* .hasEchoCtl */ + "set +v", /* .echoOff */ + "set -v", /* .echoOn */ + "set +v", /* .noPrint */ + 6, /* .noPrintLen */ + FALSE, /* .hasErrCtl */ + "", /* .errOn */ + "", /* .errOff */ + "echo \"%s\"\n", /* .echoTmpl */ + "%s\n", /* .runIgnTmpl */ + "{ %s \n} || exit $?\n", /* .runChkTmpl */ + "'\n'", /* .newline */ + '#', /* .commentChar */ + "v", /* .echoFlag */ + "", /* .errFlag */ + }, + /* + * CSH description. The csh can do echo control by playing + * with the setting of the 'echo' shell variable. Sadly, + * however, it is unable to do error control nicely. + */ + { + "csh", /* .name */ + TRUE, /* .hasEchoCtl */ + "unset verbose", /* .echoOff */ + "set verbose", /* .echoOn */ + "unset verbose", /* .noPrint */ + 13, /* .noPrintLen */ + FALSE, /* .hasErrCtl */ + "", /* .errOn */ + "", /* .errOff */ + "echo \"%s\"\n", /* .echoTmpl */ + "csh -c \"%s || exit 0\"\n", /* .runIgnTmpl */ + "", /* .runChkTmpl */ + "'\\\n'", /* .newline */ + '#', /* .commentChar */ + "v", /* .echoFlag */ + "e", /* .errFlag */ + } +}; + +/* + * This is the shell to which we pass all commands in the Makefile. + * It is set by the Job_ParseShell function. + */ +static Shell *shell = &shells[DEFSHELL_INDEX]; +const char *shellPath = NULL; /* full pathname of executable image */ +const char *shellName = NULL; /* last component of shellPath */ +char *shellErrFlag = NULL; - static char *shellArgv = NULL; /* Custom shell args */ ++static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */ + + +static Job *job_table; /* The structures that describe them */ +static Job *job_table_end; /* job_table + maxJobs */ +static unsigned int wantToken; /* we want a token */ +static Boolean lurking_children = FALSE; +static Boolean make_suspended = FALSE; /* Whether we've seen a SIGTSTP (etc) */ + +/* + * Set of descriptors of pipes connected to + * the output channels of children + */ +static struct pollfd *fds = NULL; - static Job **allJobs = NULL; - static nfds_t nJobs = 0; ++static Job **jobByFdIndex = NULL; ++static nfds_t fdsLen = 0; +static void watchfd(Job *); +static void clearfd(Job *); +static Boolean readyfd(Job *); + +static char *targPrefix = NULL; /* To identify a job change in the output. */ +static Job tokenWaitJob; /* token wait pseudo-job */ + +static Job childExitJob; /* child exit pseudo-job */ +#define CHILD_EXIT "." +#define DO_JOB_RESUME "R" + +enum { + npseudojobs = 2 /* number of pseudo-jobs */ +}; + +static sigset_t caught_signals; /* Set of signals we handle */ ++static volatile sig_atomic_t caught_sigchld; + +static void JobDoOutput(Job *, Boolean); +static void JobInterrupt(Boolean, int) MAKE_ATTR_DEAD; +static void JobRestartJobs(void); +static void JobSigReset(void); + +static void +SwitchOutputTo(GNode *gn) +{ + /* The node for which output was most recently produced. */ + static GNode *lastNode = NULL; + + if (gn == lastNode) + return; + lastNode = gn; + + if (opts.maxJobs != 1 && targPrefix != NULL && targPrefix[0] != '\0') + (void)fprintf(stdout, "%s %s ---\n", targPrefix, gn->name); +} + +static unsigned +nfds_per_job(void) +{ +#if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) + if (useMeta) + return 2; +#endif + return 1; +} + +void +Job_FlagsToString(const Job *job, char *buf, size_t bufsize) +{ + snprintf(buf, bufsize, "%c%c%c", + job->ignerr ? 'i' : '-', + !job->echo ? 's' : '-', + job->special ? 'S' : '-'); +} + +static void +job_table_dump(const char *where) +{ + Job *job; + char flags[4]; + + debug_printf("job table @ %s\n", where); + for (job = job_table; job < job_table_end; job++) { + Job_FlagsToString(job, flags, sizeof flags); + debug_printf("job %d, status %d, flags %s, pid %d\n", + (int)(job - job_table), job->status, flags, job->pid); + } +} + +/* + * Delete the target of a failed, interrupted, or otherwise + * unsuccessful job unless inhibited by .PRECIOUS. + */ +static void +JobDeleteTarget(GNode *gn) +{ + const char *file; + + if (gn->type & OP_JOIN) + return; + if (gn->type & OP_PHONY) + return; + if (Targ_Precious(gn)) + return; + if (opts.noExecute) + return; + + file = GNode_Path(gn); + if (eunlink(file) != -1) + Error("*** %s removed", file); +} + +/* + * JobSigLock/JobSigUnlock + * + * Signal lock routines to get exclusive access. Currently used to + * protect `jobs' and `stoppedJobs' list manipulations. + */ +static void +JobSigLock(sigset_t *omaskp) +{ + if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) { + Punt("JobSigLock: sigprocmask: %s", strerror(errno)); + sigemptyset(omaskp); + } +} + +static void +JobSigUnlock(sigset_t *omaskp) +{ + (void)sigprocmask(SIG_SETMASK, omaskp, NULL); +} + +static void +JobCreatePipe(Job *job, int minfd) +{ + int i, fd, flags; + int pipe_fds[2]; + + if (pipe(pipe_fds) == -1) + Punt("Cannot create pipe: %s", strerror(errno)); + + for (i = 0; i < 2; i++) { + /* Avoid using low numbered fds */ + fd = fcntl(pipe_fds[i], F_DUPFD, minfd); + if (fd != -1) { + close(pipe_fds[i]); + pipe_fds[i] = fd; + } + } + + job->inPipe = pipe_fds[0]; + job->outPipe = pipe_fds[1]; + + /* Set close-on-exec flag for both */ + if (fcntl(job->inPipe, F_SETFD, FD_CLOEXEC) == -1) + Punt("Cannot set close-on-exec: %s", strerror(errno)); + if (fcntl(job->outPipe, F_SETFD, FD_CLOEXEC) == -1) + Punt("Cannot set close-on-exec: %s", strerror(errno)); + + /* + * We mark the input side of the pipe non-blocking; we poll(2) the + * pipe when we're waiting for a job token, but we might lose the + * race for the token when a new one becomes available, so the read + * from the pipe should not block. + */ + flags = fcntl(job->inPipe, F_GETFL, 0); + if (flags == -1) + Punt("Cannot get flags: %s", strerror(errno)); + flags |= O_NONBLOCK; + if (fcntl(job->inPipe, F_SETFL, flags) == -1) + Punt("Cannot set flags: %s", strerror(errno)); +} + +/* Pass the signal to each running job. */ +static void +JobCondPassSig(int signo) +{ + Job *job; + + DEBUG1(JOB, "JobCondPassSig(%d) called.\n", signo); + + for (job = job_table; job < job_table_end; job++) { + if (job->status != JOB_ST_RUNNING) + continue; + DEBUG2(JOB, "JobCondPassSig passing signal %d to child %d.\n", + signo, job->pid); + KILLPG(job->pid, signo); + } +} + +/* + * SIGCHLD handler. + * + * Sends a token on the child exit pipe to wake us up from select()/poll(). + */ +/*ARGSUSED*/ +static void +JobChildSig(int signo MAKE_ATTR_UNUSED) +{ ++ caught_sigchld = 1; + while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && + errno == EAGAIN) + continue; +} + + +/* Resume all stopped jobs. */ +/*ARGSUSED*/ +static void +JobContinueSig(int signo MAKE_ATTR_UNUSED) +{ + /* + * Defer sending SIGCONT to our stopped children until we return + * from the signal handler. + */ + while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 && + errno == EAGAIN) + continue; +} + +/* + * Pass a signal on to all jobs, then resend to ourselves. + * We die by the same signal. + */ +MAKE_ATTR_DEAD static void +JobPassSig_int(int signo) +{ + /* Run .INTERRUPT target then exit */ + JobInterrupt(TRUE, signo); +} + +/* + * Pass a signal on to all jobs, then resend to ourselves. + * We die by the same signal. + */ +MAKE_ATTR_DEAD static void +JobPassSig_term(int signo) +{ + /* Dont run .INTERRUPT target then exit */ + JobInterrupt(FALSE, signo); +} + +static void +JobPassSig_suspend(int signo) +{ + sigset_t nmask, omask; + struct sigaction act; + + /* Suppress job started/continued messages */ + make_suspended = TRUE; + + /* Pass the signal onto every job */ + JobCondPassSig(signo); + + /* + * Send ourselves the signal now we've given the message to everyone + * else. Note we block everything else possible while we're getting + * the signal. This ensures that all our jobs get continued when we + * wake up before we take any other signal. + */ + sigfillset(&nmask); + sigdelset(&nmask, signo); + (void)sigprocmask(SIG_SETMASK, &nmask, &omask); + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(signo, &act, NULL); + + DEBUG1(JOB, "JobPassSig passing signal %d to self.\n", signo); + + (void)kill(getpid(), signo); + + /* + * We've been continued. + * + * A whole host of signals continue to happen! + * SIGCHLD for any processes that actually suspended themselves. + * SIGCHLD for any processes that exited while we were alseep. + * The SIGCONT that actually caused us to wakeup. + * + * Since we defer passing the SIGCONT on to our children until + * the main processing loop, we can be sure that all the SIGCHLD + * events will have happened by then - and that the waitpid() will + * collect the child 'suspended' events. + * For correct sequencing we just need to ensure we process the + * waitpid() before passing on the SIGCONT. + * + * In any case nothing else is needed here. + */ + + /* Restore handler and signal mask */ + act.sa_handler = JobPassSig_suspend; + (void)sigaction(signo, &act, NULL); + (void)sigprocmask(SIG_SETMASK, &omask, NULL); +} + +static Job * +JobFindPid(int pid, JobStatus status, Boolean isJobs) +{ + Job *job; + + for (job = job_table; job < job_table_end; job++) { + if (job->status == status && job->pid == pid) + return job; + } + if (DEBUG(JOB) && isJobs) + job_table_dump("no pid"); + return NULL; +} + +/* Parse leading '@', '-' and '+', which control the exact execution mode. */ +static void +ParseCommandFlags(char **pp, CommandFlags *out_cmdFlags) +{ + char *p = *pp; + out_cmdFlags->echo = TRUE; + out_cmdFlags->ignerr = FALSE; + out_cmdFlags->always = FALSE; + + for (;;) { + if (*p == '@') + out_cmdFlags->echo = DEBUG(LOUD); + else if (*p == '-') + out_cmdFlags->ignerr = TRUE; + else if (*p == '+') + out_cmdFlags->always = TRUE; + else + break; + p++; + } + + pp_skip_whitespace(&p); + + *pp = p; +} + +/* Escape a string for a double-quoted string literal in sh, csh and ksh. */ +static char * +EscapeShellDblQuot(const char *cmd) +{ + size_t i, j; + + /* Worst that could happen is every char needs escaping. */ + char *esc = bmake_malloc(strlen(cmd) * 2 + 1); + for (i = 0, j = 0; cmd[i] != '\0'; i++, j++) { + if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || + cmd[i] == '"') + esc[j++] = '\\'; + esc[j] = cmd[i]; + } + esc[j] = '\0'; + + return esc; +} + +static void +ShellWriter_PrintFmt(ShellWriter *wr, const char *fmt, const char *arg) +{ + DEBUG1(JOB, fmt, arg); + + (void)fprintf(wr->f, fmt, arg); + /* XXX: Is flushing needed in any case, or only if f == stdout? */ + (void)fflush(wr->f); +} + +static void +ShellWriter_Println(ShellWriter *wr, const char *line) +{ + ShellWriter_PrintFmt(wr, "%s\n", line); *** 5994 LINES SKIPPED ***
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?202102110604.11B64RFg078214>