1 /* $NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg Exp $ */
4 * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
7 * This code is derived from software contributed to Berkeley by
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
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.
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
36 * Copyright (c) 1988, 1989 by Adam de Boor
37 * Copyright (c) 1989 by Berkeley Softworks
38 * All rights reserved.
40 * This code is derived from software contributed to Berkeley by
43 * Redistribution and use in source and binary forms, with or without
44 * modification, are permitted provided that the following conditions
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.
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
73 * This file implements the full-compatibility mode of make, which makes the
74 * targets without parallelism and without a custom shell.
77 * Compat_MakeAll Initialize this module and make the given targets.
83 #include <sys/types.h>
94 #include "pathnames.h"
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 $");
99 static GNode *curTarg = NULL;
100 static pid_t compatChild;
101 static int compatSigno;
104 * Delete the file of a failed, interrupted, or otherwise duffed target,
105 * unless inhibited by .PRECIOUS.
108 CompatDeleteTarget(GNode *gn)
110 if (gn != NULL && !GNode_IsPrecious(gn) &&
111 (gn->type & OP_PHONY) == 0) {
112 const char *file = GNode_VarTarget(gn);
114 if (!opts.noExecute && unlink_file(file) == 0) {
115 Error("*** %s removed", file);
121 * Interrupt the creation of the current target and remove it if it ain't
122 * precious. Then exit.
124 * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED.
126 * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've
127 * left the logic alone for now. - dholland 20160826
130 CompatInterrupt(int signo)
132 CompatDeleteTarget(curTarg);
134 if (curTarg != NULL && !GNode_IsPrecious(curTarg)) {
136 * Run .INTERRUPT only if hit with interrupt signal
138 if (signo == SIGINT) {
139 GNode *gn = Targ_FindNode(".INTERRUPT");
146 if (signo == SIGQUIT)
150 * If there is a child running, pass the signal on.
151 * We will exist after it has exited.
154 if (compatChild > 0) {
155 KILLPG(compatChild, signo);
157 bmake_signal(signo, SIG_DFL);
163 DebugFailedTarget(const char *cmd, const GNode *gn)
166 debug_printf("\n*** Failed target: %s\n*** Failed command: ",
170 * Replace runs of whitespace with a single space, to reduce the
171 * amount of whitespace for multi-line command lines.
174 if (ch_isspace(*p)) {
176 cpp_skip_whitespace(&p);
178 debug_printf("%c", *p);
186 UseShell(const char *cmd MAKE_ATTR_UNUSED)
188 #if defined(FORCE_USE_SHELL) || !defined(MAKE_NATIVE)
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.
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
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).
207 return needshell(cmd);
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.
216 * cmdp Command to execute
217 * gn Node from which the command came
218 * ln List node that contains the command
221 * true if the command succeeded.
224 Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
226 char *cmdStart; /* Start of expanded command */
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
239 const char *volatile cmd = cmdp;
241 silent = (gn->type & OP_SILENT) != OP_NONE;
242 errCheck = !(gn->type & OP_IGNORE);
245 cmdStart = Var_Subst(cmd, gn, VARE_WANTRES);
246 /* TODO: handle errors */
248 if (cmdStart[0] == '\0') {
253 LstNode_Set(ln, cmdStart);
255 if (gn->type & OP_SAVE_CMDS) {
256 GNode *endNode = Targ_GetEndNode();
259 * Append the expanded command, to prevent the
260 * local variables from being interpreted in the
261 * scope of the .END node.
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
269 Lst_Append(&endNode->commands, cmdStart);
273 if (strcmp(cmdStart, "...") == 0) {
274 gn->type |= OP_SAVE_CMDS;
280 silent = !DEBUG(LOUD);
281 else if (*cmd == '-')
283 else if (*cmd == '+') {
285 if (shellName == NULL) /* we came here from jobs */
287 } else if (!ch_isspace(*cmd))
288 /* Ignore whitespace for compatibility with gnu make */
293 while (ch_isspace(*cmd))
297 * If we did not end up with a command, just skip it.
302 useShell = UseShell(cmd);
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.
307 if (!silent || !GNode_ShouldExecute(gn)) {
313 * If we're not supposed to execute any commands, this is as far as
316 if (!doIt && !GNode_ShouldExecute(gn))
319 DEBUG1(JOB, "Execute: '%s'\n", cmd);
323 * We need to pass the command off to the shell, typically
324 * because the command contains a "meta" character.
326 static const char *shargv[5];
328 /* The following work for any of the builtin shell specs. */
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;
341 * No meta-characters, so no need to exec a shell. Break the
342 * command into words to form an argument vector we can
345 Words words = Str_Words(cmd, false);
358 compatChild = cpid = vfork();
360 Fatal("Could not fork");
367 (void)execvp(av[0], (char *const *)UNCONST(av));
368 execDie("exec", av[0]);
374 /* XXX: Memory management looks suspicious here. */
375 /* XXX: Setting a list item to NULL is unexpected. */
380 meta_compat_parent(cpid);
384 * The child is off and running. Now all we can do is wait...
386 while ((retstat = wait(&reason)) != cpid) {
388 JobReapChild(retstat, reason, false); /* not ours? */
389 if (retstat == -1 && errno != EINTR) {
395 Fatal("error in wait: %d: %s", retstat, strerror(errno));
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)
403 meta_cmd_finish(NULL);
407 DebugFailedTarget(cmd, gn);
408 printf("*** Error code %d", status);
411 status = WTERMSIG(reason); /* signaled */
412 printf("*** Signal %d", status);
416 if (!WIFEXITED(reason) || status != 0) {
420 meta_job_error(NULL, gn, false, status);
423 if (opts.keepgoing) {
425 * Abort the current target,
426 * but let others continue.
428 printf(" (continuing)\n");
433 CompatDeleteTarget(gn);
436 * Continue executing commands for this target.
437 * If we return 0, this will happen...
439 printf(" (ignored)\n");
446 if (compatSigno != 0) {
447 bmake_signal(compatSigno, SIG_DFL);
448 kill(myPid, compatSigno);
455 RunCommands(GNode *gn)
459 for (ln = gn->commands.first; ln != NULL; ln = ln->next) {
460 const char *cmd = ln->datum;
461 if (!Compat_RunCommand(cmd, gn, ln))
467 MakeInRandomOrder(GNode **gnodes, GNode **end, GNode *pgn)
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];
480 for (it = gnodes; it != end; it++)
481 Compat_Make(*it, pgn);
485 MakeWaitGroupsInRandomOrder(GNodeList *gnodes, GNode *pgn)
492 Vector_Init(&vec, sizeof(GNode *));
493 for (ln = gnodes->first; ln != NULL; ln = ln->next)
494 *(GNode **)Vector_Push(&vec) = ln->datum;
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);
506 MakeInRandomOrder(nodes + start, nodes + i, pgn);
512 MakeNodes(GNodeList *gnodes, GNode *pgn)
516 if (Lst_IsEmpty(gnodes))
518 if (opts.randomizeTargets) {
519 MakeWaitGroupsInRandomOrder(gnodes, pgn);
523 for (ln = gnodes->first; ln != NULL; ln = ln->next) {
524 GNode *cgn = ln->datum;
525 Compat_Make(cgn, pgn);
530 MakeUnmade(GNode *gn, GNode *pgn)
533 assert(gn->made == UNMADE);
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.
543 gn->flags.remake = true;
544 gn->made = BEINGMADE;
546 if (!(gn->type & OP_MADE))
549 MakeNodes(&gn->children, gn);
551 if (!gn->flags.remake) {
553 pgn->flags.remake = false;
557 if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL)
558 Var_Set(pgn, IMPSRC, GNode_VarTarget(gn));
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.
566 DEBUG1(MAKE, "Examining %s...", gn->name);
567 if (!GNode_IsOODate(gn)) {
569 DEBUG0(MAKE, "up-to-date.\n");
574 * If the user is just seeing if something is out-of-date, exit now
575 * to tell him/her "yes".
577 DEBUG0(MAKE, "out-of-date.\n");
578 if (opts.query && gn != Targ_GetEndNode())
582 * We need to be re-made.
583 * Ensure that $? (.OODATE) and $> (.ALLSRC) are both set.
585 GNode_SetLocalVars(gn);
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.
591 if (opts.ignoreErrors)
592 gn->type |= OP_IGNORE;
594 gn->type |= OP_SILENT;
596 if (Job_CheckCommands(gn, Fatal)) {
598 * Our commands are ok, but we still have to worry about
601 if (!opts.touch || (gn->type & OP_MAKE)) {
604 if (useMeta && GNode_ShouldExecute(gn))
605 meta_job_start(NULL, gn);
610 Job_Touch(gn, (gn->type & OP_SILENT) != OP_NONE);
616 if (useMeta && GNode_ShouldExecute(gn)) {
617 if (meta_job_finish(NULL) != 0)
622 if (gn->made != ERROR) {
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.
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);
635 } else if (opts.keepgoing) {
636 pgn->flags.remake = false;
638 PrintOnError(gn, "\nStop.\n");
645 MakeOther(GNode *gn, GNode *pgn)
648 if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) {
649 const char *target = GNode_VarTarget(gn);
650 Var_Set(pgn, IMPSRC, target != NULL ? target : "");
655 Error("Graph cycles through %s", gn->name);
657 pgn->flags.remake = false;
660 if (!(gn->type & OP_EXEC)) {
661 pgn->flags.childMade = true;
662 GNode_UpdateYoungestChild(pgn, gn);
666 if (!(gn->type & OP_EXEC))
667 GNode_UpdateYoungestChild(pgn, gn);
677 * If an error is detected and not being ignored, the process exits.
680 * gn The node to make
681 * pgn Parent to abort if necessary
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.
693 Compat_Make(GNode *gn, GNode *pgn)
695 if (shellName == NULL) /* we came here from jobs */
698 if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
699 if (!MakeUnmade(gn, pgn))
702 /* XXX: Replace with GNode_IsError(gn) */
703 } else if (gn->made == ERROR) {
705 * Already had an error when making this.
706 * Tell the parent to abort.
708 pgn->flags.remake = false;
714 MakeNodes(&gn->cohorts, pgn);
720 GNode *gn = Targ_FindNode(".BEGIN");
725 if (GNode_IsError(gn)) {
726 PrintOnError(gn, "\nStop.\n");
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);
745 Compat_MakeAll(GNodeList *targs)
747 GNode *errorNode = NULL;
749 if (shellName == NULL)
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.
759 (void)Targ_GetEndNode();
765 * Expand .USE nodes right now, because they can modify the structure
768 Make_ExpandUse(targs);
770 while (!Lst_IsEmpty(targs)) {
771 GNode *gn = Lst_Dequeue(targs);
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",
780 if (GNode_IsError(gn) && errorNode == NULL)
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))
792 if (errorNode != NULL) {
795 else if (DEBUG(GRAPH3))
797 PrintOnError(errorNode, "\nStop.\n");