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