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