1253883Ssjg/* $NetBSD: meta.c,v 1.32 2013/06/25 00:20:54 sjg Exp $ */ 2236769Sobrien 3236769Sobrien/* 4236769Sobrien * Implement 'meta' mode. 5236769Sobrien * Adapted from John Birrell's patches to FreeBSD make. 6236769Sobrien * --sjg 7236769Sobrien */ 8236769Sobrien/* 9236769Sobrien * Copyright (c) 2009-2010, 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> 40236769Sobrien#include <fcntl.h> 41236769Sobrien#include <libgen.h> 42236769Sobrien#include <errno.h> 43236769Sobrien#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) 44236769Sobrien#include <err.h> 45236769Sobrien#endif 46236769Sobrien 47236769Sobrien#include "make.h" 48236769Sobrien#include "job.h" 49236769Sobrien 50236769Sobrien#ifdef HAVE_FILEMON_H 51236769Sobrien# include <filemon.h> 52236769Sobrien#endif 53236769Sobrien#if !defined(USE_FILEMON) && defined(FILEMON_SET_FD) 54236769Sobrien# define USE_FILEMON 55236769Sobrien#endif 56236769Sobrien 57236769Sobrienstatic BuildMon Mybm; /* for compat */ 58253883Ssjgstatic Lst metaBailiwick; /* our scope of control */ 59253883Ssjgstatic Lst metaIgnorePaths; /* paths we deliberately ignore */ 60236769Sobrien 61253883Ssjg#ifndef MAKE_META_IGNORE_PATHS 62253883Ssjg#define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS" 63253883Ssjg#endif 64253883Ssjg 65236769SobrienBoolean useMeta = FALSE; 66236769Sobrienstatic Boolean useFilemon = FALSE; 67236769Sobrienstatic Boolean writeMeta = FALSE; 68236769Sobrienstatic Boolean metaEnv = FALSE; /* don't save env unless asked */ 69236769Sobrienstatic Boolean metaVerbose = FALSE; 70236769Sobrienstatic Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ 71236769Sobrienstatic Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ 72236769Sobrienstatic Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */ 73236769Sobrien 74236769Sobrienextern Boolean forceJobs; 75236769Sobrienextern Boolean comatMake; 76238152Sobrienextern char **environ; 77236769Sobrien 78236769Sobrien#define MAKE_META_PREFIX ".MAKE.META.PREFIX" 79236769Sobrien 80236769Sobrien#ifndef N2U 81236769Sobrien# define N2U(n, u) (((n) + ((u) - 1)) / (u)) 82236769Sobrien#endif 83236769Sobrien#ifndef ROUNDUP 84236769Sobrien# define ROUNDUP(n, u) (N2U((n), (u)) * (u)) 85236769Sobrien#endif 86236769Sobrien 87236769Sobrien#if !defined(HAVE_STRSEP) 88236769Sobrien# define strsep(s, d) stresep((s), (d), 0) 89236769Sobrien#endif 90236769Sobrien 91236769Sobrien/* 92236769Sobrien * Filemon is a kernel module which snoops certain syscalls. 93236769Sobrien * 94236769Sobrien * C chdir 95236769Sobrien * E exec 96236769Sobrien * F [v]fork 97236769Sobrien * L [sym]link 98236769Sobrien * M rename 99236769Sobrien * R read 100236769Sobrien * W write 101236769Sobrien * S stat 102236769Sobrien * 103236769Sobrien * See meta_oodate below - we mainly care about 'E' and 'R'. 104236769Sobrien * 105236769Sobrien * We can still use meta mode without filemon, but 106236769Sobrien * the benefits are more limited. 107236769Sobrien */ 108236769Sobrien#ifdef USE_FILEMON 109236769Sobrien# ifndef _PATH_FILEMON 110236769Sobrien# define _PATH_FILEMON "/dev/filemon" 111236769Sobrien# endif 112236769Sobrien 113236769Sobrien/* 114236769Sobrien * Open the filemon device. 115236769Sobrien */ 116236769Sobrienstatic void 117236769Sobrienfilemon_open(BuildMon *pbm) 118236769Sobrien{ 119236769Sobrien int retry; 120236769Sobrien 121236769Sobrien pbm->mon_fd = pbm->filemon_fd = -1; 122236769Sobrien if (!useFilemon) 123236769Sobrien return; 124236769Sobrien 125236769Sobrien for (retry = 5; retry >= 0; retry--) { 126236769Sobrien if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0) 127236769Sobrien break; 128236769Sobrien } 129236769Sobrien 130236769Sobrien if (pbm->filemon_fd < 0) { 131236769Sobrien useFilemon = FALSE; 132236769Sobrien warn("Could not open %s", _PATH_FILEMON); 133236769Sobrien return; 134236769Sobrien } 135236769Sobrien 136236769Sobrien /* 137236769Sobrien * We use a file outside of '.' 138236769Sobrien * to avoid a FreeBSD kernel bug where unlink invalidates 139236769Sobrien * cwd causing getcwd to do a lot more work. 140236769Sobrien * We only care about the descriptor. 141236769Sobrien */ 142236769Sobrien pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); 143236769Sobrien if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) { 144236769Sobrien err(1, "Could not set filemon file descriptor!"); 145236769Sobrien } 146236769Sobrien /* we don't need these once we exec */ 147236769Sobrien (void)fcntl(pbm->mon_fd, F_SETFD, 1); 148236769Sobrien (void)fcntl(pbm->filemon_fd, F_SETFD, 1); 149236769Sobrien} 150236769Sobrien 151236769Sobrien/* 152236769Sobrien * Read the build monitor output file and write records to the target's 153236769Sobrien * metadata file. 154236769Sobrien */ 155236769Sobrienstatic void 156236769Sobrienfilemon_read(FILE *mfp, int fd) 157236769Sobrien{ 158236769Sobrien FILE *fp; 159236769Sobrien char buf[BUFSIZ]; 160236769Sobrien 161236769Sobrien /* Check if we're not writing to a meta data file.*/ 162236769Sobrien if (mfp == NULL) { 163236769Sobrien if (fd >= 0) 164236769Sobrien close(fd); /* not interested */ 165236769Sobrien return; 166236769Sobrien } 167236769Sobrien /* rewind */ 168236769Sobrien (void)lseek(fd, (off_t)0, SEEK_SET); 169236769Sobrien if ((fp = fdopen(fd, "r")) == NULL) 170236769Sobrien err(1, "Could not read build monitor file '%d'", fd); 171236769Sobrien 172236769Sobrien fprintf(mfp, "-- filemon acquired metadata --\n"); 173236769Sobrien 174236769Sobrien while (fgets(buf, sizeof(buf), fp)) { 175236769Sobrien fprintf(mfp, "%s", buf); 176236769Sobrien } 177236769Sobrien fflush(mfp); 178236769Sobrien clearerr(fp); 179236769Sobrien fclose(fp); 180236769Sobrien} 181236769Sobrien#endif 182236769Sobrien 183236769Sobrien/* 184236769Sobrien * when realpath() fails, 185236769Sobrien * we use this, to clean up ./ and ../ 186236769Sobrien */ 187236769Sobrienstatic void 188236769Sobrieneat_dots(char *buf, size_t bufsz, int dots) 189236769Sobrien{ 190236769Sobrien char *cp; 191236769Sobrien char *cp2; 192236769Sobrien const char *eat; 193236769Sobrien size_t eatlen; 194236769Sobrien 195236769Sobrien switch (dots) { 196236769Sobrien case 1: 197236769Sobrien eat = "/./"; 198236769Sobrien eatlen = 2; 199236769Sobrien break; 200236769Sobrien case 2: 201236769Sobrien eat = "/../"; 202236769Sobrien eatlen = 3; 203236769Sobrien break; 204236769Sobrien default: 205236769Sobrien return; 206236769Sobrien } 207236769Sobrien 208236769Sobrien do { 209236769Sobrien cp = strstr(buf, eat); 210236769Sobrien if (cp) { 211236769Sobrien cp2 = cp + eatlen; 212236769Sobrien if (dots == 2 && cp > buf) { 213236769Sobrien do { 214236769Sobrien cp--; 215236769Sobrien } while (cp > buf && *cp != '/'); 216236769Sobrien } 217236769Sobrien if (*cp == '/') { 218236769Sobrien strlcpy(cp, cp2, bufsz - (cp - buf)); 219236769Sobrien } else { 220236769Sobrien return; /* can't happen? */ 221236769Sobrien } 222236769Sobrien } 223236769Sobrien } while (cp); 224236769Sobrien} 225236769Sobrien 226236769Sobrienstatic char * 227236769Sobrienmeta_name(struct GNode *gn, char *mname, size_t mnamelen, 228236769Sobrien const char *dname, 229236769Sobrien const char *tname) 230236769Sobrien{ 231236769Sobrien char buf[MAXPATHLEN]; 232236769Sobrien char cwd[MAXPATHLEN]; 233236769Sobrien char *rp; 234236769Sobrien char *cp; 235236769Sobrien char *tp; 236236769Sobrien char *p[4]; /* >= number of possible uses */ 237236769Sobrien int i; 238236769Sobrien 239236769Sobrien i = 0; 240236769Sobrien if (!dname) 241236769Sobrien dname = Var_Value(".OBJDIR", gn, &p[i++]); 242236769Sobrien if (!tname) 243236769Sobrien tname = Var_Value(TARGET, gn, &p[i++]); 244236769Sobrien 245236769Sobrien if (realpath(dname, cwd)) 246236769Sobrien dname = cwd; 247236769Sobrien 248236769Sobrien /* 249236769Sobrien * Weed out relative paths from the target file name. 250236769Sobrien * We have to be careful though since if target is a 251236769Sobrien * symlink, the result will be unstable. 252236769Sobrien * So we use realpath() just to get the dirname, and leave the 253236769Sobrien * basename as given to us. 254236769Sobrien */ 255236769Sobrien if ((cp = strrchr(tname, '/'))) { 256236769Sobrien if (realpath(tname, buf)) { 257236769Sobrien if ((rp = strrchr(buf, '/'))) { 258236769Sobrien rp++; 259236769Sobrien cp++; 260236769Sobrien if (strcmp(cp, rp) != 0) 261236769Sobrien strlcpy(rp, cp, sizeof(buf) - (rp - buf)); 262236769Sobrien } 263236769Sobrien tname = buf; 264236769Sobrien } else { 265236769Sobrien /* 266236769Sobrien * We likely have a directory which is about to be made. 267236769Sobrien * We pretend realpath() succeeded, to have a chance 268236769Sobrien * of generating the same meta file name that we will 269236769Sobrien * next time through. 270236769Sobrien */ 271236769Sobrien if (tname[0] == '/') { 272236769Sobrien strlcpy(buf, tname, sizeof(buf)); 273236769Sobrien } else { 274236769Sobrien snprintf(buf, sizeof(buf), "%s/%s", cwd, tname); 275236769Sobrien } 276236769Sobrien eat_dots(buf, sizeof(buf), 1); /* ./ */ 277236769Sobrien eat_dots(buf, sizeof(buf), 2); /* ../ */ 278236769Sobrien tname = buf; 279236769Sobrien } 280236769Sobrien } 281236769Sobrien /* on some systems dirname may modify its arg */ 282236769Sobrien tp = bmake_strdup(tname); 283236769Sobrien if (strcmp(dname, dirname(tp)) == 0) 284236769Sobrien snprintf(mname, mnamelen, "%s.meta", tname); 285236769Sobrien else { 286236769Sobrien snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); 287236769Sobrien 288236769Sobrien /* 289236769Sobrien * Replace path separators in the file name after the 290236769Sobrien * current object directory path. 291236769Sobrien */ 292236769Sobrien cp = mname + strlen(dname) + 1; 293236769Sobrien 294236769Sobrien while (*cp != '\0') { 295236769Sobrien if (*cp == '/') 296236769Sobrien *cp = '_'; 297236769Sobrien cp++; 298236769Sobrien } 299236769Sobrien } 300236769Sobrien free(tp); 301236769Sobrien for (i--; i >= 0; i--) { 302236769Sobrien if (p[i]) 303236769Sobrien free(p[i]); 304236769Sobrien } 305236769Sobrien return (mname); 306236769Sobrien} 307236769Sobrien 308236769Sobrien/* 309236769Sobrien * Return true if running ${.MAKE} 310236769Sobrien * Bypassed if target is flagged .MAKE 311236769Sobrien */ 312236769Sobrienstatic int 313236769Sobrienis_submake(void *cmdp, void *gnp) 314236769Sobrien{ 315236769Sobrien static char *p_make = NULL; 316236769Sobrien static int p_len; 317236769Sobrien char *cmd = cmdp; 318236769Sobrien GNode *gn = gnp; 319236769Sobrien char *mp = NULL; 320236769Sobrien char *cp; 321236769Sobrien char *cp2; 322236769Sobrien int rc = 0; /* keep looking */ 323236769Sobrien 324236769Sobrien if (!p_make) { 325236769Sobrien p_make = Var_Value(".MAKE", gn, &cp); 326236769Sobrien p_len = strlen(p_make); 327236769Sobrien } 328236769Sobrien cp = strchr(cmd, '$'); 329236769Sobrien if ((cp)) { 330236769Sobrien mp = Var_Subst(NULL, cmd, gn, FALSE); 331236769Sobrien cmd = mp; 332236769Sobrien } 333236769Sobrien cp2 = strstr(cmd, p_make); 334236769Sobrien if ((cp2)) { 335236769Sobrien switch (cp2[p_len]) { 336236769Sobrien case '\0': 337236769Sobrien case ' ': 338236769Sobrien case '\t': 339236769Sobrien case '\n': 340236769Sobrien rc = 1; 341236769Sobrien break; 342236769Sobrien } 343236769Sobrien if (cp2 > cmd && rc > 0) { 344236769Sobrien switch (cp2[-1]) { 345236769Sobrien case ' ': 346236769Sobrien case '\t': 347236769Sobrien case '\n': 348236769Sobrien break; 349236769Sobrien default: 350236769Sobrien rc = 0; /* no match */ 351236769Sobrien break; 352236769Sobrien } 353236769Sobrien } 354236769Sobrien } 355236769Sobrien if (mp) 356236769Sobrien free(mp); 357236769Sobrien return (rc); 358236769Sobrien} 359236769Sobrien 360236769Sobrientypedef struct meta_file_s { 361236769Sobrien FILE *fp; 362236769Sobrien GNode *gn; 363236769Sobrien} meta_file_t; 364236769Sobrien 365236769Sobrienstatic int 366236769SobrienprintCMD(void *cmdp, void *mfpp) 367236769Sobrien{ 368236769Sobrien meta_file_t *mfp = mfpp; 369236769Sobrien char *cmd = cmdp; 370236769Sobrien char *cp = NULL; 371236769Sobrien 372236769Sobrien if (strchr(cmd, '$')) { 373236769Sobrien cmd = cp = Var_Subst(NULL, cmd, mfp->gn, FALSE); 374236769Sobrien } 375236769Sobrien fprintf(mfp->fp, "CMD %s\n", cmd); 376236769Sobrien if (cp) 377236769Sobrien 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))) { \ 386236769Sobrien if (DEBUG(META)) { \ 387236769Sobrien fprintf(debug_file, "Skipping meta for %s: .%s\n", \ 388236769Sobrien gn->name, __STRING(_type)); \ 389236769Sobrien } \ 390236769Sobrien return (NULL); \ 391236769Sobrien } \ 392236769Sobrien} while (0) 393236769Sobrien 394236769Sobrienstatic FILE * 395236769Sobrienmeta_create(BuildMon *pbm, GNode *gn) 396236769Sobrien{ 397236769Sobrien meta_file_t mf; 398236769Sobrien char buf[MAXPATHLEN]; 399236769Sobrien char objdir[MAXPATHLEN]; 400236769Sobrien char **ptr; 401236769Sobrien const char *dname; 402236769Sobrien const char *tname; 403236769Sobrien char *fname; 404236769Sobrien const char *cp; 405236769Sobrien char *p[4]; /* >= possible uses */ 406236769Sobrien int i; 407236769Sobrien struct stat fs; 408236769Sobrien 409236769Sobrien 410236769Sobrien /* This may be a phony node which we don't want meta data for... */ 411236769Sobrien /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ 412236769Sobrien /* Or it may be explicitly flagged as .NOMETA */ 413236769Sobrien SKIP_META_TYPE(NOMETA); 414236769Sobrien /* Unless it is explicitly flagged as .META */ 415236769Sobrien if (!(gn->type & OP_META)) { 416236769Sobrien SKIP_META_TYPE(PHONY); 417236769Sobrien SKIP_META_TYPE(SPECIAL); 418236769Sobrien SKIP_META_TYPE(MAKE); 419236769Sobrien } 420236769Sobrien 421236769Sobrien mf.fp = NULL; 422236769Sobrien 423236769Sobrien i = 0; 424236769Sobrien 425236769Sobrien dname = Var_Value(".OBJDIR", gn, &p[i++]); 426236769Sobrien tname = Var_Value(TARGET, gn, &p[i++]); 427236769Sobrien 428236769Sobrien /* The object directory may not exist. Check it.. */ 429236769Sobrien if (stat(dname, &fs) != 0) { 430236769Sobrien if (DEBUG(META)) 431236769Sobrien fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", 432236769Sobrien gn->name); 433236769Sobrien goto out; 434236769Sobrien } 435236769Sobrien /* Check if there are no commands to execute. */ 436236769Sobrien if (Lst_IsEmpty(gn->commands)) { 437236769Sobrien if (DEBUG(META)) 438236769Sobrien fprintf(debug_file, "Skipping meta for %s: no commands\n", 439236769Sobrien gn->name); 440236769Sobrien goto out; 441236769Sobrien } 442236769Sobrien 443236769Sobrien /* make sure these are canonical */ 444236769Sobrien if (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) { 449236769Sobrien if (DEBUG(META)) 450236769Sobrien fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", 451236769Sobrien gn->name); 452236769Sobrien goto out; 453236769Sobrien } 454236769Sobrien if (!(gn->type & OP_META)) { 455236769Sobrien /* We do not generate .meta files for sub-makes */ 456236769Sobrien if (Lst_ForEach(gn->commands, is_submake, gn)) { 457236769Sobrien if (DEBUG(META)) 458236769Sobrien fprintf(debug_file, "Skipping meta for %s: .MAKE\n", 459236769Sobrien gn->name); 460236769Sobrien goto out; 461236769Sobrien } 462236769Sobrien } 463236769Sobrien 464236769Sobrien if (metaVerbose) { 465236769Sobrien char *mp; 466236769Sobrien 467236769Sobrien /* Describe the target we are building */ 468236769Sobrien mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, 0); 469236769Sobrien if (*mp) 470236769Sobrien fprintf(stdout, "%s\n", mp); 471236769Sobrien free(mp); 472236769Sobrien } 473236769Sobrien /* Get the basename of the target */ 474236769Sobrien if ((cp = strrchr(tname, '/')) == NULL) { 475236769Sobrien cp = tname; 476236769Sobrien } else { 477236769Sobrien cp++; 478236769Sobrien } 479236769Sobrien 480236769Sobrien fflush(stdout); 481236769Sobrien 482236769Sobrien if (strcmp(cp, makeDependfile) == 0) 483236769Sobrien goto out; 484236769Sobrien 485236769Sobrien if (!writeMeta) 486236769Sobrien /* Don't create meta data. */ 487236769Sobrien goto out; 488236769Sobrien 489236769Sobrien fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname), 490236769Sobrien dname, tname); 491236769Sobrien 492236769Sobrien#ifdef DEBUG_META_MODE 493236769Sobrien if (DEBUG(META)) 494236769Sobrien fprintf(debug_file, "meta_create: %s\n", fname); 495236769Sobrien#endif 496236769Sobrien 497236769Sobrien if ((mf.fp = fopen(fname, "w")) == NULL) 498236769Sobrien err(1, "Could not open meta file '%s'", fname); 499236769Sobrien 500236769Sobrien fprintf(mf.fp, "# Meta data file %s\n", fname); 501236769Sobrien 502236769Sobrien mf.gn = gn; 503236769Sobrien 504236769Sobrien Lst_ForEach(gn->commands, printCMD, &mf); 505236769Sobrien 506236769Sobrien fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf))); 507236769Sobrien fprintf(mf.fp, "TARGET %s\n", tname); 508236769Sobrien 509236769Sobrien if (metaEnv) { 510236769Sobrien for (ptr = environ; *ptr != NULL; ptr++) 511236769Sobrien fprintf(mf.fp, "ENV %s\n", *ptr); 512236769Sobrien } 513236769Sobrien 514236769Sobrien fprintf(mf.fp, "-- command output --\n"); 515236769Sobrien fflush(mf.fp); 516236769Sobrien 517236769Sobrien Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 518236769Sobrien Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); 519236769Sobrien 520236769Sobrien gn->type |= OP_META; /* in case anyone wants to know */ 521236769Sobrien if (metaSilent) { 522236769Sobrien gn->type |= OP_SILENT; 523236769Sobrien } 524236769Sobrien out: 525236769Sobrien for (i--; i >= 0; i--) { 526236769Sobrien if (p[i]) 527236769Sobrien free(p[i]); 528236769Sobrien } 529236769Sobrien 530236769Sobrien return (mf.fp); 531236769Sobrien} 532236769Sobrien 533236769Sobrienstatic Boolean 534236769SobrienboolValue(char *s) 535236769Sobrien{ 536236769Sobrien switch(*s) { 537236769Sobrien case '0': 538236769Sobrien case 'N': 539236769Sobrien case 'n': 540236769Sobrien case 'F': 541236769Sobrien case 'f': 542236769Sobrien return FALSE; 543236769Sobrien } 544236769Sobrien return TRUE; 545236769Sobrien} 546236769Sobrien 547249033Ssjg/* 548249033Ssjg * Initialization we need before reading makefiles. 549249033Ssjg */ 550236769Sobrienvoid 551250837Ssjgmeta_init(void) 552236769Sobrien{ 553249033Ssjg#ifdef USE_FILEMON 554249033Ssjg /* this allows makefiles to test if we have filemon support */ 555249033Ssjg Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0); 556249033Ssjg#endif 557249033Ssjg} 558249033Ssjg 559249033Ssjg 560249033Ssjg/* 561249033Ssjg * Initialization we need after reading makefiles. 562249033Ssjg */ 563249033Ssjgvoid 564249033Ssjgmeta_mode_init(const char *make_mode) 565249033Ssjg{ 566236769Sobrien static int once = 0; 567236769Sobrien char *cp; 568236769Sobrien 569236769Sobrien useMeta = TRUE; 570236769Sobrien useFilemon = TRUE; 571236769Sobrien writeMeta = TRUE; 572236769Sobrien 573236769Sobrien if (make_mode) { 574236769Sobrien if (strstr(make_mode, "env")) 575236769Sobrien metaEnv = TRUE; 576236769Sobrien if (strstr(make_mode, "verb")) 577236769Sobrien metaVerbose = TRUE; 578236769Sobrien if (strstr(make_mode, "read")) 579236769Sobrien writeMeta = FALSE; 580236769Sobrien if (strstr(make_mode, "nofilemon")) 581236769Sobrien useFilemon = FALSE; 582236769Sobrien if ((cp = strstr(make_mode, "curdirok="))) { 583236769Sobrien metaCurdirOk = boolValue(&cp[9]); 584236769Sobrien } 585236769Sobrien if ((cp = strstr(make_mode, "silent="))) { 586236769Sobrien metaSilent = boolValue(&cp[7]); 587236769Sobrien } 588236769Sobrien if (strstr(make_mode, "ignore-cmd")) 589236769Sobrien metaIgnoreCMDs = TRUE; 590236769Sobrien /* for backwards compatability */ 591236769Sobrien Var_Set(".MAKE.META_CREATED", "${.MAKE.META.CREATED}", VAR_GLOBAL, 0); 592236769Sobrien Var_Set(".MAKE.META_FILES", "${.MAKE.META.FILES}", VAR_GLOBAL, 0); 593236769Sobrien } 594236769Sobrien if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { 595236769Sobrien /* 596236769Sobrien * The default value for MAKE_META_PREFIX 597236769Sobrien * prints the absolute path of the target. 598236769Sobrien * This works be cause :H will generate '.' if there is no / 599236769Sobrien * and :tA will resolve that to cwd. 600236769Sobrien */ 601236769Sobrien Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0); 602236769Sobrien } 603236769Sobrien if (once) 604236769Sobrien return; 605236769Sobrien once = 1; 606236769Sobrien memset(&Mybm, 0, sizeof(Mybm)); 607236769Sobrien /* 608236769Sobrien * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} 609236769Sobrien */ 610236769Sobrien metaBailiwick = Lst_Init(FALSE); 611236769Sobrien cp = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", VAR_GLOBAL, 0); 612236769Sobrien if (cp) { 613236769Sobrien str2Lst_Append(metaBailiwick, cp, NULL); 614236769Sobrien } 615253883Ssjg /* 616253883Ssjg * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} 617253883Ssjg */ 618253883Ssjg metaIgnorePaths = Lst_Init(FALSE); 619253883Ssjg Var_Append(MAKE_META_IGNORE_PATHS, 620253883Ssjg "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); 621253883Ssjg cp = Var_Subst(NULL, 622253883Ssjg "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL, 0); 623253883Ssjg if (cp) { 624253883Ssjg str2Lst_Append(metaIgnorePaths, cp, NULL); 625253883Ssjg } 626236769Sobrien} 627236769Sobrien 628236769Sobrien/* 629236769Sobrien * In each case below we allow for job==NULL 630236769Sobrien */ 631236769Sobrienvoid 632236769Sobrienmeta_job_start(Job *job, GNode *gn) 633236769Sobrien{ 634236769Sobrien BuildMon *pbm; 635236769Sobrien 636236769Sobrien if (job != NULL) { 637236769Sobrien pbm = &job->bm; 638236769Sobrien } else { 639236769Sobrien pbm = &Mybm; 640236769Sobrien } 641236769Sobrien pbm->mfp = meta_create(pbm, gn); 642236769Sobrien#ifdef USE_FILEMON_ONCE 643236769Sobrien /* compat mode we open the filemon dev once per command */ 644236769Sobrien if (job == NULL) 645236769Sobrien return; 646236769Sobrien#endif 647236769Sobrien#ifdef USE_FILEMON 648236769Sobrien if (pbm->mfp != NULL && useFilemon) { 649236769Sobrien filemon_open(pbm); 650236769Sobrien } else { 651236769Sobrien pbm->mon_fd = pbm->filemon_fd = -1; 652236769Sobrien } 653236769Sobrien#endif 654236769Sobrien} 655236769Sobrien 656236769Sobrien/* 657236769Sobrien * The child calls this before doing anything. 658236769Sobrien * It does not disturb our state. 659236769Sobrien */ 660236769Sobrienvoid 661236769Sobrienmeta_job_child(Job *job) 662236769Sobrien{ 663236769Sobrien#ifdef USE_FILEMON 664236769Sobrien BuildMon *pbm; 665236769Sobrien pid_t pid; 666236769Sobrien 667236769Sobrien if (job != NULL) { 668236769Sobrien pbm = &job->bm; 669236769Sobrien } else { 670236769Sobrien pbm = &Mybm; 671236769Sobrien } 672236769Sobrien pid = getpid(); 673236769Sobrien if (pbm->mfp != NULL && useFilemon) { 674236769Sobrien if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) { 675236769Sobrien err(1, "Could not set filemon pid!"); 676236769Sobrien } 677236769Sobrien } 678236769Sobrien#endif 679236769Sobrien} 680236769Sobrien 681236769Sobrienvoid 682236769Sobrienmeta_job_error(Job *job, GNode *gn, int flags, int status) 683236769Sobrien{ 684236769Sobrien char cwd[MAXPATHLEN]; 685236769Sobrien BuildMon *pbm; 686236769Sobrien 687236769Sobrien if (job != NULL) { 688236769Sobrien pbm = &job->bm; 689236769Sobrien } else { 690236769Sobrien if (!gn) 691236769Sobrien gn = job->node; 692236769Sobrien pbm = &Mybm; 693236769Sobrien } 694236769Sobrien if (pbm->mfp != NULL) { 695236769Sobrien fprintf(pbm->mfp, "*** Error code %d%s\n", 696236769Sobrien status, 697236769Sobrien (flags & JOB_IGNERR) ? 698236769Sobrien "(ignored)" : ""); 699236769Sobrien } 700236769Sobrien if (gn) { 701236769Sobrien Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0); 702236769Sobrien } 703236769Sobrien getcwd(cwd, sizeof(cwd)); 704236769Sobrien Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0); 705236769Sobrien if (pbm && pbm->meta_fname[0]) { 706236769Sobrien Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0); 707236769Sobrien } 708236769Sobrien meta_job_finish(job); 709236769Sobrien} 710236769Sobrien 711236769Sobrienvoid 712236769Sobrienmeta_job_output(Job *job, char *cp, const char *nl) 713236769Sobrien{ 714236769Sobrien BuildMon *pbm; 715236769Sobrien 716236769Sobrien if (job != NULL) { 717236769Sobrien pbm = &job->bm; 718236769Sobrien } else { 719236769Sobrien pbm = &Mybm; 720236769Sobrien } 721236769Sobrien if (pbm->mfp != NULL) { 722236769Sobrien if (metaVerbose) { 723236769Sobrien static char *meta_prefix = NULL; 724236769Sobrien static int meta_prefix_len; 725236769Sobrien 726236769Sobrien if (!meta_prefix) { 727236769Sobrien char *cp2; 728236769Sobrien 729236769Sobrien meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", VAR_GLOBAL, 0); 730236769Sobrien if ((cp2 = strchr(meta_prefix, '$'))) 731236769Sobrien meta_prefix_len = cp2 - meta_prefix; 732236769Sobrien else 733236769Sobrien meta_prefix_len = strlen(meta_prefix); 734236769Sobrien } 735236769Sobrien if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { 736236769Sobrien cp = strchr(cp+1, '\n'); 737236769Sobrien if (!cp++) 738236769Sobrien return; 739236769Sobrien } 740236769Sobrien } 741236769Sobrien fprintf(pbm->mfp, "%s%s", cp, nl); 742236769Sobrien } 743236769Sobrien} 744236769Sobrien 745236769Sobrienvoid 746236769Sobrienmeta_cmd_finish(void *pbmp) 747236769Sobrien{ 748236769Sobrien#ifdef USE_FILEMON 749236769Sobrien BuildMon *pbm = pbmp; 750236769Sobrien 751236769Sobrien if (!pbm) 752236769Sobrien pbm = &Mybm; 753236769Sobrien 754236769Sobrien if (pbm->filemon_fd >= 0) { 755236769Sobrien close(pbm->filemon_fd); 756236769Sobrien filemon_read(pbm->mfp, pbm->mon_fd); 757236769Sobrien pbm->filemon_fd = pbm->mon_fd = -1; 758236769Sobrien } 759236769Sobrien#endif 760236769Sobrien} 761236769Sobrien 762236769Sobrienvoid 763236769Sobrienmeta_job_finish(Job *job) 764236769Sobrien{ 765236769Sobrien BuildMon *pbm; 766236769Sobrien 767236769Sobrien if (job != NULL) { 768236769Sobrien pbm = &job->bm; 769236769Sobrien } else { 770236769Sobrien pbm = &Mybm; 771236769Sobrien } 772236769Sobrien if (pbm->mfp != NULL) { 773236769Sobrien meta_cmd_finish(pbm); 774236769Sobrien fclose(pbm->mfp); 775236769Sobrien pbm->mfp = NULL; 776236769Sobrien pbm->meta_fname[0] = '\0'; 777236769Sobrien } 778236769Sobrien} 779236769Sobrien 780236769Sobrien/* 781236769Sobrien * Fetch a full line from fp - growing bufp if needed 782236769Sobrien * Return length in bufp. 783236769Sobrien */ 784236769Sobrienstatic int 785236769SobrienfgetLine(char **bufp, size_t *szp, int o, FILE *fp) 786236769Sobrien{ 787236769Sobrien char *buf = *bufp; 788236769Sobrien size_t bufsz = *szp; 789236769Sobrien struct stat fs; 790236769Sobrien int x; 791236769Sobrien 792236769Sobrien if (fgets(&buf[o], bufsz - o, fp) != NULL) { 793236769Sobrien check_newline: 794236769Sobrien x = o + strlen(&buf[o]); 795236769Sobrien if (buf[x - 1] == '\n') 796236769Sobrien return x; 797236769Sobrien /* 798236769Sobrien * We need to grow the buffer. 799236769Sobrien * The meta file can give us a clue. 800236769Sobrien */ 801236769Sobrien if (fstat(fileno(fp), &fs) == 0) { 802236769Sobrien size_t newsz; 803236769Sobrien char *p; 804236769Sobrien 805236769Sobrien newsz = ROUNDUP((fs.st_size / 2), BUFSIZ); 806236769Sobrien if (newsz <= bufsz) 807236769Sobrien newsz = ROUNDUP(fs.st_size, BUFSIZ); 808236769Sobrien if (DEBUG(META)) 809236769Sobrien fprintf(debug_file, "growing buffer %u -> %u\n", 810236769Sobrien (unsigned)bufsz, (unsigned)newsz); 811236769Sobrien p = bmake_realloc(buf, newsz); 812236769Sobrien if (p) { 813236769Sobrien *bufp = buf = p; 814236769Sobrien *szp = bufsz = newsz; 815236769Sobrien /* fetch the rest */ 816236769Sobrien if (!fgets(&buf[x], bufsz - x, fp)) 817236769Sobrien return x; /* truncated! */ 818236769Sobrien goto check_newline; 819236769Sobrien } 820236769Sobrien } 821236769Sobrien } 822236769Sobrien return 0; 823236769Sobrien} 824236769Sobrien 825236769Sobrienstatic int 826236769Sobrienprefix_match(void *p, void *q) 827236769Sobrien{ 828236769Sobrien const char *prefix = p; 829236769Sobrien const char *path = q; 830236769Sobrien size_t n = strlen(prefix); 831236769Sobrien 832236769Sobrien return (0 == strncmp(path, prefix, n)); 833236769Sobrien} 834236769Sobrien 835236769Sobrienstatic int 836236769Sobrienstring_match(const void *p, const void *q) 837236769Sobrien{ 838236769Sobrien const char *p1 = p; 839236769Sobrien const char *p2 = q; 840236769Sobrien 841236769Sobrien return strcmp(p1, p2); 842236769Sobrien} 843236769Sobrien 844236769Sobrien 845236769Sobrien/* 846236769Sobrien * When running with 'meta' functionality, a target can be out-of-date 847236769Sobrien * if any of the references in it's meta data file is more recent. 848236769Sobrien * We have to track the latestdir on a per-process basis. 849236769Sobrien */ 850236769Sobrien#define LDIR_VNAME_FMT ".meta.%d.ldir" 851236769Sobrien 852236769Sobrien/* 853236769Sobrien * It is possible that a .meta file is corrupted, 854236769Sobrien * if we detect this we want to reproduce it. 855236769Sobrien * Setting oodate TRUE will have that effect. 856236769Sobrien */ 857236769Sobrien#define CHECK_VALID_META(p) if (!(p && *p)) { \ 858236769Sobrien warnx("%s: %d: malformed", fname, lineno); \ 859236769Sobrien oodate = TRUE; \ 860236769Sobrien continue; \ 861236769Sobrien } 862236769Sobrien 863236769SobrienBoolean 864236769Sobrienmeta_oodate(GNode *gn, Boolean oodate) 865236769Sobrien{ 866236769Sobrien static char *tmpdir = NULL; 867236769Sobrien static char cwd[MAXPATHLEN]; 868236769Sobrien char ldir_vname[64]; 869236769Sobrien char latestdir[MAXPATHLEN]; 870236769Sobrien char fname[MAXPATHLEN]; 871236769Sobrien char fname1[MAXPATHLEN]; 872236769Sobrien char fname2[MAXPATHLEN]; 873236769Sobrien char *p; 874236769Sobrien char *cp; 875236769Sobrien static size_t cwdlen = 0; 876236769Sobrien static size_t tmplen = 0; 877236769Sobrien FILE *fp; 878246223Ssjg Boolean needOODATE = FALSE; 879236769Sobrien Lst missingFiles; 880236769Sobrien 881236769Sobrien if (oodate) 882236769Sobrien return oodate; /* we're done */ 883236769Sobrien 884236769Sobrien missingFiles = Lst_Init(FALSE); 885236769Sobrien 886236769Sobrien /* 887236769Sobrien * We need to check if the target is out-of-date. This includes 888236769Sobrien * checking if the expanded command has changed. This in turn 889236769Sobrien * requires that all variables are set in the same way that they 890236769Sobrien * would be if the target needs to be re-built. 891236769Sobrien */ 892236769Sobrien Make_DoAllVar(gn); 893236769Sobrien 894236769Sobrien meta_name(gn, fname, sizeof(fname), NULL, NULL); 895236769Sobrien 896236769Sobrien#ifdef DEBUG_META_MODE 897236769Sobrien if (DEBUG(META)) 898236769Sobrien fprintf(debug_file, "meta_oodate: %s\n", fname); 899236769Sobrien#endif 900236769Sobrien 901236769Sobrien if ((fp = fopen(fname, "r")) != NULL) { 902236769Sobrien static char *buf = NULL; 903236769Sobrien static size_t bufsz; 904236769Sobrien int lineno = 0; 905236769Sobrien int lastpid = 0; 906236769Sobrien int pid; 907236769Sobrien int f = 0; 908236769Sobrien int x; 909236769Sobrien LstNode ln; 910236769Sobrien struct stat fs; 911236769Sobrien 912236769Sobrien if (!buf) { 913236769Sobrien bufsz = 8 * BUFSIZ; 914236769Sobrien buf = bmake_malloc(bufsz); 915236769Sobrien } 916236769Sobrien 917236769Sobrien if (!cwdlen) { 918236769Sobrien if (getcwd(cwd, sizeof(cwd)) == NULL) 919236769Sobrien err(1, "Could not get current working directory"); 920236769Sobrien cwdlen = strlen(cwd); 921236769Sobrien } 922236769Sobrien 923236769Sobrien if (!tmpdir) { 924236769Sobrien tmpdir = getTmpdir(); 925236769Sobrien tmplen = strlen(tmpdir); 926236769Sobrien } 927236769Sobrien 928236769Sobrien /* we want to track all the .meta we read */ 929236769Sobrien Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 930236769Sobrien 931236769Sobrien ln = Lst_First(gn->commands); 932236769Sobrien while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { 933236769Sobrien lineno++; 934236769Sobrien if (buf[x - 1] == '\n') 935236769Sobrien buf[x - 1] = '\0'; 936236769Sobrien else { 937236769Sobrien warnx("%s: %d: line truncated at %u", fname, lineno, x); 938236769Sobrien oodate = TRUE; 939236769Sobrien break; 940236769Sobrien } 941236769Sobrien /* Find the start of the build monitor section. */ 942236769Sobrien if (!f) { 943236769Sobrien if (strncmp(buf, "-- filemon", 10) == 0) { 944236769Sobrien f = 1; 945236769Sobrien continue; 946236769Sobrien } 947236769Sobrien if (strncmp(buf, "# buildmon", 10) == 0) { 948236769Sobrien f = 1; 949236769Sobrien continue; 950236769Sobrien } 951236769Sobrien } 952236769Sobrien 953236769Sobrien /* Delimit the record type. */ 954236769Sobrien p = buf; 955236769Sobrien#ifdef DEBUG_META_MODE 956236769Sobrien if (DEBUG(META)) 957236769Sobrien fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); 958236769Sobrien#endif 959236769Sobrien strsep(&p, " "); 960236769Sobrien if (f) { 961236769Sobrien /* 962236769Sobrien * We are in the 'filemon' output section. 963236769Sobrien * Each record from filemon follows the general form: 964236769Sobrien * 965236769Sobrien * <key> <pid> <data> 966236769Sobrien * 967236769Sobrien * Where: 968236769Sobrien * <key> is a single letter, denoting the syscall. 969236769Sobrien * <pid> is the process that made the syscall. 970236769Sobrien * <data> is the arguments (of interest). 971236769Sobrien */ 972236769Sobrien switch(buf[0]) { 973236769Sobrien case '#': /* comment */ 974236769Sobrien case 'V': /* version */ 975236769Sobrien break; 976236769Sobrien default: 977236769Sobrien /* 978236769Sobrien * We need to track pathnames per-process. 979236769Sobrien * 980236769Sobrien * Each process run by make, starts off in the 'CWD' 981236769Sobrien * recorded in the .meta file, if it chdirs ('C') 982236769Sobrien * elsewhere we need to track that - but only for 983236769Sobrien * that process. If it forks ('F'), we initialize 984236769Sobrien * the child to have the same cwd as its parent. 985236769Sobrien * 986236769Sobrien * We also need to track the 'latestdir' of 987236769Sobrien * interest. This is usually the same as cwd, but 988236769Sobrien * not if a process is reading directories. 989236769Sobrien * 990236769Sobrien * Each time we spot a different process ('pid') 991236769Sobrien * we save the current value of 'latestdir' in a 992236769Sobrien * variable qualified by 'lastpid', and 993236769Sobrien * re-initialize 'latestdir' to any pre-saved 994236769Sobrien * value for the current 'pid' and 'CWD' if none. 995236769Sobrien */ 996236769Sobrien CHECK_VALID_META(p); 997236769Sobrien pid = atoi(p); 998236769Sobrien if (pid > 0 && pid != lastpid) { 999236769Sobrien char *ldir; 1000236769Sobrien char *tp; 1001236769Sobrien 1002236769Sobrien if (lastpid > 0) { 1003236769Sobrien /* We need to remember this. */ 1004236769Sobrien Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0); 1005236769Sobrien } 1006236769Sobrien snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); 1007236769Sobrien lastpid = pid; 1008236769Sobrien ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); 1009236769Sobrien if (ldir) { 1010236769Sobrien strlcpy(latestdir, ldir, sizeof(latestdir)); 1011236769Sobrien if (tp) 1012236769Sobrien free(tp); 1013236769Sobrien } else 1014236769Sobrien strlcpy(latestdir, cwd, sizeof(latestdir)); 1015236769Sobrien } 1016236769Sobrien /* Skip past the pid. */ 1017236769Sobrien if (strsep(&p, " ") == NULL) 1018236769Sobrien continue; 1019236769Sobrien#ifdef DEBUG_META_MODE 1020236769Sobrien if (DEBUG(META)) 1021236769Sobrien fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, latestdir); 1022236769Sobrien#endif 1023236769Sobrien break; 1024236769Sobrien } 1025236769Sobrien 1026236769Sobrien CHECK_VALID_META(p); 1027236769Sobrien 1028236769Sobrien /* Process according to record type. */ 1029236769Sobrien switch (buf[0]) { 1030236769Sobrien case 'X': /* eXit */ 1031236769Sobrien Var_Delete(ldir_vname, VAR_GLOBAL); 1032236769Sobrien lastpid = 0; /* no need to save ldir_vname */ 1033236769Sobrien break; 1034236769Sobrien 1035236769Sobrien case 'F': /* [v]Fork */ 1036236769Sobrien { 1037236769Sobrien char cldir[64]; 1038236769Sobrien int child; 1039236769Sobrien 1040236769Sobrien child = atoi(p); 1041236769Sobrien if (child > 0) { 1042236769Sobrien snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); 1043236769Sobrien Var_Set(cldir, latestdir, VAR_GLOBAL, 0); 1044236769Sobrien } 1045236769Sobrien } 1046236769Sobrien break; 1047236769Sobrien 1048236769Sobrien case 'C': /* Chdir */ 1049236769Sobrien /* Update the latest directory. */ 1050236769Sobrien strlcpy(latestdir, p, sizeof(latestdir)); 1051236769Sobrien break; 1052236769Sobrien 1053236769Sobrien case 'M': /* renaMe */ 1054236769Sobrien if (Lst_IsEmpty(missingFiles)) 1055236769Sobrien break; 1056236769Sobrien /* 'L' and 'M' put single quotes around the args */ 1057236769Sobrien if (*p == '\'') { 1058236769Sobrien char *ep; 1059236769Sobrien 1060236769Sobrien p++; 1061236769Sobrien if ((ep = strchr(p, '\''))) 1062236769Sobrien *ep = '\0'; 1063236769Sobrien } 1064236769Sobrien /* FALLTHROUGH */ 1065236769Sobrien case 'D': /* unlink */ 1066236769Sobrien if (*p == '/' && !Lst_IsEmpty(missingFiles)) { 1067236769Sobrien /* remove p from the missingFiles list if present */ 1068236769Sobrien if ((ln = Lst_Find(missingFiles, p, string_match)) != NULL) { 1069236769Sobrien char *tp = Lst_Datum(ln); 1070236769Sobrien Lst_Remove(missingFiles, ln); 1071236769Sobrien free(tp); 1072249033Ssjg ln = NULL; /* we're done with it */ 1073236769Sobrien } 1074236769Sobrien } 1075236769Sobrien break; 1076236769Sobrien case 'L': /* Link */ 1077236769Sobrien /* we want the target */ 1078236769Sobrien if (strsep(&p, " ") == NULL) 1079236769Sobrien continue; 1080236769Sobrien CHECK_VALID_META(p); 1081236769Sobrien /* 'L' and 'M' put single quotes around the args */ 1082236769Sobrien if (*p == '\'') { 1083236769Sobrien char *ep; 1084236769Sobrien 1085236769Sobrien p++; 1086236769Sobrien if ((ep = strchr(p, '\''))) 1087236769Sobrien *ep = '\0'; 1088236769Sobrien } 1089236769Sobrien /* FALLTHROUGH */ 1090236769Sobrien case 'W': /* Write */ 1091236769Sobrien /* 1092236769Sobrien * If a file we generated within our bailiwick 1093236769Sobrien * but outside of .OBJDIR is missing, 1094236769Sobrien * we need to do it again. 1095236769Sobrien */ 1096236769Sobrien /* ignore non-absolute paths */ 1097236769Sobrien if (*p != '/') 1098236769Sobrien break; 1099236769Sobrien 1100236769Sobrien if (Lst_IsEmpty(metaBailiwick)) 1101236769Sobrien break; 1102236769Sobrien 1103236769Sobrien /* ignore cwd - normal dependencies handle those */ 1104236769Sobrien if (strncmp(p, cwd, cwdlen) == 0) 1105236769Sobrien break; 1106236769Sobrien 1107236769Sobrien if (!Lst_ForEach(metaBailiwick, prefix_match, p)) 1108236769Sobrien break; 1109236769Sobrien 1110236769Sobrien /* tmpdir might be within */ 1111236769Sobrien if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) 1112236769Sobrien break; 1113236769Sobrien 1114236769Sobrien /* ignore anything containing the string "tmp" */ 1115236769Sobrien if ((strstr("tmp", p))) 1116236769Sobrien break; 1117236769Sobrien 1118236769Sobrien if (stat(p, &fs) < 0) { 1119236769Sobrien Lst_AtEnd(missingFiles, bmake_strdup(p)); 1120236769Sobrien } 1121236769Sobrien break; 1122236769Sobrien case 'R': /* Read */ 1123236769Sobrien case 'E': /* Exec */ 1124236769Sobrien /* 1125236769Sobrien * Check for runtime files that can't 1126236769Sobrien * be part of the dependencies because 1127236769Sobrien * they are _expected_ to change. 1128236769Sobrien */ 1129253883Ssjg if (*p == '/' && 1130253883Ssjg Lst_ForEach(metaIgnorePaths, prefix_match, p)) { 1131253883Ssjg#ifdef DEBUG_META_MODE 1132253883Ssjg if (DEBUG(META)) 1133253883Ssjg fprintf(debug_file, "meta_oodate: ignoring: %s\n", 1134253883Ssjg p); 1135253883Ssjg#endif 1136236769Sobrien break; 1137253883Ssjg } 1138236769Sobrien 1139236769Sobrien if ((cp = strrchr(p, '/'))) { 1140236769Sobrien cp++; 1141236769Sobrien /* 1142236769Sobrien * We don't normally expect to see this, 1143236769Sobrien * but we do expect it to change. 1144236769Sobrien */ 1145236769Sobrien if (strcmp(cp, makeDependfile) == 0) 1146236769Sobrien break; 1147236769Sobrien } 1148236769Sobrien 1149236769Sobrien /* 1150236769Sobrien * The rest of the record is the file name. 1151236769Sobrien * Check if it's not an absolute path. 1152236769Sobrien */ 1153236769Sobrien { 1154236769Sobrien char *sdirs[4]; 1155236769Sobrien char **sdp; 1156236769Sobrien int sdx = 0; 1157236769Sobrien int found = 0; 1158236769Sobrien 1159236769Sobrien if (*p == '/') { 1160236769Sobrien sdirs[sdx++] = p; /* done */ 1161236769Sobrien } else { 1162236769Sobrien if (strcmp(".", p) == 0) 1163236769Sobrien continue; /* no point */ 1164236769Sobrien 1165236769Sobrien /* Check vs latestdir */ 1166236769Sobrien snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); 1167236769Sobrien sdirs[sdx++] = fname1; 1168236769Sobrien 1169236769Sobrien if (strcmp(latestdir, cwd) != 0) { 1170236769Sobrien /* Check vs cwd */ 1171236769Sobrien snprintf(fname2, sizeof(fname2), "%s/%s", cwd, p); 1172236769Sobrien sdirs[sdx++] = fname2; 1173236769Sobrien } 1174236769Sobrien } 1175236769Sobrien sdirs[sdx++] = NULL; 1176236769Sobrien 1177236769Sobrien for (sdp = sdirs; *sdp && !found; sdp++) { 1178236769Sobrien#ifdef DEBUG_META_MODE 1179236769Sobrien if (DEBUG(META)) 1180236769Sobrien fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); 1181236769Sobrien#endif 1182236769Sobrien if (stat(*sdp, &fs) == 0) { 1183236769Sobrien found = 1; 1184236769Sobrien p = *sdp; 1185236769Sobrien } 1186236769Sobrien } 1187236769Sobrien if (found) { 1188236769Sobrien#ifdef DEBUG_META_MODE 1189236769Sobrien if (DEBUG(META)) 1190236769Sobrien fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); 1191236769Sobrien#endif 1192236769Sobrien if (!S_ISDIR(fs.st_mode) && 1193236769Sobrien fs.st_mtime > gn->mtime) { 1194236769Sobrien if (DEBUG(META)) 1195236769Sobrien fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); 1196236769Sobrien oodate = TRUE; 1197236769Sobrien } else if (S_ISDIR(fs.st_mode)) { 1198236769Sobrien /* Update the latest directory. */ 1199236769Sobrien realpath(p, latestdir); 1200236769Sobrien } 1201236769Sobrien } else if (errno == ENOENT && *p == '/' && 1202236769Sobrien strncmp(p, cwd, cwdlen) != 0) { 1203236769Sobrien /* 1204236769Sobrien * A referenced file outside of CWD is missing. 1205236769Sobrien * We cannot catch every eventuality here... 1206236769Sobrien */ 1207236769Sobrien if (DEBUG(META)) 1208236769Sobrien fprintf(debug_file, "%s: %d: file '%s' may have moved?...\n", fname, lineno, p); 1209236769Sobrien oodate = TRUE; 1210236769Sobrien } 1211236769Sobrien } 1212236769Sobrien break; 1213236769Sobrien default: 1214236769Sobrien break; 1215236769Sobrien } 1216236769Sobrien } else if (strcmp(buf, "CMD") == 0) { 1217236769Sobrien /* 1218236769Sobrien * Compare the current command with the one in the 1219236769Sobrien * meta data file. 1220236769Sobrien */ 1221236769Sobrien if (ln == NULL) { 1222236769Sobrien if (DEBUG(META)) 1223236769Sobrien fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); 1224236769Sobrien oodate = TRUE; 1225236769Sobrien } else { 1226236769Sobrien char *cmd = (char *)Lst_Datum(ln); 1227249033Ssjg Boolean hasOODATE = FALSE; 1228236769Sobrien 1229249033Ssjg if (strstr(cmd, "$?")) 1230249033Ssjg hasOODATE = TRUE; 1231249033Ssjg else if ((cp = strstr(cmd, ".OODATE"))) { 1232249033Ssjg /* check for $[{(].OODATE[:)}] */ 1233249033Ssjg if (cp > cmd + 2 && cp[-2] == '$') 1234249033Ssjg hasOODATE = TRUE; 1235236769Sobrien } 1236249033Ssjg if (hasOODATE) { 1237249033Ssjg needOODATE = TRUE; 1238249033Ssjg if (DEBUG(META)) 1239249033Ssjg fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno); 1240249033Ssjg } 1241236769Sobrien cmd = Var_Subst(NULL, cmd, gn, TRUE); 1242236769Sobrien 1243236769Sobrien if ((cp = strchr(cmd, '\n'))) { 1244236769Sobrien int n; 1245236769Sobrien 1246236769Sobrien /* 1247236769Sobrien * This command contains newlines, we need to 1248236769Sobrien * fetch more from the .meta file before we 1249236769Sobrien * attempt a comparison. 1250236769Sobrien */ 1251236769Sobrien /* first put the newline back at buf[x - 1] */ 1252236769Sobrien buf[x - 1] = '\n'; 1253236769Sobrien do { 1254236769Sobrien /* now fetch the next line */ 1255236769Sobrien if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) 1256236769Sobrien break; 1257236769Sobrien x = n; 1258236769Sobrien lineno++; 1259236769Sobrien if (buf[x - 1] != '\n') { 1260236769Sobrien warnx("%s: %d: line truncated at %u", fname, lineno, x); 1261236769Sobrien break; 1262236769Sobrien } 1263236769Sobrien cp = strchr(++cp, '\n'); 1264236769Sobrien } while (cp); 1265236769Sobrien if (buf[x - 1] == '\n') 1266236769Sobrien buf[x - 1] = '\0'; 1267236769Sobrien } 1268249033Ssjg if (!hasOODATE && 1269236769Sobrien !(gn->type & OP_NOMETA_CMP) && 1270236769Sobrien strcmp(p, cmd) != 0) { 1271236769Sobrien if (DEBUG(META)) 1272236769Sobrien fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); 1273236769Sobrien if (!metaIgnoreCMDs) 1274236769Sobrien oodate = TRUE; 1275236769Sobrien } 1276236769Sobrien free(cmd); 1277236769Sobrien ln = Lst_Succ(ln); 1278236769Sobrien } 1279236769Sobrien } else if (strcmp(buf, "CWD") == 0) { 1280236769Sobrien /* 1281236769Sobrien * Check if there are extra commands now 1282236769Sobrien * that weren't in the meta data file. 1283236769Sobrien */ 1284236769Sobrien if (!oodate && ln != NULL) { 1285236769Sobrien if (DEBUG(META)) 1286236769Sobrien fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno); 1287236769Sobrien oodate = TRUE; 1288236769Sobrien } 1289236769Sobrien if (strcmp(p, cwd) != 0) { 1290236769Sobrien if (DEBUG(META)) 1291236769Sobrien fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); 1292236769Sobrien oodate = TRUE; 1293236769Sobrien } 1294236769Sobrien } 1295236769Sobrien } 1296236769Sobrien 1297236769Sobrien fclose(fp); 1298236769Sobrien if (!Lst_IsEmpty(missingFiles)) { 1299236769Sobrien if (DEBUG(META)) 1300236769Sobrien fprintf(debug_file, "%s: missing files: %s...\n", 1301236769Sobrien fname, (char *)Lst_Datum(Lst_First(missingFiles))); 1302236769Sobrien oodate = TRUE; 1303236769Sobrien Lst_Destroy(missingFiles, (FreeProc *)free); 1304236769Sobrien } 1305236769Sobrien } else { 1306236769Sobrien if ((gn->type & OP_META)) { 1307236769Sobrien if (DEBUG(META)) 1308236769Sobrien fprintf(debug_file, "%s: required but missing\n", fname); 1309236769Sobrien oodate = TRUE; 1310236769Sobrien } 1311236769Sobrien } 1312246223Ssjg if (oodate && needOODATE) { 1313236769Sobrien /* 1314246223Ssjg * Target uses .OODATE which is empty; or we wouldn't be here. 1315246223Ssjg * We have decided it is oodate, so .OODATE needs to be set. 1316246223Ssjg * All we can sanely do is set it to .ALLSRC. 1317236769Sobrien */ 1318236769Sobrien Var_Delete(OODATE, gn); 1319246223Ssjg Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn, 0); 1320246223Ssjg if (cp) 1321246223Ssjg free(cp); 1322236769Sobrien } 1323236769Sobrien return oodate; 1324236769Sobrien} 1325236769Sobrien 1326236769Sobrien/* support for compat mode */ 1327236769Sobrien 1328236769Sobrienstatic int childPipe[2]; 1329236769Sobrien 1330236769Sobrienvoid 1331236769Sobrienmeta_compat_start(void) 1332236769Sobrien{ 1333236769Sobrien#ifdef USE_FILEMON_ONCE 1334236769Sobrien /* 1335236769Sobrien * We need to re-open filemon for each cmd. 1336236769Sobrien */ 1337236769Sobrien BuildMon *pbm = &Mybm; 1338236769Sobrien 1339236769Sobrien if (pbm->mfp != NULL && useFilemon) { 1340236769Sobrien filemon_open(pbm); 1341236769Sobrien } else { 1342236769Sobrien pbm->mon_fd = pbm->filemon_fd = -1; 1343236769Sobrien } 1344236769Sobrien#endif 1345236769Sobrien if (pipe(childPipe) < 0) 1346236769Sobrien Punt("Cannot create pipe: %s", strerror(errno)); 1347236769Sobrien /* Set close-on-exec flag for both */ 1348236769Sobrien (void)fcntl(childPipe[0], F_SETFD, 1); 1349236769Sobrien (void)fcntl(childPipe[1], F_SETFD, 1); 1350236769Sobrien} 1351236769Sobrien 1352236769Sobrienvoid 1353236769Sobrienmeta_compat_child(void) 1354236769Sobrien{ 1355236769Sobrien meta_job_child(NULL); 1356236769Sobrien if (dup2(childPipe[1], 1) < 0 || 1357236769Sobrien dup2(1, 2) < 0) { 1358236769Sobrien execError("dup2", "pipe"); 1359236769Sobrien _exit(1); 1360236769Sobrien } 1361236769Sobrien} 1362236769Sobrien 1363236769Sobrienvoid 1364236769Sobrienmeta_compat_parent(void) 1365236769Sobrien{ 1366236769Sobrien FILE *fp; 1367236769Sobrien char buf[BUFSIZ]; 1368236769Sobrien 1369236769Sobrien close(childPipe[1]); /* child side */ 1370236769Sobrien fp = fdopen(childPipe[0], "r"); 1371236769Sobrien while (fgets(buf, sizeof(buf), fp)) { 1372236769Sobrien meta_job_output(NULL, buf, ""); 1373236769Sobrien printf("%s", buf); 1374236769Sobrien } 1375236769Sobrien fclose(fp); 1376236769Sobrien} 1377236769Sobrien 1378236769Sobrien#endif /* USE_META */ 1379