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