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