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