1321964Ssjg/* $NetBSD: meta.c,v 1.68 2017/07/09 04:54:00 sjg Exp $ */ 2236769Sobrien 3236769Sobrien/* 4236769Sobrien * Implement 'meta' mode. 5236769Sobrien * Adapted from John Birrell's patches to FreeBSD make. 6236769Sobrien * --sjg 7236769Sobrien */ 8236769Sobrien/* 9321964Ssjg * Copyright (c) 2009-2016, Juniper Networks, Inc. 10236769Sobrien * Portions Copyright (c) 2009, John Birrell. 11236769Sobrien * 12236769Sobrien * Redistribution and use in source and binary forms, with or without 13236769Sobrien * modification, are permitted provided that the following conditions 14236769Sobrien * are met: 15236769Sobrien * 1. Redistributions of source code must retain the above copyright 16236769Sobrien * notice, this list of conditions and the following disclaimer. 17236769Sobrien * 2. Redistributions in binary form must reproduce the above copyright 18236769Sobrien * notice, this list of conditions and the following disclaimer in the 19236769Sobrien * documentation and/or other materials provided with the distribution. 20236769Sobrien * 21236769Sobrien * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22236769Sobrien * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23236769Sobrien * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24236769Sobrien * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 25236769Sobrien * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 26236769Sobrien * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 27236769Sobrien * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 28236769Sobrien * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 29236769Sobrien * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 30236769Sobrien * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 31236769Sobrien * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32236769Sobrien */ 33236769Sobrien#if defined(USE_META) 34236769Sobrien 35236769Sobrien#ifdef HAVE_CONFIG_H 36236769Sobrien# include "config.h" 37236769Sobrien#endif 38236769Sobrien#include <sys/stat.h> 39236769Sobrien#include <sys/ioctl.h> 40292068Ssjg#ifdef HAVE_LIBGEN_H 41236769Sobrien#include <libgen.h> 42292068Ssjg#elif !defined(HAVE_DIRNAME) 43292068Ssjgchar * dirname(char *); 44292068Ssjg#endif 45236769Sobrien#include <errno.h> 46236769Sobrien#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) 47236769Sobrien#include <err.h> 48236769Sobrien#endif 49236769Sobrien 50236769Sobrien#include "make.h" 51236769Sobrien#include "job.h" 52236769Sobrien 53236769Sobrien#ifdef HAVE_FILEMON_H 54236769Sobrien# include <filemon.h> 55236769Sobrien#endif 56236769Sobrien#if !defined(USE_FILEMON) && defined(FILEMON_SET_FD) 57236769Sobrien# define USE_FILEMON 58236769Sobrien#endif 59236769Sobrien 60236769Sobrienstatic BuildMon Mybm; /* for compat */ 61253883Ssjgstatic Lst metaBailiwick; /* our scope of control */ 62321964Ssjgstatic char *metaBailiwickStr; /* string storage for the list */ 63253883Ssjgstatic Lst metaIgnorePaths; /* paths we deliberately ignore */ 64321964Ssjgstatic char *metaIgnorePathsStr; /* string storage for the list */ 65236769Sobrien 66253883Ssjg#ifndef MAKE_META_IGNORE_PATHS 67253883Ssjg#define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS" 68253883Ssjg#endif 69321964Ssjg#ifndef MAKE_META_IGNORE_PATTERNS 70321964Ssjg#define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS" 71321964Ssjg#endif 72321964Ssjg#ifndef MAKE_META_IGNORE_FILTER 73321964Ssjg#define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER" 74321964Ssjg#endif 75253883Ssjg 76236769SobrienBoolean useMeta = FALSE; 77236769Sobrienstatic Boolean useFilemon = FALSE; 78236769Sobrienstatic Boolean writeMeta = FALSE; 79321964Ssjgstatic Boolean metaMissing = FALSE; /* oodate if missing */ 80321964Ssjgstatic Boolean filemonMissing = FALSE; /* oodate if missing */ 81236769Sobrienstatic Boolean metaEnv = FALSE; /* don't save env unless asked */ 82236769Sobrienstatic Boolean metaVerbose = FALSE; 83236769Sobrienstatic Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ 84321964Ssjgstatic Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */ 85321964Ssjgstatic Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */ 86236769Sobrienstatic Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ 87236769Sobrienstatic Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */ 88236769Sobrien 89236769Sobrienextern Boolean forceJobs; 90236769Sobrienextern Boolean comatMake; 91238152Sobrienextern char **environ; 92236769Sobrien 93236769Sobrien#define MAKE_META_PREFIX ".MAKE.META.PREFIX" 94236769Sobrien 95236769Sobrien#ifndef N2U 96236769Sobrien# define N2U(n, u) (((n) + ((u) - 1)) / (u)) 97236769Sobrien#endif 98236769Sobrien#ifndef ROUNDUP 99236769Sobrien# define ROUNDUP(n, u) (N2U((n), (u)) * (u)) 100236769Sobrien#endif 101236769Sobrien 102236769Sobrien#if !defined(HAVE_STRSEP) 103236769Sobrien# define strsep(s, d) stresep((s), (d), 0) 104236769Sobrien#endif 105236769Sobrien 106236769Sobrien/* 107236769Sobrien * Filemon is a kernel module which snoops certain syscalls. 108236769Sobrien * 109236769Sobrien * C chdir 110236769Sobrien * E exec 111236769Sobrien * F [v]fork 112236769Sobrien * L [sym]link 113236769Sobrien * M rename 114236769Sobrien * R read 115236769Sobrien * W write 116236769Sobrien * S stat 117236769Sobrien * 118236769Sobrien * See meta_oodate below - we mainly care about 'E' and 'R'. 119236769Sobrien * 120236769Sobrien * We can still use meta mode without filemon, but 121236769Sobrien * the benefits are more limited. 122236769Sobrien */ 123236769Sobrien#ifdef USE_FILEMON 124236769Sobrien# ifndef _PATH_FILEMON 125236769Sobrien# define _PATH_FILEMON "/dev/filemon" 126236769Sobrien# endif 127236769Sobrien 128236769Sobrien/* 129236769Sobrien * Open the filemon device. 130236769Sobrien */ 131236769Sobrienstatic void 132236769Sobrienfilemon_open(BuildMon *pbm) 133236769Sobrien{ 134236769Sobrien int retry; 135236769Sobrien 136236769Sobrien pbm->mon_fd = pbm->filemon_fd = -1; 137236769Sobrien if (!useFilemon) 138236769Sobrien return; 139236769Sobrien 140236769Sobrien for (retry = 5; retry >= 0; retry--) { 141236769Sobrien if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0) 142236769Sobrien break; 143236769Sobrien } 144236769Sobrien 145236769Sobrien if (pbm->filemon_fd < 0) { 146236769Sobrien useFilemon = FALSE; 147236769Sobrien warn("Could not open %s", _PATH_FILEMON); 148236769Sobrien return; 149236769Sobrien } 150236769Sobrien 151236769Sobrien /* 152236769Sobrien * We use a file outside of '.' 153236769Sobrien * to avoid a FreeBSD kernel bug where unlink invalidates 154236769Sobrien * cwd causing getcwd to do a lot more work. 155236769Sobrien * We only care about the descriptor. 156236769Sobrien */ 157236769Sobrien pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); 158236769Sobrien if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) { 159236769Sobrien err(1, "Could not set filemon file descriptor!"); 160236769Sobrien } 161236769Sobrien /* we don't need these once we exec */ 162321964Ssjg (void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC); 163321964Ssjg (void)fcntl(pbm->filemon_fd, F_SETFD, FD_CLOEXEC); 164236769Sobrien} 165236769Sobrien 166236769Sobrien/* 167236769Sobrien * Read the build monitor output file and write records to the target's 168236769Sobrien * metadata file. 169236769Sobrien */ 170321964Ssjgstatic int 171236769Sobrienfilemon_read(FILE *mfp, int fd) 172236769Sobrien{ 173236769Sobrien char buf[BUFSIZ]; 174292068Ssjg int n; 175321964Ssjg int error; 176236769Sobrien 177236769Sobrien /* Check if we're not writing to a meta data file.*/ 178236769Sobrien if (mfp == NULL) { 179236769Sobrien if (fd >= 0) 180236769Sobrien close(fd); /* not interested */ 181321964Ssjg return 0; 182236769Sobrien } 183236769Sobrien /* rewind */ 184236769Sobrien (void)lseek(fd, (off_t)0, SEEK_SET); 185236769Sobrien 186321964Ssjg error = 0; 187292068Ssjg fprintf(mfp, "\n-- filemon acquired metadata --\n"); 188236769Sobrien 189292068Ssjg while ((n = read(fd, buf, sizeof(buf))) > 0) { 190321964Ssjg if ((int)fwrite(buf, 1, n, mfp) < n) 191321964Ssjg error = EIO; 192236769Sobrien } 193236769Sobrien fflush(mfp); 194321964Ssjg if (close(fd) < 0) 195321964Ssjg error = errno; 196321964Ssjg return error; 197236769Sobrien} 198236769Sobrien#endif 199236769Sobrien 200236769Sobrien/* 201236769Sobrien * when realpath() fails, 202236769Sobrien * we use this, to clean up ./ and ../ 203236769Sobrien */ 204236769Sobrienstatic void 205236769Sobrieneat_dots(char *buf, size_t bufsz, int dots) 206236769Sobrien{ 207236769Sobrien char *cp; 208236769Sobrien char *cp2; 209236769Sobrien const char *eat; 210236769Sobrien size_t eatlen; 211236769Sobrien 212236769Sobrien switch (dots) { 213236769Sobrien case 1: 214236769Sobrien eat = "/./"; 215236769Sobrien eatlen = 2; 216236769Sobrien break; 217236769Sobrien case 2: 218236769Sobrien eat = "/../"; 219236769Sobrien eatlen = 3; 220236769Sobrien break; 221236769Sobrien default: 222236769Sobrien return; 223236769Sobrien } 224236769Sobrien 225236769Sobrien do { 226236769Sobrien cp = strstr(buf, eat); 227236769Sobrien if (cp) { 228236769Sobrien cp2 = cp + eatlen; 229236769Sobrien if (dots == 2 && cp > buf) { 230236769Sobrien do { 231236769Sobrien cp--; 232236769Sobrien } while (cp > buf && *cp != '/'); 233236769Sobrien } 234236769Sobrien if (*cp == '/') { 235236769Sobrien strlcpy(cp, cp2, bufsz - (cp - buf)); 236236769Sobrien } else { 237236769Sobrien return; /* can't happen? */ 238236769Sobrien } 239236769Sobrien } 240236769Sobrien } while (cp); 241236769Sobrien} 242236769Sobrien 243236769Sobrienstatic char * 244321964Ssjgmeta_name(char *mname, size_t mnamelen, 245236769Sobrien const char *dname, 246321964Ssjg const char *tname, 247321964Ssjg const char *cwd) 248236769Sobrien{ 249236769Sobrien char buf[MAXPATHLEN]; 250236769Sobrien char *rp; 251236769Sobrien char *cp; 252236769Sobrien char *tp; 253236769Sobrien 254236769Sobrien /* 255236769Sobrien * Weed out relative paths from the target file name. 256236769Sobrien * We have to be careful though since if target is a 257236769Sobrien * symlink, the result will be unstable. 258236769Sobrien * So we use realpath() just to get the dirname, and leave the 259236769Sobrien * basename as given to us. 260236769Sobrien */ 261236769Sobrien if ((cp = strrchr(tname, '/'))) { 262321964Ssjg if (cached_realpath(tname, buf)) { 263236769Sobrien if ((rp = strrchr(buf, '/'))) { 264236769Sobrien rp++; 265236769Sobrien cp++; 266236769Sobrien if (strcmp(cp, rp) != 0) 267236769Sobrien strlcpy(rp, cp, sizeof(buf) - (rp - buf)); 268236769Sobrien } 269236769Sobrien tname = buf; 270236769Sobrien } else { 271236769Sobrien /* 272236769Sobrien * We likely have a directory which is about to be made. 273236769Sobrien * We pretend realpath() succeeded, to have a chance 274236769Sobrien * of generating the same meta file name that we will 275236769Sobrien * next time through. 276236769Sobrien */ 277236769Sobrien if (tname[0] == '/') { 278236769Sobrien strlcpy(buf, tname, sizeof(buf)); 279236769Sobrien } else { 280236769Sobrien snprintf(buf, sizeof(buf), "%s/%s", cwd, tname); 281236769Sobrien } 282236769Sobrien eat_dots(buf, sizeof(buf), 1); /* ./ */ 283236769Sobrien eat_dots(buf, sizeof(buf), 2); /* ../ */ 284236769Sobrien tname = buf; 285236769Sobrien } 286236769Sobrien } 287236769Sobrien /* on some systems dirname may modify its arg */ 288236769Sobrien tp = bmake_strdup(tname); 289236769Sobrien if (strcmp(dname, dirname(tp)) == 0) 290236769Sobrien snprintf(mname, mnamelen, "%s.meta", tname); 291236769Sobrien else { 292236769Sobrien snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); 293236769Sobrien 294236769Sobrien /* 295236769Sobrien * Replace path separators in the file name after the 296236769Sobrien * current object directory path. 297236769Sobrien */ 298236769Sobrien cp = mname + strlen(dname) + 1; 299236769Sobrien 300236769Sobrien while (*cp != '\0') { 301236769Sobrien if (*cp == '/') 302236769Sobrien *cp = '_'; 303236769Sobrien cp++; 304236769Sobrien } 305236769Sobrien } 306236769Sobrien free(tp); 307236769Sobrien return (mname); 308236769Sobrien} 309236769Sobrien 310236769Sobrien/* 311236769Sobrien * Return true if running ${.MAKE} 312236769Sobrien * Bypassed if target is flagged .MAKE 313236769Sobrien */ 314236769Sobrienstatic int 315236769Sobrienis_submake(void *cmdp, void *gnp) 316236769Sobrien{ 317236769Sobrien static char *p_make = NULL; 318236769Sobrien static int p_len; 319236769Sobrien char *cmd = cmdp; 320236769Sobrien GNode *gn = gnp; 321236769Sobrien char *mp = NULL; 322236769Sobrien char *cp; 323236769Sobrien char *cp2; 324236769Sobrien int rc = 0; /* keep looking */ 325236769Sobrien 326236769Sobrien if (!p_make) { 327236769Sobrien p_make = Var_Value(".MAKE", gn, &cp); 328236769Sobrien p_len = strlen(p_make); 329236769Sobrien } 330236769Sobrien cp = strchr(cmd, '$'); 331236769Sobrien if ((cp)) { 332321964Ssjg mp = Var_Subst(NULL, cmd, gn, VARF_WANTRES); 333236769Sobrien cmd = mp; 334236769Sobrien } 335236769Sobrien cp2 = strstr(cmd, p_make); 336236769Sobrien if ((cp2)) { 337236769Sobrien switch (cp2[p_len]) { 338236769Sobrien case '\0': 339236769Sobrien case ' ': 340236769Sobrien case '\t': 341236769Sobrien case '\n': 342236769Sobrien rc = 1; 343236769Sobrien break; 344236769Sobrien } 345236769Sobrien if (cp2 > cmd && rc > 0) { 346236769Sobrien switch (cp2[-1]) { 347236769Sobrien case ' ': 348236769Sobrien case '\t': 349236769Sobrien case '\n': 350236769Sobrien break; 351236769Sobrien default: 352236769Sobrien rc = 0; /* no match */ 353236769Sobrien break; 354236769Sobrien } 355236769Sobrien } 356236769Sobrien } 357321964Ssjg free(mp); 358236769Sobrien return (rc); 359236769Sobrien} 360236769Sobrien 361236769Sobrientypedef struct meta_file_s { 362236769Sobrien FILE *fp; 363236769Sobrien GNode *gn; 364236769Sobrien} meta_file_t; 365236769Sobrien 366236769Sobrienstatic int 367236769SobrienprintCMD(void *cmdp, void *mfpp) 368236769Sobrien{ 369236769Sobrien meta_file_t *mfp = mfpp; 370236769Sobrien char *cmd = cmdp; 371236769Sobrien char *cp = NULL; 372236769Sobrien 373236769Sobrien if (strchr(cmd, '$')) { 374321964Ssjg cmd = cp = Var_Subst(NULL, cmd, mfp->gn, VARF_WANTRES); 375236769Sobrien } 376236769Sobrien fprintf(mfp->fp, "CMD %s\n", cmd); 377321964Ssjg free(cp); 378236769Sobrien return 0; 379236769Sobrien} 380236769Sobrien 381236769Sobrien/* 382236769Sobrien * Certain node types never get a .meta file 383236769Sobrien */ 384236769Sobrien#define SKIP_META_TYPE(_type) do { \ 385236769Sobrien if ((gn->type & __CONCAT(OP_, _type))) { \ 386321964Ssjg if (verbose) { \ 387236769Sobrien fprintf(debug_file, "Skipping meta for %s: .%s\n", \ 388236769Sobrien gn->name, __STRING(_type)); \ 389236769Sobrien } \ 390321964Ssjg return FALSE; \ 391236769Sobrien } \ 392236769Sobrien} while (0) 393236769Sobrien 394321964Ssjg 395321964Ssjg/* 396321964Ssjg * Do we need/want a .meta file ? 397321964Ssjg */ 398321964Ssjgstatic Boolean 399321964Ssjgmeta_needed(GNode *gn, const char *dname, 400321964Ssjg char *objdir, int verbose) 401236769Sobrien{ 402236769Sobrien struct stat fs; 403236769Sobrien 404321964Ssjg if (verbose) 405321964Ssjg verbose = DEBUG(META); 406236769Sobrien 407236769Sobrien /* This may be a phony node which we don't want meta data for... */ 408236769Sobrien /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ 409236769Sobrien /* Or it may be explicitly flagged as .NOMETA */ 410236769Sobrien SKIP_META_TYPE(NOMETA); 411236769Sobrien /* Unless it is explicitly flagged as .META */ 412236769Sobrien if (!(gn->type & OP_META)) { 413236769Sobrien SKIP_META_TYPE(PHONY); 414236769Sobrien SKIP_META_TYPE(SPECIAL); 415236769Sobrien SKIP_META_TYPE(MAKE); 416236769Sobrien } 417236769Sobrien 418236769Sobrien /* Check if there are no commands to execute. */ 419236769Sobrien if (Lst_IsEmpty(gn->commands)) { 420321964Ssjg if (verbose) 421236769Sobrien fprintf(debug_file, "Skipping meta for %s: no commands\n", 422236769Sobrien gn->name); 423321964Ssjg return FALSE; 424236769Sobrien } 425321964Ssjg if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) { 426321964Ssjg /* OP_SUBMAKE is a bit too aggressive */ 427321964Ssjg if (Lst_ForEach(gn->commands, is_submake, gn)) { 428321964Ssjg if (DEBUG(META)) 429321964Ssjg fprintf(debug_file, "Skipping meta for %s: .SUBMAKE\n", 430321964Ssjg gn->name); 431321964Ssjg return FALSE; 432321964Ssjg } 433321964Ssjg } 434236769Sobrien 435321964Ssjg /* The object directory may not exist. Check it.. */ 436321964Ssjg if (cached_stat(dname, &fs) != 0) { 437321964Ssjg if (verbose) 438321964Ssjg fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", 439321964Ssjg gn->name); 440321964Ssjg return FALSE; 441321964Ssjg } 442321964Ssjg 443236769Sobrien /* make sure these are canonical */ 444321964Ssjg if (cached_realpath(dname, objdir)) 445236769Sobrien dname = objdir; 446236769Sobrien 447236769Sobrien /* If we aren't in the object directory, don't create a meta file. */ 448236769Sobrien if (!metaCurdirOk && strcmp(curdir, dname) == 0) { 449321964Ssjg if (verbose) 450236769Sobrien fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", 451236769Sobrien gn->name); 452321964Ssjg return FALSE; 453236769Sobrien } 454321964Ssjg return TRUE; 455321964Ssjg} 456236769Sobrien 457321964Ssjg 458321964Ssjgstatic FILE * 459321964Ssjgmeta_create(BuildMon *pbm, GNode *gn) 460321964Ssjg{ 461321964Ssjg meta_file_t mf; 462321964Ssjg char buf[MAXPATHLEN]; 463321964Ssjg char objdir[MAXPATHLEN]; 464321964Ssjg char **ptr; 465321964Ssjg const char *dname; 466321964Ssjg const char *tname; 467321964Ssjg char *fname; 468321964Ssjg const char *cp; 469321964Ssjg char *p[4]; /* >= possible uses */ 470321964Ssjg int i; 471321964Ssjg 472321964Ssjg mf.fp = NULL; 473321964Ssjg i = 0; 474321964Ssjg 475321964Ssjg dname = Var_Value(".OBJDIR", gn, &p[i++]); 476321964Ssjg tname = Var_Value(TARGET, gn, &p[i++]); 477321964Ssjg 478321964Ssjg /* if this succeeds objdir is realpath of dname */ 479321964Ssjg if (!meta_needed(gn, dname, objdir, TRUE)) 480321964Ssjg goto out; 481321964Ssjg dname = objdir; 482321964Ssjg 483236769Sobrien if (metaVerbose) { 484236769Sobrien char *mp; 485236769Sobrien 486236769Sobrien /* Describe the target we are building */ 487321964Ssjg mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, VARF_WANTRES); 488236769Sobrien if (*mp) 489236769Sobrien fprintf(stdout, "%s\n", mp); 490236769Sobrien free(mp); 491236769Sobrien } 492236769Sobrien /* Get the basename of the target */ 493236769Sobrien if ((cp = strrchr(tname, '/')) == NULL) { 494236769Sobrien cp = tname; 495236769Sobrien } else { 496236769Sobrien cp++; 497236769Sobrien } 498236769Sobrien 499236769Sobrien fflush(stdout); 500236769Sobrien 501236769Sobrien if (!writeMeta) 502236769Sobrien /* Don't create meta data. */ 503236769Sobrien goto out; 504236769Sobrien 505321964Ssjg fname = meta_name(pbm->meta_fname, sizeof(pbm->meta_fname), 506321964Ssjg dname, tname, objdir); 507236769Sobrien 508236769Sobrien#ifdef DEBUG_META_MODE 509236769Sobrien if (DEBUG(META)) 510236769Sobrien fprintf(debug_file, "meta_create: %s\n", fname); 511236769Sobrien#endif 512236769Sobrien 513236769Sobrien if ((mf.fp = fopen(fname, "w")) == NULL) 514236769Sobrien err(1, "Could not open meta file '%s'", fname); 515236769Sobrien 516236769Sobrien fprintf(mf.fp, "# Meta data file %s\n", fname); 517236769Sobrien 518236769Sobrien mf.gn = gn; 519236769Sobrien 520236769Sobrien Lst_ForEach(gn->commands, printCMD, &mf); 521236769Sobrien 522236769Sobrien fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf))); 523236769Sobrien fprintf(mf.fp, "TARGET %s\n", tname); 524236769Sobrien 525236769Sobrien if (metaEnv) { 526236769Sobrien for (ptr = environ; *ptr != NULL; ptr++) 527236769Sobrien fprintf(mf.fp, "ENV %s\n", *ptr); 528236769Sobrien } 529236769Sobrien 530236769Sobrien fprintf(mf.fp, "-- command output --\n"); 531236769Sobrien fflush(mf.fp); 532236769Sobrien 533236769Sobrien Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 534236769Sobrien Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); 535236769Sobrien 536236769Sobrien gn->type |= OP_META; /* in case anyone wants to know */ 537236769Sobrien if (metaSilent) { 538236769Sobrien gn->type |= OP_SILENT; 539236769Sobrien } 540236769Sobrien out: 541236769Sobrien for (i--; i >= 0; i--) { 542321964Ssjg free(p[i]); 543236769Sobrien } 544236769Sobrien 545236769Sobrien return (mf.fp); 546236769Sobrien} 547236769Sobrien 548236769Sobrienstatic Boolean 549236769SobrienboolValue(char *s) 550236769Sobrien{ 551236769Sobrien switch(*s) { 552236769Sobrien case '0': 553236769Sobrien case 'N': 554236769Sobrien case 'n': 555236769Sobrien case 'F': 556236769Sobrien case 'f': 557236769Sobrien return FALSE; 558236769Sobrien } 559236769Sobrien return TRUE; 560236769Sobrien} 561236769Sobrien 562249033Ssjg/* 563249033Ssjg * Initialization we need before reading makefiles. 564249033Ssjg */ 565236769Sobrienvoid 566250837Ssjgmeta_init(void) 567236769Sobrien{ 568249033Ssjg#ifdef USE_FILEMON 569249033Ssjg /* this allows makefiles to test if we have filemon support */ 570249033Ssjg Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0); 571249033Ssjg#endif 572249033Ssjg} 573249033Ssjg 574249033Ssjg 575321964Ssjg#define get_mode_bf(bf, token) \ 576321964Ssjg if ((cp = strstr(make_mode, token))) \ 577321964Ssjg bf = boolValue(&cp[sizeof(token) - 1]) 578321964Ssjg 579249033Ssjg/* 580249033Ssjg * Initialization we need after reading makefiles. 581249033Ssjg */ 582249033Ssjgvoid 583249033Ssjgmeta_mode_init(const char *make_mode) 584249033Ssjg{ 585236769Sobrien static int once = 0; 586236769Sobrien char *cp; 587236769Sobrien 588236769Sobrien useMeta = TRUE; 589236769Sobrien useFilemon = TRUE; 590236769Sobrien writeMeta = TRUE; 591236769Sobrien 592236769Sobrien if (make_mode) { 593236769Sobrien if (strstr(make_mode, "env")) 594236769Sobrien metaEnv = TRUE; 595236769Sobrien if (strstr(make_mode, "verb")) 596236769Sobrien metaVerbose = TRUE; 597236769Sobrien if (strstr(make_mode, "read")) 598236769Sobrien writeMeta = FALSE; 599236769Sobrien if (strstr(make_mode, "nofilemon")) 600236769Sobrien useFilemon = FALSE; 601236769Sobrien if (strstr(make_mode, "ignore-cmd")) 602236769Sobrien metaIgnoreCMDs = TRUE; 603321964Ssjg if (useFilemon) 604321964Ssjg get_mode_bf(filemonMissing, "missing-filemon="); 605321964Ssjg get_mode_bf(metaCurdirOk, "curdirok="); 606321964Ssjg get_mode_bf(metaMissing, "missing-meta="); 607321964Ssjg get_mode_bf(metaSilent, "silent="); 608236769Sobrien } 609236769Sobrien if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { 610236769Sobrien /* 611236769Sobrien * The default value for MAKE_META_PREFIX 612236769Sobrien * prints the absolute path of the target. 613236769Sobrien * This works be cause :H will generate '.' if there is no / 614236769Sobrien * and :tA will resolve that to cwd. 615236769Sobrien */ 616236769Sobrien Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0); 617236769Sobrien } 618236769Sobrien if (once) 619236769Sobrien return; 620236769Sobrien once = 1; 621236769Sobrien memset(&Mybm, 0, sizeof(Mybm)); 622236769Sobrien /* 623236769Sobrien * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} 624236769Sobrien */ 625236769Sobrien metaBailiwick = Lst_Init(FALSE); 626321964Ssjg metaBailiwickStr = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", 627321964Ssjg VAR_GLOBAL, VARF_WANTRES); 628321964Ssjg if (metaBailiwickStr) { 629321964Ssjg str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL); 630236769Sobrien } 631253883Ssjg /* 632253883Ssjg * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} 633253883Ssjg */ 634253883Ssjg metaIgnorePaths = Lst_Init(FALSE); 635253883Ssjg Var_Append(MAKE_META_IGNORE_PATHS, 636253883Ssjg "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); 637321964Ssjg metaIgnorePathsStr = Var_Subst(NULL, 638292068Ssjg "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL, 639321964Ssjg VARF_WANTRES); 640321964Ssjg if (metaIgnorePathsStr) { 641321964Ssjg str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL); 642253883Ssjg } 643321964Ssjg 644321964Ssjg /* 645321964Ssjg * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS} 646321964Ssjg */ 647321964Ssjg cp = NULL; 648321964Ssjg if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &cp)) { 649321964Ssjg metaIgnorePatterns = TRUE; 650321964Ssjg free(cp); 651321964Ssjg } 652321964Ssjg cp = NULL; 653321964Ssjg if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &cp)) { 654321964Ssjg metaIgnoreFilter = TRUE; 655321964Ssjg free(cp); 656321964Ssjg } 657236769Sobrien} 658236769Sobrien 659236769Sobrien/* 660236769Sobrien * In each case below we allow for job==NULL 661236769Sobrien */ 662236769Sobrienvoid 663236769Sobrienmeta_job_start(Job *job, GNode *gn) 664236769Sobrien{ 665236769Sobrien BuildMon *pbm; 666236769Sobrien 667236769Sobrien if (job != NULL) { 668236769Sobrien pbm = &job->bm; 669236769Sobrien } else { 670236769Sobrien pbm = &Mybm; 671236769Sobrien } 672236769Sobrien pbm->mfp = meta_create(pbm, gn); 673236769Sobrien#ifdef USE_FILEMON_ONCE 674236769Sobrien /* compat mode we open the filemon dev once per command */ 675236769Sobrien if (job == NULL) 676236769Sobrien return; 677236769Sobrien#endif 678236769Sobrien#ifdef USE_FILEMON 679236769Sobrien if (pbm->mfp != NULL && useFilemon) { 680236769Sobrien filemon_open(pbm); 681236769Sobrien } else { 682236769Sobrien pbm->mon_fd = pbm->filemon_fd = -1; 683236769Sobrien } 684236769Sobrien#endif 685236769Sobrien} 686236769Sobrien 687236769Sobrien/* 688236769Sobrien * The child calls this before doing anything. 689236769Sobrien * It does not disturb our state. 690236769Sobrien */ 691236769Sobrienvoid 692236769Sobrienmeta_job_child(Job *job) 693236769Sobrien{ 694236769Sobrien#ifdef USE_FILEMON 695236769Sobrien BuildMon *pbm; 696236769Sobrien 697236769Sobrien if (job != NULL) { 698236769Sobrien pbm = &job->bm; 699236769Sobrien } else { 700236769Sobrien pbm = &Mybm; 701236769Sobrien } 702292068Ssjg if (pbm->mfp != NULL) { 703292068Ssjg close(fileno(pbm->mfp)); 704292068Ssjg if (useFilemon) { 705292068Ssjg pid_t pid; 706292068Ssjg 707292068Ssjg pid = getpid(); 708292068Ssjg if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) { 709292068Ssjg err(1, "Could not set filemon pid!"); 710292068Ssjg } 711236769Sobrien } 712236769Sobrien } 713236769Sobrien#endif 714236769Sobrien} 715236769Sobrien 716236769Sobrienvoid 717236769Sobrienmeta_job_error(Job *job, GNode *gn, int flags, int status) 718236769Sobrien{ 719236769Sobrien char cwd[MAXPATHLEN]; 720236769Sobrien BuildMon *pbm; 721236769Sobrien 722236769Sobrien if (job != NULL) { 723236769Sobrien pbm = &job->bm; 724236769Sobrien if (!gn) 725236769Sobrien gn = job->node; 726321964Ssjg } else { 727236769Sobrien pbm = &Mybm; 728236769Sobrien } 729236769Sobrien if (pbm->mfp != NULL) { 730321964Ssjg fprintf(pbm->mfp, "\n*** Error code %d%s\n", 731236769Sobrien status, 732236769Sobrien (flags & JOB_IGNERR) ? 733236769Sobrien "(ignored)" : ""); 734236769Sobrien } 735236769Sobrien if (gn) { 736236769Sobrien Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0); 737236769Sobrien } 738236769Sobrien getcwd(cwd, sizeof(cwd)); 739236769Sobrien Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0); 740321964Ssjg if (pbm->meta_fname[0]) { 741236769Sobrien Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0); 742236769Sobrien } 743236769Sobrien meta_job_finish(job); 744236769Sobrien} 745236769Sobrien 746236769Sobrienvoid 747236769Sobrienmeta_job_output(Job *job, char *cp, const char *nl) 748236769Sobrien{ 749236769Sobrien BuildMon *pbm; 750236769Sobrien 751236769Sobrien if (job != NULL) { 752236769Sobrien pbm = &job->bm; 753236769Sobrien } else { 754236769Sobrien pbm = &Mybm; 755236769Sobrien } 756236769Sobrien if (pbm->mfp != NULL) { 757236769Sobrien if (metaVerbose) { 758236769Sobrien static char *meta_prefix = NULL; 759236769Sobrien static int meta_prefix_len; 760236769Sobrien 761236769Sobrien if (!meta_prefix) { 762236769Sobrien char *cp2; 763236769Sobrien 764292068Ssjg meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", 765321964Ssjg VAR_GLOBAL, VARF_WANTRES); 766236769Sobrien if ((cp2 = strchr(meta_prefix, '$'))) 767236769Sobrien meta_prefix_len = cp2 - meta_prefix; 768236769Sobrien else 769236769Sobrien meta_prefix_len = strlen(meta_prefix); 770236769Sobrien } 771236769Sobrien if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { 772236769Sobrien cp = strchr(cp+1, '\n'); 773236769Sobrien if (!cp++) 774236769Sobrien return; 775236769Sobrien } 776236769Sobrien } 777236769Sobrien fprintf(pbm->mfp, "%s%s", cp, nl); 778236769Sobrien } 779236769Sobrien} 780236769Sobrien 781321964Ssjgint 782236769Sobrienmeta_cmd_finish(void *pbmp) 783236769Sobrien{ 784321964Ssjg int error = 0; 785236769Sobrien BuildMon *pbm = pbmp; 786321964Ssjg int x; 787236769Sobrien 788236769Sobrien if (!pbm) 789236769Sobrien pbm = &Mybm; 790236769Sobrien 791321964Ssjg#ifdef USE_FILEMON 792236769Sobrien if (pbm->filemon_fd >= 0) { 793321964Ssjg if (close(pbm->filemon_fd) < 0) 794321964Ssjg error = errno; 795321964Ssjg x = filemon_read(pbm->mfp, pbm->mon_fd); 796321964Ssjg if (error == 0 && x != 0) 797321964Ssjg error = x; 798236769Sobrien pbm->filemon_fd = pbm->mon_fd = -1; 799321964Ssjg } else 800236769Sobrien#endif 801321964Ssjg fprintf(pbm->mfp, "\n"); /* ensure end with newline */ 802321964Ssjg return error; 803236769Sobrien} 804236769Sobrien 805321964Ssjgint 806236769Sobrienmeta_job_finish(Job *job) 807236769Sobrien{ 808236769Sobrien BuildMon *pbm; 809321964Ssjg int error = 0; 810321964Ssjg int x; 811236769Sobrien 812236769Sobrien if (job != NULL) { 813236769Sobrien pbm = &job->bm; 814236769Sobrien } else { 815236769Sobrien pbm = &Mybm; 816236769Sobrien } 817236769Sobrien if (pbm->mfp != NULL) { 818321964Ssjg error = meta_cmd_finish(pbm); 819321964Ssjg x = fclose(pbm->mfp); 820321964Ssjg if (error == 0 && x != 0) 821321964Ssjg error = errno; 822236769Sobrien pbm->mfp = NULL; 823236769Sobrien pbm->meta_fname[0] = '\0'; 824236769Sobrien } 825321964Ssjg return error; 826236769Sobrien} 827236769Sobrien 828321964Ssjgvoid 829321964Ssjgmeta_finish(void) 830321964Ssjg{ 831321964Ssjg Lst_Destroy(metaBailiwick, NULL); 832321964Ssjg free(metaBailiwickStr); 833321964Ssjg Lst_Destroy(metaIgnorePaths, NULL); 834321964Ssjg free(metaIgnorePathsStr); 835321964Ssjg} 836321964Ssjg 837236769Sobrien/* 838236769Sobrien * Fetch a full line from fp - growing bufp if needed 839236769Sobrien * Return length in bufp. 840236769Sobrien */ 841236769Sobrienstatic int 842236769SobrienfgetLine(char **bufp, size_t *szp, int o, FILE *fp) 843236769Sobrien{ 844236769Sobrien char *buf = *bufp; 845236769Sobrien size_t bufsz = *szp; 846236769Sobrien struct stat fs; 847236769Sobrien int x; 848236769Sobrien 849236769Sobrien if (fgets(&buf[o], bufsz - o, fp) != NULL) { 850236769Sobrien check_newline: 851236769Sobrien x = o + strlen(&buf[o]); 852236769Sobrien if (buf[x - 1] == '\n') 853236769Sobrien return x; 854236769Sobrien /* 855236769Sobrien * We need to grow the buffer. 856236769Sobrien * The meta file can give us a clue. 857236769Sobrien */ 858236769Sobrien if (fstat(fileno(fp), &fs) == 0) { 859236769Sobrien size_t newsz; 860236769Sobrien char *p; 861236769Sobrien 862236769Sobrien newsz = ROUNDUP((fs.st_size / 2), BUFSIZ); 863236769Sobrien if (newsz <= bufsz) 864236769Sobrien newsz = ROUNDUP(fs.st_size, BUFSIZ); 865321964Ssjg if (newsz <= bufsz) 866321964Ssjg return x; /* truncated */ 867236769Sobrien if (DEBUG(META)) 868236769Sobrien fprintf(debug_file, "growing buffer %u -> %u\n", 869236769Sobrien (unsigned)bufsz, (unsigned)newsz); 870236769Sobrien p = bmake_realloc(buf, newsz); 871236769Sobrien if (p) { 872236769Sobrien *bufp = buf = p; 873236769Sobrien *szp = bufsz = newsz; 874236769Sobrien /* fetch the rest */ 875236769Sobrien if (!fgets(&buf[x], bufsz - x, fp)) 876236769Sobrien return x; /* truncated! */ 877236769Sobrien goto check_newline; 878236769Sobrien } 879236769Sobrien } 880236769Sobrien } 881236769Sobrien return 0; 882236769Sobrien} 883236769Sobrien 884321964Ssjg/* Lst_ForEach wants 1 to stop search */ 885236769Sobrienstatic int 886236769Sobrienprefix_match(void *p, void *q) 887236769Sobrien{ 888236769Sobrien const char *prefix = p; 889236769Sobrien const char *path = q; 890236769Sobrien size_t n = strlen(prefix); 891236769Sobrien 892236769Sobrien return (0 == strncmp(path, prefix, n)); 893236769Sobrien} 894236769Sobrien 895321964Ssjg/* 896321964Ssjg * looking for exact or prefix/ match to 897321964Ssjg * Lst_Find wants 0 to stop search 898321964Ssjg */ 899236769Sobrienstatic int 900321964Ssjgpath_match(const void *p, const void *q) 901321964Ssjg{ 902321964Ssjg const char *prefix = q; 903321964Ssjg const char *path = p; 904321964Ssjg size_t n = strlen(prefix); 905321964Ssjg int rc; 906321964Ssjg 907321964Ssjg if ((rc = strncmp(path, prefix, n)) == 0) { 908321964Ssjg switch (path[n]) { 909321964Ssjg case '\0': 910321964Ssjg case '/': 911321964Ssjg break; 912321964Ssjg default: 913321964Ssjg rc = 1; 914321964Ssjg break; 915321964Ssjg } 916321964Ssjg } 917321964Ssjg return rc; 918321964Ssjg} 919321964Ssjg 920321964Ssjg/* Lst_Find wants 0 to stop search */ 921321964Ssjgstatic int 922236769Sobrienstring_match(const void *p, const void *q) 923236769Sobrien{ 924236769Sobrien const char *p1 = p; 925236769Sobrien const char *p2 = q; 926236769Sobrien 927236769Sobrien return strcmp(p1, p2); 928236769Sobrien} 929236769Sobrien 930236769Sobrien 931321964Ssjgstatic int 932321964Ssjgmeta_ignore(GNode *gn, const char *p) 933321964Ssjg{ 934321964Ssjg char fname[MAXPATHLEN]; 935321964Ssjg 936321964Ssjg if (p == NULL) 937321964Ssjg return TRUE; 938321964Ssjg 939321964Ssjg if (*p == '/') { 940321964Ssjg cached_realpath(p, fname); /* clean it up */ 941321964Ssjg if (Lst_ForEach(metaIgnorePaths, prefix_match, fname)) { 942321964Ssjg#ifdef DEBUG_META_MODE 943321964Ssjg if (DEBUG(META)) 944321964Ssjg fprintf(debug_file, "meta_oodate: ignoring path: %s\n", 945321964Ssjg p); 946321964Ssjg#endif 947321964Ssjg return TRUE; 948321964Ssjg } 949321964Ssjg } 950321964Ssjg 951321964Ssjg if (metaIgnorePatterns) { 952321964Ssjg char *pm; 953321964Ssjg 954321964Ssjg Var_Set(".p.", p, gn, 0); 955321964Ssjg pm = Var_Subst(NULL, 956321964Ssjg "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}", 957321964Ssjg gn, VARF_WANTRES); 958321964Ssjg if (*pm) { 959321964Ssjg#ifdef DEBUG_META_MODE 960321964Ssjg if (DEBUG(META)) 961321964Ssjg fprintf(debug_file, "meta_oodate: ignoring pattern: %s\n", 962321964Ssjg p); 963321964Ssjg#endif 964321964Ssjg free(pm); 965321964Ssjg return TRUE; 966321964Ssjg } 967321964Ssjg free(pm); 968321964Ssjg } 969321964Ssjg 970321964Ssjg if (metaIgnoreFilter) { 971321964Ssjg char *fm; 972321964Ssjg 973321964Ssjg /* skip if filter result is empty */ 974321964Ssjg snprintf(fname, sizeof(fname), 975321964Ssjg "${%s:L:${%s:ts:}}", 976321964Ssjg p, MAKE_META_IGNORE_FILTER); 977321964Ssjg fm = Var_Subst(NULL, fname, gn, VARF_WANTRES); 978321964Ssjg if (*fm == '\0') { 979321964Ssjg#ifdef DEBUG_META_MODE 980321964Ssjg if (DEBUG(META)) 981321964Ssjg fprintf(debug_file, "meta_oodate: ignoring filtered: %s\n", 982321964Ssjg p); 983321964Ssjg#endif 984321964Ssjg free(fm); 985321964Ssjg return TRUE; 986321964Ssjg } 987321964Ssjg free(fm); 988321964Ssjg } 989321964Ssjg return FALSE; 990321964Ssjg} 991321964Ssjg 992236769Sobrien/* 993236769Sobrien * When running with 'meta' functionality, a target can be out-of-date 994292068Ssjg * if any of the references in its meta data file is more recent. 995236769Sobrien * We have to track the latestdir on a per-process basis. 996236769Sobrien */ 997292068Ssjg#define LCWD_VNAME_FMT ".meta.%d.lcwd" 998236769Sobrien#define LDIR_VNAME_FMT ".meta.%d.ldir" 999236769Sobrien 1000236769Sobrien/* 1001236769Sobrien * It is possible that a .meta file is corrupted, 1002236769Sobrien * if we detect this we want to reproduce it. 1003236769Sobrien * Setting oodate TRUE will have that effect. 1004236769Sobrien */ 1005236769Sobrien#define CHECK_VALID_META(p) if (!(p && *p)) { \ 1006236769Sobrien warnx("%s: %d: malformed", fname, lineno); \ 1007236769Sobrien oodate = TRUE; \ 1008236769Sobrien continue; \ 1009236769Sobrien } 1010236769Sobrien 1011276305Sngie#define DEQUOTE(p) if (*p == '\'') { \ 1012276305Sngie char *ep; \ 1013276305Sngie p++; \ 1014276305Sngie if ((ep = strchr(p, '\''))) \ 1015276305Sngie *ep = '\0'; \ 1016276305Sngie } 1017276305Sngie 1018236769SobrienBoolean 1019236769Sobrienmeta_oodate(GNode *gn, Boolean oodate) 1020236769Sobrien{ 1021236769Sobrien static char *tmpdir = NULL; 1022236769Sobrien static char cwd[MAXPATHLEN]; 1023292068Ssjg char lcwd_vname[64]; 1024236769Sobrien char ldir_vname[64]; 1025292068Ssjg char lcwd[MAXPATHLEN]; 1026236769Sobrien char latestdir[MAXPATHLEN]; 1027236769Sobrien char fname[MAXPATHLEN]; 1028236769Sobrien char fname1[MAXPATHLEN]; 1029236769Sobrien char fname2[MAXPATHLEN]; 1030292068Ssjg char fname3[MAXPATHLEN]; 1031321964Ssjg const char *dname; 1032321964Ssjg const char *tname; 1033236769Sobrien char *p; 1034236769Sobrien char *cp; 1035276305Sngie char *link_src; 1036276305Sngie char *move_target; 1037236769Sobrien static size_t cwdlen = 0; 1038236769Sobrien static size_t tmplen = 0; 1039236769Sobrien FILE *fp; 1040246223Ssjg Boolean needOODATE = FALSE; 1041236769Sobrien Lst missingFiles; 1042321964Ssjg char *pa[4]; /* >= possible uses */ 1043321964Ssjg int i; 1044321964Ssjg int have_filemon = FALSE; 1045321964Ssjg 1046236769Sobrien if (oodate) 1047236769Sobrien return oodate; /* we're done */ 1048236769Sobrien 1049321964Ssjg i = 0; 1050321964Ssjg 1051321964Ssjg dname = Var_Value(".OBJDIR", gn, &pa[i++]); 1052321964Ssjg tname = Var_Value(TARGET, gn, &pa[i++]); 1053321964Ssjg 1054321964Ssjg /* if this succeeds fname3 is realpath of dname */ 1055321964Ssjg if (!meta_needed(gn, dname, fname3, FALSE)) 1056321964Ssjg goto oodate_out; 1057321964Ssjg dname = fname3; 1058321964Ssjg 1059236769Sobrien missingFiles = Lst_Init(FALSE); 1060236769Sobrien 1061236769Sobrien /* 1062236769Sobrien * We need to check if the target is out-of-date. This includes 1063236769Sobrien * checking if the expanded command has changed. This in turn 1064236769Sobrien * requires that all variables are set in the same way that they 1065236769Sobrien * would be if the target needs to be re-built. 1066236769Sobrien */ 1067236769Sobrien Make_DoAllVar(gn); 1068236769Sobrien 1069321964Ssjg meta_name(fname, sizeof(fname), dname, tname, dname); 1070236769Sobrien 1071236769Sobrien#ifdef DEBUG_META_MODE 1072236769Sobrien if (DEBUG(META)) 1073236769Sobrien fprintf(debug_file, "meta_oodate: %s\n", fname); 1074236769Sobrien#endif 1075236769Sobrien 1076236769Sobrien if ((fp = fopen(fname, "r")) != NULL) { 1077236769Sobrien static char *buf = NULL; 1078236769Sobrien static size_t bufsz; 1079236769Sobrien int lineno = 0; 1080236769Sobrien int lastpid = 0; 1081236769Sobrien int pid; 1082236769Sobrien int x; 1083236769Sobrien LstNode ln; 1084236769Sobrien struct stat fs; 1085236769Sobrien 1086236769Sobrien if (!buf) { 1087236769Sobrien bufsz = 8 * BUFSIZ; 1088236769Sobrien buf = bmake_malloc(bufsz); 1089236769Sobrien } 1090236769Sobrien 1091236769Sobrien if (!cwdlen) { 1092236769Sobrien if (getcwd(cwd, sizeof(cwd)) == NULL) 1093236769Sobrien err(1, "Could not get current working directory"); 1094236769Sobrien cwdlen = strlen(cwd); 1095236769Sobrien } 1096292068Ssjg strlcpy(lcwd, cwd, sizeof(lcwd)); 1097292068Ssjg strlcpy(latestdir, cwd, sizeof(latestdir)); 1098236769Sobrien 1099236769Sobrien if (!tmpdir) { 1100236769Sobrien tmpdir = getTmpdir(); 1101236769Sobrien tmplen = strlen(tmpdir); 1102236769Sobrien } 1103236769Sobrien 1104236769Sobrien /* we want to track all the .meta we read */ 1105236769Sobrien Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 1106236769Sobrien 1107236769Sobrien ln = Lst_First(gn->commands); 1108236769Sobrien while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { 1109236769Sobrien lineno++; 1110236769Sobrien if (buf[x - 1] == '\n') 1111236769Sobrien buf[x - 1] = '\0'; 1112236769Sobrien else { 1113236769Sobrien warnx("%s: %d: line truncated at %u", fname, lineno, x); 1114236769Sobrien oodate = TRUE; 1115236769Sobrien break; 1116236769Sobrien } 1117276305Sngie link_src = NULL; 1118276305Sngie move_target = NULL; 1119236769Sobrien /* Find the start of the build monitor section. */ 1120321964Ssjg if (!have_filemon) { 1121236769Sobrien if (strncmp(buf, "-- filemon", 10) == 0) { 1122321964Ssjg have_filemon = TRUE; 1123236769Sobrien continue; 1124236769Sobrien } 1125236769Sobrien if (strncmp(buf, "# buildmon", 10) == 0) { 1126321964Ssjg have_filemon = TRUE; 1127236769Sobrien continue; 1128236769Sobrien } 1129236769Sobrien } 1130236769Sobrien 1131236769Sobrien /* Delimit the record type. */ 1132236769Sobrien p = buf; 1133236769Sobrien#ifdef DEBUG_META_MODE 1134236769Sobrien if (DEBUG(META)) 1135236769Sobrien fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); 1136236769Sobrien#endif 1137236769Sobrien strsep(&p, " "); 1138321964Ssjg if (have_filemon) { 1139236769Sobrien /* 1140236769Sobrien * We are in the 'filemon' output section. 1141236769Sobrien * Each record from filemon follows the general form: 1142236769Sobrien * 1143236769Sobrien * <key> <pid> <data> 1144236769Sobrien * 1145236769Sobrien * Where: 1146236769Sobrien * <key> is a single letter, denoting the syscall. 1147236769Sobrien * <pid> is the process that made the syscall. 1148236769Sobrien * <data> is the arguments (of interest). 1149236769Sobrien */ 1150236769Sobrien switch(buf[0]) { 1151236769Sobrien case '#': /* comment */ 1152236769Sobrien case 'V': /* version */ 1153236769Sobrien break; 1154236769Sobrien default: 1155236769Sobrien /* 1156236769Sobrien * We need to track pathnames per-process. 1157236769Sobrien * 1158236769Sobrien * Each process run by make, starts off in the 'CWD' 1159236769Sobrien * recorded in the .meta file, if it chdirs ('C') 1160236769Sobrien * elsewhere we need to track that - but only for 1161236769Sobrien * that process. If it forks ('F'), we initialize 1162236769Sobrien * the child to have the same cwd as its parent. 1163236769Sobrien * 1164236769Sobrien * We also need to track the 'latestdir' of 1165236769Sobrien * interest. This is usually the same as cwd, but 1166236769Sobrien * not if a process is reading directories. 1167236769Sobrien * 1168236769Sobrien * Each time we spot a different process ('pid') 1169236769Sobrien * we save the current value of 'latestdir' in a 1170236769Sobrien * variable qualified by 'lastpid', and 1171236769Sobrien * re-initialize 'latestdir' to any pre-saved 1172236769Sobrien * value for the current 'pid' and 'CWD' if none. 1173236769Sobrien */ 1174236769Sobrien CHECK_VALID_META(p); 1175236769Sobrien pid = atoi(p); 1176236769Sobrien if (pid > 0 && pid != lastpid) { 1177236769Sobrien char *ldir; 1178236769Sobrien char *tp; 1179236769Sobrien 1180236769Sobrien if (lastpid > 0) { 1181292068Ssjg /* We need to remember these. */ 1182292068Ssjg Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0); 1183236769Sobrien Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0); 1184236769Sobrien } 1185292068Ssjg snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid); 1186236769Sobrien snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); 1187236769Sobrien lastpid = pid; 1188236769Sobrien ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); 1189236769Sobrien if (ldir) { 1190236769Sobrien strlcpy(latestdir, ldir, sizeof(latestdir)); 1191321964Ssjg free(tp); 1192292068Ssjg } 1193292068Ssjg ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp); 1194292068Ssjg if (ldir) { 1195292068Ssjg strlcpy(lcwd, ldir, sizeof(lcwd)); 1196321964Ssjg free(tp); 1197292068Ssjg } 1198236769Sobrien } 1199236769Sobrien /* Skip past the pid. */ 1200236769Sobrien if (strsep(&p, " ") == NULL) 1201236769Sobrien continue; 1202236769Sobrien#ifdef DEBUG_META_MODE 1203236769Sobrien if (DEBUG(META)) 1204292068Ssjg fprintf(debug_file, "%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n", 1205292068Ssjg fname, lineno, 1206292068Ssjg pid, buf[0], cwd, lcwd, latestdir); 1207236769Sobrien#endif 1208236769Sobrien break; 1209236769Sobrien } 1210236769Sobrien 1211236769Sobrien CHECK_VALID_META(p); 1212236769Sobrien 1213236769Sobrien /* Process according to record type. */ 1214236769Sobrien switch (buf[0]) { 1215236769Sobrien case 'X': /* eXit */ 1216292068Ssjg Var_Delete(lcwd_vname, VAR_GLOBAL); 1217236769Sobrien Var_Delete(ldir_vname, VAR_GLOBAL); 1218236769Sobrien lastpid = 0; /* no need to save ldir_vname */ 1219236769Sobrien break; 1220236769Sobrien 1221236769Sobrien case 'F': /* [v]Fork */ 1222236769Sobrien { 1223236769Sobrien char cldir[64]; 1224236769Sobrien int child; 1225236769Sobrien 1226236769Sobrien child = atoi(p); 1227236769Sobrien if (child > 0) { 1228292068Ssjg snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child); 1229292068Ssjg Var_Set(cldir, lcwd, VAR_GLOBAL, 0); 1230236769Sobrien snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); 1231236769Sobrien Var_Set(cldir, latestdir, VAR_GLOBAL, 0); 1232292068Ssjg#ifdef DEBUG_META_MODE 1233292068Ssjg if (DEBUG(META)) 1234292068Ssjg fprintf(debug_file, "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n", 1235292068Ssjg fname, lineno, 1236292068Ssjg child, cwd, lcwd, latestdir); 1237292068Ssjg#endif 1238236769Sobrien } 1239236769Sobrien } 1240236769Sobrien break; 1241236769Sobrien 1242236769Sobrien case 'C': /* Chdir */ 1243292068Ssjg /* Update lcwd and latest directory. */ 1244292068Ssjg strlcpy(latestdir, p, sizeof(latestdir)); 1245292068Ssjg strlcpy(lcwd, p, sizeof(lcwd)); 1246292068Ssjg Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0); 1247292068Ssjg Var_Set(ldir_vname, lcwd, VAR_GLOBAL, 0); 1248292068Ssjg#ifdef DEBUG_META_MODE 1249292068Ssjg if (DEBUG(META)) 1250292068Ssjg fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd); 1251292068Ssjg#endif 1252236769Sobrien break; 1253236769Sobrien 1254236769Sobrien case 'M': /* renaMe */ 1255276305Sngie /* 1256276305Sngie * For 'M'oves we want to check 1257276305Sngie * the src as for 'R'ead 1258276305Sngie * and the target as for 'W'rite. 1259276305Sngie */ 1260276305Sngie cp = p; /* save this for a second */ 1261276305Sngie /* now get target */ 1262276305Sngie if (strsep(&p, " ") == NULL) 1263276305Sngie continue; 1264276305Sngie CHECK_VALID_META(p); 1265276305Sngie move_target = p; 1266276305Sngie p = cp; 1267236769Sobrien /* 'L' and 'M' put single quotes around the args */ 1268276305Sngie DEQUOTE(p); 1269276305Sngie DEQUOTE(move_target); 1270236769Sobrien /* FALLTHROUGH */ 1271236769Sobrien case 'D': /* unlink */ 1272236769Sobrien if (*p == '/' && !Lst_IsEmpty(missingFiles)) { 1273321964Ssjg /* remove any missingFiles entries that match p */ 1274321964Ssjg if ((ln = Lst_Find(missingFiles, p, 1275321964Ssjg path_match)) != NULL) { 1276321964Ssjg LstNode nln; 1277321964Ssjg char *tp; 1278321964Ssjg 1279321964Ssjg do { 1280321964Ssjg nln = Lst_FindFrom(missingFiles, Lst_Succ(ln), 1281321964Ssjg p, path_match); 1282321964Ssjg tp = Lst_Datum(ln); 1283321964Ssjg Lst_Remove(missingFiles, ln); 1284321964Ssjg free(tp); 1285321964Ssjg } while ((ln = nln) != NULL); 1286236769Sobrien } 1287236769Sobrien } 1288276305Sngie if (buf[0] == 'M') { 1289276305Sngie /* the target of the mv is a file 'W'ritten */ 1290276305Sngie#ifdef DEBUG_META_MODE 1291276305Sngie if (DEBUG(META)) 1292276305Sngie fprintf(debug_file, "meta_oodate: M %s -> %s\n", 1293276305Sngie p, move_target); 1294276305Sngie#endif 1295276305Sngie p = move_target; 1296276305Sngie goto check_write; 1297276305Sngie } 1298236769Sobrien break; 1299236769Sobrien case 'L': /* Link */ 1300276305Sngie /* 1301276305Sngie * For 'L'inks check 1302276305Sngie * the src as for 'R'ead 1303276305Sngie * and the target as for 'W'rite. 1304276305Sngie */ 1305276305Sngie link_src = p; 1306276305Sngie /* now get target */ 1307236769Sobrien if (strsep(&p, " ") == NULL) 1308236769Sobrien continue; 1309236769Sobrien CHECK_VALID_META(p); 1310236769Sobrien /* 'L' and 'M' put single quotes around the args */ 1311276305Sngie DEQUOTE(p); 1312276305Sngie DEQUOTE(link_src); 1313276305Sngie#ifdef DEBUG_META_MODE 1314276305Sngie if (DEBUG(META)) 1315276305Sngie fprintf(debug_file, "meta_oodate: L %s -> %s\n", 1316276305Sngie link_src, p); 1317276305Sngie#endif 1318236769Sobrien /* FALLTHROUGH */ 1319236769Sobrien case 'W': /* Write */ 1320276305Sngie check_write: 1321236769Sobrien /* 1322236769Sobrien * If a file we generated within our bailiwick 1323236769Sobrien * but outside of .OBJDIR is missing, 1324236769Sobrien * we need to do it again. 1325236769Sobrien */ 1326236769Sobrien /* ignore non-absolute paths */ 1327236769Sobrien if (*p != '/') 1328236769Sobrien break; 1329236769Sobrien 1330236769Sobrien if (Lst_IsEmpty(metaBailiwick)) 1331236769Sobrien break; 1332236769Sobrien 1333236769Sobrien /* ignore cwd - normal dependencies handle those */ 1334236769Sobrien if (strncmp(p, cwd, cwdlen) == 0) 1335236769Sobrien break; 1336236769Sobrien 1337236769Sobrien if (!Lst_ForEach(metaBailiwick, prefix_match, p)) 1338236769Sobrien break; 1339236769Sobrien 1340236769Sobrien /* tmpdir might be within */ 1341236769Sobrien if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) 1342236769Sobrien break; 1343236769Sobrien 1344236769Sobrien /* ignore anything containing the string "tmp" */ 1345236769Sobrien if ((strstr("tmp", p))) 1346236769Sobrien break; 1347236769Sobrien 1348321964Ssjg if ((link_src != NULL && cached_lstat(p, &fs) < 0) || 1349321964Ssjg (link_src == NULL && cached_stat(p, &fs) < 0)) { 1350321964Ssjg if (!meta_ignore(gn, p)) { 1351321964Ssjg if (Lst_Find(missingFiles, p, string_match) == NULL) 1352321964Ssjg Lst_AtEnd(missingFiles, bmake_strdup(p)); 1353321964Ssjg } 1354236769Sobrien } 1355236769Sobrien break; 1356276305Sngie check_link_src: 1357276305Sngie p = link_src; 1358276305Sngie link_src = NULL; 1359276305Sngie#ifdef DEBUG_META_MODE 1360276305Sngie if (DEBUG(META)) 1361276305Sngie fprintf(debug_file, "meta_oodate: L src %s\n", p); 1362276305Sngie#endif 1363276305Sngie /* FALLTHROUGH */ 1364236769Sobrien case 'R': /* Read */ 1365236769Sobrien case 'E': /* Exec */ 1366236769Sobrien /* 1367236769Sobrien * Check for runtime files that can't 1368236769Sobrien * be part of the dependencies because 1369236769Sobrien * they are _expected_ to change. 1370236769Sobrien */ 1371321964Ssjg if (meta_ignore(gn, p)) 1372236769Sobrien break; 1373321964Ssjg 1374236769Sobrien /* 1375236769Sobrien * The rest of the record is the file name. 1376236769Sobrien * Check if it's not an absolute path. 1377236769Sobrien */ 1378236769Sobrien { 1379236769Sobrien char *sdirs[4]; 1380236769Sobrien char **sdp; 1381236769Sobrien int sdx = 0; 1382236769Sobrien int found = 0; 1383236769Sobrien 1384236769Sobrien if (*p == '/') { 1385236769Sobrien sdirs[sdx++] = p; /* done */ 1386236769Sobrien } else { 1387236769Sobrien if (strcmp(".", p) == 0) 1388236769Sobrien continue; /* no point */ 1389236769Sobrien 1390236769Sobrien /* Check vs latestdir */ 1391236769Sobrien snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); 1392236769Sobrien sdirs[sdx++] = fname1; 1393236769Sobrien 1394292068Ssjg if (strcmp(latestdir, lcwd) != 0) { 1395292068Ssjg /* Check vs lcwd */ 1396292068Ssjg snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p); 1397236769Sobrien sdirs[sdx++] = fname2; 1398236769Sobrien } 1399292068Ssjg if (strcmp(lcwd, cwd) != 0) { 1400292068Ssjg /* Check vs cwd */ 1401292068Ssjg snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p); 1402292068Ssjg sdirs[sdx++] = fname3; 1403292068Ssjg } 1404236769Sobrien } 1405236769Sobrien sdirs[sdx++] = NULL; 1406236769Sobrien 1407236769Sobrien for (sdp = sdirs; *sdp && !found; sdp++) { 1408236769Sobrien#ifdef DEBUG_META_MODE 1409236769Sobrien if (DEBUG(META)) 1410236769Sobrien fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); 1411236769Sobrien#endif 1412321964Ssjg if (cached_stat(*sdp, &fs) == 0) { 1413236769Sobrien found = 1; 1414236769Sobrien p = *sdp; 1415236769Sobrien } 1416236769Sobrien } 1417236769Sobrien if (found) { 1418236769Sobrien#ifdef DEBUG_META_MODE 1419236769Sobrien if (DEBUG(META)) 1420236769Sobrien fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); 1421236769Sobrien#endif 1422236769Sobrien if (!S_ISDIR(fs.st_mode) && 1423236769Sobrien fs.st_mtime > gn->mtime) { 1424236769Sobrien if (DEBUG(META)) 1425236769Sobrien fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); 1426236769Sobrien oodate = TRUE; 1427236769Sobrien } else if (S_ISDIR(fs.st_mode)) { 1428236769Sobrien /* Update the latest directory. */ 1429321964Ssjg cached_realpath(p, latestdir); 1430236769Sobrien } 1431236769Sobrien } else if (errno == ENOENT && *p == '/' && 1432236769Sobrien strncmp(p, cwd, cwdlen) != 0) { 1433236769Sobrien /* 1434236769Sobrien * A referenced file outside of CWD is missing. 1435236769Sobrien * We cannot catch every eventuality here... 1436236769Sobrien */ 1437321964Ssjg if (Lst_Find(missingFiles, p, string_match) == NULL) 1438321964Ssjg Lst_AtEnd(missingFiles, bmake_strdup(p)); 1439236769Sobrien } 1440236769Sobrien } 1441292068Ssjg if (buf[0] == 'E') { 1442292068Ssjg /* previous latestdir is no longer relevant */ 1443292068Ssjg strlcpy(latestdir, lcwd, sizeof(latestdir)); 1444292068Ssjg } 1445236769Sobrien break; 1446236769Sobrien default: 1447236769Sobrien break; 1448236769Sobrien } 1449276305Sngie if (!oodate && buf[0] == 'L' && link_src != NULL) 1450276305Sngie goto check_link_src; 1451236769Sobrien } else if (strcmp(buf, "CMD") == 0) { 1452236769Sobrien /* 1453236769Sobrien * Compare the current command with the one in the 1454236769Sobrien * meta data file. 1455236769Sobrien */ 1456236769Sobrien if (ln == NULL) { 1457236769Sobrien if (DEBUG(META)) 1458236769Sobrien fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); 1459236769Sobrien oodate = TRUE; 1460236769Sobrien } else { 1461236769Sobrien char *cmd = (char *)Lst_Datum(ln); 1462249033Ssjg Boolean hasOODATE = FALSE; 1463236769Sobrien 1464249033Ssjg if (strstr(cmd, "$?")) 1465249033Ssjg hasOODATE = TRUE; 1466249033Ssjg else if ((cp = strstr(cmd, ".OODATE"))) { 1467249033Ssjg /* check for $[{(].OODATE[:)}] */ 1468249033Ssjg if (cp > cmd + 2 && cp[-2] == '$') 1469249033Ssjg hasOODATE = TRUE; 1470236769Sobrien } 1471249033Ssjg if (hasOODATE) { 1472249033Ssjg needOODATE = TRUE; 1473249033Ssjg if (DEBUG(META)) 1474249033Ssjg fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno); 1475249033Ssjg } 1476321964Ssjg cmd = Var_Subst(NULL, cmd, gn, VARF_WANTRES|VARF_UNDEFERR); 1477236769Sobrien 1478236769Sobrien if ((cp = strchr(cmd, '\n'))) { 1479236769Sobrien int n; 1480236769Sobrien 1481236769Sobrien /* 1482236769Sobrien * This command contains newlines, we need to 1483236769Sobrien * fetch more from the .meta file before we 1484236769Sobrien * attempt a comparison. 1485236769Sobrien */ 1486236769Sobrien /* first put the newline back at buf[x - 1] */ 1487236769Sobrien buf[x - 1] = '\n'; 1488236769Sobrien do { 1489236769Sobrien /* now fetch the next line */ 1490236769Sobrien if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) 1491236769Sobrien break; 1492236769Sobrien x = n; 1493236769Sobrien lineno++; 1494236769Sobrien if (buf[x - 1] != '\n') { 1495236769Sobrien warnx("%s: %d: line truncated at %u", fname, lineno, x); 1496236769Sobrien break; 1497236769Sobrien } 1498236769Sobrien cp = strchr(++cp, '\n'); 1499236769Sobrien } while (cp); 1500236769Sobrien if (buf[x - 1] == '\n') 1501236769Sobrien buf[x - 1] = '\0'; 1502236769Sobrien } 1503249033Ssjg if (!hasOODATE && 1504236769Sobrien !(gn->type & OP_NOMETA_CMP) && 1505236769Sobrien strcmp(p, cmd) != 0) { 1506236769Sobrien if (DEBUG(META)) 1507236769Sobrien fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); 1508236769Sobrien if (!metaIgnoreCMDs) 1509236769Sobrien oodate = TRUE; 1510236769Sobrien } 1511236769Sobrien free(cmd); 1512236769Sobrien ln = Lst_Succ(ln); 1513236769Sobrien } 1514236769Sobrien } else if (strcmp(buf, "CWD") == 0) { 1515236769Sobrien /* 1516236769Sobrien * Check if there are extra commands now 1517236769Sobrien * that weren't in the meta data file. 1518236769Sobrien */ 1519236769Sobrien if (!oodate && ln != NULL) { 1520236769Sobrien if (DEBUG(META)) 1521236769Sobrien fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno); 1522236769Sobrien oodate = TRUE; 1523236769Sobrien } 1524236769Sobrien if (strcmp(p, cwd) != 0) { 1525236769Sobrien if (DEBUG(META)) 1526236769Sobrien fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); 1527236769Sobrien oodate = TRUE; 1528236769Sobrien } 1529236769Sobrien } 1530236769Sobrien } 1531236769Sobrien 1532236769Sobrien fclose(fp); 1533236769Sobrien if (!Lst_IsEmpty(missingFiles)) { 1534236769Sobrien if (DEBUG(META)) 1535236769Sobrien fprintf(debug_file, "%s: missing files: %s...\n", 1536236769Sobrien fname, (char *)Lst_Datum(Lst_First(missingFiles))); 1537236769Sobrien oodate = TRUE; 1538236769Sobrien } 1539321964Ssjg if (!oodate && !have_filemon && filemonMissing) { 1540236769Sobrien if (DEBUG(META)) 1541321964Ssjg fprintf(debug_file, "%s: missing filemon data\n", fname); 1542236769Sobrien oodate = TRUE; 1543236769Sobrien } 1544321964Ssjg } else { 1545321964Ssjg if (writeMeta && metaMissing) { 1546321964Ssjg cp = NULL; 1547321964Ssjg 1548321964Ssjg /* if target is in .CURDIR we do not need a meta file */ 1549321964Ssjg if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) { 1550321964Ssjg if (strncmp(curdir, gn->path, (cp - gn->path)) != 0) { 1551321964Ssjg cp = NULL; /* not in .CURDIR */ 1552321964Ssjg } 1553321964Ssjg } 1554321964Ssjg if (!cp) { 1555321964Ssjg if (DEBUG(META)) 1556321964Ssjg fprintf(debug_file, "%s: required but missing\n", fname); 1557321964Ssjg oodate = TRUE; 1558321964Ssjg needOODATE = TRUE; /* assume the worst */ 1559321964Ssjg } 1560321964Ssjg } 1561236769Sobrien } 1562321964Ssjg 1563321964Ssjg Lst_Destroy(missingFiles, (FreeProc *)free); 1564321964Ssjg 1565246223Ssjg if (oodate && needOODATE) { 1566236769Sobrien /* 1567246223Ssjg * Target uses .OODATE which is empty; or we wouldn't be here. 1568246223Ssjg * We have decided it is oodate, so .OODATE needs to be set. 1569246223Ssjg * All we can sanely do is set it to .ALLSRC. 1570236769Sobrien */ 1571236769Sobrien Var_Delete(OODATE, gn); 1572246223Ssjg Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn, 0); 1573321964Ssjg free(cp); 1574236769Sobrien } 1575321964Ssjg 1576321964Ssjg oodate_out: 1577321964Ssjg for (i--; i >= 0; i--) { 1578321964Ssjg free(pa[i]); 1579321964Ssjg } 1580236769Sobrien return oodate; 1581236769Sobrien} 1582236769Sobrien 1583236769Sobrien/* support for compat mode */ 1584236769Sobrien 1585236769Sobrienstatic int childPipe[2]; 1586236769Sobrien 1587236769Sobrienvoid 1588236769Sobrienmeta_compat_start(void) 1589236769Sobrien{ 1590236769Sobrien#ifdef USE_FILEMON_ONCE 1591236769Sobrien /* 1592236769Sobrien * We need to re-open filemon for each cmd. 1593236769Sobrien */ 1594236769Sobrien BuildMon *pbm = &Mybm; 1595236769Sobrien 1596236769Sobrien if (pbm->mfp != NULL && useFilemon) { 1597236769Sobrien filemon_open(pbm); 1598236769Sobrien } else { 1599236769Sobrien pbm->mon_fd = pbm->filemon_fd = -1; 1600236769Sobrien } 1601236769Sobrien#endif 1602236769Sobrien if (pipe(childPipe) < 0) 1603236769Sobrien Punt("Cannot create pipe: %s", strerror(errno)); 1604236769Sobrien /* Set close-on-exec flag for both */ 1605321964Ssjg (void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC); 1606321964Ssjg (void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC); 1607236769Sobrien} 1608236769Sobrien 1609236769Sobrienvoid 1610236769Sobrienmeta_compat_child(void) 1611236769Sobrien{ 1612236769Sobrien meta_job_child(NULL); 1613236769Sobrien if (dup2(childPipe[1], 1) < 0 || 1614236769Sobrien dup2(1, 2) < 0) { 1615236769Sobrien execError("dup2", "pipe"); 1616236769Sobrien _exit(1); 1617236769Sobrien } 1618236769Sobrien} 1619236769Sobrien 1620236769Sobrienvoid 1621236769Sobrienmeta_compat_parent(void) 1622236769Sobrien{ 1623236769Sobrien FILE *fp; 1624236769Sobrien char buf[BUFSIZ]; 1625236769Sobrien 1626236769Sobrien close(childPipe[1]); /* child side */ 1627236769Sobrien fp = fdopen(childPipe[0], "r"); 1628236769Sobrien while (fgets(buf, sizeof(buf), fp)) { 1629236769Sobrien meta_job_output(NULL, buf, ""); 1630236769Sobrien printf("%s", buf); 1631321964Ssjg fflush(stdout); 1632236769Sobrien } 1633236769Sobrien fclose(fp); 1634236769Sobrien} 1635236769Sobrien 1636236769Sobrien#endif /* USE_META */ 1637