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