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