compat.c revision 1.55 1 /* $NetBSD: compat.c,v 1.55 2004/07/01 04:39:30 jmc 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 #ifndef MAKE_NATIVE
73 static char rcsid[] = "$NetBSD: compat.c,v 1.55 2004/07/01 04:39:30 jmc Exp $";
74 #else
75 #include <sys/cdefs.h>
76 #ifndef lint
77 #if 0
78 static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94";
79 #else
80 __RCSID("$NetBSD: compat.c,v 1.55 2004/07/01 04:39:30 jmc Exp $");
81 #endif
82 #endif /* not lint */
83 #endif
84
85 /*-
86 * compat.c --
87 * The routines in this file implement the full-compatibility
88 * mode of PMake. Most of the special functionality of PMake
89 * is available in this mode. Things not supported:
90 * - different shells.
91 * - friendly variable substitution.
92 *
93 * Interface:
94 * Compat_Run Initialize things for this module and recreate
95 * thems as need creatin'
96 */
97
98 #include <sys/types.h>
99 #include <sys/stat.h>
100 #include <sys/wait.h>
101
102 #include <ctype.h>
103 #include <errno.h>
104 #include <signal.h>
105 #include <stdio.h>
106
107 #include "make.h"
108 #include "hash.h"
109 #include "dir.h"
110 #include "job.h"
111 #include "pathnames.h"
112
113 /*
114 * The following array is used to make a fast determination of which
115 * characters are interpreted specially by the shell. If a command
116 * contains any of these characters, it is executed by the shell, not
117 * directly by us.
118 */
119
120 static char meta[256];
121
122 static GNode *curTarg = NILGNODE;
123 static GNode *ENDNode;
124 static void CompatInterrupt(int);
125 static int CompatMake(ClientData, ClientData);
126
127 static void
128 Compat_Init(void)
129 {
130 const char *cp;
131
132 Shell_Init(); /* setup default shell */
133
134 for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) {
135 meta[(unsigned char) *cp] = 1;
136 }
137 /*
138 * The null character serves as a sentinel in the string.
139 */
140 meta[0] = 1;
141 }
142
143 /*-
144 *-----------------------------------------------------------------------
145 * CompatInterrupt --
146 * Interrupt the creation of the current target and remove it if
147 * it ain't precious.
148 *
149 * Results:
150 * None.
151 *
152 * Side Effects:
153 * The target is removed and the process exits. If .INTERRUPT exists,
154 * its commands are run first WITH INTERRUPTS IGNORED..
155 *
156 *-----------------------------------------------------------------------
157 */
158 static void
159 CompatInterrupt(int signo)
160 {
161 GNode *gn;
162
163 if ((curTarg != NILGNODE) && !Targ_Precious (curTarg)) {
164 char *p1;
165 char *file = Var_Value (TARGET, curTarg, &p1);
166
167 if (!noExecute && eunlink(file) != -1) {
168 Error("*** %s removed", file);
169 }
170 if (p1)
171 free(p1);
172
173 /*
174 * Run .INTERRUPT only if hit with interrupt signal
175 */
176 if (signo == SIGINT) {
177 gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
178 if (gn != NILGNODE) {
179 Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn);
180 }
181 }
182
183 }
184 exit (signo);
185 }
186
187 /*-
189 *-----------------------------------------------------------------------
190 * CompatRunCommand --
191 * Execute the next command for a target. If the command returns an
192 * error, the node's made field is set to ERROR and creation stops.
193 *
194 * Input:
195 * cmdp Command to execute
196 * gnp Node from which the command came
197 *
198 * Results:
199 * 0 if the command succeeded, 1 if an error occurred.
200 *
201 * Side Effects:
202 * The node's 'made' field may be set to ERROR.
203 *
204 *-----------------------------------------------------------------------
205 */
206 int
207 CompatRunCommand(ClientData cmdp, ClientData gnp)
208 {
209 char *cmdStart; /* Start of expanded command */
210 char *cp, *bp;
211 Boolean silent, /* Don't print command */
212 doIt, /* Execute even if -n */
213 errCheck; /* Check errors */
214 int reason; /* Reason for child's death */
215 int status; /* Description of child's death */
216 int cpid; /* Child actually found */
217 ReturnStatus retstat; /* Status of fork */
218 LstNode cmdNode; /* Node where current command is located */
219 const char **av; /* Argument vector for thing to exec */
220 int argc; /* Number of arguments in av or 0 if not
221 * dynamically allocated */
222 Boolean local; /* TRUE if command should be executed
223 * locally */
224 char *cmd = (char *) cmdp;
225 GNode *gn = (GNode *) gnp;
226
227 /*
228 * Avoid clobbered variable warnings by forcing the compiler
229 * to ``unregister'' variables
230 */
231 #if __GNUC__
232 (void) &av;
233 (void) &errCheck;
234 (void) &cmd;
235 #endif
236 silent = gn->type & OP_SILENT;
237 errCheck = !(gn->type & OP_IGNORE);
238 doIt = FALSE;
239
240 cmdNode = Lst_Member (gn->commands, (ClientData)cmd);
241 cmdStart = Var_Subst (NULL, cmd, gn, FALSE);
242
243 /*
244 * brk_string will return an argv with a NULL in av[0], thus causing
245 * execvp to choke and die horribly. Besides, how can we execute a null
246 * command? In any case, we warn the user that the command expanded to
247 * nothing (is this the right thing to do?).
248 */
249
250 if (*cmdStart == '\0') {
251 free(cmdStart);
252 Error("%s expands to empty string", cmd);
253 return(0);
254 } else {
255 cmd = cmdStart;
256 }
257 Lst_Replace (cmdNode, (ClientData)cmdStart);
258
259 if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
260 (void)Lst_AtEnd(ENDNode->commands, (ClientData)cmdStart);
261 return(0);
262 } else if (strcmp(cmdStart, "...") == 0) {
263 gn->type |= OP_SAVE_CMDS;
264 return(0);
265 }
266
267 while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) {
268 switch (*cmd) {
269 case '@':
270 silent = TRUE;
271 break;
272 case '-':
273 errCheck = FALSE;
274 break;
275 case '+':
276 doIt = TRUE;
277 if (!meta[0]) /* we came here from jobs */
278 Compat_Init();
279 break;
280 }
281 cmd++;
282 }
283
284 while (isspace((unsigned char)*cmd))
285 cmd++;
286
287 /*
288 * Search for meta characters in the command. If there are no meta
289 * characters, there's no need to execute a shell to execute the
290 * command.
291 */
292 for (cp = cmd; !meta[(unsigned char)*cp]; cp++) {
293 continue;
294 }
295
296 /*
297 * Print the command before echoing if we're not supposed to be quiet for
298 * this one. We also print the command if -n given.
299 */
300 if (!silent || NoExecute(gn)) {
301 printf ("%s\n", cmd);
302 fflush(stdout);
303 }
304
305 /*
306 * If we're not supposed to execute any commands, this is as far as
307 * we go...
308 */
309 if (!doIt && NoExecute(gn)) {
310 return (0);
311 }
312
313 if (*cp != '\0') {
314 /*
315 * If *cp isn't the null character, we hit a "meta" character and
316 * need to pass the command off to the shell.
317 */
318 static const char *shargv[4];
319
320 shargv[0] = shellPath;
321 /*
322 * The following work for any of the builtin shell specs.
323 */
324 if (DEBUG(SHELL))
325 shargv[1] = "-xc";
326 else
327 shargv[1] = "-c";
328 shargv[2] = cmd;
329 shargv[3] = (char *)NULL;
330 av = shargv;
331 argc = 0;
332 bp = NULL;
333 } else {
334 /*
335 * No meta-characters, so no need to exec a shell. Break the command
336 * into words to form an argument vector we can execute.
337 */
338 av = (const char **)brk_string(cmd, &argc, TRUE, &bp);
339 }
340
341 local = TRUE;
342
343 /*
344 * Fork and execute the single command. If the fork fails, we abort.
345 */
346 cpid = vfork();
347 if (cpid < 0) {
348 Fatal("Could not fork");
349 }
350 if (cpid == 0) {
351 Check_Cwd(av);
352 if (local)
353 (void)execvp(av[0], (char *const *)UNCONST(av));
354 else
355 (void)execv(av[0], (char *const *)UNCONST(av));
356 execError("exec", av[0]);
357 _exit(1);
358 }
359 if (bp) {
360 free(av);
361 free(bp);
362 }
363 Lst_Replace (cmdNode, (ClientData) NULL);
364
365 /*
366 * The child is off and running. Now all we can do is wait...
367 */
368 while (1) {
369
370 while ((retstat = wait(&reason)) != cpid) {
371 if (retstat == -1 && errno != EINTR) {
372 break;
373 }
374 }
375
376 if (retstat > -1) {
377 if (WIFSTOPPED(reason)) {
378 status = WSTOPSIG(reason); /* stopped */
379 } else if (WIFEXITED(reason)) {
380 status = WEXITSTATUS(reason); /* exited */
381 if (status != 0) {
382 if (DEBUG(ERROR)) {
383 printf("\n*** Failed target: %s\n*** Failed command: ",
384 gn->name);
385 for (cp = cmd; *cp; ) {
386 if (isspace((unsigned char)*cp)) {
387 putchar(' ');
388 while (isspace((unsigned char)*cp))
389 cp++;
390 } else {
391 putchar(*cp);
392 cp++;
393 }
394 }
395 printf ("\n");
396 }
397 printf ("*** Error code %d", status);
398 }
399 } else {
400 status = WTERMSIG(reason); /* signaled */
401 printf ("*** Signal %d", status);
402 }
403
404
405 if (!WIFEXITED(reason) || (status != 0)) {
406 if (errCheck) {
407 gn->made = ERROR;
408 if (keepgoing) {
409 /*
410 * Abort the current target, but let others
411 * continue.
412 */
413 printf (" (continuing)\n");
414 }
415 } else {
416 /*
417 * Continue executing commands for this target.
418 * If we return 0, this will happen...
419 */
420 printf (" (ignored)\n");
421 status = 0;
422 }
423 }
424 break;
425 } else {
426 Fatal ("error in wait: %d: %s", retstat, strerror(errno));
427 /*NOTREACHED*/
428 }
429 }
430 free(cmdStart);
431
432 return (status);
433 }
434
435 /*-
437 *-----------------------------------------------------------------------
438 * CompatMake --
439 * Make a target.
440 *
441 * Input:
442 * gnp The node to make
443 * pgnp Parent to abort if necessary
444 *
445 * Results:
446 * 0
447 *
448 * Side Effects:
449 * If an error is detected and not being ignored, the process exits.
450 *
451 *-----------------------------------------------------------------------
452 */
453 static int
454 CompatMake(ClientData gnp, ClientData pgnp)
455 {
456 GNode *gn = (GNode *) gnp;
457 GNode *pgn = (GNode *) pgnp;
458
459 if (gn->made == UNMADE && (gn == pgn || (pgn->type & OP_MADE) == 0)) {
460 /*
461 * First mark ourselves to be made, then apply whatever transformations
462 * the suffix module thinks are necessary. Once that's done, we can
463 * descend and make all our children. If any of them has an error
464 * but the -k flag was given, our 'make' field will be set FALSE again.
465 * This is our signal to not attempt to do anything but abort our
466 * parent as well.
467 */
468 gn->flags |= REMAKE;
469 gn->made = BEINGMADE;
470 if ((gn->type & OP_MADE) == 0)
471 Suff_FindDeps (gn);
472 Lst_ForEach (gn->children, CompatMake, (ClientData)gn);
473 if ((gn->flags & REMAKE) == 0) {
474 gn->made = ABORTED;
475 pgn->flags &= ~REMAKE;
476 goto cohorts;
477 }
478
479 if (Lst_Member (gn->iParents, pgn) != NILLNODE) {
480 char *p1;
481 Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0);
482 if (p1)
483 free(p1);
484 }
485
486 /*
487 * All the children were made ok. Now cmtime contains the modification
488 * time of the newest child, we need to find out if we exist and when
489 * we were modified last. The criteria for datedness are defined by the
490 * Make_OODate function.
491 */
492 if (DEBUG(MAKE)) {
493 printf("Examining %s...", gn->name);
494 }
495 if (! Make_OODate(gn)) {
496 gn->made = UPTODATE;
497 if (DEBUG(MAKE)) {
498 printf("up-to-date.\n");
499 }
500 goto cohorts;
501 } else if (DEBUG(MAKE)) {
502 printf("out-of-date.\n");
503 }
504
505 /*
506 * If the user is just seeing if something is out-of-date, exit now
507 * to tell him/her "yes".
508 */
509 if (queryFlag) {
510 exit (1);
511 }
512
513 /*
514 * We need to be re-made. We also have to make sure we've got a $?
515 * variable. To be nice, we also define the $> variable using
516 * Make_DoAllVar().
517 */
518 Make_DoAllVar(gn);
519
520 /*
521 * Alter our type to tell if errors should be ignored or things
522 * should not be printed so CompatRunCommand knows what to do.
523 */
524 if (Targ_Ignore (gn)) {
525 gn->type |= OP_IGNORE;
526 }
527 if (Targ_Silent (gn)) {
528 gn->type |= OP_SILENT;
529 }
530
531 if (Job_CheckCommands (gn, Fatal)) {
532 /*
533 * Our commands are ok, but we still have to worry about the -t
534 * flag...
535 */
536 if (!touchFlag || (gn->type & OP_MAKE)) {
537 curTarg = gn;
538 Lst_ForEach (gn->commands, CompatRunCommand, (ClientData)gn);
539 curTarg = NILGNODE;
540 } else {
541 Job_Touch (gn, gn->type & OP_SILENT);
542 }
543 } else {
544 gn->made = ERROR;
545 }
546
547 if (gn->made != ERROR) {
548 /*
549 * If the node was made successfully, mark it so, update
550 * its modification time and timestamp all its parents. Note
551 * that for .ZEROTIME targets, the timestamping isn't done.
552 * This is to keep its state from affecting that of its parent.
553 */
554 gn->made = MADE;
555 pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0;
556 if (!(gn->type & OP_EXEC)) {
557 pgn->flags |= CHILDMADE;
558 Make_TimeStamp(pgn, gn);
559 }
560 } else if (keepgoing) {
561 pgn->flags &= ~REMAKE;
562 } else {
563 PrintOnError("\n\nStop.");
564 exit (1);
565 }
566 } else if (gn->made == ERROR) {
567 /*
568 * Already had an error when making this beastie. Tell the parent
569 * to abort.
570 */
571 pgn->flags &= ~REMAKE;
572 } else {
573 if (Lst_Member (gn->iParents, pgn) != NILLNODE) {
574 char *p1;
575 Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0);
576 if (p1)
577 free(p1);
578 }
579 switch(gn->made) {
580 case BEINGMADE:
581 Error("Graph cycles through %s", gn->name);
582 gn->made = ERROR;
583 pgn->flags &= ~REMAKE;
584 break;
585 case MADE:
586 if ((gn->type & OP_EXEC) == 0) {
587 pgn->flags |= CHILDMADE;
588 Make_TimeStamp(pgn, gn);
589 }
590 break;
591 case UPTODATE:
592 if ((gn->type & OP_EXEC) == 0) {
593 Make_TimeStamp(pgn, gn);
594 }
595 break;
596 default:
597 break;
598 }
599 }
600
601 cohorts:
602 Lst_ForEach (gn->cohorts, CompatMake, pgnp);
603 return (0);
604 }
605
606 /*-
608 *-----------------------------------------------------------------------
609 * Compat_Run --
610 * Initialize this mode and start making.
611 *
612 * Input:
613 * targs List of target nodes to re-create
614 *
615 * Results:
616 * None.
617 *
618 * Side Effects:
619 * Guess what?
620 *
621 *-----------------------------------------------------------------------
622 */
623 void
624 Compat_Run(Lst targs)
625 {
626 GNode *gn = NULL;/* Current root target */
627 int errors; /* Number of targets not remade due to errors */
628
629 Compat_Init();
630
631 if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
632 signal(SIGINT, CompatInterrupt);
633 }
634 if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
635 signal(SIGTERM, CompatInterrupt);
636 }
637 if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
638 signal(SIGHUP, CompatInterrupt);
639 }
640 if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
641 signal(SIGQUIT, CompatInterrupt);
642 }
643
644 ENDNode = Targ_FindNode(".END", TARG_CREATE);
645 /*
646 * If the user has defined a .BEGIN target, execute the commands attached
647 * to it.
648 */
649 if (!queryFlag) {
650 gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
651 if (gn != NILGNODE) {
652 Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn);
653 if (gn->made == ERROR) {
654 PrintOnError("\n\nStop.");
655 exit(1);
656 }
657 }
658 }
659
660 /*
661 * Expand .USE nodes right now, because they can modify the structure
662 * of the tree.
663 */
664 Lst_Destroy(Make_ExpandUse(targs), NOFREE);
665
666 /*
667 * For each entry in the list of targets to create, call CompatMake on
668 * it to create the thing. CompatMake will leave the 'made' field of gn
669 * in one of several states:
670 * UPTODATE gn was already up-to-date
671 * MADE gn was recreated successfully
672 * ERROR An error occurred while gn was being created
673 * ABORTED gn was not remade because one of its inferiors
674 * could not be made due to errors.
675 */
676 errors = 0;
677 while (!Lst_IsEmpty (targs)) {
678 gn = (GNode *) Lst_DeQueue (targs);
679 CompatMake (gn, gn);
680
681 if (gn->made == UPTODATE) {
682 printf ("`%s' is up to date.\n", gn->name);
683 } else if (gn->made == ABORTED) {
684 printf ("`%s' not remade because of errors.\n", gn->name);
685 errors += 1;
686 }
687 }
688
689 /*
690 * If the user has defined a .END target, run its commands.
691 */
692 if (errors == 0) {
693 Lst_ForEach(ENDNode->commands, CompatRunCommand, (ClientData)gn);
694 if (gn->made == ERROR) {
695 PrintOnError("\n\nStop.");
696 exit(1);
697 }
698 }
699 }
700