VirtualBox

source: kBuild/trunk/src/kmk/compat.c@ 25

Last change on this file since 25 was 25, checked in by bird, 22 years ago

This commit was generated by cvs2svn to compensate for changes in r24,
which included commits to RCS files with non-trunk default branches.

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