VirtualBox

source: kBuild/branches/FREEBSD/src/kmk/compat.c@ 10

Last change on this file since 10 was 10, checked in by (none), 23 years ago

This commit was manufactured by cvs2svn to create branch 'FREEBSD'.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette