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