Module Name: src Committed By: sjg Date: Mon Sep 13 15:36:58 UTC 2010
Modified Files: src/usr.bin/make: Makefile compat.c job.c job.h main.c make.1 make.c make.h parse.c Added Files: src/usr.bin/make: meta.c meta.h Log Message: Add meta.c which implements "meta" mode for make. In this mode, a .meta file is created for each target, capturing the expanded commands used, any command output, and if filemon(9) is available, a record of system calls which are of interest. Not enabled unless USE_META=yes is set when building make. Also, if FILEMON_H exists, meta.c will be compiled to use filemon(9). To generate a diff of this commit: cvs rdiff -u -r1.50 -r1.51 src/usr.bin/make/Makefile cvs rdiff -u -r1.80 -r1.81 src/usr.bin/make/compat.c cvs rdiff -u -r1.154 -r1.155 src/usr.bin/make/job.c cvs rdiff -u -r1.39 -r1.40 src/usr.bin/make/job.h cvs rdiff -u -r1.189 -r1.190 src/usr.bin/make/main.c cvs rdiff -u -r1.179 -r1.180 src/usr.bin/make/make.1 cvs rdiff -u -r1.81 -r1.82 src/usr.bin/make/make.c cvs rdiff -u -r1.82 -r1.83 src/usr.bin/make/make.h cvs rdiff -u -r0 -r1.1 src/usr.bin/make/meta.c src/usr.bin/make/meta.h cvs rdiff -u -r1.164 -r1.165 src/usr.bin/make/parse.c Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/usr.bin/make/Makefile diff -u src/usr.bin/make/Makefile:1.50 src/usr.bin/make/Makefile:1.51 --- src/usr.bin/make/Makefile:1.50 Thu Apr 22 19:15:23 2010 +++ src/usr.bin/make/Makefile Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.50 2010/04/22 19:15:23 sjg Exp $ +# $NetBSD: Makefile,v 1.51 2010/09/13 15:36:57 sjg Exp $ # @(#)Makefile 5.2 (Berkeley) 12/28/90 PROG= make @@ -13,6 +13,17 @@ lstMember.c lstNext.c lstOpen.c lstRemove.c lstReplace.c lstSucc.c SRCS += lstPrev.c +# let people experiment for a bit +USE_META ?= no +.if ${USE_META:tl} != "no" +SRCS+= meta.c +CPPFLAGS+= -DUSE_META +FILEMON_H ?= ${.CURDIR:H:H}/sys/dev/filemon/filemon.h +.if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h" +COPTS.meta.c += -DHAVE_FILEMON_H -I${FILEMON_H:H} +.endif +.endif + .PATH: ${.CURDIR}/lst.lib .if make(install) SUBDIR= PSD.doc Index: src/usr.bin/make/compat.c diff -u src/usr.bin/make/compat.c:1.80 src/usr.bin/make/compat.c:1.81 --- src/usr.bin/make/compat.c:1.80 Sat Aug 7 06:44:08 2010 +++ src/usr.bin/make/compat.c Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: compat.c,v 1.80 2010/08/07 06:44:08 sjg Exp $ */ +/* $NetBSD: compat.c,v 1.81 2010/09/13 15:36:57 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -70,14 +70,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: compat.c,v 1.80 2010/08/07 06:44:08 sjg Exp $"; +static char rcsid[] = "$NetBSD: compat.c,v 1.81 2010/09/13 15:36:57 sjg Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: compat.c,v 1.80 2010/08/07 06:44:08 sjg Exp $"); +__RCSID("$NetBSD: compat.c,v 1.81 2010/09/13 15:36:57 sjg Exp $"); #endif #endif /* not lint */ #endif @@ -352,6 +352,12 @@ local = TRUE; +#ifdef USE_META + if (useMeta) { + meta_compat_start(); + } +#endif + /* * Fork and execute the single command. If the fork fails, we abort. */ @@ -362,6 +368,11 @@ if (cpid == 0) { Check_Cwd(av); Var_ExportVars(); +#ifdef USE_META + if (useMeta) { + meta_compat_child(); + } +#endif if (local) (void)execvp(av[0], (char *const *)UNCONST(av)); else @@ -375,6 +386,12 @@ free(bp); Lst_Replace(cmdNode, NULL); +#ifdef USE_META + if (useMeta) { + meta_compat_parent(); + } +#endif + /* * The child is off and running. Now all we can do is wait... */ @@ -393,6 +410,11 @@ status = WSTOPSIG(reason); /* stopped */ } else if (WIFEXITED(reason)) { status = WEXITSTATUS(reason); /* exited */ +#if defined(USE_META) && defined(USE_FILEMON_ONCE) + if (useMeta) { + meta_cmd_finish(NULL); + } +#endif if (status != 0) { if (DEBUG(ERROR)) { fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", @@ -419,6 +441,11 @@ if (!WIFEXITED(reason) || (status != 0)) { if (errCheck) { +#ifdef USE_META + if (useMeta) { + meta_job_error(NULL, gn, 0, status); + } +#endif gn->made = ERROR; if (keepgoing) { /* @@ -551,6 +578,11 @@ */ if (!touchFlag || (gn->type & OP_MAKE)) { curTarg = gn; +#ifdef USE_META + if (useMeta && !NoExecute(gn)) { + meta_job_start(NULL, gn); + } +#endif Lst_ForEach(gn->commands, CompatRunCommand, gn); curTarg = NULL; } else { @@ -559,6 +591,11 @@ } else { gn->made = ERROR; } +#ifdef USE_META + if (useMeta && !NoExecute(gn)) { + meta_job_finish(NULL); + } +#endif if (gn->made != ERROR) { /* Index: src/usr.bin/make/job.c diff -u src/usr.bin/make/job.c:1.154 src/usr.bin/make/job.c:1.155 --- src/usr.bin/make/job.c:1.154 Sat Aug 7 21:28:40 2010 +++ src/usr.bin/make/job.c Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: job.c,v 1.154 2010/08/07 21:28:40 sjg Exp $ */ +/* $NetBSD: job.c,v 1.155 2010/09/13 15:36:57 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -70,14 +70,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: job.c,v 1.154 2010/08/07 21:28:40 sjg Exp $"; +static char rcsid[] = "$NetBSD: job.c,v 1.155 2010/09/13 15:36:57 sjg Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: job.c,v 1.154 2010/08/07 21:28:40 sjg Exp $"); +__RCSID("$NetBSD: job.c,v 1.155 2010/09/13 15:36:57 sjg Exp $"); #endif #endif /* not lint */ #endif @@ -960,6 +960,12 @@ { Boolean done, return_job_token; +#ifdef USE_META + if (useMeta) { + meta_job_finish(job); + } +#endif + if (DEBUG(JOB)) { fprintf(debug_file, "Jobfinish: %d [%s], status %d\n", job->pid, job->node->name, status); @@ -1017,6 +1023,11 @@ MESSAGE(stdout, job->node); lastNode = job->node; } +#ifdef USE_META + if (useMeta) { + meta_job_error(job, job->node, job->flags, WEXITSTATUS(status)); + } +#endif (void)printf("*** [%s] Error code %d%s\n", job->node->name, WEXITSTATUS(status), @@ -1311,6 +1322,11 @@ /* Child */ sigset_t tmask; +#ifdef USE_META + if (useMeta) { + meta_job_child(job); + } +#endif /* * Reset all signal handlers; this is necessary because we also * need to unblock signals before we exec(2). @@ -1574,6 +1590,11 @@ */ noExec = FALSE; +#ifdef USE_META + if (useMeta) { + meta_job_start(job, gn); + } +#endif /* * We can do all the commands at once. hooray for sanity */ @@ -1846,6 +1867,11 @@ MESSAGE(stdout, job->node); lastNode = job->node; } +#ifdef USE_META + if (useMeta) { + meta_job_output(job, cp, gotNL ? "\n" : ""); + } +#endif (void)fprintf(stdout, "%s%s", cp, gotNL ? "\n" : ""); (void)fflush(stdout); } Index: src/usr.bin/make/job.h diff -u src/usr.bin/make/job.h:1.39 src/usr.bin/make/job.h:1.40 --- src/usr.bin/make/job.h:1.39 Sat Apr 11 09:41:18 2009 +++ src/usr.bin/make/job.h Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: job.h,v 1.39 2009/04/11 09:41:18 apb Exp $ */ +/* $NetBSD: job.h,v 1.40 2010/09/13 15:36:57 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -135,6 +135,11 @@ */ struct pollfd; + +#ifdef USE_META +# include "meta.h" +#endif + #define JOB_BUFSIZE 1024 typedef struct Job { int pid; /* The child's process ID */ @@ -165,6 +170,10 @@ /* Buffer for storing the output of the * job, line by line */ int curPos; /* Current position in op_outBuf */ + +#ifdef USE_META + struct BuildMon bm; +#endif } Job; #define inPipe jobPipe[0] Index: src/usr.bin/make/main.c diff -u src/usr.bin/make/main.c:1.189 src/usr.bin/make/main.c:1.190 --- src/usr.bin/make/main.c:1.189 Sat Aug 7 06:44:08 2010 +++ src/usr.bin/make/main.c Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.189 2010/08/07 06:44:08 sjg Exp $ */ +/* $NetBSD: main.c,v 1.190 2010/09/13 15:36:57 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,7 +69,7 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: main.c,v 1.189 2010/08/07 06:44:08 sjg Exp $"; +static char rcsid[] = "$NetBSD: main.c,v 1.190 2010/09/13 15:36:57 sjg Exp $"; #else #include <sys/cdefs.h> #ifndef lint @@ -81,7 +81,7 @@ #if 0 static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: main.c,v 1.189 2010/08/07 06:44:08 sjg Exp $"); +__RCSID("$NetBSD: main.c,v 1.190 2010/09/13 15:36:57 sjg Exp $"); #endif #endif /* not lint */ #endif @@ -242,6 +242,9 @@ case 'l': debug |= DEBUG_LOUD; break; + case 'M': + debug |= DEBUG_META; + break; case 'm': debug |= DEBUG_MAKE; break; @@ -732,6 +735,10 @@ compatMake = TRUE; forceJobs = FALSE; } +#if USE_META + if (strstr(mode, "meta")) + meta_init(mode); +#endif } if (mp) free(mp); Index: src/usr.bin/make/make.1 diff -u src/usr.bin/make/make.1:1.179 src/usr.bin/make/make.1:1.180 --- src/usr.bin/make/make.1:1.179 Wed Jun 30 01:03:54 2010 +++ src/usr.bin/make/make.1 Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.179 2010/06/30 01:03:54 dholland Exp $ +.\" $NetBSD: make.1,v 1.180 2010/09/13 15:36:57 sjg Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -194,6 +194,8 @@ .Ql @ or other "quiet" flags. Also known as "loud" behavior. +.It Ar M +Print debugging information about "meta" mode decisions about targets. .It Ar m Print debugging information about making targets, including modification dates. @@ -266,6 +268,8 @@ Specify the maximum number of jobs that .Nm may have running at any one time. +The value is saved in +.Va .MAKE.JOBS . Turns compatibility mode off, unless the .Ar B flag is also specified. @@ -759,9 +763,49 @@ Can affect the mode that .Nm runs in. -Currently just -.Ql Pa compat -mode. +It can contain a number of keywords: +.Bl -hang -width ignore-cmd +.It Pa compat +Like +.Fl B +puts +.Nm +into "compat" mode. +.It Pa meta +Puts +.Nm +into "meta" mode, where meta files are created for each target +to capture the command run, the output generated and if +.Xr filemon 4 +is available, the system calls which are of interest to +.Nm . +The captured output can be very useful when diagnosing errors. +.It Pa verbose +If in "meta" mode, print a clue about the target being built. +This is useful if the build is otherwise running silently. +The message printed the value of: +.Va .MAKE.META.PREFIX . +.It Pa ignore-cmd +Some makefiles have commands which are simply not stable. +This keyword causes them to be ignored for +determining whether a target is out of date in "meta" mode. +See also +.Ic .NOMETA_CMP . +.El +.It Va .MAKE.META.CREATED +In "meta" mode, this variable contains a list of all the meta files +updated. +If not empty, it can be used to trigger processing of +.Va .MAKE.META.FILES . +.It Va .MAKE.META.FILES +In "meta" mode, this variable contains a list of all the meta files +used (updated or not). +This list can be used to process the meta files to extract dependency +information. +.It Va .MAKE.META.PREFIX +Defines the message printed for each meta file updated in "meta verbose" mode. +The default value is: +.Dl Building ${.TARGET:H:tA}/${.TARGET:T} .It Va .MAKEOVERRIDES This variable is used to record the names of variables assigned to on the command line, so that they may be exported as part of @@ -1612,6 +1656,27 @@ options were specified. Normally used to mark recursive .Nm Ns 's . +.It Ic .META +Create a meta file for the target, even if it is flagged as +.Ic .PHONY , +.Ic .MAKE +or +.Ic .SPECIAL . +Usage in conjunction with +.Ic .MAKE +is the most likely case. +.It Ic .NOMETA +Do not create a meta file for the target. +Meta files are also not created for +.Ic .PHONY , +.Ic .MAKE +or +.Ic .SPECIAL +targets. +.It Ic .NOMETA_CMP +Ignore differences in commands when deciding if target is out of date. +This is useful if the command contains a value which always changes. +If the number of commands change, though the target will still be out of date. .It Ic .NOPATH Do not search for the target in the directories specified by .Ic .PATH . Index: src/usr.bin/make/make.c diff -u src/usr.bin/make/make.c:1.81 src/usr.bin/make/make.c:1.82 --- src/usr.bin/make/make.c:1.81 Tue Jul 6 03:56:59 2010 +++ src/usr.bin/make/make.c Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: make.c,v 1.81 2010/07/06 03:56:59 dholland Exp $ */ +/* $NetBSD: make.c,v 1.82 2010/09/13 15:36:57 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: make.c,v 1.81 2010/07/06 03:56:59 dholland Exp $"; +static char rcsid[] = "$NetBSD: make.c,v 1.82 2010/09/13 15:36:57 sjg Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93"; #else -__RCSID("$NetBSD: make.c,v 1.81 2010/07/06 03:56:59 dholland Exp $"); +__RCSID("$NetBSD: make.c,v 1.82 2010/09/13 15:36:57 sjg Exp $"); #endif #endif /* not lint */ #endif @@ -330,6 +330,12 @@ oodate = (gn->flags & FORCE) ? TRUE : FALSE; } +#ifdef USE_META + if (useMeta) { + oodate = meta_oodate(gn, oodate); + } +#endif + /* * If the target isn't out-of-date, the parents need to know its * modification time. Note that targets that appear to be out-of-date Index: src/usr.bin/make/make.h diff -u src/usr.bin/make/make.h:1.82 src/usr.bin/make/make.h:1.83 --- src/usr.bin/make/make.h:1.82 Fri Apr 23 00:18:50 2010 +++ src/usr.bin/make/make.h Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: make.h,v 1.82 2010/04/23 00:18:50 sjg Exp $ */ +/* $NetBSD: make.h,v 1.83 2010/09/13 15:36:57 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -260,6 +260,9 @@ #define OP_PHONY 0x00010000 /* Not a file target; run always */ #define OP_NOPATH 0x00020000 /* Don't search for file in the path */ #define OP_WAIT 0x00040000 /* .WAIT phony node */ +#define OP_NOMETA 0x00080000 /* .NOMETA do not create a .meta file */ +#define OP_META 0x00100000 /* .META we _do_ want a .meta file */ +#define OP_NOMETA_CMP 0x00200000 /* Do not compare commands in .meta file */ /* Attributes applied by PMake */ #define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */ #define OP_MEMBER 0x40000000 /* Target is a member of an archive */ @@ -433,6 +436,8 @@ #define DEBUG_SHELL 0x00800 #define DEBUG_ERROR 0x01000 #define DEBUG_LOUD 0x02000 +#define DEBUG_META 0x04000 + #define DEBUG_GRAPH3 0x10000 #define DEBUG_SCRIPT 0x20000 #define DEBUG_PARSE 0x40000 Index: src/usr.bin/make/parse.c diff -u src/usr.bin/make/parse.c:1.164 src/usr.bin/make/parse.c:1.165 --- src/usr.bin/make/parse.c:1.164 Mon May 24 21:04:49 2010 +++ src/usr.bin/make/parse.c Mon Sep 13 15:36:57 2010 @@ -1,4 +1,4 @@ -/* $NetBSD: parse.c,v 1.164 2010/05/24 21:04:49 sjg Exp $ */ +/* $NetBSD: parse.c,v 1.165 2010/09/13 15:36:57 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: parse.c,v 1.164 2010/05/24 21:04:49 sjg Exp $"; +static char rcsid[] = "$NetBSD: parse.c,v 1.165 2010/09/13 15:36:57 sjg Exp $"; #else #include <sys/cdefs.h> #ifndef lint #if 0 static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; #else -__RCSID("$NetBSD: parse.c,v 1.164 2010/05/24 21:04:49 sjg Exp $"); +__RCSID("$NetBSD: parse.c,v 1.165 2010/09/13 15:36:57 sjg Exp $"); #endif #endif /* not lint */ #endif @@ -196,10 +196,13 @@ Includes, /* .INCLUDES */ Interrupt, /* .INTERRUPT */ Libs, /* .LIBS */ + Meta, /* .META */ MFlags, /* .MFLAGS or .MAKEFLAGS */ Main, /* .MAIN and we don't have anything user-specified to * make */ NoExport, /* .NOEXPORT */ + NoMeta, /* .NOMETA */ + NoMetaCmp, /* .NOMETA_CMP */ NoPath, /* .NOPATH */ Not, /* Not special */ NotParallel, /* .NOTPARALLEL */ @@ -258,7 +261,10 @@ { ".MAIN", Main, 0 }, { ".MAKE", Attribute, OP_MAKE }, { ".MAKEFLAGS", MFlags, 0 }, +{ ".META", Meta, OP_META }, { ".MFLAGS", MFlags, 0 }, +{ ".NOMETA", NoMeta, OP_NOMETA }, +{ ".NOMETA_CMP", NoMetaCmp, OP_NOMETA_CMP }, { ".NOPATH", NoPath, OP_NOPATH }, { ".NOTMAIN", Attribute, OP_NOTMAIN }, { ".NOTPARALLEL", NotParallel, 0 }, Added files: Index: src/usr.bin/make/meta.c diff -u /dev/null src/usr.bin/make/meta.c:1.1 --- /dev/null Mon Sep 13 15:36:58 2010 +++ src/usr.bin/make/meta.c Mon Sep 13 15:36:57 2010 @@ -0,0 +1,955 @@ +/* + * Implement 'meta' mode. + * Adapted from John Birrell's patches to FreeBSD make. + * --sjg + */ +/* + * Copyright (c) 2009-2010, Juniper Networks, Inc. + * 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. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <libgen.h> +#include <errno.h> +#include <err.h> + +#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 */ + +Boolean useMeta = FALSE; +static Boolean useFilemon = FALSE; +static Boolean writeMeta = FALSE; +static Boolean metaEnv = FALSE; /* don't save env unless asked */ +static Boolean metaVerbose = FALSE; +static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ + +extern Boolean forceJobs; +extern Boolean comatMake; + +#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 + +/* + * 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 */ + (void)fcntl(pbm->mon_fd, F_SETFD, 1); + (void)fcntl(pbm->filemon_fd, F_SETFD, 1); +} + +/* + * Read the build monitor output file and write records to the target's + * metadata file. + */ +static void +filemon_read(FILE *mfp, int fd) +{ + FILE *fp; + char buf[BUFSIZ]; + + /* Check if we're not writing to a meta data file.*/ + if (mfp == NULL) { + if (fd >= 0) + close(fd); /* not interested */ + return; + } + /* rewind */ + lseek(fd, SEEK_SET, 0); + if ((fp = fdopen(fd, "r")) == NULL) + err(1, "Could not read build monitor file '%d'", fd); + + fprintf(mfp, "-- filemon acquired metadata --\n"); + + while (fgets(buf, sizeof(buf), fp)) { + fprintf(mfp, "%s", buf); + } + fflush(mfp); + clearerr(fp); + fclose(fp); +} +#endif + +static char * +meta_name(struct GNode *gn, char *mname, size_t mnamelen, + const char *dname, + const char *tname) +{ + char buf[MAXPATHLEN]; + char cwd[MAXPATHLEN]; + char *rp; + char *cp; + char *tp; + char *p[4]; /* >= number of possible uses */ + int i; + + i = 0; + if (!dname) + dname = Var_Value(".OBJDIR", gn, &p[i++]); + if (!tname) + tname = Var_Value(TARGET, gn, &p[i++]); + + /* + * 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, '/'))) { + if (realpath(tname, buf)) { + if ((rp = strrchr(buf, '/'))) { + rp++; + cp++; + if (strcmp(cp, rp) != 0) + strlcpy(rp, cp, sizeof(buf) - (rp - buf)); + } + tname = buf; + } + } + if (realpath(dname, cwd)) + dname = cwd; + /* 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); + for (i--; i >= 0; i--) { + if (p[i]) + free(p[i]); + } + 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)) { + mp = Var_Subst(NULL, cmd, gn, FALSE); + 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; + } + } + } + if (mp) + free(mp); + 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, '$')) { + cmd = cp = Var_Subst(NULL, cmd, mfp->gn, FALSE); + } + fprintf(mfp->fp, "CMD %s\n", cmd); + if (cp) + free(cp); + return 0; +} + +/* + * Certain node types never get a .meta file + */ +#define SKIP_META_TYPE(_type) do { \ + if ((gn->type & __CONCAT(OP_, _type))) { \ + if (DEBUG(META)) { \ + fprintf(debug_file, "Skipping meta for %s: .%s\n", \ + gn->name, __STRING(_type)); \ + } \ + return (NULL); \ + } \ +} while (0) + +static FILE * +meta_create(BuildMon *pbm, GNode *gn) +{ + extern char **environ; + meta_file_t mf; + char buf[MAXPATHLEN]; + char curdir[MAXPATHLEN]; + char objdir[MAXPATHLEN]; + char **ptr; + const char *cname; + const char *dname; + const char *tname; + char *fname; + const char *cp; + char *p[4]; /* >= possible uses */ + int i; + struct stat fs; + + + /* 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); + } + + mf.fp = NULL; + + i = 0; + + dname = Var_Value(".OBJDIR", gn, &p[i++]); + cname = Var_Value(".CURDIR", gn, &p[i++]); + tname = Var_Value(TARGET, gn, &p[i++]); + + /* The object directory may not exist. Check it.. */ + if (stat(dname, &fs) != 0) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", + gn->name); + goto out; + } + /* Check if there are no commands to execute. */ + if (Lst_IsEmpty(gn->commands)) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: no commands\n", + gn->name); + goto out; + } + + /* make sure these are canonical */ + if (realpath(dname, objdir)) + dname = objdir; + if (realpath(cname, curdir)) + cname = curdir; + + /* If we aren't in the object directory, don't create a meta file. */ + if (strcmp(cname, dname) == 0) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", + gn->name); + goto out; + } + if (!(gn->type & OP_META)) { + /* We do not generate .meta files for sub-makes */ + if (Lst_ForEach(gn->commands, is_submake, gn)) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: .MAKE\n", + gn->name); + goto out; + } + } + + if (metaVerbose) { + char *mp; + + /* Describe the target we are building */ + mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, 0); + 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 (strcmp(cp, makeDependfile) == 0) + goto out; + + if (!writeMeta) + /* Don't create meta data. */ + goto out; + + fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname), + dname, tname); + + 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); + + out: + for (i--; i >= 0; i--) { + if (p[i]) + free(p[i]); + } + + return (mf.fp); +} + + +void +meta_init(const char *make_mode) +{ + static int once = 0; + + 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; + /* for backwards compatability */ + Var_Set(".MAKE.META_CREATED", "${.MAKE.META.CREATED}", VAR_GLOBAL, 0); + Var_Set(".MAKE.META_FILES", "${.MAKE.META.FILES}", VAR_GLOBAL, 0); + } + 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)); +} + +/* + * 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; + pid_t pid; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + pid = getpid(); + if (pbm->mfp != NULL && useFilemon) { + if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) { + err(1, "Could not set filemon pid!"); + } + } +#endif +} + +void +meta_job_error(Job *job, GNode *gn, int flags, int status) +{ + char cwd[MAXPATHLEN]; + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + if (!gn) + gn = job->node; + 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); +#ifdef USE_FILEMON + if (pbm) { + Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0); + } +#endif +} + +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; + + meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", VAR_GLOBAL, 0); + 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); + } +} + +void +meta_cmd_finish(void *pbmp) +{ +#ifdef USE_FILEMON + BuildMon *pbm = pbmp; + + if (!pbm) + pbm = &Mybm; + + if (pbm->filemon_fd >= 0) { + close(pbm->filemon_fd); + filemon_read(pbm->mfp, pbm->mon_fd); + pbm->filemon_fd = pbm->mon_fd = -1; + } +#endif +} + +void +meta_job_finish(Job *job) +{ + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + if (pbm->mfp != NULL) { + meta_cmd_finish(pbm); + fclose(pbm->mfp); + pbm->mfp = NULL; + } +} + +/* + * 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)) + fprintf(debug_file, "growing buffer %u -> %u\n", + bufsz, newsz); + 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; +} + +/* + * When running with 'meta' functionality, a target can be out-of-date + * if any of the references in it's meta data file is more recent. + */ +Boolean +meta_oodate(GNode *gn, Boolean oodate) +{ + char latestdir[MAXPATHLEN]; + char fname[MAXPATHLEN]; + char fname1[MAXPATHLEN]; + char *p; + char *cp; + FILE *fp; + Boolean ignoreOODATE = FALSE; + + /* + * 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); + + if (oodate) + return oodate; /* we're done */ + + if (getcwd(latestdir, sizeof(latestdir)) == NULL) + err(1, "Could not get current working directory"); + + meta_name(gn, fname, sizeof(fname), NULL, NULL); + + if ((fp = fopen(fname, "r")) != NULL) { + static char *buf = NULL; + static size_t bufsz; + int lineno = 0; + int f = 0; + int x; + LstNode ln; + struct stat fs; + + if (!buf) { + bufsz = 8 * BUFSIZ; + buf = bmake_malloc(bufsz); + } + + /* 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'; + else + warnx("%s: %d: line truncated at %u", fname, lineno, x); + + /* Find the start of the build monitor section. */ + if (!f) { + if (strncmp(buf, "-- filemon", 10) == 0) { + f = 1; + continue; + } + if (strncmp(buf, "# buildmon", 10) == 0) { + f = 1; + continue; + } + } + + /* Delimit the record type. */ + p = buf; + strsep(&p, " "); + if (f) { + /* Process according to record type. */ + switch (buf[0]) { + case 'C': + /* Skip the pid. */ + if (strsep(&p, " ") == NULL) + break; + + /* Update the latest directory. */ + strlcpy(latestdir, p, sizeof(latestdir)); + break; + + case 'R': + case 'E': + /* Skip the pid. */ + if (strsep(&p, " ") == NULL) + break; + + /* + * Check for runtime files that can't + * be part of the dependencies because + * they are _expected_ to change. + */ + if (strncmp(p, "/var/", 5) == 0) + break; + + /* Ignore device files. */ + if (strncmp(p, "/dev/", 5) == 0) + break; + + /* Ignore /etc/ files. */ + if (strncmp(p, "/etc/", 5) == 0) + break; + + /* + * The rest of the record is the + * file name. + * Check if it's not an absolute + * path. + */ + if (*p != '/') { + /* Use the latest path seen. */ + snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); + p = fname1; + } + + if (stat(p, &fs) == 0 && + !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; + } + break; + default: + break; + } + + /* + * Compare the current command with the one in the + * meta data file. + */ + } else if (strcmp(buf, "CMD") == 0) { + 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); + + if (!ignoreOODATE) { + if (strstr(cmd, "$?")) + ignoreOODATE = TRUE; + else if ((cp = strstr(cmd, ".OODATE"))) { + /* check for $[{(].OODATE[)}] */ + if (cp > cmd + 2 && cp[-2] == '$') + ignoreOODATE = TRUE; + } + if (ignoreOODATE && DEBUG(META)) + fprintf(debug_file, "%s: %d: cannot compare commands using .OODATE\n", fname, lineno); + } + cmd = Var_Subst(NULL, cmd, gn, TRUE); + + 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'; + } + if (!ignoreOODATE && + !(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) { + char curdir[MAXPATHLEN]; + if (strcmp(p, getcwd(curdir, sizeof(curdir))) != 0) { + 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; + } + } + } + + /* + * 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; + } + + fclose(fp); + } + 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 */ + (void)fcntl(childPipe[0], F_SETFD, 1); + (void)fcntl(childPipe[1], F_SETFD, 1); +} + +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); + } + fclose(fp); +} Index: src/usr.bin/make/meta.h diff -u /dev/null src/usr.bin/make/meta.h:1.1 --- /dev/null Mon Sep 13 15:36:58 2010 +++ src/usr.bin/make/meta.h Mon Sep 13 15:36:57 2010 @@ -0,0 +1,52 @@ +/* + * Things needed for 'meta' mode. + */ +/* + * Copyright (c) 2009-2010, Juniper Networks, Inc. + * + * 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. + * 3. Neither the name of the copyright holders nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * 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. + */ + +typedef struct BuildMon { + char meta_fname[MAXPATHLEN]; + int filemon_fd; + int mon_fd; + FILE *mfp; +} BuildMon; + +extern Boolean useMeta; + +struct Job; /* not defined yet */ +void meta_init(const char *); +void meta_job_start(struct Job *, GNode *); +void meta_job_child(struct Job *); +void meta_job_error(struct Job *, GNode *, int, int); +void meta_job_output(struct Job *, char *, const char *); +void meta_cmd_finish(void *); +void meta_job_finish(struct Job *); +Boolean meta_oodate(GNode *, Boolean); +void meta_compat_start(void); +void meta_compat_child(void); +void meta_compat_parent(void);