Home | History | Annotate | Line # | Download | only in make
compat.c revision 1.176
      1 /*	$NetBSD: compat.c,v 1.176 2020/11/07 10:23:20 rillig Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to Berkeley by
      8  * Adam de Boor.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  * 3. Neither the name of the University nor the names of its contributors
     19  *    may be used to endorse or promote products derived from this software
     20  *    without specific prior written permission.
     21  *
     22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     32  * SUCH DAMAGE.
     33  */
     34 
     35 /*
     36  * Copyright (c) 1988, 1989 by Adam de Boor
     37  * Copyright (c) 1989 by Berkeley Softworks
     38  * All rights reserved.
     39  *
     40  * This code is derived from software contributed to Berkeley by
     41  * Adam de Boor.
     42  *
     43  * Redistribution and use in source and binary forms, with or without
     44  * modification, are permitted provided that the following conditions
     45  * are met:
     46  * 1. Redistributions of source code must retain the above copyright
     47  *    notice, this list of conditions and the following disclaimer.
     48  * 2. Redistributions in binary form must reproduce the above copyright
     49  *    notice, this list of conditions and the following disclaimer in the
     50  *    documentation and/or other materials provided with the distribution.
     51  * 3. All advertising materials mentioning features or use of this software
     52  *    must display the following acknowledgement:
     53  *	This product includes software developed by the University of
     54  *	California, Berkeley and its contributors.
     55  * 4. Neither the name of the University nor the names of its contributors
     56  *    may be used to endorse or promote products derived from this software
     57  *    without specific prior written permission.
     58  *
     59  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     60  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     61  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     62  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     63  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     64  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     65  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     66  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     67  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     68  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     69  * SUCH DAMAGE.
     70  */
     71 
     72 /*-
     73  * compat.c --
     74  *	The routines in this file implement the full-compatibility
     75  *	mode of PMake. Most of the special functionality of PMake
     76  *	is available in this mode. Things not supported:
     77  *	    - different shells.
     78  *	    - friendly variable substitution.
     79  *
     80  * Interface:
     81  *	Compat_Run	Initialize things for this module and recreate
     82  *			thems as need creatin'
     83  */
     84 
     85 #include <sys/types.h>
     86 #include <sys/stat.h>
     87 #include <sys/wait.h>
     88 
     89 #include <errno.h>
     90 #include <signal.h>
     91 
     92 #include "make.h"
     93 #include "dir.h"
     94 #include "job.h"
     95 #include "metachar.h"
     96 #include "pathnames.h"
     97 
     98 /*	"@(#)compat.c	8.2 (Berkeley) 3/19/94"	*/
     99 MAKE_RCSID("$NetBSD: compat.c,v 1.176 2020/11/07 10:23:20 rillig Exp $");
    100 
    101 static GNode *curTarg = NULL;
    102 static pid_t compatChild;
    103 static int compatSigno;
    104 
    105 /*
    106  * CompatDeleteTarget -- delete the file of a failed, interrupted, or
    107  * otherwise duffed target if not inhibited by .PRECIOUS.
    108  */
    109 static void
    110 CompatDeleteTarget(GNode *gn)
    111 {
    112     if (gn != NULL && !Targ_Precious(gn)) {
    113 	const char *file = GNode_VarTarget(gn);
    114 
    115 	if (!opts.noExecute && eunlink(file) != -1) {
    116 	    Error("*** %s removed", file);
    117 	}
    118     }
    119 }
    120 
    121 /* Interrupt the creation of the current target and remove it if it ain't
    122  * precious. Then exit.
    123  *
    124  * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED.
    125  *
    126  * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've
    127  * left the logic alone for now. - dholland 20160826
    128  */
    129 static void
    130 CompatInterrupt(int signo)
    131 {
    132     CompatDeleteTarget(curTarg);
    133 
    134     if (curTarg != NULL && !Targ_Precious(curTarg)) {
    135 	/*
    136 	 * Run .INTERRUPT only if hit with interrupt signal
    137 	 */
    138 	if (signo == SIGINT) {
    139 	    GNode *gn = Targ_FindNode(".INTERRUPT");
    140 	    if (gn != NULL) {
    141 		Compat_Make(gn, gn);
    142 	    }
    143 	}
    144     }
    145 
    146     if (signo == SIGQUIT)
    147 	_exit(signo);
    148 
    149     /*
    150      * If there is a child running, pass the signal on.
    151      * We will exist after it has exited.
    152      */
    153     compatSigno = signo;
    154     if (compatChild > 0) {
    155 	KILLPG(compatChild, signo);
    156     } else {
    157 	bmake_signal(signo, SIG_DFL);
    158 	kill(myPid, signo);
    159     }
    160 }
    161 
    162 /* Execute the next command for a target. If the command returns an error,
    163  * the node's made field is set to ERROR and creation stops.
    164  *
    165  * Input:
    166  *	cmdp		Command to execute
    167  *	gnp		Node from which the command came
    168  *
    169  * Results:
    170  *	0 if the command succeeded, 1 if an error occurred.
    171  */
    172 int
    173 Compat_RunCommand(const char *cmdp, GNode *gn)
    174 {
    175     char *cmdStart;		/* Start of expanded command */
    176     char *bp;
    177     Boolean silent;		/* Don't print command */
    178     Boolean doIt;		/* Execute even if -n */
    179     volatile Boolean errCheck;	/* Check errors */
    180     int reason;			/* Reason for child's death */
    181     int status;			/* Description of child's death */
    182     pid_t cpid;			/* Child actually found */
    183     pid_t retstat;		/* Result of wait */
    184     StringListNode *cmdNode;	/* Node where current command is located */
    185     const char **volatile av;	/* Argument vector for thing to exec */
    186     char **volatile mav;	/* Copy of the argument vector for freeing */
    187     Boolean useShell;		/* TRUE if command should be executed
    188 				 * using a shell */
    189     const char *volatile cmd = cmdp;
    190 
    191     silent = (gn->type & OP_SILENT) != 0;
    192     errCheck = !(gn->type & OP_IGNORE);
    193     doIt = FALSE;
    194 
    195     /* Luckily the commands don't end up in a string pool, otherwise
    196      * this comparison could match too early, in a dependency using "..."
    197      * for delayed commands, run in parallel mode, using the same shell
    198      * command line more than once; see JobPrintCommand.
    199      * TODO: write a unit-test to protect against this potential bug. */
    200     cmdNode = Lst_FindDatum(gn->commands, cmd);
    201     (void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
    202     /* TODO: handle errors */
    203 
    204     if (cmdStart[0] == '\0') {
    205 	free(cmdStart);
    206 	return 0;
    207     }
    208     cmd = cmdStart;
    209     LstNode_Set(cmdNode, cmdStart);
    210 
    211     if (gn->type & OP_SAVE_CMDS) {
    212 	GNode *endNode = Targ_GetEndNode();
    213 	if (gn != endNode) {
    214 	    Lst_Append(endNode->commands, cmdStart);
    215 	    return 0;
    216 	}
    217     }
    218     if (strcmp(cmdStart, "...") == 0) {
    219 	gn->type |= OP_SAVE_CMDS;
    220 	return 0;
    221     }
    222 
    223     while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
    224 	switch (*cmd) {
    225 	case '@':
    226 	    silent = !DEBUG(LOUD);
    227 	    break;
    228 	case '-':
    229 	    errCheck = FALSE;
    230 	    break;
    231 	case '+':
    232 	    doIt = TRUE;
    233 	    if (!shellName)		/* we came here from jobs */
    234 		Shell_Init();
    235 	    break;
    236 	}
    237 	cmd++;
    238     }
    239 
    240     while (ch_isspace(*cmd))
    241 	cmd++;
    242 
    243     /*
    244      * If we did not end up with a command, just skip it.
    245      */
    246     if (cmd[0] == '\0')
    247 	return 0;
    248 
    249 #if !defined(MAKE_NATIVE)
    250     /*
    251      * In a non-native build, the host environment might be weird enough
    252      * that it's necessary to go through a shell to get the correct
    253      * behaviour.  Or perhaps the shell has been replaced with something
    254      * that does extra logging, and that should not be bypassed.
    255      */
    256     useShell = TRUE;
    257 #else
    258     /*
    259      * Search for meta characters in the command. If there are no meta
    260      * characters, there's no need to execute a shell to execute the
    261      * command.
    262      *
    263      * Additionally variable assignments and empty commands
    264      * go to the shell. Therefore treat '=' and ':' like shell
    265      * meta characters as documented in make(1).
    266      */
    267 
    268     useShell = needshell(cmd);
    269 #endif
    270 
    271     /*
    272      * Print the command before echoing if we're not supposed to be quiet for
    273      * this one. We also print the command if -n given.
    274      */
    275     if (!silent || !GNode_ShouldExecute(gn)) {
    276 	printf("%s\n", cmd);
    277 	fflush(stdout);
    278     }
    279 
    280     /*
    281      * If we're not supposed to execute any commands, this is as far as
    282      * we go...
    283      */
    284     if (!doIt && !GNode_ShouldExecute(gn)) {
    285 	return 0;
    286     }
    287     DEBUG1(JOB, "Execute: '%s'\n", cmd);
    288 
    289     if (useShell) {
    290 	/*
    291 	 * We need to pass the command off to the shell, typically
    292 	 * because the command contains a "meta" character.
    293 	 */
    294 	static const char *shargv[5];
    295 	int shargc;
    296 
    297 	shargc = 0;
    298 	shargv[shargc++] = shellPath;
    299 	/*
    300 	 * The following work for any of the builtin shell specs.
    301 	 */
    302 	if (errCheck && shellErrFlag) {
    303 	    shargv[shargc++] = shellErrFlag;
    304 	}
    305 	if (DEBUG(SHELL))
    306 		shargv[shargc++] = "-xc";
    307 	else
    308 		shargv[shargc++] = "-c";
    309 	shargv[shargc++] = cmd;
    310 	shargv[shargc] = NULL;
    311 	av = shargv;
    312 	bp = NULL;
    313 	mav = NULL;
    314     } else {
    315 	/*
    316 	 * No meta-characters, so no need to exec a shell. Break the command
    317 	 * into words to form an argument vector we can execute.
    318 	 */
    319 	Words words = Str_Words(cmd, FALSE);
    320 	mav = words.words;
    321 	bp = words.freeIt;
    322 	av = (void *)mav;
    323     }
    324 
    325 #ifdef USE_META
    326     if (useMeta) {
    327 	meta_compat_start();
    328     }
    329 #endif
    330 
    331     /*
    332      * Fork and execute the single command. If the fork fails, we abort.
    333      */
    334     compatChild = cpid = vFork();
    335     if (cpid < 0) {
    336 	Fatal("Could not fork");
    337     }
    338     if (cpid == 0) {
    339 	Var_ExportVars();
    340 #ifdef USE_META
    341 	if (useMeta) {
    342 	    meta_compat_child();
    343 	}
    344 #endif
    345 	(void)execvp(av[0], (char *const *)UNCONST(av));
    346 	execDie("exec", av[0]);
    347     }
    348 
    349     free(mav);
    350     free(bp);
    351 
    352     /* XXX: Memory management looks suspicious here. */
    353     /* XXX: Setting a list item to NULL is unexpected. */
    354     LstNode_SetNull(cmdNode);
    355 
    356 #ifdef USE_META
    357     if (useMeta) {
    358 	meta_compat_parent(cpid);
    359     }
    360 #endif
    361 
    362     /*
    363      * The child is off and running. Now all we can do is wait...
    364      */
    365     while ((retstat = wait(&reason)) != cpid) {
    366 	if (retstat > 0)
    367 	    JobReapChild(retstat, reason, FALSE); /* not ours? */
    368 	if (retstat == -1 && errno != EINTR) {
    369 	    break;
    370 	}
    371     }
    372 
    373     if (retstat < 0)
    374 	Fatal("error in wait: %d: %s", retstat, strerror(errno));
    375 
    376     if (WIFSTOPPED(reason)) {
    377 	status = WSTOPSIG(reason);		/* stopped */
    378     } else if (WIFEXITED(reason)) {
    379 	status = WEXITSTATUS(reason);		/* exited */
    380 #if defined(USE_META) && defined(USE_FILEMON_ONCE)
    381 	if (useMeta) {
    382 	    meta_cmd_finish(NULL);
    383 	}
    384 #endif
    385 	if (status != 0) {
    386 	    if (DEBUG(ERROR)) {
    387 		const char *cp;
    388 		debug_printf("\n*** Failed target:  %s\n*** Failed command: ",
    389 			     gn->name);
    390 		for (cp = cmd; *cp; ) {
    391 		    if (ch_isspace(*cp)) {
    392 			debug_printf(" ");
    393 			while (ch_isspace(*cp))
    394 			    cp++;
    395 		    } else {
    396 			debug_printf("%c", *cp);
    397 			cp++;
    398 		    }
    399 		}
    400 		debug_printf("\n");
    401 	    }
    402 	    printf("*** Error code %d", status);
    403 	}
    404     } else {
    405 	status = WTERMSIG(reason);		/* signaled */
    406 	printf("*** Signal %d", status);
    407     }
    408 
    409 
    410     if (!WIFEXITED(reason) || status != 0) {
    411 	if (errCheck) {
    412 #ifdef USE_META
    413 	    if (useMeta) {
    414 		meta_job_error(NULL, gn, 0, status);
    415 	    }
    416 #endif
    417 	    gn->made = ERROR;
    418 	    if (opts.keepgoing) {
    419 		/* Abort the current target, but let others continue. */
    420 		printf(" (continuing)\n");
    421 	    } else {
    422 		printf("\n");
    423 	    }
    424 	    if (deleteOnError)
    425 		CompatDeleteTarget(gn);
    426 	} else {
    427 	    /*
    428 	     * Continue executing commands for this target.
    429 	     * If we return 0, this will happen...
    430 	     */
    431 	    printf(" (ignored)\n");
    432 	    status = 0;
    433 	}
    434     }
    435 
    436     free(cmdStart);
    437     compatChild = 0;
    438     if (compatSigno) {
    439 	bmake_signal(compatSigno, SIG_DFL);
    440 	kill(myPid, compatSigno);
    441     }
    442 
    443     return status;
    444 }
    445 
    446 static void
    447 RunCommands(GNode *gn)
    448 {
    449     StringListNode *ln;
    450     for (ln = gn->commands->first; ln != NULL; ln = ln->next) {
    451 	const char *cmd = ln->datum;
    452 	if (Compat_RunCommand(cmd, gn) != 0)
    453 	    break;
    454     }
    455 }
    456 
    457 static void
    458 MakeNodes(GNodeList *gnodes, GNode *pgn)
    459 {
    460     GNodeListNode *ln;
    461     for (ln = gnodes->first; ln != NULL; ln = ln->next) {
    462 	GNode *cohort = ln->datum;
    463 	Compat_Make(cohort, pgn);
    464     }
    465 }
    466 
    467 /* Make a target.
    468  *
    469  * If an error is detected and not being ignored, the process exits.
    470  *
    471  * Input:
    472  *	gn		The node to make
    473  *	pgn		Parent to abort if necessary
    474  */
    475 void
    476 Compat_Make(GNode *gn, GNode *pgn)
    477 {
    478     if (!shellName)		/* we came here from jobs */
    479 	Shell_Init();
    480     if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
    481 	/*
    482 	 * First mark ourselves to be made, then apply whatever transformations
    483 	 * the suffix module thinks are necessary. Once that's done, we can
    484 	 * descend and make all our children. If any of them has an error
    485 	 * but the -k flag was given, our 'make' field will be set FALSE again.
    486 	 * This is our signal to not attempt to do anything but abort our
    487 	 * parent as well.
    488 	 */
    489 	gn->flags |= REMAKE;
    490 	gn->made = BEINGMADE;
    491 	if (!(gn->type & OP_MADE))
    492 	    Suff_FindDeps(gn);
    493 	MakeNodes(gn->children, gn);
    494 	if (!(gn->flags & REMAKE)) {
    495 	    gn->made = ABORTED;
    496 	    pgn->flags &= ~(unsigned)REMAKE;
    497 	    goto cohorts;
    498 	}
    499 
    500 	if (Lst_FindDatum(gn->implicitParents, pgn) != NULL)
    501 	    Var_Set(IMPSRC, GNode_VarTarget(gn), pgn);
    502 
    503 	/*
    504 	 * All the children were made ok. Now youngestChild->mtime contains the
    505 	 * modification time of the newest child, we need to find out if we
    506 	 * exist and when we were modified last. The criteria for datedness
    507 	 * are defined by the Make_OODate function.
    508 	 */
    509 	DEBUG1(MAKE, "Examining %s...", gn->name);
    510 	if (!Make_OODate(gn)) {
    511 	    gn->made = UPTODATE;
    512 	    DEBUG0(MAKE, "up-to-date.\n");
    513 	    goto cohorts;
    514 	} else
    515 	    DEBUG0(MAKE, "out-of-date.\n");
    516 
    517 	/*
    518 	 * If the user is just seeing if something is out-of-date, exit now
    519 	 * to tell him/her "yes".
    520 	 */
    521 	if (opts.queryFlag) {
    522 	    exit(1);
    523 	}
    524 
    525 	/*
    526 	 * We need to be re-made. We also have to make sure we've got a $?
    527 	 * variable. To be nice, we also define the $> variable using
    528 	 * Make_DoAllVar().
    529 	 */
    530 	Make_DoAllVar(gn);
    531 
    532 	/*
    533 	 * Alter our type to tell if errors should be ignored or things
    534 	 * should not be printed so CompatRunCommand knows what to do.
    535 	 */
    536 	if (Targ_Ignore(gn))
    537 	    gn->type |= OP_IGNORE;
    538 	if (Targ_Silent(gn))
    539 	    gn->type |= OP_SILENT;
    540 
    541 	if (Job_CheckCommands(gn, Fatal)) {
    542 	    /*
    543 	     * Our commands are ok, but we still have to worry about the -t
    544 	     * flag...
    545 	     */
    546 	    if (!opts.touchFlag || (gn->type & OP_MAKE)) {
    547 		curTarg = gn;
    548 #ifdef USE_META
    549 		if (useMeta && GNode_ShouldExecute(gn)) {
    550 		    meta_job_start(NULL, gn);
    551 		}
    552 #endif
    553 		RunCommands(gn);
    554 		curTarg = NULL;
    555 	    } else {
    556 		Job_Touch(gn, (gn->type & OP_SILENT) != 0);
    557 	    }
    558 	} else {
    559 	    gn->made = ERROR;
    560 	}
    561 #ifdef USE_META
    562 	if (useMeta && GNode_ShouldExecute(gn)) {
    563 	    if (meta_job_finish(NULL) != 0)
    564 		gn->made = ERROR;
    565 	}
    566 #endif
    567 
    568 	if (gn->made != ERROR) {
    569 	    /*
    570 	     * If the node was made successfully, mark it so, update
    571 	     * its modification time and timestamp all its parents. Note
    572 	     * that for .ZEROTIME targets, the timestamping isn't done.
    573 	     * This is to keep its state from affecting that of its parent.
    574 	     */
    575 	    gn->made = MADE;
    576 	    if (Make_Recheck(gn) == 0)
    577 		pgn->flags |= FORCE;
    578 	    if (!(gn->type & OP_EXEC)) {
    579 		pgn->flags |= CHILDMADE;
    580 		Make_TimeStamp(pgn, gn);
    581 	    }
    582 	} else if (opts.keepgoing) {
    583 	    pgn->flags &= ~(unsigned)REMAKE;
    584 	} else {
    585 	    PrintOnError(gn, "\nStop.");
    586 	    exit(1);
    587 	}
    588     } else if (gn->made == ERROR) {
    589 	/* Already had an error when making this. Tell the parent to abort. */
    590 	pgn->flags &= ~(unsigned)REMAKE;
    591     } else {
    592 	if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
    593 	    const char *target = GNode_VarTarget(gn);
    594 	    Var_Set(IMPSRC, target != NULL ? target : "", pgn);
    595 	}
    596 	switch(gn->made) {
    597 	    case BEINGMADE:
    598 		Error("Graph cycles through %s", gn->name);
    599 		gn->made = ERROR;
    600 		pgn->flags &= ~(unsigned)REMAKE;
    601 		break;
    602 	    case MADE:
    603 		if (!(gn->type & OP_EXEC)) {
    604 		    pgn->flags |= CHILDMADE;
    605 		    Make_TimeStamp(pgn, gn);
    606 		}
    607 		break;
    608 	    case UPTODATE:
    609 		if (!(gn->type & OP_EXEC))
    610 		    Make_TimeStamp(pgn, gn);
    611 		break;
    612 	    default:
    613 		break;
    614 	}
    615     }
    616 
    617 cohorts:
    618     MakeNodes(gn->cohorts, pgn);
    619 }
    620 
    621 /* Initialize this module and start making.
    622  *
    623  * Input:
    624  *	targs		The target nodes to re-create
    625  */
    626 void
    627 Compat_Run(GNodeList *targs)
    628 {
    629     GNode *gn = NULL;		/* Current root target */
    630     int errors;			/* Number of targets not remade due to errors */
    631 
    632     if (!shellName)
    633 	Shell_Init();
    634 
    635     if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN)
    636 	bmake_signal(SIGINT, CompatInterrupt);
    637     if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN)
    638 	bmake_signal(SIGTERM, CompatInterrupt);
    639     if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN)
    640 	bmake_signal(SIGHUP, CompatInterrupt);
    641     if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN)
    642 	bmake_signal(SIGQUIT, CompatInterrupt);
    643 
    644     /* Create the .END node now, to keep the (debug) output of the
    645      * counter.mk test the same as before 2020-09-23.  This implementation
    646      * detail probably doesn't matter though. */
    647     (void)Targ_GetEndNode();
    648     /*
    649      * If the user has defined a .BEGIN target, execute the commands attached
    650      * to it.
    651      */
    652     if (!opts.queryFlag) {
    653 	gn = Targ_FindNode(".BEGIN");
    654 	if (gn != NULL) {
    655 	    Compat_Make(gn, gn);
    656 	    if (gn->made == ERROR) {
    657 		PrintOnError(gn, "\nStop.");
    658 		exit(1);
    659 	    }
    660 	}
    661     }
    662 
    663     /*
    664      * Expand .USE nodes right now, because they can modify the structure
    665      * of the tree.
    666      */
    667     Make_ExpandUse(targs);
    668 
    669     /*
    670      * For each entry in the list of targets to create, call Compat_Make on
    671      * it to create the thing. Compat_Make will leave the 'made' field of gn
    672      * in one of several states:
    673      *	    UPTODATE	gn was already up-to-date
    674      *	    MADE	gn was recreated successfully
    675      *	    ERROR	An error occurred while gn was being created
    676      *	    ABORTED	gn was not remade because one of its inferiors
    677      *			could not be made due to errors.
    678      */
    679     errors = 0;
    680     while (!Lst_IsEmpty(targs)) {
    681 	gn = Lst_Dequeue(targs);
    682 	Compat_Make(gn, gn);
    683 
    684 	if (gn->made == UPTODATE) {
    685 	    printf("`%s' is up to date.\n", gn->name);
    686 	} else if (gn->made == ABORTED) {
    687 	    printf("`%s' not remade because of errors.\n", gn->name);
    688 	    errors++;
    689 	}
    690     }
    691 
    692     /*
    693      * If the user has defined a .END target, run its commands.
    694      */
    695     if (errors == 0) {
    696 	GNode *endNode = Targ_GetEndNode();
    697 	Compat_Make(endNode, endNode);
    698 	/* XXX: Did you mean endNode->made instead of gn->made? */
    699 	if (gn->made == ERROR) {
    700 	    PrintOnError(gn, "\nStop.");
    701 	    exit(1);
    702 	}
    703     }
    704 }
    705