git: dba7b0ef928a - main - Merge bmake-20210206

Simon J. Gerraty sjg at FreeBSD.org
Thu Feb 11 06:04:27 UTC 2021


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 at FreeBSD.org>
AuthorDate: 2021-02-11 06:01:59 +0000
Commit:     Simon J. Gerraty <sjg at 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 ***


More information about the dev-commits-src-main mailing list