xref: /netbsd-src/usr.bin/make/compat.c (revision 2a399c6883d870daece976daec6ffa7bb7f934ce)
1 /*	$NetBSD: compat.c,v 1.23 1997/12/31 06:06:12 thorpej Exp $	*/
2 
3 /*
4  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
5  * Copyright (c) 1988, 1989 by Adam de Boor
6  * Copyright (c) 1989 by Berkeley Softworks
7  * All rights reserved.
8  *
9  * This code is derived from software contributed to Berkeley by
10  * Adam de Boor.
11  *
12  * Redistribution and use in source and binary forms, with or without
13  * modification, are permitted provided that the following conditions
14  * are met:
15  * 1. Redistributions of source code must retain the above copyright
16  *    notice, this list of conditions and the following disclaimer.
17  * 2. Redistributions in binary form must reproduce the above copyright
18  *    notice, this list of conditions and the following disclaimer in the
19  *    documentation and/or other materials provided with the distribution.
20  * 3. All advertising materials mentioning features or use of this software
21  *    must display the following acknowledgement:
22  *	This product includes software developed by the University of
23  *	California, Berkeley and its contributors.
24  * 4. Neither the name of the University nor the names of its contributors
25  *    may be used to endorse or promote products derived from this software
26  *    without specific prior written permission.
27  *
28  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
29  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
32  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38  * SUCH DAMAGE.
39  */
40 
41 #ifdef MAKE_BOOTSTRAP
42 static char rcsid[] = "$NetBSD: compat.c,v 1.23 1997/12/31 06:06:12 thorpej Exp $";
43 #else
44 #include <sys/cdefs.h>
45 #ifndef lint
46 #if 0
47 static char sccsid[] = "@(#)compat.c	8.2 (Berkeley) 3/19/94";
48 #else
49 __RCSID("$NetBSD: compat.c,v 1.23 1997/12/31 06:06:12 thorpej Exp $");
50 #endif
51 #endif /* not lint */
52 #endif
53 
54 /*-
55  * compat.c --
56  *	The routines in this file implement the full-compatibility
57  *	mode of PMake. Most of the special functionality of PMake
58  *	is available in this mode. Things not supported:
59  *	    - different shells.
60  *	    - friendly variable substitution.
61  *
62  * Interface:
63  *	Compat_Run	    Initialize things for this module and recreate
64  *	    	  	    thems as need creatin'
65  */
66 
67 #include    <stdio.h>
68 #include    <sys/types.h>
69 #include    <sys/stat.h>
70 #include    <sys/wait.h>
71 #include    <ctype.h>
72 #include    <errno.h>
73 #include    <signal.h>
74 #include    "make.h"
75 #include    "hash.h"
76 #include    "dir.h"
77 #include    "job.h"
78 extern int errno;
79 
80 /*
81  * The following array is used to make a fast determination of which
82  * characters are interpreted specially by the shell.  If a command
83  * contains any of these characters, it is executed by the shell, not
84  * directly by us.
85  */
86 
87 static char 	    meta[256];
88 
89 static GNode	    *curTarg = NILGNODE;
90 static GNode	    *ENDNode;
91 static void CompatInterrupt __P((int));
92 static int CompatRunCommand __P((ClientData, ClientData));
93 static int CompatMake __P((ClientData, ClientData));
94 
95 /*-
96  *-----------------------------------------------------------------------
97  * CompatInterrupt --
98  *	Interrupt the creation of the current target and remove it if
99  *	it ain't precious.
100  *
101  * Results:
102  *	None.
103  *
104  * Side Effects:
105  *	The target is removed and the process exits. If .INTERRUPT exists,
106  *	its commands are run first WITH INTERRUPTS IGNORED..
107  *
108  *-----------------------------------------------------------------------
109  */
110 static void
111 CompatInterrupt (signo)
112     int	    signo;
113 {
114     GNode   *gn;
115 
116     if ((curTarg != NILGNODE) && !Targ_Precious (curTarg)) {
117 	char	  *p1;
118 	char 	  *file = Var_Value (TARGET, curTarg, &p1);
119 
120 	if (!noExecute && eunlink(file) != -1) {
121 	    Error("*** %s removed\n", file);
122 	}
123 	if (p1)
124 	    free(p1);
125 
126 	/*
127 	 * Run .INTERRUPT only if hit with interrupt signal
128 	 */
129 	if (signo == SIGINT) {
130 	    gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE);
131 	    if (gn != NILGNODE) {
132 		Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn);
133 	    }
134 	}
135 
136     }
137     exit (signo);
138 }
139 
140 /*-
141  *-----------------------------------------------------------------------
142  * CompatRunCommand --
143  *	Execute the next command for a target. If the command returns an
144  *	error, the node's made field is set to ERROR and creation stops.
145  *
146  * Results:
147  *	0 if the command succeeded, 1 if an error occurred.
148  *
149  * Side Effects:
150  *	The node's 'made' field may be set to ERROR.
151  *
152  *-----------------------------------------------------------------------
153  */
154 static int
155 CompatRunCommand (cmdp, gnp)
156     ClientData    cmdp;	    	/* Command to execute */
157     ClientData    gnp;    	/* Node from which the command came */
158 {
159     char    	  *cmdStart;	/* Start of expanded command */
160     register char *cp;
161     Boolean 	  silent,   	/* Don't print command */
162 		  errCheck; 	/* Check errors */
163     int 	  reason;   	/* Reason for child's death */
164     int	    	  status;   	/* Description of child's death */
165     int	    	  cpid;	    	/* Child actually found */
166     ReturnStatus  stat;	    	/* Status of fork */
167     LstNode 	  cmdNode;  	/* Node where current command is located */
168     char    	  **av;	    	/* Argument vector for thing to exec */
169     int	    	  argc;	    	/* Number of arguments in av or 0 if not
170 				 * dynamically allocated */
171     Boolean 	  local;    	/* TRUE if command should be executed
172 				 * locally */
173     char	  *cmd = (char *) cmdp;
174     GNode	  *gn = (GNode *) gnp;
175 
176     /*
177      * Avoid clobbered variable warnings by forcing the compiler
178      * to ``unregister'' variables
179      */
180 #if __GNUC__
181     (void) &av;
182     (void) &errCheck;
183 #endif
184     silent = gn->type & OP_SILENT;
185     errCheck = !(gn->type & OP_IGNORE);
186 
187     cmdNode = Lst_Member (gn->commands, (ClientData)cmd);
188     cmdStart = Var_Subst (NULL, cmd, gn, FALSE);
189 
190     /*
191      * brk_string will return an argv with a NULL in av[1], thus causing
192      * execvp to choke and die horribly. Besides, how can we execute a null
193      * command? In any case, we warn the user that the command expanded to
194      * nothing (is this the right thing to do?).
195      */
196 
197     if (*cmdStart == '\0') {
198 	free(cmdStart);
199 	Error("%s expands to empty string", cmd);
200 	return(0);
201     } else {
202 	cmd = cmdStart;
203     }
204     Lst_Replace (cmdNode, (ClientData)cmdStart);
205 
206     if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) {
207 	(void)Lst_AtEnd(ENDNode->commands, (ClientData)cmdStart);
208 	return(0);
209     } else if (strcmp(cmdStart, "...") == 0) {
210 	gn->type |= OP_SAVE_CMDS;
211 	return(0);
212     }
213 
214     while ((*cmd == '@') || (*cmd == '-')) {
215 	if (*cmd == '@') {
216 	    silent = TRUE;
217 	} else {
218 	    errCheck = FALSE;
219 	}
220 	cmd++;
221     }
222 
223     while (isspace((unsigned char)*cmd))
224 	cmd++;
225 
226     /*
227      * Search for meta characters in the command. If there are no meta
228      * characters, there's no need to execute a shell to execute the
229      * command.
230      */
231     for (cp = cmd; !meta[(unsigned char)*cp]; cp++) {
232 	continue;
233     }
234 
235     /*
236      * Print the command before echoing if we're not supposed to be quiet for
237      * this one. We also print the command if -n given.
238      */
239     if (!silent || (noExecute && !(gn->type & OP_MAKE))) {
240 	printf ("%s\n", cmd);
241 	fflush(stdout);
242     }
243 
244     /*
245      * If we're not supposed to execute any commands, this is as far as
246      * we go...
247      */
248     if (noExecute && !(gn->type & OP_MAKE)) {
249 	return (0);
250     }
251 
252     if (*cp != '\0') {
253 	/*
254 	 * If *cp isn't the null character, we hit a "meta" character and
255 	 * need to pass the command off to the shell. We give the shell the
256 	 * -e flag as well as -c if it's supposed to exit when it hits an
257 	 * error.
258 	 */
259 	static char	*shargv[4] = { "/bin/sh" };
260 
261 	shargv[1] = (errCheck ? "-ec" : "-c");
262 	shargv[2] = cmd;
263 	shargv[3] = (char *)NULL;
264 	av = shargv;
265 	argc = 0;
266     } else {
267 	/*
268 	 * No meta-characters, so no need to exec a shell. Break the command
269 	 * into words to form an argument vector we can execute.
270 	 * brk_string sticks our name in av[0], so we have to
271 	 * skip over it...
272 	 */
273 	av = brk_string(cmd, &argc, TRUE);
274 	av += 1;
275     }
276 
277     local = TRUE;
278 
279     /*
280      * Fork and execute the single command. If the fork fails, we abort.
281      */
282     cpid = vfork();
283     if (cpid < 0) {
284 	Fatal("Could not fork");
285     }
286     if (cpid == 0) {
287 	if (local) {
288 	    execvp(av[0], av);
289 	    (void) write (2, av[0], strlen (av[0]));
290 	    (void) write (2, ": not found\n", sizeof(": not found"));
291 	} else {
292 	    (void)execv(av[0], av);
293 	}
294 	_exit(1);
295     }
296     free(cmdStart);
297     Lst_Replace (cmdNode, (ClientData) NULL);
298 
299     /*
300      * The child is off and running. Now all we can do is wait...
301      */
302     while (1) {
303 
304 	while ((stat = wait(&reason)) != cpid) {
305 	    if (stat == -1 && errno != EINTR) {
306 		break;
307 	    }
308 	}
309 
310 	if (stat > -1) {
311 	    if (WIFSTOPPED(reason)) {
312 		status = WSTOPSIG(reason);		/* stopped */
313 	    } else if (WIFEXITED(reason)) {
314 		status = WEXITSTATUS(reason);		/* exited */
315 		if (status != 0) {
316 		    printf ("*** Error code %d", status);
317 		}
318 	    } else {
319 		status = WTERMSIG(reason);		/* signaled */
320 		printf ("*** Signal %d", status);
321 	    }
322 
323 
324 	    if (!WIFEXITED(reason) || (status != 0)) {
325 		if (errCheck) {
326 		    gn->made = ERROR;
327 		    if (keepgoing) {
328 			/*
329 			 * Abort the current target, but let others
330 			 * continue.
331 			 */
332 			printf (" (continuing)\n");
333 		    }
334 		} else {
335 		    /*
336 		     * Continue executing commands for this target.
337 		     * If we return 0, this will happen...
338 		     */
339 		    printf (" (ignored)\n");
340 		    status = 0;
341 		}
342 	    }
343 	    break;
344 	} else {
345 	    Fatal ("error in wait: %d", stat);
346 	    /*NOTREACHED*/
347 	}
348     }
349 
350     return (status);
351 }
352 
353 /*-
354  *-----------------------------------------------------------------------
355  * CompatMake --
356  *	Make a target.
357  *
358  * Results:
359  *	0
360  *
361  * Side Effects:
362  *	If an error is detected and not being ignored, the process exits.
363  *
364  *-----------------------------------------------------------------------
365  */
366 static int
367 CompatMake (gnp, pgnp)
368     ClientData	gnp;	    /* The node to make */
369     ClientData  pgnp;	    /* Parent to abort if necessary */
370 {
371     GNode *gn = (GNode *) gnp;
372     GNode *pgn = (GNode *) pgnp;
373 
374     if (pgn->type & OP_MADE) {
375 	(void) Dir_MTime(gn);
376 	gn->made = UPTODATE;
377     }
378 
379     if (gn->made == UNMADE) {
380 	/*
381 	 * First mark ourselves to be made, then apply whatever transformations
382 	 * the suffix module thinks are necessary. Once that's done, we can
383 	 * descend and make all our children. If any of them has an error
384 	 * but the -k flag was given, our 'make' field will be set FALSE again.
385 	 * This is our signal to not attempt to do anything but abort our
386 	 * parent as well.
387 	 */
388 	gn->make = TRUE;
389 	gn->made = BEINGMADE;
390 	Suff_FindDeps (gn);
391 	Lst_ForEach (gn->children, CompatMake, (ClientData)gn);
392 	if (!gn->make) {
393 	    gn->made = ABORTED;
394 	    pgn->make = FALSE;
395 	    return (0);
396 	}
397 
398 	if (Lst_Member (gn->iParents, pgn) != NILLNODE) {
399 	    char *p1;
400 	    Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
401 	    if (p1)
402 		free(p1);
403 	}
404 
405 	/*
406 	 * All the children were made ok. Now cmtime contains the modification
407 	 * time of the newest child, we need to find out if we exist and when
408 	 * we were modified last. The criteria for datedness are defined by the
409 	 * Make_OODate function.
410 	 */
411 	if (DEBUG(MAKE)) {
412 	    printf("Examining %s...", gn->name);
413 	}
414 	if (! Make_OODate(gn)) {
415 	    gn->made = UPTODATE;
416 	    if (DEBUG(MAKE)) {
417 		printf("up-to-date.\n");
418 	    }
419 	    return (0);
420 	} else if (DEBUG(MAKE)) {
421 	    printf("out-of-date.\n");
422 	}
423 
424 	/*
425 	 * If the user is just seeing if something is out-of-date, exit now
426 	 * to tell him/her "yes".
427 	 */
428 	if (queryFlag) {
429 	    exit (-1);
430 	}
431 
432 	/*
433 	 * We need to be re-made. We also have to make sure we've got a $?
434 	 * variable. To be nice, we also define the $> variable using
435 	 * Make_DoAllVar().
436 	 */
437 	Make_DoAllVar(gn);
438 
439 	/*
440 	 * Alter our type to tell if errors should be ignored or things
441 	 * should not be printed so CompatRunCommand knows what to do.
442 	 */
443 	if (Targ_Ignore (gn)) {
444 	    gn->type |= OP_IGNORE;
445 	}
446 	if (Targ_Silent (gn)) {
447 	    gn->type |= OP_SILENT;
448 	}
449 
450 	if (Job_CheckCommands (gn, Fatal)) {
451 	    /*
452 	     * Our commands are ok, but we still have to worry about the -t
453 	     * flag...
454 	     */
455 	    if (!touchFlag || (gn->type & OP_MAKE)) {
456 		curTarg = gn;
457 		Lst_ForEach (gn->commands, CompatRunCommand, (ClientData)gn);
458 		curTarg = NILGNODE;
459 	    } else {
460 		Job_Touch (gn, gn->type & OP_SILENT);
461 	    }
462 	} else {
463 	    gn->made = ERROR;
464 	}
465 
466 	if (gn->made != ERROR) {
467 	    /*
468 	     * If the node was made successfully, mark it so, update
469 	     * its modification time and timestamp all its parents. Note
470 	     * that for .ZEROTIME targets, the timestamping isn't done.
471 	     * This is to keep its state from affecting that of its parent.
472 	     */
473 	    gn->made = MADE;
474 #ifndef RECHECK
475 	    /*
476 	     * We can't re-stat the thing, but we can at least take care of
477 	     * rules where a target depends on a source that actually creates
478 	     * the target, but only if it has changed, e.g.
479 	     *
480 	     * parse.h : parse.o
481 	     *
482 	     * parse.o : parse.y
483 	     *  	yacc -d parse.y
484 	     *  	cc -c y.tab.c
485 	     *  	mv y.tab.o parse.o
486 	     *  	cmp -s y.tab.h parse.h || mv y.tab.h parse.h
487 	     *
488 	     * In this case, if the definitions produced by yacc haven't
489 	     * changed from before, parse.h won't have been updated and
490 	     * gn->mtime will reflect the current modification time for
491 	     * parse.h. This is something of a kludge, I admit, but it's a
492 	     * useful one..
493 	     *
494 	     * XXX: People like to use a rule like
495 	     *
496 	     * FRC:
497 	     *
498 	     * To force things that depend on FRC to be made, so we have to
499 	     * check for gn->children being empty as well...
500 	     */
501 	    if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) {
502 		gn->mtime = now;
503 	    }
504 #else
505 	    /*
506 	     * This is what Make does and it's actually a good thing, as it
507 	     * allows rules like
508 	     *
509 	     *	cmp -s y.tab.h parse.h || cp y.tab.h parse.h
510 	     *
511 	     * to function as intended. Unfortunately, thanks to the stateless
512 	     * nature of NFS (and the speed of this program), there are times
513 	     * when the modification time of a file created on a remote
514 	     * machine will not be modified before the stat() implied by
515 	     * the Dir_MTime occurs, thus leading us to believe that the file
516 	     * is unchanged, wreaking havoc with files that depend on this one.
517 	     *
518 	     * I have decided it is better to make too much than to make too
519 	     * little, so this stuff is commented out unless you're sure it's
520 	     * ok.
521 	     * -- ardeb 1/12/88
522 	     */
523 	    if ((noExecute && !(gn->type & OP_MAKE)) ||
524 		(gn->type & OP_SAVE_CMDS) || Dir_MTime(gn) == 0) {
525 		gn->mtime = now;
526 	    }
527 	    if (gn->cmtime > gn->mtime)
528 		gn->mtime = gn->cmtime;
529 	    if (DEBUG(MAKE)) {
530 		printf("update time: %s\n", Targ_FmtTime(gn->mtime));
531 	    }
532 #endif
533 	    if (!(gn->type & OP_EXEC)) {
534 		pgn->childMade = TRUE;
535 		Make_TimeStamp(pgn, gn);
536 	    }
537 	} else if (keepgoing) {
538 	    pgn->make = FALSE;
539 	} else {
540 	    printf ("\n\nStop.\n");
541 	    exit (1);
542 	}
543     } else if (gn->made == ERROR) {
544 	/*
545 	 * Already had an error when making this beastie. Tell the parent
546 	 * to abort.
547 	 */
548 	pgn->make = FALSE;
549     } else {
550 	if (Lst_Member (gn->iParents, pgn) != NILLNODE) {
551 	    char *p1;
552 	    Var_Set (IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
553 	    if (p1)
554 		free(p1);
555 	}
556 	switch(gn->made) {
557 	    case BEINGMADE:
558 		Error("Graph cycles through %s\n", gn->name);
559 		gn->made = ERROR;
560 		pgn->make = FALSE;
561 		break;
562 	    case MADE:
563 		if ((gn->type & OP_EXEC) == 0) {
564 		    pgn->childMade = TRUE;
565 		    Make_TimeStamp(pgn, gn);
566 		}
567 		break;
568 	    case UPTODATE:
569 		if ((gn->type & OP_EXEC) == 0) {
570 		    Make_TimeStamp(pgn, gn);
571 		}
572 		break;
573 	    default:
574 		break;
575 	}
576     }
577 
578     return (0);
579 }
580 
581 /*-
582  *-----------------------------------------------------------------------
583  * Compat_Run --
584  *	Initialize this mode and start making.
585  *
586  * Results:
587  *	None.
588  *
589  * Side Effects:
590  *	Guess what?
591  *
592  *-----------------------------------------------------------------------
593  */
594 void
595 Compat_Run(targs)
596     Lst	    	  targs;    /* List of target nodes to re-create */
597 {
598     char    	  *cp;	    /* Pointer to string of shell meta-characters */
599     GNode   	  *gn = NULL;/* Current root target */
600     int	    	  errors;   /* Number of targets not remade due to errors */
601 
602     if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
603 	signal(SIGINT, CompatInterrupt);
604     }
605     if (signal(SIGTERM, SIG_IGN) != SIG_IGN) {
606 	signal(SIGTERM, CompatInterrupt);
607     }
608     if (signal(SIGHUP, SIG_IGN) != SIG_IGN) {
609 	signal(SIGHUP, CompatInterrupt);
610     }
611     if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) {
612 	signal(SIGQUIT, CompatInterrupt);
613     }
614 
615     for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) {
616 	meta[(unsigned char) *cp] = 1;
617     }
618     /*
619      * The null character serves as a sentinel in the string.
620      */
621     meta[0] = 1;
622 
623     ENDNode = Targ_FindNode(".END", TARG_CREATE);
624     /*
625      * If the user has defined a .BEGIN target, execute the commands attached
626      * to it.
627      */
628     if (!queryFlag) {
629 	gn = Targ_FindNode(".BEGIN", TARG_NOCREATE);
630 	if (gn != NILGNODE) {
631 	    Lst_ForEach(gn->commands, CompatRunCommand, (ClientData)gn);
632             if (gn->made == ERROR) {
633                 printf("\n\nStop.\n");
634                 exit(1);
635             }
636 	}
637     }
638 
639     /*
640      * Expand .USE nodes right now, because they can modify the structure
641      * of the tree.
642      */
643     Lst_Destroy(Make_ExpandUse(targs), NOFREE);
644 
645     /*
646      * For each entry in the list of targets to create, call CompatMake on
647      * it to create the thing. CompatMake will leave the 'made' field of gn
648      * in one of several states:
649      *	    UPTODATE	    gn was already up-to-date
650      *	    MADE  	    gn was recreated successfully
651      *	    ERROR 	    An error occurred while gn was being created
652      *	    ABORTED	    gn was not remade because one of its inferiors
653      *	    	  	    could not be made due to errors.
654      */
655     errors = 0;
656     while (!Lst_IsEmpty (targs)) {
657 	gn = (GNode *) Lst_DeQueue (targs);
658 	CompatMake (gn, gn);
659 
660 	if (gn->made == UPTODATE) {
661 	    printf ("`%s' is up to date.\n", gn->name);
662 	} else if (gn->made == ABORTED) {
663 	    printf ("`%s' not remade because of errors.\n", gn->name);
664 	    errors += 1;
665 	}
666     }
667 
668     /*
669      * If the user has defined a .END target, run its commands.
670      */
671     if (errors == 0) {
672 	Lst_ForEach(ENDNode->commands, CompatRunCommand, (ClientData)gn);
673     }
674 }
675