1 /* $NetBSD: compat.c,v 1.268 2025/07/06 07:11:31 rillig 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 /* 73 * This file implements the full-compatibility mode of make, which makes the 74 * targets without parallelism and without a custom shell. 75 * 76 * Interface: 77 * Compat_MakeAll Initialize this module and make the given targets. 78 */ 79 80 #include <sys/types.h> 81 #include <sys/stat.h> 82 #include <sys/wait.h> 83 84 #include <errno.h> 85 #include <signal.h> 86 87 #include "make.h" 88 #include "dir.h" 89 #include "job.h" 90 #ifdef USE_META 91 # include "meta.h" 92 #endif 93 #include "metachar.h" 94 #include "pathnames.h" 95 96 /* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */ 97 MAKE_RCSID("$NetBSD: compat.c,v 1.268 2025/07/06 07:11:31 rillig Exp $"); 98 99 static GNode *curTarg; 100 static pid_t compatChild; 101 static int compatSigno; 102 103 /* 104 * Delete the file of a failed, interrupted, or otherwise duffed target, 105 * unless inhibited by .PRECIOUS. 106 */ 107 static void 108 CompatDeleteTarget(GNode *gn) 109 { 110 if (!GNode_IsPrecious(gn) && 111 (gn->type & OP_PHONY) == 0) { 112 const char *file = GNode_VarTarget(gn); 113 if (!opts.noExecute && unlink_file(file) == 0) 114 Error("*** %s removed", file); 115 } 116 } 117 118 /* 119 * Interrupt the creation of the current target and remove it if it ain't 120 * precious. Then exit. 121 * 122 * If .INTERRUPT exists, its commands are run first WITH INTERRUPTS IGNORED. 123 * 124 * XXX: is .PRECIOUS supposed to inhibit .INTERRUPT? I doubt it, but I've 125 * left the logic alone for now. - dholland 20160826 126 */ 127 static void 128 CompatInterrupt(int signo) 129 { 130 if (curTarg != NULL) { 131 CompatDeleteTarget(curTarg); 132 if (signo == SIGINT && !GNode_IsPrecious(curTarg)) { 133 GNode *gn = Targ_FindNode(".INTERRUPT"); 134 if (gn != NULL) 135 Compat_Make(gn, gn); 136 } 137 } 138 139 if (signo == SIGQUIT) 140 _exit(signo); 141 142 /* 143 * If there is a child running, pass the signal on. 144 * We will exist after it has exited. 145 */ 146 compatSigno = signo; 147 if (compatChild > 0) { 148 KILLPG(compatChild, signo); 149 } else { 150 bmake_signal(signo, SIG_DFL); 151 kill(myPid, signo); 152 } 153 } 154 155 static void 156 DebugFailedTarget(const char *cmd, const GNode *gn) 157 { 158 const char *p = cmd; 159 debug_printf("\n*** Failed target: %s\n*** Failed command: ", 160 gn->name); 161 162 /* 163 * Replace runs of whitespace with a single space, to reduce the 164 * amount of whitespace for multi-line command lines. 165 */ 166 while (*p != '\0') { 167 if (ch_isspace(*p)) { 168 debug_printf(" "); 169 cpp_skip_whitespace(&p); 170 } else { 171 debug_printf("%c", *p); 172 p++; 173 } 174 } 175 debug_printf("\n"); 176 } 177 178 static bool 179 UseShell(const char *cmd MAKE_ATTR_UNUSED) 180 { 181 #if !defined(MAKE_NATIVE) 182 /* 183 * In a non-native build, the host environment might be weird enough 184 * that it's necessary to go through a shell to get the correct 185 * behaviour. Or perhaps the shell has been replaced with something 186 * that does extra logging, and that should not be bypassed. 187 */ 188 return true; 189 #else 190 /* 191 * Search for meta characters in the command. If there are no meta 192 * characters, there's no need to execute a shell to execute the 193 * command. 194 * 195 * Additionally variable assignments and empty commands 196 * go to the shell. Therefore treat '=' and ':' like shell 197 * meta characters as documented in make(1). 198 */ 199 200 return needshell(cmd); 201 #endif 202 } 203 204 static int 205 Compat_Spawn(const char **av) 206 { 207 int pid = FORK_FUNCTION(); 208 if (pid < 0) 209 Fatal("Could not fork"); 210 211 if (pid == 0) { 212 #ifdef USE_META 213 if (useMeta) 214 meta_compat_child(); 215 #endif 216 (void)execvp(av[0], (char *const *)UNCONST(av)); 217 execDie("exec", av[0]); 218 } 219 return pid; 220 } 221 222 /* 223 * Execute the next command for a target. If the command returns an error, 224 * the node's made field is set to ERROR and creation stops. 225 * 226 * Input: 227 * cmdp Command to execute 228 * gn Node from which the command came 229 * ln List node that contains the command 230 * 231 * Results: 232 * true if the command succeeded. 233 */ 234 bool 235 Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln) 236 { 237 char *cmdStart; /* Start of expanded command */ 238 char *volatile bp; 239 bool silent; /* Don't print command */ 240 bool doIt; /* Execute even if -n */ 241 volatile bool errCheck; /* Check errors */ 242 int reason; /* Reason for child's death */ 243 int status; /* Description of child's death */ 244 pid_t retstat; /* Result of wait */ 245 const char **av; /* Arguments for the child process */ 246 char **volatile mav; /* Copy of the argument vector for freeing */ 247 bool useShell; /* True if command should be executed using a 248 * shell */ 249 const char *cmd = cmdp; 250 char cmd_file[MAXPATHLEN]; 251 size_t cmd_len; 252 int parseErrorsBefore; 253 254 silent = (gn->type & OP_SILENT) != OP_NONE; 255 errCheck = !(gn->type & OP_IGNORE); 256 doIt = false; 257 258 parseErrorsBefore = parseErrors; 259 cmdStart = Var_SubstInTarget(cmd, gn); 260 if (parseErrors != parseErrorsBefore) { 261 free(cmdStart); 262 return false; 263 } 264 265 if (cmdStart[0] == '\0') { 266 free(cmdStart); 267 return true; 268 } 269 cmd = cmdStart; 270 LstNode_Set(ln, cmdStart); 271 272 if (gn->type & OP_SAVE_CMDS) { 273 GNode *endNode = Targ_GetEndNode(); 274 if (gn != endNode) { 275 /* 276 * Append the expanded command, to prevent the 277 * local variables from being interpreted in the 278 * scope of the .END node. 279 * 280 * A probably unintended side effect of this is that 281 * the expanded command will be expanded again in the 282 * .END node. Therefore, a literal '$' in these 283 * commands must be written as '$$$$' instead of the 284 * usual '$$'. 285 */ 286 Lst_Append(&endNode->commands, cmdStart); 287 goto register_command; 288 } 289 } 290 if (strcmp(cmdStart, "...") == 0) { 291 gn->type |= OP_SAVE_CMDS; 292 register_command: 293 Parse_RegisterCommand(cmdStart); 294 return true; 295 } 296 297 for (;;) { 298 if (*cmd == '@') 299 silent = !DEBUG(LOUD); 300 else if (*cmd == '-') 301 errCheck = false; 302 else if (*cmd == '+') 303 doIt = true; 304 else if (!ch_isspace(*cmd)) 305 /* Ignore whitespace for compatibility with gnu make */ 306 break; 307 cmd++; 308 } 309 310 if (cmd[0] == '\0') 311 goto register_command; 312 313 useShell = UseShell(cmd); 314 315 if (!silent || !GNode_ShouldExecute(gn)) { 316 printf("%s\n", cmd); 317 fflush(stdout); 318 } 319 320 if (!doIt && !GNode_ShouldExecute(gn)) 321 goto register_command; 322 323 DEBUG1(JOB, "Execute: '%s'\n", cmd); 324 325 cmd_len = strlen(cmd); 326 if (cmd_len > MAKE_CMDLEN_LIMIT) 327 useShell = true; 328 else 329 cmd_file[0] = '\0'; 330 331 if (useShell) { 332 static const char *shargv[5]; 333 334 Cmd_Argv(cmd, cmd_len, shargv, cmd_file, sizeof(cmd_file), 335 errCheck && shellErrFlag != NULL, DEBUG(SHELL)); 336 av = shargv; 337 bp = NULL; 338 mav = NULL; 339 } else { 340 Words words = Str_Words(cmd, false); 341 mav = words.words; 342 bp = words.freeIt; 343 av = (void *)mav; 344 } 345 346 #ifdef USE_META 347 if (useMeta) 348 meta_compat_start(); 349 #endif 350 351 Var_ReexportVars(gn); 352 Var_ExportStackTrace(gn->name, cmd); 353 354 compatChild = Compat_Spawn(av); 355 free(mav); 356 free(bp); 357 358 /* XXX: Memory management looks suspicious here. */ 359 /* XXX: Setting a list item to NULL is unexpected. */ 360 LstNode_SetNull(ln); 361 362 #ifdef USE_META 363 if (useMeta) 364 meta_compat_parent(compatChild); 365 #endif 366 367 /* The child is off and running. Now all we can do is wait... */ 368 while ((retstat = wait(&reason)) != compatChild) { 369 if (retstat > 0) 370 JobReapChild(retstat, reason, false); /* not ours? */ 371 if (retstat == -1 && errno != EINTR) 372 break; 373 } 374 375 if (retstat < 0) 376 Fatal("error in wait: %d: %s", retstat, strerror(errno)); 377 378 if (WIFSTOPPED(reason)) { 379 status = WSTOPSIG(reason); 380 } else if (WIFEXITED(reason)) { 381 status = WEXITSTATUS(reason); 382 #if defined(USE_META) && defined(USE_FILEMON_ONCE) 383 if (useMeta) 384 meta_cmd_finish(NULL); 385 #endif 386 if (status != 0) { 387 if (DEBUG(ERROR)) 388 DebugFailedTarget(cmd, gn); 389 printf("*** Error code %d", status); 390 } 391 } else { 392 status = WTERMSIG(reason); 393 printf("*** Signal %d", status); 394 } 395 396 397 if (!WIFEXITED(reason) || status != 0) { 398 if (errCheck) { 399 #ifdef USE_META 400 if (useMeta) 401 meta_job_error(NULL, gn, false, status); 402 #endif 403 gn->made = ERROR; 404 if (WIFEXITED(reason)) 405 gn->exit_status = status; 406 if (opts.keepgoing) { 407 /* 408 * Abort the current target, 409 * but let others continue. 410 */ 411 printf(" (continuing)\n"); 412 } else { 413 printf("\n"); 414 } 415 if (deleteOnError) 416 CompatDeleteTarget(gn); 417 } else { 418 /* 419 * Continue executing commands for this target. 420 * If we return 0, this will happen... 421 */ 422 printf(" (ignored)\n"); 423 status = 0; 424 } 425 fflush(stdout); 426 } 427 428 free(cmdStart); 429 if (cmd_file[0] != '\0') 430 unlink(cmd_file); 431 compatChild = 0; 432 if (compatSigno != 0) { 433 bmake_signal(compatSigno, SIG_DFL); 434 kill(myPid, compatSigno); 435 } 436 437 return status == 0; 438 } 439 440 static void 441 RunCommands(GNode *gn) 442 { 443 StringListNode *ln; 444 445 for (ln = gn->commands.first; ln != NULL; ln = ln->next) { 446 const char *cmd = ln->datum; 447 if (!Compat_RunCommand(cmd, gn, ln)) 448 break; 449 } 450 } 451 452 static void 453 MakeInRandomOrder(GNode **gnodes, GNode **end, GNode *pgn) 454 { 455 GNode **it; 456 size_t r; 457 458 for (r = (size_t)(end - gnodes); r >= 2; r--) { 459 /* Biased, but irrelevant in practice. */ 460 size_t i = (size_t)random() % r; 461 GNode *t = gnodes[r - 1]; 462 gnodes[r - 1] = gnodes[i]; 463 gnodes[i] = t; 464 } 465 466 for (it = gnodes; it != end; it++) 467 Compat_Make(*it, pgn); 468 } 469 470 static void 471 MakeWaitGroupsInRandomOrder(GNodeList *gnodes, GNode *pgn) 472 { 473 Vector vec; 474 GNodeListNode *ln; 475 GNode **nodes; 476 size_t i, n, start; 477 478 Vector_Init(&vec, sizeof(GNode *)); 479 for (ln = gnodes->first; ln != NULL; ln = ln->next) 480 *(GNode **)Vector_Push(&vec) = ln->datum; 481 nodes = vec.items; 482 n = vec.len; 483 484 start = 0; 485 for (i = 0; i < n; i++) { 486 if (nodes[i]->type & OP_WAIT) { 487 MakeInRandomOrder(nodes + start, nodes + i, pgn); 488 Compat_Make(nodes[i], pgn); 489 start = i + 1; 490 } 491 } 492 MakeInRandomOrder(nodes + start, nodes + i, pgn); 493 494 Vector_Done(&vec); 495 } 496 497 static void 498 MakeNodes(GNodeList *gnodes, GNode *pgn) 499 { 500 GNodeListNode *ln; 501 502 if (Lst_IsEmpty(gnodes)) 503 return; 504 if (opts.randomizeTargets) { 505 MakeWaitGroupsInRandomOrder(gnodes, pgn); 506 return; 507 } 508 509 for (ln = gnodes->first; ln != NULL; ln = ln->next) { 510 GNode *cgn = ln->datum; 511 Compat_Make(cgn, pgn); 512 } 513 } 514 515 static bool 516 MakeUnmade(GNode *gn, GNode *pgn) 517 { 518 519 assert(gn->made == UNMADE); 520 521 /* 522 * First mark ourselves to be made, then apply whatever transformations 523 * the suffix module thinks are necessary. Once that's done, we can 524 * descend and make all our children. If any of them has an error 525 * but the -k flag was given, our 'make' field will be set to false 526 * again. This is our signal to not attempt to do anything but abort 527 * our parent as well. 528 */ 529 gn->flags.remake = true; 530 gn->made = BEINGMADE; 531 532 if (!(gn->type & OP_MADE)) 533 Suff_FindDeps(gn); 534 535 MakeNodes(&gn->children, gn); 536 537 if (!gn->flags.remake) { 538 gn->made = ABORTED; 539 pgn->flags.remake = false; 540 return false; 541 } 542 543 if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) 544 Var_Set(pgn, IMPSRC, GNode_VarTarget(gn)); 545 546 /* 547 * All the children were made ok. Now youngestChild->mtime contains the 548 * modification time of the newest child, we need to find out if we 549 * exist and when we were modified last. The criteria for datedness 550 * are defined by GNode_IsOODate. 551 */ 552 DEBUG1(MAKE, "Examining %s...", gn->name); 553 if (!GNode_IsOODate(gn)) { 554 gn->made = UPTODATE; 555 DEBUG0(MAKE, "up-to-date.\n"); 556 return false; 557 } 558 559 /* 560 * If the user is just seeing if something is out-of-date, exit now 561 * to tell him/her "yes". 562 */ 563 DEBUG0(MAKE, "out-of-date.\n"); 564 if (opts.query && gn != Targ_GetEndNode()) 565 exit(1); 566 567 /* 568 * We need to be re-made. 569 * Ensure that $? (.OODATE) and $> (.ALLSRC) are both set. 570 */ 571 GNode_SetLocalVars(gn); 572 573 /* 574 * Alter our type to tell if errors should be ignored or things 575 * should not be printed so Compat_RunCommand knows what to do. 576 */ 577 if (opts.ignoreErrors) 578 gn->type |= OP_IGNORE; 579 if (opts.silent) 580 gn->type |= OP_SILENT; 581 582 if (Job_CheckCommands(gn, Fatal)) { 583 if (!opts.touch || (gn->type & OP_MAKE)) { 584 curTarg = gn; 585 #ifdef USE_META 586 if (useMeta && GNode_ShouldExecute(gn)) 587 meta_job_start(NULL, gn); 588 #endif 589 RunCommands(gn); 590 curTarg = NULL; 591 } else { 592 Job_Touch(gn, (gn->type & OP_SILENT) != OP_NONE); 593 } 594 } else { 595 gn->made = ERROR; 596 } 597 #ifdef USE_META 598 if (useMeta && GNode_ShouldExecute(gn)) { 599 if (meta_job_finish(NULL) != 0) 600 gn->made = ERROR; 601 } 602 #endif 603 604 if (gn->made != ERROR) { 605 /* 606 * If the node was made successfully, mark it so, update 607 * its modification time and timestamp all its parents. 608 * This is to keep its state from affecting that of its parent. 609 */ 610 gn->made = MADE; 611 if (Make_Recheck(gn) == 0) 612 pgn->flags.force = true; 613 if (!(gn->type & OP_EXEC)) { 614 pgn->flags.childMade = true; 615 GNode_UpdateYoungestChild(pgn, gn); 616 } 617 } else if (opts.keepgoing) { 618 pgn->flags.remake = false; 619 } else { 620 PrintOnError(gn, "\nStop.\n"); 621 exit(1); 622 } 623 return true; 624 } 625 626 static void 627 MakeOther(GNode *gn, GNode *pgn) 628 { 629 630 if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) { 631 const char *target = GNode_VarTarget(gn); 632 Var_Set(pgn, IMPSRC, target != NULL ? target : ""); 633 } 634 635 switch (gn->made) { 636 case BEINGMADE: 637 Error("Graph cycles through %s", gn->name); 638 gn->made = ERROR; 639 pgn->flags.remake = false; 640 break; 641 case MADE: 642 if (!(gn->type & OP_EXEC)) { 643 pgn->flags.childMade = true; 644 GNode_UpdateYoungestChild(pgn, gn); 645 } 646 break; 647 case UPTODATE: 648 if (!(gn->type & OP_EXEC)) 649 GNode_UpdateYoungestChild(pgn, gn); 650 break; 651 default: 652 break; 653 } 654 } 655 656 /* 657 * Make a target. 658 * 659 * If an error is detected and not being ignored, the process exits. 660 * 661 * Input: 662 * gn The node to make 663 * pgn Parent to abort if necessary 664 * 665 * Output: 666 * gn->made 667 * UPTODATE gn was already up-to-date. 668 * MADE gn was recreated successfully. 669 * ERROR An error occurred while gn was being created, 670 * either due to missing commands or in -k mode. 671 * ABORTED gn was not remade because one of its 672 * dependencies could not be made due to errors. 673 */ 674 void 675 Compat_Make(GNode *gn, GNode *pgn) 676 { 677 if (shellName == NULL) /* we came here from jobs */ 678 Shell_Init(); 679 680 if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) { 681 if (!MakeUnmade(gn, pgn)) 682 goto cohorts; 683 684 /* XXX: Replace with GNode_IsError(gn) */ 685 } else if (gn->made == ERROR) { 686 /* 687 * Already had an error when making this. 688 * Tell the parent to abort. 689 */ 690 pgn->flags.remake = false; 691 } else { 692 MakeOther(gn, pgn); 693 } 694 695 cohorts: 696 MakeNodes(&gn->cohorts, pgn); 697 } 698 699 static void 700 MakeBeginNode(void) 701 { 702 GNode *gn = Targ_FindNode(".BEGIN"); 703 if (gn == NULL) 704 return; 705 706 Compat_Make(gn, gn); 707 if (GNode_IsError(gn)) { 708 PrintOnError(gn, "\nStop.\n"); 709 exit(1); 710 } 711 } 712 713 static void 714 InitSignals(void) 715 { 716 if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN) 717 bmake_signal(SIGINT, CompatInterrupt); 718 if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) 719 bmake_signal(SIGTERM, CompatInterrupt); 720 if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) 721 bmake_signal(SIGHUP, CompatInterrupt); 722 if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) 723 bmake_signal(SIGQUIT, CompatInterrupt); 724 } 725 726 void 727 Compat_MakeAll(GNodeList *targets) 728 { 729 GNode *errorNode = NULL; 730 731 if (shellName == NULL) 732 Shell_Init(); 733 734 InitSignals(); 735 736 /* 737 * Create the .END node now, to keep the (debug) output of the 738 * counter.mk test the same as before 2020-09-23. This 739 * implementation detail probably doesn't matter though. 740 */ 741 (void)Targ_GetEndNode(); 742 743 if (!opts.query) 744 MakeBeginNode(); 745 746 /* 747 * Expand .USE nodes right now, because they can modify the structure 748 * of the tree. 749 */ 750 Make_ExpandUse(targets); 751 752 while (!Lst_IsEmpty(targets)) { 753 GNode *gn = Lst_Dequeue(targets); 754 Compat_Make(gn, gn); 755 756 if (gn->made == UPTODATE) { 757 printf("`%s' is up to date.\n", gn->name); 758 } else if (gn->made == ABORTED) { 759 printf("`%s' not remade because of errors.\n", 760 gn->name); 761 } 762 if (GNode_IsError(gn) && errorNode == NULL) 763 errorNode = gn; 764 } 765 766 if (errorNode == NULL) { 767 GNode *endNode = Targ_GetEndNode(); 768 Compat_Make(endNode, endNode); 769 if (GNode_IsError(endNode)) 770 errorNode = endNode; 771 } 772 773 if (errorNode != NULL) { 774 if (DEBUG(GRAPH2)) 775 Targ_PrintGraph(2); 776 else if (DEBUG(GRAPH3)) 777 Targ_PrintGraph(3); 778 PrintOnError(errorNode, "\nStop.\n"); 779 exit(1); 780 } 781 } 782