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