1 /* $NetBSD: main.c,v 1.661 2025/07/06 07:11:31 rillig Exp $ */ 2 3 /* 4 * Copyright (c) 1988, 1989, 1990, 1993 5 * The Regents of the University of California. 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) 1989 by Berkeley Softworks 37 * All rights reserved. 38 * 39 * This code is derived from software contributed to Berkeley by 40 * Adam de Boor. 41 * 42 * Redistribution and use in source and binary forms, with or without 43 * modification, are permitted provided that the following conditions 44 * are met: 45 * 1. Redistributions of source code must retain the above copyright 46 * notice, this list of conditions and the following disclaimer. 47 * 2. Redistributions in binary form must reproduce the above copyright 48 * notice, this list of conditions and the following disclaimer in the 49 * documentation and/or other materials provided with the distribution. 50 * 3. All advertising materials mentioning features or use of this software 51 * must display the following acknowledgement: 52 * This product includes software developed by the University of 53 * California, Berkeley and its contributors. 54 * 4. Neither the name of the University nor the names of its contributors 55 * may be used to endorse or promote products derived from this software 56 * without specific prior written permission. 57 * 58 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 59 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 60 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 61 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 62 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 63 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 64 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 65 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 66 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 67 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 68 * SUCH DAMAGE. 69 */ 70 71 /* 72 * The main file for this entire program. Exit routines etc. reside here. 73 * 74 * Utility functions defined in this file: 75 * 76 * Main_ParseArgLine 77 * Parse and process command line arguments from a 78 * single string. Used to implement the special targets 79 * .MFLAGS and .MAKEFLAGS. 80 * 81 * Error Print a tagged error message. 82 * 83 * Fatal Print an error message and exit. 84 * 85 * Punt Abort all jobs and exit with a message. 86 */ 87 88 #include <sys/types.h> 89 #include <sys/time.h> 90 #include <sys/param.h> 91 #include <sys/resource.h> 92 #include <sys/stat.h> 93 #if defined(MAKE_NATIVE) && defined(HAVE_SYSCTL) 94 #include <sys/sysctl.h> 95 #endif 96 #include <sys/utsname.h> 97 #include "wait.h" 98 99 #include <errno.h> 100 #include <signal.h> 101 #include <stdarg.h> 102 #include <time.h> 103 104 #include "make.h" 105 #include "dir.h" 106 #include "job.h" 107 #ifdef USE_META 108 # include "meta.h" 109 #endif 110 #include "pathnames.h" 111 #include "trace.h" 112 113 /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ 114 MAKE_RCSID("$NetBSD: main.c,v 1.661 2025/07/06 07:11:31 rillig Exp $"); 115 #if defined(MAKE_NATIVE) 116 __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " 117 "The Regents of the University of California. " 118 "All rights reserved."); 119 #endif 120 121 #ifndef __arraycount 122 # define __arraycount(__x) (sizeof(__x) / sizeof(__x[0])) 123 #endif 124 125 CmdOpts opts; 126 time_t now; /* Time at start of make */ 127 GNode *defaultNode; /* .DEFAULT node */ 128 bool allPrecious; /* .PRECIOUS given on a line by itself */ 129 bool deleteOnError; /* .DELETE_ON_ERROR: set */ 130 131 static int maxJobTokens; /* -j argument */ 132 static bool enterFlagObj; /* -w and objdir != srcdir */ 133 static bool bogusJflag; /* -J invalid */ 134 135 static int tokenPoolReader = -1, tokenPoolWriter = -1; 136 bool doing_depend; /* Set while reading .depend */ 137 static bool jobsRunning; /* true if the jobs might be running */ 138 static const char *tracefile; 139 static bool ReadMakefile(const char *); 140 static void purge_relative_cached_realpaths(void); 141 142 static bool ignorePWD; /* if we use -C, PWD is meaningless */ 143 static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ 144 char curdir[MAXPATHLEN + 1]; /* Startup directory */ 145 const char *progname; 146 char *makeDependfile; 147 pid_t myPid; 148 int makelevel; 149 150 bool forceJobs = false; 151 static int main_errors = 0; 152 static HashTable cached_realpaths; 153 154 /* 155 * For compatibility with the POSIX version of MAKEFLAGS that includes 156 * all the options without '-', convert 'flags' to '-f -l -a -g -s '. 157 */ 158 static char * 159 explode(const char *flags) 160 { 161 char *exploded, *ep; 162 const char *p; 163 164 if (flags == NULL) 165 return NULL; 166 167 for (p = flags; *p != '\0'; p++) 168 if (!ch_isalpha(*p)) 169 return bmake_strdup(flags); 170 171 exploded = bmake_malloc((size_t)(p - flags) * 3 + 1); 172 for (p = flags, ep = exploded; *p != '\0'; p++) { 173 *ep++ = '-'; 174 *ep++ = *p; 175 *ep++ = ' '; 176 } 177 *ep = '\0'; 178 return exploded; 179 } 180 181 MAKE_ATTR_DEAD static void 182 usage(void) 183 { 184 size_t prognameLen = strcspn(progname, "["); 185 186 (void)fprintf(stderr, 187 "usage: %.*s [-BeikNnqrSstWwX]\n" 188 " [-C directory] [-D variable] [-d flags] [-f makefile]\n" 189 " [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n" 190 " [-V variable] [-v variable] [variable=value] [target ...]\n", 191 (int)prognameLen, progname); 192 exit(2); 193 } 194 195 static void 196 MainParseArgDebugFile(const char *arg) 197 { 198 const char *mode; 199 size_t len; 200 char *fname; 201 202 if (opts.debug_file != stdout && opts.debug_file != stderr) 203 fclose(opts.debug_file); 204 205 if (*arg == '+') { 206 arg++; 207 mode = "a"; 208 } else 209 mode = "w"; 210 211 if (strcmp(arg, "stdout") == 0) { 212 opts.debug_file = stdout; 213 return; 214 } 215 if (strcmp(arg, "stderr") == 0) { 216 opts.debug_file = stderr; 217 return; 218 } 219 220 len = strlen(arg); 221 fname = bmake_malloc(len + 20); 222 memcpy(fname, arg, len + 1); 223 224 /* Replace the trailing '%d' after '.%d' with the pid. */ 225 if (len >= 3 && memcmp(fname + len - 3, ".%d", 3) == 0) 226 snprintf(fname + len - 2, 20, "%d", getpid()); 227 228 opts.debug_file = fopen(fname, mode); 229 if (opts.debug_file == NULL) { 230 fprintf(stderr, "Cannot open debug file \"%s\"\n", fname); 231 exit(2); 232 } 233 free(fname); 234 } 235 236 static void 237 MainParseArgDebug(const char *argvalue) 238 { 239 const char *modules; 240 DebugFlags debug = opts.debug; 241 242 for (modules = argvalue; *modules != '\0'; modules++) { 243 switch (*modules) { 244 case '0': /* undocumented, only intended for tests */ 245 memset(&debug, 0, sizeof(debug)); 246 break; 247 case 'A': 248 memset(&debug, ~0, sizeof(debug)); 249 break; 250 case 'a': 251 debug.DEBUG_ARCH = true; 252 break; 253 case 'C': 254 debug.DEBUG_CWD = true; 255 break; 256 case 'c': 257 debug.DEBUG_COND = true; 258 break; 259 case 'd': 260 debug.DEBUG_DIR = true; 261 break; 262 case 'e': 263 debug.DEBUG_ERROR = true; 264 break; 265 case 'f': 266 debug.DEBUG_FOR = true; 267 break; 268 case 'g': 269 if (modules[1] == '1') { 270 debug.DEBUG_GRAPH1 = true; 271 modules++; 272 } else if (modules[1] == '2') { 273 debug.DEBUG_GRAPH2 = true; 274 modules++; 275 } else if (modules[1] == '3') { 276 debug.DEBUG_GRAPH3 = true; 277 modules++; 278 } 279 break; 280 case 'h': 281 debug.DEBUG_HASH = true; 282 break; 283 case 'j': 284 debug.DEBUG_JOB = true; 285 break; 286 case 'L': 287 opts.strict = true; 288 break; 289 case 'l': 290 debug.DEBUG_LOUD = true; 291 break; 292 case 'M': 293 debug.DEBUG_META = true; 294 break; 295 case 'm': 296 debug.DEBUG_MAKE = true; 297 break; 298 case 'n': 299 debug.DEBUG_SCRIPT = true; 300 break; 301 case 'p': 302 debug.DEBUG_PARSE = true; 303 break; 304 case 's': 305 debug.DEBUG_SUFF = true; 306 break; 307 case 't': 308 debug.DEBUG_TARG = true; 309 break; 310 case 'V': 311 opts.debugVflag = true; 312 break; 313 case 'v': 314 debug.DEBUG_VAR = true; 315 break; 316 case 'x': 317 debug.DEBUG_SHELL = true; 318 break; 319 case 'F': 320 MainParseArgDebugFile(modules + 1); 321 goto finish; 322 default: 323 (void)fprintf(stderr, 324 "%s: illegal argument to d option -- %c\n", 325 progname, *modules); 326 usage(); 327 } 328 } 329 330 finish: 331 opts.debug = debug; 332 333 setvbuf(opts.debug_file, NULL, _IONBF, 0); 334 if (opts.debug_file != stdout) 335 setvbuf(stdout, NULL, _IOLBF, 0); 336 } 337 338 /* Is path relative or does it contain any relative component "." or ".."? */ 339 static bool 340 IsRelativePath(const char *path) 341 { 342 const char *p; 343 344 if (path[0] != '/') 345 return true; 346 p = path; 347 while ((p = strstr(p, "/.")) != NULL) { 348 p += 2; 349 if (*p == '.') 350 p++; 351 if (*p == '/' || *p == '\0') 352 return true; 353 } 354 return false; 355 } 356 357 static void 358 MainParseArgChdir(const char *argvalue) 359 { 360 struct stat sa, sb; 361 362 if (chdir(argvalue) == -1) { 363 (void)fprintf(stderr, "%s: chdir %s: %s\n", 364 progname, argvalue, strerror(errno)); 365 exit(2); /* Not 1 so -q can distinguish error */ 366 } 367 if (getcwd(curdir, MAXPATHLEN) == NULL) { 368 (void)fprintf(stderr, "%s: getcwd: %s\n", 369 progname, strerror(errno)); 370 exit(2); 371 } 372 if (!IsRelativePath(argvalue) && 373 stat(argvalue, &sa) != -1 && 374 stat(curdir, &sb) != -1 && 375 sa.st_ino == sb.st_ino && 376 sa.st_dev == sb.st_dev) 377 snprintf(curdir, MAXPATHLEN, "%s", argvalue); 378 ignorePWD = true; 379 } 380 381 static void 382 MainParseArgJobsInternal(const char *argvalue) 383 { 384 char end; 385 if (sscanf(argvalue, "%d,%d%c", 386 &tokenPoolReader, &tokenPoolWriter, &end) != 2) { 387 (void)fprintf(stderr, 388 "%s: error: invalid internal option " 389 "\"-J %s\" in \"%s\"\n", 390 progname, argvalue, curdir); 391 exit(2); 392 } 393 if ((fcntl(tokenPoolReader, F_GETFD, 0) < 0) || 394 (fcntl(tokenPoolWriter, F_GETFD, 0) < 0)) { 395 tokenPoolReader = -1; 396 tokenPoolWriter = -1; 397 bogusJflag = true; 398 } else { 399 Global_Append(MAKEFLAGS, "-J"); 400 Global_Append(MAKEFLAGS, argvalue); 401 } 402 } 403 404 static void 405 MainParseArgJobs(const char *arg) 406 { 407 const char *p; 408 char *end; 409 char v[12]; 410 411 forceJobs = true; 412 opts.maxJobs = (int)strtol(arg, &end, 0); 413 p = end; 414 #ifdef _SC_NPROCESSORS_ONLN 415 if (*p != '\0') { 416 double d; 417 418 if (*p == 'C') 419 d = (opts.maxJobs > 0) ? opts.maxJobs : 1; 420 else if (*p == '.') { 421 d = strtod(arg, &end); 422 p = end; 423 } else 424 d = 0.0; 425 if (d > 0.0) { 426 p = ""; 427 opts.maxJobs = (int)sysconf(_SC_NPROCESSORS_ONLN); 428 opts.maxJobs = (int)(d * (double)opts.maxJobs); 429 } 430 } 431 #endif 432 if (*p != '\0' || opts.maxJobs < 1) { 433 (void)fprintf(stderr, 434 "%s: argument '%s' to option '-j' " 435 "must be a positive number\n", 436 progname, arg); 437 exit(2); /* Not 1 so -q can distinguish error */ 438 } 439 snprintf(v, sizeof(v), "%d", opts.maxJobs); 440 Global_Append(MAKEFLAGS, "-j"); 441 Global_Append(MAKEFLAGS, v); 442 Global_Set(".MAKE.JOBS", v); 443 maxJobTokens = opts.maxJobs; 444 } 445 446 static void 447 MainParseArgSysInc(const char *argvalue) 448 { 449 if (strncmp(argvalue, ".../", 4) == 0) { 450 char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4); 451 if (found_path == NULL) 452 return; 453 (void)SearchPath_Add(sysIncPath, found_path); 454 free(found_path); 455 } else { 456 (void)SearchPath_Add(sysIncPath, argvalue); 457 } 458 Global_Append(MAKEFLAGS, "-m"); 459 Global_Append(MAKEFLAGS, argvalue); 460 Dir_SetSYSPATH(); 461 } 462 463 static bool 464 MainParseOption(char c, const char *argvalue) 465 { 466 switch (c) { 467 case '\0': 468 break; 469 case 'B': 470 opts.compatMake = true; 471 Global_Append(MAKEFLAGS, "-B"); 472 Global_Set(".MAKE.MODE", "compat"); 473 break; 474 case 'C': 475 MainParseArgChdir(argvalue); 476 break; 477 case 'D': 478 if (argvalue[0] == '\0') 479 return false; 480 Var_SetExpand(SCOPE_GLOBAL, argvalue, "1"); 481 Global_Append(MAKEFLAGS, "-D"); 482 Global_Append(MAKEFLAGS, argvalue); 483 break; 484 case 'I': 485 SearchPath_Add(parseIncPath, argvalue); 486 Global_Append(MAKEFLAGS, "-I"); 487 Global_Append(MAKEFLAGS, argvalue); 488 break; 489 case 'J': 490 MainParseArgJobsInternal(argvalue); 491 break; 492 case 'N': 493 opts.noExecute = true; 494 opts.noRecursiveExecute = true; 495 Global_Append(MAKEFLAGS, "-N"); 496 break; 497 case 'S': 498 opts.keepgoing = false; 499 Global_Append(MAKEFLAGS, "-S"); 500 break; 501 case 'T': 502 tracefile = bmake_strdup(argvalue); 503 Global_Append(MAKEFLAGS, "-T"); 504 Global_Append(MAKEFLAGS, argvalue); 505 break; 506 case 'V': 507 case 'v': 508 opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED; 509 Lst_Append(&opts.variables, bmake_strdup(argvalue)); 510 /* XXX: Why always -V? */ 511 Global_Append(MAKEFLAGS, "-V"); 512 Global_Append(MAKEFLAGS, argvalue); 513 break; 514 case 'W': 515 opts.parseWarnFatal = true; 516 /* XXX: why no Global_Append? */ 517 break; 518 case 'X': 519 opts.varNoExportEnv = true; 520 Global_Append(MAKEFLAGS, "-X"); 521 break; 522 case 'd': 523 /* If '-d-opts' don't pass to children */ 524 if (argvalue[0] == '-') 525 argvalue++; 526 else { 527 Global_Append(MAKEFLAGS, "-d"); 528 Global_Append(MAKEFLAGS, argvalue); 529 } 530 MainParseArgDebug(argvalue); 531 break; 532 case 'e': 533 opts.checkEnvFirst = true; 534 Global_Append(MAKEFLAGS, "-e"); 535 break; 536 case 'f': 537 Lst_Append(&opts.makefiles, bmake_strdup(argvalue)); 538 break; 539 case 'i': 540 opts.ignoreErrors = true; 541 Global_Append(MAKEFLAGS, "-i"); 542 break; 543 case 'j': 544 MainParseArgJobs(argvalue); 545 break; 546 case 'k': 547 opts.keepgoing = true; 548 Global_Append(MAKEFLAGS, "-k"); 549 break; 550 case 'm': 551 MainParseArgSysInc(argvalue); 552 /* XXX: why no Var_Append? */ 553 break; 554 case 'n': 555 opts.noExecute = true; 556 Global_Append(MAKEFLAGS, "-n"); 557 break; 558 case 'q': 559 opts.query = true; 560 /* Kind of nonsensical, wot? */ 561 Global_Append(MAKEFLAGS, "-q"); 562 break; 563 case 'r': 564 opts.noBuiltins = true; 565 Global_Append(MAKEFLAGS, "-r"); 566 break; 567 case 's': 568 opts.silent = true; 569 Global_Append(MAKEFLAGS, "-s"); 570 break; 571 case 't': 572 opts.touch = true; 573 Global_Append(MAKEFLAGS, "-t"); 574 break; 575 case 'w': 576 opts.enterFlag = true; 577 Global_Append(MAKEFLAGS, "-w"); 578 break; 579 default: 580 usage(); 581 } 582 return true; 583 } 584 585 /* 586 * Parse the given arguments. Called from main() and from 587 * Main_ParseArgLine() when the .MAKEFLAGS target is used. 588 * 589 * The arguments must be treated as read-only and will be freed after the 590 * call. 591 * 592 * XXX: Deal with command line overriding .MAKEFLAGS in makefile 593 */ 594 static void 595 MainParseArgs(int argc, char **argv) 596 { 597 char c; 598 int arginc; 599 char *argvalue; 600 char *optscan; 601 bool inOption, dashDash = false; 602 603 const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w"; 604 /* Can't actually use getopt(3) because rescanning is not portable */ 605 606 rearg: 607 inOption = false; 608 optscan = NULL; 609 while (argc > 1) { 610 const char *optspec; 611 if (!inOption) 612 optscan = argv[1]; 613 c = *optscan++; 614 arginc = 0; 615 if (inOption) { 616 if (c == '\0') { 617 argv++; 618 argc--; 619 inOption = false; 620 continue; 621 } 622 } else { 623 if (c != '-' || dashDash) 624 break; 625 inOption = true; 626 c = *optscan++; 627 } 628 /* '-' found at some earlier point */ 629 optspec = strchr(optspecs, c); 630 if (c != '\0' && optspec != NULL && optspec[1] == ':') { 631 /* 632 * -<something> found, and <something> should have an 633 * argument 634 */ 635 inOption = false; 636 arginc = 1; 637 argvalue = optscan; 638 if (*argvalue == '\0') { 639 if (argc < 3) 640 goto noarg; 641 argvalue = argv[2]; 642 arginc = 2; 643 } 644 } else { 645 argvalue = NULL; 646 } 647 switch (c) { 648 case '\0': 649 arginc = 1; 650 inOption = false; 651 break; 652 case '-': 653 dashDash = true; 654 break; 655 default: 656 if (!MainParseOption(c, argvalue)) 657 goto noarg; 658 } 659 argv += arginc; 660 argc -= arginc; 661 } 662 663 /* 664 * See if the rest of the arguments are variable assignments and 665 * perform them if so. Else take them to be targets and stuff them 666 * on the end of the "create" list. 667 */ 668 for (; argc > 1; argv++, argc--) { 669 if (!Parse_VarAssign(argv[1], false, SCOPE_CMDLINE)) { 670 if (argv[1][0] == '\0') 671 Punt("illegal (null) argument."); 672 if (argv[1][0] == '-' && !dashDash) 673 goto rearg; 674 Lst_Append(&opts.create, bmake_strdup(argv[1])); 675 } 676 } 677 678 return; 679 noarg: 680 (void)fprintf(stderr, "%s: option requires an argument -- %c\n", 681 progname, c); 682 usage(); 683 } 684 685 /* 686 * Break a line of arguments into words and parse them. 687 * 688 * Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and 689 * by main() when reading the MAKEFLAGS environment variable. 690 */ 691 void 692 Main_ParseArgLine(const char *line) 693 { 694 Words words; 695 char *buf; 696 const char *p; 697 698 if (line == NULL) 699 return; 700 for (p = line; *p == ' '; p++) 701 continue; 702 if (p[0] == '\0') 703 return; 704 705 { 706 FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE"); 707 buf = str_concat3(argv0.str, " ", p); 708 FStr_Done(&argv0); 709 } 710 711 words = Str_Words(buf, true); 712 if (words.words == NULL) { 713 Error("Unterminated quoted string [%s]", buf); 714 free(buf); 715 return; 716 } 717 free(buf); 718 EvalStack_PushMakeflags(line); 719 MainParseArgs((int)words.len, words.words); 720 EvalStack_Pop(); 721 722 Words_Free(words); 723 } 724 725 bool 726 Main_SetObjdir(bool writable, const char *fmt, ...) 727 { 728 struct stat sb; 729 char *path; 730 char buf[MAXPATHLEN + 1]; 731 char buf2[MAXPATHLEN + 1]; 732 va_list ap; 733 734 va_start(ap, fmt); 735 vsnprintf(path = buf, MAXPATHLEN, fmt, ap); 736 va_end(ap); 737 738 if (path[0] != '/') { 739 if (snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path) <= MAXPATHLEN) 740 path = buf2; 741 else 742 return false; 743 } 744 745 /* look for the directory and try to chdir there */ 746 if (stat(path, &sb) != 0 || !S_ISDIR(sb.st_mode)) 747 return false; 748 749 if ((writable && access(path, W_OK) != 0) || chdir(path) != 0) { 750 (void)fprintf(stderr, "%s: warning: %s: %s\n", 751 progname, path, strerror(errno)); 752 /* Allow debugging how we got here - not always obvious */ 753 if (GetBooleanExpr("${MAKE_DEBUG_OBJDIR_CHECK_WRITABLE}", 754 false)) 755 PrintOnError(NULL, ""); 756 return false; 757 } 758 759 snprintf(objdir, sizeof objdir, "%s", path); 760 Global_Set(".OBJDIR", objdir); 761 setenv("PWD", objdir, 1); 762 Dir_InitDot(); 763 purge_relative_cached_realpaths(); 764 if (opts.enterFlag && strcmp(objdir, curdir) != 0) 765 enterFlagObj = true; 766 return true; 767 } 768 769 static bool 770 SetVarObjdir(bool writable, const char *var, const char *suffix) 771 { 772 FStr path = Var_Value(SCOPE_CMDLINE, var); 773 774 if (path.str == NULL || path.str[0] == '\0') { 775 FStr_Done(&path); 776 return false; 777 } 778 779 Var_Expand(&path, SCOPE_GLOBAL, VARE_EVAL); 780 781 (void)Main_SetObjdir(writable, "%s%s", path.str, suffix); 782 783 FStr_Done(&path); 784 return true; 785 } 786 787 /* 788 * Splits str into words (in-place, modifying it), adding them to the list. 789 * The string must be kept alive as long as the list. 790 */ 791 void 792 AppendWords(StringList *lp, char *str) 793 { 794 char *p; 795 const char *sep = " \t"; 796 797 for (p = strtok(str, sep); p != NULL; p = strtok(NULL, sep)) 798 Lst_Append(lp, p); 799 } 800 801 #ifdef SIGINFO 802 static void 803 siginfo(int signo MAKE_ATTR_UNUSED) 804 { 805 char dir[MAXPATHLEN]; 806 char str[2 * MAXPATHLEN]; 807 int len; 808 if (getcwd(dir, sizeof dir) == NULL) 809 return; 810 len = snprintf(str, sizeof str, "%s: Working in: %s\n", progname, dir); 811 if (len > 0) 812 (void)write(STDERR_FILENO, str, (size_t)len); 813 } 814 #endif 815 816 /* Allow makefiles some control over the mode we run in. */ 817 static void 818 MakeMode(void) 819 { 820 char *mode = Var_Subst("${.MAKE.MODE:tl}", SCOPE_GLOBAL, VARE_EVAL); 821 /* TODO: handle errors */ 822 823 if (mode[0] != '\0') { 824 if (strstr(mode, "compat") != NULL) { 825 opts.compatMake = true; 826 forceJobs = false; 827 } 828 #if USE_META 829 if (strstr(mode, "meta") != NULL) 830 meta_mode_init(mode); 831 #endif 832 if (strstr(mode, "randomize-targets") != NULL) 833 opts.randomizeTargets = true; 834 } 835 836 free(mode); 837 } 838 839 static void 840 PrintVariable(const char *varname, bool expandVars) 841 { 842 if (strchr(varname, '$') != NULL) { 843 char *evalue = Var_Subst(varname, SCOPE_GLOBAL, VARE_EVAL); 844 /* TODO: handle errors */ 845 printf("%s\n", evalue); 846 free(evalue); 847 848 } else if (expandVars) { 849 char *expr = str_concat3("${", varname, "}"); 850 char *evalue = Var_Subst(expr, SCOPE_GLOBAL, VARE_EVAL); 851 /* TODO: handle errors */ 852 free(expr); 853 printf("%s\n", evalue); 854 free(evalue); 855 856 } else { 857 FStr value = Var_Value(SCOPE_GLOBAL, varname); 858 printf("%s\n", value.str != NULL ? value.str : ""); 859 FStr_Done(&value); 860 } 861 } 862 863 /* 864 * Return a bool based on a variable. 865 * 866 * If the knob is not set, return the fallback. 867 * If set, anything that looks or smells like "No", "False", "Off", "0", etc. 868 * is false, otherwise true. 869 */ 870 bool 871 GetBooleanExpr(const char *expr, bool fallback) 872 { 873 char *value; 874 bool res; 875 876 value = Var_Subst(expr, SCOPE_GLOBAL, VARE_EVAL); 877 /* TODO: handle errors */ 878 res = ParseBoolean(value, fallback); 879 free(value); 880 return res; 881 } 882 883 static void 884 PrintVariables(void) 885 { 886 StringListNode *ln; 887 bool expandVars; 888 889 if (opts.printVars == PVM_EXPANDED) 890 expandVars = true; 891 else if (opts.debugVflag) 892 expandVars = false; 893 else 894 expandVars = GetBooleanExpr("${.MAKE.EXPAND_VARIABLES}", 895 false); 896 897 for (ln = opts.variables.first; ln != NULL; ln = ln->next) 898 PrintVariable(ln->datum, expandVars); 899 } 900 901 static bool 902 MakeTargets(void) 903 { 904 GNodeList targets = LST_INIT; 905 bool outOfDate; /* false if all targets up to date */ 906 907 if (Lst_IsEmpty(&opts.create)) 908 Parse_MainName(&targets); 909 else 910 Targ_FindList(&targets, &opts.create); 911 912 if (!opts.compatMake) { 913 if (!opts.query) { 914 Job_Init(); 915 jobsRunning = true; 916 } 917 918 outOfDate = Make_MakeParallel(&targets); 919 } else { 920 Compat_MakeAll(&targets); 921 outOfDate = false; 922 } 923 Lst_Done(&targets); /* Don't free the targets themselves. */ 924 return outOfDate; 925 } 926 927 /* 928 * Set up the .TARGETS variable to contain the list of targets to be created. 929 * If none specified, make the variable empty for now, the parser will fill 930 * in the default or .MAIN target later. 931 */ 932 static void 933 InitVarTargets(void) 934 { 935 StringListNode *ln; 936 937 if (Lst_IsEmpty(&opts.create)) { 938 Global_Set(".TARGETS", ""); 939 return; 940 } 941 942 for (ln = opts.create.first; ln != NULL; ln = ln->next) { 943 const char *name = ln->datum; 944 Global_Append(".TARGETS", name); 945 } 946 } 947 948 static void 949 InitRandom(void) 950 { 951 struct timeval tv; 952 953 gettimeofday(&tv, NULL); 954 srandom((unsigned)(tv.tv_sec + tv.tv_usec)); 955 } 956 957 static const char * 958 InitVarMachine(const struct utsname *utsname MAKE_ATTR_UNUSED) 959 { 960 #ifdef FORCE_MACHINE 961 return FORCE_MACHINE; 962 #else 963 const char *machine = getenv("MACHINE"); 964 965 if (machine != NULL) 966 return machine; 967 968 #if defined(MAKE_NATIVE) 969 return utsname->machine; 970 #elif defined(MAKE_MACHINE) 971 return MAKE_MACHINE; 972 #else 973 return "unknown"; 974 #endif 975 #endif 976 } 977 978 static const char * 979 InitVarMachineArch(void) 980 { 981 #ifdef FORCE_MACHINE_ARCH 982 return FORCE_MACHINE_ARCH; 983 #else 984 const char *env = getenv("MACHINE_ARCH"); 985 if (env != NULL) 986 return env; 987 988 #if defined(MAKE_NATIVE) && defined(CTL_HW) 989 { 990 struct utsname utsname; 991 static char machine_arch_buf[sizeof utsname.machine]; 992 const int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; 993 size_t len = sizeof machine_arch_buf; 994 995 if (sysctl(mib, (unsigned)__arraycount(mib), 996 machine_arch_buf, &len, NULL, 0) < 0) { 997 (void)fprintf(stderr, "%s: sysctl: %s\n", 998 progname, strerror(errno)); 999 exit(2); 1000 } 1001 1002 return machine_arch_buf; 1003 } 1004 #elif defined(MACHINE_ARCH) 1005 return MACHINE_ARCH; 1006 #elif defined(MAKE_MACHINE_ARCH) 1007 return MAKE_MACHINE_ARCH; 1008 #else 1009 return "unknown"; 1010 #endif 1011 #endif 1012 } 1013 1014 #ifndef NO_PWD_OVERRIDE 1015 /* 1016 * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX 1017 * since the value of curdir can vary depending on how we got 1018 * here. That is, sitting at a shell prompt (shell that provides $PWD) 1019 * or via subdir.mk, in which case it's likely a shell which does 1020 * not provide it. 1021 * 1022 * So, to stop it breaking this case only, we ignore PWD if 1023 * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains an expression. 1024 */ 1025 static void 1026 HandlePWD(const struct stat *curdir_st) 1027 { 1028 char *pwd; 1029 FStr makeobjdir; 1030 struct stat pwd_st; 1031 1032 if (ignorePWD || (pwd = getenv("PWD")) == NULL) 1033 return; 1034 1035 if (Var_Exists(SCOPE_CMDLINE, "MAKEOBJDIRPREFIX")) 1036 return; 1037 1038 makeobjdir = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIR"); 1039 if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL) 1040 goto ignore_pwd; 1041 1042 if (stat(pwd, &pwd_st) == 0 && 1043 curdir_st->st_ino == pwd_st.st_ino && 1044 curdir_st->st_dev == pwd_st.st_dev) 1045 snprintf(curdir, MAXPATHLEN, "%s", pwd); 1046 1047 ignore_pwd: 1048 FStr_Done(&makeobjdir); 1049 } 1050 #endif 1051 1052 /* 1053 * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, MAKEOBJDIR is set 1054 * in the environment, try only that value and fall back to .CURDIR if it 1055 * does not exist. 1056 * 1057 * Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE, 1058 * and finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none of these 1059 * paths exist, just use .CURDIR. 1060 */ 1061 static void 1062 InitObjdir(const char *machine, const char *machine_arch) 1063 { 1064 bool writable; 1065 1066 Dir_InitCur(curdir); 1067 writable = GetBooleanExpr("${MAKE_OBJDIR_CHECK_WRITABLE}", true); 1068 (void)Main_SetObjdir(false, "%s", curdir); 1069 1070 if (!SetVarObjdir(writable, "MAKEOBJDIRPREFIX", curdir) && 1071 !SetVarObjdir(writable, "MAKEOBJDIR", "") && 1072 !Main_SetObjdir(writable, "%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) && 1073 !Main_SetObjdir(writable, "%s.%s", _PATH_OBJDIR, machine) && 1074 !Main_SetObjdir(writable, "%s", _PATH_OBJDIR)) 1075 (void)Main_SetObjdir(writable, "%s%s", _PATH_OBJDIRPREFIX, curdir); 1076 } 1077 1078 /* get rid of resource limit on file descriptors */ 1079 static void 1080 UnlimitFiles(void) 1081 { 1082 #if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE) 1083 struct rlimit rl; 1084 if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && 1085 rl.rlim_cur != rl.rlim_max) { 1086 #ifdef BMAKE_NOFILE_MAX 1087 if (BMAKE_NOFILE_MAX < rl.rlim_max) 1088 rl.rlim_cur = BMAKE_NOFILE_MAX; 1089 else 1090 #endif 1091 rl.rlim_cur = rl.rlim_max; 1092 (void)setrlimit(RLIMIT_NOFILE, &rl); 1093 } 1094 #endif 1095 } 1096 1097 static void 1098 CmdOpts_Init(void) 1099 { 1100 opts.compatMake = false; 1101 memset(&opts.debug, 0, sizeof(opts.debug)); 1102 /* opts.debug_file has already been initialized earlier */ 1103 opts.strict = false; 1104 opts.debugVflag = false; 1105 opts.checkEnvFirst = false; 1106 Lst_Init(&opts.makefiles); 1107 opts.ignoreErrors = false; /* Pay attention to non-zero returns */ 1108 opts.maxJobs = 1; 1109 opts.keepgoing = false; /* Stop on error */ 1110 opts.noRecursiveExecute = false; /* Execute all .MAKE targets */ 1111 opts.noExecute = false; /* Execute all commands */ 1112 opts.query = false; 1113 opts.noBuiltins = false; /* Read the built-in rules */ 1114 opts.silent = false; /* Print commands as executed */ 1115 opts.touch = false; 1116 opts.printVars = PVM_NONE; 1117 Lst_Init(&opts.variables); 1118 opts.parseWarnFatal = false; 1119 opts.enterFlag = false; 1120 opts.varNoExportEnv = false; 1121 Lst_Init(&opts.create); 1122 } 1123 1124 /* 1125 * Initialize MAKE and .MAKE to the path of the executable, so that it can be 1126 * found by execvp(3) and the shells, even after a chdir. 1127 * 1128 * If it's a relative path and contains a '/', resolve it to an absolute path. 1129 * Otherwise keep it as is, assuming it will be found in the PATH. 1130 */ 1131 static void 1132 InitVarMake(const char *argv0) 1133 { 1134 const char *make = argv0; 1135 char pathbuf[MAXPATHLEN]; 1136 1137 if (argv0[0] != '/' && strchr(argv0, '/') != NULL) { 1138 const char *abspath = cached_realpath(argv0, pathbuf); 1139 struct stat st; 1140 if (abspath != NULL && abspath[0] == '/' && 1141 stat(make, &st) == 0) 1142 make = abspath; 1143 } 1144 1145 Global_Set("MAKE", make); 1146 Global_Set(".MAKE", make); 1147 } 1148 1149 /* 1150 * Add the directories from the colon-separated syspath to defSysIncPath. 1151 * After returning, the contents of syspath is unspecified. 1152 */ 1153 static void 1154 InitDefSysIncPath(char *syspath) 1155 { 1156 static char defsyspath[] = _PATH_DEFSYSPATH; 1157 char *start, *p; 1158 1159 /* 1160 * If no user-supplied system path was given (through the -m option) 1161 * add the directories from the DEFSYSPATH (more than one may be given 1162 * as dir1:...:dirn) to the system include path. 1163 */ 1164 if (syspath == NULL || syspath[0] == '\0') 1165 syspath = defsyspath; 1166 else 1167 syspath = bmake_strdup(syspath); 1168 1169 for (start = syspath; *start != '\0'; start = p) { 1170 for (p = start; *p != '\0' && *p != ':'; p++) 1171 continue; 1172 if (*p == ':') 1173 *p++ = '\0'; 1174 1175 /* look for magic parent directory search string */ 1176 if (strncmp(start, ".../", 4) == 0) { 1177 char *dir = Dir_FindHereOrAbove(curdir, start + 4); 1178 if (dir != NULL) { 1179 (void)SearchPath_Add(defSysIncPath, dir); 1180 free(dir); 1181 } 1182 } else { 1183 (void)SearchPath_Add(defSysIncPath, start); 1184 } 1185 } 1186 1187 if (syspath != defsyspath) 1188 free(syspath); 1189 } 1190 1191 static void 1192 ReadBuiltinRules(void) 1193 { 1194 StringListNode *ln; 1195 StringList sysMkFiles = LST_INIT; 1196 1197 SearchPath_Expand( 1198 Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath, 1199 _PATH_DEFSYSMK, 1200 &sysMkFiles); 1201 if (Lst_IsEmpty(&sysMkFiles)) 1202 Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); 1203 1204 for (ln = sysMkFiles.first; ln != NULL; ln = ln->next) 1205 if (ReadMakefile(ln->datum)) 1206 break; 1207 1208 if (ln == NULL) 1209 Fatal("%s: cannot open %s.", 1210 progname, (const char *)sysMkFiles.first->datum); 1211 1212 Lst_DoneFree(&sysMkFiles); 1213 } 1214 1215 static void 1216 InitMaxJobs(void) 1217 { 1218 char *value; 1219 int n; 1220 1221 if (bogusJflag && !opts.compatMake) { 1222 opts.compatMake = true; 1223 Parse_Error(PARSE_WARNING, 1224 "Invalid internal option \"-J\" in \"%s\"; " 1225 "see the manual page", 1226 curdir); 1227 PrintStackTrace(true); 1228 return; 1229 } 1230 if (forceJobs || opts.compatMake || 1231 !Var_Exists(SCOPE_GLOBAL, ".MAKE.JOBS")) 1232 return; 1233 1234 value = Var_Subst("${.MAKE.JOBS}", SCOPE_GLOBAL, VARE_EVAL); 1235 /* TODO: handle errors */ 1236 n = (int)strtol(value, NULL, 0); 1237 if (n < 1) { 1238 (void)fprintf(stderr, 1239 "%s: illegal value for .MAKE.JOBS " 1240 "-- must be positive integer!\n", 1241 progname); 1242 exit(2); /* Not 1 so -q can distinguish error */ 1243 } 1244 1245 if (n != opts.maxJobs) { 1246 Global_Append(MAKEFLAGS, "-j"); 1247 Global_Append(MAKEFLAGS, value); 1248 } 1249 1250 opts.maxJobs = n; 1251 maxJobTokens = opts.maxJobs; 1252 forceJobs = true; 1253 free(value); 1254 } 1255 1256 /* 1257 * For compatibility, look at the directories in the VPATH variable 1258 * and add them to the search path, if the variable is defined. The 1259 * variable's value is in the same format as the PATH environment 1260 * variable, i.e. <directory>:<directory>:<directory>... 1261 */ 1262 static void 1263 InitVpath(void) 1264 { 1265 char *vpath, savec, *path; 1266 if (!Var_Exists(SCOPE_CMDLINE, "VPATH")) 1267 return; 1268 1269 vpath = Var_Subst("${VPATH}", SCOPE_CMDLINE, VARE_EVAL); 1270 /* TODO: handle errors */ 1271 path = vpath; 1272 do { 1273 char *p; 1274 /* skip to end of directory */ 1275 for (p = path; *p != ':' && *p != '\0'; p++) 1276 continue; 1277 /* Save terminator character so know when to stop */ 1278 savec = *p; 1279 *p = '\0'; 1280 /* Add directory to search path */ 1281 (void)SearchPath_Add(&dirSearchPath, path); 1282 *p = savec; 1283 path = p + 1; 1284 } while (savec == ':'); 1285 free(vpath); 1286 } 1287 1288 static void 1289 ReadAllMakefiles(const StringList *makefiles) 1290 { 1291 StringListNode *ln; 1292 1293 for (ln = makefiles->first; ln != NULL; ln = ln->next) { 1294 const char *fname = ln->datum; 1295 if (!ReadMakefile(fname)) 1296 Fatal("%s: cannot open %s.", progname, fname); 1297 } 1298 } 1299 1300 static void 1301 ReadFirstDefaultMakefile(void) 1302 { 1303 StringList makefiles = LST_INIT; 1304 StringListNode *ln; 1305 char *prefs = Var_Subst("${.MAKE.MAKEFILE_PREFERENCE}", 1306 SCOPE_CMDLINE, VARE_EVAL); 1307 /* TODO: handle errors */ 1308 1309 AppendWords(&makefiles, prefs); 1310 1311 for (ln = makefiles.first; ln != NULL; ln = ln->next) 1312 if (ReadMakefile(ln->datum)) 1313 break; 1314 1315 Lst_Done(&makefiles); 1316 free(prefs); 1317 } 1318 1319 /* 1320 * Initialize variables such as MAKE, MACHINE, .MAKEFLAGS. 1321 * Initialize a few modules. 1322 * Parse the arguments from MAKEFLAGS and the command line. 1323 */ 1324 static void 1325 main_Init(int argc, char **argv) 1326 { 1327 struct stat sa; 1328 const char *machine; 1329 const char *machine_arch; 1330 char *syspath = getenv("MAKESYSPATH"); 1331 struct utsname utsname; 1332 1333 /* default to writing debug to stderr */ 1334 opts.debug_file = stderr; 1335 1336 Str_Intern_Init(); 1337 HashTable_Init(&cached_realpaths); 1338 1339 #ifdef SIGINFO 1340 (void)bmake_signal(SIGINFO, siginfo); 1341 #endif 1342 1343 InitRandom(); 1344 1345 progname = str_basename(argv[0]); 1346 1347 UnlimitFiles(); 1348 1349 if (uname(&utsname) == -1) { 1350 (void)fprintf(stderr, "%s: uname: %s\n", progname, 1351 strerror(errno)); 1352 exit(2); 1353 } 1354 1355 machine = InitVarMachine(&utsname); 1356 machine_arch = InitVarMachineArch(); 1357 1358 myPid = getpid(); 1359 1360 /* Just in case MAKEOBJDIR wants us to do something tricky. */ 1361 Targ_Init(); 1362 #ifdef FORCE_MAKE_OS 1363 Global_Set_ReadOnly(".MAKE.OS", FORCE_MAKE_OS); 1364 #else 1365 Global_Set_ReadOnly(".MAKE.OS", utsname.sysname); 1366 #endif 1367 Global_Set("MACHINE", machine); 1368 Global_Set("MACHINE_ARCH", machine_arch); 1369 #ifdef MAKE_VERSION 1370 Global_Set("MAKE_VERSION", MAKE_VERSION); 1371 #endif 1372 Global_Set_ReadOnly(".newline", "\n"); 1373 #ifndef MAKEFILE_PREFERENCE_LIST 1374 /* This is the traditional preference for makefiles. */ 1375 # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" 1376 #endif 1377 Global_Set(".MAKE.MAKEFILE_PREFERENCE", MAKEFILE_PREFERENCE_LIST); 1378 Global_Set(".MAKE.DEPENDFILE", ".depend"); 1379 /* Tell makefiles like jobs.mk whether we support -jC */ 1380 #ifdef _SC_NPROCESSORS_ONLN 1381 Global_Set_ReadOnly(".MAKE.JOBS.C", "yes"); 1382 #else 1383 Global_Set_ReadOnly(".MAKE.JOBS.C", "no"); 1384 #endif 1385 1386 CmdOpts_Init(); 1387 allPrecious = false; /* Remove targets when interrupted */ 1388 deleteOnError = false; /* Historical default behavior */ 1389 jobsRunning = false; 1390 1391 maxJobTokens = opts.maxJobs; 1392 ignorePWD = false; 1393 1394 /* 1395 * Initialize the parsing, directory and variable modules to prepare 1396 * for the reading of inclusion paths and variable settings on the 1397 * command line 1398 */ 1399 1400 /* 1401 * Initialize various variables. 1402 * MAKE also gets this name, for compatibility 1403 * .MAKEFLAGS gets set to the empty string just in case. 1404 * MFLAGS also gets initialized empty, for compatibility. 1405 */ 1406 Parse_Init(); 1407 InitVarMake(argv[0]); 1408 Global_Set(MAKEFLAGS, ""); 1409 Global_Set(".MAKEOVERRIDES", ""); 1410 Global_Set("MFLAGS", ""); 1411 Global_Set(".ALLTARGETS", ""); 1412 Global_Set_ReadOnly(".MAKE.LEVEL.ENV", MAKE_LEVEL_ENV); 1413 1414 /* Set some other useful variables. */ 1415 { 1416 char buf[64]; 1417 const char *ep = getenv(MAKE_LEVEL_ENV); 1418 1419 makelevel = ep != NULL && ep[0] != '\0' ? atoi(ep) : 0; 1420 if (makelevel < 0) 1421 makelevel = 0; 1422 snprintf(buf, sizeof buf, "%d", makelevel); 1423 Global_Set(".MAKE.LEVEL", buf); 1424 snprintf(buf, sizeof buf, "%u", myPid); 1425 Global_Set_ReadOnly(".MAKE.PID", buf); 1426 snprintf(buf, sizeof buf, "%u", getppid()); 1427 Global_Set_ReadOnly(".MAKE.PPID", buf); 1428 snprintf(buf, sizeof buf, "%u", getuid()); 1429 Global_Set_ReadOnly(".MAKE.UID", buf); 1430 snprintf(buf, sizeof buf, "%u", getgid()); 1431 Global_Set_ReadOnly(".MAKE.GID", buf); 1432 } 1433 if (makelevel > 0) { 1434 char pn[1024]; 1435 snprintf(pn, sizeof pn, "%s[%d]", progname, makelevel); 1436 progname = bmake_strdup(pn); 1437 } 1438 1439 #ifdef USE_META 1440 meta_init(); 1441 #endif 1442 Dir_Init(); 1443 1444 if (getcwd(curdir, MAXPATHLEN) == NULL) { 1445 (void)fprintf(stderr, "%s: getcwd: %s\n", 1446 progname, strerror(errno)); 1447 exit(2); 1448 } 1449 1450 { 1451 char *makeflags = explode(getenv("MAKEFLAGS")); 1452 Main_ParseArgLine(makeflags); 1453 free(makeflags); 1454 } 1455 1456 MainParseArgs(argc, argv); 1457 1458 if (opts.enterFlag) 1459 printf("%s: Entering directory `%s'\n", progname, curdir); 1460 1461 if (stat(curdir, &sa) == -1) { 1462 (void)fprintf(stderr, "%s: stat %s: %s\n", 1463 progname, curdir, strerror(errno)); 1464 exit(2); 1465 } 1466 1467 #ifndef NO_PWD_OVERRIDE 1468 HandlePWD(&sa); 1469 #endif 1470 Global_Set(".CURDIR", curdir); 1471 1472 InitObjdir(machine, machine_arch); 1473 1474 Arch_Init(); 1475 Suff_Init(); 1476 Trace_Init(tracefile); 1477 1478 defaultNode = NULL; 1479 (void)time(&now); 1480 1481 Trace_Log(MAKESTART, NULL); 1482 1483 InitVarTargets(); 1484 1485 InitDefSysIncPath(syspath); 1486 } 1487 1488 /* 1489 * Read the system makefile followed by either makefile, Makefile or the 1490 * files given by the -f option. Exit on parse errors. 1491 */ 1492 static void 1493 main_ReadFiles(void) 1494 { 1495 1496 if (Lst_IsEmpty(&sysIncPath->dirs)) 1497 SearchPath_AddAll(sysIncPath, defSysIncPath); 1498 1499 Dir_SetSYSPATH(); 1500 if (!opts.noBuiltins) 1501 ReadBuiltinRules(); 1502 1503 posix_state = PS_MAYBE_NEXT_LINE; 1504 if (!Lst_IsEmpty(&opts.makefiles)) 1505 ReadAllMakefiles(&opts.makefiles); 1506 else 1507 ReadFirstDefaultMakefile(); 1508 } 1509 1510 /* Compute the dependency graph. */ 1511 static void 1512 main_PrepareMaking(void) 1513 { 1514 /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ 1515 if (!opts.noBuiltins || opts.printVars == PVM_NONE) { 1516 makeDependfile = Var_Subst("${.MAKE.DEPENDFILE}", 1517 SCOPE_CMDLINE, VARE_EVAL); 1518 if (makeDependfile[0] != '\0') { 1519 /* TODO: handle errors */ 1520 doing_depend = true; 1521 (void)ReadMakefile(makeDependfile); 1522 doing_depend = false; 1523 } 1524 } 1525 1526 if (enterFlagObj) 1527 printf("%s: Entering directory `%s'\n", progname, objdir); 1528 1529 MakeMode(); 1530 1531 { 1532 FStr makeflags = Var_Value(SCOPE_GLOBAL, MAKEFLAGS); 1533 Global_Append("MFLAGS", makeflags.str); 1534 FStr_Done(&makeflags); 1535 } 1536 1537 InitMaxJobs(); 1538 1539 if (!opts.compatMake && !forceJobs) 1540 opts.compatMake = true; 1541 1542 if (!opts.compatMake) 1543 TokenPool_Init(maxJobTokens, tokenPoolReader, tokenPoolWriter); 1544 DEBUG5(JOB, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", 1545 tokenPoolReader, tokenPoolWriter, opts.maxJobs, maxJobTokens, 1546 opts.compatMake ? 1 : 0); 1547 1548 if (opts.printVars == PVM_NONE) 1549 Main_ExportMAKEFLAGS(true); /* initial export */ 1550 1551 InitVpath(); 1552 1553 /* 1554 * Now that all search paths have been read for suffixes et al, it's 1555 * time to add the default search path to their lists... 1556 */ 1557 Suff_ExtendPaths(); 1558 1559 /* 1560 * Propagate attributes through :: dependency lists. 1561 */ 1562 Targ_Propagate(); 1563 1564 /* print the initial graph, if the user requested it */ 1565 if (DEBUG(GRAPH1)) 1566 Targ_PrintGraph(1); 1567 } 1568 1569 /* 1570 * Make the targets, or print variables. 1571 * Return whether any of the targets is out-of-date. 1572 */ 1573 static bool 1574 main_Run(void) 1575 { 1576 if (opts.printVars != PVM_NONE) { 1577 PrintVariables(); 1578 return false; 1579 } else 1580 return MakeTargets(); 1581 } 1582 1583 /* Clean up after making the targets. */ 1584 static void 1585 main_CleanUp(void) 1586 { 1587 #ifdef CLEANUP 1588 Lst_DoneFree(&opts.variables); 1589 Lst_DoneFree(&opts.makefiles); 1590 Lst_DoneFree(&opts.create); 1591 #endif 1592 1593 if (DEBUG(GRAPH2)) 1594 Targ_PrintGraph(2); 1595 1596 Trace_Log(MAKEEND, NULL); 1597 1598 if (enterFlagObj) 1599 printf("%s: Leaving directory `%s'\n", progname, objdir); 1600 if (opts.enterFlag) 1601 printf("%s: Leaving directory `%s'\n", progname, curdir); 1602 1603 Var_Stats(); 1604 Targ_Stats(); 1605 1606 #ifdef USE_META 1607 meta_finish(); 1608 #endif 1609 #ifdef CLEANUP 1610 Suff_End(); 1611 Targ_End(); 1612 Arch_End(); 1613 Parse_End(); 1614 Dir_End(); 1615 Job_End(); 1616 #endif 1617 Trace_End(); 1618 #ifdef CLEANUP 1619 Str_Intern_End(); 1620 #endif 1621 } 1622 1623 static int 1624 main_ExitCode(bool outOfDate) 1625 { 1626 if ((opts.strict && main_errors > 0) || parseErrors > 0) 1627 return 2; /* Not 1 so -q can distinguish error */ 1628 return outOfDate ? 1 : 0; 1629 } 1630 1631 int 1632 main(int argc, char **argv) 1633 { 1634 bool outOfDate; 1635 1636 main_Init(argc, argv); 1637 main_ReadFiles(); 1638 main_PrepareMaking(); 1639 outOfDate = main_Run(); 1640 main_CleanUp(); 1641 return main_ExitCode(outOfDate); 1642 } 1643 1644 /* 1645 * Open and parse the given makefile, with all its side effects. 1646 * Return false if the file could not be opened. 1647 */ 1648 static bool 1649 ReadMakefile(const char *fname) 1650 { 1651 int fd; 1652 char *name, *path = NULL; 1653 1654 if (strcmp(fname, "-") == 0) { 1655 Parse_File("(stdin)", -1); 1656 Var_Set(SCOPE_INTERNAL, "MAKEFILE", ""); 1657 } else { 1658 if (strncmp(fname, ".../", 4) == 0) { 1659 name = Dir_FindHereOrAbove(curdir, fname + 4); 1660 if (name != NULL) { 1661 /* Dir_FindHereOrAbove returns dirname */ 1662 path = str_concat3(name, "/", 1663 str_basename(fname)); 1664 free(name); 1665 fd = open(path, O_RDONLY); 1666 if (fd != -1) { 1667 fname = path; 1668 goto found; 1669 } 1670 } 1671 } 1672 /* if we've chdir'd, rebuild the path name */ 1673 if (strcmp(curdir, objdir) != 0 && *fname != '/') { 1674 path = str_concat3(curdir, "/", fname); 1675 fd = open(path, O_RDONLY); 1676 if (fd != -1) { 1677 fname = path; 1678 goto found; 1679 } 1680 free(path); 1681 1682 /* If curdir failed, try objdir (ala .depend) */ 1683 path = str_concat3(objdir, "/", fname); 1684 fd = open(path, O_RDONLY); 1685 if (fd != -1) { 1686 fname = path; 1687 goto found; 1688 } 1689 } else { 1690 fd = open(fname, O_RDONLY); 1691 if (fd != -1) 1692 goto found; 1693 } 1694 /* look in -I and system include directories. */ 1695 name = Dir_FindFile(fname, parseIncPath); 1696 if (name == NULL) { 1697 SearchPath *sysInc = Lst_IsEmpty(&sysIncPath->dirs) 1698 ? defSysIncPath : sysIncPath; 1699 name = Dir_FindFile(fname, sysInc); 1700 } 1701 if (name == NULL || (fd = open(name, O_RDONLY)) == -1) { 1702 free(name); 1703 free(path); 1704 return false; 1705 } 1706 fname = name; 1707 /* 1708 * set the MAKEFILE variable desired by System V fans -- the 1709 * placement of the setting here means it gets set to the last 1710 * makefile specified, as it is set by SysV make. 1711 */ 1712 found: 1713 if (!doing_depend) 1714 Var_Set(SCOPE_INTERNAL, "MAKEFILE", fname); 1715 Parse_File(fname, fd); 1716 } 1717 free(path); 1718 return true; 1719 } 1720 1721 /* populate av for Cmd_Exec and Compat_RunCommand */ 1722 void 1723 Cmd_Argv(const char *cmd, size_t cmd_len, const char *av[5], 1724 char *cmd_file, size_t cmd_filesz, bool eflag, bool xflag) 1725 { 1726 int cmd_fd = -1; 1727 1728 if (shellPath == NULL) 1729 Shell_Init(); 1730 1731 if (cmd_file != NULL) { 1732 if (cmd_len == 0) 1733 cmd_len = strlen(cmd); 1734 1735 if (cmd_len > MAKE_CMDLEN_LIMIT) { 1736 cmd_fd = mkTempFile(NULL, cmd_file, cmd_filesz); 1737 if (cmd_fd >= 0) { 1738 ssize_t n; 1739 1740 n = write(cmd_fd, cmd, cmd_len); 1741 close(cmd_fd); 1742 if (n < (ssize_t)cmd_len) { 1743 unlink(cmd_file); 1744 cmd_fd = -1; 1745 } 1746 } 1747 } else 1748 cmd_file[0] = '\0'; 1749 } 1750 1751 /* The following works for any of the builtin shell specs. */ 1752 *av++ = shellPath; 1753 if (eflag) 1754 *av++ = shellErrFlag; 1755 if (cmd_fd >= 0) { 1756 if (xflag) 1757 *av++ = "-x"; 1758 *av++ = cmd_file; 1759 } else { 1760 *av++ = xflag ? "-xc" : "-c"; 1761 *av++ = cmd; 1762 } 1763 *av = NULL; 1764 } 1765 1766 /* 1767 * Execute the command in cmd, and return its output (only stdout, not 1768 * stderr, possibly empty). In the output, replace newlines with spaces. 1769 */ 1770 char * 1771 Cmd_Exec(const char *cmd, char **error) 1772 { 1773 const char *args[5]; /* Arguments for invoking the shell */ 1774 int pipefds[2]; 1775 int cpid; /* Child PID */ 1776 int pid; /* PID from wait() */ 1777 WAIT_T status; /* command exit status */ 1778 Buffer buf; /* buffer to store the result */ 1779 ssize_t bytes_read; 1780 char *output; 1781 char *p; 1782 int saved_errno; 1783 char cmd_file[MAXPATHLEN]; 1784 1785 DEBUG1(VAR, "Capturing the output of command \"%s\"\n", cmd); 1786 1787 Cmd_Argv(cmd, 0, args, cmd_file, sizeof(cmd_file), false, false); 1788 if (pipe(pipefds) == -1) { 1789 *error = str_concat3( 1790 "Couldn't create pipe for \"", cmd, "\""); 1791 return bmake_strdup(""); 1792 } 1793 1794 Var_ReexportVars(SCOPE_GLOBAL); 1795 Var_ExportStackTrace(NULL, cmd); 1796 1797 switch (cpid = FORK_FUNCTION()) { 1798 case 0: 1799 (void)close(pipefds[0]); 1800 (void)dup2(pipefds[1], STDOUT_FILENO); 1801 (void)close(pipefds[1]); 1802 1803 (void)execv(shellPath, UNCONST(args)); 1804 _exit(1); 1805 /* NOTREACHED */ 1806 1807 case -1: 1808 *error = str_concat3("Couldn't exec \"", cmd, "\""); 1809 return bmake_strdup(""); 1810 } 1811 1812 (void)close(pipefds[1]); /* No need for the writing half */ 1813 1814 saved_errno = 0; 1815 Buf_Init(&buf); 1816 1817 do { 1818 char result[BUFSIZ]; 1819 bytes_read = read(pipefds[0], result, sizeof result); 1820 if (bytes_read > 0) 1821 Buf_AddBytes(&buf, result, (size_t)bytes_read); 1822 } while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR)); 1823 if (bytes_read == -1) 1824 saved_errno = errno; 1825 1826 (void)close(pipefds[0]); /* Close the input side of the pipe. */ 1827 1828 while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0) 1829 JobReapChild(pid, status, false); 1830 1831 if (Buf_EndsWith(&buf, '\n')) 1832 buf.data[buf.len - 1] = '\0'; 1833 1834 output = Buf_DoneData(&buf); 1835 for (p = output; *p != '\0'; p++) 1836 if (*p == '\n') 1837 *p = ' '; 1838 1839 if (WIFSIGNALED(status)) 1840 *error = str_concat3("\"", cmd, "\" exited on a signal"); 1841 else if (WEXITSTATUS(status) != 0) { 1842 Buffer errBuf; 1843 Buf_Init(&errBuf); 1844 Buf_AddStr(&errBuf, "Command \""); 1845 Buf_AddStr(&errBuf, cmd); 1846 Buf_AddStr(&errBuf, "\" exited with status "); 1847 Buf_AddInt(&errBuf, WEXITSTATUS(status)); 1848 *error = Buf_DoneData(&errBuf); 1849 } else if (saved_errno != 0) 1850 *error = str_concat3( 1851 "Couldn't read shell's output for \"", cmd, "\""); 1852 else 1853 *error = NULL; 1854 if (cmd_file[0] != '\0') 1855 unlink(cmd_file); 1856 return output; 1857 } 1858 1859 /* 1860 * Print a printf-style error message. 1861 * 1862 * In default mode, this error message has no consequences, for compatibility 1863 * reasons, in particular it does not affect the exit status. Only in lint 1864 * mode (-dL) it does. 1865 */ 1866 void 1867 Error(const char *fmt, ...) 1868 { 1869 va_list ap; 1870 FILE *f; 1871 1872 f = opts.debug_file; 1873 if (f == stdout) 1874 f = stderr; 1875 (void)fflush(stdout); 1876 1877 for (;;) { 1878 fprintf(f, "%s: ", progname); 1879 va_start(ap, fmt); 1880 (void)vfprintf(f, fmt, ap); 1881 va_end(ap); 1882 (void)fprintf(f, "\n"); 1883 (void)fflush(f); 1884 if (f == stderr) 1885 break; 1886 f = stderr; 1887 } 1888 main_errors++; 1889 } 1890 1891 /* 1892 * Wait for any running jobs to finish, then produce an error message, 1893 * finally exit immediately. 1894 * 1895 * Exiting immediately differs from Parse_Error, which exits only after the 1896 * current top-level makefile has been parsed completely. 1897 */ 1898 void 1899 Fatal(const char *fmt, ...) 1900 { 1901 va_list ap; 1902 1903 if (jobsRunning) 1904 Job_Wait(); 1905 1906 (void)fflush(stdout); 1907 fprintf(stderr, "%s: ", progname); 1908 va_start(ap, fmt); 1909 (void)vfprintf(stderr, fmt, ap); 1910 va_end(ap); 1911 (void)fprintf(stderr, "\n"); 1912 (void)fflush(stderr); 1913 PrintStackTrace(true); 1914 1915 PrintOnError(NULL, "\n"); 1916 1917 if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) 1918 Targ_PrintGraph(2); 1919 Trace_Log(MAKEERROR, NULL); 1920 exit(2); /* Not 1 so -q can distinguish error */ 1921 } 1922 1923 /* 1924 * Major exception once jobs are being created. 1925 * Kills all jobs, prints a message and exits. 1926 */ 1927 void 1928 Punt(const char *fmt, ...) 1929 { 1930 va_list ap; 1931 1932 (void)fflush(stdout); 1933 (void)fprintf(stderr, "%s: ", progname); 1934 va_start(ap, fmt); 1935 (void)vfprintf(stderr, fmt, ap); 1936 va_end(ap); 1937 (void)fprintf(stderr, "\n"); 1938 (void)fflush(stderr); 1939 1940 PrintOnError(NULL, "\n"); 1941 1942 DieHorribly(); 1943 } 1944 1945 /* Exit without giving a message. */ 1946 void 1947 DieHorribly(void) 1948 { 1949 if (jobsRunning) 1950 Job_AbortAll(); 1951 if (DEBUG(GRAPH2)) 1952 Targ_PrintGraph(2); 1953 Trace_Log(MAKEERROR, NULL); 1954 exit(2); /* Not 1 so -q can distinguish error */ 1955 } 1956 1957 int 1958 unlink_file(const char *file) 1959 { 1960 struct stat st; 1961 1962 if (lstat(file, &st) == -1) 1963 return -1; 1964 1965 if (S_ISDIR(st.st_mode)) { 1966 /* 1967 * POSIX says for unlink: "The path argument shall not name 1968 * a directory unless [...]". 1969 */ 1970 errno = EISDIR; 1971 return -1; 1972 } 1973 return unlink(file); 1974 } 1975 1976 static void 1977 write_all(int fd, const void *data, size_t n) 1978 { 1979 const char *mem = data; 1980 1981 while (n > 0) { 1982 ssize_t written = write(fd, mem, n); 1983 /* XXX: Should this EAGAIN be EINTR? */ 1984 if (written == -1 && errno == EAGAIN) 1985 continue; 1986 if (written == -1) 1987 break; 1988 mem += written; 1989 n -= (size_t)written; 1990 } 1991 } 1992 1993 /* Print why exec failed, avoiding stdio. */ 1994 void MAKE_ATTR_DEAD 1995 execDie(const char *func, const char *arg) 1996 { 1997 char msg[1024]; 1998 int len; 1999 2000 len = snprintf(msg, sizeof(msg), "%s: %s(%s): %s\n", 2001 progname, func, arg, strerror(errno)); 2002 write_all(STDERR_FILENO, msg, (size_t)len); 2003 _exit(1); 2004 } 2005 2006 static void 2007 purge_relative_cached_realpaths(void) 2008 { 2009 HashIter hi; 2010 bool more; 2011 2012 HashIter_Init(&hi, &cached_realpaths); 2013 more = HashIter_Next(&hi); 2014 while (more) { 2015 HashEntry *he = hi.entry; 2016 more = HashIter_Next(&hi); 2017 if (he->key[0] != '/') { 2018 DEBUG1(DIR, "cached_realpath: purging %s\n", he->key); 2019 free(he->value); 2020 HashTable_DeleteEntry(&cached_realpaths, he); 2021 } 2022 } 2023 } 2024 2025 const char * 2026 cached_realpath(const char *pathname, char *resolved) 2027 { 2028 const char *rp; 2029 2030 if (pathname == NULL || pathname[0] == '\0') 2031 return NULL; 2032 2033 rp = HashTable_FindValue(&cached_realpaths, pathname); 2034 if (rp != NULL) { 2035 snprintf(resolved, MAXPATHLEN, "%s", rp); 2036 return resolved; 2037 } 2038 2039 rp = realpath(pathname, resolved); 2040 if (rp != NULL) { 2041 HashTable_Set(&cached_realpaths, pathname, bmake_strdup(rp)); 2042 DEBUG2(DIR, "cached_realpath: %s -> %s\n", pathname, rp); 2043 return resolved; 2044 } 2045 2046 /* should we negative-cache? */ 2047 return NULL; 2048 } 2049 2050 /* 2051 * Return true if we should die without noise. 2052 * For example our failing child was a sub-make or failure happened elsewhere. 2053 */ 2054 bool 2055 shouldDieQuietly(GNode *gn, int bf) 2056 { 2057 static int quietly = -1; 2058 2059 if (quietly < 0) { 2060 if (DEBUG(JOB) || 2061 !GetBooleanExpr("${.MAKE.DIE_QUIETLY}", true)) 2062 quietly = 0; 2063 else if (bf >= 0) 2064 quietly = bf; 2065 else 2066 quietly = gn != NULL && gn->type & OP_MAKE ? 1 : 0; 2067 } 2068 return quietly != 0; 2069 } 2070 2071 static void 2072 SetErrorVars(GNode *gn) 2073 { 2074 StringListNode *ln; 2075 char sts[16]; 2076 2077 /* 2078 * We can print this even if there is no .ERROR target. 2079 */ 2080 snprintf(sts, sizeof(sts), "%d", gn->exit_status); 2081 Global_Set(".ERROR_EXIT", sts); 2082 Global_Set(".ERROR_TARGET", gn->name); 2083 Global_Delete(".ERROR_CMD"); 2084 2085 for (ln = gn->commands.first; ln != NULL; ln = ln->next) { 2086 const char *cmd = ln->datum; 2087 2088 if (cmd == NULL) 2089 break; 2090 Global_Append(".ERROR_CMD", cmd); 2091 } 2092 } 2093 2094 /* 2095 * Print some helpful information in case of an error. 2096 * The caller should exit soon after calling this function. 2097 */ 2098 void 2099 PrintOnError(GNode *gn, const char *msg) 2100 { 2101 static GNode *errorNode = NULL; 2102 StringListNode *ln; 2103 2104 if (DEBUG(HASH)) { 2105 Targ_Stats(); 2106 Var_Stats(); 2107 } 2108 2109 if (errorNode != NULL) 2110 return; /* we've been here! */ 2111 2112 printf("%s%s: stopped", msg, progname); 2113 ln = opts.create.first; 2114 if (ln != NULL || mainNode != NULL) { 2115 printf(" making \""); 2116 if (ln != NULL) { 2117 printf("%s", (const char *)ln->datum); 2118 for (ln = ln->next; ln != NULL; ln = ln->next) 2119 printf(" %s", (const char *)ln->datum); 2120 } else 2121 printf("%s", mainNode->name); 2122 printf("\""); 2123 } 2124 printf(" in %s\n", curdir); 2125 2126 /* we generally want to keep quiet if a sub-make died */ 2127 if (shouldDieQuietly(gn, -1)) 2128 return; 2129 2130 if (gn != NULL) 2131 SetErrorVars(gn); 2132 2133 { 2134 char *errorVarsValues; 2135 enum PosixState p_s = posix_state; 2136 2137 posix_state = PS_TOO_LATE; 2138 errorVarsValues = Var_Subst( 2139 "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", 2140 SCOPE_GLOBAL, VARE_EVAL); 2141 /* TODO: handle errors */ 2142 printf("%s", errorVarsValues); 2143 free(errorVarsValues); 2144 posix_state = p_s; 2145 } 2146 2147 fflush(stdout); 2148 2149 /* 2150 * Finally, see if there is a .ERROR target, and run it if so. 2151 */ 2152 errorNode = Targ_FindNode(".ERROR"); 2153 if (errorNode != NULL) { 2154 errorNode->type |= OP_SPECIAL; 2155 Compat_Make(errorNode, errorNode); 2156 } 2157 } 2158 2159 void 2160 Main_ExportMAKEFLAGS(bool first) 2161 { 2162 static bool once = true; 2163 enum PosixState p_s; 2164 char *flags; 2165 2166 if (once != first) 2167 return; 2168 once = false; 2169 2170 p_s = posix_state; 2171 posix_state = PS_TOO_LATE; 2172 flags = Var_Subst( 2173 "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", 2174 SCOPE_CMDLINE, VARE_EVAL); 2175 /* TODO: handle errors */ 2176 if (flags[0] != '\0') 2177 setenv("MAKEFLAGS", flags, 1); 2178 free(flags); 2179 posix_state = p_s; 2180 } 2181 2182 char * 2183 getTmpdir(void) 2184 { 2185 static char *tmpdir = NULL; 2186 struct stat st; 2187 2188 if (tmpdir != NULL) 2189 return tmpdir; 2190 2191 /* Honor $TMPDIR if it is valid, strip a trailing '/'. */ 2192 tmpdir = Var_Subst("${TMPDIR:tA:U" _PATH_TMP ":S,/$,,W}/", 2193 SCOPE_GLOBAL, VARE_EVAL); 2194 /* TODO: handle errors */ 2195 2196 if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { 2197 free(tmpdir); 2198 tmpdir = bmake_strdup(_PATH_TMP); 2199 } 2200 return tmpdir; 2201 } 2202 2203 /* 2204 * Create and open a temp file using "pattern". 2205 * If tfile is provided, set it to a copy of the filename created. 2206 * Otherwise unlink the file once open. 2207 */ 2208 int 2209 mkTempFile(const char *pattern, char *tfile, size_t tfile_sz) 2210 { 2211 static char *tmpdir = NULL; 2212 char tbuf[MAXPATHLEN]; 2213 int fd; 2214 2215 if (pattern == NULL) 2216 pattern = "makeXXXXXX"; 2217 if (tmpdir == NULL) 2218 tmpdir = getTmpdir(); 2219 if (tfile == NULL) { 2220 tfile = tbuf; 2221 tfile_sz = sizeof tbuf; 2222 } 2223 2224 if (pattern[0] == '/') 2225 snprintf(tfile, tfile_sz, "%s", pattern); 2226 else 2227 snprintf(tfile, tfile_sz, "%s%s", tmpdir, pattern); 2228 2229 if ((fd = mkstemp(tfile)) < 0) 2230 Punt("mkstemp %s: %s", tfile, strerror(errno)); 2231 if (tfile == tbuf) 2232 unlink(tfile); /* we just want the descriptor */ 2233 2234 return fd; 2235 } 2236 2237 /* 2238 * Convert a string representation of a boolean into a boolean value. 2239 * Anything that looks like "No", "False", "Off", "0" etc. is false, 2240 * the empty string is the fallback, everything else is true. 2241 */ 2242 bool 2243 ParseBoolean(const char *s, bool fallback) 2244 { 2245 char ch = ch_tolower(s[0]); 2246 if (ch == '\0') 2247 return fallback; 2248 if (ch == '0' || ch == 'f' || ch == 'n') 2249 return false; 2250 if (ch == 'o') 2251 return ch_tolower(s[1]) != 'f'; 2252 return true; 2253 } 2254