diff --git a/commands/Makefile b/commands/Makefile index 5e80f2179..a52473970 100644 --- a/commands/Makefile +++ b/commands/Makefile @@ -16,7 +16,7 @@ SUBDIR= add_route arp ash at \ 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 \ diff --git a/commands/make/Makefile b/commands/make/Makefile deleted file mode 100644 index bc7fd639e..000000000 --- a/commands/make/Makefile +++ /dev/null @@ -1,39 +0,0 @@ -# $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 -.include - -# 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} diff --git a/commands/make/unit-tests/export-all b/commands/make/unit-tests/export-all deleted file mode 100644 index 05429379e..000000000 --- a/commands/make/unit-tests/export-all +++ /dev/null @@ -1,11 +0,0 @@ -# $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 diff --git a/commands/make/unit-tests/modts b/commands/make/unit-tests/modts deleted file mode 100644 index d0efd6d19..000000000 --- a/commands/make/unit-tests/modts +++ /dev/null @@ -1,32 +0,0 @@ - -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?' diff --git a/usr.bin/Makefile b/usr.bin/Makefile index d608224d5..468adfc79 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -3,7 +3,7 @@ .include # 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 diff --git a/commands/make/Makefile.netbsd b/usr.bin/make/Makefile similarity index 59% rename from commands/make/Makefile.netbsd rename to usr.bin/make/Makefile index acbe83aa7..bcfd975e8 100644 --- a/commands/make/Makefile.netbsd +++ b/usr.bin/make/Makefile @@ -1,4 +1,4 @@ -# $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 @@ -13,10 +13,28 @@ SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \ 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 @@ -24,10 +42,13 @@ SUBDIR+= unit-tests .include .include -CPPFLAGS+= -DMAKE_NATIVE -COPTS.var.c+= -Wno-cast-qual - .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 diff --git a/usr.bin/make/Makefile.bak b/usr.bin/make/Makefile.bak new file mode 100644 index 000000000..fe2f61363 --- /dev/null +++ b/usr.bin/make/Makefile.bak @@ -0,0 +1,71 @@ +# $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 +.include + +.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} diff --git a/commands/make/Makefile.boot b/usr.bin/make/Makefile.boot similarity index 84% rename from commands/make/Makefile.boot rename to usr.bin/make/Makefile.boot index f6c2bb071..953dba6a6 100644 --- a/commands/make/Makefile.boot +++ b/usr.bin/make/Makefile.boot @@ -1,4 +1,4 @@ -# $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... # @@ -6,9 +6,7 @@ # # 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 $@ @@ -18,7 +16,7 @@ MACHINE_ARCH=i386 # 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= diff --git a/commands/make/PSD.doc/Makefile b/usr.bin/make/PSD.doc/Makefile similarity index 100% rename from commands/make/PSD.doc/Makefile rename to usr.bin/make/PSD.doc/Makefile diff --git a/commands/make/PSD.doc/tutorial.ms b/usr.bin/make/PSD.doc/tutorial.ms similarity index 99% rename from commands/make/PSD.doc/tutorial.ms rename to usr.bin/make/PSD.doc/tutorial.ms index 9802e4b40..c1a6444d4 100644 --- a/commands/make/PSD.doc/tutorial.ms +++ b/usr.bin/make/PSD.doc/tutorial.ms @@ -1,4 +1,4 @@ -.\" $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. .\" @@ -114,14 +114,15 @@ .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 @@ -131,7 +132,7 @@ \h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9 .sp |\\n(g8u .sp 166u -\D't 3u'\D's -1u' +\D't 3u' .br .po .rt diff --git a/commands/make/arch.c b/usr.bin/make/arch.c similarity index 98% rename from commands/make/arch.c rename to usr.bin/make/arch.c index 263a4470d..650aaec7e 100644 --- a/commands/make/arch.c +++ b/usr.bin/make/arch.c @@ -1,4 +1,4 @@ -/* $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 @@ -69,14 +69,14 @@ */ #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 #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 @@ -1212,7 +1212,7 @@ Arch_FindLib(GNode *gn, Lst path) * 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). @@ -1245,8 +1245,9 @@ Arch_LibOODate(GNode *gn) 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 @@ -1261,7 +1262,7 @@ Arch_LibOODate(GNode *gn) 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 diff --git a/commands/make/buf.c b/usr.bin/make/buf.c similarity index 100% rename from commands/make/buf.c rename to usr.bin/make/buf.c diff --git a/commands/make/buf.h b/usr.bin/make/buf.h similarity index 100% rename from commands/make/buf.h rename to usr.bin/make/buf.h diff --git a/commands/make/compat.c b/usr.bin/make/compat.c similarity index 94% rename from commands/make/compat.c rename to usr.bin/make/compat.c index 054cfd75e..099ba39c8 100644 --- a/commands/make/compat.c +++ b/usr.bin/make/compat.c @@ -1,4 +1,4 @@ -/* $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. @@ -70,14 +70,14 @@ */ #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 #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 @@ -121,7 +121,7 @@ static char meta[256]; static GNode *curTarg = NULL; static GNode *ENDNode; -static void CompatInterrupt(int); +static void CompatInterrupt(int) __dead; static void Compat_Init(void) @@ -347,11 +347,17 @@ again: 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. */ @@ -362,6 +368,11 @@ again: if (cpid == 0) { Check_Cwd(av); Var_ExportVars(); +#ifdef USE_META + if (useMeta) { + meta_compat_child(); + } +#endif if (local) (void)execvp(av[0], (char *const *)UNCONST(av)); else @@ -375,12 +386,20 @@ again: 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; } @@ -391,6 +410,11 @@ again: 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: ", @@ -417,6 +441,11 @@ again: 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) { /* @@ -498,10 +527,10 @@ Compat_Make(void *gnp, void *pgnp) } /* - * 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); @@ -549,6 +578,11 @@ Compat_Make(void *gnp, void *pgnp) */ 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 { @@ -557,6 +591,11 @@ Compat_Make(void *gnp, void *pgnp) } else { gn->made = ERROR; } +#ifdef USE_META + if (useMeta && !NoExecute(gn)) { + meta_job_finish(NULL); + } +#endif if (gn->made != ERROR) { /* diff --git a/commands/make/cond.c b/usr.bin/make/cond.c similarity index 98% rename from commands/make/cond.c rename to usr.bin/make/cond.c index 3292f19eb..24db629c0 100644 --- a/commands/make/cond.c +++ b/usr.bin/make/cond.c @@ -1,4 +1,4 @@ -/* $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. @@ -70,14 +70,14 @@ */ #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 #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 @@ -327,7 +327,7 @@ CondGetArg(char **linePtr, char **argPtr, const char *func) *----------------------------------------------------------------------- */ static Boolean -CondDoDefined(int argLen, const char *arg) +CondDoDefined(int argLen __unused, const char *arg) { char *p1; Boolean result; @@ -376,7 +376,7 @@ CondStrMatch(const void *string, const void *pattern) *----------------------------------------------------------------------- */ static Boolean -CondDoMake(int argLen, const char *arg) +CondDoMake(int argLen __unused, const char *arg) { return Lst_Find(create, arg, CondStrMatch) != NULL; } @@ -395,22 +395,22 @@ CondDoMake(int argLen, const char *arg) *----------------------------------------------------------------------- */ 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); } @@ -428,7 +428,7 @@ CondDoExists(int argLen, const char *arg) *----------------------------------------------------------------------- */ static Boolean -CondDoTarget(int argLen, const char *arg) +CondDoTarget(int argLen __unused, const char *arg) { GNode *gn; @@ -452,7 +452,7 @@ CondDoTarget(int argLen, const char *arg) *----------------------------------------------------------------------- */ static Boolean -CondDoCommands(int argLen, const char *arg) +CondDoCommands(int argLen __unused, const char *arg) { GNode *gn; @@ -790,7 +790,7 @@ done: } 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 @@ -831,7 +831,7 @@ get_mpt_arg(char **linePtr, char **argPtr, const char *func) } static Boolean -CondDoEmpty(int arglen, const char *arg) +CondDoEmpty(int arglen, const char *arg __unused) { return arglen == 1; } diff --git a/commands/make/config.h b/usr.bin/make/config.h similarity index 97% rename from commands/make/config.h rename to usr.bin/make/config.h index 7ff999572..06af09c2d 100644 --- a/commands/make/config.h +++ b/usr.bin/make/config.h @@ -1,4 +1,4 @@ -/* $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. @@ -132,6 +132,12 @@ #define SYSVINCLUDE #define SYSVVARSUB +/* + * GMAKEEXPORT + * Recognize gmake like variable export directives [export =] + */ +#define GMAKEEXPORT + /* * SUNSHCMD * Recognize SunOS and Solaris: diff --git a/commands/make/dir.c b/usr.bin/make/dir.c similarity index 97% rename from commands/make/dir.c rename to usr.bin/make/dir.c index 7863d490c..6560f30d0 100644 --- a/commands/make/dir.c +++ b/usr.bin/make/dir.c @@ -1,4 +1,4 @@ -/* $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. @@ -70,14 +70,14 @@ */ #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 #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 @@ -1061,6 +1061,7 @@ Dir_FindFile(const char *name, Lst path) 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 @@ -1165,6 +1166,11 @@ Dir_FindFile(const char *name, Lst path) return NULL; } + if (*cp == '\0') { + /* we were given a trailing "/" */ + cp = trailing_dot; + } + if (name[0] != '/') { Boolean checkedDot = FALSE; @@ -1272,6 +1278,10 @@ Dir_FindFile(const char *name, Lst path) * 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] = '/'; @@ -1418,7 +1428,7 @@ Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { *----------------------------------------------------------------------- */ 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 */ @@ -1434,6 +1444,31 @@ Dir_MTime(GNode *gn) 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)" ); @@ -1446,19 +1481,16 @@ Dir_MTime(GNode *gn) 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) @@ -1467,12 +1499,16 @@ Dir_MTime(GNode *gn) } 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) { diff --git a/commands/make/dir.h b/usr.bin/make/dir.h similarity index 97% rename from commands/make/dir.h rename to usr.bin/make/dir.h index d758371ca..aa004504a 100644 --- a/commands/make/dir.h +++ b/usr.bin/make/dir.h @@ -1,4 +1,4 @@ -/* $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. @@ -95,7 +95,7 @@ Boolean Dir_HasWildcards(char *); 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); diff --git a/commands/make/for.c b/usr.bin/make/for.c similarity index 97% rename from commands/make/for.c rename to usr.bin/make/for.c index 4a320a38a..fd4f2b732 100644 --- a/commands/make/for.c +++ b/usr.bin/make/for.c @@ -1,4 +1,4 @@ -/* $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. @@ -30,14 +30,14 @@ */ #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 #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 @@ -366,7 +366,7 @@ for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech) } static char * -For_Iterate(void *v_arg) +For_Iterate(void *v_arg, size_t *ret_len) { For *arg = v_arg; int i, len; @@ -451,6 +451,7 @@ For_Iterate(void *v_arg) arg->sub_next += strlist_num(&arg->vars); arg->parse_buf = cp; + *ret_len = strlen(cp); return cp; } diff --git a/commands/make/hash.c b/usr.bin/make/hash.c similarity index 100% rename from commands/make/hash.c rename to usr.bin/make/hash.c diff --git a/commands/make/hash.h b/usr.bin/make/hash.h similarity index 100% rename from commands/make/hash.h rename to usr.bin/make/hash.h diff --git a/commands/make/job.c b/usr.bin/make/job.c similarity index 96% rename from commands/make/job.c rename to usr.bin/make/job.c index aeda9da93..1e8eefb14 100644 --- a/commands/make/job.c +++ b/usr.bin/make/job.c @@ -1,4 +1,4 @@ -/* $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. @@ -70,14 +70,14 @@ */ #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 #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 @@ -321,10 +321,7 @@ static int readyfd(Job *); 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 */ @@ -333,10 +330,11 @@ 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)) @@ -344,7 +342,7 @@ static sigset_t caught_signals; /* Set of signals we handle */ 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 *); @@ -354,7 +352,7 @@ static int JobStart(GNode *, int); 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 *); @@ -525,14 +523,14 @@ JobContinueSig(int signo __unused) * *----------------------------------------------------------------------- */ -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 */ @@ -616,7 +614,7 @@ JobPassSig_suspend(int signo) *----------------------------------------------------------------------- */ static Job * -JobFindPid(int pid, int status) +JobFindPid(int pid, int status, Boolean isJobs) { Job *job; @@ -624,7 +622,7 @@ JobFindPid(int pid, int status) if ((job->job_state == status) && job->pid == pid) return job; } - if (DEBUG(JOB)) + if (DEBUG(JOB) && isJobs) job_table_dump("no pid"); return NULL; } @@ -713,6 +711,7 @@ JobPrintCommand(void *cmdp, void *jobp) shutUp = DEBUG(LOUD) ? FALSE : TRUE; break; case '-': + job->flags |= JOB_IGNERR; errOff = TRUE; break; case '+': @@ -761,7 +760,7 @@ JobPrintCommand(void *cmdp, void *jobp) } if (errOff) { - if ( !(job->flags & JOB_IGNERR) && !noSpecials) { + if (!noSpecials) { if (commandShell->hasErrCtl) { /* * we don't want the error-control commands showing @@ -1016,10 +1015,15 @@ JobFinish(Job *job, int status) 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 { @@ -1044,6 +1048,12 @@ JobFinish(Job *job, int status) (void)fflush(stdout); } +#ifdef USE_META + if (useMeta) { + meta_job_finish(job); + } +#endif + return_job_token = FALSE; Trace_Log(JOBEND, job); @@ -1123,7 +1133,8 @@ Job_Touch(GNode *gn, Boolean silent) 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. @@ -1215,7 +1226,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) 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 @@ -1232,11 +1243,11 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) } 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; @@ -1310,6 +1321,11 @@ JobExec(Job *job, char **argv) /* 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). @@ -1552,6 +1568,7 @@ JobStart(GNode *gn, int flags) * also dead... */ if (!cmdsOK) { + PrintOnError(gn, NULL); /* provide some clue */ DieHorribly(); } @@ -1572,6 +1589,14 @@ JobStart(GNode *gn, int flags) */ 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 */ @@ -1844,6 +1869,11 @@ end_loop: 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); } @@ -1926,7 +1956,6 @@ void Job_CatchChildren(void) { int pid; /* pid of dead child */ - Job *job; /* job descriptor for dead child */ int status; /* Exit/termination status */ /* @@ -1940,41 +1969,60 @@ Job_CatchChildren(void) (void)fprintf(debug_file, "Process %d exited/stopped status %x.\n", pid, status); } + JobReapChild(pid, status, TRUE); + } +} - job = JobFindPid(pid, JOB_ST_RUNNING); - if (job == NULL) { +/* + * 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, 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; - } - (void)fflush(stdout); - continue; - } - - job->job_state = JOB_ST_FINISHED; - job->exit_status = status; - - JobFinish(job, status); + 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)); + } + job->job_suspended = 1; + } + (void)fflush(stdout); + return; + } + + job->job_state = JOB_ST_FINISHED; + job->exit_status = status; + + JobFinish(job, status); } /*- @@ -2131,16 +2179,6 @@ Job_Init(void) 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- < #ifndef lint @@ -81,7 +81,7 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\ #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 @@ -178,11 +178,11 @@ static const char * tracefile; 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; @@ -242,6 +242,9 @@ parse_debug_options(const char *argvalue) case 'l': debug |= DEBUG_LOUD; break; + case 'M': + debug |= DEBUG_META; + break; case 'm': debug |= DEBUG_MAKE; break; @@ -684,7 +687,7 @@ ReadAllMakefiles(const void *p, const void *q) return (ReadMakefile(p, q) == 0); } -static int +int str2Lst_Append(Lst lp, char *str, const char *sep) { char *cp; @@ -703,7 +706,7 @@ str2Lst_Append(Lst lp, char *str, const char *sep) #ifdef SIGINFO /*ARGSUSED*/ static void -siginfo(int signo) +siginfo(int signo __unused) { char dir[MAXPATHLEN]; char str[2 * MAXPATHLEN]; @@ -732,6 +735,10 @@ MakeMode(const char *mode) compatMake = TRUE; forceJobs = FALSE; } +#if USE_META + if (strstr(mode, "meta")) + meta_init(mode); +#endif } if (mp) free(mp); @@ -762,7 +769,7 @@ main(int argc, char **argv) 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 */ @@ -957,7 +964,8 @@ main(int argc, char **argv) * 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); } @@ -1300,7 +1308,7 @@ ReadMakefile(const void *p, const void *q __unused) 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 */ @@ -1641,9 +1649,10 @@ Cmd_Exec(const char *cmd, const char **errnum) /* * 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); @@ -1958,20 +1967,10 @@ Main_ExportMAKEFLAGS(Boolean first) } } -/* - * 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; @@ -1986,6 +1985,25 @@ mkTempFile(const char *pattern, char **fnamep) 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 { diff --git a/commands/make/make.1 b/usr.bin/make/make.1 similarity index 91% rename from commands/make/make.1 rename to usr.bin/make/make.1 index a5aed6740..51e77dbff 100644 --- a/commands/make/make.1 +++ b/usr.bin/make/make.1 @@ -1,4 +1,4 @@ -.\" $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. @@ -29,7 +29,7 @@ .\" .\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 .\" -.Dd June 9, 2010 +.Dd March 31, 2012 .Dt MAKE 1 .Os .Sh NAME @@ -38,40 +38,18 @@ .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. @@ -95,7 +73,7 @@ This manual page is intended as a reference document only. 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 @@ -126,7 +104,7 @@ Turn on debugging, and specify which portions of .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. @@ -194,6 +172,8 @@ Print commands in Makefiles regardless of whether or not they are prefixed by .Ql @ or other "quiet" flags. Also known as "loud" behavior. +.It Ar M +Print debugging information about "meta" mode decisions about targets. .It Ar m Print debugging information about making targets, including modification dates. @@ -266,6 +246,8 @@ cooperate to avoid overloading the system. 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. @@ -613,7 +595,7 @@ The list of sources for this target that were deemed out-of-date; also 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 @@ -759,9 +741,73 @@ Processed after reading all makefiles. 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 @@ -858,6 +904,9 @@ This variable and 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 @@ -892,6 +941,8 @@ is set to the value of 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 \&: @@ -988,6 +1039,18 @@ safely through recursive invocations of .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 , @@ -1127,7 +1190,7 @@ A common error is trying to use expressions like .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 @@ -1175,6 +1238,9 @@ The ODE convention is that 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 @@ -1196,6 +1262,8 @@ The path of the node which has the same name as the variable 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 @@ -1256,7 +1324,7 @@ For the purposes of the 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 @@ -1355,6 +1423,11 @@ except for internal variables (those that start with 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 @@ -1610,6 +1683,28 @@ or 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 . @@ -1630,6 +1725,9 @@ correspond to an actual file; it is always considered to be out of date, 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 @@ -1769,7 +1867,7 @@ could be built, unless 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 @@ -1846,8 +1944,8 @@ character when used outside of any quoting characters. 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 @@ -1868,7 +1966,7 @@ Example: .Bd -literal \&.SUFFIXES: .o \&.c.o: - cc -o ${.TARGET} -c ${.IMPSRC} + cc \-o ${.TARGET} \-c ${.IMPSRC} .Ed .El .Sh ENVIRONMENT @@ -1913,7 +2011,7 @@ however the special variables, variable modifiers and conditionals are not. .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 @@ -1921,6 +2019,21 @@ 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 @@ -1928,6 +2041,13 @@ A .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 diff --git a/commands/make/make.c b/usr.bin/make/make.c similarity index 97% rename from commands/make/make.c rename to usr.bin/make/make.c index 11e2ca8cb..704718c20 100644 --- a/commands/make/make.c +++ b/usr.bin/make/make.c @@ -1,4 +1,4 @@ -/* $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 @@ -69,14 +69,14 @@ */ #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 #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 @@ -94,12 +94,12 @@ __RCSID("$NetBSD: make.c,v 1.79 2010/04/07 00:11:27 sjg Exp $"); * * 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 @@ -139,7 +139,7 @@ static int MakeCheckOrder(void *, void *); 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; @@ -154,7 +154,7 @@ make_abort(GNode *gn, int line) /*- *----------------------------------------------------------------------- * 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: @@ -165,15 +165,15 @@ make_abort(GNode *gn, int line) * 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); } @@ -207,7 +207,7 @@ MakeTimeStamp(void *pgn, void *cgn) * 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. *----------------------------------------------------------------------- */ @@ -221,7 +221,7 @@ Make_OODate(GNode *gn) * 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)); @@ -265,7 +265,7 @@ Make_OODate(GNode *gn) * 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 @@ -293,21 +293,22 @@ Make_OODate(GNode *gn) } } 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 { @@ -330,6 +331,12 @@ Make_OODate(GNode *gn) 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 @@ -389,7 +396,7 @@ MakeAddChild(void *gnp, void *lp) * 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. *----------------------------------------------------------------------- */ @@ -399,7 +406,7 @@ MakeFindChild(void *gnp, void *pgnp) GNode *gn = (GNode *)gnp; GNode *pgn = (GNode *)pgnp; - (void)Dir_MTime(gn); + (void)Dir_MTime(gn, 0); Make_TimeStamp(pgn, gn); pgn->unmade--; @@ -567,7 +574,7 @@ MakeHandleUse(void *cgnp, void *pgnp) time_t Make_Recheck(GNode *gn) { - time_t mtime = Dir_MTime(gn); + time_t mtime = Dir_MTime(gn, 1); #ifndef RECHECK /* @@ -657,12 +664,12 @@ Make_Recheck(GNode *gn) * 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 @@ -1329,7 +1336,7 @@ Make_ExpandUse(Lst targs) *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); diff --git a/commands/make/make.h b/usr.bin/make/make.h similarity index 97% rename from commands/make/make.h rename to usr.bin/make/make.h index d770586d6..04b2ebc80 100644 --- a/commands/make/make.h +++ b/usr.bin/make/make.h @@ -1,4 +1,4 @@ -/* $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 @@ -111,6 +111,10 @@ #endif #endif +#if !defined(__dead) +#define __dead +#endif + #include "sprite.h" #include "lst.h" #include "hash.h" @@ -187,8 +191,7 @@ typedef struct GNode { 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 */ @@ -260,6 +263,9 @@ typedef struct GNode { #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 */ @@ -392,6 +398,7 @@ extern Boolean oldVars; /* Do old-style variable substitution */ 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 */ @@ -399,11 +406,7 @@ 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" @@ -437,6 +440,8 @@ extern int debug; #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 @@ -462,6 +467,7 @@ void PrintOnError(GNode *, const char *); 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) ({ \ diff --git a/commands/make/make_malloc.c b/usr.bin/make/make_malloc.c similarity index 92% rename from commands/make/make_malloc.c rename to usr.bin/make/make_malloc.c index ac90d265f..f0ccbc44b 100644 --- a/commands/make/make_malloc.c +++ b/usr.bin/make/make_malloc.c @@ -1,4 +1,4 @@ -/* $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. @@ -28,7 +28,7 @@ #ifdef MAKE_NATIVE #include -__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 @@ -48,7 +48,7 @@ enomem(void) { extern char *progname; - (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); + (void)fprintf(stderr, "%s: %s.\n", progname, strerror(ENOMEM)); exit(2); } diff --git a/commands/make/make_malloc.h b/usr.bin/make/make_malloc.h similarity index 100% rename from commands/make/make_malloc.h rename to usr.bin/make/make_malloc.h diff --git a/usr.bin/make/meta.c b/usr.bin/make/meta.c new file mode 100644 index 000000000..37a5b29c2 --- /dev/null +++ b/usr.bin/make/meta.c @@ -0,0 +1,1346 @@ +/* $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 +#include +#include +#include +#include +#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) +#include +#endif + +#include "make.h" +#include "job.h" + +#ifdef HAVE_FILEMON_H +# include +#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: + * + * + * + * Where: + * is a single letter, denoting the syscall. + * is the process that made the syscall. + * 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 */ diff --git a/usr.bin/make/meta.h b/usr.bin/make/meta.h new file mode 100644 index 000000000..1ce01ca90 --- /dev/null +++ b/usr.bin/make/meta.h @@ -0,0 +1,54 @@ +/* $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); diff --git a/commands/make/nonints.h b/usr.bin/make/nonints.h similarity index 96% rename from commands/make/nonints.h rename to usr.bin/make/nonints.h index f49759a70..5050e2a9c 100644 --- a/commands/make/nonints.h +++ b/usr.bin/make/nonints.h @@ -1,4 +1,4 @@ -/* $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 @@ -106,6 +106,9 @@ int For_Eval(char *); 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 *); @@ -118,9 +121,10 @@ void Punt(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 *, ...) @@ -132,7 +136,7 @@ void Parse_AddIncludeDir(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 */ diff --git a/commands/make/parse.c b/usr.bin/make/parse.c similarity index 87% rename from commands/make/parse.c rename to usr.bin/make/parse.c index 7c6538e09..ade527837 100644 --- a/commands/make/parse.c +++ b/usr.bin/make/parse.c @@ -1,4 +1,4 @@ -/* $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 @@ -69,14 +69,14 @@ */ #ifndef MAKE_NATIVE -static char rcsid[] = "$NetBSD: parse.c,v 1.164 2010/05/24 21:04:49 sjg Exp $"; +static char rcsid[] = "$NetBSD: parse.c,v 1.182 2012/03/31 00:12:24 christos Exp $"; #else #include #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 @@ -123,12 +123,23 @@ __RCSID("$NetBSD: parse.c,v 1.164 2010/05/24 21:04:49 sjg Exp $"); * Parse_MainName Returns a Lst of the main target to create. */ +#include +#include +#include +#include #include #include #include #include #include +#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" @@ -136,56 +147,36 @@ __RCSID("$NetBSD: parse.c,v 1.164 2010/05/24 21:04:49 sjg Exp $"); #include "buf.h" #include "pathnames.h" +//////////////////////////////////////////////////////////// +// types and constants + +/* + * Structure for a file being read ("included file") + */ +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 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 */ + 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; + + /* * 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 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 */ - void *nextbuf_arg; /* Opaque arg for nextbuf() */ -} IFile; - -#define IFILE_BUFLEN 0x8000 -static IFile *curFile; - +#define CONTINUE 1 +#define DONE 0 /* - * Definitions for handling #include specifications - */ - -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 */ @@ -196,10 +187,13 @@ typedef enum { 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 */ @@ -221,16 +215,74 @@ typedef enum { 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 @@ -238,7 +290,7 @@ static GNode *predecessor; * 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 */ @@ -258,7 +310,10 @@ static struct { { ".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 }, @@ -284,6 +339,9 @@ static struct { { ".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))); @@ -304,13 +362,220 @@ static void ParseSetParseFile(const char *); #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 /*- *---------------------------------------------------------------------- @@ -404,7 +669,7 @@ ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type, 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); @@ -915,7 +1180,7 @@ ParseDoDependency(char *line) tOp = 0; specType = Not; - paths = (Lst)NULL; + paths = NULL; curTargs = Lst_Init(FALSE); @@ -1085,7 +1350,7 @@ ParseDoDependency(char *line) &line[5]); goto out; } else { - if (paths == (Lst)NULL) { + if (paths == NULL) { paths = Lst_Init(FALSE); } (void)Lst_AtEnd(paths, path); @@ -1786,6 +2051,7 @@ Parse_AddIncludeDir(char *dir) 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; @@ -1876,8 +2142,12 @@ Parse_include_file(char *file, Boolean isSystem, int silent) 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 @@ -1949,23 +2219,27 @@ ParseDoInclude(char *line) 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); } /* @@ -2014,9 +2288,11 @@ ParseTrackInput(const char *name) *--------------------------------------------------------------------- */ 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; @@ -2047,33 +2323,22 @@ Parse_SetInput(const char *name, int line, int fd, char *(*nextbuf)(void *), voi 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); @@ -2143,6 +2408,55 @@ ParseTraditionalInclude(char *line) } #endif +#ifdef SYSVINCLUDE +/*- + *--------------------------------------------------------------------- + * ParseGmakeExport -- + * Parse export = + * + * 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 -- @@ -2162,26 +2476,31 @@ static int ParseEOF(void) { char *ptr; + size_t len; - 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; - } + 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); @@ -2195,8 +2514,8 @@ ParseEOF(void) } 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); @@ -2217,7 +2536,6 @@ ParseGetLine(int flags, int *length) char *escaped; char *comment; char *tp; - int len, dist; /* Loop through blank lines and comment lines */ for (;;) { @@ -2228,67 +2546,25 @@ ParseGetLine(int flags, int *length) 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 == '\\') { @@ -2303,7 +2579,9 @@ ParseGetLine(int flags, int *length) } 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') @@ -2459,7 +2737,7 @@ ParseReadLine(void) 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)); @@ -2522,11 +2800,19 @@ Parse_File(const char *name, int fd) { 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; ) { @@ -2619,6 +2905,17 @@ Parse_File(const char *name, int fd) 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(); @@ -2719,7 +3016,7 @@ Parse_File(const char *name, int fd) 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); diff --git a/commands/make/pathnames.h b/usr.bin/make/pathnames.h similarity index 100% rename from commands/make/pathnames.h rename to usr.bin/make/pathnames.h diff --git a/commands/make/sprite.h b/usr.bin/make/sprite.h similarity index 100% rename from commands/make/sprite.h rename to usr.bin/make/sprite.h diff --git a/commands/make/str.c b/usr.bin/make/str.c similarity index 97% rename from commands/make/str.c rename to usr.bin/make/str.c index f1b93a0a1..bc324b8c9 100644 --- a/commands/make/str.c +++ b/usr.bin/make/str.c @@ -1,4 +1,4 @@ -/* $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 @@ -69,14 +69,14 @@ */ #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 #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 @@ -319,6 +319,8 @@ Str_FindSubstring(const char *string, const char *substring) * 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 diff --git a/commands/make/strlist.c b/usr.bin/make/strlist.c similarity index 100% rename from commands/make/strlist.c rename to usr.bin/make/strlist.c diff --git a/commands/make/strlist.h b/usr.bin/make/strlist.h similarity index 100% rename from commands/make/strlist.h rename to usr.bin/make/strlist.h diff --git a/commands/make/suff.c b/usr.bin/make/suff.c similarity index 99% rename from commands/make/suff.c rename to usr.bin/make/suff.c index a4cd626d3..6abdeb045 100644 --- a/commands/make/suff.c +++ b/usr.bin/make/suff.c @@ -1,4 +1,4 @@ -/* $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 @@ -69,14 +69,14 @@ */ #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 #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 @@ -136,7 +136,6 @@ __RCSID("$NetBSD: suff.c,v 1.67 2009/01/23 21:58:28 dsl Exp $"); */ #include -#include #include "make.h" #include "hash.h" #include "dir.h" @@ -1435,7 +1434,7 @@ SuffFindCmds(Src *targ, Lst slst) * 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; @@ -2407,16 +2406,25 @@ Suff_FindDeps(GNode *gn) 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); } diff --git a/commands/make/targ.c b/usr.bin/make/targ.c similarity index 98% rename from commands/make/targ.c rename to usr.bin/make/targ.c index b0f63d9e7..20fe05ca3 100644 --- a/commands/make/targ.c +++ b/usr.bin/make/targ.c @@ -1,4 +1,4 @@ -/* $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 @@ -69,14 +69,14 @@ */ #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 #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 @@ -130,7 +130,6 @@ __RCSID("$NetBSD: targ.c,v 1.55 2009/01/23 21:26:30 dsl Exp $"); */ #include -#include #include #include "make.h" @@ -249,8 +248,9 @@ Targ_NewGN(const char *name) 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); diff --git a/commands/make/trace.c b/usr.bin/make/trace.c similarity index 94% rename from commands/make/trace.c rename to usr.bin/make/trace.c index 0640e5bf9..267177ff5 100644 --- a/commands/make/trace.c +++ b/usr.bin/make/trace.c @@ -96,17 +96,10 @@ Trace_Log(TrEvent event, Job *job) 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); diff --git a/commands/make/trace.h b/usr.bin/make/trace.h similarity index 100% rename from commands/make/trace.h rename to usr.bin/make/trace.h diff --git a/commands/make/unit-tests/Makefile b/usr.bin/make/unit-tests/Makefile similarity index 83% rename from commands/make/unit-tests/Makefile rename to usr.bin/make/unit-tests/Makefile index b3fd0a818..05ccd4f0f 100644 --- a/commands/make/unit-tests/Makefile +++ b/usr.bin/make/unit-tests/Makefile @@ -1,4 +1,4 @@ -# $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: @@ -21,18 +21,24 @@ UNIT_TESTS:= ${.PARSEDIR} 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 \ @@ -40,18 +46,26 @@ SUBFILES= \ 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 +.-include 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' diff --git a/commands/make/unit-tests/comment b/usr.bin/make/unit-tests/comment similarity index 100% rename from commands/make/unit-tests/comment rename to usr.bin/make/unit-tests/comment diff --git a/commands/make/unit-tests/cond1 b/usr.bin/make/unit-tests/cond1 similarity index 92% rename from commands/make/unit-tests/cond1 rename to usr.bin/make/unit-tests/cond1 index cf51b5c52..7d3df40dc 100644 --- a/commands/make/unit-tests/cond1 +++ b/usr.bin/make/unit-tests/cond1 @@ -1,4 +1,4 @@ -# $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 @@ -95,6 +95,11 @@ C=clever 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}'" diff --git a/usr.bin/make/unit-tests/doterror b/usr.bin/make/unit-tests/doterror new file mode 100644 index 000000000..ec63321c8 --- /dev/null +++ b/usr.bin/make/unit-tests/doterror @@ -0,0 +1,20 @@ +# $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 + diff --git a/commands/make/unit-tests/dotwait b/usr.bin/make/unit-tests/dotwait similarity index 100% rename from commands/make/unit-tests/dotwait rename to usr.bin/make/unit-tests/dotwait diff --git a/usr.bin/make/unit-tests/error b/usr.bin/make/unit-tests/error new file mode 100644 index 000000000..05c3d86ff --- /dev/null +++ b/usr.bin/make/unit-tests/error @@ -0,0 +1,10 @@ +# $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 diff --git a/commands/make/unit-tests/export b/usr.bin/make/unit-tests/export similarity index 100% rename from commands/make/unit-tests/export rename to usr.bin/make/unit-tests/export diff --git a/usr.bin/make/unit-tests/export-all b/usr.bin/make/unit-tests/export-all new file mode 100644 index 000000000..d1c79e2d3 --- /dev/null +++ b/usr.bin/make/unit-tests/export-all @@ -0,0 +1,23 @@ +# $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 diff --git a/commands/make/unit-tests/forsubst b/usr.bin/make/unit-tests/forsubst similarity index 100% rename from commands/make/unit-tests/forsubst rename to usr.bin/make/unit-tests/forsubst diff --git a/usr.bin/make/unit-tests/hash b/usr.bin/make/unit-tests/hash new file mode 100644 index 000000000..1ed84e776 --- /dev/null +++ b/usr.bin/make/unit-tests/hash @@ -0,0 +1,18 @@ +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} diff --git a/usr.bin/make/unit-tests/misc b/usr.bin/make/unit-tests/misc new file mode 100644 index 000000000..bb54df488 --- /dev/null +++ b/usr.bin/make/unit-tests/misc @@ -0,0 +1,16 @@ +# $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 diff --git a/commands/make/unit-tests/moderrs b/usr.bin/make/unit-tests/moderrs similarity index 100% rename from commands/make/unit-tests/moderrs rename to usr.bin/make/unit-tests/moderrs diff --git a/commands/make/unit-tests/modmatch b/usr.bin/make/unit-tests/modmatch similarity index 100% rename from commands/make/unit-tests/modmatch rename to usr.bin/make/unit-tests/modmatch diff --git a/commands/make/unit-tests/modmisc b/usr.bin/make/unit-tests/modmisc similarity index 74% rename from commands/make/unit-tests/modmisc rename to usr.bin/make/unit-tests/modmisc index 11f22eaed..bb5375587 100644 --- a/commands/make/unit-tests/modmisc +++ b/usr.bin/make/unit-tests/modmisc @@ -1,8 +1,10 @@ -# $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 @@ -13,7 +15,10 @@ MOD_HOMES=S,/home/,/homes/, 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}'" diff --git a/commands/make/unit-tests/modorder b/usr.bin/make/unit-tests/modorder similarity index 100% rename from commands/make/unit-tests/modorder rename to usr.bin/make/unit-tests/modorder diff --git a/usr.bin/make/unit-tests/modts b/usr.bin/make/unit-tests/modts new file mode 100644 index 000000000..616bd8944 --- /dev/null +++ b/usr.bin/make/unit-tests/modts @@ -0,0 +1,43 @@ + +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?' diff --git a/commands/make/unit-tests/modword b/usr.bin/make/unit-tests/modword similarity index 100% rename from commands/make/unit-tests/modword rename to usr.bin/make/unit-tests/modword diff --git a/usr.bin/make/unit-tests/phony-end b/usr.bin/make/unit-tests/phony-end new file mode 100644 index 000000000..f7e627c70 --- /dev/null +++ b/usr.bin/make/unit-tests/phony-end @@ -0,0 +1,9 @@ +# $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 diff --git a/commands/make/unit-tests/posix b/usr.bin/make/unit-tests/posix similarity index 100% rename from commands/make/unit-tests/posix rename to usr.bin/make/unit-tests/posix diff --git a/commands/make/unit-tests/qequals b/usr.bin/make/unit-tests/qequals similarity index 100% rename from commands/make/unit-tests/qequals rename to usr.bin/make/unit-tests/qequals diff --git a/usr.bin/make/unit-tests/sysv b/usr.bin/make/unit-tests/sysv new file mode 100644 index 000000000..2d304f6b4 --- /dev/null +++ b/usr.bin/make/unit-tests/sysv @@ -0,0 +1,26 @@ +# $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}} diff --git a/commands/make/unit-tests/ternary b/usr.bin/make/unit-tests/ternary similarity index 100% rename from commands/make/unit-tests/ternary rename to usr.bin/make/unit-tests/ternary diff --git a/commands/make/unit-tests/test.exp b/usr.bin/make/unit-tests/test.exp similarity index 89% rename from commands/make/unit-tests/test.exp rename to usr.bin/make/unit-tests/test.exp index c242a040d..cbb2912a0 100644 --- a/commands/make/unit-tests/test.exp +++ b/usr.bin/make/unit-tests/test.exp @@ -24,20 +24,33 @@ make: warning: String comparison operator should be either == or != 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 @@ -68,6 +81,14 @@ make: Graph cycles through `cycle.2.97' 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= @@ -99,14 +120,15 @@ LIB=e X_LIBS:M${LIB${LIB:tu}} is "/tmp/libe.a" 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 @@ -270,6 +292,11 @@ LIST:tw:C/ /,/g="one two three four five six" 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, @@ -293,6 +320,12 @@ Now we expect an error... *** 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 @@ -314,3 +347,5 @@ five FU=bar FOO=goo VAR=Internal 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) diff --git a/commands/make/unit-tests/unexport b/usr.bin/make/unit-tests/unexport similarity index 100% rename from commands/make/unit-tests/unexport rename to usr.bin/make/unit-tests/unexport diff --git a/commands/make/unit-tests/unexport-env b/usr.bin/make/unit-tests/unexport-env similarity index 100% rename from commands/make/unit-tests/unexport-env rename to usr.bin/make/unit-tests/unexport-env diff --git a/commands/make/unit-tests/varcmd b/usr.bin/make/unit-tests/varcmd similarity index 100% rename from commands/make/unit-tests/varcmd rename to usr.bin/make/unit-tests/varcmd diff --git a/commands/make/util.c b/usr.bin/make/util.c similarity index 97% rename from commands/make/util.c rename to usr.bin/make/util.c index 3c27089d3..e50f2a3b4 100644 --- a/commands/make/util.c +++ b/usr.bin/make/util.c @@ -1,15 +1,15 @@ -/* $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 #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 @@ -49,7 +49,7 @@ findenv(const char *name, int *offset) 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) { diff --git a/commands/make/var.c b/usr.bin/make/var.c similarity index 96% rename from commands/make/var.c rename to usr.bin/make/var.c index d644674c7..d66e5aa0d 100644 --- a/commands/make/var.c +++ b/usr.bin/make/var.c @@ -1,4 +1,4 @@ -/* $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 @@ -69,14 +69,14 @@ */ #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 #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 @@ -129,8 +129,10 @@ __RCSID("$NetBSD: var.c,v 1.159 2010/06/06 01:13:12 sjg Exp $"); #include #endif #include +#include #include #include +#include #include "make.h" #include "buf.h" @@ -302,6 +304,7 @@ static char *VarGetPattern(GNode *, Var_Parse_State *, 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 *), @@ -379,6 +382,12 @@ VarFind(const char *name, GNode *ctxt, int flags) 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, @@ -2254,6 +2263,79 @@ VarQuote(char *str) 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 -- @@ -2285,6 +2367,21 @@ VarChangeCase(char *str, int upper) 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: @@ -2370,6 +2467,10 @@ VarChangeCase(char *str, int upper) * 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, @@ -2397,14 +2498,28 @@ ApplyModifiers(char *nstr, const char *tstr, 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); @@ -2436,6 +2551,7 @@ ApplyModifiers(char *nstr, const char *tstr, } continue; } + apply_mods: if (DEBUG(VAR)) { fprintf(debug_file, "Applying :%c to \"%s\"\n", *tstr, nstr); } @@ -2486,7 +2602,7 @@ ApplyModifiers(char *nstr, const char *tstr, cp = ++tstr; break; } - delim = BRCLOSE; + delim = startc == PROPEN ? PRCLOSE : BRCLOSE; pattern.flags = 0; pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, @@ -2815,6 +2931,36 @@ ApplyModifiers(char *nstr, const char *tstr, } } + 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 */ @@ -3340,9 +3486,13 @@ ApplyModifiers(char *nstr, const char *tstr, */ 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 @@ -3740,7 +3890,7 @@ Var_Parse(const char *str, GNode *ctxt, Boolean errnum, int *lengthPtr, nstr = bmake_strndup(start, *lengthPtr); *freePtr = nstr; } else { - nstr = var_Error; + nstr = errnum ? var_Error : varNoError; } } if (nstr != Buf_GetAll(&v->val, NULL))