1 /* $NetBSD: main.c,v 1.662 2025/08/09 23:13:28 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.662 2025/08/09 23:13:28 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] [-T file]\n" 186 " [-V variable] [-v variable] [variable=value] [target ...]\n", 187 (int)prognameLen, progname); 188 exit(2); 189 } 190 191 static void 192 MainParseArgDebugFile(const char *arg) 193 { 194 const char *mode; 195 size_t len; 196 char *fname; 197 198 if (opts.debug_file != stdout && opts.debug_file != stderr) 199 fclose(opts.debug_file); 200 201 if (*arg == '+') { 202 arg++; 203 mode = "a"; 204 } else 205 mode = "w"; 206 207 if (strcmp(arg, "stdout") == 0) { 208 opts.debug_file = stdout; 209 return; 210 } 211 if (strcmp(arg, "stderr") == 0) { 212 opts.debug_file = stderr; 213 return; 214 } 215 216 len = strlen(arg); 217 fname = bmake_malloc(len + 20); 218 memcpy(fname, arg, len + 1); 219 220 /* Replace the trailing '%d' after '.%d' with the pid. */ 221 if (len >= 3 && memcmp(fname + len - 3, ".%d", 3) == 0) 222 snprintf(fname + len - 2, 20, "%d", getpid()); 223 224 opts.debug_file = fopen(fname, mode); 225 if (opts.debug_file == NULL) { 226 fprintf(stderr, "Cannot open debug file \"%s\"\n", fname); 227 exit(2); 228 } 229 free(fname); 230 } 231 232 static void 233 MainParseArgDebug(const char *argvalue) 234 { 235 const char *modules; 236 DebugFlags debug = opts.debug; 237 238 for (modules = argvalue; *modules != '\0'; modules++) { 239 switch (*modules) { 240 case '0': /* undocumented, only intended for tests */ 241 memset(&debug, 0, sizeof(debug)); 242 break; 243 case 'A': 244 memset(&debug, ~0, sizeof(debug)); 245 break; 246 case 'a': 247 debug.DEBUG_ARCH = true; 248 break; 249 case 'C': 250 debug.DEBUG_CWD = true; 251 break; 252 case 'c': 253 debug.DEBUG_COND = true; 254 break; 255 case 'd': 256 debug.DEBUG_DIR = true; 257 break; 258 case 'e': 259 debug.DEBUG_ERROR = true; 260 break; 261 case 'f': 262 debug.DEBUG_FOR = true; 263 break; 264 case 'g': 265 if (modules[1] == '1') { 266 debug.DEBUG_GRAPH1 = true; 267 modules++; 268 } else if (modules[1] == '2') { 269 debug.DEBUG_GRAPH2 = true; 270 modules++; 271 } else if (modules[1] == '3') { 272 debug.DEBUG_GRAPH3 = true; 273 modules++; 274 } 275 break; 276 case 'h': 277 debug.DEBUG_HASH = true; 278 break; 279 case 'j': 280 debug.DEBUG_JOB = true; 281 break; 282 case 'L': 283 opts.strict = true; 284 break; 285 case 'l': 286 debug.DEBUG_LOUD = true; 287 break; 288 case 'M': 289 debug.DEBUG_META = true; 290 break; 291 case 'm': 292 debug.DEBUG_MAKE = true; 293 break; 294 case 'n': 295 debug.DEBUG_SCRIPT = true; 296 break; 297 case 'p': 298 debug.DEBUG_PARSE = true; 299 break; 300 case 's': 301 debug.DEBUG_SUFF = true; 302 break; 303 case 't': 304 debug.DEBUG_TARG = true; 305 break; 306 case 'V': 307 opts.debugVflag = true; 308 break; 309 case 'v': 310 debug.DEBUG_VAR = true; 311 break; 312 case 'x': 313 debug.DEBUG_SHELL = true; 314 break; 315 case 'F': 316 MainParseArgDebugFile(modules + 1); 317 goto finish; 318 default: 319 (void)fprintf(stderr, 320 "%s: illegal argument to d option -- %c\n", 321 progname, *modules); 322 usage(); 323 } 324 } 325 326 finish: 327 opts.debug = debug; 328 329 setvbuf(opts.debug_file, NULL, _IONBF, 0); 330 if (opts.debug_file != stdout) 331 setvbuf(stdout, NULL, _IOLBF, 0); 332 } 333 334 /* Is path relative or does it contain any relative component "." or ".."? */ 335 static bool 336 IsRelativePath(const char *path) 337 { 338 const char *p; 339 340 if (path[0] != '/') 341 return true; 342 p = path; 343 while ((p = strstr(p, "/.")) != NULL) { 344 p += 2; 345 if (*p == '.') 346 p++; 347 if (*p == '/' || *p == '\0') 348 return true; 349 } 350 return false; 351 } 352 353 static void 354 MainParseArgChdir(const char *argvalue) 355 { 356 struct stat sa, sb; 357 358 if (chdir(argvalue) == -1) { 359 (void)fprintf(stderr, "%s: chdir %s: %s\n", 360 progname, argvalue, strerror(errno)); 361 exit(2); /* Not 1 so -q can distinguish error */ 362 } 363 if (getcwd(curdir, MAXPATHLEN) == NULL) { 364 (void)fprintf(stderr, "%s: getcwd: %s\n", 365 progname, strerror(errno)); 366 exit(2); 367 } 368 if (!IsRelativePath(argvalue) && 369 stat(argvalue, &sa) != -1 && 370 stat(curdir, &sb) != -1 && 371 sa.st_ino == sb.st_ino && 372 sa.st_dev == sb.st_dev) 373 snprintf(curdir, MAXPATHLEN, "%s", argvalue); 374 ignorePWD = true; 375 } 376 377 static void 378 MainParseArgJobsInternal(const char *argvalue) 379 { 380 char end; 381 if (sscanf(argvalue, "%d,%d%c", 382 &tokenPoolReader, &tokenPoolWriter, &end) != 2) { 383 (void)fprintf(stderr, 384 "%s: error: invalid internal option " 385 "\"-J %s\" in \"%s\"\n", 386 progname, argvalue, curdir); 387 exit(2); 388 } 389 if (fcntl(tokenPoolReader, F_GETFD, 0) < 0 || 390 fcntl(tokenPoolWriter, F_GETFD, 0) < 0) { 391 tokenPoolReader = -1; 392 tokenPoolWriter = -1; 393 bogusJflag = true; 394 } else { 395 Global_Append(MAKEFLAGS, "-J"); 396 Global_Append(MAKEFLAGS, argvalue); 397 } 398 } 399 400 static void 401 MainParseArgJobs(const char *arg) 402 { 403 const char *p; 404 char *end; 405 char v[12]; 406 407 forceJobs = true; 408 opts.maxJobs = (int)strtol(arg, &end, 0); 409 p = end; 410 #ifdef _SC_NPROCESSORS_ONLN 411 if (*p != '\0') { 412 double d; 413 414 if (*p == 'C') 415 d = (opts.maxJobs > 0) ? opts.maxJobs : 1; 416 else if (*p == '.') { 417 d = strtod(arg, &end); 418 p = end; 419 } else 420 d = 0.0; 421 if (d > 0.0) { 422 p = ""; 423 opts.maxJobs = (int)sysconf(_SC_NPROCESSORS_ONLN); 424 opts.maxJobs = (int)(d * (double)opts.maxJobs); 425 } 426 } 427 #endif 428 if (*p != '\0' || opts.maxJobs < 1) { 429 (void)fprintf(stderr, 430 "%s: argument '%s' to option '-j' " 431 "must be a positive number\n", 432 progname, arg); 433 exit(2); /* Not 1 so -q can distinguish error */ 434 } 435 snprintf(v, sizeof(v), "%d", opts.maxJobs); 436 Global_Append(MAKEFLAGS, "-j"); 437 Global_Append(MAKEFLAGS, v); 438 Global_Set(".MAKE.JOBS", v); 439 maxJobTokens = opts.maxJobs; 440 } 441 442 static void 443 MainParseArgSysInc(const char *argvalue) 444 { 445 if (strncmp(argvalue, ".../", 4) == 0) { 446 char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4); 447 if (found_path == NULL) 448 return; 449 (void)SearchPath_Add(sysIncPath, found_path); 450 free(found_path); 451 } else { 452 (void)SearchPath_Add(sysIncPath, argvalue); 453 } 454 Global_Append(MAKEFLAGS, "-m"); 455 Global_Append(MAKEFLAGS, argvalue); 456 Dir_SetSYSPATH(); 457 } 458 459 static bool 460 MainParseOption(char c, const char *argvalue) 461 { 462 switch (c) { 463 case '\0': 464 break; 465 case 'B': 466 opts.compatMake = true; 467 Global_Append(MAKEFLAGS, "-B"); 468 Global_Set(".MAKE.MODE", "compat"); 469 break; 470 case 'C': 471 MainParseArgChdir(argvalue); 472 break; 473 case 'D': 474 if (argvalue[0] == '\0') 475 return false; 476 Var_SetExpand(SCOPE_GLOBAL, argvalue, "1"); 477 Global_Append(MAKEFLAGS, "-D"); 478 Global_Append(MAKEFLAGS, argvalue); 479 break; 480 case 'I': 481 SearchPath_Add(parseIncPath, argvalue); 482 Global_Append(MAKEFLAGS, "-I"); 483 Global_Append(MAKEFLAGS, argvalue); 484 break; 485 case 'J': 486 MainParseArgJobsInternal(argvalue); 487 break; 488 case 'N': 489 opts.noExecute = true; 490 opts.noRecursiveExecute = true; 491 Global_Append(MAKEFLAGS, "-N"); 492 break; 493 case 'S': 494 opts.keepgoing = false; 495 Global_Append(MAKEFLAGS, "-S"); 496 break; 497 case 'T': 498 tracefile = bmake_strdup(argvalue); 499 Global_Append(MAKEFLAGS, "-T"); 500 Global_Append(MAKEFLAGS, argvalue); 501 break; 502 case 'V': 503 case 'v': 504 opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED; 505 Lst_Append(&opts.variables, bmake_strdup(argvalue)); 506 /* XXX: Why always -V? */ 507 Global_Append(MAKEFLAGS, "-V"); 508 Global_Append(MAKEFLAGS, argvalue); 509 break; 510 case 'W': 511 opts.parseWarnFatal = true; 512 /* XXX: why no Global_Append? */ 513 break; 514 case 'X': 515 opts.varNoExportEnv = true; 516 Global_Append(MAKEFLAGS, "-X"); 517 break; 518 case 'd': 519 /* If '-d-opts' don't pass to children */ 520 if (argvalue[0] == '-') 521 argvalue++; 522 else { 523 Global_Append(MAKEFLAGS, "-d"); 524 Global_Append(MAKEFLAGS, argvalue); 525 } 526 MainParseArgDebug(argvalue); 527 break; 528 case 'e': 529 opts.checkEnvFirst = true; 530 Global_Append(MAKEFLAGS, "-e"); 531 break; 532 case 'f': 533 Lst_Append(&opts.makefiles, bmake_strdup(argvalue)); 534 break; 535 case 'i': 536 opts.ignoreErrors = true; 537 Global_Append(MAKEFLAGS, "-i"); 538 break; 539 case 'j': 540 MainParseArgJobs(argvalue); 541 break; 542 case 'k': 543 opts.keepgoing = true; 544 Global_Append(MAKEFLAGS, "-k"); 545 break; 546 case 'm': 547 MainParseArgSysInc(argvalue); 548 /* XXX: why no Var_Append? */ 549 break; 550 case 'n': 551 opts.noExecute = true; 552 Global_Append(MAKEFLAGS, "-n"); 553 break; 554 case 'q': 555 opts.query = true; 556 /* Kind of nonsensical, wot? */ 557 Global_Append(MAKEFLAGS, "-q"); 558 break; 559 case 'r': 560 opts.noBuiltins = true; 561 Global_Append(MAKEFLAGS, "-r"); 562 break; 563 case 's': 564 opts.silent = true; 565 Global_Append(MAKEFLAGS, "-s"); 566 break; 567 case 't': 568 opts.touch = true; 569 Global_Append(MAKEFLAGS, "-t"); 570 break; 571 case 'w': 572 opts.enterFlag = true; 573 Global_Append(MAKEFLAGS, "-w"); 574 break; 575 default: 576 usage(); 577 } 578 return true; 579 } 580 581 /* 582 * Parse the given arguments. Called from main() and from 583 * Main_ParseArgLine() when the .MAKEFLAGS target is used. 584 * 585 * The arguments must be treated as read-only and will be freed after the 586 * call. 587 * 588 * XXX: Deal with command line overriding .MAKEFLAGS in makefile 589 */ 590 static void 591 MainParseArgs(int argc, char **argv) 592 { 593 char c; 594 int arginc; 595 char *argvalue; 596 char *optscan; 597 bool inOption, dashDash = false; 598 599 const char *optspecs = "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstv:w"; 600 /* Can't actually use getopt(3) because rescanning is not portable */ 601 602 rearg: 603 inOption = false; 604 optscan = NULL; 605 while (argc > 1) { 606 const char *optspec; 607 if (!inOption) 608 optscan = argv[1]; 609 c = *optscan++; 610 arginc = 0; 611 if (inOption) { 612 if (c == '\0') { 613 argv++; 614 argc--; 615 inOption = false; 616 continue; 617 } 618 } else { 619 if (c != '-' || dashDash) 620 break; 621 inOption = true; 622 c = *optscan++; 623 } 624 /* '-' found at some earlier point */ 625 optspec = strchr(optspecs, c); 626 if (c != '\0' && optspec != NULL && optspec[1] == ':') { 627 /* 628 * -<something> found, and <something> should have an 629 * argument 630 */ 631 inOption = false; 632 arginc = 1; 633 argvalue = optscan; 634 if (*argvalue == '\0') { 635 if (argc < 3) 636 goto noarg; 637 argvalue = argv[2]; 638 arginc = 2; 639 } 640 } else { 641 argvalue = NULL; 642 } 643 switch (c) { 644 case '\0': 645 arginc = 1; 646 inOption = false; 647 break; 648 case '-': 649 dashDash = true; 650 break; 651 default: 652 if (!MainParseOption(c, argvalue)) 653 goto noarg; 654 } 655 argv += arginc; 656 argc -= arginc; 657 } 658 659 /* 660 * See if the rest of the arguments are variable assignments and 661 * perform them if so. Else take them to be targets and stuff them 662 * on the end of the "create" list. 663 */ 664 for (; argc > 1; argv++, argc--) { 665 if (!Parse_VarAssign(argv[1], false, SCOPE_CMDLINE)) { 666 if (argv[1][0] == '\0') 667 Punt("illegal (null) argument."); 668 if (argv[1][0] == '-' && !dashDash) 669 goto rearg; 670 Lst_Append(&opts.create, bmake_strdup(argv[1])); 671 } 672 } 673 674 return; 675 noarg: 676 (void)fprintf(stderr, "%s: option requires an argument -- %c\n", 677 progname, c); 678 usage(); 679 } 680 681 /* 682 * Break a line of arguments into words and parse them. 683 * 684 * Used when a .MFLAGS or .MAKEFLAGS target is encountered during parsing and 685 * by main() when reading the MAKEFLAGS environment variable. 686 */ 687 void 688 Main_ParseArgLine(const char *line) 689 { 690 Words words; 691 char *buf; 692 const char *p; 693 694 if (line == NULL) 695 return; 696 for (p = line; *p == ' '; p++) 697 continue; 698 if (p[0] == '\0') 699 return; 700 701 { 702 FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE"); 703 buf = str_concat3(argv0.str, " ", p); 704 FStr_Done(&argv0); 705 } 706 707 words = Str_Words(buf, true); 708 if (words.words == NULL) { 709 Error("Unterminated quoted string [%s]", buf); 710 free(buf); 711 return; 712 } 713 free(buf); 714 EvalStack_PushMakeflags(line); 715 MainParseArgs((int)words.len, words.words); 716 EvalStack_Pop(); 717 718 Words_Free(words); 719 } 720 721 bool 722 Main_SetObjdir(bool writable, const char *fmt, ...) 723 { 724 struct stat sb; 725 char *path; 726 char buf[MAXPATHLEN + 1]; 727 char buf2[MAXPATHLEN + 1]; 728 va_list ap; 729 730 va_start(ap, fmt); 731 vsnprintf(path = buf, MAXPATHLEN, fmt, ap); 732 va_end(ap); 733 734 if (path[0] != '/') { 735 if (snprintf(buf2, MAXPATHLEN, "%s/%s", curdir, path) <= MAXPATHLEN) 736 path = buf2; 737 else 738 return false; 739 } 740 741 /* look for the directory and try to chdir there */ 742 if (stat(path, &sb) != 0 || !S_ISDIR(sb.st_mode)) 743 return false; 744 745 if ((writable && access(path, W_OK) != 0) || chdir(path) != 0) { 746 (void)fprintf(stderr, "%s: warning: %s: %s\n", 747 progname, path, strerror(errno)); 748 /* Allow debugging how we got here - not always obvious */ 749 if (GetBooleanExpr("${MAKE_DEBUG_OBJDIR_CHECK_WRITABLE}", 750 false)) 751 PrintOnError(NULL, ""); 752 return false; 753 } 754 755 snprintf(objdir, sizeof objdir, "%s", path); 756 Global_Set(".OBJDIR", objdir); 757 setenv("PWD", objdir, 1); 758 Dir_InitDot(); 759 purge_relative_cached_realpaths(); 760 if (opts.enterFlag && strcmp(objdir, curdir) != 0) 761 enterFlagObj = true; 762 return true; 763 } 764 765 static bool 766 SetVarObjdir(bool writable, const char *var, const char *suffix) 767 { 768 FStr path = Var_Value(SCOPE_CMDLINE, var); 769 770 if (path.str == NULL || path.str[0] == '\0') { 771 FStr_Done(&path); 772 return false; 773 } 774 775 Var_Expand(&path, SCOPE_GLOBAL, VARE_EVAL); 776 777 (void)Main_SetObjdir(writable, "%s%s", path.str, suffix); 778 779 FStr_Done(&path); 780 return true; 781 } 782 783 /* 784 * Splits str into words (in-place, modifying it), adding them to the list. 785 * The string must be kept alive as long as the list. 786 */ 787 void 788 AppendWords(StringList *lp, char *str) 789 { 790 char *p; 791 const char *sep = " \t"; 792 793 for (p = strtok(str, sep); p != NULL; p = strtok(NULL, sep)) 794 Lst_Append(lp, p); 795 } 796 797 #ifdef SIGINFO 798 static void 799 siginfo(int signo MAKE_ATTR_UNUSED) 800 { 801 char dir[MAXPATHLEN]; 802 char str[2 * MAXPATHLEN]; 803 int len; 804 if (getcwd(dir, sizeof dir) == NULL) 805 return; 806 len = snprintf(str, sizeof str, "%s: Working in: %s\n", progname, dir); 807 if (len > 0) 808 (void)write(STDERR_FILENO, str, (size_t)len); 809 } 810 #endif 811 812 /* Allow makefiles some control over the mode we run in. */ 813 static void 814 MakeMode(void) 815 { 816 char *mode = Var_Subst("${.MAKE.MODE:tl}", SCOPE_GLOBAL, VARE_EVAL); 817 /* TODO: handle errors */ 818 819 if (mode[0] != '\0') { 820 if (strstr(mode, "compat") != NULL) { 821 opts.compatMake = true; 822 forceJobs = false; 823 } 824 #if USE_META 825 if (strstr(mode, "meta") != NULL) 826 meta_mode_init(mode); 827 #endif 828 if (strstr(mode, "randomize-targets") != NULL) 829 opts.randomizeTargets = true; 830 } 831 832 free(mode); 833 } 834 835 static void 836 PrintVariable(const char *varname, bool expandVars) 837 { 838 if (strchr(varname, '$') != NULL) { 839 char *evalue = Var_Subst(varname, SCOPE_GLOBAL, VARE_EVAL); 840 /* TODO: handle errors */ 841 printf("%s\n", evalue); 842 free(evalue); 843 844 } else if (expandVars) { 845 char *expr = str_concat3("${", varname, "}"); 846 char *evalue = Var_Subst(expr, SCOPE_GLOBAL, VARE_EVAL); 847 /* TODO: handle errors */ 848 free(expr); 849 printf("%s\n", evalue); 850 free(evalue); 851 852 } else { 853 FStr value = Var_Value(SCOPE_GLOBAL, varname); 854 printf("%s\n", value.str != NULL ? value.str : ""); 855 FStr_Done(&value); 856 } 857 } 858 859 /* 860 * Return a bool based on a variable. 861 * 862 * If the knob is not set, return the fallback. 863 * If set, anything that looks or smells like "No", "False", "Off", "0", etc. 864 * is false, otherwise true. 865 */ 866 bool 867 GetBooleanExpr(const char *expr, bool fallback) 868 { 869 char *value; 870 bool res; 871 872 value = Var_Subst(expr, SCOPE_GLOBAL, VARE_EVAL); 873 /* TODO: handle errors */ 874 res = ParseBoolean(value, fallback); 875 free(value); 876 return res; 877 } 878 879 static void 880 PrintVariables(void) 881 { 882 StringListNode *ln; 883 bool expandVars; 884 885 if (opts.printVars == PVM_EXPANDED) 886 expandVars = true; 887 else if (opts.debugVflag) 888 expandVars = false; 889 else 890 expandVars = GetBooleanExpr("${.MAKE.EXPAND_VARIABLES}", 891 false); 892 893 for (ln = opts.variables.first; ln != NULL; ln = ln->next) 894 PrintVariable(ln->datum, expandVars); 895 } 896 897 static bool 898 MakeTargets(void) 899 { 900 GNodeList targets = LST_INIT; 901 bool outOfDate; /* false if all targets up to date */ 902 903 if (Lst_IsEmpty(&opts.create)) 904 Parse_MainName(&targets); 905 else 906 Targ_FindList(&targets, &opts.create); 907 908 if (!opts.compatMake) { 909 if (!opts.query) { 910 Job_Init(); 911 jobsRunning = true; 912 } 913 914 outOfDate = Make_MakeParallel(&targets); 915 } else { 916 Compat_MakeAll(&targets); 917 outOfDate = false; 918 } 919 Lst_Done(&targets); /* Don't free the targets themselves. */ 920 return outOfDate; 921 } 922 923 /* 924 * Set up the .TARGETS variable to contain the list of targets to be created. 925 * If none specified, make the variable empty for now, the parser will fill 926 * in the default or .MAIN target later. 927 */ 928 static void 929 InitVarTargets(void) 930 { 931 StringListNode *ln; 932 933 if (Lst_IsEmpty(&opts.create)) { 934 Global_Set(".TARGETS", ""); 935 return; 936 } 937 938 for (ln = opts.create.first; ln != NULL; ln = ln->next) { 939 const char *name = ln->datum; 940 Global_Append(".TARGETS", name); 941 } 942 } 943 944 static void 945 InitRandom(void) 946 { 947 struct timeval tv; 948 949 gettimeofday(&tv, NULL); 950 srandom((unsigned)(tv.tv_sec + tv.tv_usec)); 951 } 952 953 static const char * 954 InitVarMachine(const struct utsname *utsname MAKE_ATTR_UNUSED) 955 { 956 const char *machine = getenv("MACHINE"); 957 if (machine != NULL) 958 return machine; 959 960 #if defined(MAKE_NATIVE) 961 return utsname->machine; 962 #elif defined(MAKE_MACHINE) 963 return MAKE_MACHINE; 964 #else 965 return "unknown"; 966 #endif 967 } 968 969 static const char * 970 InitVarMachineArch(void) 971 { 972 const char *env = getenv("MACHINE_ARCH"); 973 if (env != NULL) 974 return env; 975 976 #ifdef MAKE_NATIVE 977 { 978 struct utsname utsname; 979 static char machine_arch_buf[sizeof utsname.machine]; 980 const int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; 981 size_t len = sizeof machine_arch_buf; 982 983 if (sysctl(mib, (unsigned)__arraycount(mib), 984 machine_arch_buf, &len, NULL, 0) < 0) { 985 (void)fprintf(stderr, "%s: sysctl: %s\n", 986 progname, strerror(errno)); 987 exit(2); 988 } 989 990 return machine_arch_buf; 991 } 992 #elif defined(MACHINE_ARCH) 993 return MACHINE_ARCH; 994 #elif defined(MAKE_MACHINE_ARCH) 995 return MAKE_MACHINE_ARCH; 996 #else 997 return "unknown"; 998 #endif 999 } 1000 1001 #ifndef NO_PWD_OVERRIDE 1002 /* 1003 * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX 1004 * since the value of curdir can vary depending on how we got 1005 * here. That is, sitting at a shell prompt (shell that provides $PWD) 1006 * or via subdir.mk, in which case it's likely a shell which does 1007 * not provide it. 1008 * 1009 * So, to stop it breaking this case only, we ignore PWD if 1010 * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains an expression. 1011 */ 1012 static void 1013 HandlePWD(const struct stat *curdir_st) 1014 { 1015 char *pwd; 1016 FStr makeobjdir; 1017 struct stat pwd_st; 1018 1019 if (ignorePWD || (pwd = getenv("PWD")) == NULL) 1020 return; 1021 1022 if (Var_Exists(SCOPE_CMDLINE, "MAKEOBJDIRPREFIX")) 1023 return; 1024 1025 makeobjdir = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIR"); 1026 if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL) 1027 goto ignore_pwd; 1028 1029 if (stat(pwd, &pwd_st) == 0 && 1030 curdir_st->st_ino == pwd_st.st_ino && 1031 curdir_st->st_dev == pwd_st.st_dev) 1032 snprintf(curdir, MAXPATHLEN, "%s", pwd); 1033 1034 ignore_pwd: 1035 FStr_Done(&makeobjdir); 1036 } 1037 #endif 1038 1039 /* 1040 * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, MAKEOBJDIR is set 1041 * in the environment, try only that value and fall back to .CURDIR if it 1042 * does not exist. 1043 * 1044 * Otherwise, try _PATH_OBJDIR.MACHINE-MACHINE_ARCH, _PATH_OBJDIR.MACHINE, 1045 * and finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none of these 1046 * paths exist, just use .CURDIR. 1047 */ 1048 static void 1049 InitObjdir(const char *machine, const char *machine_arch) 1050 { 1051 bool writable; 1052 1053 Dir_InitCur(curdir); 1054 writable = GetBooleanExpr("${MAKE_OBJDIR_CHECK_WRITABLE}", true); 1055 (void)Main_SetObjdir(false, "%s", curdir); 1056 1057 if (!SetVarObjdir(writable, "MAKEOBJDIRPREFIX", curdir) && 1058 !SetVarObjdir(writable, "MAKEOBJDIR", "") && 1059 !Main_SetObjdir(writable, "%s.%s-%s", _PATH_OBJDIR, machine, machine_arch) && 1060 !Main_SetObjdir(writable, "%s.%s", _PATH_OBJDIR, machine) && 1061 !Main_SetObjdir(writable, "%s", _PATH_OBJDIR)) 1062 (void)Main_SetObjdir(writable, "%s%s", _PATH_OBJDIRPREFIX, curdir); 1063 } 1064 1065 /* get rid of resource limit on file descriptors */ 1066 static void 1067 UnlimitFiles(void) 1068 { 1069 #if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)) 1070 struct rlimit rl; 1071 if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && 1072 rl.rlim_cur != rl.rlim_max) { 1073 rl.rlim_cur = rl.rlim_max; 1074 (void)setrlimit(RLIMIT_NOFILE, &rl); 1075 } 1076 #endif 1077 } 1078 1079 static void 1080 CmdOpts_Init(void) 1081 { 1082 opts.compatMake = false; 1083 memset(&opts.debug, 0, sizeof(opts.debug)); 1084 /* opts.debug_file has already been initialized earlier */ 1085 opts.strict = false; 1086 opts.debugVflag = false; 1087 opts.checkEnvFirst = false; 1088 Lst_Init(&opts.makefiles); 1089 opts.ignoreErrors = false; /* Pay attention to non-zero returns */ 1090 opts.maxJobs = 1; 1091 opts.keepgoing = false; /* Stop on error */ 1092 opts.noRecursiveExecute = false; /* Execute all .MAKE targets */ 1093 opts.noExecute = false; /* Execute all commands */ 1094 opts.query = false; 1095 opts.noBuiltins = false; /* Read the built-in rules */ 1096 opts.silent = false; /* Print commands as executed */ 1097 opts.touch = false; 1098 opts.printVars = PVM_NONE; 1099 Lst_Init(&opts.variables); 1100 opts.parseWarnFatal = false; 1101 opts.enterFlag = false; 1102 opts.varNoExportEnv = false; 1103 Lst_Init(&opts.create); 1104 } 1105 1106 /* 1107 * Initialize MAKE and .MAKE to the path of the executable, so that it can be 1108 * found by execvp(3) and the shells, even after a chdir. 1109 * 1110 * If it's a relative path and contains a '/', resolve it to an absolute path. 1111 * Otherwise keep it as is, assuming it will be found in the PATH. 1112 */ 1113 static void 1114 InitVarMake(const char *argv0) 1115 { 1116 const char *make = argv0; 1117 char pathbuf[MAXPATHLEN]; 1118 1119 if (argv0[0] != '/' && strchr(argv0, '/') != NULL) { 1120 const char *abspath = cached_realpath(argv0, pathbuf); 1121 struct stat st; 1122 if (abspath != NULL && abspath[0] == '/' && 1123 stat(make, &st) == 0) 1124 make = abspath; 1125 } 1126 1127 Global_Set("MAKE", make); 1128 Global_Set(".MAKE", make); 1129 } 1130 1131 /* 1132 * Add the directories from the colon-separated syspath to defSysIncPath. 1133 * After returning, the contents of syspath is unspecified. 1134 */ 1135 static void 1136 InitDefSysIncPath(char *syspath) 1137 { 1138 static char defsyspath[] = _PATH_DEFSYSPATH; 1139 char *start, *p; 1140 1141 /* 1142 * If no user-supplied system path was given (through the -m option) 1143 * add the directories from the DEFSYSPATH (more than one may be given 1144 * as dir1:...:dirn) to the system include path. 1145 */ 1146 if (syspath == NULL || syspath[0] == '\0') 1147 syspath = defsyspath; 1148 else 1149 syspath = bmake_strdup(syspath); 1150 1151 for (start = syspath; *start != '\0'; start = p) { 1152 for (p = start; *p != '\0' && *p != ':'; p++) 1153 continue; 1154 if (*p == ':') 1155 *p++ = '\0'; 1156 1157 /* look for magic parent directory search string */ 1158 if (strncmp(start, ".../", 4) == 0) { 1159 char *dir = Dir_FindHereOrAbove(curdir, start + 4); 1160 if (dir != NULL) { 1161 (void)SearchPath_Add(defSysIncPath, dir); 1162 free(dir); 1163 } 1164 } else { 1165 (void)SearchPath_Add(defSysIncPath, start); 1166 } 1167 } 1168 1169 if (syspath != defsyspath) 1170 free(syspath); 1171 } 1172 1173 static void 1174 ReadBuiltinRules(void) 1175 { 1176 StringListNode *ln; 1177 StringList sysMkFiles = LST_INIT; 1178 1179 SearchPath_Expand( 1180 Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath, 1181 _PATH_DEFSYSMK, 1182 &sysMkFiles); 1183 if (Lst_IsEmpty(&sysMkFiles)) 1184 Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); 1185 1186 for (ln = sysMkFiles.first; ln != NULL; ln = ln->next) 1187 if (ReadMakefile(ln->datum)) 1188 break; 1189 1190 if (ln == NULL) 1191 Fatal("%s: cannot open %s.", 1192 progname, (const char *)sysMkFiles.first->datum); 1193 1194 Lst_DoneFree(&sysMkFiles); 1195 } 1196 1197 static void 1198 InitMaxJobs(void) 1199 { 1200 char *value; 1201 int n; 1202 1203 if (bogusJflag && !opts.compatMake) { 1204 opts.compatMake = true; 1205 Parse_Error(PARSE_WARNING, 1206 "Invalid internal option \"-J\" in \"%s\"; " 1207 "see the manual page", 1208 curdir); 1209 PrintStackTrace(true); 1210 return; 1211 } 1212 if (forceJobs || opts.compatMake || 1213 !Var_Exists(SCOPE_GLOBAL, ".MAKE.JOBS")) 1214 return; 1215 1216 value = Var_Subst("${.MAKE.JOBS}", SCOPE_GLOBAL, VARE_EVAL); 1217 /* TODO: handle errors */ 1218 n = (int)strtol(value, NULL, 0); 1219 if (n < 1) { 1220 (void)fprintf(stderr, 1221 "%s: illegal value for .MAKE.JOBS " 1222 "-- must be positive integer!\n", 1223 progname); 1224 exit(2); /* Not 1 so -q can distinguish error */ 1225 } 1226 1227 if (n != opts.maxJobs) { 1228 Global_Append(MAKEFLAGS, "-j"); 1229 Global_Append(MAKEFLAGS, value); 1230 } 1231 1232 opts.maxJobs = n; 1233 maxJobTokens = opts.maxJobs; 1234 forceJobs = true; 1235 free(value); 1236 } 1237 1238 /* 1239 * For compatibility, look at the directories in the VPATH variable 1240 * and add them to the search path, if the variable is defined. The 1241 * variable's value is in the same format as the PATH environment 1242 * variable, i.e. <directory>:<directory>:<directory>... 1243 */ 1244 static void 1245 InitVpath(void) 1246 { 1247 char *vpath, savec, *path; 1248 if (!Var_Exists(SCOPE_CMDLINE, "VPATH")) 1249 return; 1250 1251 vpath = Var_Subst("${VPATH}", SCOPE_CMDLINE, VARE_EVAL); 1252 /* TODO: handle errors */ 1253 path = vpath; 1254 do { 1255 char *p; 1256 /* skip to end of directory */ 1257 for (p = path; *p != ':' && *p != '\0'; p++) 1258 continue; 1259 /* Save terminator character so know when to stop */ 1260 savec = *p; 1261 *p = '\0'; 1262 /* Add directory to search path */ 1263 (void)SearchPath_Add(&dirSearchPath, path); 1264 *p = savec; 1265 path = p + 1; 1266 } while (savec == ':'); 1267 free(vpath); 1268 } 1269 1270 static void 1271 ReadAllMakefiles(const StringList *makefiles) 1272 { 1273 StringListNode *ln; 1274 1275 for (ln = makefiles->first; ln != NULL; ln = ln->next) { 1276 const char *fname = ln->datum; 1277 if (!ReadMakefile(fname)) 1278 Fatal("%s: cannot open %s.", progname, fname); 1279 } 1280 } 1281 1282 static void 1283 ReadFirstDefaultMakefile(void) 1284 { 1285 StringList makefiles = LST_INIT; 1286 StringListNode *ln; 1287 char *prefs = Var_Subst("${.MAKE.MAKEFILE_PREFERENCE}", 1288 SCOPE_CMDLINE, VARE_EVAL); 1289 /* TODO: handle errors */ 1290 1291 AppendWords(&makefiles, prefs); 1292 1293 for (ln = makefiles.first; ln != NULL; ln = ln->next) 1294 if (ReadMakefile(ln->datum)) 1295 break; 1296 1297 Lst_Done(&makefiles); 1298 free(prefs); 1299 } 1300 1301 /* 1302 * Initialize variables such as MAKE, MACHINE, .MAKEFLAGS. 1303 * Initialize a few modules. 1304 * Parse the arguments from MAKEFLAGS and the command line. 1305 */ 1306 static void 1307 main_Init(int argc, char **argv) 1308 { 1309 struct stat sa; 1310 const char *machine; 1311 const char *machine_arch; 1312 char *syspath = getenv("MAKESYSPATH"); 1313 struct utsname utsname; 1314 1315 /* default to writing debug to stderr */ 1316 opts.debug_file = stderr; 1317 1318 Str_Intern_Init(); 1319 HashTable_Init(&cached_realpaths); 1320 1321 #ifdef SIGINFO 1322 (void)bmake_signal(SIGINFO, siginfo); 1323 #endif 1324 1325 InitRandom(); 1326 1327 progname = str_basename(argv[0]); 1328 1329 UnlimitFiles(); 1330 1331 if (uname(&utsname) == -1) { 1332 (void)fprintf(stderr, "%s: uname: %s\n", progname, 1333 strerror(errno)); 1334 exit(2); 1335 } 1336 1337 machine = InitVarMachine(&utsname); 1338 machine_arch = InitVarMachineArch(); 1339 1340 myPid = getpid(); 1341 1342 /* Just in case MAKEOBJDIR wants us to do something tricky. */ 1343 Targ_Init(); 1344 Global_Set_ReadOnly(".MAKE.OS", utsname.sysname); 1345 Global_Set("MACHINE", machine); 1346 Global_Set("MACHINE_ARCH", machine_arch); 1347 #ifdef MAKE_VERSION 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(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