|
|
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.