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