2016-08-17 18:52:42 +03:00
|
|
|
/* $NetBSD: meta.c,v 1.67 2016/08/17 15:52:42 sjg Exp $ */
|
2011-03-31 02:03:49 +04:00
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
|
|
|
* Implement 'meta' mode.
|
|
|
|
* Adapted from John Birrell's patches to FreeBSD make.
|
|
|
|
* --sjg
|
|
|
|
*/
|
|
|
|
/*
|
2016-02-27 03:13:21 +03:00
|
|
|
* Copyright (c) 2009-2016, Juniper Networks, Inc.
|
2010-09-13 19:36:57 +04:00
|
|
|
* Portions Copyright (c) 2009, John Birrell.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
|
|
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
2010-09-14 01:31:59 +04:00
|
|
|
#if defined(USE_META)
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
# include "config.h"
|
|
|
|
#endif
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <libgen.h>
|
|
|
|
#include <errno.h>
|
2010-09-14 00:34:21 +04:00
|
|
|
#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H)
|
2010-09-13 19:36:57 +04:00
|
|
|
#include <err.h>
|
2010-09-14 00:34:21 +04:00
|
|
|
#endif
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
#include "make.h"
|
|
|
|
#include "job.h"
|
|
|
|
|
|
|
|
#ifdef HAVE_FILEMON_H
|
|
|
|
# include <filemon.h>
|
|
|
|
#endif
|
|
|
|
#if !defined(USE_FILEMON) && defined(FILEMON_SET_FD)
|
|
|
|
# define USE_FILEMON
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static BuildMon Mybm; /* for compat */
|
2013-06-25 04:20:54 +04:00
|
|
|
static Lst metaBailiwick; /* our scope of control */
|
2016-03-08 00:45:43 +03:00
|
|
|
static char *metaBailiwickStr; /* string storage for the list */
|
2013-06-25 04:20:54 +04:00
|
|
|
static Lst metaIgnorePaths; /* paths we deliberately ignore */
|
2016-03-08 00:45:43 +03:00
|
|
|
static char *metaIgnorePathsStr; /* string storage for the list */
|
2013-06-25 04:20:54 +04:00
|
|
|
|
|
|
|
#ifndef MAKE_META_IGNORE_PATHS
|
|
|
|
#define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS"
|
|
|
|
#endif
|
2016-05-11 02:45:45 +03:00
|
|
|
#ifndef MAKE_META_IGNORE_PATTERNS
|
|
|
|
#define MAKE_META_IGNORE_PATTERNS ".MAKE.META.IGNORE_PATTERNS"
|
|
|
|
#endif
|
2016-08-15 22:20:17 +03:00
|
|
|
#ifndef MAKE_META_IGNORE_FILTER
|
|
|
|
#define MAKE_META_IGNORE_FILTER ".MAKE.META.IGNORE_FILTER"
|
|
|
|
#endif
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
Boolean useMeta = FALSE;
|
|
|
|
static Boolean useFilemon = FALSE;
|
|
|
|
static Boolean writeMeta = FALSE;
|
2016-06-03 04:16:27 +03:00
|
|
|
static Boolean metaMissing = FALSE; /* oodate if missing */
|
|
|
|
static Boolean filemonMissing = FALSE; /* oodate if missing */
|
2010-09-13 19:36:57 +04:00
|
|
|
static Boolean metaEnv = FALSE; /* don't save env unless asked */
|
|
|
|
static Boolean metaVerbose = FALSE;
|
|
|
|
static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */
|
2016-05-11 02:45:45 +03:00
|
|
|
static Boolean metaIgnorePatterns = FALSE; /* do we need to do pattern matches */
|
2016-08-15 22:20:17 +03:00
|
|
|
static Boolean metaIgnoreFilter = FALSE; /* do we have more complex filtering? */
|
2011-03-27 23:39:21 +04:00
|
|
|
static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */
|
2011-08-28 07:54:07 +04:00
|
|
|
static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
extern Boolean forceJobs;
|
|
|
|
extern Boolean comatMake;
|
2012-06-27 21:22:58 +04:00
|
|
|
extern char **environ;
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
#define MAKE_META_PREFIX ".MAKE.META.PREFIX"
|
|
|
|
|
|
|
|
#ifndef N2U
|
|
|
|
# define N2U(n, u) (((n) + ((u) - 1)) / (u))
|
|
|
|
#endif
|
|
|
|
#ifndef ROUNDUP
|
|
|
|
# define ROUNDUP(n, u) (N2U((n), (u)) * (u))
|
|
|
|
#endif
|
|
|
|
|
2010-09-14 00:34:21 +04:00
|
|
|
#if !defined(HAVE_STRSEP)
|
|
|
|
# define strsep(s, d) stresep((s), (d), 0)
|
|
|
|
#endif
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
|
|
|
* Filemon is a kernel module which snoops certain syscalls.
|
|
|
|
*
|
|
|
|
* C chdir
|
|
|
|
* E exec
|
|
|
|
* F [v]fork
|
|
|
|
* L [sym]link
|
|
|
|
* M rename
|
|
|
|
* R read
|
|
|
|
* W write
|
|
|
|
* S stat
|
|
|
|
*
|
|
|
|
* See meta_oodate below - we mainly care about 'E' and 'R'.
|
|
|
|
*
|
|
|
|
* We can still use meta mode without filemon, but
|
|
|
|
* the benefits are more limited.
|
|
|
|
*/
|
|
|
|
#ifdef USE_FILEMON
|
|
|
|
# ifndef _PATH_FILEMON
|
|
|
|
# define _PATH_FILEMON "/dev/filemon"
|
|
|
|
# endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Open the filemon device.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
filemon_open(BuildMon *pbm)
|
|
|
|
{
|
|
|
|
int retry;
|
|
|
|
|
|
|
|
pbm->mon_fd = pbm->filemon_fd = -1;
|
|
|
|
if (!useFilemon)
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (retry = 5; retry >= 0; retry--) {
|
|
|
|
if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pbm->filemon_fd < 0) {
|
|
|
|
useFilemon = FALSE;
|
|
|
|
warn("Could not open %s", _PATH_FILEMON);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We use a file outside of '.'
|
|
|
|
* to avoid a FreeBSD kernel bug where unlink invalidates
|
|
|
|
* cwd causing getcwd to do a lot more work.
|
|
|
|
* We only care about the descriptor.
|
|
|
|
*/
|
|
|
|
pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL);
|
|
|
|
if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) {
|
|
|
|
err(1, "Could not set filemon file descriptor!");
|
|
|
|
}
|
|
|
|
/* we don't need these once we exec */
|
2016-01-17 18:30:23 +03:00
|
|
|
(void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC);
|
|
|
|
(void)fcntl(pbm->filemon_fd, F_SETFD, FD_CLOEXEC);
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the build monitor output file and write records to the target's
|
|
|
|
* metadata file.
|
|
|
|
*/
|
2016-05-12 23:28:34 +03:00
|
|
|
static int
|
2010-09-13 19:36:57 +04:00
|
|
|
filemon_read(FILE *mfp, int fd)
|
|
|
|
{
|
|
|
|
char buf[BUFSIZ];
|
2014-10-21 03:22:57 +04:00
|
|
|
int n;
|
2016-05-12 23:28:34 +03:00
|
|
|
int error;
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
/* Check if we're not writing to a meta data file.*/
|
|
|
|
if (mfp == NULL) {
|
|
|
|
if (fd >= 0)
|
|
|
|
close(fd); /* not interested */
|
2016-05-12 23:28:34 +03:00
|
|
|
return 0;
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
/* rewind */
|
2011-09-21 18:30:47 +04:00
|
|
|
(void)lseek(fd, (off_t)0, SEEK_SET);
|
2010-09-13 19:36:57 +04:00
|
|
|
|
2016-05-12 23:28:34 +03:00
|
|
|
error = 0;
|
2014-11-06 04:36:57 +03:00
|
|
|
fprintf(mfp, "\n-- filemon acquired metadata --\n");
|
2010-09-13 19:36:57 +04:00
|
|
|
|
2014-10-21 03:22:57 +04:00
|
|
|
while ((n = read(fd, buf, sizeof(buf))) > 0) {
|
2016-05-12 23:28:34 +03:00
|
|
|
if ((int)fwrite(buf, 1, n, mfp) < n)
|
|
|
|
error = EIO;
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
fflush(mfp);
|
2016-05-12 23:28:34 +03:00
|
|
|
if (close(fd) < 0)
|
|
|
|
error = errno;
|
|
|
|
return error;
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2011-02-14 00:22:41 +03:00
|
|
|
/*
|
|
|
|
* when realpath() fails,
|
|
|
|
* we use this, to clean up ./ and ../
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
eat_dots(char *buf, size_t bufsz, int dots)
|
|
|
|
{
|
|
|
|
char *cp;
|
|
|
|
char *cp2;
|
|
|
|
const char *eat;
|
|
|
|
size_t eatlen;
|
|
|
|
|
|
|
|
switch (dots) {
|
|
|
|
case 1:
|
|
|
|
eat = "/./";
|
|
|
|
eatlen = 2;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
eat = "/../";
|
|
|
|
eatlen = 3;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
do {
|
|
|
|
cp = strstr(buf, eat);
|
|
|
|
if (cp) {
|
|
|
|
cp2 = cp + eatlen;
|
|
|
|
if (dots == 2 && cp > buf) {
|
|
|
|
do {
|
|
|
|
cp--;
|
|
|
|
} while (cp > buf && *cp != '/');
|
|
|
|
}
|
|
|
|
if (*cp == '/') {
|
|
|
|
strlcpy(cp, cp2, bufsz - (cp - buf));
|
|
|
|
} else {
|
|
|
|
return; /* can't happen? */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} while (cp);
|
|
|
|
}
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
static char *
|
|
|
|
meta_name(struct GNode *gn, char *mname, size_t mnamelen,
|
|
|
|
const char *dname,
|
2016-06-03 04:16:27 +03:00
|
|
|
const char *tname,
|
|
|
|
const char *cwd)
|
2010-09-13 19:36:57 +04:00
|
|
|
{
|
|
|
|
char buf[MAXPATHLEN];
|
|
|
|
char *rp;
|
|
|
|
char *cp;
|
|
|
|
char *tp;
|
2011-02-14 00:22:41 +03:00
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
|
|
|
* Weed out relative paths from the target file name.
|
|
|
|
* We have to be careful though since if target is a
|
|
|
|
* symlink, the result will be unstable.
|
|
|
|
* So we use realpath() just to get the dirname, and leave the
|
|
|
|
* basename as given to us.
|
|
|
|
*/
|
|
|
|
if ((cp = strrchr(tname, '/'))) {
|
2016-06-03 04:21:59 +03:00
|
|
|
if (cached_realpath(tname, buf)) {
|
2010-09-13 19:36:57 +04:00
|
|
|
if ((rp = strrchr(buf, '/'))) {
|
|
|
|
rp++;
|
|
|
|
cp++;
|
|
|
|
if (strcmp(cp, rp) != 0)
|
|
|
|
strlcpy(rp, cp, sizeof(buf) - (rp - buf));
|
|
|
|
}
|
|
|
|
tname = buf;
|
2011-02-14 00:22:41 +03:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* We likely have a directory which is about to be made.
|
|
|
|
* We pretend realpath() succeeded, to have a chance
|
|
|
|
* of generating the same meta file name that we will
|
|
|
|
* next time through.
|
|
|
|
*/
|
|
|
|
if (tname[0] == '/') {
|
|
|
|
strlcpy(buf, tname, sizeof(buf));
|
|
|
|
} else {
|
|
|
|
snprintf(buf, sizeof(buf), "%s/%s", cwd, tname);
|
|
|
|
}
|
|
|
|
eat_dots(buf, sizeof(buf), 1); /* ./ */
|
|
|
|
eat_dots(buf, sizeof(buf), 2); /* ../ */
|
|
|
|
tname = buf;
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
/* on some systems dirname may modify its arg */
|
|
|
|
tp = bmake_strdup(tname);
|
|
|
|
if (strcmp(dname, dirname(tp)) == 0)
|
|
|
|
snprintf(mname, mnamelen, "%s.meta", tname);
|
|
|
|
else {
|
|
|
|
snprintf(mname, mnamelen, "%s/%s.meta", dname, tname);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Replace path separators in the file name after the
|
|
|
|
* current object directory path.
|
|
|
|
*/
|
|
|
|
cp = mname + strlen(dname) + 1;
|
|
|
|
|
|
|
|
while (*cp != '\0') {
|
|
|
|
if (*cp == '/')
|
|
|
|
*cp = '_';
|
|
|
|
cp++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free(tp);
|
|
|
|
return (mname);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return true if running ${.MAKE}
|
|
|
|
* Bypassed if target is flagged .MAKE
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
is_submake(void *cmdp, void *gnp)
|
|
|
|
{
|
|
|
|
static char *p_make = NULL;
|
|
|
|
static int p_len;
|
|
|
|
char *cmd = cmdp;
|
|
|
|
GNode *gn = gnp;
|
|
|
|
char *mp = NULL;
|
|
|
|
char *cp;
|
|
|
|
char *cp2;
|
|
|
|
int rc = 0; /* keep looking */
|
|
|
|
|
|
|
|
if (!p_make) {
|
|
|
|
p_make = Var_Value(".MAKE", gn, &cp);
|
|
|
|
p_len = strlen(p_make);
|
|
|
|
}
|
|
|
|
cp = strchr(cmd, '$');
|
|
|
|
if ((cp)) {
|
2016-02-18 23:33:40 +03:00
|
|
|
mp = Var_Subst(NULL, cmd, gn, VARF_WANTRES);
|
2010-09-13 19:36:57 +04:00
|
|
|
cmd = mp;
|
|
|
|
}
|
|
|
|
cp2 = strstr(cmd, p_make);
|
|
|
|
if ((cp2)) {
|
|
|
|
switch (cp2[p_len]) {
|
|
|
|
case '\0':
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
case '\n':
|
|
|
|
rc = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (cp2 > cmd && rc > 0) {
|
|
|
|
switch (cp2[-1]) {
|
|
|
|
case ' ':
|
|
|
|
case '\t':
|
|
|
|
case '\n':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rc = 0; /* no match */
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2016-01-17 20:45:21 +03:00
|
|
|
free(mp);
|
2010-09-13 19:36:57 +04:00
|
|
|
return (rc);
|
|
|
|
}
|
|
|
|
|
|
|
|
typedef struct meta_file_s {
|
|
|
|
FILE *fp;
|
|
|
|
GNode *gn;
|
|
|
|
} meta_file_t;
|
|
|
|
|
|
|
|
static int
|
|
|
|
printCMD(void *cmdp, void *mfpp)
|
|
|
|
{
|
|
|
|
meta_file_t *mfp = mfpp;
|
|
|
|
char *cmd = cmdp;
|
|
|
|
char *cp = NULL;
|
|
|
|
|
|
|
|
if (strchr(cmd, '$')) {
|
2016-02-18 23:33:40 +03:00
|
|
|
cmd = cp = Var_Subst(NULL, cmd, mfp->gn, VARF_WANTRES);
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
fprintf(mfp->fp, "CMD %s\n", cmd);
|
2016-01-17 20:45:21 +03:00
|
|
|
free(cp);
|
2010-09-13 19:36:57 +04:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Certain node types never get a .meta file
|
|
|
|
*/
|
|
|
|
#define SKIP_META_TYPE(_type) do { \
|
|
|
|
if ((gn->type & __CONCAT(OP_, _type))) { \
|
2016-06-03 04:16:27 +03:00
|
|
|
if (verbose) { \
|
2010-09-13 19:36:57 +04:00
|
|
|
fprintf(debug_file, "Skipping meta for %s: .%s\n", \
|
|
|
|
gn->name, __STRING(_type)); \
|
|
|
|
} \
|
2016-06-03 04:16:27 +03:00
|
|
|
return FALSE; \
|
2010-09-13 19:36:57 +04:00
|
|
|
} \
|
|
|
|
} while (0)
|
|
|
|
|
2016-06-03 04:16:27 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Do we need/want a .meta file ?
|
|
|
|
*/
|
|
|
|
static Boolean
|
|
|
|
meta_needed(GNode *gn, const char *dname, const char *tname,
|
|
|
|
char *objdir, int verbose)
|
2010-09-13 19:36:57 +04:00
|
|
|
{
|
|
|
|
struct stat fs;
|
|
|
|
|
2016-06-03 04:16:27 +03:00
|
|
|
if (verbose)
|
|
|
|
verbose = DEBUG(META);
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
/* This may be a phony node which we don't want meta data for... */
|
|
|
|
/* Skip .meta for .BEGIN, .END, .ERROR etc as well. */
|
|
|
|
/* Or it may be explicitly flagged as .NOMETA */
|
|
|
|
SKIP_META_TYPE(NOMETA);
|
|
|
|
/* Unless it is explicitly flagged as .META */
|
|
|
|
if (!(gn->type & OP_META)) {
|
|
|
|
SKIP_META_TYPE(PHONY);
|
|
|
|
SKIP_META_TYPE(SPECIAL);
|
|
|
|
SKIP_META_TYPE(MAKE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Check if there are no commands to execute. */
|
|
|
|
if (Lst_IsEmpty(gn->commands)) {
|
2016-06-03 04:16:27 +03:00
|
|
|
if (verbose)
|
2010-09-13 19:36:57 +04:00
|
|
|
fprintf(debug_file, "Skipping meta for %s: no commands\n",
|
|
|
|
gn->name);
|
2016-06-03 04:16:27 +03:00
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
if ((gn->type & (OP_META|OP_SUBMAKE)) == OP_SUBMAKE) {
|
|
|
|
/* OP_SUBMAKE is a bit too aggressive */
|
|
|
|
if (Lst_ForEach(gn->commands, is_submake, gn)) {
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "Skipping meta for %s: .SUBMAKE\n",
|
|
|
|
gn->name);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* The object directory may not exist. Check it.. */
|
2016-06-07 03:40:00 +03:00
|
|
|
if (cached_stat(dname, &fs) != 0) {
|
2016-06-03 04:16:27 +03:00
|
|
|
if (verbose)
|
|
|
|
fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n",
|
|
|
|
gn->name);
|
|
|
|
return FALSE;
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/* make sure these are canonical */
|
2016-06-03 04:21:59 +03:00
|
|
|
if (cached_realpath(dname, objdir))
|
2010-09-13 19:36:57 +04:00
|
|
|
dname = objdir;
|
|
|
|
|
|
|
|
/* If we aren't in the object directory, don't create a meta file. */
|
2011-03-27 23:39:21 +04:00
|
|
|
if (!metaCurdirOk && strcmp(curdir, dname) == 0) {
|
2016-06-03 04:16:27 +03:00
|
|
|
if (verbose)
|
2010-09-13 19:36:57 +04:00
|
|
|
fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n",
|
|
|
|
gn->name);
|
2016-06-03 04:16:27 +03:00
|
|
|
return FALSE;
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
2016-06-03 04:16:27 +03:00
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static FILE *
|
|
|
|
meta_create(BuildMon *pbm, GNode *gn)
|
|
|
|
{
|
|
|
|
meta_file_t mf;
|
|
|
|
char buf[MAXPATHLEN];
|
|
|
|
char objdir[MAXPATHLEN];
|
|
|
|
char **ptr;
|
|
|
|
const char *dname;
|
|
|
|
const char *tname;
|
|
|
|
char *fname;
|
|
|
|
const char *cp;
|
|
|
|
char *p[4]; /* >= possible uses */
|
|
|
|
int i;
|
|
|
|
|
|
|
|
mf.fp = NULL;
|
|
|
|
i = 0;
|
|
|
|
|
|
|
|
dname = Var_Value(".OBJDIR", gn, &p[i++]);
|
|
|
|
tname = Var_Value(TARGET, gn, &p[i++]);
|
|
|
|
|
|
|
|
/* if this succeeds objdir is realpath of dname */
|
|
|
|
if (!meta_needed(gn, dname, tname, objdir, TRUE))
|
|
|
|
goto out;
|
|
|
|
dname = objdir;
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
if (metaVerbose) {
|
|
|
|
char *mp;
|
|
|
|
|
|
|
|
/* Describe the target we are building */
|
2016-02-18 23:33:40 +03:00
|
|
|
mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, VARF_WANTRES);
|
2010-09-13 19:36:57 +04:00
|
|
|
if (*mp)
|
|
|
|
fprintf(stdout, "%s\n", mp);
|
|
|
|
free(mp);
|
|
|
|
}
|
|
|
|
/* Get the basename of the target */
|
|
|
|
if ((cp = strrchr(tname, '/')) == NULL) {
|
|
|
|
cp = tname;
|
|
|
|
} else {
|
|
|
|
cp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
fflush(stdout);
|
|
|
|
|
|
|
|
if (!writeMeta)
|
|
|
|
/* Don't create meta data. */
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname),
|
2016-06-03 04:16:27 +03:00
|
|
|
dname, tname, objdir);
|
2010-09-13 19:36:57 +04:00
|
|
|
|
2011-02-14 00:22:41 +03:00
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "meta_create: %s\n", fname);
|
|
|
|
#endif
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
if ((mf.fp = fopen(fname, "w")) == NULL)
|
|
|
|
err(1, "Could not open meta file '%s'", fname);
|
|
|
|
|
|
|
|
fprintf(mf.fp, "# Meta data file %s\n", fname);
|
|
|
|
|
|
|
|
mf.gn = gn;
|
|
|
|
|
|
|
|
Lst_ForEach(gn->commands, printCMD, &mf);
|
|
|
|
|
|
|
|
fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf)));
|
|
|
|
fprintf(mf.fp, "TARGET %s\n", tname);
|
|
|
|
|
|
|
|
if (metaEnv) {
|
|
|
|
for (ptr = environ; *ptr != NULL; ptr++)
|
|
|
|
fprintf(mf.fp, "ENV %s\n", *ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(mf.fp, "-- command output --\n");
|
|
|
|
fflush(mf.fp);
|
|
|
|
|
|
|
|
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
|
|
|
|
Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL);
|
2011-08-28 07:54:07 +04:00
|
|
|
|
|
|
|
gn->type |= OP_META; /* in case anyone wants to know */
|
|
|
|
if (metaSilent) {
|
|
|
|
gn->type |= OP_SILENT;
|
|
|
|
}
|
2010-09-13 19:36:57 +04:00
|
|
|
out:
|
|
|
|
for (i--; i >= 0; i--) {
|
2016-01-17 20:45:21 +03:00
|
|
|
free(p[i]);
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
return (mf.fp);
|
|
|
|
}
|
|
|
|
|
2011-03-27 23:39:21 +04:00
|
|
|
static Boolean
|
|
|
|
boolValue(char *s)
|
|
|
|
{
|
|
|
|
switch(*s) {
|
|
|
|
case '0':
|
|
|
|
case 'N':
|
|
|
|
case 'n':
|
|
|
|
case 'F':
|
|
|
|
case 'f':
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
2010-09-13 19:36:57 +04:00
|
|
|
|
2013-03-23 09:31:29 +04:00
|
|
|
/*
|
|
|
|
* Initialization we need before reading makefiles.
|
|
|
|
*/
|
|
|
|
void
|
2013-05-17 01:56:56 +04:00
|
|
|
meta_init(void)
|
2013-03-23 09:31:29 +04:00
|
|
|
{
|
|
|
|
#ifdef USE_FILEMON
|
|
|
|
/* this allows makefiles to test if we have filemon support */
|
|
|
|
Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-06-03 04:16:27 +03:00
|
|
|
#define get_mode_bf(bf, token) \
|
|
|
|
if ((cp = strstr(make_mode, token))) \
|
|
|
|
bf = boolValue(&cp[sizeof(token) - 1])
|
|
|
|
|
2013-03-23 09:31:29 +04:00
|
|
|
/*
|
|
|
|
* Initialization we need after reading makefiles.
|
|
|
|
*/
|
2010-09-13 19:36:57 +04:00
|
|
|
void
|
2013-03-23 09:31:29 +04:00
|
|
|
meta_mode_init(const char *make_mode)
|
2010-09-13 19:36:57 +04:00
|
|
|
{
|
|
|
|
static int once = 0;
|
2011-03-27 23:39:21 +04:00
|
|
|
char *cp;
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
useMeta = TRUE;
|
|
|
|
useFilemon = TRUE;
|
|
|
|
writeMeta = TRUE;
|
|
|
|
|
|
|
|
if (make_mode) {
|
|
|
|
if (strstr(make_mode, "env"))
|
|
|
|
metaEnv = TRUE;
|
|
|
|
if (strstr(make_mode, "verb"))
|
|
|
|
metaVerbose = TRUE;
|
|
|
|
if (strstr(make_mode, "read"))
|
|
|
|
writeMeta = FALSE;
|
|
|
|
if (strstr(make_mode, "nofilemon"))
|
|
|
|
useFilemon = FALSE;
|
|
|
|
if (strstr(make_mode, "ignore-cmd"))
|
|
|
|
metaIgnoreCMDs = TRUE;
|
2016-06-03 04:16:27 +03:00
|
|
|
if (useFilemon)
|
|
|
|
get_mode_bf(filemonMissing, "missing-filemon=");
|
|
|
|
get_mode_bf(metaCurdirOk, "curdirok=");
|
|
|
|
get_mode_bf(metaMissing, "missing-meta=");
|
|
|
|
get_mode_bf(metaSilent, "silent=");
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) {
|
|
|
|
/*
|
|
|
|
* The default value for MAKE_META_PREFIX
|
|
|
|
* prints the absolute path of the target.
|
|
|
|
* This works be cause :H will generate '.' if there is no /
|
|
|
|
* and :tA will resolve that to cwd.
|
|
|
|
*/
|
|
|
|
Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0);
|
|
|
|
}
|
|
|
|
if (once)
|
|
|
|
return;
|
|
|
|
once = 1;
|
|
|
|
memset(&Mybm, 0, sizeof(Mybm));
|
2011-05-05 00:38:31 +04:00
|
|
|
/*
|
|
|
|
* We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
|
|
|
|
*/
|
|
|
|
metaBailiwick = Lst_Init(FALSE);
|
2016-03-08 00:45:43 +03:00
|
|
|
metaBailiwickStr = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}",
|
|
|
|
VAR_GLOBAL, VARF_WANTRES);
|
|
|
|
if (metaBailiwickStr) {
|
|
|
|
str2Lst_Append(metaBailiwick, metaBailiwickStr, NULL);
|
2011-05-05 00:38:31 +04:00
|
|
|
}
|
2013-06-25 04:20:54 +04:00
|
|
|
/*
|
|
|
|
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
|
|
|
|
*/
|
|
|
|
metaIgnorePaths = Lst_Init(FALSE);
|
|
|
|
Var_Append(MAKE_META_IGNORE_PATHS,
|
|
|
|
"/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL);
|
2016-03-08 00:45:43 +03:00
|
|
|
metaIgnorePathsStr = Var_Subst(NULL,
|
2015-10-11 07:51:24 +03:00
|
|
|
"${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL,
|
2016-02-18 23:33:40 +03:00
|
|
|
VARF_WANTRES);
|
2016-03-08 00:45:43 +03:00
|
|
|
if (metaIgnorePathsStr) {
|
|
|
|
str2Lst_Append(metaIgnorePaths, metaIgnorePathsStr, NULL);
|
2013-06-25 04:20:54 +04:00
|
|
|
}
|
2016-05-11 02:45:45 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
|
|
|
|
*/
|
|
|
|
cp = NULL;
|
|
|
|
if (Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL, &cp)) {
|
|
|
|
metaIgnorePatterns = TRUE;
|
|
|
|
free(cp);
|
|
|
|
}
|
2016-08-15 22:20:17 +03:00
|
|
|
cp = NULL;
|
|
|
|
if (Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL, &cp)) {
|
|
|
|
metaIgnoreFilter = TRUE;
|
|
|
|
free(cp);
|
|
|
|
}
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* In each case below we allow for job==NULL
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
meta_job_start(Job *job, GNode *gn)
|
|
|
|
{
|
|
|
|
BuildMon *pbm;
|
|
|
|
|
|
|
|
if (job != NULL) {
|
|
|
|
pbm = &job->bm;
|
|
|
|
} else {
|
|
|
|
pbm = &Mybm;
|
|
|
|
}
|
|
|
|
pbm->mfp = meta_create(pbm, gn);
|
|
|
|
#ifdef USE_FILEMON_ONCE
|
|
|
|
/* compat mode we open the filemon dev once per command */
|
|
|
|
if (job == NULL)
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
#ifdef USE_FILEMON
|
|
|
|
if (pbm->mfp != NULL && useFilemon) {
|
|
|
|
filemon_open(pbm);
|
|
|
|
} else {
|
|
|
|
pbm->mon_fd = pbm->filemon_fd = -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The child calls this before doing anything.
|
|
|
|
* It does not disturb our state.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
meta_job_child(Job *job)
|
|
|
|
{
|
|
|
|
#ifdef USE_FILEMON
|
|
|
|
BuildMon *pbm;
|
|
|
|
|
|
|
|
if (job != NULL) {
|
|
|
|
pbm = &job->bm;
|
|
|
|
} else {
|
|
|
|
pbm = &Mybm;
|
|
|
|
}
|
2015-04-01 04:03:55 +03:00
|
|
|
if (pbm->mfp != NULL) {
|
|
|
|
close(fileno(pbm->mfp));
|
|
|
|
if (useFilemon) {
|
|
|
|
pid_t pid;
|
|
|
|
|
|
|
|
pid = getpid();
|
|
|
|
if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) {
|
|
|
|
err(1, "Could not set filemon pid!");
|
|
|
|
}
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_job_error(Job *job, GNode *gn, int flags, int status)
|
|
|
|
{
|
|
|
|
char cwd[MAXPATHLEN];
|
|
|
|
BuildMon *pbm;
|
|
|
|
|
|
|
|
if (job != NULL) {
|
|
|
|
pbm = &job->bm;
|
|
|
|
if (!gn)
|
|
|
|
gn = job->node;
|
2016-02-27 19:20:06 +03:00
|
|
|
} else {
|
2010-09-13 19:36:57 +04:00
|
|
|
pbm = &Mybm;
|
|
|
|
}
|
|
|
|
if (pbm->mfp != NULL) {
|
|
|
|
fprintf(pbm->mfp, "*** Error code %d%s\n",
|
|
|
|
status,
|
|
|
|
(flags & JOB_IGNERR) ?
|
|
|
|
"(ignored)" : "");
|
|
|
|
}
|
|
|
|
if (gn) {
|
|
|
|
Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0);
|
|
|
|
}
|
|
|
|
getcwd(cwd, sizeof(cwd));
|
|
|
|
Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0);
|
2016-02-27 19:14:23 +03:00
|
|
|
if (pbm->meta_fname[0]) {
|
2010-09-13 19:36:57 +04:00
|
|
|
Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0);
|
|
|
|
}
|
2011-03-31 10:50:43 +04:00
|
|
|
meta_job_finish(job);
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_job_output(Job *job, char *cp, const char *nl)
|
|
|
|
{
|
|
|
|
BuildMon *pbm;
|
|
|
|
|
|
|
|
if (job != NULL) {
|
|
|
|
pbm = &job->bm;
|
|
|
|
} else {
|
|
|
|
pbm = &Mybm;
|
|
|
|
}
|
|
|
|
if (pbm->mfp != NULL) {
|
|
|
|
if (metaVerbose) {
|
|
|
|
static char *meta_prefix = NULL;
|
|
|
|
static int meta_prefix_len;
|
|
|
|
|
|
|
|
if (!meta_prefix) {
|
|
|
|
char *cp2;
|
|
|
|
|
2015-10-11 07:51:24 +03:00
|
|
|
meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}",
|
2016-02-18 23:33:40 +03:00
|
|
|
VAR_GLOBAL, VARF_WANTRES);
|
2010-09-13 19:36:57 +04:00
|
|
|
if ((cp2 = strchr(meta_prefix, '$')))
|
|
|
|
meta_prefix_len = cp2 - meta_prefix;
|
|
|
|
else
|
|
|
|
meta_prefix_len = strlen(meta_prefix);
|
|
|
|
}
|
|
|
|
if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) {
|
|
|
|
cp = strchr(cp+1, '\n');
|
|
|
|
if (!cp++)
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
fprintf(pbm->mfp, "%s%s", cp, nl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-12 23:28:34 +03:00
|
|
|
int
|
2010-09-13 19:36:57 +04:00
|
|
|
meta_cmd_finish(void *pbmp)
|
|
|
|
{
|
2016-05-12 23:28:34 +03:00
|
|
|
int error = 0;
|
2010-09-13 19:36:57 +04:00
|
|
|
#ifdef USE_FILEMON
|
|
|
|
BuildMon *pbm = pbmp;
|
2016-05-12 23:28:34 +03:00
|
|
|
int x;
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
if (!pbm)
|
|
|
|
pbm = &Mybm;
|
|
|
|
|
|
|
|
if (pbm->filemon_fd >= 0) {
|
2016-05-12 23:28:34 +03:00
|
|
|
if (close(pbm->filemon_fd) < 0)
|
|
|
|
error = errno;
|
|
|
|
x = filemon_read(pbm->mfp, pbm->mon_fd);
|
|
|
|
if (error == 0 && x != 0)
|
|
|
|
error = x;
|
2010-09-13 19:36:57 +04:00
|
|
|
pbm->filemon_fd = pbm->mon_fd = -1;
|
|
|
|
}
|
|
|
|
#endif
|
2016-05-12 23:28:34 +03:00
|
|
|
return error;
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
|
2016-05-12 23:28:34 +03:00
|
|
|
int
|
2010-09-13 19:36:57 +04:00
|
|
|
meta_job_finish(Job *job)
|
|
|
|
{
|
|
|
|
BuildMon *pbm;
|
2016-05-12 23:28:34 +03:00
|
|
|
int error = 0;
|
|
|
|
int x;
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
if (job != NULL) {
|
|
|
|
pbm = &job->bm;
|
|
|
|
} else {
|
|
|
|
pbm = &Mybm;
|
|
|
|
}
|
|
|
|
if (pbm->mfp != NULL) {
|
2016-05-12 23:28:34 +03:00
|
|
|
error = meta_cmd_finish(pbm);
|
|
|
|
x = fclose(pbm->mfp);
|
|
|
|
if (error == 0 && x != 0)
|
|
|
|
error = errno;
|
2010-09-13 19:36:57 +04:00
|
|
|
pbm->mfp = NULL;
|
2010-12-16 02:03:35 +03:00
|
|
|
pbm->meta_fname[0] = '\0';
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
2016-05-12 23:28:34 +03:00
|
|
|
return error;
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
|
2016-03-08 00:45:43 +03:00
|
|
|
void
|
|
|
|
meta_finish(void)
|
|
|
|
{
|
|
|
|
Lst_Destroy(metaBailiwick, NULL);
|
|
|
|
free(metaBailiwickStr);
|
|
|
|
Lst_Destroy(metaIgnorePaths, NULL);
|
|
|
|
free(metaIgnorePathsStr);
|
|
|
|
}
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
|
|
|
* Fetch a full line from fp - growing bufp if needed
|
|
|
|
* Return length in bufp.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fgetLine(char **bufp, size_t *szp, int o, FILE *fp)
|
|
|
|
{
|
|
|
|
char *buf = *bufp;
|
|
|
|
size_t bufsz = *szp;
|
|
|
|
struct stat fs;
|
|
|
|
int x;
|
|
|
|
|
|
|
|
if (fgets(&buf[o], bufsz - o, fp) != NULL) {
|
|
|
|
check_newline:
|
|
|
|
x = o + strlen(&buf[o]);
|
|
|
|
if (buf[x - 1] == '\n')
|
|
|
|
return x;
|
|
|
|
/*
|
|
|
|
* We need to grow the buffer.
|
|
|
|
* The meta file can give us a clue.
|
|
|
|
*/
|
|
|
|
if (fstat(fileno(fp), &fs) == 0) {
|
|
|
|
size_t newsz;
|
|
|
|
char *p;
|
|
|
|
|
|
|
|
newsz = ROUNDUP((fs.st_size / 2), BUFSIZ);
|
|
|
|
if (newsz <= bufsz)
|
|
|
|
newsz = ROUNDUP(fs.st_size, BUFSIZ);
|
|
|
|
if (DEBUG(META))
|
2011-06-11 06:10:48 +04:00
|
|
|
fprintf(debug_file, "growing buffer %zu -> %zu\n",
|
|
|
|
bufsz, newsz);
|
2010-09-13 19:36:57 +04:00
|
|
|
p = bmake_realloc(buf, newsz);
|
|
|
|
if (p) {
|
|
|
|
*bufp = buf = p;
|
|
|
|
*szp = bufsz = newsz;
|
|
|
|
/* fetch the rest */
|
|
|
|
if (!fgets(&buf[x], bufsz - x, fp))
|
|
|
|
return x; /* truncated! */
|
|
|
|
goto check_newline;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-08-10 21:25:00 +03:00
|
|
|
/* Lst_ForEach wants 1 to stop search */
|
2011-05-05 00:38:31 +04:00
|
|
|
static int
|
|
|
|
prefix_match(void *p, void *q)
|
|
|
|
{
|
|
|
|
const char *prefix = p;
|
|
|
|
const char *path = q;
|
|
|
|
size_t n = strlen(prefix);
|
|
|
|
|
|
|
|
return (0 == strncmp(path, prefix, n));
|
|
|
|
}
|
|
|
|
|
2016-08-10 21:25:00 +03:00
|
|
|
/*
|
|
|
|
* looking for exact or prefix/ match to
|
|
|
|
* Lst_Find wants 0 to stop search
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
path_match(const void *p, const void *q)
|
|
|
|
{
|
|
|
|
const char *prefix = q;
|
|
|
|
const char *path = p;
|
|
|
|
size_t n = strlen(prefix);
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if ((rc = strncmp(path, prefix, n)) == 0) {
|
|
|
|
switch (path[n]) {
|
|
|
|
case '\0':
|
|
|
|
case '/':
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rc = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Lst_Find wants 0 to stop search */
|
2011-05-05 00:38:31 +04:00
|
|
|
static int
|
|
|
|
string_match(const void *p, const void *q)
|
|
|
|
{
|
|
|
|
const char *p1 = p;
|
|
|
|
const char *p2 = q;
|
|
|
|
|
|
|
|
return strcmp(p1, p2);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-17 18:52:42 +03:00
|
|
|
static int
|
|
|
|
meta_ignore(GNode *gn, const char *p)
|
|
|
|
{
|
|
|
|
char fname[MAXPATHLEN];
|
|
|
|
|
|
|
|
if (p == NULL)
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
if (*p == '/') {
|
|
|
|
cached_realpath(p, fname); /* clean it up */
|
|
|
|
if (Lst_ForEach(metaIgnorePaths, prefix_match, fname)) {
|
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "meta_oodate: ignoring path: %s\n",
|
|
|
|
p);
|
|
|
|
#endif
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (metaIgnorePatterns) {
|
|
|
|
char *pm;
|
|
|
|
|
|
|
|
snprintf(fname, sizeof(fname),
|
|
|
|
"${%s:@m@${%s:L:M$m}@}",
|
|
|
|
MAKE_META_IGNORE_PATTERNS, p);
|
|
|
|
pm = Var_Subst(NULL, fname, gn, VARF_WANTRES);
|
|
|
|
if (*pm) {
|
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "meta_oodate: ignoring pattern: %s\n",
|
|
|
|
p);
|
|
|
|
#endif
|
|
|
|
free(pm);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
free(pm);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (metaIgnoreFilter) {
|
|
|
|
char *fm;
|
|
|
|
|
|
|
|
/* skip if filter result is empty */
|
|
|
|
snprintf(fname, sizeof(fname),
|
|
|
|
"${%s:L:${%s:ts:}}",
|
|
|
|
p, MAKE_META_IGNORE_FILTER);
|
|
|
|
fm = Var_Subst(NULL, fname, gn, VARF_WANTRES);
|
|
|
|
if (*fm == '\0') {
|
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "meta_oodate: ignoring filtered: %s\n",
|
|
|
|
p);
|
|
|
|
#endif
|
|
|
|
free(fm);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
free(fm);
|
|
|
|
}
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
|
|
|
* When running with 'meta' functionality, a target can be out-of-date
|
2014-10-18 12:33:23 +04:00
|
|
|
* if any of the references in its meta data file is more recent.
|
2010-12-10 01:30:16 +03:00
|
|
|
* We have to track the latestdir on a per-process basis.
|
2010-09-13 19:36:57 +04:00
|
|
|
*/
|
2015-04-11 08:24:30 +03:00
|
|
|
#define LCWD_VNAME_FMT ".meta.%d.lcwd"
|
2010-12-10 01:30:16 +03:00
|
|
|
#define LDIR_VNAME_FMT ".meta.%d.ldir"
|
|
|
|
|
2011-06-23 01:13:12 +04:00
|
|
|
/*
|
|
|
|
* It is possible that a .meta file is corrupted,
|
|
|
|
* if we detect this we want to reproduce it.
|
|
|
|
* Setting oodate TRUE will have that effect.
|
|
|
|
*/
|
|
|
|
#define CHECK_VALID_META(p) if (!(p && *p)) { \
|
|
|
|
warnx("%s: %d: malformed", fname, lineno); \
|
|
|
|
oodate = TRUE; \
|
|
|
|
continue; \
|
|
|
|
}
|
|
|
|
|
2013-10-01 09:37:17 +04:00
|
|
|
#define DEQUOTE(p) if (*p == '\'') { \
|
|
|
|
char *ep; \
|
|
|
|
p++; \
|
|
|
|
if ((ep = strchr(p, '\''))) \
|
|
|
|
*ep = '\0'; \
|
|
|
|
}
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
Boolean
|
|
|
|
meta_oodate(GNode *gn, Boolean oodate)
|
|
|
|
{
|
2010-12-10 01:30:16 +03:00
|
|
|
static char *tmpdir = NULL;
|
2011-03-06 20:41:11 +03:00
|
|
|
static char cwd[MAXPATHLEN];
|
2015-04-11 08:24:30 +03:00
|
|
|
char lcwd_vname[64];
|
2010-12-10 01:30:16 +03:00
|
|
|
char ldir_vname[64];
|
2015-04-11 08:24:30 +03:00
|
|
|
char lcwd[MAXPATHLEN];
|
2010-09-13 19:36:57 +04:00
|
|
|
char latestdir[MAXPATHLEN];
|
|
|
|
char fname[MAXPATHLEN];
|
|
|
|
char fname1[MAXPATHLEN];
|
2010-12-10 01:30:16 +03:00
|
|
|
char fname2[MAXPATHLEN];
|
2015-04-11 08:24:30 +03:00
|
|
|
char fname3[MAXPATHLEN];
|
2016-06-03 04:16:27 +03:00
|
|
|
const char *dname;
|
|
|
|
const char *tname;
|
2010-09-13 19:36:57 +04:00
|
|
|
char *p;
|
|
|
|
char *cp;
|
2013-10-01 09:37:17 +04:00
|
|
|
char *link_src;
|
|
|
|
char *move_target;
|
2011-03-06 20:41:11 +03:00
|
|
|
static size_t cwdlen = 0;
|
2011-02-08 08:29:13 +03:00
|
|
|
static size_t tmplen = 0;
|
2010-09-13 19:36:57 +04:00
|
|
|
FILE *fp;
|
2013-01-19 08:23:37 +04:00
|
|
|
Boolean needOODATE = FALSE;
|
2011-05-05 00:38:31 +04:00
|
|
|
Lst missingFiles;
|
2016-06-03 04:16:27 +03:00
|
|
|
char *pa[4]; /* >= possible uses */
|
|
|
|
int i;
|
|
|
|
int have_filemon = FALSE;
|
|
|
|
|
When a source file moves, make will ignore the stale dependency,
but if the file in question is one that needs to be compiled (.c or .cc),
it still hands the bogus name to the compiler.
If Dir_MTime() cannot find such a file (gn->iParents is not empty),
see if the basename can be found via .PATH, and if so set gn->path to
the found file. This prevents the stale path being given to the
compiler.
In meta_oodate(), if a referenced file no longer exists, consider the
target out-of-date.
Also, if meta_oodate() decides a target is out-of-date, and it
it uses .OODATE in its commands, we need .OODATE recomputed.
Undo our call to Make_DoAllVar() so that the call from Make_OODate()
will do the right thing.
2010-11-27 08:02:35 +03:00
|
|
|
if (oodate)
|
|
|
|
return oodate; /* we're done */
|
|
|
|
|
2016-06-03 04:16:27 +03:00
|
|
|
i = 0;
|
|
|
|
|
|
|
|
dname = Var_Value(".OBJDIR", gn, &pa[i++]);
|
|
|
|
tname = Var_Value(TARGET, gn, &pa[i++]);
|
|
|
|
|
|
|
|
/* if this succeeds fname3 is realpath of dname */
|
|
|
|
if (!meta_needed(gn, dname, tname, fname3, FALSE))
|
|
|
|
goto oodate_out;
|
|
|
|
dname = fname3;
|
|
|
|
|
2011-05-05 00:38:31 +04:00
|
|
|
missingFiles = Lst_Init(FALSE);
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
|
|
|
* We need to check if the target is out-of-date. This includes
|
|
|
|
* checking if the expanded command has changed. This in turn
|
|
|
|
* requires that all variables are set in the same way that they
|
|
|
|
* would be if the target needs to be re-built.
|
|
|
|
*/
|
|
|
|
Make_DoAllVar(gn);
|
|
|
|
|
2016-06-03 04:16:27 +03:00
|
|
|
meta_name(gn, fname, sizeof(fname), dname, tname, dname);
|
2010-09-13 19:36:57 +04:00
|
|
|
|
2011-02-14 00:22:41 +03:00
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "meta_oodate: %s\n", fname);
|
|
|
|
#endif
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
if ((fp = fopen(fname, "r")) != NULL) {
|
|
|
|
static char *buf = NULL;
|
|
|
|
static size_t bufsz;
|
|
|
|
int lineno = 0;
|
2010-12-10 01:30:16 +03:00
|
|
|
int lastpid = 0;
|
|
|
|
int pid;
|
2010-09-13 19:36:57 +04:00
|
|
|
int x;
|
|
|
|
LstNode ln;
|
|
|
|
struct stat fs;
|
|
|
|
|
|
|
|
if (!buf) {
|
|
|
|
bufsz = 8 * BUFSIZ;
|
|
|
|
buf = bmake_malloc(bufsz);
|
|
|
|
}
|
2010-12-10 01:30:16 +03:00
|
|
|
|
2011-03-06 20:41:11 +03:00
|
|
|
if (!cwdlen) {
|
|
|
|
if (getcwd(cwd, sizeof(cwd)) == NULL)
|
|
|
|
err(1, "Could not get current working directory");
|
|
|
|
cwdlen = strlen(cwd);
|
|
|
|
}
|
2015-04-11 08:24:30 +03:00
|
|
|
strlcpy(lcwd, cwd, sizeof(lcwd));
|
|
|
|
strlcpy(latestdir, cwd, sizeof(latestdir));
|
2011-03-06 20:41:11 +03:00
|
|
|
|
2010-12-10 01:30:16 +03:00
|
|
|
if (!tmpdir) {
|
|
|
|
tmpdir = getTmpdir();
|
|
|
|
tmplen = strlen(tmpdir);
|
|
|
|
}
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/* we want to track all the .meta we read */
|
|
|
|
Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL);
|
|
|
|
|
|
|
|
ln = Lst_First(gn->commands);
|
|
|
|
while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) {
|
|
|
|
lineno++;
|
|
|
|
if (buf[x - 1] == '\n')
|
|
|
|
buf[x - 1] = '\0';
|
2011-06-23 01:13:12 +04:00
|
|
|
else {
|
2010-09-13 19:36:57 +04:00
|
|
|
warnx("%s: %d: line truncated at %u", fname, lineno, x);
|
2011-06-23 01:13:12 +04:00
|
|
|
oodate = TRUE;
|
|
|
|
break;
|
|
|
|
}
|
2013-10-01 09:37:17 +04:00
|
|
|
link_src = NULL;
|
|
|
|
move_target = NULL;
|
2010-09-13 19:36:57 +04:00
|
|
|
/* Find the start of the build monitor section. */
|
2016-06-03 04:16:27 +03:00
|
|
|
if (!have_filemon) {
|
2010-09-13 19:36:57 +04:00
|
|
|
if (strncmp(buf, "-- filemon", 10) == 0) {
|
2016-06-03 04:16:27 +03:00
|
|
|
have_filemon = TRUE;
|
2010-09-13 19:36:57 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (strncmp(buf, "# buildmon", 10) == 0) {
|
2016-06-03 04:16:27 +03:00
|
|
|
have_filemon = TRUE;
|
2010-09-13 19:36:57 +04:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Delimit the record type. */
|
|
|
|
p = buf;
|
2011-02-08 08:29:13 +03:00
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf);
|
|
|
|
#endif
|
2010-09-13 19:36:57 +04:00
|
|
|
strsep(&p, " ");
|
2016-06-03 04:16:27 +03:00
|
|
|
if (have_filemon) {
|
2010-12-10 01:30:16 +03:00
|
|
|
/*
|
|
|
|
* We are in the 'filemon' output section.
|
|
|
|
* Each record from filemon follows the general form:
|
|
|
|
*
|
|
|
|
* <key> <pid> <data>
|
|
|
|
*
|
|
|
|
* Where:
|
|
|
|
* <key> is a single letter, denoting the syscall.
|
|
|
|
* <pid> is the process that made the syscall.
|
|
|
|
* <data> is the arguments (of interest).
|
|
|
|
*/
|
|
|
|
switch(buf[0]) {
|
|
|
|
case '#': /* comment */
|
|
|
|
case 'V': /* version */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* We need to track pathnames per-process.
|
|
|
|
*
|
|
|
|
* Each process run by make, starts off in the 'CWD'
|
|
|
|
* recorded in the .meta file, if it chdirs ('C')
|
|
|
|
* elsewhere we need to track that - but only for
|
|
|
|
* that process. If it forks ('F'), we initialize
|
|
|
|
* the child to have the same cwd as its parent.
|
|
|
|
*
|
|
|
|
* We also need to track the 'latestdir' of
|
|
|
|
* interest. This is usually the same as cwd, but
|
|
|
|
* not if a process is reading directories.
|
|
|
|
*
|
|
|
|
* Each time we spot a different process ('pid')
|
|
|
|
* we save the current value of 'latestdir' in a
|
|
|
|
* variable qualified by 'lastpid', and
|
|
|
|
* re-initialize 'latestdir' to any pre-saved
|
|
|
|
* value for the current 'pid' and 'CWD' if none.
|
|
|
|
*/
|
2011-06-23 01:13:12 +04:00
|
|
|
CHECK_VALID_META(p);
|
2010-12-10 01:30:16 +03:00
|
|
|
pid = atoi(p);
|
|
|
|
if (pid > 0 && pid != lastpid) {
|
|
|
|
char *ldir;
|
|
|
|
char *tp;
|
|
|
|
|
|
|
|
if (lastpid > 0) {
|
2015-04-11 08:24:30 +03:00
|
|
|
/* We need to remember these. */
|
|
|
|
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0);
|
2010-12-10 01:30:16 +03:00
|
|
|
Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0);
|
|
|
|
}
|
2015-04-11 08:24:30 +03:00
|
|
|
snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid);
|
2010-12-10 01:30:16 +03:00
|
|
|
snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid);
|
|
|
|
lastpid = pid;
|
|
|
|
ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp);
|
|
|
|
if (ldir) {
|
|
|
|
strlcpy(latestdir, ldir, sizeof(latestdir));
|
2016-01-17 20:45:21 +03:00
|
|
|
free(tp);
|
2015-04-11 08:24:30 +03:00
|
|
|
}
|
|
|
|
ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp);
|
|
|
|
if (ldir) {
|
|
|
|
strlcpy(lcwd, ldir, sizeof(lcwd));
|
2016-01-17 20:45:21 +03:00
|
|
|
free(tp);
|
2015-04-11 08:24:30 +03:00
|
|
|
}
|
2010-12-10 01:30:16 +03:00
|
|
|
}
|
|
|
|
/* Skip past the pid. */
|
|
|
|
if (strsep(&p, " ") == NULL)
|
|
|
|
continue;
|
2011-02-08 08:29:13 +03:00
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
2015-04-11 08:24:30 +03:00
|
|
|
fprintf(debug_file, "%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n",
|
|
|
|
fname, lineno,
|
|
|
|
pid, buf[0], cwd, lcwd, latestdir);
|
2011-02-08 08:29:13 +03:00
|
|
|
#endif
|
2010-12-10 01:30:16 +03:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2011-06-23 01:13:12 +04:00
|
|
|
CHECK_VALID_META(p);
|
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/* Process according to record type. */
|
|
|
|
switch (buf[0]) {
|
2010-12-10 01:30:16 +03:00
|
|
|
case 'X': /* eXit */
|
2015-04-11 08:24:30 +03:00
|
|
|
Var_Delete(lcwd_vname, VAR_GLOBAL);
|
2010-12-10 01:30:16 +03:00
|
|
|
Var_Delete(ldir_vname, VAR_GLOBAL);
|
|
|
|
lastpid = 0; /* no need to save ldir_vname */
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'F': /* [v]Fork */
|
|
|
|
{
|
|
|
|
char cldir[64];
|
|
|
|
int child;
|
2010-09-13 19:36:57 +04:00
|
|
|
|
2010-12-10 01:30:16 +03:00
|
|
|
child = atoi(p);
|
|
|
|
if (child > 0) {
|
2015-04-11 08:24:30 +03:00
|
|
|
snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child);
|
|
|
|
Var_Set(cldir, lcwd, VAR_GLOBAL, 0);
|
2010-12-10 01:30:16 +03:00
|
|
|
snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child);
|
|
|
|
Var_Set(cldir, latestdir, VAR_GLOBAL, 0);
|
2015-04-11 08:24:30 +03:00
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n",
|
|
|
|
fname, lineno,
|
|
|
|
child, cwd, lcwd, latestdir);
|
|
|
|
#endif
|
2010-12-10 01:30:16 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'C': /* Chdir */
|
2015-04-11 08:24:30 +03:00
|
|
|
/* Update lcwd and latest directory. */
|
|
|
|
strlcpy(latestdir, p, sizeof(latestdir));
|
|
|
|
strlcpy(lcwd, p, sizeof(lcwd));
|
|
|
|
Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0);
|
|
|
|
Var_Set(ldir_vname, lcwd, VAR_GLOBAL, 0);
|
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd);
|
|
|
|
#endif
|
2010-09-13 19:36:57 +04:00
|
|
|
break;
|
|
|
|
|
2011-05-05 00:38:31 +04:00
|
|
|
case 'M': /* renaMe */
|
2013-10-01 09:37:17 +04:00
|
|
|
/*
|
|
|
|
* For 'M'oves we want to check
|
|
|
|
* the src as for 'R'ead
|
|
|
|
* and the target as for 'W'rite.
|
|
|
|
*/
|
|
|
|
cp = p; /* save this for a second */
|
|
|
|
/* now get target */
|
|
|
|
if (strsep(&p, " ") == NULL)
|
|
|
|
continue;
|
|
|
|
CHECK_VALID_META(p);
|
|
|
|
move_target = p;
|
|
|
|
p = cp;
|
2011-05-05 00:38:31 +04:00
|
|
|
/* 'L' and 'M' put single quotes around the args */
|
2013-10-01 09:37:17 +04:00
|
|
|
DEQUOTE(p);
|
|
|
|
DEQUOTE(move_target);
|
2011-05-05 00:38:31 +04:00
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 'D': /* unlink */
|
|
|
|
if (*p == '/' && !Lst_IsEmpty(missingFiles)) {
|
2016-08-10 21:25:00 +03:00
|
|
|
/* remove any missingFiles entries that match p */
|
|
|
|
if ((ln = Lst_Find(missingFiles, p,
|
|
|
|
path_match)) != NULL) {
|
2016-08-10 21:49:40 +03:00
|
|
|
LstNode nln;
|
2016-08-10 21:25:00 +03:00
|
|
|
char *tp;
|
|
|
|
|
|
|
|
do {
|
2016-08-10 21:49:40 +03:00
|
|
|
nln = Lst_FindFrom(missingFiles, Lst_Succ(ln),
|
|
|
|
p, path_match);
|
2016-08-10 21:25:00 +03:00
|
|
|
tp = Lst_Datum(ln);
|
|
|
|
Lst_Remove(missingFiles, ln);
|
|
|
|
free(tp);
|
2016-08-10 21:49:40 +03:00
|
|
|
} while ((ln = nln) != NULL);
|
2011-05-05 00:38:31 +04:00
|
|
|
}
|
|
|
|
}
|
2013-10-01 09:37:17 +04:00
|
|
|
if (buf[0] == 'M') {
|
|
|
|
/* the target of the mv is a file 'W'ritten */
|
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "meta_oodate: M %s -> %s\n",
|
|
|
|
p, move_target);
|
|
|
|
#endif
|
|
|
|
p = move_target;
|
|
|
|
goto check_write;
|
|
|
|
}
|
2011-05-05 00:38:31 +04:00
|
|
|
break;
|
|
|
|
case 'L': /* Link */
|
2013-10-01 09:37:17 +04:00
|
|
|
/*
|
|
|
|
* For 'L'inks check
|
|
|
|
* the src as for 'R'ead
|
|
|
|
* and the target as for 'W'rite.
|
|
|
|
*/
|
|
|
|
link_src = p;
|
|
|
|
/* now get target */
|
2011-05-05 00:38:31 +04:00
|
|
|
if (strsep(&p, " ") == NULL)
|
|
|
|
continue;
|
2011-06-23 01:13:12 +04:00
|
|
|
CHECK_VALID_META(p);
|
2011-05-05 00:38:31 +04:00
|
|
|
/* 'L' and 'M' put single quotes around the args */
|
2013-10-01 09:37:17 +04:00
|
|
|
DEQUOTE(p);
|
|
|
|
DEQUOTE(link_src);
|
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "meta_oodate: L %s -> %s\n",
|
|
|
|
link_src, p);
|
|
|
|
#endif
|
2011-05-05 00:38:31 +04:00
|
|
|
/* FALLTHROUGH */
|
|
|
|
case 'W': /* Write */
|
2013-10-01 09:37:17 +04:00
|
|
|
check_write:
|
2011-05-05 00:38:31 +04:00
|
|
|
/*
|
|
|
|
* If a file we generated within our bailiwick
|
|
|
|
* but outside of .OBJDIR is missing,
|
|
|
|
* we need to do it again.
|
|
|
|
*/
|
|
|
|
/* ignore non-absolute paths */
|
|
|
|
if (*p != '/')
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (Lst_IsEmpty(metaBailiwick))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* ignore cwd - normal dependencies handle those */
|
|
|
|
if (strncmp(p, cwd, cwdlen) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (!Lst_ForEach(metaBailiwick, prefix_match, p))
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* tmpdir might be within */
|
|
|
|
if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* ignore anything containing the string "tmp" */
|
|
|
|
if ((strstr("tmp", p)))
|
|
|
|
break;
|
|
|
|
|
2016-06-07 03:40:00 +03:00
|
|
|
if ((link_src != NULL && cached_lstat(p, &fs) < 0) ||
|
|
|
|
(link_src == NULL && cached_stat(p, &fs) < 0)) {
|
2016-08-17 18:52:42 +03:00
|
|
|
if (!meta_ignore(gn, p)) {
|
|
|
|
if (Lst_Find(missingFiles, p, string_match) == NULL)
|
2016-03-11 10:01:21 +03:00
|
|
|
Lst_AtEnd(missingFiles, bmake_strdup(p));
|
2016-08-17 18:52:42 +03:00
|
|
|
}
|
2011-05-05 00:38:31 +04:00
|
|
|
}
|
|
|
|
break;
|
2013-10-01 09:37:17 +04:00
|
|
|
check_link_src:
|
|
|
|
p = link_src;
|
|
|
|
link_src = NULL;
|
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "meta_oodate: L src %s\n", p);
|
|
|
|
#endif
|
|
|
|
/* FALLTHROUGH */
|
2010-12-10 01:30:16 +03:00
|
|
|
case 'R': /* Read */
|
|
|
|
case 'E': /* Exec */
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
|
|
|
* Check for runtime files that can't
|
|
|
|
* be part of the dependencies because
|
|
|
|
* they are _expected_ to change.
|
|
|
|
*/
|
2016-08-17 18:52:42 +03:00
|
|
|
if (meta_ignore(gn, p))
|
|
|
|
break;
|
2016-08-15 22:20:17 +03:00
|
|
|
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
2010-12-10 01:30:16 +03:00
|
|
|
* The rest of the record is the file name.
|
|
|
|
* Check if it's not an absolute path.
|
2010-09-13 19:36:57 +04:00
|
|
|
*/
|
2010-12-10 01:30:16 +03:00
|
|
|
{
|
|
|
|
char *sdirs[4];
|
|
|
|
char **sdp;
|
|
|
|
int sdx = 0;
|
|
|
|
int found = 0;
|
|
|
|
|
|
|
|
if (*p == '/') {
|
|
|
|
sdirs[sdx++] = p; /* done */
|
|
|
|
} else {
|
|
|
|
if (strcmp(".", p) == 0)
|
|
|
|
continue; /* no point */
|
|
|
|
|
|
|
|
/* Check vs latestdir */
|
|
|
|
snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p);
|
|
|
|
sdirs[sdx++] = fname1;
|
|
|
|
|
2015-04-11 08:24:30 +03:00
|
|
|
if (strcmp(latestdir, lcwd) != 0) {
|
|
|
|
/* Check vs lcwd */
|
|
|
|
snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p);
|
2010-12-10 01:30:16 +03:00
|
|
|
sdirs[sdx++] = fname2;
|
|
|
|
}
|
2015-04-11 08:24:30 +03:00
|
|
|
if (strcmp(lcwd, cwd) != 0) {
|
|
|
|
/* Check vs cwd */
|
|
|
|
snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p);
|
|
|
|
sdirs[sdx++] = fname3;
|
|
|
|
}
|
2010-12-10 01:30:16 +03:00
|
|
|
}
|
|
|
|
sdirs[sdx++] = NULL;
|
2010-09-13 19:36:57 +04:00
|
|
|
|
2010-12-10 01:30:16 +03:00
|
|
|
for (sdp = sdirs; *sdp && !found; sdp++) {
|
2011-02-08 08:29:13 +03:00
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp);
|
|
|
|
#endif
|
2016-06-07 03:40:00 +03:00
|
|
|
if (cached_stat(*sdp, &fs) == 0) {
|
2010-12-10 01:30:16 +03:00
|
|
|
found = 1;
|
|
|
|
p = *sdp;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (found) {
|
2011-02-08 08:29:13 +03:00
|
|
|
#ifdef DEBUG_META_MODE
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p);
|
|
|
|
#endif
|
2010-12-10 01:30:16 +03:00
|
|
|
if (!S_ISDIR(fs.st_mode) &&
|
|
|
|
fs.st_mtime > gn->mtime) {
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p);
|
|
|
|
oodate = TRUE;
|
|
|
|
} else if (S_ISDIR(fs.st_mode)) {
|
|
|
|
/* Update the latest directory. */
|
2016-06-03 04:21:59 +03:00
|
|
|
cached_realpath(p, latestdir);
|
2010-12-10 01:30:16 +03:00
|
|
|
}
|
|
|
|
} else if (errno == ENOENT && *p == '/' &&
|
|
|
|
strncmp(p, cwd, cwdlen) != 0) {
|
|
|
|
/*
|
|
|
|
* A referenced file outside of CWD is missing.
|
|
|
|
* We cannot catch every eventuality here...
|
|
|
|
*/
|
2016-03-11 10:01:21 +03:00
|
|
|
if (Lst_Find(missingFiles, p, string_match) == NULL)
|
|
|
|
Lst_AtEnd(missingFiles, bmake_strdup(p));
|
When a source file moves, make will ignore the stale dependency,
but if the file in question is one that needs to be compiled (.c or .cc),
it still hands the bogus name to the compiler.
If Dir_MTime() cannot find such a file (gn->iParents is not empty),
see if the basename can be found via .PATH, and if so set gn->path to
the found file. This prevents the stale path being given to the
compiler.
In meta_oodate(), if a referenced file no longer exists, consider the
target out-of-date.
Also, if meta_oodate() decides a target is out-of-date, and it
it uses .OODATE in its commands, we need .OODATE recomputed.
Undo our call to Make_DoAllVar() so that the call from Make_OODate()
will do the right thing.
2010-11-27 08:02:35 +03:00
|
|
|
}
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
2015-04-11 08:24:30 +03:00
|
|
|
if (buf[0] == 'E') {
|
|
|
|
/* previous latestdir is no longer relevant */
|
|
|
|
strlcpy(latestdir, lcwd, sizeof(latestdir));
|
|
|
|
}
|
2010-09-13 19:36:57 +04:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
2013-10-01 09:37:17 +04:00
|
|
|
if (!oodate && buf[0] == 'L' && link_src != NULL)
|
|
|
|
goto check_link_src;
|
2010-12-10 01:30:16 +03:00
|
|
|
} else if (strcmp(buf, "CMD") == 0) {
|
2010-09-13 19:36:57 +04:00
|
|
|
/*
|
|
|
|
* Compare the current command with the one in the
|
|
|
|
* meta data file.
|
|
|
|
*/
|
|
|
|
if (ln == NULL) {
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno);
|
|
|
|
oodate = TRUE;
|
|
|
|
} else {
|
|
|
|
char *cmd = (char *)Lst_Datum(ln);
|
2013-03-31 09:49:51 +04:00
|
|
|
Boolean hasOODATE = FALSE;
|
|
|
|
|
|
|
|
if (strstr(cmd, "$?"))
|
|
|
|
hasOODATE = TRUE;
|
|
|
|
else if ((cp = strstr(cmd, ".OODATE"))) {
|
|
|
|
/* check for $[{(].OODATE[:)}] */
|
|
|
|
if (cp > cmd + 2 && cp[-2] == '$')
|
|
|
|
hasOODATE = TRUE;
|
|
|
|
}
|
|
|
|
if (hasOODATE) {
|
|
|
|
needOODATE = TRUE;
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno);
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
2016-02-18 23:33:40 +03:00
|
|
|
cmd = Var_Subst(NULL, cmd, gn, VARF_WANTRES|VARF_UNDEFERR);
|
2010-09-13 19:36:57 +04:00
|
|
|
|
|
|
|
if ((cp = strchr(cmd, '\n'))) {
|
|
|
|
int n;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This command contains newlines, we need to
|
|
|
|
* fetch more from the .meta file before we
|
|
|
|
* attempt a comparison.
|
|
|
|
*/
|
|
|
|
/* first put the newline back at buf[x - 1] */
|
|
|
|
buf[x - 1] = '\n';
|
|
|
|
do {
|
|
|
|
/* now fetch the next line */
|
|
|
|
if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0)
|
|
|
|
break;
|
|
|
|
x = n;
|
|
|
|
lineno++;
|
|
|
|
if (buf[x - 1] != '\n') {
|
|
|
|
warnx("%s: %d: line truncated at %u", fname, lineno, x);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cp = strchr(++cp, '\n');
|
|
|
|
} while (cp);
|
|
|
|
if (buf[x - 1] == '\n')
|
|
|
|
buf[x - 1] = '\0';
|
|
|
|
}
|
2013-03-31 09:49:51 +04:00
|
|
|
if (!hasOODATE &&
|
2010-09-13 19:36:57 +04:00
|
|
|
!(gn->type & OP_NOMETA_CMP) &&
|
|
|
|
strcmp(p, cmd) != 0) {
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd);
|
|
|
|
if (!metaIgnoreCMDs)
|
|
|
|
oodate = TRUE;
|
|
|
|
}
|
|
|
|
free(cmd);
|
|
|
|
ln = Lst_Succ(ln);
|
|
|
|
}
|
|
|
|
} else if (strcmp(buf, "CWD") == 0) {
|
2011-03-29 21:29:20 +04:00
|
|
|
/*
|
|
|
|
* Check if there are extra commands now
|
|
|
|
* that weren't in the meta data file.
|
|
|
|
*/
|
|
|
|
if (!oodate && ln != NULL) {
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno);
|
|
|
|
oodate = TRUE;
|
|
|
|
}
|
2011-03-06 20:41:11 +03:00
|
|
|
if (strcmp(p, cwd) != 0) {
|
2010-09-13 19:36:57 +04:00
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir);
|
|
|
|
oodate = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fclose(fp);
|
2011-05-05 00:38:31 +04:00
|
|
|
if (!Lst_IsEmpty(missingFiles)) {
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: missing files: %s...\n",
|
|
|
|
fname, (char *)Lst_Datum(Lst_First(missingFiles)));
|
|
|
|
oodate = TRUE;
|
|
|
|
}
|
2016-06-05 01:17:14 +03:00
|
|
|
if (!oodate && !have_filemon && filemonMissing) {
|
2011-08-18 04:00:21 +04:00
|
|
|
if (DEBUG(META))
|
2016-06-05 01:17:14 +03:00
|
|
|
fprintf(debug_file, "%s: missing filemon data\n", fname);
|
2011-08-18 04:00:21 +04:00
|
|
|
oodate = TRUE;
|
|
|
|
}
|
2016-06-05 01:17:14 +03:00
|
|
|
} else {
|
|
|
|
if (writeMeta && metaMissing) {
|
|
|
|
cp = NULL;
|
2016-02-27 19:17:26 +03:00
|
|
|
|
2016-06-05 01:17:14 +03:00
|
|
|
/* if target is in .CURDIR we do not need a meta file */
|
|
|
|
if (gn->path && (cp = strrchr(gn->path, '/')) && cp > gn->path) {
|
|
|
|
if (strncmp(curdir, gn->path, (cp - gn->path)) != 0) {
|
|
|
|
cp = NULL; /* not in .CURDIR */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!cp) {
|
|
|
|
if (DEBUG(META))
|
|
|
|
fprintf(debug_file, "%s: required but missing\n", fname);
|
|
|
|
oodate = TRUE;
|
2016-06-14 21:16:06 +03:00
|
|
|
needOODATE = TRUE; /* assume the worst */
|
2016-06-05 01:17:14 +03:00
|
|
|
}
|
|
|
|
}
|
2016-06-03 04:16:27 +03:00
|
|
|
}
|
|
|
|
|
2016-02-27 19:17:26 +03:00
|
|
|
Lst_Destroy(missingFiles, (FreeProc *)free);
|
|
|
|
|
2013-01-19 08:23:37 +04:00
|
|
|
if (oodate && needOODATE) {
|
When a source file moves, make will ignore the stale dependency,
but if the file in question is one that needs to be compiled (.c or .cc),
it still hands the bogus name to the compiler.
If Dir_MTime() cannot find such a file (gn->iParents is not empty),
see if the basename can be found via .PATH, and if so set gn->path to
the found file. This prevents the stale path being given to the
compiler.
In meta_oodate(), if a referenced file no longer exists, consider the
target out-of-date.
Also, if meta_oodate() decides a target is out-of-date, and it
it uses .OODATE in its commands, we need .OODATE recomputed.
Undo our call to Make_DoAllVar() so that the call from Make_OODate()
will do the right thing.
2010-11-27 08:02:35 +03:00
|
|
|
/*
|
2013-01-19 08:23:37 +04:00
|
|
|
* Target uses .OODATE which is empty; or we wouldn't be here.
|
|
|
|
* We have decided it is oodate, so .OODATE needs to be set.
|
|
|
|
* All we can sanely do is set it to .ALLSRC.
|
When a source file moves, make will ignore the stale dependency,
but if the file in question is one that needs to be compiled (.c or .cc),
it still hands the bogus name to the compiler.
If Dir_MTime() cannot find such a file (gn->iParents is not empty),
see if the basename can be found via .PATH, and if so set gn->path to
the found file. This prevents the stale path being given to the
compiler.
In meta_oodate(), if a referenced file no longer exists, consider the
target out-of-date.
Also, if meta_oodate() decides a target is out-of-date, and it
it uses .OODATE in its commands, we need .OODATE recomputed.
Undo our call to Make_DoAllVar() so that the call from Make_OODate()
will do the right thing.
2010-11-27 08:02:35 +03:00
|
|
|
*/
|
|
|
|
Var_Delete(OODATE, gn);
|
2013-01-19 08:23:37 +04:00
|
|
|
Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn, 0);
|
2016-01-17 20:45:21 +03:00
|
|
|
free(cp);
|
When a source file moves, make will ignore the stale dependency,
but if the file in question is one that needs to be compiled (.c or .cc),
it still hands the bogus name to the compiler.
If Dir_MTime() cannot find such a file (gn->iParents is not empty),
see if the basename can be found via .PATH, and if so set gn->path to
the found file. This prevents the stale path being given to the
compiler.
In meta_oodate(), if a referenced file no longer exists, consider the
target out-of-date.
Also, if meta_oodate() decides a target is out-of-date, and it
it uses .OODATE in its commands, we need .OODATE recomputed.
Undo our call to Make_DoAllVar() so that the call from Make_OODate()
will do the right thing.
2010-11-27 08:02:35 +03:00
|
|
|
}
|
2016-06-03 04:16:27 +03:00
|
|
|
|
|
|
|
oodate_out:
|
|
|
|
for (i--; i >= 0; i--) {
|
|
|
|
free(pa[i]);
|
|
|
|
}
|
2010-09-13 19:36:57 +04:00
|
|
|
return oodate;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* support for compat mode */
|
|
|
|
|
|
|
|
static int childPipe[2];
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_compat_start(void)
|
|
|
|
{
|
|
|
|
#ifdef USE_FILEMON_ONCE
|
|
|
|
/*
|
|
|
|
* We need to re-open filemon for each cmd.
|
|
|
|
*/
|
|
|
|
BuildMon *pbm = &Mybm;
|
|
|
|
|
|
|
|
if (pbm->mfp != NULL && useFilemon) {
|
|
|
|
filemon_open(pbm);
|
|
|
|
} else {
|
|
|
|
pbm->mon_fd = pbm->filemon_fd = -1;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if (pipe(childPipe) < 0)
|
|
|
|
Punt("Cannot create pipe: %s", strerror(errno));
|
|
|
|
/* Set close-on-exec flag for both */
|
2016-01-17 18:30:23 +03:00
|
|
|
(void)fcntl(childPipe[0], F_SETFD, FD_CLOEXEC);
|
|
|
|
(void)fcntl(childPipe[1], F_SETFD, FD_CLOEXEC);
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_compat_child(void)
|
|
|
|
{
|
|
|
|
meta_job_child(NULL);
|
|
|
|
if (dup2(childPipe[1], 1) < 0 ||
|
|
|
|
dup2(1, 2) < 0) {
|
|
|
|
execError("dup2", "pipe");
|
|
|
|
_exit(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
meta_compat_parent(void)
|
|
|
|
{
|
|
|
|
FILE *fp;
|
|
|
|
char buf[BUFSIZ];
|
|
|
|
|
|
|
|
close(childPipe[1]); /* child side */
|
|
|
|
fp = fdopen(childPipe[0], "r");
|
|
|
|
while (fgets(buf, sizeof(buf), fp)) {
|
|
|
|
meta_job_output(NULL, buf, "");
|
|
|
|
printf("%s", buf);
|
2016-06-30 01:10:08 +03:00
|
|
|
fflush(stdout);
|
2010-09-13 19:36:57 +04:00
|
|
|
}
|
|
|
|
fclose(fp);
|
|
|
|
}
|
2010-09-14 01:31:59 +04:00
|
|
|
|
|
|
|
#endif /* USE_META */
|