xref: /dragonfly/contrib/bmake/meta.c (revision 5f1e34d9)
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