hostaddr id ifconfig ifdef install \
intr ipcrm ipcs irdpd isoread join kill last \
less loadkeys loadramdisk logger look lp \
- lpd ls lspci mail make MAKEDEV \
+ lpd ls lspci mail MAKEDEV \
mdb mesg mined mkfifo mkfs.mfs mknod \
mkproto mount mt netconf nice acknm nohup \
nonamed od paste patch pax \
+++ /dev/null
-# $NetBSD: Makefile,v 1.50 2010/04/22 19:15:23 sjg Exp $
-# @(#)Makefile 5.2 (Berkeley) 12/28/90
-
-PROG= make
-SRCS= arch.c buf.c compat.c cond.c dir.c for.c hash.c job.c main.c \
- make.c parse.c str.c suff.c targ.c trace.c var.c util.c
-SRCS+= strlist.c
-SRCS+= make_malloc.c
-SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \
- lstDatum.c lstDeQueue.c lstDestroy.c lstDupl.c lstEnQueue.c \
- lstFind.c lstFindFrom.c lstFirst.c lstForEach.c lstForEachFrom.c \
- lstInit.c lstInsert.c lstIsAtEnd.c lstIsEmpty.c lstLast.c \
- lstMember.c lstNext.c lstOpen.c lstRemove.c lstReplace.c lstSucc.c
-SRCS+= lstPrev.c
-
-
-# For MINIX
-CPPFLAGS+= -DHAVE_SETENV -DHAVE_STRERROR -DHAVE_STRDUP \
- -DHAVE_STRFTIME -DHAVE_VSNPRINTF -DUSE_SELECT
-MACHINE=${ARCH}
-MACHINE_ARCH=${ARCH}
-CPPFLAGS+= -DTARGET_MACHINE=\"${MACHINE}\" \
- -DTARGET_MACHINE_ARCH=\"${MACHINE_ARCH}\" \
- -DMAKE_MACHINE=\"${MACHINE}\" \
- -DMAKE_MACHINE_ARCH=\"${MACHINE_ARCH}\"
-
-
- .PATH: ${.CURDIR}/lst.lib
-
-# .if make(obj) || make(clean)
-# SUBDIR+= unit-tests
-# .endif
-
-.include <bsd.prog.mk>
-.include <bsd.subdir.mk>
-
-# A simple unit-test driver to help catch regressions
-accept test:
- cd ${.CURDIR}/unit-tests && ${.MAKE} -r -m / TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET}
+++ /dev/null
-# $Id: export-all,v 1.1 2007/10/05 15:27:46 sjg Exp $
-
-UT_OK=good
-UT_F=fine
-
-.export
-
-.include "export"
-
-UT_TEST=export-all
-UT_ALL=even this gets exported
+++ /dev/null
-
-LIST= one two three
-LIST+= four five six
-
-FU_mod-ts = a / b / cool
-
-AAA= a a a
-B.aaa= Baaa
-
-all: mod-ts
-
-mod-ts:
- @echo 'LIST="${LIST}"'
- @echo 'LIST:ts,="${LIST:ts,}"'
- @echo 'LIST:ts/:tu="${LIST:ts/:tu}"'
- @echo 'LIST:ts::tu="${LIST:ts::tu}"'
- @echo 'LIST:ts:tu="${LIST:ts:tu}"'
- @echo 'LIST:tu:ts/="${LIST:tu:ts/}"'
- @echo 'LIST:ts:="${LIST:ts:}"'
- @echo 'LIST:ts="${LIST:ts}"'
- @echo 'LIST:ts:S/two/2/="${LIST:ts:S/two/2/}"'
- @echo 'LIST:S/two/2/:ts="${LIST:S/two/2/:ts}"'
- @echo 'LIST:ts/:S/two/2/="${LIST:ts/:S/two/2/}"'
- @echo "Pretend the '/' in '/n' etc. below are back-slashes."
- @echo 'LIST:ts/n="${LIST:ts\n}"'
- @echo 'LIST:ts/t="${LIST:ts\t}"'
- @echo 'LIST:ts/012:tu="${LIST:ts\012:tu}"'
- @echo 'LIST:tx="${LIST:tx}"'
- @echo 'LIST:ts/x:tu="${LIST:ts\x:tu}"'
- @echo 'FU_$@="${FU_${@:ts}:ts}"'
- @echo 'FU_$@:ts:T="${FU_${@:ts}:ts:T}" == cool?'
- @echo 'B.$${AAA:ts}="${B.${AAA:ts}}" == Baaa?'
.include <bsd.own.mk>
# NetBSD imports
-SUBDIR= login indent m4 stat tic sed mkdep uniq seq du man \
+SUBDIR= login indent m4 make stat tic sed mkdep uniq seq du man \
apropos chpass newgrp passwd bzip2 bzip2recover gzip su genassym \
ldd/elf32 .WAIT ldd
-# $NetBSD: Makefile,v 1.50 2010/04/22 19:15:23 sjg Exp $
+# $NetBSD: Makefile,v 1.55 2011/08/14 13:06:09 christos Exp $
# @(#)Makefile 5.2 (Berkeley) 12/28/90
PROG= make
lstMember.c lstNext.c lstOpen.c lstRemove.c lstReplace.c lstSucc.c
SRCS += lstPrev.c
-.PATH: ${.CURDIR}/lst.lib
-.if make(install)
-SUBDIR= PSD.doc
+# 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
+
+# For MINIX
+CPPFLAGS+= -DHAVE_SETENV -DHAVE_STRERROR -DHAVE_STRDUP \
+ -DHAVE_STRFTIME -DHAVE_VSNPRINTF -DUSE_SELECT
+
+CPPFLAGS+= -DMAKE_MACHINE=\"${MACHINE}\" -DMAKE_MACHINE_ARCH=\"${MACHINE_ARCH}\"
+
+
+.PATH: ${.CURDIR}/lst.lib
+#.if make(install)
+#SUBDIR= PSD.doc
+#.endif
.if make(obj) || make(clean)
SUBDIR+= unit-tests
.endif
.include <bsd.prog.mk>
.include <bsd.subdir.mk>
+.ifdef TOOLDIR
CPPFLAGS+= -DMAKE_NATIVE
-COPTS.var.c+= -Wno-cast-qual
+COPTS.var.c += -Wno-cast-qual
+COPTS.job.c += -Wno-format-nonliteral
+COPTS.parse.c += -Wno-format-nonliteral
+COPTS.var.c += -Wno-format-nonliteral
-.ifdef TOOLDIR
# this is a native netbsd build,
# use libutil rather than the local emalloc etc.
CPPFLAGS+= -DUSE_EMALLOC
--- /dev/null
+# $NetBSD: Makefile,v 1.55 2011/08/14 13:06:09 christos Exp $
+# @(#)Makefile 5.2 (Berkeley) 12/28/90
+
+PROG= make
+SRCS= arch.c buf.c compat.c cond.c dir.c for.c hash.c job.c main.c \
+ make.c parse.c str.c suff.c targ.c trace.c var.c util.c
+SRCS+= strlist.c
+SRCS+= make_malloc.c
+SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \
+ lstDatum.c lstDeQueue.c lstDestroy.c lstDupl.c lstEnQueue.c \
+ lstFind.c lstFindFrom.c lstFirst.c lstForEach.c lstForEachFrom.c \
+ lstInit.c lstInsert.c lstIsAtEnd.c lstIsEmpty.c lstLast.c \
+ 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
+
+# For MINIX
+CPPFLAGS+= -DHAVE_SETENV -DHAVE_STRERROR -DHAVE_STRDUP \
+ -DHAVE_STRFTIME -DHAVE_VSNPRINTF -DUSE_SELECT
+
+# Gross hack, but we assume i386 everywhere
+.if ${MACHINE_ARCH} == "i686"
+MACHINE_ARCH=i386
+.endif
+.if $(MACHINE) == "i686"
+MACHINE=i386
+.endif
+CPPFLAGS+= -DTARGET_MACHINE=\"${MACHINE}\" \
+ -DTARGET_MACHINE_ARCH=\"${MACHINE_ARCH}\" \
+ -DMAKE_MACHINE=\"${MACHINE}\" \
+ -DMAKE_MACHINE_ARCH=\"${MACHINE_ARCH}\"
+
+
+.PATH: ${.CURDIR}/lst.lib
+.if make(install)
+SUBDIR= PSD.doc
+.endif
+.if make(obj) || make(clean)
+SUBDIR+= unit-tests
+.endif
+
+.include <bsd.prog.mk>
+.include <bsd.subdir.mk>
+
+.ifdef TOOLDIR
+CPPFLAGS+= -DMAKE_NATIVE
+COPTS.var.c += -Wno-cast-qual
+COPTS.job.c += -Wno-format-nonliteral
+COPTS.parse.c += -Wno-format-nonliteral
+COPTS.var.c += -Wno-format-nonliteral
+
+# this is a native netbsd build,
+# use libutil rather than the local emalloc etc.
+CPPFLAGS+= -DUSE_EMALLOC
+LDADD+=-lutil
+DPADD+=${LIBUTIL}
+.endif
+
+# A simple unit-test driver to help catch regressions
+accept test:
+ cd ${.CURDIR}/unit-tests && ${.MAKE} -r -m / TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET}
-# $NetBSD: Makefile.boot,v 1.19 2009/01/24 11:59:39 dsl Exp $
+# $NetBSD: Makefile.boot,v 1.20 2011/03/26 21:42:12 dholland Exp $
#
# a very simple makefile...
#
#
# modify MACHINE and MACHINE_ARCH as appropriate for your target architecture
#
-#CC=gcc -O -g
-CC=cc
-CFLAGS=-g -Wall -DHAVE_SETENV -DHAVE_STRERROR -DHAVE_STRDUP -DHAVE_STRFTIME -DHAVE_VSNPRINTF -DUSE_SELECT -D_POSIX_SOURCE
+CC=gcc -O -g
.c.o:
${CC} ${CFLAGS} -c $< -o $@
# tested on HP-UX 10.20
#MAKE_MACHINE=hp700
#MAKE_MACHINE_ARCH=hppa
-CFLAGS+= -DTARGET_MACHINE=\"${MACHINE}\" \
+CFLAGS= -DTARGET_MACHINE=\"${MACHINE}\" \
-DTARGET_MACHINE_ARCH=\"${MACHINE_ARCH}\" \
-DMAKE_MACHINE=\"${MACHINE}\"
LIBS=
-.\" $NetBSD: tutorial.ms,v 1.10 2004/06/27 19:12:33 uwe Exp $
+.\" $NetBSD: tutorial.ms,v 1.11 2011/08/18 15:19:30 sjg Exp $
.\" Copyright (c) 1988, 1989, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.nr g4 \\n(.s
.sp -1
.\" .st cf
-\D's -1u'\D't 5u'
+\D't 5u'
.sp -1
-\h'50u'\D'l 71u 0u'\D'l 50u 50u'\D'l 0u 71u'\D'l -50u 50u'\D'l -71u 0u'\D'l -50u -50u'\D'l 0u -71u'\D'l 50u -50u'
+\h'50u'
.sp -1
\D't 3u'
.sp -1
.sp 7u
-\h'53u'\D'p 14 68u 0u 46u 46u 0u 68u -46u 46u -68u 0u -47u -46u 0u -68u 47u -46u'
+\h'53u'
+\d\D'p -0.19i 0.0i 0.0i -0.13i 0.30i 0.0i 0.0i 0.13i'
.sp -1
.ft R
.ps 6
\h'85u'\v'0.85n'\h\a-\w\a\\*(g9\au/2u\a\&\\*(g9
.sp |\\n(g8u
.sp 166u
-\D't 3u'\D's -1u'
+\D't 3u'
.br
.po
.rt
-/* $NetBSD: arch.c,v 1.59 2009/01/23 21:58:27 dsl Exp $ */
+/* $NetBSD: arch.c,v 1.62 2010/11/27 16:00:09 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: arch.c,v 1.59 2009/01/23 21:58:27 dsl Exp $";
+static char rcsid[] = "$NetBSD: arch.c,v 1.62 2010/11/27 16:00:09 christos Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94";
#else
-__RCSID("$NetBSD: arch.c,v 1.59 2009/01/23 21:58:27 dsl Exp $");
+__RCSID("$NetBSD: arch.c,v 1.62 2010/11/27 16:00:09 christos Exp $");
#endif
#endif /* not lint */
#endif
* A library will be considered out-of-date for any of these reasons,
* given that it is a target on a dependency line somewhere:
* Its modification time is less than that of one of its
- * sources (gn->mtime < gn->cmtime).
+ * sources (gn->mtime < gn->cmgn->mtime).
* Its modification time is greater than the time at which the
* make began (i.e. it's been modified in the course
* of the make, probably by archiving).
oodate = TRUE;
} else if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) {
oodate = FALSE;
- } else if ((!Lst_IsEmpty(gn->children) && gn->cmtime == 0) ||
- (gn->mtime > now) || (gn->mtime < gn->cmtime)) {
+ } else if ((!Lst_IsEmpty(gn->children) && gn->cmgn == NULL) ||
+ (gn->mtime > now) ||
+ (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime)) {
oodate = TRUE;
} else {
#ifdef RANLIBMAG
if (DEBUG(ARCH) || DEBUG(MAKE)) {
fprintf(debug_file, "%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC));
}
- oodate = (gn->cmtime > modTimeTOC);
+ oodate = (gn->cmgn == NULL || gn->cmgn->mtime > modTimeTOC);
} else {
/*
* A library w/o a table of contents is out-of-date
-/* $NetBSD: compat.c,v 1.79 2010/06/03 15:40:15 sjg Exp $ */
+/* $NetBSD: compat.c,v 1.84 2011/09/16 15:38:03 joerg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: compat.c,v 1.79 2010/06/03 15:40:15 sjg Exp $";
+static char rcsid[] = "$NetBSD: compat.c,v 1.84 2011/09/16 15:38:03 joerg 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.79 2010/06/03 15:40:15 sjg Exp $");
+__RCSID("$NetBSD: compat.c,v 1.84 2011/09/16 15:38:03 joerg Exp $");
#endif
#endif /* not lint */
#endif
static GNode *curTarg = NULL;
static GNode *ENDNode;
-static void CompatInterrupt(int);
+static void CompatInterrupt(int) __dead;
static void
Compat_Init(void)
useShell = 1;
goto again;
}
- av = (const char **)mav;
+ av = (void *)mav;
}
local = TRUE;
+#ifdef USE_META
+ if (useMeta) {
+ meta_compat_start();
+ }
+#endif
+
/*
* Fork and execute the single command. If the fork fails, we abort.
*/
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
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...
*/
while (1) {
while ((retstat = wait(&reason)) != cpid) {
+ if (retstat > 0)
+ JobReapChild(retstat, reason, FALSE); /* not ours? */
if (retstat == -1 && errno != EINTR) {
break;
}
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: ",
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) {
/*
}
/*
- * All the children were made ok. Now cmtime contains the modification
- * time of the newest child, we need to find out if we exist and when
- * we were modified last. The criteria for datedness are defined by the
- * Make_OODate function.
+ * All the children were made ok. Now cmgn->mtime contains the
+ * modification time of the newest child, we need to find out if we
+ * exist and when we were modified last. The criteria for datedness
+ * are defined by the Make_OODate function.
*/
if (DEBUG(MAKE)) {
fprintf(debug_file, "Examining %s...", gn->name);
*/
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 {
} else {
gn->made = ERROR;
}
+#ifdef USE_META
+ if (useMeta && !NoExecute(gn)) {
+ meta_job_finish(NULL);
+ }
+#endif
if (gn->made != ERROR) {
/*
-/* $NetBSD: cond.c,v 1.60 2009/11/06 19:44:06 dsl Exp $ */
+/* $NetBSD: cond.c,v 1.62 2011/03/29 17:19:22 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: cond.c,v 1.60 2009/11/06 19:44:06 dsl Exp $";
+static char rcsid[] = "$NetBSD: cond.c,v 1.62 2011/03/29 17:19:22 sjg Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94";
#else
-__RCSID("$NetBSD: cond.c,v 1.60 2009/11/06 19:44:06 dsl Exp $");
+__RCSID("$NetBSD: cond.c,v 1.62 2011/03/29 17:19:22 sjg Exp $");
#endif
#endif /* not lint */
#endif
*-----------------------------------------------------------------------
*/
static Boolean
-CondDoDefined(int argLen, const char *arg)
+CondDoDefined(int argLen __unused, const char *arg)
{
char *p1;
Boolean result;
*-----------------------------------------------------------------------
*/
static Boolean
-CondDoMake(int argLen, const char *arg)
+CondDoMake(int argLen __unused, const char *arg)
{
return Lst_Find(create, arg, CondStrMatch) != NULL;
}
*-----------------------------------------------------------------------
*/
static Boolean
-CondDoExists(int argLen, const char *arg)
+CondDoExists(int argLen __unused, const char *arg)
{
Boolean result;
char *path;
path = Dir_FindFile(arg, dirSearchPath);
+ if (DEBUG(COND)) {
+ fprintf(debug_file, "exists(%s) result is \"%s\"\n",
+ arg, path ? path : "");
+ }
if (path != NULL) {
result = TRUE;
free(path);
} else {
result = FALSE;
}
- if (DEBUG(COND)) {
- fprintf(debug_file, "exists(%s) result is \"%s\"\n",
- arg, path ? path : "");
- }
return (result);
}
\f
*-----------------------------------------------------------------------
*/
static Boolean
-CondDoTarget(int argLen, const char *arg)
+CondDoTarget(int argLen __unused, const char *arg)
{
GNode *gn;
*-----------------------------------------------------------------------
*/
static Boolean
-CondDoCommands(int argLen, const char *arg)
+CondDoCommands(int argLen __unused, const char *arg)
{
GNode *gn;
}
static int
-get_mpt_arg(char **linePtr, char **argPtr, const char *func)
+get_mpt_arg(char **linePtr, char **argPtr, const char *func __unused)
{
/*
* Use Var_Parse to parse the spec in parens and return
}
static Boolean
-CondDoEmpty(int arglen, const char *arg)
+CondDoEmpty(int arglen, const char *arg __unused)
{
return arglen == 1;
}
-/* $NetBSD: config.h,v 1.20 2007/10/14 20:22:53 apb Exp $ */
+/* $NetBSD: config.h,v 1.21 2012/03/31 00:12:24 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
#define SYSVINCLUDE
#define SYSVVARSUB
+/*
+ * GMAKEEXPORT
+ * Recognize gmake like variable export directives [export <VAR>=<VALUE>]
+ */
+#define GMAKEEXPORT
+
/*
* SUNSHCMD
* Recognize SunOS and Solaris:
-/* $NetBSD: dir.c,v 1.61 2009/01/24 10:59:09 dsl Exp $ */
+/* $NetBSD: dir.c,v 1.64 2012/04/07 18:29:08 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: dir.c,v 1.61 2009/01/24 10:59:09 dsl Exp $";
+static char rcsid[] = "$NetBSD: dir.c,v 1.64 2012/04/07 18:29:08 christos Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94";
#else
-__RCSID("$NetBSD: dir.c,v 1.61 2009/01/24 10:59:09 dsl Exp $");
+__RCSID("$NetBSD: dir.c,v 1.64 2012/04/07 18:29:08 christos Exp $");
#endif
#endif /* not lint */
#endif
Boolean hasSlash; /* true if 'name' contains a / */
struct stat stb; /* Buffer for stat, if necessary */
Hash_Entry *entry; /* Entry for mtimes table */
+ const char *trailing_dot = ".";
/*
* Find the final component of the name and note whether it has a
return NULL;
}
+ if (*cp == '\0') {
+ /* we were given a trailing "/" */
+ cp = trailing_dot;
+ }
+
if (name[0] != '/') {
Boolean checkedDot = FALSE;
* b/c we added it here. This is not good...
*/
#ifdef notdef
+ if (cp == traling_dot) {
+ cp = strrchr(name, '/');
+ cp += 1;
+ }
cp[-1] = '\0';
(void)Dir_AddDir(path, name);
cp[-1] = '/';
*-----------------------------------------------------------------------
*/
int
-Dir_MTime(GNode *gn)
+Dir_MTime(GNode *gn, Boolean recheck)
{
char *fullName; /* the full pathname of name */
struct stat stb; /* buffer for finding the mod time */
fullName = NULL;
else {
fullName = Dir_FindFile(gn->name, Suff_FindPath(gn));
+ if (fullName == NULL && gn->flags & FROM_DEPEND &&
+ !Lst_IsEmpty(gn->iParents)) {
+ char *cp;
+
+ cp = strrchr(gn->name, '/');
+ if (cp) {
+ /*
+ * This is an implied source, and it may have moved,
+ * see if we can find it via the current .PATH
+ */
+ cp++;
+
+ fullName = Dir_FindFile(cp, Suff_FindPath(gn));
+ if (fullName) {
+ /*
+ * Put the found file in gn->path
+ * so that we give that to the compiler.
+ */
+ gn->path = bmake_strdup(fullName);
+ fprintf(stdout,
+ "%s: ignoring stale %s for %s, found %s\n",
+ progname, makeDependfile, gn->name, fullName);
+ }
+ }
+ }
if (DEBUG(DIR))
fprintf(debug_file, "Found '%s' as '%s'\n",
gn->name, fullName ? fullName : "(not found)" );
fullName = bmake_strdup(gn->name);
}
- entry = Hash_FindEntry(&mtimes, fullName);
+ if (!recheck)
+ entry = Hash_FindEntry(&mtimes, fullName);
+ else
+ entry = NULL;
if (entry != NULL) {
- /*
- * Only do this once -- the second time folks are checking to
- * see if the file was actually updated, so we need to actually go
- * to the file system.
- */
if (DEBUG(DIR)) {
fprintf(debug_file, "Using cached time %s for %s\n",
Targ_FmtTime(Hash_GetTimeValue(entry)), fullName);
}
stb.st_mtime = Hash_GetTimeValue(entry);
- Hash_DeleteEntry(&mtimes, entry);
} else if (stat(fullName, &stb) < 0) {
if (gn->type & OP_MEMBER) {
if (fullName != gn->path)
} else {
stb.st_mtime = 0;
}
- } else if (stb.st_mtime == 0) {
- /*
- * 0 handled specially by the code, if the time is really 0, return
- * something else instead
- */
- stb.st_mtime = 1;
+ } else {
+ if (stb.st_mtime == 0) {
+ /*
+ * 0 handled specially by the code, if the time is really 0,
+ * return something else instead
+ */
+ stb.st_mtime = 1;
+ }
+ entry = Hash_CreateEntry(&mtimes, fullName, NULL);
+ Hash_SetTimeValue(entry, stb.st_mtime);
}
if (fullName && gn->path == NULL) {
-/* $NetBSD: dir.h,v 1.14 2009/01/23 21:26:30 dsl Exp $ */
+/* $NetBSD: dir.h,v 1.15 2012/04/07 18:29:08 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
void Dir_Expand(const char *, Lst, Lst);
char *Dir_FindFile(const char *, Lst);
int Dir_FindHereOrAbove(char *, char *, char *, int);
-int Dir_MTime(GNode *);
+int Dir_MTime(GNode *, Boolean);
Path *Dir_AddDir(Lst, const char *);
char *Dir_MakeFlags(const char *, Lst);
void Dir_ClearPath(Lst);
-/* $NetBSD: for.c,v 1.47 2010/02/06 20:37:13 dholland Exp $ */
+/* $NetBSD: for.c,v 1.48 2010/12/25 04:57:07 dholland Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: for.c,v 1.47 2010/02/06 20:37:13 dholland Exp $";
+static char rcsid[] = "$NetBSD: for.c,v 1.48 2010/12/25 04:57:07 dholland Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93";
#else
-__RCSID("$NetBSD: for.c,v 1.47 2010/02/06 20:37:13 dholland Exp $");
+__RCSID("$NetBSD: for.c,v 1.48 2010/12/25 04:57:07 dholland Exp $");
#endif
#endif /* not lint */
#endif
}
static char *
-For_Iterate(void *v_arg)
+For_Iterate(void *v_arg, size_t *ret_len)
{
For *arg = v_arg;
int i, len;
arg->sub_next += strlist_num(&arg->vars);
arg->parse_buf = cp;
+ *ret_len = strlen(cp);
return cp;
}
-/* $NetBSD: job.c,v 1.151 2010/06/17 03:36:05 sjg Exp $ */
+/* $NetBSD: job.c,v 1.161 2012/04/07 18:29:08 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: job.c,v 1.151 2010/06/17 03:36:05 sjg Exp $";
+static char rcsid[] = "$NetBSD: job.c,v 1.161 2012/04/07 18:29:08 christos 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.151 2010/06/17 03:36:05 sjg Exp $");
+__RCSID("$NetBSD: job.c,v 1.161 2012/04/07 18:29:08 christos Exp $");
#endif
#endif /* not lint */
#endif
STATIC GNode *lastNode; /* The node for which output was most recently
* produced. */
-STATIC const char *targFmt; /* Format string to use to head output from a
- * job when it's not the most-recent job heard
- * from */
-static char *targPrefix = NULL; /* What we print at the start of targFmt */
+static char *targPrefix = NULL; /* What we print at the start of TARG_FMT */
static Job tokenWaitJob; /* token wait pseudo-job */
static Job childExitJob; /* child exit pseudo-job */
#define TARG_FMT "%s %s ---\n" /* Default format */
#define MESSAGE(fp, gn) \
- (void)fprintf(fp, targFmt, targPrefix, gn->name)
+ if (maxJobs != 1) \
+ (void)fprintf(fp, TARG_FMT, targPrefix, gn->name)
static sigset_t caught_signals; /* Set of signals we handle */
-#if defined(SYSV) || defined(__minix)
+#if defined(SYSV)
#define KILLPG(pid, sig) kill(-(pid), (sig))
#else
#define KILLPG(pid, sig) killpg((pid), (sig))
static void JobChildSig(int);
static void JobContinueSig(int);
-static Job *JobFindPid(int, int);
+static Job *JobFindPid(int, int, Boolean);
static int JobPrintCommand(void *, void *);
static int JobSaveCommand(void *, void *);
static void JobClose(Job *);
static char *JobOutput(Job *, char *, char *, int);
static void JobDoOutput(Job *, Boolean);
static Shell *JobMatchShell(const char *);
-static void JobInterrupt(int, int);
+static void JobInterrupt(int, int) __dead;
static void JobRestartJobs(void);
static void JobTokenAdd(void);
static void JobSigLock(sigset_t *);
*
*-----------------------------------------------------------------------
*/
-static void
+__dead static void
JobPassSig_int(int signo)
{
/* Run .INTERRUPT target then exit */
JobInterrupt(TRUE, signo);
}
-static void
+__dead static void
JobPassSig_term(int signo)
{
/* Dont run .INTERRUPT target then exit */
*-----------------------------------------------------------------------
*/
static Job *
-JobFindPid(int pid, int status)
+JobFindPid(int pid, int status, Boolean isJobs)
{
Job *job;
if ((job->job_state == status) && job->pid == pid)
return job;
}
- if (DEBUG(JOB))
+ if (DEBUG(JOB) && isJobs)
job_table_dump("no pid");
return NULL;
}
shutUp = DEBUG(LOUD) ? FALSE : TRUE;
break;
case '-':
+ job->flags |= JOB_IGNERR;
errOff = TRUE;
break;
case '+':
}
if (errOff) {
- if ( !(job->flags & JOB_IGNERR) && !noSpecials) {
+ if (!noSpecials) {
if (commandShell->hasErrCtl) {
/*
* we don't want the error-control commands showing
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),
- (job->flags & JOB_IGNERR) ? "(ignored)" : "");
+ (job->flags & JOB_IGNERR) ? " (ignored)" : "");
if (job->flags & JOB_IGNERR) {
status = 0;
} else {
(void)fflush(stdout);
}
+#ifdef USE_META
+ if (useMeta) {
+ meta_job_finish(job);
+ }
+#endif
+
return_job_token = FALSE;
Trace_Log(JOBEND, job);
int streamID; /* ID of stream opened to do the touch */
struct utimbuf times; /* Times for utime() call */
- if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL|OP_PHONY)) {
+ if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL|
+ OP_SPECIAL|OP_PHONY)) {
/*
* .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets
* and, as such, shouldn't really be created.
Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn, 0);
if (p1)
free(p1);
- } else if (Dir_MTime(gn) == 0 && (gn->type & OP_SPECIAL) == 0) {
+ } else if (Dir_MTime(gn, 0) == 0 && (gn->type & OP_SPECIAL) == 0) {
/*
* The node wasn't the target of an operator we have no .DEFAULT
* rule to go on and the target doesn't already exist. There's
}
if (gn->type & OP_OPTIONAL) {
- (void)fprintf(stdout, "%s%s %s(ignored)\n", progname,
+ (void)fprintf(stdout, "%s%s %s (ignored)\n", progname,
msg, gn->name);
(void)fflush(stdout);
} else if (keepgoing) {
- (void)fprintf(stdout, "%s%s %s(continuing)\n", progname,
+ (void)fprintf(stdout, "%s%s %s (continuing)\n", progname,
msg, gn->name);
(void)fflush(stdout);
return FALSE;
/* 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).
* also dead...
*/
if (!cmdsOK) {
+ PrintOnError(gn, NULL); /* provide some clue */
DieHorribly();
}
*/
noExec = FALSE;
+#ifdef USE_META
+ if (useMeta) {
+ meta_job_start(job, gn);
+ if (Targ_Silent(gn)) { /* might have changed */
+ job->flags |= JOB_SILENT;
+ }
+ }
+#endif
/*
* We can do all the commands at once. hooray for sanity
*/
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);
}
Job_CatchChildren(void)
{
int pid; /* pid of dead child */
- Job *job; /* job descriptor for dead child */
int status; /* Exit/termination status */
/*
(void)fprintf(debug_file, "Process %d exited/stopped status %x.\n", pid,
status);
}
+ JobReapChild(pid, status, TRUE);
+ }
+}
+
+/*
+ * It is possible that wait[pid]() was called from elsewhere,
+ * this lets us reap jobs regardless.
+ */
+void
+JobReapChild(pid_t pid, int status, Boolean isJobs)
+{
+ Job *job; /* job descriptor for dead child */
+
+ /*
+ * Don't even bother if we know there's no one around.
+ */
+ if (jobTokensRunning == 0)
+ return;
- job = JobFindPid(pid, JOB_ST_RUNNING);
- if (job == NULL) {
+ job = JobFindPid(pid, JOB_ST_RUNNING, isJobs);
+ if (job == NULL) {
+ if (isJobs) {
if (!lurking_children)
Error("Child (%d) status %x not in table?", pid, status);
- continue;
}
- if (WIFSTOPPED(status)) {
- if (DEBUG(JOB)) {
- (void)fprintf(debug_file, "Process %d (%s) stopped.\n",
- job->pid, job->node->name);
- }
- if (!make_suspended) {
- switch (WSTOPSIG(status)) {
- case SIGTSTP:
- (void)printf("*** [%s] Suspended\n", job->node->name);
- break;
- case SIGSTOP:
- (void)printf("*** [%s] Stopped\n", job->node->name);
- break;
- default:
- (void)printf("*** [%s] Stopped -- signal %d\n",
- job->node->name, WSTOPSIG(status));
- }
- job->job_suspended = 1;
+ return; /* not ours */
+ }
+ if (WIFSTOPPED(status)) {
+ if (DEBUG(JOB)) {
+ (void)fprintf(debug_file, "Process %d (%s) stopped.\n",
+ job->pid, job->node->name);
+ }
+ if (!make_suspended) {
+ switch (WSTOPSIG(status)) {
+ case SIGTSTP:
+ (void)printf("*** [%s] Suspended\n", job->node->name);
+ break;
+ case SIGSTOP:
+ (void)printf("*** [%s] Stopped\n", job->node->name);
+ break;
+ default:
+ (void)printf("*** [%s] Stopped -- signal %d\n",
+ job->node->name, WSTOPSIG(status));
}
- (void)fflush(stdout);
- continue;
+ job->job_suspended = 1;
}
+ (void)fflush(stdout);
+ return;
+ }
- job->job_state = JOB_ST_FINISHED;
- job->exit_status = status;
+ job->job_state = JOB_ST_FINISHED;
+ job->exit_status = status;
- JobFinish(job, status);
- }
+ JobFinish(job, status);
}
/*-
lastNode = NULL;
- if (maxJobs == 1) {
- /*
- * If only one job can run at a time, there's no need for a banner,
- * is there?
- */
- targFmt = "";
- } else {
- targFmt = TARG_FMT;
- }
-
/*
* There is a non-zero chance that we already have children.
* eg after 'make -f- <<EOF'
-/* $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.
*/
struct pollfd;
+
+#ifdef USE_META
+# include "meta.h"
+#endif
+
#define JOB_BUFSIZE 1024
typedef struct Job {
int pid; /* The child's process ID */
/* 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]
-/* $NetBSD: main.c,v 1.188 2010/06/03 15:40:16 sjg Exp $ */
+/* $NetBSD: main.c,v 1.198 2011/09/16 15:38:04 joerg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: main.c,v 1.188 2010/06/03 15:40:16 sjg Exp $";
+static char rcsid[] = "$NetBSD: main.c,v 1.198 2011/09/16 15:38:04 joerg Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: main.c,v 1.188 2010/06/03 15:40:16 sjg Exp $");
+__RCSID("$NetBSD: main.c,v 1.198 2011/09/16 15:38:04 joerg Exp $");
#endif
#endif /* not lint */
#endif
static char * Check_Cwd_av(int, char **, int);
static void MainParseArgs(int, char **);
static int ReadMakefile(const void *, const void *);
-static void usage(void);
+static void usage(void) __dead;
static Boolean ignorePWD; /* if we use -C, PWD is meaningless */
-static char curdir[MAXPATHLEN + 1]; /* startup directory */
static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */
+char curdir[MAXPATHLEN + 1]; /* Startup directory */
char *progname; /* the program name */
char *makeDependfile;
pid_t myPid;
case 'l':
debug |= DEBUG_LOUD;
break;
+ case 'M':
+ debug |= DEBUG_META;
+ break;
case 'm':
debug |= DEBUG_MAKE;
break;
return (ReadMakefile(p, q) == 0);
}
-static int
+int
str2Lst_Append(Lst lp, char *str, const char *sep)
{
char *cp;
#ifdef SIGINFO
/*ARGSUSED*/
static void
-siginfo(int signo)
+siginfo(int signo __unused)
{
char dir[MAXPATHLEN];
char str[2 * MAXPATHLEN];
compatMake = TRUE;
forceJobs = FALSE;
}
+#if USE_META
+ if (strstr(mode, "meta"))
+ meta_init(mode);
+#endif
}
if (mp)
free(mp);
struct stat sb, sa;
char *p1, *path, *pwd;
char mdpath[MAXPATHLEN];
- char *machine = getenv("MACHINE");
+ const char *machine = getenv("MACHINE");
const char *machine_arch = getenv("MACHINE_ARCH");
char *syspath = getenv("MAKESYSPATH");
Lst sysMkPath; /* Path of sys.mk */
* We take care of PWD for the automounter below...
*/
if (getcwd(curdir, MAXPATHLEN) == NULL) {
- (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno));
+ (void)fprintf(stderr, "%s: getcwd: %s.\n",
+ progname, strerror(errno));
exit(2);
}
char *name, *path = bmake_malloc(len);
if (!strcmp(fname, "-")) {
- Parse_File("(stdin)", dup(fileno(stdin)));
+ Parse_File(NULL /*stdin*/, -1);
Var_Set("MAKEFILE", "", VAR_GLOBAL, 0);
} else {
/* if we've chdir'd, rebuild the path name */
/*
* Wait for the process to exit.
*/
- while(((pid = waitpid(cpid, &status, 0)) != cpid) && (pid >= 0))
+ while(((pid = waitpid(cpid, &status, 0)) != cpid) && (pid >= 0)) {
+ JobReapChild(pid, status, FALSE);
continue;
-
+ }
cc = Buf_Size(&buf);
res = Buf_Destroy(&buf, FALSE);
}
}
-/*
- * Create and open a temp file using "pattern".
- * If "fnamep" is provided set it to a copy of the filename created.
- * Otherwise unlink the file once open.
- */
-int
-mkTempFile(const char *pattern, char **fnamep)
+char *
+getTmpdir(void)
{
static char *tmpdir = NULL;
- char tfile[MAXPATHLEN];
- int fd;
-
- if (!pattern)
- pattern = TMPPAT;
if (!tmpdir) {
struct stat st;
tmpdir = bmake_strdup(_PATH_TMP);
}
}
+ return tmpdir;
+}
+
+/*
+ * Create and open a temp file using "pattern".
+ * If "fnamep" is provided set it to a copy of the filename created.
+ * Otherwise unlink the file once open.
+ */
+int
+mkTempFile(const char *pattern, char **fnamep)
+{
+ static char *tmpdir = NULL;
+ char tfile[MAXPATHLEN];
+ int fd;
+
+ if (!pattern)
+ pattern = TMPPAT;
+ if (!tmpdir)
+ tmpdir = getTmpdir();
if (pattern[0] == '/') {
snprintf(tfile, sizeof(tfile), "%s", pattern);
} else {
-.\" $NetBSD: make.1,v 1.176 2010/06/10 18:35:22 wiz Exp $
+.\" $NetBSD: make.1,v 1.202 2012/04/08 22:00:39 wiz Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd June 9, 2010
+.Dd March 31, 2012
.Dt MAKE 1
.Os
.Sh NAME
.Sh SYNOPSIS
.Nm
.Op Fl BeikNnqrstWX
-.Bk -words
.Op Fl C Ar directory
-.Ek
-.Bk -words
.Op Fl D Ar variable
-.Ek
-.Bk -words
.Op Fl d Ar flags
-.Ek
-.Bk -words
.Op Fl f Ar makefile
-.Ek
-.Bk -words
.Op Fl I Ar directory
-.Ek
-.Bk -words
.Op Fl J Ar private
-.Ek
-.Bk -words
.Op Fl j Ar max_jobs
-.Ek
-.Bk -words
.Op Fl m Ar directory
-.Ek
-.Bk -words
.Op Fl T Ar file
-.Ek
-.Bk -words
.Op Fl V Ar variable
-.Ek
.Op Ar variable=value
-.Bk -words
.Op Ar target ...
-.Ek
.Sh DESCRIPTION
.Nm
is a program designed to simplify the maintenance of other programs.
For a more thorough description of
.Nm
and makefiles, please refer to
-.%T "Make \- A Tutorial" .
+.%T "PMake \- A Tutorial" .
.Pp
.Nm
will prepend the contents of the
.Nm
are to print debugging information.
Unless the flags are preceded by
-.Ql -
+.Ql \-
they are added to the
.Va MAKEFLAGS
environment variable and will be processed by any child make processes.
.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.
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.
known as
.Ql Va \&? .
.It Va .PREFIX
-The file prefix of the file, containing only the file portion, no suffix
+The file prefix of the target, containing only the file portion, no suffix
or preceding directory components; also known as
.Ql Va * .
.It Va .TARGET
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 curdirOk= Ar bf
+Normally
+.Nm
+will not create .meta files in
+.Ql Va .CURDIR .
+This can be overridden by setting
+.Va bf
+to a value which represents True.
+.It Pa env
+For debugging, it can be useful to inlcude the environment
+in the .meta file.
+.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 .
+.It Pa silent= Ar bf
+If
+.Va bf
+is True, when a .meta file is created, mark the target
+.Ic .SILENT .
+.El
+.It Va .MAKE.META.BAILIWICK
+In "meta" mode, provides a list of prefixes which
+match the directories controlled by
+.Nm .
+If a file that was generated outside of
+.Va .OBJDIR
+but within said bailiwick is missing,
+the current target is considered out-of-date.
+.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
are both set only while the
.Ql Pa Makefiles
are being parsed.
+If you want to retain their current values, assign them to a variable
+using assignment with expansion:
+.Pq Ql Cm \&:= .
.It Va .PATH
A variable that represents the list of directories that
.Nm
for all programs which
.Nm
executes.
+.It Ev .TARGETS
+The list of targets explicitly specified on the command line, if any.
.It Ev VPATH
Colon-separated
.Pq Dq \&:
.Nm .
.It Cm \&:R
Replaces each word in the variable with everything but its suffix.
+.It Cm \&:gmtime
+The value is a format string for
+.Xr strftime 3 ,
+using the current
+.Xr gmtime 3 .
+.It Cm \&:hash
+Compute a 32bit hash of the value and encode it as hex digits.
+.It Cm \&:localtime
+The value is a format string for
+.Xr strftime 3 ,
+using the current
+.Xr localtime 3 .
.It Cm \&:tA
Attempt to convert variable to an absolute path using
.Xr realpath 3 ,
.Dl ${NUMBERS:M42:?match:no}
which actually tests defined(NUMBERS),
to determine is any words match "42" you need to use something like:
-.Dl ${${NUMBERS:M42} != "":?match:no} .
+.Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} .
.It Ar :old_string=new_string
This is the
.At V
should start and end with a period.
For example.
.Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@}
+.Pp
+However a single character varaiable is often more readable:
+.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}
.It Cm \&:U Ns Ar newval
If the variable is undefined
.Ar newval
is the value.
If no such node exists or its path is null, then the
name of the variable is used.
+In order for this modifier to work, the name (node) must at least have
+appeared on the rhs of a dependency.
.Sm off
.It Cm \&:\&! Ar cmd Cm \&!
.Sm on
modifier, the words are indexed both forwards using positive integers
(where index 1 represents the first word),
and backwards using negative integers
-(where index -1 represents the last word).
+(where index \-1 represents the last word).
.Pp
The
.Ar range
This is not affected by the
.Fl X
flag, so should be used with caution.
+For compatibility with other
+.Nm
+programs
+.Ql export variable=value
+is also accepted.
.Pp
Appending a variable name to
.Va .MAKE.EXPORTED
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.
+In "meta" mode, the target is out-of-date if the meta file is missing.
+.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 .
and will not be created with the
.Fl t
option.
+Suffix-transformation rules are not applied to
+.Ic .PHONY
+targets.
.It Ic .PRECIOUS
When
.Nm
is built by another part of the dependency graph,
the following is a dependency loop:
.Bd -literal
-\&.ORDER: a b
+\&.ORDER: b a
b: a
.Ed
.Pp
Example:
.Bd -literal
\&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \e
- check="set -e" ignore="set +e" \e
- echo="set -v" quiet="set +v" filter="set +v" \e
+ check="set \-e" ignore="set +e" \e
+ echo="set \-v" quiet="set +v" filter="set +v" \e
echoFlag=v errFlag=e newline="'\en'"
.Ed
.It Ic .SILENT
.Bd -literal
\&.SUFFIXES: .o
\&.c.o:
- cc -o ${.TARGET} -c ${.IMPSRC}
+ cc \-o ${.TARGET} \-c ${.IMPSRC}
.Ed
.El
.Sh ENVIRONMENT
.Pp
The way that parallel makes are scheduled changed in
.Nx 4.0
-so that .ORDER and .WAIT apply recursively to the dependant nodes.
+so that .ORDER and .WAIT apply recursively to the dependent nodes.
The algorithms used may change again in the future.
.Pp
The way that .for loop variables are substituted changed after
so that they still appear to be variable expansions.
In particular this stops them being treated as syntax, and removes some
obscure problems using them in .if statements.
+.Pp
+Unlike other
+.Nm
+programs, this implementation by default executes all commands for a given
+target using a single shell invocation.
+This is done for both efficiency and to simplify error handling in remote
+command invocations.
+Typically this is transparent to the user, unless the target commands change
+the current working directory using
+.Dq cd
+or
+.Dq chdir .
+To be compatible with Makefiles that do this, one can use
+.Fl B
+to disable this behavior.
.Sh SEE ALSO
.Xr mkdep 1
.Sh HISTORY
.Nm
command appeared in
.At v7 .
+This
+.Nm
+implementation is based on Adam De Boor's pmake program which was written
+for Sprint at Berkeley.
+It was designed to be a parallel distributed make running jobs on different
+machines using a daemon called
+.Dq customs .
.Sh BUGS
The
.Nm
-/* $NetBSD: make.c,v 1.79 2010/04/07 00:11:27 sjg Exp $ */
+/* $NetBSD: make.c,v 1.85 2012/04/07 18:29:08 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: make.c,v 1.79 2010/04/07 00:11:27 sjg Exp $";
+static char rcsid[] = "$NetBSD: make.c,v 1.85 2012/04/07 18:29:08 christos 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.79 2010/04/07 00:11:27 sjg Exp $");
+__RCSID("$NetBSD: make.c,v 1.85 2012/04/07 18:29:08 christos Exp $");
#endif
#endif /* not lint */
#endif
*
* Make_Update Update all parents of a given child. Performs
* various bookkeeping chores like the updating
- * of the cmtime field of the parent, filling
+ * of the cmgn field of the parent, filling
* of the IMPSRC context variable, etc. It will
* place the parent on the toBeMade queue if it
* should be.
*
- * Make_TimeStamp Function to set the parent's cmtime field
+ * Make_TimeStamp Function to set the parent's cmgn field
* based on a child's modification time.
*
* Make_DoAllVar Set up the various local variables for a
static int MakeBuildChild(void *, void *);
static int MakeBuildParent(void *, void *);
-static void
+__dead static void
make_abort(GNode *gn, int line)
{
static int two = 2;
/*-
*-----------------------------------------------------------------------
* Make_TimeStamp --
- * Set the cmtime field of a parent node based on the mtime stamp in its
+ * Set the cmgn field of a parent node based on the mtime stamp in its
* child. Called from MakeOODate via Lst_ForEach.
*
* Input:
* Always returns 0.
*
* Side Effects:
- * The cmtime of the parent node will be changed if the mtime
+ * The cmgn of the parent node will be changed if the mtime
* field of the child is greater than it.
*-----------------------------------------------------------------------
*/
int
Make_TimeStamp(GNode *pgn, GNode *cgn)
{
- if (cgn->mtime > pgn->cmtime) {
- pgn->cmtime = cgn->mtime;
+ if (pgn->cmgn == NULL || cgn->mtime > pgn->cmgn->mtime) {
+ pgn->cmgn = cgn;
}
return (0);
}
* TRUE if the node is out of date. FALSE otherwise.
*
* Side Effects:
- * The mtime field of the node and the cmtime field of its parents
+ * The mtime field of the node and the cmgn field of its parents
* will/may be changed.
*-----------------------------------------------------------------------
*/
* doesn't depend on their modification time...
*/
if ((gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC)) == 0) {
- (void)Dir_MTime(gn);
+ (void)Dir_MTime(gn, 0);
if (DEBUG(MAKE)) {
if (gn->mtime != 0) {
fprintf(debug_file, "modified %s...", Targ_FmtTime(gn->mtime));
* or non-existent.
*/
oodate = (gn->mtime == 0 || Arch_LibOODate(gn) ||
- (gn->cmtime == 0 && (gn->type & OP_DOUBLEDEP)));
+ (gn->cmgn == NULL && (gn->type & OP_DOUBLEDEP)));
} else if (gn->type & OP_JOIN) {
/*
* A target with the .JOIN attribute is only considered
}
}
oodate = TRUE;
- } else if (gn->mtime < gn->cmtime ||
- (gn->cmtime == 0 &&
+ } else if ((gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) ||
+ (gn->cmgn == NULL &&
((gn->mtime == 0 && !(gn->type & OP_OPTIONAL))
|| gn->type & OP_DOUBLEDEP)))
{
/*
* A node whose modification time is less than that of its
- * youngest child or that has no children (cmtime == 0) and
+ * youngest child or that has no children (cmgn == NULL) and
* either doesn't exist (mtime == 0) and it isn't optional
* or was the object of a * :: operator is out-of-date.
* Why? Because that's the way Make does it.
*/
if (DEBUG(MAKE)) {
- if (gn->mtime < gn->cmtime) {
- fprintf(debug_file, "modified before source...");
+ if (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) {
+ fprintf(debug_file, "modified before source %s...",
+ gn->cmgn->path);
} else if (gn->mtime == 0) {
fprintf(debug_file, "non-existent and no sources...");
} else {
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
* Always returns 0
*
* Side Effects:
- * The path and mtime of the node and the cmtime of the parent are
+ * The path and mtime of the node and the cmgn of the parent are
* updated; the unmade children count of the parent is decremented.
*-----------------------------------------------------------------------
*/
GNode *gn = (GNode *)gnp;
GNode *pgn = (GNode *)pgnp;
- (void)Dir_MTime(gn);
+ (void)Dir_MTime(gn, 0);
Make_TimeStamp(pgn, gn);
pgn->unmade--;
time_t
Make_Recheck(GNode *gn)
{
- time_t mtime = Dir_MTime(gn);
+ time_t mtime = Dir_MTime(gn, 1);
#ifndef RECHECK
/*
* the toBeMade queue if this field becomes 0.
*
* If the child was made, the parent's flag CHILDMADE field will be
- * set true and its cmtime set to now.
+ * set true.
*
* If the child is not up-to-date and still does not exist,
* set the FORCE flag on the parents.
*
- * If the child wasn't made, the cmtime field of the parent will be
+ * If the child wasn't made, the cmgn field of the parent will be
* altered if the child's mtime is big enough.
*
* Finally, if the child is the implied source for the parent, the
*eon = ')';
}
- (void)Dir_MTime(gn);
+ (void)Dir_MTime(gn, 0);
Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0);
Lst_ForEach(gn->children, MakeUnmark, gn);
Lst_ForEach(gn->children, MakeHandleUse, gn);
-/* $NetBSD: make.h,v 1.82 2010/04/23 00:18:50 sjg Exp $ */
+/* $NetBSD: make.h,v 1.87 2011/09/16 15:38:04 joerg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
#endif
#endif
+#if !defined(__dead)
+#define __dead
+#endif
+
#include "sprite.h"
#include "lst.h"
#include "hash.h"
int unmade; /* The number of unmade children */
time_t mtime; /* Its modification time */
- time_t cmtime; /* The modification time of its youngest
- * child */
+ struct GNode *cmgn; /* The youngest child */
Lst iParents; /* Links to parents for which this is an
* implied source, if any */
#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 */
extern Lst sysIncPath; /* The system include path. */
extern Lst defIncPath; /* The default include path. */
+extern char curdir[]; /* Startup directory */
extern char *progname; /* The program name */
extern char *makeDependfile; /* .depend */
* We cannot vfork() in a child of vfork().
* Most systems do not enforce this but some do.
*/
-#if defined(__minix)
-#define vFork() fork()
-#else
#define vFork() ((getpid() == myPid) ? vfork() : fork())
-#endif
extern pid_t myPid;
#define MAKEFLAGS ".MAKEFLAGS"
#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
void Main_ExportMAKEFLAGS(Boolean);
Boolean Main_SetObjdir(const char *);
int mkTempFile(const char *, char **);
+int str2Lst_Append(Lst, char *, const char *);
#ifdef __GNUC__
#define UNCONST(ptr) ({ \
-/* $NetBSD: make_malloc.c,v 1.5 2009/01/24 23:19:50 dsl Exp $ */
+/* $NetBSD: make_malloc.c,v 1.6 2010/12/25 20:35:25 dholland Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
#ifdef MAKE_NATIVE
#include <sys/cdefs.h>
-__RCSID("$NetBSD: make_malloc.c,v 1.5 2009/01/24 23:19:50 dsl Exp $");
+__RCSID("$NetBSD: make_malloc.c,v 1.6 2010/12/25 20:35:25 dholland Exp $");
#endif
#include <stdio.h>
{
extern char *progname;
- (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno));
+ (void)fprintf(stderr, "%s: %s.\n", progname, strerror(ENOMEM));
exit(2);
}
--- /dev/null
+/* $NetBSD: meta.c,v 1.24 2011/09/21 14:30:47 christos Exp $ */
+
+/*
+ * 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.
+ */
+#if defined(USE_META)
+
+#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>
+#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H)
+#include <err.h>
+#endif
+
+#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 */
+static Lst metaBailiwick; /* our scope of control */
+
+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 */
+static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */
+static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */
+
+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
+
+#if !defined(HAVE_STRSEP)
+# define strsep(s, d) stresep((s), (d), 0)
+#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 */
+ (void)lseek(fd, (off_t)0, SEEK_SET);
+ 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
+
+/*
+ * 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);
+}
+
+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++]);
+
+ if (realpath(dname, cwd))
+ dname = cwd;
+
+ /*
+ * 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;
+ } 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;
+ }
+ }
+ /* 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 objdir[MAXPATHLEN];
+ char **ptr;
+ 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++]);
+ 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 we aren't in the object directory, don't create a meta file. */
+ if (!metaCurdirOk && strcmp(curdir, 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);
+
+#ifdef DEBUG_META_MODE
+ if (DEBUG(META))
+ fprintf(debug_file, "meta_create: %s\n", fname);
+#endif
+
+ 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);
+
+ gn->type |= OP_META; /* in case anyone wants to know */
+ if (metaSilent) {
+ gn->type |= OP_SILENT;
+ }
+ out:
+ for (i--; i >= 0; i--) {
+ if (p[i])
+ free(p[i]);
+ }
+
+ return (mf.fp);
+}
+
+static Boolean
+boolValue(char *s)
+{
+ switch(*s) {
+ case '0':
+ case 'N':
+ case 'n':
+ case 'F':
+ case 'f':
+ return FALSE;
+ }
+ return TRUE;
+}
+
+void
+meta_init(const char *make_mode)
+{
+ static int once = 0;
+ char *cp;
+
+ 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 ((cp = strstr(make_mode, "curdirok="))) {
+ metaCurdirOk = boolValue(&cp[9]);
+ }
+ if ((cp = strstr(make_mode, "silent="))) {
+ metaSilent = boolValue(&cp[7]);
+ }
+ 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));
+ /*
+ * We consider ourselves master of all within ${.MAKE.META.BAILIWICK}
+ */
+ metaBailiwick = Lst_Init(FALSE);
+ cp = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", VAR_GLOBAL, 0);
+ if (cp) {
+ str2Lst_Append(metaBailiwick, cp, NULL);
+ }
+}
+
+/*
+ * 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);
+ if (pbm && pbm->meta_fname[0]) {
+ Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0);
+ }
+ meta_job_finish(job);
+}
+
+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;
+ pbm->meta_fname[0] = '\0';
+ }
+}
+
+/*
+ * 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 %zu -> %zu\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;
+}
+
+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));
+}
+
+static int
+string_match(const void *p, const void *q)
+{
+ const char *p1 = p;
+ const char *p2 = q;
+
+ return strcmp(p1, p2);
+}
+
+
+/*
+ * 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.
+ * We have to track the latestdir on a per-process basis.
+ */
+#define LDIR_VNAME_FMT ".meta.%d.ldir"
+
+/*
+ * 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; \
+ }
+
+Boolean
+meta_oodate(GNode *gn, Boolean oodate)
+{
+ static char *tmpdir = NULL;
+ static char cwd[MAXPATHLEN];
+ char ldir_vname[64];
+ char latestdir[MAXPATHLEN];
+ char fname[MAXPATHLEN];
+ char fname1[MAXPATHLEN];
+ char fname2[MAXPATHLEN];
+ char *p;
+ char *cp;
+ static size_t cwdlen = 0;
+ static size_t tmplen = 0;
+ FILE *fp;
+ Boolean ignoreOODATE = FALSE;
+ Lst missingFiles;
+
+ if (oodate)
+ return oodate; /* we're done */
+
+ missingFiles = Lst_Init(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);
+
+ meta_name(gn, fname, sizeof(fname), NULL, NULL);
+
+#ifdef DEBUG_META_MODE
+ if (DEBUG(META))
+ fprintf(debug_file, "meta_oodate: %s\n", fname);
+#endif
+
+ if ((fp = fopen(fname, "r")) != NULL) {
+ static char *buf = NULL;
+ static size_t bufsz;
+ int lineno = 0;
+ int lastpid = 0;
+ int pid;
+ int f = 0;
+ int x;
+ LstNode ln;
+ struct stat fs;
+
+ if (!buf) {
+ bufsz = 8 * BUFSIZ;
+ buf = bmake_malloc(bufsz);
+ }
+
+ if (!cwdlen) {
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ err(1, "Could not get current working directory");
+ cwdlen = strlen(cwd);
+ }
+
+ if (!tmpdir) {
+ tmpdir = getTmpdir();
+ tmplen = strlen(tmpdir);
+ }
+
+ /* 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);
+ oodate = TRUE;
+ break;
+ }
+ /* 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;
+#ifdef DEBUG_META_MODE
+ if (DEBUG(META))
+ fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf);
+#endif
+ strsep(&p, " ");
+ if (f) {
+ /*
+ * 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.
+ */
+ CHECK_VALID_META(p);
+ pid = atoi(p);
+ if (pid > 0 && pid != lastpid) {
+ char *ldir;
+ char *tp;
+
+ if (lastpid > 0) {
+ /* We need to remember this. */
+ Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0);
+ }
+ 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));
+ if (tp)
+ free(tp);
+ } else
+ strlcpy(latestdir, cwd, sizeof(latestdir));
+ }
+ /* Skip past the pid. */
+ if (strsep(&p, " ") == NULL)
+ continue;
+#ifdef DEBUG_META_MODE
+ if (DEBUG(META))
+ fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, latestdir);
+#endif
+ break;
+ }
+
+ CHECK_VALID_META(p);
+
+ /* Process according to record type. */
+ switch (buf[0]) {
+ case 'X': /* eXit */
+ Var_Delete(ldir_vname, VAR_GLOBAL);
+ lastpid = 0; /* no need to save ldir_vname */
+ break;
+
+ case 'F': /* [v]Fork */
+ {
+ char cldir[64];
+ int child;
+
+ child = atoi(p);
+ if (child > 0) {
+ snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child);
+ Var_Set(cldir, latestdir, VAR_GLOBAL, 0);
+ }
+ }
+ break;
+
+ case 'C': /* Chdir */
+ /* Update the latest directory. */
+ strlcpy(latestdir, p, sizeof(latestdir));
+ break;
+
+ case 'M': /* renaMe */
+ if (Lst_IsEmpty(missingFiles))
+ break;
+ /* 'L' and 'M' put single quotes around the args */
+ if (*p == '\'') {
+ char *ep;
+
+ p++;
+ if ((ep = strchr(p, '\'')))
+ *ep = '\0';
+ }
+ /* FALLTHROUGH */
+ case 'D': /* unlink */
+ if (*p == '/' && !Lst_IsEmpty(missingFiles)) {
+ /* remove p from the missingFiles list if present */
+ if ((ln = Lst_Find(missingFiles, p, string_match)) != NULL) {
+ char *tp = Lst_Datum(ln);
+ Lst_Remove(missingFiles, ln);
+ free(tp);
+ }
+ }
+ break;
+ case 'L': /* Link */
+ /* we want the target */
+ if (strsep(&p, " ") == NULL)
+ continue;
+ CHECK_VALID_META(p);
+ /* 'L' and 'M' put single quotes around the args */
+ if (*p == '\'') {
+ char *ep;
+
+ p++;
+ if ((ep = strchr(p, '\'')))
+ *ep = '\0';
+ }
+ /* FALLTHROUGH */
+ case 'W': /* Write */
+ /*
+ * 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;
+
+ if (stat(p, &fs) < 0) {
+ Lst_AtEnd(missingFiles, bmake_strdup(p));
+ }
+ break;
+ case 'R': /* Read */
+ case 'E': /* Exec */
+ /*
+ * Check for runtime files that can't
+ * be part of the dependencies because
+ * they are _expected_ to change.
+ */
+ if (strncmp(p, "/tmp/", 5) == 0 ||
+ (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0))
+ break;
+
+ 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;
+
+ if ((cp = strrchr(p, '/'))) {
+ cp++;
+ /*
+ * We don't normally expect to see this,
+ * but we do expect it to change.
+ */
+ if (strcmp(cp, makeDependfile) == 0)
+ break;
+ }
+
+ /*
+ * The rest of the record is the file name.
+ * Check if it's not an absolute path.
+ */
+ {
+ 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;
+
+ if (strcmp(latestdir, cwd) != 0) {
+ /* Check vs cwd */
+ snprintf(fname2, sizeof(fname2), "%s/%s", cwd, p);
+ sdirs[sdx++] = fname2;
+ }
+ }
+ sdirs[sdx++] = NULL;
+
+ for (sdp = sdirs; *sdp && !found; sdp++) {
+#ifdef DEBUG_META_MODE
+ if (DEBUG(META))
+ fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp);
+#endif
+ if (stat(*sdp, &fs) == 0) {
+ found = 1;
+ p = *sdp;
+ }
+ }
+ if (found) {
+#ifdef DEBUG_META_MODE
+ if (DEBUG(META))
+ fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p);
+#endif
+ 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. */
+ realpath(p, latestdir);
+ }
+ } else if (errno == ENOENT && *p == '/' &&
+ strncmp(p, cwd, cwdlen) != 0) {
+ /*
+ * A referenced file outside of CWD is missing.
+ * We cannot catch every eventuality here...
+ */
+ if (DEBUG(META))
+ fprintf(debug_file, "%s: %d: file '%s' may have moved?...\n", fname, lineno, p);
+ oodate = TRUE;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ } else if (strcmp(buf, "CMD") == 0) {
+ /*
+ * 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);
+
+ 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) {
+ /*
+ * 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;
+ }
+ if (strcmp(p, cwd) != 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;
+ }
+ }
+ }
+
+ fclose(fp);
+ if (!Lst_IsEmpty(missingFiles)) {
+ if (DEBUG(META))
+ fprintf(debug_file, "%s: missing files: %s...\n",
+ fname, (char *)Lst_Datum(Lst_First(missingFiles)));
+ oodate = TRUE;
+ Lst_Destroy(missingFiles, (FreeProc *)free);
+ }
+ } else {
+ if ((gn->type & OP_META)) {
+ if (DEBUG(META))
+ fprintf(debug_file, "%s: required but missing\n", fname);
+ oodate = TRUE;
+ }
+ }
+ if (oodate && ignoreOODATE) {
+ /*
+ * Target uses .OODATE, so we need to re-compute it.
+ * We need to clean up what Make_DoAllVar() did.
+ */
+ Var_Delete(ALLSRC, gn);
+ Var_Delete(OODATE, gn);
+ gn->flags &= ~DONE_ALLSRC;
+ }
+ 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);
+}
+
+#endif /* USE_META */
--- /dev/null
+/* $NetBSD: meta.h,v 1.2 2011/03/30 22:03:49 sjg Exp $ */
+
+/*
+ * 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);
-/* $NetBSD: nonints.h,v 1.59 2010/06/03 15:40:16 sjg Exp $ */
+/* $NetBSD: nonints.h,v 1.63 2011/09/16 15:38:04 joerg Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
int For_Accum(char *);
void For_Run(int);
+/* job.c */
+void JobReapChild(pid_t, int, Boolean);
+
/* main.c */
void Main_ParseArgLine(const char *);
void MakeMode(const char *);
__attribute__((__format__(__printf__, 1, 2),__noreturn__));
void DieHorribly(void) __attribute__((__noreturn__));
int PrintAddr(void *, void *);
-void Finish(int);
+void Finish(int) __dead;
int eunlink(const char *);
void execError(const char *, const char *);
+char *getTmpdir(void);
/* parse.c */
void Parse_Error(int, const char *, ...)
void Parse_File(const char *, int);
void Parse_Init(void);
void Parse_End(void);
-void Parse_SetInput(const char *, int, int, char *(*)(void *), void *);
+void Parse_SetInput(const char *, int, int, char *(*)(void *, size_t *), void *);
Lst Parse_MainName(void);
/* str.c */
-/* $NetBSD: parse.c,v 1.164 2010/05/24 21:04:49 sjg Exp $ */
+/* $NetBSD: parse.c,v 1.182 2012/03/31 00:12:24 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
*/
#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.182 2012/03/31 00:12:24 christos 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.182 2012/03/31 00:12:24 christos Exp $");
#endif
#endif /* not lint */
#endif
* Parse_MainName Returns a Lst of the main target to create.
*/
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
+#ifndef MAP_FILE
+#define MAP_FILE 0
+#endif
+#ifndef MAP_COPY
+#define MAP_COPY MAP_PRIVATE
+#endif
+
#include "make.h"
#include "hash.h"
#include "dir.h"
#include "buf.h"
#include "pathnames.h"
+////////////////////////////////////////////////////////////
+// types and constants
+
/*
- * These values are returned by ParseEOF to tell Parse_File whether to
- * CONTINUE parsing, i.e. it had only reached the end of an include file,
- * or if it's DONE.
+ * Structure for a file being read ("included file")
*/
-#define CONTINUE 1
-#define DONE 0
-static Lst targets; /* targets we're working on */
-#ifdef CLEANUP
-static Lst targCmds; /* command lines for targets */
-#endif
-static Boolean inLine; /* true if currently in a dependency
- * line or its commands */
-static int fatals = 0;
-
-static GNode *mainNode; /* The main target to create. This is the
- * first target on the first dependency
- * line in the first makefile */
typedef struct IFile {
const char *fname; /* name of file */
int lineno; /* current line number in file */
int first_lineno; /* line number of start of text */
- int fd; /* the open file */
int cond_depth; /* 'if' nesting when file opened */
char *P_str; /* point to base of string buffer */
char *P_ptr; /* point to next char of string buffer */
char *P_end; /* point to the end of string buffer */
- int P_buflen; /* current size of file buffer */
- char *(*nextbuf)(void *); /* Function to get more data */
+ char *(*nextbuf)(void *, size_t *); /* Function to get more data */
void *nextbuf_arg; /* Opaque arg for nextbuf() */
+ struct loadedfile *lf; /* loadedfile object, if any */
} IFile;
-#define IFILE_BUFLEN 0x8000
-static IFile *curFile;
-
/*
- * Definitions for handling #include specifications
+ * These values are returned by ParseEOF to tell Parse_File whether to
+ * CONTINUE parsing, i.e. it had only reached the end of an include file,
+ * or if it's DONE.
*/
+#define CONTINUE 1
+#define DONE 0
-static Lst includes; /* stack of IFiles generated by .includes */
-Lst parseIncPath; /* list of directories for "..." includes */
-Lst sysIncPath; /* list of directories for <...> includes */
-Lst defIncPath; /* default directories for <...> includes */
-
-/*-
- * specType contains the SPECial TYPE of the current target. It is
- * Not if the target is unspecial. If it *is* special, however, the children
- * are linked as children of the parent but not vice versa. This variable is
- * set in ParseDoDependency
+/*
+ * Tokens for target attributes
*/
typedef enum {
Begin, /* .BEGIN */
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 */
Attribute /* Generic attribute */
} ParseSpecial;
+/*
+ * Other tokens
+ */
+#define LPAREN '('
+#define RPAREN ')'
+
+
+////////////////////////////////////////////////////////////
+// result data
+
+/*
+ * The main target to create. This is the first target on the first
+ * dependency line in the first makefile.
+ */
+static GNode *mainNode;
+
+////////////////////////////////////////////////////////////
+// eval state
+
+/* targets we're working on */
+static Lst targets;
+
+#ifdef CLEANUP
+/* command lines for targets */
+static Lst targCmds;
+#endif
+
+/*
+ * specType contains the SPECial TYPE of the current target. It is
+ * Not if the target is unspecial. If it *is* special, however, the children
+ * are linked as children of the parent but not vice versa. This variable is
+ * set in ParseDoDependency
+ */
static ParseSpecial specType;
-#define LPAREN '('
-#define RPAREN ')'
/*
* Predecessor node for handling .ORDER. Initialized to NULL when .ORDER
* seen, then set to each successive source on the line.
*/
static GNode *predecessor;
+////////////////////////////////////////////////////////////
+// parser state
+
+/* true if currently in a dependency line or its commands */
+static Boolean inLine;
+
+/* number of fatal errors */
+static int fatals = 0;
+
+/*
+ * Variables for doing includes
+ */
+
+/* current file being read */
+static IFile *curFile;
+
+/* stack of IFiles generated by .includes */
+static Lst includes;
+
+/* include paths (lists of directories) */
+Lst parseIncPath; /* dirs for "..." includes */
+Lst sysIncPath; /* dirs for <...> includes */
+Lst defIncPath; /* default for sysIncPath */
+
+////////////////////////////////////////////////////////////
+// parser tables
+
/*
* The parseKeywords table is searched using binary search when deciding
* if a target or source is special. The 'spec' field is the ParseSpecial
* the 'op' field is the operator to apply to the list of targets if the
* keyword is used as a source ("0" if the keyword isn't special as a source)
*/
-static struct {
+static const struct {
const char *name; /* Name of keyword */
ParseSpecial spec; /* Type when used as a target */
int op; /* Operator when used as a source */
{ ".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 },
{ ".WAIT", Wait, 0 },
};
+////////////////////////////////////////////////////////////
+// local functions
+
static int ParseIsEscaped(const char *, const char *);
static void ParseErrorInternal(const char *, size_t, int, const char *, ...)
__attribute__((__format__(__printf__, 4, 5)));
#ifdef SYSVINCLUDE
static void ParseTraditionalInclude(char *);
#endif
+#ifdef GMAKEEXPORT
+static void ParseGmakeExport(char *);
+#endif
static int ParseEOF(void);
static char *ParseReadLine(void);
static void ParseFinishLine(void);
static void ParseMark(GNode *);
-extern int maxJobs;
+////////////////////////////////////////////////////////////
+// file loader
+
+struct loadedfile {
+ const char *path; /* name, for error reports */
+ char *buf; /* contents buffer */
+ size_t len; /* length of contents */
+ size_t maplen; /* length of mmap area, or 0 */
+ Boolean used; /* XXX: have we used the data yet */
+};
+
+/*
+ * Constructor/destructor for loadedfile
+ */
+static struct loadedfile *
+loadedfile_create(const char *path)
+{
+ struct loadedfile *lf;
+
+ lf = bmake_malloc(sizeof(*lf));
+ lf->path = (path == NULL ? "(stdin)" : path);
+ lf->buf = NULL;
+ lf->len = 0;
+ lf->maplen = 0;
+ lf->used = FALSE;
+ return lf;
+}
+
+static void
+loadedfile_destroy(struct loadedfile *lf)
+{
+ if (lf->buf != NULL) {
+ if (lf->maplen > 0) {
+#ifndef __minix
+ munmap(lf->buf, lf->maplen);
+#endif
+ } else {
+ free(lf->buf);
+ }
+ }
+ free(lf);
+}
+
+/*
+ * nextbuf() operation for loadedfile, as needed by the weird and twisted
+ * logic below. Once that's cleaned up, we can get rid of lf->used...
+ */
+static char *
+loadedfile_nextbuf(void *x, size_t *len)
+{
+ struct loadedfile *lf = x;
+
+ if (lf->used) {
+ return NULL;
+ }
+ lf->used = TRUE;
+ *len = lf->len;
+ return lf->buf;
+}
+
+/*
+ * Try to get the size of a file.
+ */
+static ReturnStatus
+load_getsize(int fd, size_t *ret)
+{
+ struct stat st;
+
+ if (fstat(fd, &st) < 0) {
+ return FAILURE;
+ }
+
+ if (!S_ISREG(st.st_mode)) {
+ return FAILURE;
+ }
+
+ /*
+ * st_size is an off_t, which is 64 bits signed; *ret is
+ * size_t, which might be 32 bits unsigned or 64 bits
+ * unsigned. Rather than being elaborate, just punt on
+ * files that are more than 2^31 bytes. We should never
+ * see a makefile that size in practice...
+ *
+ * While we're at it reject negative sizes too, just in case.
+ */
+ if (st.st_size < 0 || st.st_size > 0x7fffffff) {
+ return FAILURE;
+ }
+
+ *ret = (size_t) st.st_size;
+ return SUCCESS;
+}
+
+/*
+ * Read in a file.
+ *
+ * Until the path search logic can be moved under here instead of
+ * being in the caller in another source file, we need to have the fd
+ * passed in already open. Bleh.
+ *
+ * If the path is NULL use stdin and (to insure against fd leaks)
+ * assert that the caller passed in -1.
+ */
+static struct loadedfile *
+loadfile(const char *path, int fd)
+{
+ struct loadedfile *lf;
+ long pagesize;
+ ssize_t result;
+ size_t bufpos;
+
+ lf = loadedfile_create(path);
+
+ if (path == NULL) {
+ assert(fd == -1);
+ fd = STDIN_FILENO;
+ } else {
+#if 0 /* notyet */
+ fd = open(path, O_RDONLY);
+ if (fd < 0) {
+ ...
+ Error("%s: %s", path, strerror(errno));
+ exit(1);
+ }
+#endif
+ }
+
+#ifndef __minix
+ if (load_getsize(fd, &lf->len) == SUCCESS) {
+ /* found a size, try mmap */
+ pagesize = sysconf(_SC_PAGESIZE);
+ if (pagesize <= 0) {
+ pagesize = 0x1000;
+ }
+ /* round size up to a page */
+ lf->maplen = pagesize * ((lf->len + pagesize - 1)/pagesize);
+
+ /*
+ * XXX hack for dealing with empty files; remove when
+ * we're no longer limited by interfacing to the old
+ * logic elsewhere in this file.
+ */
+ if (lf->maplen == 0) {
+ lf->maplen = pagesize;
+ }
+
+ /*
+ * FUTURE: remove PROT_WRITE when the parser no longer
+ * needs to scribble on the input.
+ */
+ lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE,
+ MAP_FILE|MAP_COPY, fd, 0);
+ if (lf->buf != MAP_FAILED) {
+ /* succeeded */
+ if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') {
+ char *b = malloc(lf->len + 1);
+ b[lf->len] = '\n';
+ memcpy(b, lf->buf, lf->len++);
+ munmap(lf->buf, lf->maplen);
+ lf->maplen = 0;
+ lf->buf = b;
+ }
+ goto done;
+ }
+ }
+#endif
+ /* cannot mmap; load the traditional way */
+
+ lf->maplen = 0;
+ lf->len = 1024;
+ lf->buf = bmake_malloc(lf->len);
+
+ bufpos = 0;
+ while (1) {
+ assert(bufpos <= lf->len);
+ if (bufpos == lf->len) {
+ lf->len *= 2;
+ lf->buf = bmake_realloc(lf->buf, lf->len);
+ }
+ result = read(fd, lf->buf + bufpos, lf->len - bufpos);
+ if (result < 0) {
+ Error("%s: read error: %s", path, strerror(errno));
+ exit(1);
+ }
+ if (result == 0) {
+ break;
+ }
+ bufpos += result;
+ }
+ assert(bufpos <= lf->len);
+ lf->len = bufpos;
+
+ /* truncate malloc region to actual length (maybe not useful) */
+ if (lf->len > 0) {
+ lf->buf = bmake_realloc(lf->buf, lf->len);
+ }
+
+done:
+ if (path != NULL) {
+ close(fd);
+ }
+ return lf;
+}
+////////////////////////////////////////////////////////////
+// old code
/*-
*----------------------------------------------------------------------
const char *dir;
/*
- * Nothing is more anoying than not knowing
+ * Nothing is more annoying than not knowing
* which Makefile is the culprit.
*/
dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &cp);
tOp = 0;
specType = Not;
- paths = (Lst)NULL;
+ paths = NULL;
curTargs = Lst_Init(FALSE);
&line[5]);
goto out;
} else {
- if (paths == (Lst)NULL) {
+ if (paths == NULL) {
paths = Lst_Init(FALSE);
}
(void)Lst_AtEnd(paths, path);
static void
Parse_include_file(char *file, Boolean isSystem, int silent)
{
+ struct loadedfile *lf;
char *fullname; /* full pathname of file */
char *newName;
char *prefEnd, *incdir;
return;
}
+ /* load it */
+ lf = loadfile(fullname, fd);
+
/* Start reading from this file next */
- Parse_SetInput(fullname, 0, fd, NULL, NULL);
+ Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf);
+ curFile->lf = lf;
}
static void
static void
ParseSetParseFile(const char *filename)
{
- char *slash;
- char *dirname;
+ char *slash, *dirname;
+ const char *pd, *pf;
int len;
slash = strrchr(filename, '/');
if (slash == NULL) {
- Var_Set(".PARSEDIR", ".", VAR_GLOBAL, 0);
- Var_Set(".PARSEFILE", filename, VAR_GLOBAL, 0);
+ Var_Set(".PARSEDIR", pd = curdir, VAR_GLOBAL, 0);
+ Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL, 0);
+ dirname= NULL;
} else {
len = slash - filename;
dirname = bmake_malloc(len + 1);
memcpy(dirname, filename, len);
- dirname[len] = 0;
- Var_Set(".PARSEDIR", dirname, VAR_GLOBAL, 0);
- Var_Set(".PARSEFILE", slash+1, VAR_GLOBAL, 0);
- free(dirname);
+ dirname[len] = '\0';
+ Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL, 0);
+ Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL, 0);
}
+ if (DEBUG(PARSE))
+ fprintf(debug_file, "ParseSetParseFile: ${.PARSEDIR} = `%s' "
+ "${.PARSEFILE} = `%s'\n", pd, pf);
+ free(dirname);
}
/*
*---------------------------------------------------------------------
*/
void
-Parse_SetInput(const char *name, int line, int fd, char *(*nextbuf)(void *), void *arg)
+Parse_SetInput(const char *name, int line, int fd,
+ char *(*nextbuf)(void *, size_t *), void *arg)
{
char *buf;
+ size_t len;
if (name == NULL)
name = curFile->fname;
curFile->fname = name;
curFile->lineno = line;
curFile->first_lineno = line;
- curFile->fd = fd;
curFile->nextbuf = nextbuf;
curFile->nextbuf_arg = arg;
+ curFile->lf = NULL;
- if (nextbuf == NULL) {
- /*
- * Allocate a 32k data buffer (as stdio seems to).
- * Set pointers so that first ParseReadc has to do a file read.
- */
- buf = bmake_malloc(IFILE_BUFLEN);
- buf[0] = 0;
- curFile->P_str = buf;
- curFile->P_ptr = buf;
- curFile->P_end = buf;
- curFile->P_buflen = IFILE_BUFLEN;
- } else {
- /* Get first block of input data */
- buf = curFile->nextbuf(curFile->nextbuf_arg);
- if (buf == NULL) {
- /* Was all a waste of time ... */
- free(curFile);
- return;
- }
- curFile->P_str = buf;
- curFile->P_ptr = buf;
- curFile->P_end = NULL;
+ assert(nextbuf != NULL);
+
+ /* Get first block of input data */
+ buf = curFile->nextbuf(curFile->nextbuf_arg, &len);
+ if (buf == NULL) {
+ /* Was all a waste of time ... */
+ free(curFile);
+ return;
}
+ curFile->P_str = buf;
+ curFile->P_ptr = buf;
+ curFile->P_end = buf+len;
curFile->cond_depth = Cond_save_depth();
ParseSetParseFile(name);
}
#endif
+#ifdef SYSVINCLUDE
+/*-
+ *---------------------------------------------------------------------
+ * ParseGmakeExport --
+ * Parse export <variable>=<value>
+ *
+ * And set the environment with it.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * None
+ *---------------------------------------------------------------------
+ */
+static void
+ParseGmakeExport(char *line)
+{
+ char *variable = &line[6];
+ char *value;
+
+ if (DEBUG(PARSE)) {
+ fprintf(debug_file, "ParseTraditionalInclude: %s\n", variable);
+ }
+
+ /*
+ * Skip over whitespace
+ */
+ while (isspace((unsigned char)*variable))
+ variable++;
+
+ for (value = variable; *value && *value != '='; value++)
+ continue;
+
+ if (*value != '=') {
+ Parse_Error(PARSE_FATAL,
+ "Variable/Value missing from \"include\"");
+ return;
+ }
+
+ /*
+ * Substitute for any variables in the file name before trying to
+ * find the thing.
+ */
+ value = Var_Subst(NULL, value, VAR_CMD, FALSE);
+ setenv(variable, value, 1);
+}
+#endif
+
/*-
*---------------------------------------------------------------------
* ParseEOF --
ParseEOF(void)
{
char *ptr;
-
- if (curFile->nextbuf != NULL) {
- /* eg .for loop data, get next iteration */
- ptr = curFile->nextbuf(curFile->nextbuf_arg);
- curFile->P_ptr = ptr;
- curFile->P_str = ptr;
- curFile->lineno = curFile->first_lineno;
- if (ptr != NULL) {
- /* Iterate again */
- return CONTINUE;
- }
+ size_t len;
+
+ assert(curFile->nextbuf != NULL);
+
+ /* get next input buffer, if any */
+ ptr = curFile->nextbuf(curFile->nextbuf_arg, &len);
+ curFile->P_ptr = ptr;
+ curFile->P_str = ptr;
+ curFile->P_end = ptr + len;
+ curFile->lineno = curFile->first_lineno;
+ if (ptr != NULL) {
+ /* Iterate again */
+ return CONTINUE;
}
/* Ensure the makefile (or loop) didn't have mismatched conditionals */
Cond_restore_depth(curFile->cond_depth);
+ if (curFile->lf != NULL) {
+ loadedfile_destroy(curFile->lf);
+ curFile->lf = NULL;
+ }
+
/* Dispose of curFile info */
/* Leak curFile->fname because all the gnodes have pointers to it */
- if (curFile->fd != -1)
- close(curFile->fd);
free(curFile->P_str);
free(curFile);
}
if (DEBUG(PARSE))
- fprintf(debug_file, "ParseEOF: returning to file %s, line %d, fd %d\n",
- curFile->fname, curFile->lineno, curFile->fd);
+ fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n",
+ curFile->fname, curFile->lineno);
/* Restore the PARSEDIR/PARSEFILE variables */
ParseSetParseFile(curFile->fname);
char *escaped;
char *comment;
char *tp;
- int len, dist;
/* Loop through blank lines and comment lines */
for (;;) {
escaped = NULL;
comment = NULL;
for (;;) {
+ if (cf->P_end != NULL && ptr == cf->P_end) {
+ /* end of buffer */
+ ch = 0;
+ break;
+ }
ch = *ptr;
if (ch == 0 || (ch == '\\' && ptr[1] == 0)) {
if (cf->P_end == NULL)
/* End of string (aka for loop) data */
break;
- /* End of data read from file, read more data */
- if (ptr != cf->P_end && (ch != '\\' || ptr + 1 != cf->P_end)) {
- Parse_Error(PARSE_FATAL, "Zero byte read from file");
- return NULL;
- }
- /* Move existing data to (near) start of file buffer */
- len = cf->P_end - cf->P_ptr;
- tp = cf->P_str + 32;
- memmove(tp, cf->P_ptr, len);
- dist = cf->P_ptr - tp;
- /* Update all pointers to reflect moved data */
- ptr -= dist;
- line -= dist;
- line_end -= dist;
- if (escaped)
- escaped -= dist;
- if (comment)
- comment -= dist;
- cf->P_ptr = tp;
- tp += len;
- cf->P_end = tp;
- /* Try to read more data from file into buffer space */
- len = cf->P_str + cf->P_buflen - tp - 32;
- if (len <= 0) {
- /* We need a bigger buffer to hold this line */
- tp = bmake_realloc(cf->P_str, cf->P_buflen + IFILE_BUFLEN);
- cf->P_ptr = cf->P_ptr - cf->P_str + tp;
- cf->P_end = cf->P_end - cf->P_str + tp;
- ptr = ptr - cf->P_str + tp;
- line = line - cf->P_str + tp;
- line_end = line_end - cf->P_str + tp;
- if (escaped)
- escaped = escaped - cf->P_str + tp;
- if (comment)
- comment = comment - cf->P_str + tp;
- cf->P_str = tp;
- tp = cf->P_end;
- len += IFILE_BUFLEN;
- cf->P_buflen += IFILE_BUFLEN;
- }
- len = read(cf->fd, tp, len);
- if (len <= 0) {
- if (len < 0) {
- Parse_Error(PARSE_FATAL, "Makefile read error: %s",
- strerror(errno));
- return NULL;
- }
- /* End of file */
+ if (cf->nextbuf != NULL) {
+ /*
+ * End of this buffer; return EOF and outer logic
+ * will get the next one. (eww)
+ */
break;
}
- /* 0 terminate the data, and update end pointer */
- tp += len;
- cf->P_end = tp;
- *tp = 0;
- /* Process newly read characters */
- continue;
+ Parse_Error(PARSE_FATAL, "Zero byte read from file");
+ return NULL;
}
if (ch == '\\') {
}
if (ch == '#' && comment == NULL) {
/* Remember first '#' for comment stripping */
- comment = line_end;
+ /* Unless previous char was '[', as in modifier :[#] */
+ if (!(ptr > line && ptr[-1] == '['))
+ comment = line_end;
}
ptr++;
if (ch == '\n')
line = ParseGetLine(PARSE_RAW, &lineLength);
if (line == NULL) {
Parse_Error(PARSE_FATAL,
- "Unexpected end of file in for loop.\n");
+ "Unexpected end of file in for loop.");
break;
}
} while (For_Accum(line));
{
char *cp; /* pointer into the line */
char *line; /* the line we're working on */
+ struct loadedfile *lf;
+
+ lf = loadfile(name, fd);
inLine = FALSE;
fatals = 0;
- Parse_SetInput(name, 0, fd, NULL, NULL);
+ if (name == NULL) {
+ name = "(stdin)";
+ }
+
+ Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf);
+ curFile->lf = lf;
do {
for (; (line = ParseReadLine()) != NULL; ) {
ParseTraditionalInclude(line);
continue;
}
+#endif
+#ifdef GMAKEEXPORT
+ if (strncmp(line, "export", 6) == 0 &&
+ isspace((unsigned char) line[6]) &&
+ strchr(line, ':') == NULL) {
+ /*
+ * It's an Gmake"export".
+ */
+ ParseGmakeExport(line);
+ continue;
+ }
#endif
if (Parse_IsVar(line)) {
ParseFinishLine();
if (fatals) {
(void)fflush(stdout);
(void)fprintf(stderr,
- "%s: Fatal errors encountered -- cannot continue\n",
+ "%s: Fatal errors encountered -- cannot continue",
progname);
PrintOnError(NULL, NULL);
exit(1);
-/* $NetBSD: str.c,v 1.33 2009/02/25 21:17:21 sno Exp $ */
+/* $NetBSD: str.c,v 1.34 2012/03/03 23:16:47 dholland Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: str.c,v 1.33 2009/02/25 21:17:21 sno Exp $";
+static char rcsid[] = "$NetBSD: str.c,v 1.34 2012/03/03 23:16:47 dholland Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90";
#else
-__RCSID("$NetBSD: str.c,v 1.33 2009/02/25 21:17:21 sno Exp $");
+__RCSID("$NetBSD: str.c,v 1.34 2012/03/03 23:16:47 dholland Exp $");
#endif
#endif /* not lint */
#endif
* matching operation permits the following special characters in the
* pattern: *?\[] (see the man page for details on what these mean).
*
+ * XXX this function does not detect or report malformed patterns.
+ *
* Side effects: None.
*/
int
-/* $NetBSD: suff.c,v 1.67 2009/01/23 21:58:28 dsl Exp $ */
+/* $NetBSD: suff.c,v 1.69 2011/09/29 23:38:04 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: suff.c,v 1.67 2009/01/23 21:58:28 dsl Exp $";
+static char rcsid[] = "$NetBSD: suff.c,v 1.69 2011/09/29 23:38:04 sjg Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)suff.c 8.4 (Berkeley) 3/21/94";
#else
-__RCSID("$NetBSD: suff.c,v 1.67 2009/01/23 21:58:28 dsl Exp $");
+__RCSID("$NetBSD: suff.c,v 1.69 2011/09/29 23:38:04 sjg Exp $");
#endif
#endif /* not lint */
#endif
*/
#include <stdio.h>
-#include <strings.h>
#include "make.h"
#include "hash.h"
#include "dir.h"
* We haven't looked to see if .OPTIONAL files exist yet, so
* don't use one as the implicit source.
* This allows us to use .OPTIONAL in .depend files so make won't
- * complain "don't know how to make xxx.h' when a dependant file
+ * complain "don't know how to make xxx.h' when a dependent file
* has been moved/deleted.
*/
continue;
static void
SuffFindDeps(GNode *gn, Lst slst)
{
- if (gn->type & (OP_DEPS_FOUND|OP_PHONY)) {
+ if (gn->type & OP_DEPS_FOUND) {
/*
* If dependencies already found, no need to do it again...
- * If this is a .PHONY target, we do not apply suffix rules.
*/
return;
} else {
gn->type |= OP_DEPS_FOUND;
}
-
+ /*
+ * Make sure we have these set, may get revised below.
+ */
+ Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0);
+ Var_Set(PREFIX, gn->name, gn, 0);
+ if (gn->type & OP_PHONY) {
+ /*
+ * If this is a .PHONY target, we do not apply suffix rules.
+ */
+ return;
+ }
if (DEBUG(SUFF)) {
fprintf(debug_file, "SuffFindDeps (%s)\n", gn->name);
}
-/* $NetBSD: targ.c,v 1.55 2009/01/23 21:26:30 dsl Exp $ */
+/* $NetBSD: targ.c,v 1.56 2010/11/25 21:31:09 christos Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: targ.c,v 1.55 2009/01/23 21:26:30 dsl Exp $";
+static char rcsid[] = "$NetBSD: targ.c,v 1.56 2010/11/25 21:31:09 christos Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: targ.c,v 1.55 2009/01/23 21:26:30 dsl Exp $");
+__RCSID("$NetBSD: targ.c,v 1.56 2010/11/25 21:31:09 christos Exp $");
#endif
#endif /* not lint */
#endif
*/
#include <stdio.h>
-#include <strings.h>
#include <time.h>
#include "make.h"
gn->centurion = NULL;
gn->made = UNMADE;
gn->flags = 0;
- gn->checked = 0;
- gn->mtime = gn->cmtime = 0;
+ gn->checked = 0;
+ gn->mtime = 0;
+ gn->cmgn = NULL;
gn->iParents = Lst_Init(FALSE);
gn->cohorts = Lst_Init(FALSE);
gn->parents = Lst_Init(FALSE);
gettimeofday(&rightnow, NULL);
-#if defined(__minix)
- fprintf(trfile, "%ld.%06ld %d %s %d %s",
- (long)rightnow.tv_sec, (long)rightnow.tv_usec,
- jobTokensRunning,
- evname[event], trpid, trwd);
-#else
fprintf(trfile, "%lld.%06ld %d %s %d %s",
(long long)rightnow.tv_sec, (long)rightnow.tv_usec,
jobTokensRunning,
evname[event], trpid, trwd);
-#endif
if (job != NULL) {
fprintf(trfile, " %s %d %x %x", job->node->name,
job->pid, job->flags, job->node->type);
-# $NetBSD: Makefile,v 1.25 2009/11/19 00:30:25 sjg Exp $
+# $NetBSD: Makefile,v 1.33 2011/09/29 23:38:04 sjg Exp $
#
# Unit tests for make(1)
# The main targets are:
SUBFILES= \
comment \
cond1 \
+ error \
export \
export-all \
+ doterror \
dotwait \
forsubst \
+ hash \
+ misc \
moderrs \
modmatch \
modmisc \
modorder \
modts \
modword \
+ phony-end \
posix \
qequals \
+ sysv \
ternary \
unexport \
unexport-env \
all: ${SUBFILES}
+flags.doterror=
+
# the tests are actually done with sub-makes.
.PHONY: ${SUBFILES}
.PRECIOUS: ${SUBFILES}
${SUBFILES}:
- -@${.MAKE} -k -f ${UNIT_TESTS}/$@
+ -@${.MAKE} ${flags.$@:U-k} -f ${UNIT_TESTS}/$@
clean:
rm -f *.out *.fail *.core
-.include <bsd.obj.mk>
+.-include <bsd.obj.mk>
TEST_MAKE?= ${.MAKE}
+TOOL_SED?= sed
+
+# ensure consistent results from sort(1)
+LC_ALL= C
+LANG= C
+.export LANG LC_ALL
# The driver.
# We always pretend .MAKE was called 'make'
-# $Id: cond1,v 1.4 2008/10/29 15:37:08 sjg Exp $
+# $Id: cond1,v 1.5 2011/03/06 00:02:14 sjg Exp $
# hard code these!
TEST_UNAME_S= NetBSD
C=dim
.endif
+.if defined(nosuch) && ${nosuch:Mx} != ""
+# this should not happen
+.info nosuch is x
+.endif
+
all:
@echo "$n is $X prime"
@echo "A='$A' B='$B' C='$C' o='$o,${o2}'"
--- /dev/null
+# $Id: doterror,v 1.1 2010/04/08 17:41:29 sjg Exp $
+
+
+.BEGIN:
+ @echo At first, I am
+
+.END:
+ @echo not reached
+
+.ERROR:
+ @echo "$@: Looks like '${.ERROR_TARGET}' is upset."
+
+all: happy sad
+
+happy:
+ @echo $@
+
+sad:
+ @echo and now: $@; exit 1
+
--- /dev/null
+# $Id: error,v 1.2 2010/05/24 21:04:49 sjg Exp $
+
+.info just FYI
+.warning this could be serious
+.error this is fatal
+
+all:
+
+.info.html:
+ @echo this should be ignored
--- /dev/null
+# $Id: export-all,v 1.2 2010/04/21 04:25:28 sjg Exp $
+
+UT_OK=good
+UT_F=fine
+
+# the old way to do :tA
+M_tAbad = C,.*,cd & \&\& 'pwd',:sh
+# the new
+M_tA = tA
+
+here := ${.PARSEDIR}
+
+# this will cause trouble (recursing if we let it)
+UT_BADDIR = ${${here}/../${here:T}:L:${M_tAbad}:T}
+# this will be ok
+UT_OKDIR = ${${here}/../${here:T}:L:${M_tA}:T}
+
+.export
+
+.include "export"
+
+UT_TEST=export-all
+UT_ALL=even this gets exported
--- /dev/null
+STR1=
+STR2= a
+STR3= ab
+STR4= abc
+STR5= abcd
+STR6= abcde
+STR7= abcdef
+STR8= abcdefghijklmnopqrstuvwxyz
+
+all:
+ @echo ${STR1:hash}
+ @echo ${STR2:hash}
+ @echo ${STR3:hash}
+ @echo ${STR4:hash}
+ @echo ${STR5:hash}
+ @echo ${STR6:hash}
+ @echo ${STR7:hash}
+ @echo ${STR8:hash}
--- /dev/null
+# $Id: misc,v 1.1 2011/03/06 00:02:14 sjg Exp $
+
+.if !exists(${.CURDIR}/)
+.warning ${.CURDIR}/ doesn't exist ?
+.endif
+
+.if !exists(${.CURDIR}/.)
+.warning ${.CURDIR}/. doesn't exist ?
+.endif
+
+.if !exists(${.CURDIR}/..)
+.warning ${.CURDIR}/.. doesn't exist ?
+.endif
+
+all:
+ @: all is well
-# $Id: modmisc,v 1.4 2006/05/11 15:37:07 sjg Exp $
+# $Id: modmisc,v 1.7 2011/04/11 15:10:15 sjg Exp $
#
# miscellaneous modifier tests
-path=:/bin:/usr/bin::/sbin:/usr/sbin:.:/home/user/bin:.
+# do not put any dirs in this list which exist on some
+# but not all target systems - an exists() check is below.
+path=:/bin:/tmp::/:.:/no/such/dir:.
# strip cwd from path.
MOD_NODOT=S/:/ /g:N.:ts:
# and decorate, note that $'s need to be doubled. Also note that
MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@
MOD_SEP=S,:, ,g
-all: modvar modvarloop
+all: modvar modvarloop modsysv
+
+modsysv:
+ @echo "The answer is ${libfoo.a:L:libfoo.a=42}"
modvar:
@echo "path='${path}'"
--- /dev/null
+
+LIST= one two three
+LIST+= four five six
+
+FU_mod-ts = a / b / cool
+
+AAA= a a a
+B.aaa= Baaa
+
+all: mod-ts
+
+# Use print or printf iff they are builtin.
+# XXX note that this causes problems, when make decides
+# there is no need to use a shell, so avoid where possible.
+.if ${type print 2> /dev/null || echo:L:sh:Mbuiltin} != ""
+PRINT= print -r --
+.elif ${type printf 2> /dev/null || echo:L:sh:Mbuiltin} != ""
+PRINT= printf '%s\n'
+.else
+PRINT= echo
+.endif
+
+mod-ts:
+ @echo 'LIST="${LIST}"'
+ @echo 'LIST:ts,="${LIST:ts,}"'
+ @echo 'LIST:ts/:tu="${LIST:ts/:tu}"'
+ @echo 'LIST:ts::tu="${LIST:ts::tu}"'
+ @echo 'LIST:ts:tu="${LIST:ts:tu}"'
+ @echo 'LIST:tu:ts/="${LIST:tu:ts/}"'
+ @echo 'LIST:ts:="${LIST:ts:}"'
+ @echo 'LIST:ts="${LIST:ts}"'
+ @echo 'LIST:ts:S/two/2/="${LIST:ts:S/two/2/}"'
+ @echo 'LIST:S/two/2/:ts="${LIST:S/two/2/:ts}"'
+ @echo 'LIST:ts/:S/two/2/="${LIST:ts/:S/two/2/}"'
+ @echo "Pretend the '/' in '/n' etc. below are back-slashes."
+ @${PRINT} 'LIST:ts/n="${LIST:ts\n}"'
+ @${PRINT} 'LIST:ts/t="${LIST:ts\t}"'
+ @${PRINT} 'LIST:ts/012:tu="${LIST:ts\012:tu}"'
+ @${PRINT} 'LIST:tx="${LIST:tx}"'
+ @${PRINT} 'LIST:ts/x:tu="${LIST:ts\x:tu}"'
+ @${PRINT} 'FU_$@="${FU_${@:ts}:ts}"'
+ @${PRINT} 'FU_$@:ts:T="${FU_${@:ts}:ts:T}" == cool?'
+ @${PRINT} 'B.$${AAA:ts}="${B.${AAA:ts}}" == Baaa?'
--- /dev/null
+# $Id: phony-end,v 1.1 2011/09/29 23:38:04 sjg Exp $
+
+all ok also.ok bug phony:
+ @echo '${.TARGET .PREFIX .IMPSRC:L:@v@$v="${$v}"@}'
+
+.END: ok also.ok bug
+
+phony bug: .PHONY
+all: phony
--- /dev/null
+# $Id: sysv,v 1.2 2011/06/03 21:10:42 sjg Exp $
+
+FOO ?=
+FOOBAR = $(FOO:=bar)
+
+_this := ${.PARSEDIR}/${.PARSEFILE}
+
+B = /b
+S = /
+FUN = ${B}${S}fun
+SUN = the Sun
+
+# we expect nothing when FOO is empty
+all: foo fun
+
+foo:
+ @echo FOOBAR = $(FOOBAR)
+.if empty(FOO)
+ @FOO="foo fu" ${.MAKE} -f ${_this} foo
+.endif
+
+fun:
+ @echo ${FUN:T}
+ @echo ${FUN:${B}${S}fun=fun}
+ @echo ${FUN:${B}${S}%=%}
+ @echo ${In:L:%=% ${SUN}}
make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No
OK
+make: "error" line 3: just FYI
+make: "error" line 4: warning: this could be serious
+make: "error" line 5: this is fatal
UT_DOLLAR=This is $UT_FU
UT_FOO=foobar is fubar
UT_FU=fubar
UT_TEST=export
UT_ZOO=hoopie
UT_ALL=even this gets exported
+UT_BADDIR=unit-tests
UT_DOLLAR=This is $UT_FU
UT_F=fine
UT_FOO=foobar is fubar
UT_FU=fubar
UT_NO=all
UT_OK=good
+UT_OKDIR=unit-tests
UT_TEST=export-all
UT_ZOO=hoopie
+At first, I am
+happy
+and now: sad
+.ERROR: Looks like 'sad' is upset.
+*** Error code 1
+
+Stop.
+make: stopped in unit-tests
simple.1
simple.1
simple.2
cycle.1.99
cycle.1.99
.for with :S;... OK
+b2af338b
+3360ac65
+7747f046
+9ca87054
+880fe816
+208fcbd3
+d5d376eb
+de41416c
Expect: Unknown modifier 'Z'
make: Unknown modifier 'Z'
VAR:Z=
LIB=e X_LIBS:M*/lib${LIB}.a is "/tmp/libe.a"
LIB=e X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBE.A"
Mscanner=OK
-path=':/bin:/usr/bin::/sbin:/usr/sbin:.:/home/user/bin:.'
-path='/bin:/usr/bin:/sbin:/usr/sbin:/home/user/bin'
-path='/bin:/usr/bin:/sbin:/usr/sbin:/homes/user/bin'
-path='/bin':'/usr/bin':'/sbin':'/usr/sbin':'/home/user/bin'
-path='/bin':'/usr/bin':'/sbin':'/usr/sbin':'/homes/user/bin'
+path=':/bin:/tmp::/:.:/no/such/dir:.'
+path='/bin:/tmp:/:/no/such/dir'
+path='/bin:/tmp:/:/no/such/dir'
+path='/bin':'/tmp':'/':'/no/such/dir'
+path='/bin':'/tmp':'/':'/no/such/dir'
path_/usr/xbin=/opt/xbin/
-paths=/bin /usr/bin /sbin /usr/sbin /homes/user/bin /opt/xbin
-PATHS=/BIN /USR/BIN /SBIN /USR/SBIN /HOMES/USER/BIN /OPT/XBIN
+paths=/bin /tmp / /no/such/dir /opt/xbin
+PATHS=/BIN /TMP / /NO/SUCH/DIR /OPT/XBIN
+The answer is 42
LIST = one two three four five six seven eight nine ten
LIST:O = eight five four nine one seven six ten three two
LIST:Ox = Ok
LIST:tw:C/ /,/1g="one two three four five six"
LIST:tw:tW:C/ /,/="one,two three four five six"
LIST:tW:tw:C/ /,/="one two three four five six"
+.TARGET="phony" .PREFIX="phony" .IMPSRC=""
+.TARGET="all" .PREFIX="all" .IMPSRC=""
+.TARGET="ok" .PREFIX="ok" .IMPSRC=""
+.TARGET="also.ok" .PREFIX="also.ok" .IMPSRC=""
+.TARGET="bug" .PREFIX="bug" .IMPSRC=""
Posix says we should execute the command as if run by system(3)
Expect 'Hello,' and 'World!'
Hello,
*** Error code 1 (continuing)
`all' not remade because of errors.
V.i386 ?= OK
+FOOBAR =
+FOOBAR = foobar fubar
+fun
+fun
+fun
+In the Sun
The answer is unknown
The answer is unknown
The answer is empty
five v=is x k=is x
six v=is y k=is y
show-v v=override k=override
+*** Error code 1 (ignored)
+*** Error code 1 (ignored)
-/* $NetBSD: util.c,v 1.50 2010/06/03 15:40:16 sjg Exp $ */
+/* $NetBSD: util.c,v 1.51 2011/04/02 07:58:30 mbalmer Exp $ */
/*
* Missing stuff from OS's
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: util.c,v 1.50 2010/06/03 15:40:16 sjg Exp $";
+static char rcsid[] = "$NetBSD: util.c,v 1.51 2011/04/02 07:58:30 mbalmer Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
-__RCSID("$NetBSD: util.c,v 1.50 2010/06/03 15:40:16 sjg Exp $");
+__RCSID("$NetBSD: util.c,v 1.51 2011/04/02 07:58:30 mbalmer Exp $");
#endif
#endif
char *p, *q;
for (i = 0; (q = environ[i]); i++) {
- char *p = strchr(q, '=');
+ p = strchr(q, '=');
if (p == NULL)
continue;
if (strncmp(name, q, len = p - q) == 0) {
-/* $NetBSD: var.c,v 1.159 2010/06/06 01:13:12 sjg Exp $ */
+/* $NetBSD: var.c,v 1.167 2011/06/03 21:10:42 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
*/
#ifndef MAKE_NATIVE
-static char rcsid[] = "$NetBSD: var.c,v 1.159 2010/06/06 01:13:12 sjg Exp $";
+static char rcsid[] = "$NetBSD: var.c,v 1.167 2011/06/03 21:10:42 sjg Exp $";
#else
#include <sys/cdefs.h>
#ifndef lint
#if 0
static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94";
#else
-__RCSID("$NetBSD: var.c,v 1.159 2010/06/06 01:13:12 sjg Exp $");
+__RCSID("$NetBSD: var.c,v 1.167 2011/06/03 21:10:42 sjg Exp $");
#endif
#endif /* not lint */
#endif
#include <regex.h>
#endif
#include <ctype.h>
+#include <inttypes.h>
#include <stdlib.h>
#include <limits.h>
+#include <time.h>
#include "make.h"
#include "buf.h"
VarPattern *);
static char *VarQuote(char *);
static char *VarChangeCase(char *, int);
+static char *VarHash(char *);
static char *VarModify(GNode *, Var_Parse_State *,
const char *,
Boolean (*)(GNode *, Var_Parse_State *, char *, Boolean, Buffer *, void *),
name = TARGET;
break;
}
+#ifdef notyet
+ /* for compatibility with gmake */
+ if (name[0] == '^' && name[1] == '\0')
+ name = ALLSRC;
+#endif
+
/*
* First look for the variable in the given context. If it's not there,
* look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order,
return str;
}
+/*-
+ *-----------------------------------------------------------------------
+ * VarHash --
+ * Hash the string using the MurmurHash3 algorithm.
+ * Output is computed using 32bit Little Endian arithmetic.
+ *
+ * Input:
+ * str String to modify
+ *
+ * Results:
+ * Hash value of str, encoded as 8 hex digits.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static char *
+VarHash(char *str)
+{
+ static const char hexdigits[16] = "0123456789abcdef";
+ Buffer buf;
+ size_t len, len2;
+ unsigned char *ustr = (unsigned char *)str;
+ uint32_t h, k, c1, c2;
+ int done;
+
+ done = 1;
+ h = 0x971e137bU;
+ c1 = 0x95543787U;
+ c2 = 0x2ad7eb25U;
+ len2 = strlen(str);
+
+ for (len = len2; len; ) {
+ k = 0;
+ switch (len) {
+ default:
+ k = (ustr[3] << 24) | (ustr[2] << 16) | (ustr[1] << 8) | ustr[0];
+ len -= 4;
+ ustr += 4;
+ break;
+ case 3:
+ k |= (ustr[2] << 16);
+ case 2:
+ k |= (ustr[1] << 8);
+ case 1:
+ k |= ustr[0];
+ len = 0;
+ }
+ c1 = c1 * 5 + 0x7b7d159cU;
+ c2 = c2 * 5 + 0x6bce6396U;
+ k *= c1;
+ k = (k << 11) ^ (k >> 21);
+ k *= c2;
+ h = (h << 13) ^ (h >> 19);
+ h = h * 5 + 0x52dce729U;
+ h ^= k;
+ } while (!done);
+ h ^= len2;
+ h *= 0x85ebca6b;
+ h ^= h >> 13;
+ h *= 0xc2b2ae35;
+ h ^= h >> 16;
+
+ Buf_Init(&buf, 0);
+ for (len = 0; len < 8; ++len) {
+ Buf_AddByte(&buf, hexdigits[h & 15]);
+ h >>= 4;
+ }
+
+ return Buf_Destroy(&buf, FALSE);
+}
+
/*-
*-----------------------------------------------------------------------
* VarChangeCase --
return Buf_Destroy(&buf, FALSE);
}
+static char *
+VarStrftime(const char *fmt, int zulu)
+{
+ char buf[BUFSIZ];
+ time_t utc;
+
+ time(&utc);
+ if (!*fmt)
+ fmt = "%c";
+ strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc));
+
+ buf[sizeof(buf) - 1] = '\0';
+ return bmake_strdup(buf);
+}
+
/*
* Now we need to apply any modifiers the user wants applied.
* These are:
* variable.
*/
+/* we now have some modifiers with long names */
+#define STRMOD_MATCH(s, want, n) \
+ (strncmp(s, want, n) == 0 && (s[n] == endc || s[n] == ':'))
+
static char *
ApplyModifiers(char *nstr, const char *tstr,
int startc, int endc,
if (*tstr == '$') {
/*
- * We have some complex modifiers in a variable.
+ * We may have some complex modifiers in a variable.
*/
void *freeIt;
char *rval;
int rlen;
+ int c;
rval = Var_Parse(tstr, ctxt, errnum, &rlen, &freeIt);
+ /*
+ * If we have not parsed up to endc or ':',
+ * we are not interested.
+ */
+ if (rval != NULL && *rval &&
+ (c = tstr[rlen]) != '\0' &&
+ c != ':' &&
+ c != endc) {
+ if (freeIt)
+ free(freeIt);
+ goto apply_mods;
+ }
+
if (DEBUG(VAR)) {
fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n",
rval, rlen, tstr, rlen, tstr + rlen);
}
continue;
}
+ apply_mods:
if (DEBUG(VAR)) {
fprintf(debug_file, "Applying :%c to \"%s\"\n", *tstr, nstr);
}
cp = ++tstr;
break;
}
- delim = BRCLOSE;
+ delim = startc == PROPEN ? PRCLOSE : BRCLOSE;
pattern.flags = 0;
pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum,
}
}
+ case 'g':
+ cp = tstr + 1; /* make sure it is set */
+ if (STRMOD_MATCH(tstr, "gmtime", 6)) {
+ newStr = VarStrftime(nstr, 1);
+ cp = tstr + 6;
+ termc = *cp;
+ } else {
+ goto default_case;
+ }
+ break;
+ case 'h':
+ cp = tstr + 1; /* make sure it is set */
+ if (STRMOD_MATCH(tstr, "hash", 4)) {
+ newStr = VarHash(nstr);
+ cp = tstr + 4;
+ termc = *cp;
+ } else {
+ goto default_case;
+ }
+ break;
+ case 'l':
+ cp = tstr + 1; /* make sure it is set */
+ if (STRMOD_MATCH(tstr, "localtime", 9)) {
+ newStr = VarStrftime(nstr, 0);
+ cp = tstr + 9;
+ termc = *cp;
+ } else {
+ goto default_case;
+ }
+ break;
case 't':
{
cp = tstr + 1; /* make sure it is set */
*/
termc = *--cp;
delim = '\0';
- newStr = VarModify(ctxt, &parsestate, nstr,
- VarSYSVMatch,
- &pattern);
+ if (pattern.leftLen == 0 && *nstr == '\0') {
+ newStr = nstr; /* special case */
+ } else {
+ newStr = VarModify(ctxt, &parsestate, nstr,
+ VarSYSVMatch,
+ &pattern);
+ }
free(UNCONST(pattern.lhs));
free(UNCONST(pattern.rhs));
} else
nstr = bmake_strndup(start, *lengthPtr);
*freePtr = nstr;
} else {
- nstr = var_Error;
+ nstr = errnum ? var_Error : varNoError;
}
}
if (nstr != Buf_GetAll(&v->val, NULL))