1 /* $NetBSD: main.c,v 1.669 2026/04/06 17:13:54 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.669 2026/04/06 17:13:54 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 #ifndef MAKE_SAVE_DOLLARS_DEFAULT 1300 # define MAKE_SAVE_DOLLARS_DEFAULT "yes" 1301 #endif 1302 1303 /* 1304 * Initialize variables such as MAKE, MACHINE, .MAKEFLAGS. 1305 * Initialize a few modules. 1306 * Parse the arguments from MAKEFLAGS and the command line. 1307 */ 1308 static void 1309 main_Init(int argc, char **argv) 1310 { 1311 struct stat sa; 1312 const char *machine; 1313 const char *machine_arch; 1314 char *syspath = getenv("MAKESYSPATH"); 1315 struct utsname utsname; 1316 1317 /* default to writing debug to stderr */ 1318 opts.debug_file = stderr; 1319 1320 Str_Intern_Init(); 1321 HashTable_Init(&cached_realpaths); 1322 1323 #ifdef SIGINFO 1324 (void)bmake_signal(SIGINFO, siginfo); 1325 #endif 1326 1327 InitRandom(); 1328 1329 progname = str_basename(argv[0]); 1330 1331 UnlimitFiles(); 1332 1333 if (uname(&utsname) == -1) { 1334 (void)fprintf(stderr, "%s: uname: %s\n", progname, 1335 strerror(errno)); 1336 exit(2); 1337 } 1338 1339 machine = InitVarMachine(&utsname); 1340 machine_arch = InitVarMachineArch(); 1341 1342 myPid = getpid(); 1343 1344 /* Just in case MAKEOBJDIR wants us to do something tricky. */ 1345 Targ_Init(); 1346 Global_Set_ReadOnly(".MAKE.OS", utsname.sysname); 1347 Global_Set("MACHINE", machine); 1348 Global_Set("MACHINE_ARCH", machine_arch); 1349 #ifdef MAKE_VERSION 1350 Global_Set_ReadOnly(".MAKE.VERSION", MAKE_VERSION); 1351 /* for backwards compatibility */ 1352 Global_Set("MAKE_VERSION", MAKE_VERSION); 1353 #endif 1354 Global_Set_ReadOnly(".newline", "\n"); 1355 #ifndef MAKEFILE_PREFERENCE_LIST 1356 /* This is the traditional preference for makefiles. */ 1357 # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" 1358 #endif 1359 Global_Set(".MAKE.MAKEFILE_PREFERENCE", MAKEFILE_PREFERENCE_LIST); 1360 Global_Set(".MAKE.DEPENDFILE", ".depend"); 1361 /* Tell makefiles like jobs.mk whether we support -jC */ 1362 #ifdef _SC_NPROCESSORS_ONLN 1363 Global_Set_ReadOnly(".MAKE.JOBS.C", "yes"); 1364 #else 1365 Global_Set_ReadOnly(".MAKE.JOBS.C", "no"); 1366 #endif 1367 Global_Set(MAKE_SAVE_DOLLARS, MAKE_SAVE_DOLLARS_DEFAULT); 1368 1369 CmdOpts_Init(); 1370 allPrecious = false; /* Remove targets when interrupted */ 1371 deleteOnError = false; /* Historical default behavior */ 1372 jobsRunning = false; 1373 1374 maxJobTokens = opts.maxJobs; 1375 ignorePWD = false; 1376 1377 /* 1378 * Initialize the parsing, directory and variable modules to prepare 1379 * for the reading of inclusion paths and variable settings on the 1380 * command line 1381 */ 1382 1383 /* 1384 * Initialize various variables. 1385 * MAKE also gets this name, for compatibility 1386 * .MAKEFLAGS gets set to the empty string just in case. 1387 * MFLAGS also gets initialized empty, for compatibility. 1388 */ 1389 Parse_Init(); 1390 InitVarMake(argv[0]); 1391 Global_Set(MAKEFLAGS, ""); 1392 Global_Set(".MAKEOVERRIDES", ""); 1393 Global_Set("MFLAGS", ""); 1394 Global_Set(".ALLTARGETS", ""); 1395 Global_Set_ReadOnly(".MAKE.LEVEL.ENV", MAKE_LEVEL_ENV); 1396 1397 /* Set some other useful variables. */ 1398 { 1399 char buf[64]; 1400 const char *ep = getenv(MAKE_LEVEL_ENV); 1401 1402 makelevel = ep != NULL && ep[0] != '\0' ? atoi(ep) : 0; 1403 if (makelevel < 0) 1404 makelevel = 0; 1405 snprintf(buf, sizeof buf, "%d", makelevel); 1406 Global_Set(".MAKE.LEVEL", buf); 1407 snprintf(buf, sizeof buf, "%u", myPid); 1408 Global_Set_ReadOnly(".MAKE.PID", buf); 1409 snprintf(buf, sizeof buf, "%u", getppid()); 1410 Global_Set_ReadOnly(".MAKE.PPID", buf); 1411 snprintf(buf, sizeof buf, "%u", getuid()); 1412 Global_Set_ReadOnly(".MAKE.UID", buf); 1413 snprintf(buf, sizeof buf, "%u", getgid()); 1414 Global_Set_ReadOnly(".MAKE.GID", buf); 1415 } 1416 if (makelevel > 0) { 1417 char pn[1024]; 1418 snprintf(pn, sizeof pn, "%s[%d]", progname, makelevel); 1419 progname = bmake_strdup(pn); 1420 } 1421 1422 #ifdef USE_META 1423 meta_init(); 1424 #endif 1425 Dir_Init(); 1426 1427 if (getcwd(curdir, MAXPATHLEN) == NULL) { 1428 (void)fprintf(stderr, "%s: getcwd: %s\n", 1429 progname, strerror(errno)); 1430 exit(2); 1431 } 1432 1433 { 1434 char *makeflags = explode(getenv("MAKEFLAGS")); 1435 Main_ParseArgLine(makeflags); 1436 free(makeflags); 1437 } 1438 1439 MainParseArgs(argc, argv); 1440 1441 if (opts.enterFlag) 1442 printf("%s: Entering directory `%s'\n", progname, curdir); 1443 1444 if (stat(curdir, &sa) == -1) { 1445 (void)fprintf(stderr, "%s: stat %s: %s\n", 1446 progname, curdir, strerror(errno)); 1447 exit(2); 1448 } 1449 1450 #ifndef NO_PWD_OVERRIDE 1451 HandlePWD(&sa); 1452 #endif 1453 Global_Set(".CURDIR", curdir); 1454 1455 InitObjdir(machine, machine_arch); 1456 1457 Arch_Init(); 1458 Suff_Init(); 1459 Trace_Init(tracefile); 1460 1461 defaultNode = NULL; 1462 (void)time(&now); 1463 1464 Trace_Log(MAKESTART, NULL); 1465 1466 InitVarTargets(); 1467 1468 InitDefSysIncPath(syspath); 1469 } 1470 1471 /* 1472 * Read the system makefile followed by either makefile, Makefile or the 1473 * files given by the -f option. Exit on parse errors. 1474 */ 1475 static void 1476 main_ReadFiles(void) 1477 { 1478 1479 if (Lst_IsEmpty(&sysIncPath->dirs)) 1480 SearchPath_AddAll(sysIncPath, defSysIncPath); 1481 1482 Dir_SetSYSPATH(); 1483 if (!opts.noBuiltins) 1484 ReadBuiltinRules(); 1485 1486 posix_state = PS_MAYBE_NEXT_LINE; 1487 if (!Lst_IsEmpty(&opts.makefiles)) 1488 ReadAllMakefiles(&opts.makefiles); 1489 else 1490 ReadFirstDefaultMakefile(); 1491 } 1492 1493 /* Compute the dependency graph. */ 1494 static void 1495 main_PrepareMaking(void) 1496 { 1497 /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ 1498 if (!opts.noBuiltins || opts.printVars == PVM_NONE) { 1499 makeDependfile = Var_Subst("${.MAKE.DEPENDFILE}", 1500 SCOPE_CMDLINE, VARE_EVAL); 1501 if (makeDependfile[0] != '\0') { 1502 /* TODO: handle errors */ 1503 doing_depend = true; 1504 (void)ReadMakefile(makeDependfile); 1505 doing_depend = false; 1506 } 1507 } 1508 1509 if (enterFlagObj) 1510 printf("%s: Entering directory `%s'\n", progname, objdir); 1511 1512 MakeMode(); 1513 1514 { 1515 FStr makeflags = Var_Value(SCOPE_GLOBAL, MAKEFLAGS); 1516 Global_Append("MFLAGS", makeflags.str); 1517 FStr_Done(&makeflags); 1518 } 1519 1520 InitMaxJobs(); 1521 1522 if (!opts.compatMake && !forceJobs) 1523 opts.compatMake = true; 1524 1525 if (!opts.compatMake) 1526 TokenPool_Init(maxJobTokens, tokenPoolReader, tokenPoolWriter); 1527 DEBUG5(JOB, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", 1528 tokenPoolReader, tokenPoolWriter, opts.maxJobs, maxJobTokens, 1529 opts.compatMake ? 1 : 0); 1530 1531 if (opts.printVars == PVM_NONE) 1532 Main_ExportMAKEFLAGS(true); /* initial export */ 1533 1534 InitVpath(); 1535 1536 /* 1537 * Now that all search paths have been read for suffixes et al, it's 1538 * time to add the default search path to their lists... 1539 */ 1540 Suff_ExtendPaths(); 1541 1542 /* 1543 * Propagate attributes through :: dependency lists. 1544 */ 1545 Targ_Propagate(); 1546 1547 /* print the initial graph, if the user requested it */ 1548 if (DEBUG(GRAPH1)) 1549 Targ_PrintGraph(1); 1550 } 1551 1552 /* 1553 * Make the targets, or print variables. 1554 * Return whether any of the targets is out-of-date. 1555 */ 1556 static bool 1557 main_Run(void) 1558 { 1559 if (opts.printVars != PVM_NONE) { 1560 PrintVariables(); 1561 return false; 1562 } else 1563 return MakeTargets(); 1564 } 1565 1566 /* Clean up after making the targets. */ 1567 static void 1568 main_CleanUp(void) 1569 { 1570 #ifdef CLEANUP 1571 Lst_DoneFree(&opts.variables); 1572 Lst_DoneFree(&opts.makefiles); 1573 Lst_DoneFree(&opts.create); 1574 #endif 1575 1576 if (DEBUG(GRAPH2)) 1577 Targ_PrintGraph(2); 1578 1579 Trace_Log(MAKEEND, NULL); 1580 1581 if (enterFlagObj) 1582 printf("%s: Leaving directory `%s'\n", progname, objdir); 1583 if (opts.enterFlag) 1584 printf("%s: Leaving directory `%s'\n", progname, curdir); 1585 1586 Var_Stats(); 1587 Targ_Stats(); 1588 1589 #ifdef USE_META 1590 meta_finish(); 1591 #endif 1592 #ifdef CLEANUP 1593 Suff_End(); 1594 Targ_End(); 1595 Arch_End(); 1596 Parse_End(); 1597 Dir_End(); 1598 Job_End(); 1599 #endif 1600 Trace_End(); 1601 #ifdef CLEANUP 1602 Str_Intern_End(); 1603 #endif 1604 } 1605 1606 static int 1607 main_ExitCode(bool outOfDate) 1608 { 1609 if ((opts.strict && main_errors > 0) || parseErrors > 0) 1610 return 2; /* Not 1 so -q can distinguish error */ 1611 return outOfDate ? 1 : 0; 1612 } 1613 1614 int 1615 main(int argc, char **argv) 1616 { 1617 bool outOfDate; 1618 1619 main_Init(argc, argv); 1620 main_ReadFiles(); 1621 main_PrepareMaking(); 1622 outOfDate = main_Run(); 1623 main_CleanUp(); 1624 return main_ExitCode(outOfDate); 1625 } 1626 1627 /* 1628 * Open and parse the given makefile, with all its side effects. 1629 * Return false if the file could not be opened. 1630 */ 1631 static bool 1632 ReadMakefile(const char *fname) 1633 { 1634 int fd; 1635 char *name, *path = NULL; 1636 1637 if (strcmp(fname, "-") == 0) { 1638 Parse_File("(stdin)", -1); 1639 Var_Set(SCOPE_INTERNAL, "MAKEFILE", ""); 1640 } else { 1641 if (strncmp(fname, ".../", 4) == 0) { 1642 name = Dir_FindHereOrAbove(curdir, fname + 4); 1643 if (name != NULL) { 1644 /* Dir_FindHereOrAbove returns dirname */ 1645 path = str_concat3(name, "/", 1646 str_basename(fname)); 1647 free(name); 1648 fd = open(path, O_RDONLY); 1649 if (fd != -1) { 1650 fname = path; 1651 goto found; 1652 } 1653 } 1654 } 1655 /* if we've chdir'd, rebuild the path name */ 1656 if (strcmp(curdir, objdir) != 0 && *fname != '/') { 1657 path = str_concat3(curdir, "/", fname); 1658 fd = open(path, O_RDONLY); 1659 if (fd != -1) { 1660 fname = path; 1661 goto found; 1662 } 1663 free(path); 1664 1665 /* If curdir failed, try objdir (ala .depend) */ 1666 path = str_concat3(objdir, "/", fname); 1667 fd = open(path, O_RDONLY); 1668 if (fd != -1) { 1669 fname = path; 1670 goto found; 1671 } 1672 } else { 1673 fd = open(fname, O_RDONLY); 1674 if (fd != -1) 1675 goto found; 1676 } 1677 /* look in -I and system include directories. */ 1678 name = Dir_FindFile(fname, parseIncPath); 1679 if (name == NULL) { 1680 SearchPath *sysInc = Lst_IsEmpty(&sysIncPath->dirs) 1681 ? defSysIncPath : sysIncPath; 1682 name = Dir_FindFile(fname, sysInc); 1683 } 1684 if (name == NULL || (fd = open(name, O_RDONLY)) == -1) { 1685 free(name); 1686 free(path); 1687 return false; 1688 } 1689 fname = name; 1690 /* 1691 * set the MAKEFILE variable desired by System V fans -- the 1692 * placement of the setting here means it gets set to the last 1693 * makefile specified, as it is set by SysV make. 1694 */ 1695 found: 1696 if (!doing_depend) 1697 Var_Set(SCOPE_INTERNAL, "MAKEFILE", fname); 1698 Parse_File(fname, fd); 1699 } 1700 free(path); 1701 return true; 1702 } 1703 1704 /* populate av for Cmd_Exec and Compat_RunCommand */ 1705 void 1706 Cmd_Argv(const char *cmd, size_t cmd_len, const char *av[5], 1707 char *cmd_file, size_t cmd_filesz, bool eflag, bool xflag) 1708 { 1709 int cmd_fd = -1; 1710 1711 if (shellPath == NULL) 1712 Shell_Init(); 1713 1714 if (cmd_file != NULL) { 1715 if (cmd_len == 0) 1716 cmd_len = strlen(cmd); 1717 1718 if (cmd_len > MAKE_CMDLEN_LIMIT) { 1719 cmd_fd = mkTempFile(NULL, cmd_file, cmd_filesz); 1720 if (cmd_fd >= 0) { 1721 ssize_t n; 1722 1723 n = write(cmd_fd, cmd, cmd_len); 1724 close(cmd_fd); 1725 if (n < (ssize_t)cmd_len) { 1726 unlink(cmd_file); 1727 cmd_fd = -1; 1728 } 1729 } 1730 } else 1731 cmd_file[0] = '\0'; 1732 } 1733 1734 /* The following works for any of the builtin shell specs. */ 1735 *av++ = shellPath; 1736 if (eflag) 1737 *av++ = shellErrFlag; 1738 if (cmd_fd >= 0) { 1739 if (xflag) 1740 *av++ = "-x"; 1741 *av++ = cmd_file; 1742 } else { 1743 *av++ = xflag ? "-xc" : "-c"; 1744 *av++ = cmd; 1745 } 1746 *av = NULL; 1747 } 1748 1749 /* 1750 * Execute the command in cmd, and return its output (only stdout, not 1751 * stderr, possibly empty). In the output, replace newlines with spaces. 1752 */ 1753 char * 1754 Cmd_Exec(const char *cmd, char **error) 1755 { 1756 const char *args[5]; /* Arguments for invoking the shell */ 1757 int pipefds[2]; 1758 int cpid; /* Child PID */ 1759 int pid; /* PID from wait() */ 1760 int status; /* command exit status */ 1761 Buffer buf; /* buffer to store the result */ 1762 ssize_t bytes_read; 1763 char *output; 1764 char *p; 1765 int saved_errno; 1766 char cmd_file[MAXPATHLEN]; 1767 1768 DEBUG1(VAR, "Capturing the output of command \"%s\"\n", cmd); 1769 1770 Cmd_Argv(cmd, 0, args, cmd_file, sizeof(cmd_file), false, false); 1771 if (pipe(pipefds) == -1) { 1772 *error = str_concat3( 1773 "Couldn't create pipe for \"", cmd, "\""); 1774 return bmake_strdup(""); 1775 } 1776 1777 Var_ReexportVars(SCOPE_GLOBAL); 1778 Var_ExportStackTrace(NULL, cmd); 1779 1780 switch (cpid = FORK_FUNCTION()) { 1781 case 0: 1782 (void)close(pipefds[0]); 1783 (void)dup2(pipefds[1], STDOUT_FILENO); 1784 (void)close(pipefds[1]); 1785 1786 (void)execv(shellPath, UNCONST(args)); 1787 _exit(1); 1788 1789 case -1: 1790 *error = str_concat3("Couldn't exec \"", cmd, "\""); 1791 return bmake_strdup(""); 1792 } 1793 1794 (void)close(pipefds[1]); /* No need for the writing half */ 1795 1796 saved_errno = 0; 1797 Buf_Init(&buf); 1798 1799 do { 1800 char result[BUFSIZ]; 1801 bytes_read = read(pipefds[0], result, sizeof result); 1802 if (bytes_read > 0) 1803 Buf_AddBytes(&buf, result, (size_t)bytes_read); 1804 } while (bytes_read > 0 || (bytes_read == -1 && errno == EINTR)); 1805 if (bytes_read == -1) 1806 saved_errno = errno; 1807 1808 (void)close(pipefds[0]); /* Close the input side of the pipe. */ 1809 1810 while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0) 1811 JobReapChild(pid, status, false); 1812 1813 if (Buf_EndsWith(&buf, '\n')) 1814 buf.data[buf.len - 1] = '\0'; 1815 1816 output = Buf_DoneData(&buf); 1817 for (p = output; *p != '\0'; p++) 1818 if (*p == '\n') 1819 *p = ' '; 1820 1821 if (WIFSIGNALED(status)) 1822 *error = str_concat3("\"", cmd, "\" exited on a signal"); 1823 else if (WEXITSTATUS(status) != 0) { 1824 Buffer errBuf; 1825 Buf_Init(&errBuf); 1826 Buf_AddStr(&errBuf, "Command \""); 1827 Buf_AddStr(&errBuf, cmd); 1828 Buf_AddStr(&errBuf, "\" exited with status "); 1829 Buf_AddInt(&errBuf, WEXITSTATUS(status)); 1830 *error = Buf_DoneData(&errBuf); 1831 } else if (saved_errno != 0) 1832 *error = str_concat3( 1833 "Couldn't read shell's output for \"", cmd, "\""); 1834 else 1835 *error = NULL; 1836 if (cmd_file[0] != '\0') 1837 unlink(cmd_file); 1838 return output; 1839 } 1840 1841 /* 1842 * Print a printf-style error message. 1843 * 1844 * In default mode, this error message has no consequences, for compatibility 1845 * reasons, in particular it does not affect the exit status. Only in lint 1846 * mode (-dL) it does. 1847 */ 1848 void 1849 Error(const char *fmt, ...) 1850 { 1851 va_list ap; 1852 FILE *f; 1853 1854 f = opts.debug_file; 1855 if (f == stdout) 1856 f = stderr; 1857 (void)fflush(stdout); 1858 1859 for (;;) { 1860 fprintf(f, "%s: ", progname); 1861 va_start(ap, fmt); 1862 (void)vfprintf(f, fmt, ap); 1863 va_end(ap); 1864 (void)fprintf(f, "\n"); 1865 (void)fflush(f); 1866 if (f == stderr) 1867 break; 1868 f = stderr; 1869 } 1870 main_errors++; 1871 } 1872 1873 /* 1874 * Wait for any running jobs to finish, then produce an error message, 1875 * finally exit immediately. 1876 * 1877 * Exiting immediately differs from Parse_Error, which exits only after the 1878 * current top-level makefile has been parsed completely. 1879 */ 1880 void 1881 Fatal(const char *fmt, ...) 1882 { 1883 va_list ap; 1884 1885 if (jobsRunning) 1886 Job_Wait(); 1887 1888 (void)fflush(stdout); 1889 fprintf(stderr, "%s: ", progname); 1890 va_start(ap, fmt); 1891 (void)vfprintf(stderr, fmt, ap); 1892 va_end(ap); 1893 (void)fprintf(stderr, "\n"); 1894 (void)fflush(stderr); 1895 PrintStackTrace(stderr, true); 1896 1897 PrintOnError(NULL, "\n"); 1898 1899 if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) 1900 Targ_PrintGraph(2); 1901 Trace_Log(MAKEERROR, NULL); 1902 exit(2); /* Not 1 so -q can distinguish error */ 1903 } 1904 1905 /* 1906 * Major exception once jobs are being created. 1907 * Kills all jobs, prints a message and exits. 1908 */ 1909 void 1910 Punt(const char *fmt, ...) 1911 { 1912 va_list ap; 1913 1914 (void)fflush(stdout); 1915 (void)fprintf(stderr, "%s: ", progname); 1916 va_start(ap, fmt); 1917 (void)vfprintf(stderr, fmt, ap); 1918 va_end(ap); 1919 (void)fprintf(stderr, "\n"); 1920 (void)fflush(stderr); 1921 1922 PrintOnError(NULL, "\n"); 1923 1924 DieHorribly(); 1925 } 1926 1927 /* Exit without giving a message. */ 1928 void 1929 DieHorribly(void) 1930 { 1931 if (jobsRunning) 1932 Job_AbortAll(); 1933 if (DEBUG(GRAPH2)) 1934 Targ_PrintGraph(2); 1935 Trace_Log(MAKEERROR, NULL); 1936 exit(2); /* Not 1 so -q can distinguish error */ 1937 } 1938 1939 int 1940 unlink_file(const char *file) 1941 { 1942 struct stat st; 1943 1944 if (lstat(file, &st) == -1) 1945 return -1; 1946 1947 if (S_ISDIR(st.st_mode)) { 1948 /* 1949 * POSIX says for unlink: "The path argument shall not name 1950 * a directory unless [...]". 1951 */ 1952 errno = EISDIR; 1953 return -1; 1954 } 1955 return unlink(file); 1956 } 1957 1958 static void 1959 write_all(int fd, const void *data, size_t n) 1960 { 1961 const char *mem = data; 1962 1963 while (n > 0) { 1964 ssize_t written = write(fd, mem, n); 1965 /* XXX: Should this EAGAIN be EINTR? */ 1966 if (written == -1 && errno == EAGAIN) 1967 continue; 1968 if (written == -1) 1969 break; 1970 mem += written; 1971 n -= (size_t)written; 1972 } 1973 } 1974 1975 /* Print why exec failed, avoiding stdio. */ 1976 void MAKE_ATTR_DEAD 1977 execDie(const char *func, const char *arg) 1978 { 1979 char msg[1024]; 1980 int len; 1981 1982 len = snprintf(msg, sizeof(msg), "%s: %s(%s): %s\n", 1983 progname, func, arg, strerror(errno)); 1984 write_all(STDERR_FILENO, msg, (size_t)len); 1985 _exit(1); 1986 } 1987 1988 static void 1989 purge_relative_cached_realpaths(void) 1990 { 1991 HashIter hi; 1992 bool more; 1993 1994 HashIter_Init(&hi, &cached_realpaths); 1995 more = HashIter_Next(&hi); 1996 while (more) { 1997 HashEntry *he = hi.entry; 1998 more = HashIter_Next(&hi); 1999 if (he->key[0] != '/') { 2000 DEBUG1(DIR, "cached_realpath: purging %s\n", he->key); 2001 free(he->value); 2002 HashTable_DeleteEntry(&cached_realpaths, he); 2003 } 2004 } 2005 } 2006 2007 const char * 2008 cached_realpath(const char *pathname, char *resolved) 2009 { 2010 const char *rp; 2011 2012 if (pathname == NULL || pathname[0] == '\0') 2013 return NULL; 2014 2015 rp = HashTable_FindValue(&cached_realpaths, pathname); 2016 if (rp != NULL) { 2017 snprintf(resolved, MAXPATHLEN, "%s", rp); 2018 return resolved; 2019 } 2020 2021 rp = realpath(pathname, resolved); 2022 if (rp != NULL) { 2023 HashTable_Set(&cached_realpaths, pathname, bmake_strdup(rp)); 2024 DEBUG2(DIR, "cached_realpath: %s -> %s\n", pathname, rp); 2025 return resolved; 2026 } 2027 2028 /* should we negative-cache? */ 2029 return NULL; 2030 } 2031 2032 /* 2033 * Return true if we should die without noise. 2034 * For example our failing child was a sub-make or failure happened elsewhere. 2035 */ 2036 bool 2037 shouldDieQuietly(GNode *gn, int bf) 2038 { 2039 static int quietly = -1; 2040 2041 if (quietly < 0) { 2042 if (DEBUG(JOB) || 2043 !GetBooleanExpr("${.MAKE.DIE_QUIETLY}", true)) 2044 quietly = 0; 2045 else if (bf >= 0) 2046 quietly = bf; 2047 else 2048 quietly = gn != NULL && gn->type & OP_MAKE ? 1 : 0; 2049 } 2050 return quietly != 0; 2051 } 2052 2053 static void 2054 SetErrorVars(GNode *gn) 2055 { 2056 StringListNode *ln; 2057 char sts[16]; 2058 2059 /* 2060 * We can print this even if there is no .ERROR target. 2061 */ 2062 snprintf(sts, sizeof(sts), "%d", gn->exit_status); 2063 Global_Set(".ERROR_EXIT", sts); 2064 Global_Set(".ERROR_TARGET", gn->name); 2065 Global_Delete(".ERROR_CMD"); 2066 2067 for (ln = gn->commands.first; ln != NULL; ln = ln->next) { 2068 const char *cmd = ln->datum; 2069 2070 if (cmd == NULL) 2071 break; 2072 Global_Append(".ERROR_CMD", cmd); 2073 } 2074 } 2075 2076 /* 2077 * Print some helpful information in case of an error. 2078 * The caller should exit soon after calling this function. 2079 */ 2080 void 2081 PrintOnError(GNode *gn, const char *msg) 2082 { 2083 static GNode *errorNode = NULL; 2084 StringListNode *ln; 2085 2086 if (DEBUG(HASH)) { 2087 Targ_Stats(); 2088 Var_Stats(); 2089 } 2090 2091 if (errorNode != NULL) 2092 return; /* we've been here! */ 2093 2094 printf("%s%s: stopped", msg, progname); 2095 ln = opts.create.first; 2096 if (ln != NULL || mainNode != NULL) { 2097 printf(" making \""); 2098 if (ln != NULL) { 2099 printf("%s", (const char *)ln->datum); 2100 for (ln = ln->next; ln != NULL; ln = ln->next) 2101 printf(" %s", (const char *)ln->datum); 2102 } else 2103 printf("%s", mainNode->name); 2104 printf("\""); 2105 } 2106 printf(" in %s\n", curdir); 2107 2108 /* we generally want to keep quiet if a sub-make died */ 2109 if (shouldDieQuietly(gn, -1)) 2110 return; 2111 2112 if (gn != NULL) 2113 SetErrorVars(gn); 2114 2115 { 2116 char *errorVarsValues; 2117 enum PosixState p_s = posix_state; 2118 2119 posix_state = PS_TOO_LATE; 2120 errorVarsValues = Var_Subst( 2121 "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", 2122 SCOPE_GLOBAL, VARE_EVAL); 2123 /* TODO: handle errors */ 2124 printf("%s", errorVarsValues); 2125 free(errorVarsValues); 2126 posix_state = p_s; 2127 } 2128 2129 fflush(stdout); 2130 2131 /* 2132 * Finally, see if there is a .ERROR target, and run it if so. 2133 */ 2134 errorNode = Targ_FindNode(".ERROR"); 2135 if (errorNode != NULL) { 2136 errorNode->type |= OP_SPECIAL; 2137 Compat_Make(errorNode, errorNode); 2138 } 2139 } 2140 2141 void 2142 Main_ExportMAKEFLAGS(bool first) 2143 { 2144 static bool once = true; 2145 enum PosixState p_s; 2146 char *flags; 2147 2148 if (once != first) 2149 return; 2150 once = false; 2151 2152 p_s = posix_state; 2153 posix_state = PS_TOO_LATE; 2154 flags = Var_Subst( 2155 "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", 2156 SCOPE_CMDLINE, VARE_EVAL); 2157 /* TODO: handle errors */ 2158 if (flags[0] != '\0') 2159 setenv("MAKEFLAGS", flags, 1); 2160 free(flags); 2161 posix_state = p_s; 2162 } 2163 2164 char * 2165 getTmpdir(void) 2166 { 2167 static char *tmpdir = NULL; 2168 struct stat st; 2169 2170 if (tmpdir != NULL) 2171 return tmpdir; 2172 2173 /* Honor $TMPDIR if it is valid, strip a trailing '/'. */ 2174 tmpdir = Var_Subst("${TMPDIR:tA:U" _PATH_TMP ":S,/$,,W}/", 2175 SCOPE_GLOBAL, VARE_EVAL); 2176 /* TODO: handle errors */ 2177 2178 if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { 2179 free(tmpdir); 2180 tmpdir = bmake_strdup(_PATH_TMP); 2181 } 2182 return tmpdir; 2183 } 2184 2185 /* 2186 * Create and open a temp file using "pattern". 2187 * If tfile is provided, set it to a copy of the filename created. 2188 * Otherwise unlink the file once open. 2189 */ 2190 int 2191 mkTempFile(const char *pattern, char *tfile, size_t tfile_sz) 2192 { 2193 static char *tmpdir = NULL; 2194 char tbuf[MAXPATHLEN]; 2195 int fd; 2196 2197 if (pattern == NULL) 2198 pattern = "makeXXXXXX"; 2199 if (tmpdir == NULL) 2200 tmpdir = getTmpdir(); 2201 if (tfile == NULL) { 2202 tfile = tbuf; 2203 tfile_sz = sizeof tbuf; 2204 } 2205 2206 if (pattern[0] == '/') 2207 snprintf(tfile, tfile_sz, "%s", pattern); 2208 else 2209 snprintf(tfile, tfile_sz, "%s%s", tmpdir, pattern); 2210 2211 if ((fd = mkstemp(tfile)) < 0) 2212 Punt("mkstemp %s: %s", tfile, strerror(errno)); 2213 if (tfile == tbuf) 2214 unlink(tfile); /* we just want the descriptor */ 2215 2216 return fd; 2217 } 2218 2219 /* 2220 * Convert a string representation of a boolean into a boolean value. 2221 * Anything that looks like "No", "False", "Off", "0" etc. is false, 2222 * the empty string is the fallback, everything else is true. 2223 */ 2224 bool 2225 ParseBoolean(const char *s, bool fallback) 2226 { 2227 char ch = ch_tolower(s[0]); 2228 if (ch == '\0') 2229 return fallback; 2230 if (ch == '0' || ch == 'f' || ch == 'n') 2231 return false; 2232 if (ch == 'o') 2233 return ch_tolower(s[1]) != 'f'; 2234 return true; 2235 } 2236