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