1 /* $NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg 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 * This file implements the full-compatibility mode of make, which makes the
74 * targets without parallelism and without a custom shell.
75 *
76 * Interface:
77 * Compat_MakeAll Initialize this module and make the given targets.
78 */
79
80 #ifdef HAVE_CONFIG_H
81 # include "config.h"
82 #endif
83 #include <sys/types.h>
84 #include <sys/stat.h>
85 #include "wait.h"
86
87 #include <errno.h>
88 #include <signal.h>
89
90 #include "make.h"
91 #include "dir.h"
92 #include "job.h"
93 #include "metachar.h"
94 #include "pathnames.h"
95
96 /* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
97 MAKE_RCSID("$NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg Exp $");
98
99 static GNode *curTarg = NULL;
100 static pid_t compatChild;
101 static int compatSigno;
102
103 /*
104 * Delete the file of a failed, interrupted, or otherwise duffed target,
105 * unless inhibited by .PRECIOUS.
106 */
107 static void
CompatDeleteTarget(GNode * gn)108 CompatDeleteTarget(GNode *gn)
109 {
110 if (gn != NULL && !GNode_IsPrecious(gn) &&
111 (gn->type & OP_PHONY) == 0) {
112 const char *file = GNode_VarTarget(gn);
113
114 if (!opts.noExecute && unlink_file(file) == 0) {
115 Error("*** %s removed", file);
116 }
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
CompatInterrupt(int signo)130 CompatInterrupt(int signo)
131 {
132 CompatDeleteTarget(curTarg);
133
134 if (curTarg != NULL && !GNode_IsPrecious(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 static void
DebugFailedTarget(const char * cmd,const GNode * gn)163 DebugFailedTarget(const char *cmd, const GNode *gn)
164 {
165 const char *p = cmd;
166 debug_printf("\n*** Failed target: %s\n*** Failed command: ",
167 gn->name);
168
169 /*
170 * Replace runs of whitespace with a single space, to reduce the
171 * amount of whitespace for multi-line command lines.
172 */
173 while (*p != '\0') {
174 if (ch_isspace(*p)) {
175 debug_printf(" ");
176 cpp_skip_whitespace(&p);
177 } else {
178 debug_printf("%c", *p);
179 p++;
180 }
181 }
182 debug_printf("\n");
183 }
184
185 static bool
UseShell(const char * cmd MAKE_ATTR_UNUSED)186 UseShell(const char *cmd MAKE_ATTR_UNUSED)
187 {
188 #if defined(FORCE_USE_SHELL) || !defined(MAKE_NATIVE)
189 /*
190 * In a non-native build, the host environment might be weird enough
191 * that it's necessary to go through a shell to get the correct
192 * behaviour. Or perhaps the shell has been replaced with something
193 * that does extra logging, and that should not be bypassed.
194 */
195 return true;
196 #else
197 /*
198 * Search for meta characters in the command. If there are no meta
199 * characters, there's no need to execute a shell to execute the
200 * command.
201 *
202 * Additionally variable assignments and empty commands
203 * go to the shell. Therefore treat '=' and ':' like shell
204 * meta characters as documented in make(1).
205 */
206
207 return needshell(cmd);
208 #endif
209 }
210
211 /*
212 * Execute the next command for a target. If the command returns an error,
213 * the node's made field is set to ERROR and creation stops.
214 *
215 * Input:
216 * cmdp Command to execute
217 * gn Node from which the command came
218 * ln List node that contains the command
219 *
220 * Results:
221 * true if the command succeeded.
222 */
223 bool
Compat_RunCommand(const char * cmdp,GNode * gn,StringListNode * ln)224 Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
225 {
226 char *cmdStart; /* Start of expanded command */
227 char *volatile bp;
228 bool silent; /* Don't print command */
229 bool doIt; /* Execute even if -n */
230 volatile bool errCheck; /* Check errors */
231 WAIT_T reason; /* Reason for child's death */
232 WAIT_T status; /* Description of child's death */
233 pid_t cpid; /* Child actually found */
234 pid_t retstat; /* Result of wait */
235 const char **volatile av; /* Argument vector for thing to exec */
236 char **volatile mav; /* Copy of the argument vector for freeing */
237 bool useShell; /* True if command should be executed using a
238 * shell */
239 const char *volatile cmd = cmdp;
240
241 silent = (gn->type & OP_SILENT) != OP_NONE;
242 errCheck = !(gn->type & OP_IGNORE);
243 doIt = false;
244
245 cmdStart = Var_Subst(cmd, gn, VARE_WANTRES);
246 /* TODO: handle errors */
247
248 if (cmdStart[0] == '\0') {
249 free(cmdStart);
250 return true;
251 }
252 cmd = cmdStart;
253 LstNode_Set(ln, cmdStart);
254
255 if (gn->type & OP_SAVE_CMDS) {
256 GNode *endNode = Targ_GetEndNode();
257 if (gn != endNode) {
258 /*
259 * Append the expanded command, to prevent the
260 * local variables from being interpreted in the
261 * scope of the .END node.
262 *
263 * A probably unintended side effect of this is that
264 * the expanded command will be expanded again in the
265 * .END node. Therefore, a literal '$' in these
266 * commands must be written as '$$$$' instead of the
267 * usual '$$'.
268 */
269 Lst_Append(&endNode->commands, cmdStart);
270 return true;
271 }
272 }
273 if (strcmp(cmdStart, "...") == 0) {
274 gn->type |= OP_SAVE_CMDS;
275 return true;
276 }
277
278 for (;;) {
279 if (*cmd == '@')
280 silent = !DEBUG(LOUD);
281 else if (*cmd == '-')
282 errCheck = false;
283 else if (*cmd == '+') {
284 doIt = true;
285 if (shellName == NULL) /* we came here from jobs */
286 Shell_Init();
287 } else if (!ch_isspace(*cmd))
288 /* Ignore whitespace for compatibility with gnu make */
289 break;
290 cmd++;
291 }
292
293 while (ch_isspace(*cmd))
294 cmd++;
295
296 /*
297 * If we did not end up with a command, just skip it.
298 */
299 if (cmd[0] == '\0')
300 return true;
301
302 useShell = UseShell(cmd);
303 /*
304 * Print the command before echoing if we're not supposed to be quiet
305 * for this one. We also print the command if -n given.
306 */
307 if (!silent || !GNode_ShouldExecute(gn)) {
308 printf("%s\n", cmd);
309 fflush(stdout);
310 }
311
312 /*
313 * If we're not supposed to execute any commands, this is as far as
314 * we go...
315 */
316 if (!doIt && !GNode_ShouldExecute(gn))
317 return true;
318
319 DEBUG1(JOB, "Execute: '%s'\n", cmd);
320
321 if (useShell) {
322 /*
323 * We need to pass the command off to the shell, typically
324 * because the command contains a "meta" character.
325 */
326 static const char *shargv[5];
327
328 /* The following work for any of the builtin shell specs. */
329 int shargc = 0;
330 shargv[shargc++] = shellPath;
331 if (errCheck && shellErrFlag != NULL)
332 shargv[shargc++] = shellErrFlag;
333 shargv[shargc++] = DEBUG(SHELL) ? "-xc" : "-c";
334 shargv[shargc++] = cmd;
335 shargv[shargc] = NULL;
336 av = shargv;
337 bp = NULL;
338 mav = NULL;
339 } else {
340 /*
341 * No meta-characters, so no need to exec a shell. Break the
342 * command into words to form an argument vector we can
343 * execute.
344 */
345 Words words = Str_Words(cmd, false);
346 mav = words.words;
347 bp = words.freeIt;
348 av = (void *)mav;
349 }
350
351 #ifdef USE_META
352 if (useMeta)
353 meta_compat_start();
354 #endif
355
356 Var_ReexportVars();
357
358 compatChild = cpid = vfork();
359 if (cpid < 0)
360 Fatal("Could not fork");
361
362 if (cpid == 0) {
363 #ifdef USE_META
364 if (useMeta)
365 meta_compat_child();
366 #endif
367 (void)execvp(av[0], (char *const *)UNCONST(av));
368 execDie("exec", av[0]);
369 }
370
371 free(mav);
372 free(bp);
373
374 /* XXX: Memory management looks suspicious here. */
375 /* XXX: Setting a list item to NULL is unexpected. */
376 LstNode_SetNull(ln);
377
378 #ifdef USE_META
379 if (useMeta)
380 meta_compat_parent(cpid);
381 #endif
382
383 /*
384 * The child is off and running. Now all we can do is wait...
385 */
386 while ((retstat = wait(&reason)) != cpid) {
387 if (retstat > 0)
388 JobReapChild(retstat, reason, false); /* not ours? */
389 if (retstat == -1 && errno != EINTR) {
390 break;
391 }
392 }
393
394 if (retstat < 0)
395 Fatal("error in wait: %d: %s", retstat, strerror(errno));
396
397 if (WIFSTOPPED(reason)) {
398 status = WSTOPSIG(reason); /* stopped */
399 } else if (WIFEXITED(reason)) {
400 status = WEXITSTATUS(reason); /* exited */
401 #if defined(USE_META) && defined(USE_FILEMON_ONCE)
402 if (useMeta)
403 meta_cmd_finish(NULL);
404 #endif
405 if (status != 0) {
406 if (DEBUG(ERROR))
407 DebugFailedTarget(cmd, gn);
408 printf("*** Error code %d", status);
409 }
410 } else {
411 status = WTERMSIG(reason); /* signaled */
412 printf("*** Signal %d", status);
413 }
414
415
416 if (!WIFEXITED(reason) || status != 0) {
417 if (errCheck) {
418 #ifdef USE_META
419 if (useMeta)
420 meta_job_error(NULL, gn, false, status);
421 #endif
422 gn->made = ERROR;
423 if (opts.keepgoing) {
424 /*
425 * Abort the current target,
426 * but let others continue.
427 */
428 printf(" (continuing)\n");
429 } else {
430 printf("\n");
431 }
432 if (deleteOnError)
433 CompatDeleteTarget(gn);
434 } else {
435 /*
436 * Continue executing commands for this target.
437 * If we return 0, this will happen...
438 */
439 printf(" (ignored)\n");
440 status = 0;
441 }
442 }
443
444 free(cmdStart);
445 compatChild = 0;
446 if (compatSigno != 0) {
447 bmake_signal(compatSigno, SIG_DFL);
448 kill(myPid, compatSigno);
449 }
450
451 return status == 0;
452 }
453
454 static void
RunCommands(GNode * gn)455 RunCommands(GNode *gn)
456 {
457 StringListNode *ln;
458
459 for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
460 const char *cmd = ln->datum;
461 if (!Compat_RunCommand(cmd, gn, ln))
462 break;
463 }
464 }
465
466 static void
MakeInRandomOrder(GNode ** gnodes,GNode ** end,GNode * pgn)467 MakeInRandomOrder(GNode **gnodes, GNode **end, GNode *pgn)
468 {
469 GNode **it;
470 size_t r;
471
472 for (r = (size_t)(end - gnodes); r >= 2; r--) {
473 /* Biased, but irrelevant in practice. */
474 size_t i = (size_t)random() % r;
475 GNode *t = gnodes[r - 1];
476 gnodes[r - 1] = gnodes[i];
477 gnodes[i] = t;
478 }
479
480 for (it = gnodes; it != end; it++)
481 Compat_Make(*it, pgn);
482 }
483
484 static void
MakeWaitGroupsInRandomOrder(GNodeList * gnodes,GNode * pgn)485 MakeWaitGroupsInRandomOrder(GNodeList *gnodes, GNode *pgn)
486 {
487 Vector vec;
488 GNodeListNode *ln;
489 GNode **nodes;
490 size_t i, n, start;
491
492 Vector_Init(&vec, sizeof(GNode *));
493 for (ln = gnodes->first; ln != NULL; ln = ln->next)
494 *(GNode **)Vector_Push(&vec) = ln->datum;
495 nodes = vec.items;
496 n = vec.len;
497
498 start = 0;
499 for (i = 0; i < n; i++) {
500 if (nodes[i]->type & OP_WAIT) {
501 MakeInRandomOrder(nodes + start, nodes + i, pgn);
502 Compat_Make(nodes[i], pgn);
503 start = i + 1;
504 }
505 }
506 MakeInRandomOrder(nodes + start, nodes + i, pgn);
507
508 Vector_Done(&vec);
509 }
510
511 static void
MakeNodes(GNodeList * gnodes,GNode * pgn)512 MakeNodes(GNodeList *gnodes, GNode *pgn)
513 {
514 GNodeListNode *ln;
515
516 if (Lst_IsEmpty(gnodes))
517 return;
518 if (opts.randomizeTargets) {
519 MakeWaitGroupsInRandomOrder(gnodes, pgn);
520 return;
521 }
522
523 for (ln = gnodes->first; ln != NULL; ln = ln->next) {
524 GNode *cgn = ln->datum;
525 Compat_Make(cgn, pgn);
526 }
527 }
528
529 static bool
MakeUnmade(GNode * gn,GNode * pgn)530 MakeUnmade(GNode *gn, GNode *pgn)
531 {
532
533 assert(gn->made == UNMADE);
534
535 /*
536 * First mark ourselves to be made, then apply whatever transformations
537 * the suffix module thinks are necessary. Once that's done, we can
538 * descend and make all our children. If any of them has an error
539 * but the -k flag was given, our 'make' field will be set to false
540 * again. This is our signal to not attempt to do anything but abort
541 * our parent as well.
542 */
543 gn->flags.remake = true;
544 gn->made = BEINGMADE;
545
546 if (!(gn->type & OP_MADE))
547 Suff_FindDeps(gn);
548
549 MakeNodes(&gn->children, gn);
550
551 if (!gn->flags.remake) {
552 gn->made = ABORTED;
553 pgn->flags.remake = false;
554 return false;
555 }
556
557 if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL)
558 Var_Set(pgn, IMPSRC, GNode_VarTarget(gn));
559
560 /*
561 * All the children were made ok. Now youngestChild->mtime contains the
562 * modification time of the newest child, we need to find out if we
563 * exist and when we were modified last. The criteria for datedness
564 * are defined by GNode_IsOODate.
565 */
566 DEBUG1(MAKE, "Examining %s...", gn->name);
567 if (!GNode_IsOODate(gn)) {
568 gn->made = UPTODATE;
569 DEBUG0(MAKE, "up-to-date.\n");
570 return false;
571 }
572
573 /*
574 * If the user is just seeing if something is out-of-date, exit now
575 * to tell him/her "yes".
576 */
577 DEBUG0(MAKE, "out-of-date.\n");
578 if (opts.query && gn != Targ_GetEndNode())
579 exit(1);
580
581 /*
582 * We need to be re-made.
583 * Ensure that $? (.OODATE) and $> (.ALLSRC) are both set.
584 */
585 GNode_SetLocalVars(gn);
586
587 /*
588 * Alter our type to tell if errors should be ignored or things
589 * should not be printed so Compat_RunCommand knows what to do.
590 */
591 if (opts.ignoreErrors)
592 gn->type |= OP_IGNORE;
593 if (opts.silent)
594 gn->type |= OP_SILENT;
595
596 if (Job_CheckCommands(gn, Fatal)) {
597 /*
598 * Our commands are ok, but we still have to worry about
599 * the -t flag.
600 */
601 if (!opts.touch || (gn->type & OP_MAKE)) {
602 curTarg = gn;
603 #ifdef USE_META
604 if (useMeta && GNode_ShouldExecute(gn))
605 meta_job_start(NULL, gn);
606 #endif
607 RunCommands(gn);
608 curTarg = NULL;
609 } else {
610 Job_Touch(gn, (gn->type & OP_SILENT) != OP_NONE);
611 }
612 } else {
613 gn->made = ERROR;
614 }
615 #ifdef USE_META
616 if (useMeta && GNode_ShouldExecute(gn)) {
617 if (meta_job_finish(NULL) != 0)
618 gn->made = ERROR;
619 }
620 #endif
621
622 if (gn->made != ERROR) {
623 /*
624 * If the node was made successfully, mark it so, update
625 * its modification time and timestamp all its parents.
626 * This is to keep its state from affecting that of its parent.
627 */
628 gn->made = MADE;
629 if (Make_Recheck(gn) == 0)
630 pgn->flags.force = true;
631 if (!(gn->type & OP_EXEC)) {
632 pgn->flags.childMade = true;
633 GNode_UpdateYoungestChild(pgn, gn);
634 }
635 } else if (opts.keepgoing) {
636 pgn->flags.remake = false;
637 } else {
638 PrintOnError(gn, "\nStop.\n");
639 exit(1);
640 }
641 return true;
642 }
643
644 static void
MakeOther(GNode * gn,GNode * pgn)645 MakeOther(GNode *gn, GNode *pgn)
646 {
647
648 if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) {
649 const char *target = GNode_VarTarget(gn);
650 Var_Set(pgn, IMPSRC, target != NULL ? target : "");
651 }
652
653 switch (gn->made) {
654 case BEINGMADE:
655 Error("Graph cycles through %s", gn->name);
656 gn->made = ERROR;
657 pgn->flags.remake = false;
658 break;
659 case MADE:
660 if (!(gn->type & OP_EXEC)) {
661 pgn->flags.childMade = true;
662 GNode_UpdateYoungestChild(pgn, gn);
663 }
664 break;
665 case UPTODATE:
666 if (!(gn->type & OP_EXEC))
667 GNode_UpdateYoungestChild(pgn, gn);
668 break;
669 default:
670 break;
671 }
672 }
673
674 /*
675 * Make a target.
676 *
677 * If an error is detected and not being ignored, the process exits.
678 *
679 * Input:
680 * gn The node to make
681 * pgn Parent to abort if necessary
682 *
683 * Output:
684 * gn->made
685 * UPTODATE gn was already up-to-date.
686 * MADE gn was recreated successfully.
687 * ERROR An error occurred while gn was being created,
688 * either due to missing commands or in -k mode.
689 * ABORTED gn was not remade because one of its
690 * dependencies could not be made due to errors.
691 */
692 void
Compat_Make(GNode * gn,GNode * pgn)693 Compat_Make(GNode *gn, GNode *pgn)
694 {
695 if (shellName == NULL) /* we came here from jobs */
696 Shell_Init();
697
698 if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
699 if (!MakeUnmade(gn, pgn))
700 goto cohorts;
701
702 /* XXX: Replace with GNode_IsError(gn) */
703 } else if (gn->made == ERROR) {
704 /*
705 * Already had an error when making this.
706 * Tell the parent to abort.
707 */
708 pgn->flags.remake = false;
709 } else {
710 MakeOther(gn, pgn);
711 }
712
713 cohorts:
714 MakeNodes(&gn->cohorts, pgn);
715 }
716
717 static void
MakeBeginNode(void)718 MakeBeginNode(void)
719 {
720 GNode *gn = Targ_FindNode(".BEGIN");
721 if (gn == NULL)
722 return;
723
724 Compat_Make(gn, gn);
725 if (GNode_IsError(gn)) {
726 PrintOnError(gn, "\nStop.\n");
727 exit(1);
728 }
729 }
730
731 static void
InitSignals(void)732 InitSignals(void)
733 {
734 if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN)
735 bmake_signal(SIGINT, CompatInterrupt);
736 if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN)
737 bmake_signal(SIGTERM, CompatInterrupt);
738 if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN)
739 bmake_signal(SIGHUP, CompatInterrupt);
740 if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN)
741 bmake_signal(SIGQUIT, CompatInterrupt);
742 }
743
744 void
Compat_MakeAll(GNodeList * targs)745 Compat_MakeAll(GNodeList *targs)
746 {
747 GNode *errorNode = NULL;
748
749 if (shellName == NULL)
750 Shell_Init();
751
752 InitSignals();
753
754 /*
755 * Create the .END node now, to keep the (debug) output of the
756 * counter.mk test the same as before 2020-09-23. This
757 * implementation detail probably doesn't matter though.
758 */
759 (void)Targ_GetEndNode();
760
761 if (!opts.query)
762 MakeBeginNode();
763
764 /*
765 * Expand .USE nodes right now, because they can modify the structure
766 * of the tree.
767 */
768 Make_ExpandUse(targs);
769
770 while (!Lst_IsEmpty(targs)) {
771 GNode *gn = Lst_Dequeue(targs);
772 Compat_Make(gn, gn);
773
774 if (gn->made == UPTODATE) {
775 printf("`%s' is up to date.\n", gn->name);
776 } else if (gn->made == ABORTED) {
777 printf("`%s' not remade because of errors.\n",
778 gn->name);
779 }
780 if (GNode_IsError(gn) && errorNode == NULL)
781 errorNode = gn;
782 }
783
784 /* If the user has defined a .END target, run its commands. */
785 if (errorNode == NULL) {
786 GNode *endNode = Targ_GetEndNode();
787 Compat_Make(endNode, endNode);
788 if (GNode_IsError(endNode))
789 errorNode = endNode;
790 }
791
792 if (errorNode != NULL) {
793 if (DEBUG(GRAPH2))
794 Targ_PrintGraph(2);
795 else if (DEBUG(GRAPH3))
796 Targ_PrintGraph(3);
797 PrintOnError(errorNode, "\nStop.\n");
798 exit(1);
799 }
800 }
801