1 /* $NetBSD: meta.c,v 1.144 2020/11/15 12:02:44 rillig Exp $ */ 2 3 /* 4 * Implement 'meta' mode. 5 * Adapted from John Birrell's patches to FreeBSD make. 6 * --sjg 7 */ 8 /* 9 * Copyright (c) 2009-2016, Juniper Networks, Inc. 10 * Portions Copyright (c) 2009, John Birrell. 11 * 12 * Redistribution and use in source and binary forms, with or without 13 * modification, are permitted provided that the following conditions 14 * are met: 15 * 1. Redistributions of source code must retain the above copyright 16 * notice, this list of conditions and the following disclaimer. 17 * 2. Redistributions in binary form must reproduce the above copyright 18 * notice, this list of conditions and the following disclaimer in the 19 * documentation and/or other materials provided with the distribution. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 #if defined(USE_META) 34 35 #ifdef HAVE_CONFIG_H 36 # include "config.h" 37 #endif 38 #include <sys/stat.h> 39 #ifdef HAVE_LIBGEN_H 40 #include <libgen.h> 41 #elif !defined(HAVE_DIRNAME) 42 char * dirname(char *); 43 #endif 44 #include <errno.h> 45 #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) 46 #include <err.h> 47 #endif 48 49 #include "make.h" 50 #include "dir.h" 51 #include "job.h" 52 53 #ifdef USE_FILEMON 54 #include "filemon/filemon.h" 55 #endif 56 57 static BuildMon Mybm; /* for compat */ 58 static StringList *metaBailiwick; /* our scope of control */ 59 static char *metaBailiwickStr; /* string storage for the list */ 60 static StringList *metaIgnorePaths; /* paths we deliberately ignore */ 61 static char *metaIgnorePathsStr; /* string storage for the list */ 62 63 #ifndef MAKE_META_IGNORE_PATHS 64 #define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS" 65 #endif 66 #ifndef MAKE_META_IGNORE_PATTERNS 67 #define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS" 68 #endif 69 #ifndef MAKE_META_IGNORE_FILTER 70 #define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER" 71 #endif 72 73 Boolean useMeta = FALSE; 74 static Boolean useFilemon = FALSE; 75 static Boolean writeMeta = FALSE; 76 static Boolean metaMissing = FALSE; /* oodate if missing */ 77 static Boolean filemonMissing = FALSE; /* oodate if missing */ 78 static Boolean metaEnv = FALSE; /* don't save env unless asked */ 79 static Boolean metaVerbose = FALSE; 80 static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ 81 static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */ 82 static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */ 83 static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ 84 static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */ 85 86 extern Boolean forceJobs; 87 extern char **environ; 88 89 #define MAKE_META_PREFIX ".MAKE.META.PREFIX" 90 91 #ifndef N2U 92 # define N2U(n, u) (((n) + ((u) - 1)) / (u)) 93 #endif 94 #ifndef ROUNDUP 95 # define ROUNDUP(n, u) (N2U((n), (u)) * (u)) 96 #endif 97 98 #if !defined(HAVE_STRSEP) 99 # define strsep(s, d) stresep((s), (d), '\0') 100 #endif 101 102 /* 103 * Filemon is a kernel module which snoops certain syscalls. 104 * 105 * C chdir 106 * E exec 107 * F [v]fork 108 * L [sym]link 109 * M rename 110 * R read 111 * W write 112 * S stat 113 * 114 * See meta_oodate below - we mainly care about 'E' and 'R'. 115 * 116 * We can still use meta mode without filemon, but 117 * the benefits are more limited. 118 */ 119 #ifdef USE_FILEMON 120 121 /* 122 * Open the filemon device. 123 */ 124 static void 125 meta_open_filemon(BuildMon *pbm) 126 { 127 int dupfd; 128 129 pbm->mon_fd = -1; 130 pbm->filemon = NULL; 131 if (!useFilemon || !pbm->mfp) 132 return; 133 134 pbm->filemon = filemon_open(); 135 if (pbm->filemon == NULL) { 136 useFilemon = FALSE; 137 warn("Could not open filemon %s", filemon_path()); 138 return; 139 } 140 141 /* 142 * We use a file outside of '.' 143 * to avoid a FreeBSD kernel bug where unlink invalidates 144 * cwd causing getcwd to do a lot more work. 145 * We only care about the descriptor. 146 */ 147 pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); 148 if ((dupfd = dup(pbm->mon_fd)) == -1) { 149 err(1, "Could not dup filemon output!"); 150 } 151 (void)fcntl(dupfd, F_SETFD, FD_CLOEXEC); 152 if (filemon_setfd(pbm->filemon, dupfd) == -1) { 153 err(1, "Could not set filemon file descriptor!"); 154 } 155 /* we don't need these once we exec */ 156 (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC); 157 } 158 159 /* 160 * Read the build monitor output file and write records to the target's 161 * metadata file. 162 */ 163 static int 164 filemon_read(FILE *mfp, int fd) 165 { 166 char buf[BUFSIZ]; 167 int error; 168 169 /* Check if we're not writing to a meta data file.*/ 170 if (mfp == NULL) { 171 if (fd >= 0) 172 close(fd); /* not interested */ 173 return 0; 174 } 175 /* rewind */ 176 if (lseek(fd, (off_t)0, SEEK_SET) < 0) { 177 error = errno; 178 warn("Could not rewind filemon"); 179 fprintf(mfp, "\n"); 180 } else { 181 ssize_t n; 182 183 error = 0; 184 fprintf(mfp, "\n-- filemon acquired metadata --\n"); 185 186 while ((n = read(fd, buf, sizeof buf)) > 0) { 187 if ((ssize_t)fwrite(buf, 1, (size_t)n, mfp) < n) 188 error = EIO; 189 } 190 } 191 fflush(mfp); 192 if (close(fd) < 0) 193 error = errno; 194 return error; 195 } 196 #endif 197 198 /* 199 * when realpath() fails, 200 * we use this, to clean up ./ and ../ 201 */ 202 static void 203 eat_dots(char *buf, size_t bufsz, int dots) 204 { 205 char *cp; 206 char *cp2; 207 const char *eat; 208 size_t eatlen; 209 210 switch (dots) { 211 case 1: 212 eat = "/./"; 213 eatlen = 2; 214 break; 215 case 2: 216 eat = "/../"; 217 eatlen = 3; 218 break; 219 default: 220 return; 221 } 222 223 do { 224 cp = strstr(buf, eat); 225 if (cp) { 226 cp2 = cp + eatlen; 227 if (dots == 2 && cp > buf) { 228 do { 229 cp--; 230 } while (cp > buf && *cp != '/'); 231 } 232 if (*cp == '/') { 233 strlcpy(cp, cp2, bufsz - (size_t)(cp - buf)); 234 } else { 235 return; /* can't happen? */ 236 } 237 } 238 } while (cp); 239 } 240 241 static char * 242 meta_name(char *mname, size_t mnamelen, 243 const char *dname, 244 const char *tname, 245 const char *cwd) 246 { 247 char buf[MAXPATHLEN]; 248 char *rp; 249 char *cp; 250 char *tp; 251 char *dtp; 252 size_t ldname; 253 254 /* 255 * Weed out relative paths from the target file name. 256 * We have to be careful though since if target is a 257 * symlink, the result will be unstable. 258 * So we use realpath() just to get the dirname, and leave the 259 * basename as given to us. 260 */ 261 if ((cp = strrchr(tname, '/'))) { 262 if (cached_realpath(tname, buf)) { 263 if ((rp = strrchr(buf, '/'))) { 264 rp++; 265 cp++; 266 if (strcmp(cp, rp) != 0) 267 strlcpy(rp, cp, sizeof buf - (size_t)(rp - buf)); 268 } 269 tname = buf; 270 } else { 271 /* 272 * We likely have a directory which is about to be made. 273 * We pretend realpath() succeeded, to have a chance 274 * of generating the same meta file name that we will 275 * next time through. 276 */ 277 if (tname[0] == '/') { 278 strlcpy(buf, tname, sizeof buf); 279 } else { 280 snprintf(buf, sizeof buf, "%s/%s", cwd, tname); 281 } 282 eat_dots(buf, sizeof buf, 1); /* ./ */ 283 eat_dots(buf, sizeof buf, 2); /* ../ */ 284 tname = buf; 285 } 286 } 287 /* on some systems dirname may modify its arg */ 288 tp = bmake_strdup(tname); 289 dtp = dirname(tp); 290 if (strcmp(dname, dtp) == 0) 291 snprintf(mname, mnamelen, "%s.meta", tname); 292 else { 293 ldname = strlen(dname); 294 if (strncmp(dname, dtp, ldname) == 0 && dtp[ldname] == '/') 295 snprintf(mname, mnamelen, "%s/%s.meta", dname, &tname[ldname+1]); 296 else 297 snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); 298 299 /* 300 * Replace path separators in the file name after the 301 * current object directory path. 302 */ 303 cp = mname + strlen(dname) + 1; 304 305 while (*cp != '\0') { 306 if (*cp == '/') 307 *cp = '_'; 308 cp++; 309 } 310 } 311 free(tp); 312 return mname; 313 } 314 315 /* 316 * Return true if running ${.MAKE} 317 * Bypassed if target is flagged .MAKE 318 */ 319 static int 320 is_submake(void *cmdp, void *gnp) 321 { 322 static const char *p_make = NULL; 323 static size_t p_len; 324 char *cmd = cmdp; 325 GNode *gn = gnp; 326 char *mp = NULL; 327 char *cp; 328 char *cp2; 329 int rc = 0; /* keep looking */ 330 331 if (p_make == NULL) { 332 void *dontFreeIt; 333 p_make = Var_Value(".MAKE", gn, &dontFreeIt); 334 p_len = strlen(p_make); 335 } 336 cp = strchr(cmd, '$'); 337 if ((cp)) { 338 (void)Var_Subst(cmd, gn, VARE_WANTRES, &mp); 339 /* TODO: handle errors */ 340 cmd = mp; 341 } 342 cp2 = strstr(cmd, p_make); 343 if (cp2 != NULL) { 344 switch (cp2[p_len]) { 345 case '\0': 346 case ' ': 347 case '\t': 348 case '\n': 349 rc = 1; 350 break; 351 } 352 if (cp2 > cmd && rc > 0) { 353 switch (cp2[-1]) { 354 case ' ': 355 case '\t': 356 case '\n': 357 break; 358 default: 359 rc = 0; /* no match */ 360 break; 361 } 362 } 363 } 364 free(mp); 365 return rc; 366 } 367 368 typedef struct meta_file_s { 369 FILE *fp; 370 GNode *gn; 371 } meta_file_t; 372 373 static void 374 printCMD(const char *cmd, meta_file_t *mfp) 375 { 376 char *cmd_freeIt = NULL; 377 378 if (strchr(cmd, '$')) { 379 (void)Var_Subst(cmd, mfp->gn, VARE_WANTRES, &cmd_freeIt); 380 /* TODO: handle errors */ 381 cmd = cmd_freeIt; 382 } 383 fprintf(mfp->fp, "CMD %s\n", cmd); 384 free(cmd_freeIt); 385 } 386 387 static void 388 printCMDs(GNode *gn, meta_file_t *mf) 389 { 390 GNodeListNode *ln; 391 392 for (ln = gn->commands->first; ln != NULL; ln = ln->next) 393 printCMD(ln->datum, mf); 394 } 395 396 /* 397 * Certain node types never get a .meta file 398 */ 399 #define SKIP_META_TYPE(_type) do { \ 400 if ((gn->type & __CONCAT(OP_, _type))) { \ 401 if (verbose) { \ 402 debug_printf("Skipping meta for %s: .%s\n", \ 403 gn->name, __STRING(_type)); \ 404 } \ 405 return FALSE; \ 406 } \ 407 } while (0) 408 409 410 /* 411 * Do we need/want a .meta file ? 412 */ 413 static Boolean 414 meta_needed(GNode *gn, const char *dname, 415 char *objdir, int verbose) 416 { 417 struct cached_stat cst; 418 419 if (verbose) 420 verbose = DEBUG(META); 421 422 /* This may be a phony node which we don't want meta data for... */ 423 /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ 424 /* Or it may be explicitly flagged as .NOMETA */ 425 SKIP_META_TYPE(NOMETA); 426 /* Unless it is explicitly flagged as .META */ 427 if (!(gn->type & OP_META)) { 428 SKIP_META_TYPE(PHONY); 429 SKIP_META_TYPE(SPECIAL); 430 SKIP_META_TYPE(MAKE); 431 } 432 433 /* Check if there are no commands to execute. */ 434 if (Lst_IsEmpty(gn->commands)) { 435 if (verbose) 436 debug_printf("Skipping meta for %s: no commands\n", gn->name); 437 return FALSE; 438 } 439 if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) { 440 /* OP_SUBMAKE is a bit too aggressive */ 441 if (Lst_ForEachUntil(gn->commands, is_submake, gn)) { 442 DEBUG1(META, "Skipping meta for %s: .SUBMAKE\n", gn->name); 443 return FALSE; 444 } 445 } 446 447 /* The object directory may not exist. Check it.. */ 448 if (cached_stat(dname, &cst) != 0) { 449 if (verbose) 450 debug_printf("Skipping meta for %s: no .OBJDIR\n", gn->name); 451 return FALSE; 452 } 453 454 /* make sure these are canonical */ 455 if (cached_realpath(dname, objdir)) 456 dname = objdir; 457 458 /* If we aren't in the object directory, don't create a meta file. */ 459 if (!metaCurdirOk && strcmp(curdir, dname) == 0) { 460 if (verbose) 461 debug_printf("Skipping meta for %s: .OBJDIR == .CURDIR\n", 462 gn->name); 463 return FALSE; 464 } 465 return TRUE; 466 } 467 468 469 static FILE * 470 meta_create(BuildMon *pbm, GNode *gn) 471 { 472 meta_file_t mf; 473 char buf[MAXPATHLEN]; 474 char objdir[MAXPATHLEN]; 475 char **ptr; 476 const char *dname; 477 const char *tname; 478 char *fname; 479 const char *cp; 480 void *objdir_freeIt; 481 482 mf.fp = NULL; 483 484 dname = Var_Value(".OBJDIR", gn, &objdir_freeIt); 485 tname = GNode_VarTarget(gn); 486 487 /* if this succeeds objdir is realpath of dname */ 488 if (!meta_needed(gn, dname, objdir, TRUE)) 489 goto out; 490 dname = objdir; 491 492 if (metaVerbose) { 493 char *mp; 494 495 /* Describe the target we are building */ 496 (void)Var_Subst("${" MAKE_META_PREFIX "}", gn, VARE_WANTRES, &mp); 497 /* TODO: handle errors */ 498 if (*mp) 499 fprintf(stdout, "%s\n", mp); 500 free(mp); 501 } 502 /* Get the basename of the target */ 503 if ((cp = strrchr(tname, '/')) == NULL) { 504 cp = tname; 505 } else { 506 cp++; 507 } 508 509 fflush(stdout); 510 511 if (!writeMeta) 512 /* Don't create meta data. */ 513 goto out; 514 515 fname = meta_name(pbm->meta_fname, sizeof pbm->meta_fname, 516 dname, tname, objdir); 517 518 #ifdef DEBUG_META_MODE 519 DEBUG1(META, "meta_create: %s\n", fname); 520 #endif 521 522 if ((mf.fp = fopen(fname, "w")) == NULL) 523 err(1, "Could not open meta file '%s'", fname); 524 525 fprintf(mf.fp, "# Meta data file %s\n", fname); 526 527 mf.gn = gn; 528 529 printCMDs(gn, &mf); 530 531 fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof buf)); 532 fprintf(mf.fp, "TARGET %s\n", tname); 533 cp = GNode_VarOodate(gn); 534 if (cp && *cp) { 535 fprintf(mf.fp, "OODATE %s\n", cp); 536 } 537 if (metaEnv) { 538 for (ptr = environ; *ptr != NULL; ptr++) 539 fprintf(mf.fp, "ENV %s\n", *ptr); 540 } 541 542 fprintf(mf.fp, "-- command output --\n"); 543 fflush(mf.fp); 544 545 Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 546 Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); 547 548 gn->type |= OP_META; /* in case anyone wants to know */ 549 if (metaSilent) { 550 gn->type |= OP_SILENT; 551 } 552 out: 553 bmake_free(objdir_freeIt); 554 555 return mf.fp; 556 } 557 558 static Boolean 559 boolValue(char *s) 560 { 561 switch(*s) { 562 case '0': 563 case 'N': 564 case 'n': 565 case 'F': 566 case 'f': 567 return FALSE; 568 } 569 return TRUE; 570 } 571 572 /* 573 * Initialization we need before reading makefiles. 574 */ 575 void 576 meta_init(void) 577 { 578 #ifdef USE_FILEMON 579 /* this allows makefiles to test if we have filemon support */ 580 Var_Set(".MAKE.PATH_FILEMON", filemon_path(), VAR_GLOBAL); 581 #endif 582 } 583 584 585 #define get_mode_bf(bf, token) \ 586 if ((cp = strstr(make_mode, token))) \ 587 bf = boolValue(cp + sizeof (token) - 1) 588 589 /* 590 * Initialization we need after reading makefiles. 591 */ 592 void 593 meta_mode_init(const char *make_mode) 594 { 595 static int once = 0; 596 char *cp; 597 void *freeIt; 598 599 useMeta = TRUE; 600 useFilemon = TRUE; 601 writeMeta = TRUE; 602 603 if (make_mode) { 604 if (strstr(make_mode, "env")) 605 metaEnv = TRUE; 606 if (strstr(make_mode, "verb")) 607 metaVerbose = TRUE; 608 if (strstr(make_mode, "read")) 609 writeMeta = FALSE; 610 if (strstr(make_mode, "nofilemon")) 611 useFilemon = FALSE; 612 if (strstr(make_mode, "ignore-cmd")) 613 metaIgnoreCMDs = TRUE; 614 if (useFilemon) 615 get_mode_bf(filemonMissing, "missing-filemon="); 616 get_mode_bf(metaCurdirOk, "curdirok="); 617 get_mode_bf(metaMissing, "missing-meta="); 618 get_mode_bf(metaSilent, "silent="); 619 } 620 if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { 621 /* 622 * The default value for MAKE_META_PREFIX 623 * prints the absolute path of the target. 624 * This works be cause :H will generate '.' if there is no / 625 * and :tA will resolve that to cwd. 626 */ 627 Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL); 628 } 629 if (once) 630 return; 631 once = 1; 632 memset(&Mybm, 0, sizeof Mybm); 633 /* 634 * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} 635 */ 636 metaBailiwick = Lst_New(); 637 (void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}", 638 VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr); 639 /* TODO: handle errors */ 640 str2Lst_Append(metaBailiwick, metaBailiwickStr); 641 /* 642 * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} 643 */ 644 metaIgnorePaths = Lst_New(); 645 Var_Append(MAKE_META_IGNORE_PATHS, 646 "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); 647 (void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}", 648 VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr); 649 /* TODO: handle errors */ 650 str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr); 651 652 /* 653 * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS} 654 */ 655 freeIt = NULL; 656 if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &freeIt)) { 657 metaIgnorePatterns = TRUE; 658 bmake_free(freeIt); 659 } 660 freeIt = NULL; 661 if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &freeIt)) { 662 metaIgnoreFilter = TRUE; 663 bmake_free(freeIt); 664 } 665 } 666 667 /* 668 * In each case below we allow for job==NULL 669 */ 670 void 671 meta_job_start(Job *job, GNode *gn) 672 { 673 BuildMon *pbm; 674 675 if (job != NULL) { 676 pbm = &job->bm; 677 } else { 678 pbm = &Mybm; 679 } 680 pbm->mfp = meta_create(pbm, gn); 681 #ifdef USE_FILEMON_ONCE 682 /* compat mode we open the filemon dev once per command */ 683 if (job == NULL) 684 return; 685 #endif 686 #ifdef USE_FILEMON 687 if (pbm->mfp != NULL && useFilemon) { 688 meta_open_filemon(pbm); 689 } else { 690 pbm->mon_fd = -1; 691 pbm->filemon = NULL; 692 } 693 #endif 694 } 695 696 /* 697 * The child calls this before doing anything. 698 * It does not disturb our state. 699 */ 700 void 701 meta_job_child(Job *job) 702 { 703 #ifdef USE_FILEMON 704 BuildMon *pbm; 705 706 if (job != NULL) { 707 pbm = &job->bm; 708 } else { 709 pbm = &Mybm; 710 } 711 if (pbm->mfp != NULL) { 712 close(fileno(pbm->mfp)); 713 if (useFilemon && pbm->filemon) { 714 pid_t pid; 715 716 pid = getpid(); 717 if (filemon_setpid_child(pbm->filemon, pid) == -1) { 718 err(1, "Could not set filemon pid!"); 719 } 720 } 721 } 722 #endif 723 } 724 725 void 726 meta_job_parent(Job *job, pid_t pid) 727 { 728 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) 729 BuildMon *pbm; 730 731 if (job != NULL) { 732 pbm = &job->bm; 733 } else { 734 pbm = &Mybm; 735 } 736 if (useFilemon && pbm->filemon) { 737 filemon_setpid_parent(pbm->filemon, pid); 738 } 739 #endif 740 } 741 742 int 743 meta_job_fd(Job *job) 744 { 745 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) 746 BuildMon *pbm; 747 748 if (job != NULL) { 749 pbm = &job->bm; 750 } else { 751 pbm = &Mybm; 752 } 753 if (useFilemon && pbm->filemon) { 754 return filemon_readfd(pbm->filemon); 755 } 756 #endif 757 return -1; 758 } 759 760 int 761 meta_job_event(Job *job) 762 { 763 #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) 764 BuildMon *pbm; 765 766 if (job != NULL) { 767 pbm = &job->bm; 768 } else { 769 pbm = &Mybm; 770 } 771 if (useFilemon && pbm->filemon) { 772 return filemon_process(pbm->filemon); 773 } 774 #endif 775 return 0; 776 } 777 778 void 779 meta_job_error(Job *job, GNode *gn, int flags, int status) 780 { 781 char cwd[MAXPATHLEN]; 782 BuildMon *pbm; 783 784 if (job != NULL) { 785 pbm = &job->bm; 786 if (gn == NULL) 787 gn = job->node; 788 } else { 789 pbm = &Mybm; 790 } 791 if (pbm->mfp != NULL) { 792 fprintf(pbm->mfp, "\n*** Error code %d%s\n", 793 status, 794 (flags & JOB_IGNERR) ? 795 "(ignored)" : ""); 796 } 797 if (gn) { 798 Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL); 799 } 800 getcwd(cwd, sizeof cwd); 801 Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL); 802 if (pbm->meta_fname[0] != '\0') { 803 Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL); 804 } 805 meta_job_finish(job); 806 } 807 808 void 809 meta_job_output(Job *job, char *cp, const char *nl) 810 { 811 BuildMon *pbm; 812 813 if (job != NULL) { 814 pbm = &job->bm; 815 } else { 816 pbm = &Mybm; 817 } 818 if (pbm->mfp != NULL) { 819 if (metaVerbose) { 820 static char *meta_prefix = NULL; 821 static size_t meta_prefix_len; 822 823 if (meta_prefix == NULL) { 824 char *cp2; 825 826 (void)Var_Subst("${" MAKE_META_PREFIX "}", 827 VAR_GLOBAL, VARE_WANTRES, &meta_prefix); 828 /* TODO: handle errors */ 829 if ((cp2 = strchr(meta_prefix, '$'))) 830 meta_prefix_len = (size_t)(cp2 - meta_prefix); 831 else 832 meta_prefix_len = strlen(meta_prefix); 833 } 834 if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { 835 cp = strchr(cp+1, '\n'); 836 if (!cp++) 837 return; 838 } 839 } 840 fprintf(pbm->mfp, "%s%s", cp, nl); 841 } 842 } 843 844 int 845 meta_cmd_finish(void *pbmp) 846 { 847 int error = 0; 848 BuildMon *pbm = pbmp; 849 #ifdef USE_FILEMON 850 int x; 851 #endif 852 853 if (pbm == NULL) 854 pbm = &Mybm; 855 856 #ifdef USE_FILEMON 857 if (pbm->filemon) { 858 while (filemon_process(pbm->filemon) > 0) 859 continue; 860 if (filemon_close(pbm->filemon) == -1) 861 error = errno; 862 x = filemon_read(pbm->mfp, pbm->mon_fd); 863 if (error == 0 && x != 0) 864 error = x; 865 pbm->mon_fd = -1; 866 pbm->filemon = NULL; 867 return error; 868 } 869 #endif 870 871 fprintf(pbm->mfp, "\n"); /* ensure end with newline */ 872 return error; 873 } 874 875 int 876 meta_job_finish(Job *job) 877 { 878 BuildMon *pbm; 879 int error = 0; 880 int x; 881 882 if (job != NULL) { 883 pbm = &job->bm; 884 } else { 885 pbm = &Mybm; 886 } 887 if (pbm->mfp != NULL) { 888 error = meta_cmd_finish(pbm); 889 x = fclose(pbm->mfp); 890 if (error == 0 && x != 0) 891 error = errno; 892 pbm->mfp = NULL; 893 pbm->meta_fname[0] = '\0'; 894 } 895 return error; 896 } 897 898 void 899 meta_finish(void) 900 { 901 if (metaBailiwick != NULL) 902 Lst_Free(metaBailiwick); 903 free(metaBailiwickStr); 904 if (metaIgnorePaths != NULL) 905 Lst_Free(metaIgnorePaths); 906 free(metaIgnorePathsStr); 907 } 908 909 /* 910 * Fetch a full line from fp - growing bufp if needed 911 * Return length in bufp. 912 */ 913 static int 914 fgetLine(char **bufp, size_t *szp, int o, FILE *fp) 915 { 916 char *buf = *bufp; 917 size_t bufsz = *szp; 918 struct stat fs; 919 int x; 920 921 if (fgets(&buf[o], (int)bufsz - o, fp) != NULL) { 922 check_newline: 923 x = o + (int)strlen(&buf[o]); 924 if (buf[x - 1] == '\n') 925 return x; 926 /* 927 * We need to grow the buffer. 928 * The meta file can give us a clue. 929 */ 930 if (fstat(fileno(fp), &fs) == 0) { 931 size_t newsz; 932 char *p; 933 934 newsz = ROUNDUP(((size_t)fs.st_size / 2), BUFSIZ); 935 if (newsz <= bufsz) 936 newsz = ROUNDUP((size_t)fs.st_size, BUFSIZ); 937 if (newsz <= bufsz) 938 return x; /* truncated */ 939 DEBUG2(META, "growing buffer %zu -> %zu\n", bufsz, newsz); 940 p = bmake_realloc(buf, newsz); 941 if (p) { 942 *bufp = buf = p; 943 *szp = bufsz = newsz; 944 /* fetch the rest */ 945 if (fgets(&buf[x], (int)bufsz - x, fp) == NULL) 946 return x; /* truncated! */ 947 goto check_newline; 948 } 949 } 950 } 951 return 0; 952 } 953 954 /* Lst_ForEachUntil wants 1 to stop search */ 955 static int 956 prefix_match(void *p, void *q) 957 { 958 const char *prefix = p; 959 const char *path = q; 960 size_t n = strlen(prefix); 961 962 return strncmp(path, prefix, n) == 0; 963 } 964 965 /* See if the path equals prefix or starts with "prefix/". */ 966 static Boolean 967 path_starts_with(const char *path, const char *prefix) 968 { 969 size_t n = strlen(prefix); 970 971 if (strncmp(path, prefix, n) != 0) 972 return FALSE; 973 return path[n] == '\0' || path[n] == '/'; 974 } 975 976 static int 977 meta_ignore(GNode *gn, const char *p) 978 { 979 char fname[MAXPATHLEN]; 980 981 if (p == NULL) 982 return TRUE; 983 984 if (*p == '/') { 985 cached_realpath(p, fname); /* clean it up */ 986 if (Lst_ForEachUntil(metaIgnorePaths, prefix_match, fname)) { 987 #ifdef DEBUG_META_MODE 988 DEBUG1(META, "meta_oodate: ignoring path: %s\n", p); 989 #endif 990 return TRUE; 991 } 992 } 993 994 if (metaIgnorePatterns) { 995 const char *expr; 996 char *pm; 997 998 Var_Set(".p.", p, gn); 999 expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}"; 1000 (void)Var_Subst(expr, gn, VARE_WANTRES, &pm); 1001 /* TODO: handle errors */ 1002 if (*pm) { 1003 #ifdef DEBUG_META_MODE 1004 DEBUG1(META, "meta_oodate: ignoring pattern: %s\n", p); 1005 #endif 1006 free(pm); 1007 return TRUE; 1008 } 1009 free(pm); 1010 } 1011 1012 if (metaIgnoreFilter) { 1013 char *fm; 1014 1015 /* skip if filter result is empty */ 1016 snprintf(fname, sizeof fname, 1017 "${%s:L:${%s:ts:}}", 1018 p, MAKE_META_IGNORE_FILTER); 1019 (void)Var_Subst(fname, gn, VARE_WANTRES, &fm); 1020 /* TODO: handle errors */ 1021 if (*fm == '\0') { 1022 #ifdef DEBUG_META_MODE 1023 DEBUG1(META, "meta_oodate: ignoring filtered: %s\n", p); 1024 #endif 1025 free(fm); 1026 return TRUE; 1027 } 1028 free(fm); 1029 } 1030 return FALSE; 1031 } 1032 1033 /* 1034 * When running with 'meta' functionality, a target can be out-of-date 1035 * if any of the references in its meta data file is more recent. 1036 * We have to track the latestdir on a per-process basis. 1037 */ 1038 #define LCWD_VNAME_FMT ".meta.%d.lcwd" 1039 #define LDIR_VNAME_FMT ".meta.%d.ldir" 1040 1041 /* 1042 * It is possible that a .meta file is corrupted, 1043 * if we detect this we want to reproduce it. 1044 * Setting oodate TRUE will have that effect. 1045 */ 1046 #define CHECK_VALID_META(p) if (!(p && *p)) { \ 1047 warnx("%s: %d: malformed", fname, lineno); \ 1048 oodate = TRUE; \ 1049 continue; \ 1050 } 1051 1052 #define DEQUOTE(p) if (*p == '\'') { \ 1053 char *ep; \ 1054 p++; \ 1055 if ((ep = strchr(p, '\''))) \ 1056 *ep = '\0'; \ 1057 } 1058 1059 static void 1060 append_if_new(StringList *list, const char *str) 1061 { 1062 StringListNode *ln; 1063 1064 for (ln = list->first; ln != NULL; ln = ln->next) 1065 if (strcmp(ln->datum, str) == 0) 1066 return; 1067 Lst_Append(list, bmake_strdup(str)); 1068 } 1069 1070 Boolean 1071 meta_oodate(GNode *gn, Boolean oodate) 1072 { 1073 static char *tmpdir = NULL; 1074 static char cwd[MAXPATHLEN]; 1075 char lcwd_vname[64]; 1076 char ldir_vname[64]; 1077 char lcwd[MAXPATHLEN]; 1078 char latestdir[MAXPATHLEN]; 1079 char fname[MAXPATHLEN]; 1080 char fname1[MAXPATHLEN]; 1081 char fname2[MAXPATHLEN]; 1082 char fname3[MAXPATHLEN]; 1083 const char *dname; 1084 const char *tname; 1085 char *p; 1086 char *cp; 1087 char *link_src; 1088 char *move_target; 1089 static size_t cwdlen = 0; 1090 static size_t tmplen = 0; 1091 FILE *fp; 1092 Boolean needOODATE = FALSE; 1093 StringList *missingFiles; 1094 Boolean have_filemon = FALSE; 1095 void *objdir_freeIt; 1096 1097 if (oodate) 1098 return oodate; /* we're done */ 1099 1100 dname = Var_Value(".OBJDIR", gn, &objdir_freeIt); 1101 tname = GNode_VarTarget(gn); 1102 1103 /* if this succeeds fname3 is realpath of dname */ 1104 if (!meta_needed(gn, dname, fname3, FALSE)) 1105 goto oodate_out; 1106 dname = fname3; 1107 1108 missingFiles = Lst_New(); 1109 1110 /* 1111 * We need to check if the target is out-of-date. This includes 1112 * checking if the expanded command has changed. This in turn 1113 * requires that all variables are set in the same way that they 1114 * would be if the target needs to be re-built. 1115 */ 1116 Make_DoAllVar(gn); 1117 1118 meta_name(fname, sizeof fname, dname, tname, dname); 1119 1120 #ifdef DEBUG_META_MODE 1121 DEBUG1(META, "meta_oodate: %s\n", fname); 1122 #endif 1123 1124 if ((fp = fopen(fname, "r")) != NULL) { 1125 static char *buf = NULL; 1126 static size_t bufsz; 1127 int lineno = 0; 1128 int lastpid = 0; 1129 int pid; 1130 int x; 1131 StringListNode *cmdNode; 1132 struct cached_stat cst; 1133 1134 if (buf == NULL) { 1135 bufsz = 8 * BUFSIZ; 1136 buf = bmake_malloc(bufsz); 1137 } 1138 1139 if (cwdlen == 0) { 1140 if (getcwd(cwd, sizeof cwd) == NULL) 1141 err(1, "Could not get current working directory"); 1142 cwdlen = strlen(cwd); 1143 } 1144 strlcpy(lcwd, cwd, sizeof lcwd); 1145 strlcpy(latestdir, cwd, sizeof latestdir); 1146 1147 if (tmpdir == NULL) { 1148 tmpdir = getTmpdir(); 1149 tmplen = strlen(tmpdir); 1150 } 1151 1152 /* we want to track all the .meta we read */ 1153 Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 1154 1155 cmdNode = gn->commands->first; 1156 while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { 1157 lineno++; 1158 if (buf[x - 1] == '\n') 1159 buf[x - 1] = '\0'; 1160 else { 1161 warnx("%s: %d: line truncated at %u", fname, lineno, x); 1162 oodate = TRUE; 1163 break; 1164 } 1165 link_src = NULL; 1166 move_target = NULL; 1167 /* Find the start of the build monitor section. */ 1168 if (!have_filemon) { 1169 if (strncmp(buf, "-- filemon", 10) == 0) { 1170 have_filemon = TRUE; 1171 continue; 1172 } 1173 if (strncmp(buf, "# buildmon", 10) == 0) { 1174 have_filemon = TRUE; 1175 continue; 1176 } 1177 } 1178 1179 /* Delimit the record type. */ 1180 p = buf; 1181 #ifdef DEBUG_META_MODE 1182 DEBUG3(META, "%s: %d: %s\n", fname, lineno, buf); 1183 #endif 1184 strsep(&p, " "); 1185 if (have_filemon) { 1186 /* 1187 * We are in the 'filemon' output section. 1188 * Each record from filemon follows the general form: 1189 * 1190 * <key> <pid> <data> 1191 * 1192 * Where: 1193 * <key> is a single letter, denoting the syscall. 1194 * <pid> is the process that made the syscall. 1195 * <data> is the arguments (of interest). 1196 */ 1197 switch(buf[0]) { 1198 case '#': /* comment */ 1199 case 'V': /* version */ 1200 break; 1201 default: 1202 /* 1203 * We need to track pathnames per-process. 1204 * 1205 * Each process run by make, starts off in the 'CWD' 1206 * recorded in the .meta file, if it chdirs ('C') 1207 * elsewhere we need to track that - but only for 1208 * that process. If it forks ('F'), we initialize 1209 * the child to have the same cwd as its parent. 1210 * 1211 * We also need to track the 'latestdir' of 1212 * interest. This is usually the same as cwd, but 1213 * not if a process is reading directories. 1214 * 1215 * Each time we spot a different process ('pid') 1216 * we save the current value of 'latestdir' in a 1217 * variable qualified by 'lastpid', and 1218 * re-initialize 'latestdir' to any pre-saved 1219 * value for the current 'pid' and 'CWD' if none. 1220 */ 1221 CHECK_VALID_META(p); 1222 pid = atoi(p); 1223 if (pid > 0 && pid != lastpid) { 1224 const char *ldir; 1225 void *tp; 1226 1227 if (lastpid > 0) { 1228 /* We need to remember these. */ 1229 Var_Set(lcwd_vname, lcwd, VAR_GLOBAL); 1230 Var_Set(ldir_vname, latestdir, VAR_GLOBAL); 1231 } 1232 snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid); 1233 snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid); 1234 lastpid = pid; 1235 ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); 1236 if (ldir) { 1237 strlcpy(latestdir, ldir, sizeof latestdir); 1238 bmake_free(tp); 1239 } 1240 ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp); 1241 if (ldir) { 1242 strlcpy(lcwd, ldir, sizeof lcwd); 1243 bmake_free(tp); 1244 } 1245 } 1246 /* Skip past the pid. */ 1247 if (strsep(&p, " ") == NULL) 1248 continue; 1249 #ifdef DEBUG_META_MODE 1250 if (DEBUG(META)) 1251 debug_printf("%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n", 1252 fname, lineno, 1253 pid, buf[0], cwd, lcwd, latestdir); 1254 #endif 1255 break; 1256 } 1257 1258 CHECK_VALID_META(p); 1259 1260 /* Process according to record type. */ 1261 switch (buf[0]) { 1262 case 'X': /* eXit */ 1263 Var_Delete(lcwd_vname, VAR_GLOBAL); 1264 Var_Delete(ldir_vname, VAR_GLOBAL); 1265 lastpid = 0; /* no need to save ldir_vname */ 1266 break; 1267 1268 case 'F': /* [v]Fork */ 1269 { 1270 char cldir[64]; 1271 int child; 1272 1273 child = atoi(p); 1274 if (child > 0) { 1275 snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child); 1276 Var_Set(cldir, lcwd, VAR_GLOBAL); 1277 snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child); 1278 Var_Set(cldir, latestdir, VAR_GLOBAL); 1279 #ifdef DEBUG_META_MODE 1280 if (DEBUG(META)) 1281 debug_printf( 1282 "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n", 1283 fname, lineno, 1284 child, cwd, lcwd, latestdir); 1285 #endif 1286 } 1287 } 1288 break; 1289 1290 case 'C': /* Chdir */ 1291 /* Update lcwd and latest directory. */ 1292 strlcpy(latestdir, p, sizeof latestdir); 1293 strlcpy(lcwd, p, sizeof lcwd); 1294 Var_Set(lcwd_vname, lcwd, VAR_GLOBAL); 1295 Var_Set(ldir_vname, lcwd, VAR_GLOBAL); 1296 #ifdef DEBUG_META_MODE 1297 DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n", 1298 fname, lineno, cwd, lcwd); 1299 #endif 1300 break; 1301 1302 case 'M': /* renaMe */ 1303 /* 1304 * For 'M'oves we want to check 1305 * the src as for 'R'ead 1306 * and the target as for 'W'rite. 1307 */ 1308 cp = p; /* save this for a second */ 1309 /* now get target */ 1310 if (strsep(&p, " ") == NULL) 1311 continue; 1312 CHECK_VALID_META(p); 1313 move_target = p; 1314 p = cp; 1315 /* 'L' and 'M' put single quotes around the args */ 1316 DEQUOTE(p); 1317 DEQUOTE(move_target); 1318 /* FALLTHROUGH */ 1319 case 'D': /* unlink */ 1320 if (*p == '/') { 1321 /* remove any missingFiles entries that match p */ 1322 StringListNode *ln = missingFiles->first; 1323 while (ln != NULL) { 1324 StringListNode *next = ln->next; 1325 if (path_starts_with(ln->datum, p)) { 1326 free(ln->datum); 1327 Lst_Remove(missingFiles, ln); 1328 } 1329 ln = next; 1330 } 1331 } 1332 if (buf[0] == 'M') { 1333 /* the target of the mv is a file 'W'ritten */ 1334 #ifdef DEBUG_META_MODE 1335 DEBUG2(META, "meta_oodate: M %s -> %s\n", 1336 p, move_target); 1337 #endif 1338 p = move_target; 1339 goto check_write; 1340 } 1341 break; 1342 case 'L': /* Link */ 1343 /* 1344 * For 'L'inks check 1345 * the src as for 'R'ead 1346 * and the target as for 'W'rite. 1347 */ 1348 link_src = p; 1349 /* now get target */ 1350 if (strsep(&p, " ") == NULL) 1351 continue; 1352 CHECK_VALID_META(p); 1353 /* 'L' and 'M' put single quotes around the args */ 1354 DEQUOTE(p); 1355 DEQUOTE(link_src); 1356 #ifdef DEBUG_META_MODE 1357 DEBUG2(META, "meta_oodate: L %s -> %s\n", link_src, p); 1358 #endif 1359 /* FALLTHROUGH */ 1360 case 'W': /* Write */ 1361 check_write: 1362 /* 1363 * If a file we generated within our bailiwick 1364 * but outside of .OBJDIR is missing, 1365 * we need to do it again. 1366 */ 1367 /* ignore non-absolute paths */ 1368 if (*p != '/') 1369 break; 1370 1371 if (Lst_IsEmpty(metaBailiwick)) 1372 break; 1373 1374 /* ignore cwd - normal dependencies handle those */ 1375 if (strncmp(p, cwd, cwdlen) == 0) 1376 break; 1377 1378 if (!Lst_ForEachUntil(metaBailiwick, prefix_match, p)) 1379 break; 1380 1381 /* tmpdir might be within */ 1382 if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) 1383 break; 1384 1385 /* ignore anything containing the string "tmp" */ 1386 /* XXX: The arguments to strstr must be swapped. */ 1387 if ((strstr("tmp", p))) 1388 break; 1389 1390 if ((link_src != NULL && cached_lstat(p, &cst) < 0) || 1391 (link_src == NULL && cached_stat(p, &cst) < 0)) { 1392 if (!meta_ignore(gn, p)) 1393 append_if_new(missingFiles, p); 1394 } 1395 break; 1396 check_link_src: 1397 p = link_src; 1398 link_src = NULL; 1399 #ifdef DEBUG_META_MODE 1400 DEBUG1(META, "meta_oodate: L src %s\n", p); 1401 #endif 1402 /* FALLTHROUGH */ 1403 case 'R': /* Read */ 1404 case 'E': /* Exec */ 1405 /* 1406 * Check for runtime files that can't 1407 * be part of the dependencies because 1408 * they are _expected_ to change. 1409 */ 1410 if (meta_ignore(gn, p)) 1411 break; 1412 1413 /* 1414 * The rest of the record is the file name. 1415 * Check if it's not an absolute path. 1416 */ 1417 { 1418 char *sdirs[4]; 1419 char **sdp; 1420 int sdx = 0; 1421 int found = 0; 1422 1423 if (*p == '/') { 1424 sdirs[sdx++] = p; /* done */ 1425 } else { 1426 if (strcmp(".", p) == 0) 1427 continue; /* no point */ 1428 1429 /* Check vs latestdir */ 1430 snprintf(fname1, sizeof fname1, "%s/%s", latestdir, p); 1431 sdirs[sdx++] = fname1; 1432 1433 if (strcmp(latestdir, lcwd) != 0) { 1434 /* Check vs lcwd */ 1435 snprintf(fname2, sizeof fname2, "%s/%s", lcwd, p); 1436 sdirs[sdx++] = fname2; 1437 } 1438 if (strcmp(lcwd, cwd) != 0) { 1439 /* Check vs cwd */ 1440 snprintf(fname3, sizeof fname3, "%s/%s", cwd, p); 1441 sdirs[sdx++] = fname3; 1442 } 1443 } 1444 sdirs[sdx++] = NULL; 1445 1446 for (sdp = sdirs; *sdp && !found; sdp++) { 1447 #ifdef DEBUG_META_MODE 1448 DEBUG3(META, "%s: %d: looking for: %s\n", 1449 fname, lineno, *sdp); 1450 #endif 1451 if (cached_stat(*sdp, &cst) == 0) { 1452 found = 1; 1453 p = *sdp; 1454 } 1455 } 1456 if (found) { 1457 #ifdef DEBUG_META_MODE 1458 DEBUG3(META, "%s: %d: found: %s\n", 1459 fname, lineno, p); 1460 #endif 1461 if (!S_ISDIR(cst.cst_mode) && 1462 cst.cst_mtime > gn->mtime) { 1463 DEBUG3(META, "%s: %d: file '%s' is newer than the target...\n", 1464 fname, lineno, p); 1465 oodate = TRUE; 1466 } else if (S_ISDIR(cst.cst_mode)) { 1467 /* Update the latest directory. */ 1468 cached_realpath(p, latestdir); 1469 } 1470 } else if (errno == ENOENT && *p == '/' && 1471 strncmp(p, cwd, cwdlen) != 0) { 1472 /* 1473 * A referenced file outside of CWD is missing. 1474 * We cannot catch every eventuality here... 1475 */ 1476 append_if_new(missingFiles, p); 1477 } 1478 } 1479 if (buf[0] == 'E') { 1480 /* previous latestdir is no longer relevant */ 1481 strlcpy(latestdir, lcwd, sizeof latestdir); 1482 } 1483 break; 1484 default: 1485 break; 1486 } 1487 if (!oodate && buf[0] == 'L' && link_src != NULL) 1488 goto check_link_src; 1489 } else if (strcmp(buf, "CMD") == 0) { 1490 /* 1491 * Compare the current command with the one in the 1492 * meta data file. 1493 */ 1494 if (cmdNode == NULL) { 1495 DEBUG2(META, "%s: %d: there were more build commands in the meta data file than there are now...\n", 1496 fname, lineno); 1497 oodate = TRUE; 1498 } else { 1499 char *cmd = cmdNode->datum; 1500 Boolean hasOODATE = FALSE; 1501 1502 if (strstr(cmd, "$?")) 1503 hasOODATE = TRUE; 1504 else if ((cp = strstr(cmd, ".OODATE"))) { 1505 /* check for $[{(].OODATE[:)}] */ 1506 if (cp > cmd + 2 && cp[-2] == '$') 1507 hasOODATE = TRUE; 1508 } 1509 if (hasOODATE) { 1510 needOODATE = TRUE; 1511 DEBUG2(META, "%s: %d: cannot compare command using .OODATE\n", 1512 fname, lineno); 1513 } 1514 (void)Var_Subst(cmd, gn, VARE_WANTRES|VARE_UNDEFERR, &cmd); 1515 /* TODO: handle errors */ 1516 1517 if ((cp = strchr(cmd, '\n'))) { 1518 int n; 1519 1520 /* 1521 * This command contains newlines, we need to 1522 * fetch more from the .meta file before we 1523 * attempt a comparison. 1524 */ 1525 /* first put the newline back at buf[x - 1] */ 1526 buf[x - 1] = '\n'; 1527 do { 1528 /* now fetch the next line */ 1529 if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) 1530 break; 1531 x = n; 1532 lineno++; 1533 if (buf[x - 1] != '\n') { 1534 warnx("%s: %d: line truncated at %u", fname, lineno, x); 1535 break; 1536 } 1537 cp = strchr(++cp, '\n'); 1538 } while (cp); 1539 if (buf[x - 1] == '\n') 1540 buf[x - 1] = '\0'; 1541 } 1542 if (p != NULL && 1543 !hasOODATE && 1544 !(gn->type & OP_NOMETA_CMP) && 1545 (strcmp(p, cmd) != 0)) { 1546 DEBUG4(META, "%s: %d: a build command has changed\n%s\nvs\n%s\n", 1547 fname, lineno, p, cmd); 1548 if (!metaIgnoreCMDs) 1549 oodate = TRUE; 1550 } 1551 free(cmd); 1552 cmdNode = cmdNode->next; 1553 } 1554 } else if (strcmp(buf, "CWD") == 0) { 1555 /* 1556 * Check if there are extra commands now 1557 * that weren't in the meta data file. 1558 */ 1559 if (!oodate && cmdNode != NULL) { 1560 DEBUG2(META, "%s: %d: there are extra build commands now that weren't in the meta data file\n", 1561 fname, lineno); 1562 oodate = TRUE; 1563 } 1564 CHECK_VALID_META(p); 1565 if (strcmp(p, cwd) != 0) { 1566 DEBUG4(META, "%s: %d: the current working directory has changed from '%s' to '%s'\n", 1567 fname, lineno, p, curdir); 1568 oodate = TRUE; 1569 } 1570 } 1571 } 1572 1573 fclose(fp); 1574 if (!Lst_IsEmpty(missingFiles)) { 1575 DEBUG2(META, "%s: missing files: %s...\n", 1576 fname, (char *)missingFiles->first->datum); 1577 oodate = TRUE; 1578 } 1579 if (!oodate && !have_filemon && filemonMissing) { 1580 DEBUG1(META, "%s: missing filemon data\n", fname); 1581 oodate = TRUE; 1582 } 1583 } else { 1584 if (writeMeta && (metaMissing || (gn->type & OP_META))) { 1585 cp = NULL; 1586 1587 /* if target is in .CURDIR we do not need a meta file */ 1588 if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) { 1589 if (strncmp(curdir, gn->path, (size_t)(cp - gn->path)) != 0) { 1590 cp = NULL; /* not in .CURDIR */ 1591 } 1592 } 1593 if (cp == NULL) { 1594 DEBUG1(META, "%s: required but missing\n", fname); 1595 oodate = TRUE; 1596 needOODATE = TRUE; /* assume the worst */ 1597 } 1598 } 1599 } 1600 1601 Lst_Destroy(missingFiles, free); 1602 1603 if (oodate && needOODATE) { 1604 /* 1605 * Target uses .OODATE which is empty; or we wouldn't be here. 1606 * We have decided it is oodate, so .OODATE needs to be set. 1607 * All we can sanely do is set it to .ALLSRC. 1608 */ 1609 Var_Delete(OODATE, gn); 1610 Var_Set(OODATE, GNode_VarAllsrc(gn), gn); 1611 } 1612 1613 oodate_out: 1614 bmake_free(objdir_freeIt); 1615 return oodate; 1616 } 1617 1618 /* support for compat mode */ 1619 1620 static int childPipe[2]; 1621 1622 void 1623 meta_compat_start(void) 1624 { 1625 #ifdef USE_FILEMON_ONCE 1626 /* 1627 * We need to re-open filemon for each cmd. 1628 */ 1629 BuildMon *pbm = &Mybm; 1630 1631 if (pbm->mfp != NULL && useFilemon) { 1632 meta_open_filemon(pbm); 1633 } else { 1634 pbm->mon_fd = -1; 1635 pbm->filemon = NULL; 1636 } 1637 #endif 1638 if (pipe(childPipe) < 0) 1639 Punt("Cannot create pipe: %s", strerror(errno)); 1640 /* Set close-on-exec flag for both */ 1641 (void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC); 1642 (void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC); 1643 } 1644 1645 void 1646 meta_compat_child(void) 1647 { 1648 meta_job_child(NULL); 1649 if (dup2(childPipe[1], 1) < 0 || dup2(1, 2) < 0) 1650 execDie("dup2", "pipe"); 1651 } 1652 1653 void 1654 meta_compat_parent(pid_t child) 1655 { 1656 int outfd, metafd, maxfd, nfds; 1657 char buf[BUFSIZ+1]; 1658 fd_set readfds; 1659 1660 meta_job_parent(NULL, child); 1661 close(childPipe[1]); /* child side */ 1662 outfd = childPipe[0]; 1663 #ifdef USE_FILEMON 1664 metafd = Mybm.filemon ? filemon_readfd(Mybm.filemon) : -1; 1665 #else 1666 metafd = -1; 1667 #endif 1668 maxfd = -1; 1669 if (outfd > maxfd) 1670 maxfd = outfd; 1671 if (metafd > maxfd) 1672 maxfd = metafd; 1673 1674 while (outfd != -1 || metafd != -1) { 1675 FD_ZERO(&readfds); 1676 if (outfd != -1) { 1677 FD_SET(outfd, &readfds); 1678 } 1679 if (metafd != -1) { 1680 FD_SET(metafd, &readfds); 1681 } 1682 nfds = select(maxfd + 1, &readfds, NULL, NULL, NULL); 1683 if (nfds == -1) { 1684 if (errno == EINTR) 1685 continue; 1686 err(1, "select"); 1687 } 1688 1689 if (outfd != -1 && FD_ISSET(outfd, &readfds)) do { 1690 /* XXX this is not line-buffered */ 1691 ssize_t nread = read(outfd, buf, sizeof buf - 1); 1692 if (nread == -1) 1693 err(1, "read"); 1694 if (nread == 0) { 1695 close(outfd); 1696 outfd = -1; 1697 break; 1698 } 1699 fwrite(buf, 1, (size_t)nread, stdout); 1700 fflush(stdout); 1701 buf[nread] = '\0'; 1702 meta_job_output(NULL, buf, ""); 1703 } while (0); 1704 if (metafd != -1 && FD_ISSET(metafd, &readfds)) { 1705 if (meta_job_event(NULL) <= 0) 1706 metafd = -1; 1707 } 1708 } 1709 } 1710 1711 #endif /* USE_META */ 1712