1*5f1e34d9SAlexandre Perrin /* $NetBSD: meta.c,v 1.33 2013/10/01 05:37:17 sjg Exp $ */ 201e196c8SJohn Marino 301e196c8SJohn Marino /* 401e196c8SJohn Marino * Implement 'meta' mode. 501e196c8SJohn Marino * Adapted from John Birrell's patches to FreeBSD make. 601e196c8SJohn Marino * --sjg 701e196c8SJohn Marino */ 801e196c8SJohn Marino /* 901e196c8SJohn Marino * Copyright (c) 2009-2010, Juniper Networks, Inc. 1001e196c8SJohn Marino * Portions Copyright (c) 2009, John Birrell. 1101e196c8SJohn Marino * 1201e196c8SJohn Marino * Redistribution and use in source and binary forms, with or without 1301e196c8SJohn Marino * modification, are permitted provided that the following conditions 1401e196c8SJohn Marino * are met: 1501e196c8SJohn Marino * 1. Redistributions of source code must retain the above copyright 1601e196c8SJohn Marino * notice, this list of conditions and the following disclaimer. 1701e196c8SJohn Marino * 2. Redistributions in binary form must reproduce the above copyright 1801e196c8SJohn Marino * notice, this list of conditions and the following disclaimer in the 1901e196c8SJohn Marino * documentation and/or other materials provided with the distribution. 2001e196c8SJohn Marino * 2101e196c8SJohn Marino * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2201e196c8SJohn Marino * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2301e196c8SJohn Marino * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2401e196c8SJohn Marino * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2501e196c8SJohn Marino * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 2601e196c8SJohn Marino * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 2701e196c8SJohn Marino * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 2801e196c8SJohn Marino * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 2901e196c8SJohn Marino * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3001e196c8SJohn Marino * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3101e196c8SJohn Marino * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3201e196c8SJohn Marino */ 3301e196c8SJohn Marino #if defined(USE_META) 3401e196c8SJohn Marino 3501e196c8SJohn Marino #ifdef HAVE_CONFIG_H 3601e196c8SJohn Marino # include "config.h" 3701e196c8SJohn Marino #endif 3801e196c8SJohn Marino #include <sys/stat.h> 3901e196c8SJohn Marino #include <sys/ioctl.h> 4001e196c8SJohn Marino #include <fcntl.h> 4101e196c8SJohn Marino #include <libgen.h> 4201e196c8SJohn Marino #include <errno.h> 4301e196c8SJohn Marino #if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) 4401e196c8SJohn Marino #include <err.h> 4501e196c8SJohn Marino #endif 4601e196c8SJohn Marino 4701e196c8SJohn Marino #include "make.h" 4801e196c8SJohn Marino #include "job.h" 4901e196c8SJohn Marino 5001e196c8SJohn Marino #ifdef HAVE_FILEMON_H 5101e196c8SJohn Marino # include <filemon.h> 5201e196c8SJohn Marino #endif 5301e196c8SJohn Marino #if !defined(USE_FILEMON) && defined(FILEMON_SET_FD) 5401e196c8SJohn Marino # define USE_FILEMON 5501e196c8SJohn Marino #endif 5601e196c8SJohn Marino 5701e196c8SJohn Marino static BuildMon Mybm; /* for compat */ 5801e196c8SJohn Marino static Lst metaBailiwick; /* our scope of control */ 59*5f1e34d9SAlexandre Perrin static Lst metaIgnorePaths; /* paths we deliberately ignore */ 60*5f1e34d9SAlexandre Perrin 61*5f1e34d9SAlexandre Perrin #ifndef MAKE_META_IGNORE_PATHS 62*5f1e34d9SAlexandre Perrin #define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS" 63*5f1e34d9SAlexandre Perrin #endif 6401e196c8SJohn Marino 6501e196c8SJohn Marino Boolean useMeta = FALSE; 6601e196c8SJohn Marino static Boolean useFilemon = FALSE; 6701e196c8SJohn Marino static Boolean writeMeta = FALSE; 6801e196c8SJohn Marino static Boolean metaEnv = FALSE; /* don't save env unless asked */ 6901e196c8SJohn Marino static Boolean metaVerbose = FALSE; 7001e196c8SJohn Marino static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ 7101e196c8SJohn Marino static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ 7201e196c8SJohn Marino static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */ 7301e196c8SJohn Marino 7401e196c8SJohn Marino extern Boolean forceJobs; 7501e196c8SJohn Marino extern Boolean comatMake; 7601e196c8SJohn Marino extern char **environ; 7701e196c8SJohn Marino 7801e196c8SJohn Marino #define MAKE_META_PREFIX ".MAKE.META.PREFIX" 7901e196c8SJohn Marino 8001e196c8SJohn Marino #ifndef N2U 8101e196c8SJohn Marino # define N2U(n, u) (((n) + ((u) - 1)) / (u)) 8201e196c8SJohn Marino #endif 8301e196c8SJohn Marino #ifndef ROUNDUP 8401e196c8SJohn Marino # define ROUNDUP(n, u) (N2U((n), (u)) * (u)) 8501e196c8SJohn Marino #endif 8601e196c8SJohn Marino 8701e196c8SJohn Marino #if !defined(HAVE_STRSEP) 8801e196c8SJohn Marino # define strsep(s, d) stresep((s), (d), 0) 8901e196c8SJohn Marino #endif 9001e196c8SJohn Marino 9101e196c8SJohn Marino /* 9201e196c8SJohn Marino * Filemon is a kernel module which snoops certain syscalls. 9301e196c8SJohn Marino * 9401e196c8SJohn Marino * C chdir 9501e196c8SJohn Marino * E exec 9601e196c8SJohn Marino * F [v]fork 9701e196c8SJohn Marino * L [sym]link 9801e196c8SJohn Marino * M rename 9901e196c8SJohn Marino * R read 10001e196c8SJohn Marino * W write 10101e196c8SJohn Marino * S stat 10201e196c8SJohn Marino * 10301e196c8SJohn Marino * See meta_oodate below - we mainly care about 'E' and 'R'. 10401e196c8SJohn Marino * 10501e196c8SJohn Marino * We can still use meta mode without filemon, but 10601e196c8SJohn Marino * the benefits are more limited. 10701e196c8SJohn Marino */ 10801e196c8SJohn Marino #ifdef USE_FILEMON 10901e196c8SJohn Marino # ifndef _PATH_FILEMON 11001e196c8SJohn Marino # define _PATH_FILEMON "/dev/filemon" 11101e196c8SJohn Marino # endif 11201e196c8SJohn Marino 11301e196c8SJohn Marino /* 11401e196c8SJohn Marino * Open the filemon device. 11501e196c8SJohn Marino */ 11601e196c8SJohn Marino static void 11701e196c8SJohn Marino filemon_open(BuildMon *pbm) 11801e196c8SJohn Marino { 11901e196c8SJohn Marino int retry; 12001e196c8SJohn Marino 12101e196c8SJohn Marino pbm->mon_fd = pbm->filemon_fd = -1; 12201e196c8SJohn Marino if (!useFilemon) 12301e196c8SJohn Marino return; 12401e196c8SJohn Marino 12501e196c8SJohn Marino for (retry = 5; retry >= 0; retry--) { 12601e196c8SJohn Marino if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0) 12701e196c8SJohn Marino break; 12801e196c8SJohn Marino } 12901e196c8SJohn Marino 13001e196c8SJohn Marino if (pbm->filemon_fd < 0) { 13101e196c8SJohn Marino useFilemon = FALSE; 13201e196c8SJohn Marino warn("Could not open %s", _PATH_FILEMON); 13301e196c8SJohn Marino return; 13401e196c8SJohn Marino } 13501e196c8SJohn Marino 13601e196c8SJohn Marino /* 13701e196c8SJohn Marino * We use a file outside of '.' 13801e196c8SJohn Marino * to avoid a FreeBSD kernel bug where unlink invalidates 13901e196c8SJohn Marino * cwd causing getcwd to do a lot more work. 14001e196c8SJohn Marino * We only care about the descriptor. 14101e196c8SJohn Marino */ 14201e196c8SJohn Marino pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); 14301e196c8SJohn Marino if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) { 14401e196c8SJohn Marino err(1, "Could not set filemon file descriptor!"); 14501e196c8SJohn Marino } 14601e196c8SJohn Marino /* we don't need these once we exec */ 14701e196c8SJohn Marino (void)fcntl(pbm->mon_fd, F_SETFD, 1); 14801e196c8SJohn Marino (void)fcntl(pbm->filemon_fd, F_SETFD, 1); 14901e196c8SJohn Marino } 15001e196c8SJohn Marino 15101e196c8SJohn Marino /* 15201e196c8SJohn Marino * Read the build monitor output file and write records to the target's 15301e196c8SJohn Marino * metadata file. 15401e196c8SJohn Marino */ 15501e196c8SJohn Marino static void 15601e196c8SJohn Marino filemon_read(FILE *mfp, int fd) 15701e196c8SJohn Marino { 15801e196c8SJohn Marino FILE *fp; 15901e196c8SJohn Marino char buf[BUFSIZ]; 16001e196c8SJohn Marino 16101e196c8SJohn Marino /* Check if we're not writing to a meta data file.*/ 16201e196c8SJohn Marino if (mfp == NULL) { 16301e196c8SJohn Marino if (fd >= 0) 16401e196c8SJohn Marino close(fd); /* not interested */ 16501e196c8SJohn Marino return; 16601e196c8SJohn Marino } 16701e196c8SJohn Marino /* rewind */ 16801e196c8SJohn Marino (void)lseek(fd, (off_t)0, SEEK_SET); 16901e196c8SJohn Marino if ((fp = fdopen(fd, "r")) == NULL) 17001e196c8SJohn Marino err(1, "Could not read build monitor file '%d'", fd); 17101e196c8SJohn Marino 17201e196c8SJohn Marino fprintf(mfp, "-- filemon acquired metadata --\n"); 17301e196c8SJohn Marino 17401e196c8SJohn Marino while (fgets(buf, sizeof(buf), fp)) { 17501e196c8SJohn Marino fprintf(mfp, "%s", buf); 17601e196c8SJohn Marino } 17701e196c8SJohn Marino fflush(mfp); 17801e196c8SJohn Marino clearerr(fp); 17901e196c8SJohn Marino fclose(fp); 18001e196c8SJohn Marino } 18101e196c8SJohn Marino #endif 18201e196c8SJohn Marino 18301e196c8SJohn Marino /* 18401e196c8SJohn Marino * when realpath() fails, 18501e196c8SJohn Marino * we use this, to clean up ./ and ../ 18601e196c8SJohn Marino */ 18701e196c8SJohn Marino static void 18801e196c8SJohn Marino eat_dots(char *buf, size_t bufsz, int dots) 18901e196c8SJohn Marino { 19001e196c8SJohn Marino char *cp; 19101e196c8SJohn Marino char *cp2; 19201e196c8SJohn Marino const char *eat; 19301e196c8SJohn Marino size_t eatlen; 19401e196c8SJohn Marino 19501e196c8SJohn Marino switch (dots) { 19601e196c8SJohn Marino case 1: 19701e196c8SJohn Marino eat = "/./"; 19801e196c8SJohn Marino eatlen = 2; 19901e196c8SJohn Marino break; 20001e196c8SJohn Marino case 2: 20101e196c8SJohn Marino eat = "/../"; 20201e196c8SJohn Marino eatlen = 3; 20301e196c8SJohn Marino break; 20401e196c8SJohn Marino default: 20501e196c8SJohn Marino return; 20601e196c8SJohn Marino } 20701e196c8SJohn Marino 20801e196c8SJohn Marino do { 20901e196c8SJohn Marino cp = strstr(buf, eat); 21001e196c8SJohn Marino if (cp) { 21101e196c8SJohn Marino cp2 = cp + eatlen; 21201e196c8SJohn Marino if (dots == 2 && cp > buf) { 21301e196c8SJohn Marino do { 21401e196c8SJohn Marino cp--; 21501e196c8SJohn Marino } while (cp > buf && *cp != '/'); 21601e196c8SJohn Marino } 21701e196c8SJohn Marino if (*cp == '/') { 21801e196c8SJohn Marino strlcpy(cp, cp2, bufsz - (cp - buf)); 21901e196c8SJohn Marino } else { 22001e196c8SJohn Marino return; /* can't happen? */ 22101e196c8SJohn Marino } 22201e196c8SJohn Marino } 22301e196c8SJohn Marino } while (cp); 22401e196c8SJohn Marino } 22501e196c8SJohn Marino 22601e196c8SJohn Marino static char * 22701e196c8SJohn Marino meta_name(struct GNode *gn, char *mname, size_t mnamelen, 22801e196c8SJohn Marino const char *dname, 22901e196c8SJohn Marino const char *tname) 23001e196c8SJohn Marino { 23101e196c8SJohn Marino char buf[MAXPATHLEN]; 23201e196c8SJohn Marino char cwd[MAXPATHLEN]; 23301e196c8SJohn Marino char *rp; 23401e196c8SJohn Marino char *cp; 23501e196c8SJohn Marino char *tp; 23601e196c8SJohn Marino char *p[4]; /* >= number of possible uses */ 23701e196c8SJohn Marino int i; 23801e196c8SJohn Marino 23901e196c8SJohn Marino i = 0; 24001e196c8SJohn Marino if (!dname) 24101e196c8SJohn Marino dname = Var_Value(".OBJDIR", gn, &p[i++]); 24201e196c8SJohn Marino if (!tname) 24301e196c8SJohn Marino tname = Var_Value(TARGET, gn, &p[i++]); 24401e196c8SJohn Marino 24501e196c8SJohn Marino if (realpath(dname, cwd)) 24601e196c8SJohn Marino dname = cwd; 24701e196c8SJohn Marino 24801e196c8SJohn Marino /* 24901e196c8SJohn Marino * Weed out relative paths from the target file name. 25001e196c8SJohn Marino * We have to be careful though since if target is a 25101e196c8SJohn Marino * symlink, the result will be unstable. 25201e196c8SJohn Marino * So we use realpath() just to get the dirname, and leave the 25301e196c8SJohn Marino * basename as given to us. 25401e196c8SJohn Marino */ 25501e196c8SJohn Marino if ((cp = strrchr(tname, '/'))) { 25601e196c8SJohn Marino if (realpath(tname, buf)) { 25701e196c8SJohn Marino if ((rp = strrchr(buf, '/'))) { 25801e196c8SJohn Marino rp++; 25901e196c8SJohn Marino cp++; 26001e196c8SJohn Marino if (strcmp(cp, rp) != 0) 26101e196c8SJohn Marino strlcpy(rp, cp, sizeof(buf) - (rp - buf)); 26201e196c8SJohn Marino } 26301e196c8SJohn Marino tname = buf; 26401e196c8SJohn Marino } else { 26501e196c8SJohn Marino /* 26601e196c8SJohn Marino * We likely have a directory which is about to be made. 26701e196c8SJohn Marino * We pretend realpath() succeeded, to have a chance 26801e196c8SJohn Marino * of generating the same meta file name that we will 26901e196c8SJohn Marino * next time through. 27001e196c8SJohn Marino */ 27101e196c8SJohn Marino if (tname[0] == '/') { 27201e196c8SJohn Marino strlcpy(buf, tname, sizeof(buf)); 27301e196c8SJohn Marino } else { 27401e196c8SJohn Marino snprintf(buf, sizeof(buf), "%s/%s", cwd, tname); 27501e196c8SJohn Marino } 27601e196c8SJohn Marino eat_dots(buf, sizeof(buf), 1); /* ./ */ 27701e196c8SJohn Marino eat_dots(buf, sizeof(buf), 2); /* ../ */ 27801e196c8SJohn Marino tname = buf; 27901e196c8SJohn Marino } 28001e196c8SJohn Marino } 28101e196c8SJohn Marino /* on some systems dirname may modify its arg */ 28201e196c8SJohn Marino tp = bmake_strdup(tname); 28301e196c8SJohn Marino if (strcmp(dname, dirname(tp)) == 0) 28401e196c8SJohn Marino snprintf(mname, mnamelen, "%s.meta", tname); 28501e196c8SJohn Marino else { 28601e196c8SJohn Marino snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); 28701e196c8SJohn Marino 28801e196c8SJohn Marino /* 28901e196c8SJohn Marino * Replace path separators in the file name after the 29001e196c8SJohn Marino * current object directory path. 29101e196c8SJohn Marino */ 29201e196c8SJohn Marino cp = mname + strlen(dname) + 1; 29301e196c8SJohn Marino 29401e196c8SJohn Marino while (*cp != '\0') { 29501e196c8SJohn Marino if (*cp == '/') 29601e196c8SJohn Marino *cp = '_'; 29701e196c8SJohn Marino cp++; 29801e196c8SJohn Marino } 29901e196c8SJohn Marino } 30001e196c8SJohn Marino free(tp); 30101e196c8SJohn Marino for (i--; i >= 0; i--) { 30201e196c8SJohn Marino if (p[i]) 30301e196c8SJohn Marino free(p[i]); 30401e196c8SJohn Marino } 30501e196c8SJohn Marino return (mname); 30601e196c8SJohn Marino } 30701e196c8SJohn Marino 30801e196c8SJohn Marino /* 30901e196c8SJohn Marino * Return true if running ${.MAKE} 31001e196c8SJohn Marino * Bypassed if target is flagged .MAKE 31101e196c8SJohn Marino */ 31201e196c8SJohn Marino static int 31301e196c8SJohn Marino is_submake(void *cmdp, void *gnp) 31401e196c8SJohn Marino { 31501e196c8SJohn Marino static char *p_make = NULL; 31601e196c8SJohn Marino static int p_len; 31701e196c8SJohn Marino char *cmd = cmdp; 31801e196c8SJohn Marino GNode *gn = gnp; 31901e196c8SJohn Marino char *mp = NULL; 32001e196c8SJohn Marino char *cp; 32101e196c8SJohn Marino char *cp2; 32201e196c8SJohn Marino int rc = 0; /* keep looking */ 32301e196c8SJohn Marino 32401e196c8SJohn Marino if (!p_make) { 32501e196c8SJohn Marino p_make = Var_Value(".MAKE", gn, &cp); 32601e196c8SJohn Marino p_len = strlen(p_make); 32701e196c8SJohn Marino } 32801e196c8SJohn Marino cp = strchr(cmd, '$'); 32901e196c8SJohn Marino if ((cp)) { 33001e196c8SJohn Marino mp = Var_Subst(NULL, cmd, gn, FALSE); 33101e196c8SJohn Marino cmd = mp; 33201e196c8SJohn Marino } 33301e196c8SJohn Marino cp2 = strstr(cmd, p_make); 33401e196c8SJohn Marino if ((cp2)) { 33501e196c8SJohn Marino switch (cp2[p_len]) { 33601e196c8SJohn Marino case '\0': 33701e196c8SJohn Marino case ' ': 33801e196c8SJohn Marino case '\t': 33901e196c8SJohn Marino case '\n': 34001e196c8SJohn Marino rc = 1; 34101e196c8SJohn Marino break; 34201e196c8SJohn Marino } 34301e196c8SJohn Marino if (cp2 > cmd && rc > 0) { 34401e196c8SJohn Marino switch (cp2[-1]) { 34501e196c8SJohn Marino case ' ': 34601e196c8SJohn Marino case '\t': 34701e196c8SJohn Marino case '\n': 34801e196c8SJohn Marino break; 34901e196c8SJohn Marino default: 35001e196c8SJohn Marino rc = 0; /* no match */ 35101e196c8SJohn Marino break; 35201e196c8SJohn Marino } 35301e196c8SJohn Marino } 35401e196c8SJohn Marino } 35501e196c8SJohn Marino if (mp) 35601e196c8SJohn Marino free(mp); 35701e196c8SJohn Marino return (rc); 35801e196c8SJohn Marino } 35901e196c8SJohn Marino 36001e196c8SJohn Marino typedef struct meta_file_s { 36101e196c8SJohn Marino FILE *fp; 36201e196c8SJohn Marino GNode *gn; 36301e196c8SJohn Marino } meta_file_t; 36401e196c8SJohn Marino 36501e196c8SJohn Marino static int 36601e196c8SJohn Marino printCMD(void *cmdp, void *mfpp) 36701e196c8SJohn Marino { 36801e196c8SJohn Marino meta_file_t *mfp = mfpp; 36901e196c8SJohn Marino char *cmd = cmdp; 37001e196c8SJohn Marino char *cp = NULL; 37101e196c8SJohn Marino 37201e196c8SJohn Marino if (strchr(cmd, '$')) { 37301e196c8SJohn Marino cmd = cp = Var_Subst(NULL, cmd, mfp->gn, FALSE); 37401e196c8SJohn Marino } 37501e196c8SJohn Marino fprintf(mfp->fp, "CMD %s\n", cmd); 37601e196c8SJohn Marino if (cp) 37701e196c8SJohn Marino free(cp); 37801e196c8SJohn Marino return 0; 37901e196c8SJohn Marino } 38001e196c8SJohn Marino 38101e196c8SJohn Marino /* 38201e196c8SJohn Marino * Certain node types never get a .meta file 38301e196c8SJohn Marino */ 38401e196c8SJohn Marino #define SKIP_META_TYPE(_type) do { \ 38501e196c8SJohn Marino if ((gn->type & __CONCAT(OP_, _type))) { \ 38601e196c8SJohn Marino if (DEBUG(META)) { \ 38701e196c8SJohn Marino fprintf(debug_file, "Skipping meta for %s: .%s\n", \ 38801e196c8SJohn Marino gn->name, __STRING(_type)); \ 38901e196c8SJohn Marino } \ 39001e196c8SJohn Marino return (NULL); \ 39101e196c8SJohn Marino } \ 39201e196c8SJohn Marino } while (0) 39301e196c8SJohn Marino 39401e196c8SJohn Marino static FILE * 39501e196c8SJohn Marino meta_create(BuildMon *pbm, GNode *gn) 39601e196c8SJohn Marino { 39701e196c8SJohn Marino meta_file_t mf; 39801e196c8SJohn Marino char buf[MAXPATHLEN]; 39901e196c8SJohn Marino char objdir[MAXPATHLEN]; 40001e196c8SJohn Marino char **ptr; 40101e196c8SJohn Marino const char *dname; 40201e196c8SJohn Marino const char *tname; 40301e196c8SJohn Marino char *fname; 40401e196c8SJohn Marino const char *cp; 40501e196c8SJohn Marino char *p[4]; /* >= possible uses */ 40601e196c8SJohn Marino int i; 40701e196c8SJohn Marino struct stat fs; 40801e196c8SJohn Marino 40901e196c8SJohn Marino 41001e196c8SJohn Marino /* This may be a phony node which we don't want meta data for... */ 41101e196c8SJohn Marino /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ 41201e196c8SJohn Marino /* Or it may be explicitly flagged as .NOMETA */ 41301e196c8SJohn Marino SKIP_META_TYPE(NOMETA); 41401e196c8SJohn Marino /* Unless it is explicitly flagged as .META */ 41501e196c8SJohn Marino if (!(gn->type & OP_META)) { 41601e196c8SJohn Marino SKIP_META_TYPE(PHONY); 41701e196c8SJohn Marino SKIP_META_TYPE(SPECIAL); 41801e196c8SJohn Marino SKIP_META_TYPE(MAKE); 41901e196c8SJohn Marino } 42001e196c8SJohn Marino 42101e196c8SJohn Marino mf.fp = NULL; 42201e196c8SJohn Marino 42301e196c8SJohn Marino i = 0; 42401e196c8SJohn Marino 42501e196c8SJohn Marino dname = Var_Value(".OBJDIR", gn, &p[i++]); 42601e196c8SJohn Marino tname = Var_Value(TARGET, gn, &p[i++]); 42701e196c8SJohn Marino 42801e196c8SJohn Marino /* The object directory may not exist. Check it.. */ 42901e196c8SJohn Marino if (stat(dname, &fs) != 0) { 43001e196c8SJohn Marino if (DEBUG(META)) 43101e196c8SJohn Marino fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", 43201e196c8SJohn Marino gn->name); 43301e196c8SJohn Marino goto out; 43401e196c8SJohn Marino } 43501e196c8SJohn Marino /* Check if there are no commands to execute. */ 43601e196c8SJohn Marino if (Lst_IsEmpty(gn->commands)) { 43701e196c8SJohn Marino if (DEBUG(META)) 43801e196c8SJohn Marino fprintf(debug_file, "Skipping meta for %s: no commands\n", 43901e196c8SJohn Marino gn->name); 44001e196c8SJohn Marino goto out; 44101e196c8SJohn Marino } 44201e196c8SJohn Marino 44301e196c8SJohn Marino /* make sure these are canonical */ 44401e196c8SJohn Marino if (realpath(dname, objdir)) 44501e196c8SJohn Marino dname = objdir; 44601e196c8SJohn Marino 44701e196c8SJohn Marino /* If we aren't in the object directory, don't create a meta file. */ 44801e196c8SJohn Marino if (!metaCurdirOk && strcmp(curdir, dname) == 0) { 44901e196c8SJohn Marino if (DEBUG(META)) 45001e196c8SJohn Marino fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", 45101e196c8SJohn Marino gn->name); 45201e196c8SJohn Marino goto out; 45301e196c8SJohn Marino } 45401e196c8SJohn Marino if (!(gn->type & OP_META)) { 45501e196c8SJohn Marino /* We do not generate .meta files for sub-makes */ 45601e196c8SJohn Marino if (Lst_ForEach(gn->commands, is_submake, gn)) { 45701e196c8SJohn Marino if (DEBUG(META)) 45801e196c8SJohn Marino fprintf(debug_file, "Skipping meta for %s: .MAKE\n", 45901e196c8SJohn Marino gn->name); 46001e196c8SJohn Marino goto out; 46101e196c8SJohn Marino } 46201e196c8SJohn Marino } 46301e196c8SJohn Marino 46401e196c8SJohn Marino if (metaVerbose) { 46501e196c8SJohn Marino char *mp; 46601e196c8SJohn Marino 46701e196c8SJohn Marino /* Describe the target we are building */ 46801e196c8SJohn Marino mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, 0); 46901e196c8SJohn Marino if (*mp) 47001e196c8SJohn Marino fprintf(stdout, "%s\n", mp); 47101e196c8SJohn Marino free(mp); 47201e196c8SJohn Marino } 47301e196c8SJohn Marino /* Get the basename of the target */ 47401e196c8SJohn Marino if ((cp = strrchr(tname, '/')) == NULL) { 47501e196c8SJohn Marino cp = tname; 47601e196c8SJohn Marino } else { 47701e196c8SJohn Marino cp++; 47801e196c8SJohn Marino } 47901e196c8SJohn Marino 48001e196c8SJohn Marino fflush(stdout); 48101e196c8SJohn Marino 48201e196c8SJohn Marino if (strcmp(cp, makeDependfile) == 0) 48301e196c8SJohn Marino goto out; 48401e196c8SJohn Marino 48501e196c8SJohn Marino if (!writeMeta) 48601e196c8SJohn Marino /* Don't create meta data. */ 48701e196c8SJohn Marino goto out; 48801e196c8SJohn Marino 48901e196c8SJohn Marino fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname), 49001e196c8SJohn Marino dname, tname); 49101e196c8SJohn Marino 49201e196c8SJohn Marino #ifdef DEBUG_META_MODE 49301e196c8SJohn Marino if (DEBUG(META)) 49401e196c8SJohn Marino fprintf(debug_file, "meta_create: %s\n", fname); 49501e196c8SJohn Marino #endif 49601e196c8SJohn Marino 49701e196c8SJohn Marino if ((mf.fp = fopen(fname, "w")) == NULL) 49801e196c8SJohn Marino err(1, "Could not open meta file '%s'", fname); 49901e196c8SJohn Marino 50001e196c8SJohn Marino fprintf(mf.fp, "# Meta data file %s\n", fname); 50101e196c8SJohn Marino 50201e196c8SJohn Marino mf.gn = gn; 50301e196c8SJohn Marino 50401e196c8SJohn Marino Lst_ForEach(gn->commands, printCMD, &mf); 50501e196c8SJohn Marino 50601e196c8SJohn Marino fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf))); 50701e196c8SJohn Marino fprintf(mf.fp, "TARGET %s\n", tname); 50801e196c8SJohn Marino 50901e196c8SJohn Marino if (metaEnv) { 51001e196c8SJohn Marino for (ptr = environ; *ptr != NULL; ptr++) 51101e196c8SJohn Marino fprintf(mf.fp, "ENV %s\n", *ptr); 51201e196c8SJohn Marino } 51301e196c8SJohn Marino 51401e196c8SJohn Marino fprintf(mf.fp, "-- command output --\n"); 51501e196c8SJohn Marino fflush(mf.fp); 51601e196c8SJohn Marino 51701e196c8SJohn Marino Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 51801e196c8SJohn Marino Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); 51901e196c8SJohn Marino 52001e196c8SJohn Marino gn->type |= OP_META; /* in case anyone wants to know */ 52101e196c8SJohn Marino if (metaSilent) { 52201e196c8SJohn Marino gn->type |= OP_SILENT; 52301e196c8SJohn Marino } 52401e196c8SJohn Marino out: 52501e196c8SJohn Marino for (i--; i >= 0; i--) { 52601e196c8SJohn Marino if (p[i]) 52701e196c8SJohn Marino free(p[i]); 52801e196c8SJohn Marino } 52901e196c8SJohn Marino 53001e196c8SJohn Marino return (mf.fp); 53101e196c8SJohn Marino } 53201e196c8SJohn Marino 53301e196c8SJohn Marino static Boolean 53401e196c8SJohn Marino boolValue(char *s) 53501e196c8SJohn Marino { 53601e196c8SJohn Marino switch(*s) { 53701e196c8SJohn Marino case '0': 53801e196c8SJohn Marino case 'N': 53901e196c8SJohn Marino case 'n': 54001e196c8SJohn Marino case 'F': 54101e196c8SJohn Marino case 'f': 54201e196c8SJohn Marino return FALSE; 54301e196c8SJohn Marino } 54401e196c8SJohn Marino return TRUE; 54501e196c8SJohn Marino } 54601e196c8SJohn Marino 547*5f1e34d9SAlexandre Perrin /* 548*5f1e34d9SAlexandre Perrin * Initialization we need before reading makefiles. 549*5f1e34d9SAlexandre Perrin */ 55001e196c8SJohn Marino void 551*5f1e34d9SAlexandre Perrin meta_init(void) 552*5f1e34d9SAlexandre Perrin { 553*5f1e34d9SAlexandre Perrin #ifdef USE_FILEMON 554*5f1e34d9SAlexandre Perrin /* this allows makefiles to test if we have filemon support */ 555*5f1e34d9SAlexandre Perrin Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0); 556*5f1e34d9SAlexandre Perrin #endif 557*5f1e34d9SAlexandre Perrin } 558*5f1e34d9SAlexandre Perrin 559*5f1e34d9SAlexandre Perrin 560*5f1e34d9SAlexandre Perrin /* 561*5f1e34d9SAlexandre Perrin * Initialization we need after reading makefiles. 562*5f1e34d9SAlexandre Perrin */ 563*5f1e34d9SAlexandre Perrin void 564*5f1e34d9SAlexandre Perrin meta_mode_init(const char *make_mode) 56501e196c8SJohn Marino { 56601e196c8SJohn Marino static int once = 0; 56701e196c8SJohn Marino char *cp; 56801e196c8SJohn Marino 56901e196c8SJohn Marino useMeta = TRUE; 57001e196c8SJohn Marino useFilemon = TRUE; 57101e196c8SJohn Marino writeMeta = TRUE; 57201e196c8SJohn Marino 57301e196c8SJohn Marino if (make_mode) { 57401e196c8SJohn Marino if (strstr(make_mode, "env")) 57501e196c8SJohn Marino metaEnv = TRUE; 57601e196c8SJohn Marino if (strstr(make_mode, "verb")) 57701e196c8SJohn Marino metaVerbose = TRUE; 57801e196c8SJohn Marino if (strstr(make_mode, "read")) 57901e196c8SJohn Marino writeMeta = FALSE; 58001e196c8SJohn Marino if (strstr(make_mode, "nofilemon")) 58101e196c8SJohn Marino useFilemon = FALSE; 58201e196c8SJohn Marino if ((cp = strstr(make_mode, "curdirok="))) { 58301e196c8SJohn Marino metaCurdirOk = boolValue(&cp[9]); 58401e196c8SJohn Marino } 58501e196c8SJohn Marino if ((cp = strstr(make_mode, "silent="))) { 58601e196c8SJohn Marino metaSilent = boolValue(&cp[7]); 58701e196c8SJohn Marino } 58801e196c8SJohn Marino if (strstr(make_mode, "ignore-cmd")) 58901e196c8SJohn Marino metaIgnoreCMDs = TRUE; 59001e196c8SJohn Marino /* for backwards compatability */ 59101e196c8SJohn Marino Var_Set(".MAKE.META_CREATED", "${.MAKE.META.CREATED}", VAR_GLOBAL, 0); 59201e196c8SJohn Marino Var_Set(".MAKE.META_FILES", "${.MAKE.META.FILES}", VAR_GLOBAL, 0); 59301e196c8SJohn Marino } 59401e196c8SJohn Marino if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { 59501e196c8SJohn Marino /* 59601e196c8SJohn Marino * The default value for MAKE_META_PREFIX 59701e196c8SJohn Marino * prints the absolute path of the target. 59801e196c8SJohn Marino * This works be cause :H will generate '.' if there is no / 59901e196c8SJohn Marino * and :tA will resolve that to cwd. 60001e196c8SJohn Marino */ 60101e196c8SJohn Marino Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0); 60201e196c8SJohn Marino } 60301e196c8SJohn Marino if (once) 60401e196c8SJohn Marino return; 60501e196c8SJohn Marino once = 1; 60601e196c8SJohn Marino memset(&Mybm, 0, sizeof(Mybm)); 60701e196c8SJohn Marino /* 60801e196c8SJohn Marino * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} 60901e196c8SJohn Marino */ 61001e196c8SJohn Marino metaBailiwick = Lst_Init(FALSE); 61101e196c8SJohn Marino cp = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", VAR_GLOBAL, 0); 61201e196c8SJohn Marino if (cp) { 61301e196c8SJohn Marino str2Lst_Append(metaBailiwick, cp, NULL); 61401e196c8SJohn Marino } 615*5f1e34d9SAlexandre Perrin /* 616*5f1e34d9SAlexandre Perrin * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} 617*5f1e34d9SAlexandre Perrin */ 618*5f1e34d9SAlexandre Perrin metaIgnorePaths = Lst_Init(FALSE); 619*5f1e34d9SAlexandre Perrin Var_Append(MAKE_META_IGNORE_PATHS, 620*5f1e34d9SAlexandre Perrin "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); 621*5f1e34d9SAlexandre Perrin cp = Var_Subst(NULL, 622*5f1e34d9SAlexandre Perrin "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL, 0); 623*5f1e34d9SAlexandre Perrin if (cp) { 624*5f1e34d9SAlexandre Perrin str2Lst_Append(metaIgnorePaths, cp, NULL); 625*5f1e34d9SAlexandre Perrin } 62601e196c8SJohn Marino } 62701e196c8SJohn Marino 62801e196c8SJohn Marino /* 62901e196c8SJohn Marino * In each case below we allow for job==NULL 63001e196c8SJohn Marino */ 63101e196c8SJohn Marino void 63201e196c8SJohn Marino meta_job_start(Job *job, GNode *gn) 63301e196c8SJohn Marino { 63401e196c8SJohn Marino BuildMon *pbm; 63501e196c8SJohn Marino 63601e196c8SJohn Marino if (job != NULL) { 63701e196c8SJohn Marino pbm = &job->bm; 63801e196c8SJohn Marino } else { 63901e196c8SJohn Marino pbm = &Mybm; 64001e196c8SJohn Marino } 64101e196c8SJohn Marino pbm->mfp = meta_create(pbm, gn); 64201e196c8SJohn Marino #ifdef USE_FILEMON_ONCE 64301e196c8SJohn Marino /* compat mode we open the filemon dev once per command */ 64401e196c8SJohn Marino if (job == NULL) 64501e196c8SJohn Marino return; 64601e196c8SJohn Marino #endif 64701e196c8SJohn Marino #ifdef USE_FILEMON 64801e196c8SJohn Marino if (pbm->mfp != NULL && useFilemon) { 64901e196c8SJohn Marino filemon_open(pbm); 65001e196c8SJohn Marino } else { 65101e196c8SJohn Marino pbm->mon_fd = pbm->filemon_fd = -1; 65201e196c8SJohn Marino } 65301e196c8SJohn Marino #endif 65401e196c8SJohn Marino } 65501e196c8SJohn Marino 65601e196c8SJohn Marino /* 65701e196c8SJohn Marino * The child calls this before doing anything. 65801e196c8SJohn Marino * It does not disturb our state. 65901e196c8SJohn Marino */ 66001e196c8SJohn Marino void 66101e196c8SJohn Marino meta_job_child(Job *job) 66201e196c8SJohn Marino { 66301e196c8SJohn Marino #ifdef USE_FILEMON 66401e196c8SJohn Marino BuildMon *pbm; 66501e196c8SJohn Marino pid_t pid; 66601e196c8SJohn Marino 66701e196c8SJohn Marino if (job != NULL) { 66801e196c8SJohn Marino pbm = &job->bm; 66901e196c8SJohn Marino } else { 67001e196c8SJohn Marino pbm = &Mybm; 67101e196c8SJohn Marino } 67201e196c8SJohn Marino pid = getpid(); 67301e196c8SJohn Marino if (pbm->mfp != NULL && useFilemon) { 67401e196c8SJohn Marino if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) { 67501e196c8SJohn Marino err(1, "Could not set filemon pid!"); 67601e196c8SJohn Marino } 67701e196c8SJohn Marino } 67801e196c8SJohn Marino #endif 67901e196c8SJohn Marino } 68001e196c8SJohn Marino 68101e196c8SJohn Marino void 68201e196c8SJohn Marino meta_job_error(Job *job, GNode *gn, int flags, int status) 68301e196c8SJohn Marino { 68401e196c8SJohn Marino char cwd[MAXPATHLEN]; 68501e196c8SJohn Marino BuildMon *pbm; 68601e196c8SJohn Marino 68701e196c8SJohn Marino if (job != NULL) { 68801e196c8SJohn Marino pbm = &job->bm; 68901e196c8SJohn Marino } else { 69001e196c8SJohn Marino if (!gn) 69101e196c8SJohn Marino gn = job->node; 69201e196c8SJohn Marino pbm = &Mybm; 69301e196c8SJohn Marino } 69401e196c8SJohn Marino if (pbm->mfp != NULL) { 69501e196c8SJohn Marino fprintf(pbm->mfp, "*** Error code %d%s\n", 69601e196c8SJohn Marino status, 69701e196c8SJohn Marino (flags & JOB_IGNERR) ? 69801e196c8SJohn Marino "(ignored)" : ""); 69901e196c8SJohn Marino } 70001e196c8SJohn Marino if (gn) { 70101e196c8SJohn Marino Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0); 70201e196c8SJohn Marino } 70301e196c8SJohn Marino getcwd(cwd, sizeof(cwd)); 70401e196c8SJohn Marino Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0); 70501e196c8SJohn Marino if (pbm && pbm->meta_fname[0]) { 70601e196c8SJohn Marino Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0); 70701e196c8SJohn Marino } 70801e196c8SJohn Marino meta_job_finish(job); 70901e196c8SJohn Marino } 71001e196c8SJohn Marino 71101e196c8SJohn Marino void 71201e196c8SJohn Marino meta_job_output(Job *job, char *cp, const char *nl) 71301e196c8SJohn Marino { 71401e196c8SJohn Marino BuildMon *pbm; 71501e196c8SJohn Marino 71601e196c8SJohn Marino if (job != NULL) { 71701e196c8SJohn Marino pbm = &job->bm; 71801e196c8SJohn Marino } else { 71901e196c8SJohn Marino pbm = &Mybm; 72001e196c8SJohn Marino } 72101e196c8SJohn Marino if (pbm->mfp != NULL) { 72201e196c8SJohn Marino if (metaVerbose) { 72301e196c8SJohn Marino static char *meta_prefix = NULL; 72401e196c8SJohn Marino static int meta_prefix_len; 72501e196c8SJohn Marino 72601e196c8SJohn Marino if (!meta_prefix) { 72701e196c8SJohn Marino char *cp2; 72801e196c8SJohn Marino 72901e196c8SJohn Marino meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", VAR_GLOBAL, 0); 73001e196c8SJohn Marino if ((cp2 = strchr(meta_prefix, '$'))) 73101e196c8SJohn Marino meta_prefix_len = cp2 - meta_prefix; 73201e196c8SJohn Marino else 73301e196c8SJohn Marino meta_prefix_len = strlen(meta_prefix); 73401e196c8SJohn Marino } 73501e196c8SJohn Marino if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { 73601e196c8SJohn Marino cp = strchr(cp+1, '\n'); 73701e196c8SJohn Marino if (!cp++) 73801e196c8SJohn Marino return; 73901e196c8SJohn Marino } 74001e196c8SJohn Marino } 74101e196c8SJohn Marino fprintf(pbm->mfp, "%s%s", cp, nl); 74201e196c8SJohn Marino } 74301e196c8SJohn Marino } 74401e196c8SJohn Marino 74501e196c8SJohn Marino void 74601e196c8SJohn Marino meta_cmd_finish(void *pbmp) 74701e196c8SJohn Marino { 74801e196c8SJohn Marino #ifdef USE_FILEMON 74901e196c8SJohn Marino BuildMon *pbm = pbmp; 75001e196c8SJohn Marino 75101e196c8SJohn Marino if (!pbm) 75201e196c8SJohn Marino pbm = &Mybm; 75301e196c8SJohn Marino 75401e196c8SJohn Marino if (pbm->filemon_fd >= 0) { 75501e196c8SJohn Marino close(pbm->filemon_fd); 75601e196c8SJohn Marino filemon_read(pbm->mfp, pbm->mon_fd); 75701e196c8SJohn Marino pbm->filemon_fd = pbm->mon_fd = -1; 75801e196c8SJohn Marino } 75901e196c8SJohn Marino #endif 76001e196c8SJohn Marino } 76101e196c8SJohn Marino 76201e196c8SJohn Marino void 76301e196c8SJohn Marino meta_job_finish(Job *job) 76401e196c8SJohn Marino { 76501e196c8SJohn Marino BuildMon *pbm; 76601e196c8SJohn Marino 76701e196c8SJohn Marino if (job != NULL) { 76801e196c8SJohn Marino pbm = &job->bm; 76901e196c8SJohn Marino } else { 77001e196c8SJohn Marino pbm = &Mybm; 77101e196c8SJohn Marino } 77201e196c8SJohn Marino if (pbm->mfp != NULL) { 77301e196c8SJohn Marino meta_cmd_finish(pbm); 77401e196c8SJohn Marino fclose(pbm->mfp); 77501e196c8SJohn Marino pbm->mfp = NULL; 77601e196c8SJohn Marino pbm->meta_fname[0] = '\0'; 77701e196c8SJohn Marino } 77801e196c8SJohn Marino } 77901e196c8SJohn Marino 78001e196c8SJohn Marino /* 78101e196c8SJohn Marino * Fetch a full line from fp - growing bufp if needed 78201e196c8SJohn Marino * Return length in bufp. 78301e196c8SJohn Marino */ 78401e196c8SJohn Marino static int 78501e196c8SJohn Marino fgetLine(char **bufp, size_t *szp, int o, FILE *fp) 78601e196c8SJohn Marino { 78701e196c8SJohn Marino char *buf = *bufp; 78801e196c8SJohn Marino size_t bufsz = *szp; 78901e196c8SJohn Marino struct stat fs; 79001e196c8SJohn Marino int x; 79101e196c8SJohn Marino 79201e196c8SJohn Marino if (fgets(&buf[o], bufsz - o, fp) != NULL) { 79301e196c8SJohn Marino check_newline: 79401e196c8SJohn Marino x = o + strlen(&buf[o]); 79501e196c8SJohn Marino if (buf[x - 1] == '\n') 79601e196c8SJohn Marino return x; 79701e196c8SJohn Marino /* 79801e196c8SJohn Marino * We need to grow the buffer. 79901e196c8SJohn Marino * The meta file can give us a clue. 80001e196c8SJohn Marino */ 80101e196c8SJohn Marino if (fstat(fileno(fp), &fs) == 0) { 80201e196c8SJohn Marino size_t newsz; 80301e196c8SJohn Marino char *p; 80401e196c8SJohn Marino 80501e196c8SJohn Marino newsz = ROUNDUP((fs.st_size / 2), BUFSIZ); 80601e196c8SJohn Marino if (newsz <= bufsz) 80701e196c8SJohn Marino newsz = ROUNDUP(fs.st_size, BUFSIZ); 80801e196c8SJohn Marino if (DEBUG(META)) 80901e196c8SJohn Marino fprintf(debug_file, "growing buffer %u -> %u\n", 81001e196c8SJohn Marino (unsigned)bufsz, (unsigned)newsz); 81101e196c8SJohn Marino p = bmake_realloc(buf, newsz); 81201e196c8SJohn Marino if (p) { 81301e196c8SJohn Marino *bufp = buf = p; 81401e196c8SJohn Marino *szp = bufsz = newsz; 81501e196c8SJohn Marino /* fetch the rest */ 81601e196c8SJohn Marino if (!fgets(&buf[x], bufsz - x, fp)) 81701e196c8SJohn Marino return x; /* truncated! */ 81801e196c8SJohn Marino goto check_newline; 81901e196c8SJohn Marino } 82001e196c8SJohn Marino } 82101e196c8SJohn Marino } 82201e196c8SJohn Marino return 0; 82301e196c8SJohn Marino } 82401e196c8SJohn Marino 82501e196c8SJohn Marino static int 82601e196c8SJohn Marino prefix_match(void *p, void *q) 82701e196c8SJohn Marino { 82801e196c8SJohn Marino const char *prefix = p; 82901e196c8SJohn Marino const char *path = q; 83001e196c8SJohn Marino size_t n = strlen(prefix); 83101e196c8SJohn Marino 83201e196c8SJohn Marino return (0 == strncmp(path, prefix, n)); 83301e196c8SJohn Marino } 83401e196c8SJohn Marino 83501e196c8SJohn Marino static int 83601e196c8SJohn Marino string_match(const void *p, const void *q) 83701e196c8SJohn Marino { 83801e196c8SJohn Marino const char *p1 = p; 83901e196c8SJohn Marino const char *p2 = q; 84001e196c8SJohn Marino 84101e196c8SJohn Marino return strcmp(p1, p2); 84201e196c8SJohn Marino } 84301e196c8SJohn Marino 84401e196c8SJohn Marino 84501e196c8SJohn Marino /* 84601e196c8SJohn Marino * When running with 'meta' functionality, a target can be out-of-date 84701e196c8SJohn Marino * if any of the references in it's meta data file is more recent. 84801e196c8SJohn Marino * We have to track the latestdir on a per-process basis. 84901e196c8SJohn Marino */ 85001e196c8SJohn Marino #define LDIR_VNAME_FMT ".meta.%d.ldir" 85101e196c8SJohn Marino 85201e196c8SJohn Marino /* 85301e196c8SJohn Marino * It is possible that a .meta file is corrupted, 85401e196c8SJohn Marino * if we detect this we want to reproduce it. 85501e196c8SJohn Marino * Setting oodate TRUE will have that effect. 85601e196c8SJohn Marino */ 85701e196c8SJohn Marino #define CHECK_VALID_META(p) if (!(p && *p)) { \ 85801e196c8SJohn Marino warnx("%s: %d: malformed", fname, lineno); \ 85901e196c8SJohn Marino oodate = TRUE; \ 86001e196c8SJohn Marino continue; \ 86101e196c8SJohn Marino } 86201e196c8SJohn Marino 863*5f1e34d9SAlexandre Perrin #define DEQUOTE(p) if (*p == '\'') { \ 864*5f1e34d9SAlexandre Perrin char *ep; \ 865*5f1e34d9SAlexandre Perrin p++; \ 866*5f1e34d9SAlexandre Perrin if ((ep = strchr(p, '\''))) \ 867*5f1e34d9SAlexandre Perrin *ep = '\0'; \ 868*5f1e34d9SAlexandre Perrin } 869*5f1e34d9SAlexandre Perrin 87001e196c8SJohn Marino Boolean 87101e196c8SJohn Marino meta_oodate(GNode *gn, Boolean oodate) 87201e196c8SJohn Marino { 87301e196c8SJohn Marino static char *tmpdir = NULL; 87401e196c8SJohn Marino static char cwd[MAXPATHLEN]; 87501e196c8SJohn Marino char ldir_vname[64]; 87601e196c8SJohn Marino char latestdir[MAXPATHLEN]; 87701e196c8SJohn Marino char fname[MAXPATHLEN]; 87801e196c8SJohn Marino char fname1[MAXPATHLEN]; 87901e196c8SJohn Marino char fname2[MAXPATHLEN]; 88001e196c8SJohn Marino char *p; 88101e196c8SJohn Marino char *cp; 882*5f1e34d9SAlexandre Perrin char *link_src; 883*5f1e34d9SAlexandre Perrin char *move_target; 88401e196c8SJohn Marino static size_t cwdlen = 0; 88501e196c8SJohn Marino static size_t tmplen = 0; 88601e196c8SJohn Marino FILE *fp; 887*5f1e34d9SAlexandre Perrin Boolean needOODATE = FALSE; 88801e196c8SJohn Marino Lst missingFiles; 88901e196c8SJohn Marino 89001e196c8SJohn Marino if (oodate) 89101e196c8SJohn Marino return oodate; /* we're done */ 89201e196c8SJohn Marino 89301e196c8SJohn Marino missingFiles = Lst_Init(FALSE); 89401e196c8SJohn Marino 89501e196c8SJohn Marino /* 89601e196c8SJohn Marino * We need to check if the target is out-of-date. This includes 89701e196c8SJohn Marino * checking if the expanded command has changed. This in turn 89801e196c8SJohn Marino * requires that all variables are set in the same way that they 89901e196c8SJohn Marino * would be if the target needs to be re-built. 90001e196c8SJohn Marino */ 90101e196c8SJohn Marino Make_DoAllVar(gn); 90201e196c8SJohn Marino 90301e196c8SJohn Marino meta_name(gn, fname, sizeof(fname), NULL, NULL); 90401e196c8SJohn Marino 90501e196c8SJohn Marino #ifdef DEBUG_META_MODE 90601e196c8SJohn Marino if (DEBUG(META)) 90701e196c8SJohn Marino fprintf(debug_file, "meta_oodate: %s\n", fname); 90801e196c8SJohn Marino #endif 90901e196c8SJohn Marino 91001e196c8SJohn Marino if ((fp = fopen(fname, "r")) != NULL) { 91101e196c8SJohn Marino static char *buf = NULL; 91201e196c8SJohn Marino static size_t bufsz; 91301e196c8SJohn Marino int lineno = 0; 91401e196c8SJohn Marino int lastpid = 0; 91501e196c8SJohn Marino int pid; 91601e196c8SJohn Marino int f = 0; 91701e196c8SJohn Marino int x; 91801e196c8SJohn Marino LstNode ln; 91901e196c8SJohn Marino struct stat fs; 92001e196c8SJohn Marino 92101e196c8SJohn Marino if (!buf) { 92201e196c8SJohn Marino bufsz = 8 * BUFSIZ; 92301e196c8SJohn Marino buf = bmake_malloc(bufsz); 92401e196c8SJohn Marino } 92501e196c8SJohn Marino 92601e196c8SJohn Marino if (!cwdlen) { 92701e196c8SJohn Marino if (getcwd(cwd, sizeof(cwd)) == NULL) 92801e196c8SJohn Marino err(1, "Could not get current working directory"); 92901e196c8SJohn Marino cwdlen = strlen(cwd); 93001e196c8SJohn Marino } 93101e196c8SJohn Marino 93201e196c8SJohn Marino if (!tmpdir) { 93301e196c8SJohn Marino tmpdir = getTmpdir(); 93401e196c8SJohn Marino tmplen = strlen(tmpdir); 93501e196c8SJohn Marino } 93601e196c8SJohn Marino 93701e196c8SJohn Marino /* we want to track all the .meta we read */ 93801e196c8SJohn Marino Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); 93901e196c8SJohn Marino 94001e196c8SJohn Marino ln = Lst_First(gn->commands); 94101e196c8SJohn Marino while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { 94201e196c8SJohn Marino lineno++; 94301e196c8SJohn Marino if (buf[x - 1] == '\n') 94401e196c8SJohn Marino buf[x - 1] = '\0'; 94501e196c8SJohn Marino else { 94601e196c8SJohn Marino warnx("%s: %d: line truncated at %u", fname, lineno, x); 94701e196c8SJohn Marino oodate = TRUE; 94801e196c8SJohn Marino break; 94901e196c8SJohn Marino } 950*5f1e34d9SAlexandre Perrin link_src = NULL; 951*5f1e34d9SAlexandre Perrin move_target = NULL; 95201e196c8SJohn Marino /* Find the start of the build monitor section. */ 95301e196c8SJohn Marino if (!f) { 95401e196c8SJohn Marino if (strncmp(buf, "-- filemon", 10) == 0) { 95501e196c8SJohn Marino f = 1; 95601e196c8SJohn Marino continue; 95701e196c8SJohn Marino } 95801e196c8SJohn Marino if (strncmp(buf, "# buildmon", 10) == 0) { 95901e196c8SJohn Marino f = 1; 96001e196c8SJohn Marino continue; 96101e196c8SJohn Marino } 96201e196c8SJohn Marino } 96301e196c8SJohn Marino 96401e196c8SJohn Marino /* Delimit the record type. */ 96501e196c8SJohn Marino p = buf; 96601e196c8SJohn Marino #ifdef DEBUG_META_MODE 96701e196c8SJohn Marino if (DEBUG(META)) 96801e196c8SJohn Marino fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); 96901e196c8SJohn Marino #endif 97001e196c8SJohn Marino strsep(&p, " "); 97101e196c8SJohn Marino if (f) { 97201e196c8SJohn Marino /* 97301e196c8SJohn Marino * We are in the 'filemon' output section. 97401e196c8SJohn Marino * Each record from filemon follows the general form: 97501e196c8SJohn Marino * 97601e196c8SJohn Marino * <key> <pid> <data> 97701e196c8SJohn Marino * 97801e196c8SJohn Marino * Where: 97901e196c8SJohn Marino * <key> is a single letter, denoting the syscall. 98001e196c8SJohn Marino * <pid> is the process that made the syscall. 98101e196c8SJohn Marino * <data> is the arguments (of interest). 98201e196c8SJohn Marino */ 98301e196c8SJohn Marino switch(buf[0]) { 98401e196c8SJohn Marino case '#': /* comment */ 98501e196c8SJohn Marino case 'V': /* version */ 98601e196c8SJohn Marino break; 98701e196c8SJohn Marino default: 98801e196c8SJohn Marino /* 98901e196c8SJohn Marino * We need to track pathnames per-process. 99001e196c8SJohn Marino * 99101e196c8SJohn Marino * Each process run by make, starts off in the 'CWD' 99201e196c8SJohn Marino * recorded in the .meta file, if it chdirs ('C') 99301e196c8SJohn Marino * elsewhere we need to track that - but only for 99401e196c8SJohn Marino * that process. If it forks ('F'), we initialize 99501e196c8SJohn Marino * the child to have the same cwd as its parent. 99601e196c8SJohn Marino * 99701e196c8SJohn Marino * We also need to track the 'latestdir' of 99801e196c8SJohn Marino * interest. This is usually the same as cwd, but 99901e196c8SJohn Marino * not if a process is reading directories. 100001e196c8SJohn Marino * 100101e196c8SJohn Marino * Each time we spot a different process ('pid') 100201e196c8SJohn Marino * we save the current value of 'latestdir' in a 100301e196c8SJohn Marino * variable qualified by 'lastpid', and 100401e196c8SJohn Marino * re-initialize 'latestdir' to any pre-saved 100501e196c8SJohn Marino * value for the current 'pid' and 'CWD' if none. 100601e196c8SJohn Marino */ 100701e196c8SJohn Marino CHECK_VALID_META(p); 100801e196c8SJohn Marino pid = atoi(p); 100901e196c8SJohn Marino if (pid > 0 && pid != lastpid) { 101001e196c8SJohn Marino char *ldir; 101101e196c8SJohn Marino char *tp; 101201e196c8SJohn Marino 101301e196c8SJohn Marino if (lastpid > 0) { 101401e196c8SJohn Marino /* We need to remember this. */ 101501e196c8SJohn Marino Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0); 101601e196c8SJohn Marino } 101701e196c8SJohn Marino snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); 101801e196c8SJohn Marino lastpid = pid; 101901e196c8SJohn Marino ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); 102001e196c8SJohn Marino if (ldir) { 102101e196c8SJohn Marino strlcpy(latestdir, ldir, sizeof(latestdir)); 102201e196c8SJohn Marino if (tp) 102301e196c8SJohn Marino free(tp); 102401e196c8SJohn Marino } else 102501e196c8SJohn Marino strlcpy(latestdir, cwd, sizeof(latestdir)); 102601e196c8SJohn Marino } 102701e196c8SJohn Marino /* Skip past the pid. */ 102801e196c8SJohn Marino if (strsep(&p, " ") == NULL) 102901e196c8SJohn Marino continue; 103001e196c8SJohn Marino #ifdef DEBUG_META_MODE 103101e196c8SJohn Marino if (DEBUG(META)) 103201e196c8SJohn Marino fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, latestdir); 103301e196c8SJohn Marino #endif 103401e196c8SJohn Marino break; 103501e196c8SJohn Marino } 103601e196c8SJohn Marino 103701e196c8SJohn Marino CHECK_VALID_META(p); 103801e196c8SJohn Marino 103901e196c8SJohn Marino /* Process according to record type. */ 104001e196c8SJohn Marino switch (buf[0]) { 104101e196c8SJohn Marino case 'X': /* eXit */ 104201e196c8SJohn Marino Var_Delete(ldir_vname, VAR_GLOBAL); 104301e196c8SJohn Marino lastpid = 0; /* no need to save ldir_vname */ 104401e196c8SJohn Marino break; 104501e196c8SJohn Marino 104601e196c8SJohn Marino case 'F': /* [v]Fork */ 104701e196c8SJohn Marino { 104801e196c8SJohn Marino char cldir[64]; 104901e196c8SJohn Marino int child; 105001e196c8SJohn Marino 105101e196c8SJohn Marino child = atoi(p); 105201e196c8SJohn Marino if (child > 0) { 105301e196c8SJohn Marino snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); 105401e196c8SJohn Marino Var_Set(cldir, latestdir, VAR_GLOBAL, 0); 105501e196c8SJohn Marino } 105601e196c8SJohn Marino } 105701e196c8SJohn Marino break; 105801e196c8SJohn Marino 105901e196c8SJohn Marino case 'C': /* Chdir */ 106001e196c8SJohn Marino /* Update the latest directory. */ 106101e196c8SJohn Marino strlcpy(latestdir, p, sizeof(latestdir)); 106201e196c8SJohn Marino break; 106301e196c8SJohn Marino 106401e196c8SJohn Marino case 'M': /* renaMe */ 1065*5f1e34d9SAlexandre Perrin /* 1066*5f1e34d9SAlexandre Perrin * For 'M'oves we want to check 1067*5f1e34d9SAlexandre Perrin * the src as for 'R'ead 1068*5f1e34d9SAlexandre Perrin * and the target as for 'W'rite. 1069*5f1e34d9SAlexandre Perrin */ 1070*5f1e34d9SAlexandre Perrin cp = p; /* save this for a second */ 1071*5f1e34d9SAlexandre Perrin /* now get target */ 1072*5f1e34d9SAlexandre Perrin if (strsep(&p, " ") == NULL) 1073*5f1e34d9SAlexandre Perrin continue; 1074*5f1e34d9SAlexandre Perrin CHECK_VALID_META(p); 1075*5f1e34d9SAlexandre Perrin move_target = p; 1076*5f1e34d9SAlexandre Perrin p = cp; 107701e196c8SJohn Marino /* 'L' and 'M' put single quotes around the args */ 1078*5f1e34d9SAlexandre Perrin DEQUOTE(p); 1079*5f1e34d9SAlexandre Perrin DEQUOTE(move_target); 108001e196c8SJohn Marino /* FALLTHROUGH */ 108101e196c8SJohn Marino case 'D': /* unlink */ 108201e196c8SJohn Marino if (*p == '/' && !Lst_IsEmpty(missingFiles)) { 108301e196c8SJohn Marino /* remove p from the missingFiles list if present */ 108401e196c8SJohn Marino if ((ln = Lst_Find(missingFiles, p, string_match)) != NULL) { 108501e196c8SJohn Marino char *tp = Lst_Datum(ln); 108601e196c8SJohn Marino Lst_Remove(missingFiles, ln); 108701e196c8SJohn Marino free(tp); 1088*5f1e34d9SAlexandre Perrin ln = NULL; /* we're done with it */ 108901e196c8SJohn Marino } 109001e196c8SJohn Marino } 1091*5f1e34d9SAlexandre Perrin if (buf[0] == 'M') { 1092*5f1e34d9SAlexandre Perrin /* the target of the mv is a file 'W'ritten */ 1093*5f1e34d9SAlexandre Perrin #ifdef DEBUG_META_MODE 1094*5f1e34d9SAlexandre Perrin if (DEBUG(META)) 1095*5f1e34d9SAlexandre Perrin fprintf(debug_file, "meta_oodate: M %s -> %s\n", 1096*5f1e34d9SAlexandre Perrin p, move_target); 1097*5f1e34d9SAlexandre Perrin #endif 1098*5f1e34d9SAlexandre Perrin p = move_target; 1099*5f1e34d9SAlexandre Perrin goto check_write; 1100*5f1e34d9SAlexandre Perrin } 110101e196c8SJohn Marino break; 110201e196c8SJohn Marino case 'L': /* Link */ 1103*5f1e34d9SAlexandre Perrin /* 1104*5f1e34d9SAlexandre Perrin * For 'L'inks check 1105*5f1e34d9SAlexandre Perrin * the src as for 'R'ead 1106*5f1e34d9SAlexandre Perrin * and the target as for 'W'rite. 1107*5f1e34d9SAlexandre Perrin */ 1108*5f1e34d9SAlexandre Perrin link_src = p; 1109*5f1e34d9SAlexandre Perrin /* now get target */ 111001e196c8SJohn Marino if (strsep(&p, " ") == NULL) 111101e196c8SJohn Marino continue; 111201e196c8SJohn Marino CHECK_VALID_META(p); 111301e196c8SJohn Marino /* 'L' and 'M' put single quotes around the args */ 1114*5f1e34d9SAlexandre Perrin DEQUOTE(p); 1115*5f1e34d9SAlexandre Perrin DEQUOTE(link_src); 1116*5f1e34d9SAlexandre Perrin #ifdef DEBUG_META_MODE 1117*5f1e34d9SAlexandre Perrin if (DEBUG(META)) 1118*5f1e34d9SAlexandre Perrin fprintf(debug_file, "meta_oodate: L %s -> %s\n", 1119*5f1e34d9SAlexandre Perrin link_src, p); 1120*5f1e34d9SAlexandre Perrin #endif 112101e196c8SJohn Marino /* FALLTHROUGH */ 112201e196c8SJohn Marino case 'W': /* Write */ 1123*5f1e34d9SAlexandre Perrin check_write: 112401e196c8SJohn Marino /* 112501e196c8SJohn Marino * If a file we generated within our bailiwick 112601e196c8SJohn Marino * but outside of .OBJDIR is missing, 112701e196c8SJohn Marino * we need to do it again. 112801e196c8SJohn Marino */ 112901e196c8SJohn Marino /* ignore non-absolute paths */ 113001e196c8SJohn Marino if (*p != '/') 113101e196c8SJohn Marino break; 113201e196c8SJohn Marino 113301e196c8SJohn Marino if (Lst_IsEmpty(metaBailiwick)) 113401e196c8SJohn Marino break; 113501e196c8SJohn Marino 113601e196c8SJohn Marino /* ignore cwd - normal dependencies handle those */ 113701e196c8SJohn Marino if (strncmp(p, cwd, cwdlen) == 0) 113801e196c8SJohn Marino break; 113901e196c8SJohn Marino 114001e196c8SJohn Marino if (!Lst_ForEach(metaBailiwick, prefix_match, p)) 114101e196c8SJohn Marino break; 114201e196c8SJohn Marino 114301e196c8SJohn Marino /* tmpdir might be within */ 114401e196c8SJohn Marino if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) 114501e196c8SJohn Marino break; 114601e196c8SJohn Marino 114701e196c8SJohn Marino /* ignore anything containing the string "tmp" */ 114801e196c8SJohn Marino if ((strstr("tmp", p))) 114901e196c8SJohn Marino break; 115001e196c8SJohn Marino 115101e196c8SJohn Marino if (stat(p, &fs) < 0) { 115201e196c8SJohn Marino Lst_AtEnd(missingFiles, bmake_strdup(p)); 115301e196c8SJohn Marino } 115401e196c8SJohn Marino break; 1155*5f1e34d9SAlexandre Perrin check_link_src: 1156*5f1e34d9SAlexandre Perrin p = link_src; 1157*5f1e34d9SAlexandre Perrin link_src = NULL; 1158*5f1e34d9SAlexandre Perrin #ifdef DEBUG_META_MODE 1159*5f1e34d9SAlexandre Perrin if (DEBUG(META)) 1160*5f1e34d9SAlexandre Perrin fprintf(debug_file, "meta_oodate: L src %s\n", p); 1161*5f1e34d9SAlexandre Perrin #endif 1162*5f1e34d9SAlexandre Perrin /* FALLTHROUGH */ 116301e196c8SJohn Marino case 'R': /* Read */ 116401e196c8SJohn Marino case 'E': /* Exec */ 116501e196c8SJohn Marino /* 116601e196c8SJohn Marino * Check for runtime files that can't 116701e196c8SJohn Marino * be part of the dependencies because 116801e196c8SJohn Marino * they are _expected_ to change. 116901e196c8SJohn Marino */ 1170*5f1e34d9SAlexandre Perrin if (*p == '/' && 1171*5f1e34d9SAlexandre Perrin Lst_ForEach(metaIgnorePaths, prefix_match, p)) { 1172*5f1e34d9SAlexandre Perrin #ifdef DEBUG_META_MODE 1173*5f1e34d9SAlexandre Perrin if (DEBUG(META)) 1174*5f1e34d9SAlexandre Perrin fprintf(debug_file, "meta_oodate: ignoring: %s\n", 1175*5f1e34d9SAlexandre Perrin p); 1176*5f1e34d9SAlexandre Perrin #endif 117701e196c8SJohn Marino break; 1178*5f1e34d9SAlexandre Perrin } 117901e196c8SJohn Marino 118001e196c8SJohn Marino if ((cp = strrchr(p, '/'))) { 118101e196c8SJohn Marino cp++; 118201e196c8SJohn Marino /* 118301e196c8SJohn Marino * We don't normally expect to see this, 118401e196c8SJohn Marino * but we do expect it to change. 118501e196c8SJohn Marino */ 118601e196c8SJohn Marino if (strcmp(cp, makeDependfile) == 0) 118701e196c8SJohn Marino break; 118801e196c8SJohn Marino } 118901e196c8SJohn Marino 119001e196c8SJohn Marino /* 119101e196c8SJohn Marino * The rest of the record is the file name. 119201e196c8SJohn Marino * Check if it's not an absolute path. 119301e196c8SJohn Marino */ 119401e196c8SJohn Marino { 119501e196c8SJohn Marino char *sdirs[4]; 119601e196c8SJohn Marino char **sdp; 119701e196c8SJohn Marino int sdx = 0; 119801e196c8SJohn Marino int found = 0; 119901e196c8SJohn Marino 120001e196c8SJohn Marino if (*p == '/') { 120101e196c8SJohn Marino sdirs[sdx++] = p; /* done */ 120201e196c8SJohn Marino } else { 120301e196c8SJohn Marino if (strcmp(".", p) == 0) 120401e196c8SJohn Marino continue; /* no point */ 120501e196c8SJohn Marino 120601e196c8SJohn Marino /* Check vs latestdir */ 120701e196c8SJohn Marino snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); 120801e196c8SJohn Marino sdirs[sdx++] = fname1; 120901e196c8SJohn Marino 121001e196c8SJohn Marino if (strcmp(latestdir, cwd) != 0) { 121101e196c8SJohn Marino /* Check vs cwd */ 121201e196c8SJohn Marino snprintf(fname2, sizeof(fname2), "%s/%s", cwd, p); 121301e196c8SJohn Marino sdirs[sdx++] = fname2; 121401e196c8SJohn Marino } 121501e196c8SJohn Marino } 121601e196c8SJohn Marino sdirs[sdx++] = NULL; 121701e196c8SJohn Marino 121801e196c8SJohn Marino for (sdp = sdirs; *sdp && !found; sdp++) { 121901e196c8SJohn Marino #ifdef DEBUG_META_MODE 122001e196c8SJohn Marino if (DEBUG(META)) 122101e196c8SJohn Marino fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); 122201e196c8SJohn Marino #endif 122301e196c8SJohn Marino if (stat(*sdp, &fs) == 0) { 122401e196c8SJohn Marino found = 1; 122501e196c8SJohn Marino p = *sdp; 122601e196c8SJohn Marino } 122701e196c8SJohn Marino } 122801e196c8SJohn Marino if (found) { 122901e196c8SJohn Marino #ifdef DEBUG_META_MODE 123001e196c8SJohn Marino if (DEBUG(META)) 123101e196c8SJohn Marino fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); 123201e196c8SJohn Marino #endif 123301e196c8SJohn Marino if (!S_ISDIR(fs.st_mode) && 123401e196c8SJohn Marino fs.st_mtime > gn->mtime) { 123501e196c8SJohn Marino if (DEBUG(META)) 123601e196c8SJohn Marino fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); 123701e196c8SJohn Marino oodate = TRUE; 123801e196c8SJohn Marino } else if (S_ISDIR(fs.st_mode)) { 123901e196c8SJohn Marino /* Update the latest directory. */ 124001e196c8SJohn Marino realpath(p, latestdir); 124101e196c8SJohn Marino } 124201e196c8SJohn Marino } else if (errno == ENOENT && *p == '/' && 124301e196c8SJohn Marino strncmp(p, cwd, cwdlen) != 0) { 124401e196c8SJohn Marino /* 124501e196c8SJohn Marino * A referenced file outside of CWD is missing. 124601e196c8SJohn Marino * We cannot catch every eventuality here... 124701e196c8SJohn Marino */ 124801e196c8SJohn Marino if (DEBUG(META)) 124901e196c8SJohn Marino fprintf(debug_file, "%s: %d: file '%s' may have moved?...\n", fname, lineno, p); 125001e196c8SJohn Marino oodate = TRUE; 125101e196c8SJohn Marino } 125201e196c8SJohn Marino } 125301e196c8SJohn Marino break; 125401e196c8SJohn Marino default: 125501e196c8SJohn Marino break; 125601e196c8SJohn Marino } 1257*5f1e34d9SAlexandre Perrin if (!oodate && buf[0] == 'L' && link_src != NULL) 1258*5f1e34d9SAlexandre Perrin goto check_link_src; 125901e196c8SJohn Marino } else if (strcmp(buf, "CMD") == 0) { 126001e196c8SJohn Marino /* 126101e196c8SJohn Marino * Compare the current command with the one in the 126201e196c8SJohn Marino * meta data file. 126301e196c8SJohn Marino */ 126401e196c8SJohn Marino if (ln == NULL) { 126501e196c8SJohn Marino if (DEBUG(META)) 126601e196c8SJohn Marino fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); 126701e196c8SJohn Marino oodate = TRUE; 126801e196c8SJohn Marino } else { 126901e196c8SJohn Marino char *cmd = (char *)Lst_Datum(ln); 1270*5f1e34d9SAlexandre Perrin Boolean hasOODATE = FALSE; 127101e196c8SJohn Marino 127201e196c8SJohn Marino if (strstr(cmd, "$?")) 1273*5f1e34d9SAlexandre Perrin hasOODATE = TRUE; 127401e196c8SJohn Marino else if ((cp = strstr(cmd, ".OODATE"))) { 1275*5f1e34d9SAlexandre Perrin /* check for $[{(].OODATE[:)}] */ 127601e196c8SJohn Marino if (cp > cmd + 2 && cp[-2] == '$') 1277*5f1e34d9SAlexandre Perrin hasOODATE = TRUE; 127801e196c8SJohn Marino } 1279*5f1e34d9SAlexandre Perrin if (hasOODATE) { 1280*5f1e34d9SAlexandre Perrin needOODATE = TRUE; 1281*5f1e34d9SAlexandre Perrin if (DEBUG(META)) 1282*5f1e34d9SAlexandre Perrin fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno); 128301e196c8SJohn Marino } 128401e196c8SJohn Marino cmd = Var_Subst(NULL, cmd, gn, TRUE); 128501e196c8SJohn Marino 128601e196c8SJohn Marino if ((cp = strchr(cmd, '\n'))) { 128701e196c8SJohn Marino int n; 128801e196c8SJohn Marino 128901e196c8SJohn Marino /* 129001e196c8SJohn Marino * This command contains newlines, we need to 129101e196c8SJohn Marino * fetch more from the .meta file before we 129201e196c8SJohn Marino * attempt a comparison. 129301e196c8SJohn Marino */ 129401e196c8SJohn Marino /* first put the newline back at buf[x - 1] */ 129501e196c8SJohn Marino buf[x - 1] = '\n'; 129601e196c8SJohn Marino do { 129701e196c8SJohn Marino /* now fetch the next line */ 129801e196c8SJohn Marino if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) 129901e196c8SJohn Marino break; 130001e196c8SJohn Marino x = n; 130101e196c8SJohn Marino lineno++; 130201e196c8SJohn Marino if (buf[x - 1] != '\n') { 130301e196c8SJohn Marino warnx("%s: %d: line truncated at %u", fname, lineno, x); 130401e196c8SJohn Marino break; 130501e196c8SJohn Marino } 130601e196c8SJohn Marino cp = strchr(++cp, '\n'); 130701e196c8SJohn Marino } while (cp); 130801e196c8SJohn Marino if (buf[x - 1] == '\n') 130901e196c8SJohn Marino buf[x - 1] = '\0'; 131001e196c8SJohn Marino } 1311*5f1e34d9SAlexandre Perrin if (!hasOODATE && 131201e196c8SJohn Marino !(gn->type & OP_NOMETA_CMP) && 131301e196c8SJohn Marino strcmp(p, cmd) != 0) { 131401e196c8SJohn Marino if (DEBUG(META)) 131501e196c8SJohn Marino fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); 131601e196c8SJohn Marino if (!metaIgnoreCMDs) 131701e196c8SJohn Marino oodate = TRUE; 131801e196c8SJohn Marino } 131901e196c8SJohn Marino free(cmd); 132001e196c8SJohn Marino ln = Lst_Succ(ln); 132101e196c8SJohn Marino } 132201e196c8SJohn Marino } else if (strcmp(buf, "CWD") == 0) { 132301e196c8SJohn Marino /* 132401e196c8SJohn Marino * Check if there are extra commands now 132501e196c8SJohn Marino * that weren't in the meta data file. 132601e196c8SJohn Marino */ 132701e196c8SJohn Marino if (!oodate && ln != NULL) { 132801e196c8SJohn Marino if (DEBUG(META)) 132901e196c8SJohn Marino fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno); 133001e196c8SJohn Marino oodate = TRUE; 133101e196c8SJohn Marino } 133201e196c8SJohn Marino if (strcmp(p, cwd) != 0) { 133301e196c8SJohn Marino if (DEBUG(META)) 133401e196c8SJohn Marino fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); 133501e196c8SJohn Marino oodate = TRUE; 133601e196c8SJohn Marino } 133701e196c8SJohn Marino } 133801e196c8SJohn Marino } 133901e196c8SJohn Marino 134001e196c8SJohn Marino fclose(fp); 134101e196c8SJohn Marino if (!Lst_IsEmpty(missingFiles)) { 134201e196c8SJohn Marino if (DEBUG(META)) 134301e196c8SJohn Marino fprintf(debug_file, "%s: missing files: %s...\n", 134401e196c8SJohn Marino fname, (char *)Lst_Datum(Lst_First(missingFiles))); 134501e196c8SJohn Marino oodate = TRUE; 134601e196c8SJohn Marino Lst_Destroy(missingFiles, (FreeProc *)free); 134701e196c8SJohn Marino } 134801e196c8SJohn Marino } else { 134901e196c8SJohn Marino if ((gn->type & OP_META)) { 135001e196c8SJohn Marino if (DEBUG(META)) 135101e196c8SJohn Marino fprintf(debug_file, "%s: required but missing\n", fname); 135201e196c8SJohn Marino oodate = TRUE; 135301e196c8SJohn Marino } 135401e196c8SJohn Marino } 1355*5f1e34d9SAlexandre Perrin if (oodate && needOODATE) { 135601e196c8SJohn Marino /* 1357*5f1e34d9SAlexandre Perrin * Target uses .OODATE which is empty; or we wouldn't be here. 1358*5f1e34d9SAlexandre Perrin * We have decided it is oodate, so .OODATE needs to be set. 1359*5f1e34d9SAlexandre Perrin * All we can sanely do is set it to .ALLSRC. 136001e196c8SJohn Marino */ 136101e196c8SJohn Marino Var_Delete(OODATE, gn); 1362*5f1e34d9SAlexandre Perrin Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn, 0); 1363*5f1e34d9SAlexandre Perrin if (cp) 1364*5f1e34d9SAlexandre Perrin free(cp); 136501e196c8SJohn Marino } 136601e196c8SJohn Marino return oodate; 136701e196c8SJohn Marino } 136801e196c8SJohn Marino 136901e196c8SJohn Marino /* support for compat mode */ 137001e196c8SJohn Marino 137101e196c8SJohn Marino static int childPipe[2]; 137201e196c8SJohn Marino 137301e196c8SJohn Marino void 137401e196c8SJohn Marino meta_compat_start(void) 137501e196c8SJohn Marino { 137601e196c8SJohn Marino #ifdef USE_FILEMON_ONCE 137701e196c8SJohn Marino /* 137801e196c8SJohn Marino * We need to re-open filemon for each cmd. 137901e196c8SJohn Marino */ 138001e196c8SJohn Marino BuildMon *pbm = &Mybm; 138101e196c8SJohn Marino 138201e196c8SJohn Marino if (pbm->mfp != NULL && useFilemon) { 138301e196c8SJohn Marino filemon_open(pbm); 138401e196c8SJohn Marino } else { 138501e196c8SJohn Marino pbm->mon_fd = pbm->filemon_fd = -1; 138601e196c8SJohn Marino } 138701e196c8SJohn Marino #endif 138801e196c8SJohn Marino if (pipe(childPipe) < 0) 138901e196c8SJohn Marino Punt("Cannot create pipe: %s", strerror(errno)); 139001e196c8SJohn Marino /* Set close-on-exec flag for both */ 139101e196c8SJohn Marino (void)fcntl(childPipe[0], F_SETFD, 1); 139201e196c8SJohn Marino (void)fcntl(childPipe[1], F_SETFD, 1); 139301e196c8SJohn Marino } 139401e196c8SJohn Marino 139501e196c8SJohn Marino void 139601e196c8SJohn Marino meta_compat_child(void) 139701e196c8SJohn Marino { 139801e196c8SJohn Marino meta_job_child(NULL); 139901e196c8SJohn Marino if (dup2(childPipe[1], 1) < 0 || 140001e196c8SJohn Marino dup2(1, 2) < 0) { 140101e196c8SJohn Marino execError("dup2", "pipe"); 140201e196c8SJohn Marino _exit(1); 140301e196c8SJohn Marino } 140401e196c8SJohn Marino } 140501e196c8SJohn Marino 140601e196c8SJohn Marino void 140701e196c8SJohn Marino meta_compat_parent(void) 140801e196c8SJohn Marino { 140901e196c8SJohn Marino FILE *fp; 141001e196c8SJohn Marino char buf[BUFSIZ]; 141101e196c8SJohn Marino 141201e196c8SJohn Marino close(childPipe[1]); /* child side */ 141301e196c8SJohn Marino fp = fdopen(childPipe[0], "r"); 141401e196c8SJohn Marino while (fgets(buf, sizeof(buf), fp)) { 141501e196c8SJohn Marino meta_job_output(NULL, buf, ""); 141601e196c8SJohn Marino printf("%s", buf); 141701e196c8SJohn Marino } 141801e196c8SJohn Marino fclose(fp); 141901e196c8SJohn Marino } 142001e196c8SJohn Marino 142101e196c8SJohn Marino #endif /* USE_META */ 1422