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