summaryrefslogtreecommitdiff
path: root/usr.bin/make
diff options
context:
space:
mode:
authorcgd <cgd@NetBSD.org>1994-03-05 00:34:29 +0000
committercgd <cgd@NetBSD.org>1994-03-05 00:34:29 +0000
commit3db59563ee7dca190481dfee041222f12ed3a64f (patch)
treeee00f5ed3bf2662f98775537e76f6ba74977a1f6 /usr.bin/make
parent3e02952c52fd23241dc58d7a0165a865dd508bcd (diff)
fixes/improvements from Christos Zoulas <christos@deshaw.com>.
Diffstat (limited to 'usr.bin/make')
-rw-r--r--usr.bin/make/Makefile8
-rw-r--r--usr.bin/make/Makefile.boot15
-rw-r--r--usr.bin/make/arch.c39
-rw-r--r--usr.bin/make/bit.h4
-rw-r--r--usr.bin/make/buf.c24
-rw-r--r--usr.bin/make/buf.h33
-rw-r--r--usr.bin/make/compat.c45
-rw-r--r--usr.bin/make/cond.c183
-rw-r--r--usr.bin/make/config.h6
-rw-r--r--usr.bin/make/dir.c56
-rw-r--r--usr.bin/make/dir.h70
-rw-r--r--usr.bin/make/for.c294
-rw-r--r--usr.bin/make/hash.c17
-rw-r--r--usr.bin/make/hash.h26
-rw-r--r--usr.bin/make/job.c114
-rw-r--r--usr.bin/make/job.h33
-rw-r--r--usr.bin/make/list.h4
-rw-r--r--usr.bin/make/lst.h5
-rw-r--r--usr.bin/make/main.c215
-rw-r--r--usr.bin/make/make.177
-rw-r--r--usr.bin/make/make.c19
-rw-r--r--usr.bin/make/make.h22
-rw-r--r--usr.bin/make/nonints.h180
-rw-r--r--usr.bin/make/parse.c614
-rw-r--r--usr.bin/make/sprite.h4
-rw-r--r--usr.bin/make/str.c116
-rw-r--r--usr.bin/make/suff.c97
-rw-r--r--usr.bin/make/targ.c16
-rw-r--r--usr.bin/make/tutorial.ms3385
-rw-r--r--usr.bin/make/util.c325
-rw-r--r--usr.bin/make/var.c208
31 files changed, 5566 insertions, 688 deletions
diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile
index e9af6d26a54..c2eb7d9b8b1 100644
--- a/usr.bin/make/Makefile
+++ b/usr.bin/make/Makefile
@@ -1,10 +1,10 @@
# from: @(#)Makefile 5.2 (Berkeley) 12/28/90
-# $Id: Makefile,v 1.3 1993/12/14 18:27:15 jtc Exp $
+# $Id: Makefile,v 1.4 1994/03/05 00:34:29 cgd Exp $
PROG= make
-CFLAGS+=-I${.CURDIR} -DPOSIX
-SRCS= arch.c buf.c compat.c cond.c dir.c hash.c job.c main.c \
- make.c parse.c str.c suff.c targ.c var.c
+CFLAGS+= -I${.CURDIR} -DPOSIX
+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 var.c util.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 \
diff --git a/usr.bin/make/Makefile.boot b/usr.bin/make/Makefile.boot
new file mode 100644
index 00000000000..bbdf2b4e87d
--- /dev/null
+++ b/usr.bin/make/Makefile.boot
@@ -0,0 +1,15 @@
+# a very simple makefile...
+# $Id: Makefile.boot,v 1.1 1994/03/05 00:34:30 cgd Exp $
+
+CC=gcc
+CFLAGS=-Wall -I. -g -O -DMACHINE=\"sun4\" -Dnotyet
+
+OBJ=arch.o buf.o compat.o cond.o dir.o hash.o job.o main.o make.o \
+ parse.o str.o suff.o targ.o var.o util.o
+
+pmake: ${OBJ}
+ @echo 'make of make and make.0 started.'
+ (cd lst.lib; make)
+ ${CC} *.o lst.lib/*.o -o pmake
+# nroff -h -man make.1 > make.0
+# @echo 'make of make and make.0 completed.'
diff --git a/usr.bin/make/arch.c b/usr.bin/make/arch.c
index 9cbae3042e4..3dbd3f7b3ed 100644
--- a/usr.bin/make/arch.c
+++ b/usr.bin/make/arch.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)arch.c 5.7 (Berkeley) 12/28/90";*/
-static char rcsid[] = "$Id: arch.c,v 1.4 1994/01/13 21:01:40 jtc Exp $";
+/* from: static char sccsid[] = "@(#)arch.c 5.7 (Berkeley) 12/28/90"; */
+static char *rcsid = "$Id: arch.c,v 1.5 1994/03/05 00:34:32 cgd Exp $";
#endif /* not lint */
/*-
@@ -94,13 +94,10 @@ static char rcsid[] = "$Id: arch.c,v 1.4 1994/01/13 21:01:40 jtc Exp $";
#include <ar.h>
#include <ranlib.h>
#include <stdio.h>
-#include <stdlib.h>
#include "make.h"
#include "hash.h"
-
-#ifndef RANLIBMAG
-#define RANLIBMAG "__.SYMDEF"
-#endif
+#include "dir.h"
+#include "config.h"
static Lst archives; /* Lst of archives we've already examined */
@@ -110,7 +107,9 @@ typedef struct Arch {
* by <name, struct ar_hdr *> key/value pairs */
} Arch;
-static FILE *ArchFindMember();
+static int ArchFindArchive __P((Arch *, char *));
+static struct ar_hdr *ArchStatMember __P((char *, char *, Boolean));
+static FILE *ArchFindMember __P((char *, char *, struct ar_hdr *, char *));
/*-
*-----------------------------------------------------------------------
@@ -139,7 +138,7 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt)
GNode *gn; /* New node */
char *libName; /* Library-part of specification */
char *memName; /* Member-part of specification */
- char nameBuf[BSIZE]; /* temporary place for node name */
+ char nameBuf[MAKE_BSIZE]; /* temporary place for node name */
char saveChar; /* Ending delimiter of member-name */
Boolean subLibName; /* TRUE if libName should have/had
* variable substitution performed on it */
@@ -174,11 +173,11 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt)
*cp++ = '\0';
if (subLibName) {
- libName = Var_Subst(libName, ctxt, TRUE);
+ libName = Var_Subst(NULL, libName, ctxt, TRUE);
}
- while (1) {
+ for (;;) {
/*
* First skip to the start of the member's name, mark that
* place and skip to the end of it (either white-space or
@@ -253,7 +252,7 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt)
char *sacrifice;
char *oldMemName = memName;
- memName = Var_Subst(memName, ctxt, TRUE);
+ memName = Var_Subst(NULL, memName, ctxt, TRUE);
/*
* Now form an archive spec and recurse to deal with nested
@@ -264,7 +263,7 @@ Arch_ParseArchive (linePtr, nodeLst, ctxt)
sprintf(buf, "%s(%s)", libName, memName);
- if (index(memName, '$') && strcmp(memName, oldMemName) == 0) {
+ if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) {
/*
* Must contain dynamic sources, so we can't deal with it now.
* Just create an ARCHV node for the thing and let
@@ -433,7 +432,7 @@ ArchStatMember (archive, member, hash)
* to point 'member' to the final component, if there is one, to make
* the comparisons easier...
*/
- cp = rindex (member, '/');
+ cp = strrchr (member, '/');
if (cp != (char *) NULL) {
member = cp + 1;
}
@@ -522,7 +521,7 @@ ArchStatMember (archive, member, hash)
he = Hash_CreateEntry (&ar->members, strdup (memName),
(Boolean *)NULL);
Hash_SetValue (he, (ClientData)emalloc (sizeof (struct ar_hdr)));
- bcopy ((Address)&arh, (Address)Hash_GetValue (he),
+ memcpy ((Address)Hash_GetValue (he), (Address)&arh,
sizeof (struct ar_hdr));
}
/*
@@ -605,7 +604,7 @@ ArchFindMember (archive, member, arhPtr, mode)
* to point 'member' to the final component, if there is one, to make
* the comparisons easier...
*/
- cp = rindex (member, '/');
+ cp = strrchr (member, '/');
if (cp != (char *) NULL) {
member = cp + 1;
}
@@ -690,7 +689,7 @@ Arch_Touch (gn)
arch = ArchFindMember(Var_Value (ARCHIVE, gn),
Var_Value (TARGET, gn),
&arh, "r+");
- sprintf(arh.ar_date, "%-12d", now);
+ sprintf(arh.ar_date, "%-12ld", (long) now);
if (arch != (FILE *) NULL) {
(void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch);
@@ -722,7 +721,7 @@ Arch_TouchLib (gn)
struct timeval times[2]; /* Times for utimes() call */
arch = ArchFindMember (gn->path, RANLIBMAG, &arh, "r+");
- sprintf(arh.ar_date, "%-12d", now);
+ sprintf(arh.ar_date, "%-12ld", (long) now);
if (arch != (FILE *) NULL) {
(void)fwrite ((char *)&arh, sizeof (struct ar_hdr), 1, arch);
@@ -806,8 +805,8 @@ Arch_MemMTime (gn)
* child. We keep searching its parents in case some other
* parent requires this child to exist...
*/
- nameStart = index (pgn->name, '(') + 1;
- nameEnd = index (nameStart, ')');
+ nameStart = strchr (pgn->name, '(') + 1;
+ nameEnd = strchr (nameStart, ')');
if (pgn->make &&
strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) {
diff --git a/usr.bin/make/bit.h b/usr.bin/make/bit.h
index 76bad137b81..461761032f3 100644
--- a/usr.bin/make/bit.h
+++ b/usr.bin/make/bit.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)bit.h 5.3 (Berkeley) 6/1/90
- * $Id: bit.h,v 1.2 1993/08/01 18:12:06 mycroft Exp $
+ * $Id: bit.h,v 1.3 1994/03/05 00:34:33 cgd Exp $
*/
/*
@@ -98,4 +98,4 @@ extern Boolean Bit_Union();
extern Boolean Bit_AnySet();
extern int *Bit_Expand();
-#endif _BIT
+#endif /* _BIT */
diff --git a/usr.bin/make/buf.c b/usr.bin/make/buf.c
index ea578cfdb9b..9a720acfd0a 100644
--- a/usr.bin/make/buf.c
+++ b/usr.bin/make/buf.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)buf.c 5.5 (Berkeley) 12/28/90";*/
-static char rcsid[] = "$Id: buf.c,v 1.3 1994/01/13 21:01:42 jtc Exp $";
+/* from: static char sccsid[] = "@(#)buf.c 5.5 (Berkeley) 12/28/90"; */
+static char *rcsid = "$Id: buf.c,v 1.4 1994/03/05 00:34:34 cgd Exp $";
#endif /* not lint */
/*-
@@ -46,9 +46,8 @@ static char rcsid[] = "$Id: buf.c,v 1.3 1994/01/13 21:01:42 jtc Exp $";
* Functions for automatically-expanded buffers.
*/
-#include <stdlib.h>
-#include <string.h>
#include "sprite.h"
+#include "make.h"
#include "buf.h"
#ifndef max
@@ -94,7 +93,7 @@ static char rcsid[] = "$Id: buf.c,v 1.3 1994/01/13 21:01:42 jtc Exp $";
void
Buf_OvAddByte (bp, byte)
register Buffer bp;
- Byte byte;
+ int byte;
{
bp->left = 0;
@@ -131,7 +130,7 @@ Buf_AddBytes (bp, numBytes, bytesPtr)
BufExpand (bp, numBytes);
- bcopy (bytesPtr, bp->inPtr, numBytes);
+ memcpy (bp->inPtr, bytesPtr, numBytes);
bp->inPtr += numBytes;
bp->left -= numBytes;
@@ -157,7 +156,7 @@ Buf_AddBytes (bp, numBytes, bytesPtr)
void
Buf_UngetByte (bp, byte)
register Buffer bp;
- Byte byte;
+ int byte;
{
if (bp->outPtr != bp->buffer) {
@@ -179,8 +178,7 @@ Buf_UngetByte (bp, byte)
Byte *newBuf;
newBuf = (Byte *)emalloc(bp->size + BUF_UNGET_INC);
- bcopy ((char *)bp->outPtr,
- (char *)(newBuf+BUF_UNGET_INC), numBytes+1);
+ memcpy ((char *)(newBuf+BUF_UNGET_INC), (char *)bp->outPtr, numBytes+1);
bp->outPtr = newBuf + BUF_UNGET_INC;
bp->inPtr = bp->outPtr + numBytes;
free ((char *)bp->buffer);
@@ -214,7 +212,7 @@ Buf_UngetBytes (bp, numBytes, bytesPtr)
if (bp->outPtr - bp->buffer >= numBytes) {
bp->outPtr -= numBytes;
- bcopy (bytesPtr, bp->outPtr, numBytes);
+ memcpy (bp->outPtr, bytesPtr, numBytes);
} else if (bp->outPtr == bp->inPtr) {
Buf_AddBytes (bp, numBytes, bytesPtr);
} else {
@@ -223,7 +221,7 @@ Buf_UngetBytes (bp, numBytes, bytesPtr)
int newBytes = max(numBytes,BUF_UNGET_INC);
newBuf = (Byte *)emalloc (bp->size + newBytes);
- bcopy((char *)bp->outPtr, (char *)(newBuf+newBytes), curNumBytes+1);
+ memcpy((char *)(newBuf+newBytes), (char *)bp->outPtr, curNumBytes+1);
bp->outPtr = newBuf + newBytes;
bp->inPtr = bp->outPtr + curNumBytes;
free ((char *)bp->buffer);
@@ -231,7 +229,7 @@ Buf_UngetBytes (bp, numBytes, bytesPtr)
bp->size += newBytes;
bp->left = bp->size - (bp->inPtr - bp->buffer);
bp->outPtr -= numBytes;
- bcopy ((char *)bytesPtr, (char *)bp->outPtr, numBytes);
+ memcpy ((char *)bp->outPtr, (char *)bytesPtr, numBytes);
}
}
@@ -293,7 +291,7 @@ Buf_GetBytes (bp, numBytes, bytesPtr)
if (bp->inPtr - bp->outPtr < numBytes) {
numBytes = bp->inPtr - bp->outPtr;
}
- bcopy (bp->outPtr, bytesPtr, numBytes);
+ memcpy (bytesPtr, bp->outPtr, numBytes);
bp->outPtr += numBytes;
if (bp->outPtr == bp->inPtr) {
diff --git a/usr.bin/make/buf.h b/usr.bin/make/buf.h
index c025f4f9d90..85e1ba8e830 100644
--- a/usr.bin/make/buf.h
+++ b/usr.bin/make/buf.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)buf.h 5.4 (Berkeley) 12/28/90
- * $Id: buf.h,v 1.2 1993/08/01 18:12:06 mycroft Exp $
+ * $Id: buf.h,v 1.3 1994/03/05 00:34:36 cgd Exp $
*/
/*-
@@ -59,24 +59,23 @@ typedef struct Buffer {
Byte *outPtr; /* Place to read from */
} *Buffer;
-Buffer Buf_Init(); /* Initialize a buffer */
-void Buf_Destroy(); /* Destroy a buffer */
-void Buf_AddBytes(); /* Add a range of bytes to a buffer */
-int Buf_GetByte(); /* Get a byte from a buffer */
-int Buf_GetBytes(); /* Get multiple bytes */
-void Buf_UngetByte(); /* Push a byte back into the buffer */
-void Buf_UngetBytes(); /* Push many bytes back into the buf */
-Byte *Buf_GetAll(); /* Get them all */
-void Buf_Discard(); /* Throw away some of the bytes */
-int Buf_Size(); /* See how many are there */
-
/* Buf_AddByte adds a single byte to a buffer. */
#define Buf_AddByte(bp, byte) \
- (--(bp)->left <= 0 ? Buf_OvAddByte(bp, byte) : \
- (void)(*(bp)->inPtr++ = (byte), *(bp)->inPtr = 0))
-
-void Buf_OvAddByte(); /* adds a byte when buffer overflows */
+ (void) (--(bp)->left <= 0 ? Buf_OvAddByte(bp, byte), 1 : \
+ (*(bp)->inPtr++ = (byte), *(bp)->inPtr = 0), 1)
#define BUF_ERROR 256
-#endif _BUF_H
+void Buf_OvAddByte __P((Buffer, int));
+void Buf_AddBytes __P((Buffer, int, Byte *));
+void Buf_UngetByte __P((Buffer, int));
+void Buf_UngetBytes __P((Buffer, int, Byte *));
+int Buf_GetByte __P((Buffer));
+int Buf_GetBytes __P((Buffer, int, Byte *));
+Byte *Buf_GetAll __P((Buffer, int *));
+void Buf_Discard __P((Buffer, int));
+int Buf_Size __P((Buffer));
+Buffer Buf_Init __P((int));
+void Buf_Destroy __P((Buffer, Boolean));
+
+#endif /* _BUF_H */
diff --git a/usr.bin/make/compat.c b/usr.bin/make/compat.c
index bfe6fcff919..b2ffb4c3023 100644
--- a/usr.bin/make/compat.c
+++ b/usr.bin/make/compat.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)compat.c 5.7 (Berkeley) 3/1/91";*/
-static char rcsid[] = "$Id: compat.c,v 1.4 1994/01/13 21:01:44 jtc Exp $";
+/* from: static char sccsid[] = "@(#)compat.c 5.7 (Berkeley) 3/1/91"; */
+static char *rcsid = "$Id: compat.c,v 1.5 1994/03/05 00:34:37 cgd Exp $";
#endif /* not lint */
/*-
@@ -59,10 +59,12 @@ static char rcsid[] = "$Id: compat.c,v 1.4 1994/01/13 21:01:44 jtc Exp $";
#include <sys/signal.h>
#include <sys/wait.h>
#include <sys/errno.h>
+#include <sys/stat.h>
#include <ctype.h>
-#include <sys/stat.h> /* 10 Aug 92*/
-#include <unistd.h>
#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
extern int errno;
/*
@@ -76,14 +78,15 @@ static char meta[256];
static GNode *curTarg = NILGNODE;
static GNode *ENDNode;
-static int CompatRunCommand();
+static void CompatInterrupt __P((int));
+static int CompatRunCommand __P((char *, GNode *));
+static int CompatMake __P((GNode *, GNode *));
/*-
*-----------------------------------------------------------------------
* CompatInterrupt --
* Interrupt the creation of the current target and remove it if
* it ain't precious.
- * Don't unlink it if it is a directory XXX 10 Aug 92
*
* Results:
* None.
@@ -99,16 +102,14 @@ CompatInterrupt (signo)
int signo;
{
GNode *gn;
- struct stat sbuf; /* 10 Aug 92*/
if ((curTarg != NILGNODE) && !Targ_Precious (curTarg)) {
char *file = Var_Value (TARGET, curTarg);
+ struct stat st;
- stat (file, &sbuf);
- if (!(sbuf.st_mode & S_IFDIR)) {
- if (unlink (file) == SUCCESS) {
- printf ("*** %s removed\n", file);
- }
+ if (lstat(file, &st) != -1 && !S_ISDIR(st.st_mode) &&
+ unlink(file) != -1) {
+ printf ("*** %s removed\n", file);
}
/*
@@ -150,7 +151,6 @@ CompatRunCommand (cmd, gn)
union wait reason; /* Reason for child's death */
int status; /* Description of child's death */
int cpid; /* Child actually found */
- int numWritten; /* Number of bytes written for error message */
ReturnStatus stat; /* Status of fork */
LstNode cmdNode; /* Node where current command is located */
char **av; /* Argument vector for thing to exec */
@@ -163,7 +163,7 @@ CompatRunCommand (cmd, gn)
errCheck = !(gn->type & OP_IGNORE);
cmdNode = Lst_Member (gn->commands, (ClientData)cmd);
- cmdStart = Var_Subst (cmd, gn, FALSE);
+ cmdStart = Var_Subst (NULL, cmd, gn, FALSE);
/*
* brk_string will return an argv with a NULL in av[1], thus causing
@@ -197,14 +197,15 @@ CompatRunCommand (cmd, gn)
cmd++;
}
- while (isspace(*cmd)) cmd++;
+ while (isspace((unsigned char)*cmd))
+ cmd++;
/*
* Search for meta characters in the command. If there are no meta
* characters, there's no need to execute a shell to execute the
* command.
*/
- for (cp = cmd; !meta[*cp]; cp++) {
+ for (cp = cmd; !meta[(unsigned char)*cp]; cp++) {
continue;
}
@@ -262,8 +263,8 @@ CompatRunCommand (cmd, gn)
if (cpid == 0) {
if (local) {
execvp(av[0], av);
- numWritten = write (2, av[0], strlen (av[0]));
- numWritten = write (2, ": not found\n", sizeof(": not found"));
+ (void) write (2, av[0], strlen (av[0]));
+ (void) write (2, ": not found\n", sizeof(": not found"));
} else {
(void)execv(av[0], av);
}
@@ -493,6 +494,8 @@ CompatMake (gn, pgn)
if (noExecute || Dir_MTime(gn) == 0) {
gn->mtime = now;
}
+ if (gn->cmtime > gn->mtime)
+ gn->mtime = gn->cmtime;
if (DEBUG(MAKE)) {
printf("update time: %s\n", Targ_FmtTime(gn->mtime));
}
@@ -534,6 +537,8 @@ CompatMake (gn, pgn)
Make_TimeStamp(pgn, gn);
}
break;
+ default:
+ break;
}
}
@@ -558,7 +563,7 @@ Compat_Run(targs)
Lst targs; /* List of target nodes to re-create */
{
char *cp; /* Pointer to string of shell meta-characters */
- GNode *gn; /* Current root target */
+ GNode *gn = NULL;/* Current root target */
int errors; /* Number of targets not remade due to errors */
if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
@@ -575,7 +580,7 @@ Compat_Run(targs)
}
for (cp = "#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) {
- meta[*cp] = 1;
+ meta[(unsigned char) *cp] = 1;
}
/*
* The null character serves as a sentinel in the string.
diff --git a/usr.bin/make/cond.c b/usr.bin/make/cond.c
index d84b2e9a9ea..94ee378f61b 100644
--- a/usr.bin/make/cond.c
+++ b/usr.bin/make/cond.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)cond.c 5.6 (Berkeley) 6/1/90";*/
-static char rcsid[] = "$Id: cond.c,v 1.3 1994/01/13 21:01:45 jtc Exp $";
+/* from: static char sccsid[] = "@(#)cond.c 5.6 (Berkeley) 6/1/90"; */
+static char *rcsid = "$Id: cond.c,v 1.4 1994/03/05 00:34:39 cgd Exp $";
#endif /* not lint */
/*-
@@ -50,11 +50,12 @@ static char rcsid[] = "$Id: cond.c,v 1.3 1994/01/13 21:01:45 jtc Exp $";
*
*/
-#include "make.h"
-#include <buf.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <ctype.h>
+#include <math.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "buf.h"
/*
* The parsing of conditional expressions is based on this grammar:
@@ -94,8 +95,17 @@ typedef enum {
* Structures to handle elegantly the different forms of #if's. The
* last two fields are stored in condInvert and condDefProc, respectively.
*/
-static Boolean CondDoDefined(),
- CondDoMake();
+static int CondGetArg __P((char **, char **, char *, Boolean));
+static Boolean CondDoDefined __P((int, char *));
+static int CondStrMatch __P((char *, char *));
+static Boolean CondDoMake __P((int, char *));
+static Boolean CondDoExists __P((int, char *));
+static Boolean CondDoTarget __P((int, char *));
+static Boolean CondCvtArg __P((char *, double *));
+static Token CondToken __P((Boolean));
+static Token CondT __P((Boolean));
+static Token CondF __P((Boolean));
+static Token CondE __P((Boolean));
static struct If {
char *form; /* Form of if */
@@ -103,12 +113,12 @@ static struct If {
Boolean doNot; /* TRUE if default function should be negated */
Boolean (*defProc)(); /* Default function to apply */
} ifs[] = {
- "ifdef", 5, FALSE, CondDoDefined,
- "ifndef", 6, TRUE, CondDoDefined,
- "ifmake", 6, FALSE, CondDoMake,
- "ifnmake", 7, TRUE, CondDoMake,
- "if", 2, FALSE, CondDoDefined,
- (char *)0, 0, FALSE, (Boolean (*)())0,
+ { "ifdef", 5, FALSE, CondDoDefined },
+ { "ifndef", 6, TRUE, CondDoDefined },
+ { "ifmake", 6, FALSE, CondDoMake },
+ { "ifnmake", 7, TRUE, CondDoMake },
+ { "if", 2, FALSE, CondDoDefined },
+ { (char *)0, 0, FALSE, (Boolean (*)())0 }
};
static Boolean condInvert; /* Invert the default function */
@@ -125,8 +135,6 @@ static int skipIfLevel=0; /* Depth of skipped conditionals */
static Boolean skipLine = FALSE; /* Whether the parse module is skipping
* lines */
-static Token CondT(), CondF(), CondE();
-
/*-
*-----------------------------------------------------------------------
* CondPushBack --
@@ -204,7 +212,7 @@ CondGetArg (linePtr, argPtr, func, parens)
*/
buf = Buf_Init(16);
- while ((index(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) {
+ while ((strchr(" \t)&|", *cp) == (char *)NULL) && (*cp != '\0')) {
if (*cp == '$') {
/*
* Parse the variable spec and install it as part of the argument
@@ -407,60 +415,45 @@ CondDoTarget (argLen, arg)
*-----------------------------------------------------------------------
* CondCvtArg --
* Convert the given number into a double. If the number begins
- * with 0x, or just x, it is interpreted as a hexadecimal integer
+ * with 0x, it is interpreted as a hexadecimal integer
* and converted to a double from there. All other strings just have
- * atof called on them.
+ * strtod called on them.
*
* Results:
- * The double value of string.
+ * Sets 'value' to double value of string.
+ * Returns true if the string was a valid number, false o.w.
*
* Side Effects:
+ * Can change 'value' even if string is not a valid number.
*
*
*-----------------------------------------------------------------------
*/
-static double
-CondCvtArg(str)
+static Boolean
+CondCvtArg(str, value)
register char *str;
+ double *value;
{
- int sign = 1;
- double atof();
-
- if (*str == '-') {
- sign = -1;
- str++;
- } else if (*str == '+') {
- str++;
- }
- if (((*str == '0') && (str[1] == 'x')) ||
- (*str == 'x'))
- {
- register int i;
-
- str += (*str == 'x') ? 1 : 2;
+ if ((*str == '0') && (str[1] == 'x')) {
+ register long i;
- i = 0;
-
- while (isxdigit(*str)) {
- i *= 16;
- if (*str <= '9') {
- i += *str - '0';
- } else if (*str <= 'F') {
- i += *str - 'A' + 10;
- } else {
- i += *str - 'a' + 10;
- }
- str++;
- }
- if (sign < 0) {
- return((double)(-i));
- } else {
- return((double)i);
+ for (str += 2, i = 0; *str; str++) {
+ int x;
+ if (isdigit((unsigned char) *str))
+ x = *str - '0';
+ else if (isxdigit((unsigned char) *str))
+ x = 10 + *str - isupper((unsigned char) *str) ? 'A' : 'a';
+ else
+ return FALSE;
+ i = (i << 4) + x;
}
- } else if (sign < 0) {
- return(- atof(str));
- } else {
- return(atof(str));
+ *value = (double) i;
+ return TRUE;
+ }
+ else {
+ char *eptr;
+ *value = strtod(str, &eptr);
+ return *eptr == '\0';
}
}
@@ -540,12 +533,34 @@ CondToken(doEval)
}
condExpr += varSpecLen;
+ if (!isspace(*condExpr) && strchr("!=><", *condExpr) == NULL) {
+ Buffer buf;
+ char *cp;
+
+ buf = Buf_Init(0);
+
+ for (cp = lhs; *cp; cp++)
+ Buf_AddByte(buf, (Byte)*cp);
+
+ if (doFree)
+ free(lhs);
+
+ for (;*condExpr && !isspace(*condExpr); condExpr++)
+ Buf_AddByte(buf, (Byte)*condExpr);
+
+ Buf_AddByte(buf, (Byte)'\0');
+ lhs = (char *)Buf_GetAll(buf, &varSpecLen);
+ Buf_Destroy(buf, FALSE);
+
+ doFree = TRUE;
+ }
+
/*
* Skip whitespace to get to the operator
*/
- while (isspace(*condExpr)) {
+ while (isspace(*condExpr))
condExpr++;
- }
+
/*
* Make sure the operator is a valid one. If it isn't a
* known relational operator, pretend we got a
@@ -586,8 +601,10 @@ do_compare:
*/
char *string;
char *cp, *cp2;
+ int qt;
Buffer buf;
+do_string_compare:
if (((*op != '!') && (*op != '=')) || (op[1] != '=')) {
Parse_Error(PARSE_WARNING,
"String comparison operator should be either == or !=");
@@ -595,8 +612,12 @@ do_compare:
}
buf = Buf_Init(0);
+ qt = *rhs == '"' ? 1 : 0;
- for (cp = rhs+1; (*cp != '"') && (*cp != '\0'); cp++) {
+ for (cp = &rhs[qt];
+ ((qt && (*cp != '"')) ||
+ (!qt && strchr(" \t)", *cp) == NULL)) &&
+ (*cp != '\0'); cp++) {
if ((*cp == '\\') && (cp[1] != '\0')) {
/*
* Backslash escapes things -- skip over next
@@ -643,7 +664,10 @@ do_compare:
}
free(string);
if (rhs == condExpr) {
- condExpr = cp + 1;
+ if (!qt && *cp == ')')
+ condExpr = cp;
+ else
+ condExpr = cp + 1;
}
} else {
/*
@@ -653,7 +677,8 @@ do_compare:
double left, right;
char *string;
- left = CondCvtArg(lhs);
+ if (!CondCvtArg(lhs, &left))
+ goto do_string_compare;
if (*rhs == '$') {
int len;
Boolean freeIt;
@@ -662,16 +687,19 @@ do_compare:
if (string == var_Error) {
right = 0.0;
} else {
- right = CondCvtArg(string);
- if (freeIt) {
- free(string);
+ if (!CondCvtArg(string, &right)) {
+ if (freeIt)
+ free(string);
+ goto do_string_compare;
}
- if (rhs == condExpr) {
+ if (freeIt)
+ free(string);
+ if (rhs == condExpr)
condExpr += len;
- }
}
} else {
- right = CondCvtArg(rhs);
+ if (!CondCvtArg(rhs, &right))
+ goto do_string_compare;
if (rhs == condExpr) {
/*
* Skip over the right-hand side
@@ -720,9 +748,8 @@ do_compare:
}
}
error:
- if (doFree) {
+ if (doFree)
free(lhs);
- }
break;
}
default: {
@@ -793,7 +820,14 @@ error:
if (val == var_Error) {
t = Err;
} else {
- t = (*val == '\0') ? True : False;
+ /*
+ * A variable is empty when it just contains
+ * spaces... 4/15/92, christos
+ */
+ char *p;
+ for (p = val; *p && isspace(*p); p++)
+ continue;
+ t = (*p == '\0') ? True : False;
}
if (doFree) {
free(val);
@@ -1022,12 +1056,13 @@ CondE(doEval)
*
*-----------------------------------------------------------------------
*/
+int
Cond_Eval (line)
char *line; /* Line to parse */
{
struct If *ifp;
Boolean isElse;
- Boolean value;
+ Boolean value = FALSE;
int level; /* Level at which to report errors. */
level = PARSE_FATAL;
@@ -1157,6 +1192,8 @@ Cond_Eval (line)
Parse_Error (level, "Malformed conditional (%s)",
line);
return (COND_INVALID);
+ default:
+ break;
}
}
if (!isElse) {
diff --git a/usr.bin/make/config.h b/usr.bin/make/config.h
index 28949868927..a5baadac0d8 100644
--- a/usr.bin/make/config.h
+++ b/usr.bin/make/config.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)config.h 5.9 (Berkeley) 6/1/90
- * $Id: config.h,v 1.2 1993/08/01 18:12:05 mycroft Exp $
+ * $Id: config.h,v 1.3 1994/03/05 00:34:40 cgd Exp $
*/
#define DEFSHELL 1 /* Bourne shell */
@@ -86,4 +86,8 @@
*/
#define LIBSUFF ".a"
#define RECHECK
+
+#ifndef RANLIBMAG
+#define RANLIBMAG "__.SYMDEF"
+#endif
/*#define POSIX*/
diff --git a/usr.bin/make/dir.c b/usr.bin/make/dir.c
index b4e82ce6160..5dd748c819e 100644
--- a/usr.bin/make/dir.c
+++ b/usr.bin/make/dir.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)dir.c 5.6 (Berkeley) 12/28/90";*/
-static char rcsid[] = "$Id: dir.c,v 1.4 1994/01/13 21:01:47 jtc Exp $";
+/* from: static char sccsid[] = "@(#)dir.c 5.6 (Berkeley) 12/28/90"; */
+static char *rcsid = "$Id: dir.c,v 1.5 1994/03/05 00:34:41 cgd Exp $";
#endif /* not lint */
/*-
@@ -83,12 +83,12 @@ static char rcsid[] = "$Id: dir.c,v 1.4 1994/01/13 21:01:47 jtc Exp $";
*/
#include <stdio.h>
-#include <stdlib.h>
#include <sys/types.h>
-#include <sys/stat.h>
#include <dirent.h>
+#include <sys/stat.h>
#include "make.h"
#include "hash.h"
+#include "dir.h"
/*
* A search path consists of a Lst of Path structures. A Path structure
@@ -173,14 +173,6 @@ static int hits, /* Found in directory cache */
nearmisses, /* Found under search path */
bigmisses; /* Sought by itself */
-typedef struct Path {
- char *name; /* Name of directory */
- int refCount; /* Number of paths with this directory */
- int hits; /* the number of times a file in this
- * directory has been found */
- Hash_Table files; /* Hash table of files in directory */
-} Path;
-
static Path *dot; /* contents of current directory */
static Hash_Table mtimes; /* Results of doing a last-resort stat in
* Dir_FindFile -- if we have to go to the
@@ -193,6 +185,13 @@ static Hash_Table mtimes; /* Results of doing a last-resort stat in
* should be ok, but... */
+static int DirFindName __P((Path *, char *));
+static int DirMatchFiles __P((char *, Path *, Lst));
+static void DirExpandCurly __P((char *, char *, Lst, Lst));
+static void DirExpandInt __P((char *, Lst, Lst));
+static int DirPrintWord __P((char *));
+static int DirPrintDir __P((Path *));
+
/*-
*-----------------------------------------------------------------------
* Dir_Init --
@@ -286,7 +285,7 @@ Dir_HasWildcards (name)
* Given a pattern and a Path structure, see if any files
* match the pattern and add their names to the 'expansions' list if
* any do. This is incomplete -- it doesn't take care of patterns like
- * src/*src/*.c properly (just *.c on any of the directories), but it
+ * src / *src / *.c properly (just *.c on any of the directories), but it
* will do for now.
*
* Results:
@@ -305,7 +304,6 @@ DirMatchFiles (pattern, p, expansions)
{
Hash_Search search; /* Index into the directory's table */
Hash_Entry *entry; /* Current entry in the table */
- char *f; /* Current entry in the directory */
Boolean isDot; /* TRUE if the directory being searched is . */
isDot = (*p->name == '.' && p->name[1] == '\0');
@@ -528,11 +526,11 @@ Dir_Expand (word, path, expansions)
printf("expanding \"%s\"...", word);
}
- cp = index(word, '{');
+ cp = strchr(word, '{');
if (cp) {
DirExpandCurly(word, cp, path, expansions);
} else {
- cp = index(word, '/');
+ cp = strchr(word, '/');
if (cp) {
/*
* The thing has a directory component -- find the first wildcard
@@ -559,13 +557,15 @@ Dir_Expand (word, path, expansions)
cp--;
}
if (cp != word) {
+ char sc;
/*
* If the glob isn't in the first component, try and find
* all the components up to the one with a wildcard.
*/
- *cp = '\0';
+ sc = cp[1];
+ cp[1] = '\0';
dirpath = Dir_FindFile(word, path);
- *cp = '/';
+ cp[1] = sc;
/*
* dirpath is null if can't find the leading component
* XXX: Dir_FindFile won't find internal components.
@@ -574,6 +574,9 @@ Dir_Expand (word, path, expansions)
* Probably not important.
*/
if (dirpath != (char *)NULL) {
+ char *dp = &dirpath[strlen(dirpath) - 1];
+ if (*dp == '/')
+ *dp = '\0';
path = Lst_Init(FALSE);
Dir_AddDir(path, dirpath);
DirExpandInt(cp+1, path, expansions);
@@ -605,7 +608,7 @@ Dir_Expand (word, path, expansions)
}
if (DEBUG(DIR)) {
Lst_ForEach(expansions, DirPrintWord, NULL);
- putchar('\n');
+ fputc('\n', stdout);
}
}
@@ -646,7 +649,7 @@ Dir_FindFile (name, path)
* Find the final component of the name and note whether it has a
* slash in it (the name, I mean)
*/
- cp = rindex (name, '/');
+ cp = strrchr (name, '/');
if (cp) {
hasSlash = TRUE;
cp += 1;
@@ -807,7 +810,7 @@ Dir_FindFile (name, path)
* again in such a manner, we will find it without having to do
* numerous numbers of access calls. Hurrah!
*/
- cp = rindex (file, '/');
+ cp = strrchr (file, '/');
*cp = '\0';
Dir_AddDir (path, file);
*cp = '/';
@@ -820,7 +823,7 @@ Dir_FindFile (name, path)
printf("Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
file);
}
- entry = Hash_CreateEntry(&mtimes, (ClientData)file,
+ entry = Hash_CreateEntry(&mtimes, (char *) file,
(Boolean *)NULL);
Hash_SetValue(entry, stb.st_mtime);
nearmisses += 1;
@@ -956,7 +959,7 @@ Dir_MTime (gn)
*/
if (DEBUG(DIR)) {
printf("Using cached time %s for %s\n",
- Targ_FmtTime(Hash_GetValue(entry)), fullName);
+ Targ_FmtTime((time_t) Hash_GetValue(entry)), fullName);
}
stb.st_mtime = (time_t)Hash_GetValue(entry);
Hash_DeleteEntry(&mtimes, entry);
@@ -1000,8 +1003,6 @@ Dir_AddDir (path, name)
register Path *p; /* pointer to new Path structure */
DIR *d; /* for reading directory */
register struct dirent *dp; /* entry in directory */
- Hash_Entry *he;
- char *fName;
ln = Lst_Find (openDirectories, (ClientData)name, DirFindName);
if (ln != NILLNODE) {
@@ -1135,9 +1136,6 @@ void
Dir_Destroy (p)
Path *p; /* The directory descriptor to nuke */
{
- Hash_Search thing1;
- Hash_Entry *thing2;
-
p->refCount -= 1;
if (p->refCount == 0) {
@@ -1210,6 +1208,7 @@ Dir_Concat(path1, path2)
}
/********** DEBUG INFO **********/
+void
Dir_PrintDirectories()
{
LstNode ln;
@@ -1232,6 +1231,7 @@ Dir_PrintDirectories()
static int DirPrintDir (p) Path *p; { printf ("%s ", p->name); return (0); }
+void
Dir_PrintPath (path)
Lst path;
{
diff --git a/usr.bin/make/dir.h b/usr.bin/make/dir.h
new file mode 100644
index 00000000000..4f3adc2970f
--- /dev/null
+++ b/usr.bin/make/dir.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
+ * Copyright (c) 1988, 1989 by Adam de Boor
+ * Copyright (c) 1989 by Berkeley Softworks
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Adam de Boor.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * from: @(#)dir.h 5.4 (Berkeley) 12/28/90
+ * $Id: dir.h,v 1.1 1994/03/05 00:34:43 cgd Exp $
+ */
+
+/* dir.h --
+ */
+
+#ifndef _DIR
+#define _DIR
+
+typedef struct Path {
+ char *name; /* Name of directory */
+ int refCount; /* Number of paths with this directory */
+ int hits; /* the number of times a file in this
+ * directory has been found */
+ Hash_Table files; /* Hash table of files in directory */
+} Path;
+
+void Dir_Init __P((void));
+Boolean Dir_HasWildcards __P((char *));
+void Dir_Expand __P((char *, Lst, Lst));
+char *Dir_FindFile __P((char *, Lst));
+int Dir_MTime __P((GNode *));
+void Dir_AddDir __P((Lst, char *));
+char *Dir_MakeFlags __P((char *, Lst));
+void Dir_ClearPath __P((Lst));
+void Dir_Concat __P((Lst, Lst));
+void Dir_PrintDirectories __P((void));
+void Dir_PrintPath __P((Lst));
+void Dir_Destroy __P((Path *));
+ClientData Dir_CopyDir __P((Path *));
+
+#endif /* _DIR */
diff --git a/usr.bin/make/for.c b/usr.bin/make/for.c
new file mode 100644
index 00000000000..bd7436cf750
--- /dev/null
+++ b/usr.bin/make/for.c
@@ -0,0 +1,294 @@
+/*
+ * Copyright (c) 1992, The Regents of the University of California.
+ * All rights reserved.
+ *
+ * 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. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ */
+
+#ifndef lint
+/* from: static char sccsid[] = "@(#)for.c 5.6 (Berkeley) 6/1/90"; */
+static char *rcsid = "$Id: for.c,v 1.1 1994/03/05 00:34:44 cgd Exp $";
+#endif /* not lint */
+
+/*-
+ * for.c --
+ * Functions to handle loops in a makefile.
+ *
+ * Interface:
+ * For_Eval Evaluate the loop in the passed line.
+ * For_Run Run accumulated loop
+ *
+ */
+
+#include <ctype.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "buf.h"
+
+/*
+ * For statements are of the form:
+ *
+ * .for <variable> in <varlist>
+ * ...
+ * .endfor
+ *
+ * The trick is to look for the matching end inside for for loop
+ * To do that, we count the current nesting level of the for loops.
+ * and the .endfor statements, accumulating all the statements between
+ * the initial .for loop and the matching .endfor;
+ * then we evaluate the for loop for each variable in the varlist.
+ */
+
+static int forLevel = 0; /* Nesting level */
+static char *forVar; /* Iteration variable */
+static Buffer forBuf; /* Commands in loop */
+static Lst forLst; /* List of items */
+
+/*
+ * State of a for loop.
+ */
+struct For {
+ Buffer buf; /* Unexpanded buffer */
+ char* var; /* Index name */
+ Lst lst; /* List of variables */
+};
+
+static int ForExec __P((char *, struct For *));
+
+
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * For_Eval --
+ * Evaluate the for loop in the passed line. The line
+ * looks like this:
+ * .for <variable> in <varlist>
+ *
+ * Results:
+ * TRUE: We found a for loop, or we are inside a for loop
+ * FALSE: We did not find a for loop, or we found the end of the for
+ * for loop.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+int
+For_Eval (line)
+ char *line; /* Line to parse */
+{
+ char *ptr = line, *sub, *wrd;
+ int level; /* Level at which to report errors. */
+
+ level = PARSE_FATAL;
+
+
+ if (forLevel == 0) {
+ Buffer buf;
+ int varlen;
+
+ for (ptr++; *ptr && isspace(*ptr); ptr++)
+ continue;
+ /*
+ * If we are not in a for loop quickly determine if the statement is
+ * a for.
+ */
+ if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || !isspace(ptr[3]))
+ return FALSE;
+ ptr += 3;
+
+ /*
+ * we found a for loop, and now we are going to parse it.
+ */
+ while (*ptr && isspace(*ptr))
+ ptr++;
+
+ /*
+ * Grab the variable
+ */
+ buf = Buf_Init(0);
+ for (wrd = ptr; *ptr && !isspace(*ptr); ptr++)
+ continue;
+ Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd);
+
+ forVar = (char *) Buf_GetAll(buf, &varlen);
+ if (varlen == 0) {
+ Parse_Error (level, "missing variable in for");
+ return 0;
+ }
+ Buf_Destroy(buf, FALSE);
+
+ while (*ptr && isspace(*ptr))
+ ptr++;
+
+ /*
+ * Grab the `in'
+ */
+ if (ptr[0] != 'i' || ptr[1] != 'n' || !isspace(ptr[2])) {
+ Parse_Error (level, "missing `in' in for");
+ printf("%s\n", ptr);
+ return 0;
+ }
+ ptr += 3;
+
+ while (*ptr && isspace(*ptr))
+ ptr++;
+
+ /*
+ * Make a list with the remaining words
+ */
+ forLst = Lst_Init(FALSE);
+ buf = Buf_Init(0);
+ sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE);
+
+#define ADDWORD() \
+ Buf_AddBytes(buf, ptr - wrd, (Byte *) wrd), \
+ Buf_AddByte(buf, (Byte) '\0'), \
+ Lst_AtEnd(forLst, (ClientData) Buf_GetAll(buf, &varlen)), \
+ Buf_Destroy(buf, FALSE)
+
+ for (ptr = sub; *ptr && isspace(*ptr); ptr++)
+ continue;
+
+ for (wrd = ptr; *ptr; ptr++)
+ if (isspace(*ptr)) {
+ ADDWORD();
+ buf = Buf_Init(0);
+ while (*ptr && isspace(*ptr))
+ ptr++;
+ wrd = ptr--;
+ }
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "For: Iterator %s List %s\n", forVar, sub);
+ if (ptr - wrd > 0)
+ ADDWORD();
+ else
+ Buf_Destroy(buf, TRUE);
+ free((Address) sub);
+
+ forBuf = Buf_Init(0);
+ forLevel++;
+ return 1;
+ }
+ else if (*ptr == '.') {
+
+ for (ptr++; *ptr && isspace(*ptr); ptr++)
+ continue;
+
+ if (strncmp(ptr, "endfor", 6) == 0 && (isspace(ptr[6]) || !ptr[6])) {
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "For: end for %d\n", forLevel);
+ if (--forLevel < 0) {
+ Parse_Error (level, "for-less endfor");
+ return 0;
+ }
+ }
+ else if (strncmp(ptr, "for", 3) == 0 && isspace(ptr[3])) {
+ forLevel++;
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "For: new loop %d\n", forLevel);
+ }
+ }
+
+ if (forLevel != 0) {
+ Buf_AddBytes(forBuf, strlen(line), (Byte *) line);
+ Buf_AddByte(forBuf, (Byte) '\n');
+ return 1;
+ }
+ else {
+ return 0;
+ }
+}
+
+/*-
+ *-----------------------------------------------------------------------
+ * ForExec --
+ * Expand the for loop for this index and push it in the Makefile
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+static int
+ForExec(name, arg)
+ char *name;
+ struct For *arg;
+{
+ int len;
+ Var_Set(arg->var, name, VAR_GLOBAL);
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "--- %s = %s\n", arg->var, name);
+ Parse_FromString(Var_Subst(arg->var, (char *) Buf_GetAll(arg->buf, &len),
+ VAR_GLOBAL, FALSE));
+ Var_Delete(arg->var, VAR_GLOBAL);
+
+ return 0;
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * For_Run --
+ * Run the for loop, immitating the actions of an include file
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ * None.
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+For_Run()
+{
+ struct For arg;
+
+ if (forVar == NULL || forBuf == NULL || forLst == NULL)
+ return;
+ arg.var = forVar;
+ arg.buf = forBuf;
+ arg.lst = forLst;
+ forVar = NULL;
+ forBuf = NULL;
+ forLst = NULL;
+
+ Lst_ForEach(arg.lst, ForExec, (ClientData) &arg);
+
+ free((Address)arg.var);
+ Lst_Destroy(arg.lst, free);
+ Buf_Destroy(arg.buf, TRUE);
+}
diff --git a/usr.bin/make/hash.c b/usr.bin/make/hash.c
index 2fa0b3d032a..202648a9bdf 100644
--- a/usr.bin/make/hash.c
+++ b/usr.bin/make/hash.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)hash.c 5.5 (Berkeley) 12/28/90";*/
-static char rcsid[] = "$Id: hash.c,v 1.3 1994/01/13 21:01:49 jtc Exp $";
+/* from: static char sccsid[] = "@(#)hash.c 5.5 (Berkeley) 12/28/90"; */
+static char *rcsid = "$Id: hash.c,v 1.4 1994/03/05 00:34:45 cgd Exp $";
#endif /* not lint */
/* hash.c --
@@ -48,11 +48,8 @@ static char rcsid[] = "$Id: hash.c,v 1.3 1994/01/13 21:01:49 jtc Exp $";
* table. Hash tables grow automatically as the amount of
* information increases.
*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
#include "sprite.h"
+#include "make.h"
#include "hash.h"
/*
@@ -60,7 +57,7 @@ static char rcsid[] = "$Id: hash.c,v 1.3 1994/01/13 21:01:49 jtc Exp $";
* defined:
*/
-static void RebuildTable();
+static void RebuildTable __P((Hash_Table *));
/*
* The following defines the ratio of # entries to # buckets
@@ -104,7 +101,7 @@ Hash_InitTable(t, numBuckets)
i = 16;
else {
for (i = 2; i < numBuckets; i <<= 1)
- /* void */ ;
+ continue;
}
t->numEntries = 0;
t->size = i;
@@ -136,7 +133,7 @@ void
Hash_DeleteTable(t)
Hash_Table *t;
{
- register struct Hash_Entry **hp, *h, *nexth;
+ register struct Hash_Entry **hp, *h, *nexth = NULL;
register int i;
for (hp = t->bucketPtr, i = t->size; --i >= 0;) {
@@ -397,7 +394,7 @@ static void
RebuildTable(t)
register Hash_Table *t;
{
- register Hash_Entry *e, *next, **hp, **xp;
+ register Hash_Entry *e, *next = NULL, **hp, **xp;
register int i, mask;
register Hash_Entry **oldhp;
int oldsize;
diff --git a/usr.bin/make/hash.h b/usr.bin/make/hash.h
index 23e9d982a3d..dca6c0956ab 100644
--- a/usr.bin/make/hash.h
+++ b/usr.bin/make/hash.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)hash.h 5.4 (Berkeley) 12/28/90
- * $Id: hash.h,v 1.2 1993/08/01 18:12:04 mycroft Exp $
+ * $Id: hash.h,v 1.3 1994/03/05 00:34:46 cgd Exp $
*/
/* hash.h --
@@ -106,20 +106,12 @@ typedef struct Hash_Search {
#define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int))
-/*
- * The following procedure declarations and macros
- * are the only things that should be needed outside
- * the implementation code.
- */
+void Hash_InitTable __P((Hash_Table *, int));
+void Hash_DeleteTable __P((Hash_Table *));
+Hash_Entry *Hash_FindEntry __P((Hash_Table *, char *));
+Hash_Entry *Hash_CreateEntry __P((Hash_Table *, char *, Boolean *));
+void Hash_DeleteEntry __P((Hash_Table *, Hash_Entry *));
+Hash_Entry *Hash_EnumFirst __P((Hash_Table *, Hash_Search *));
+Hash_Entry *Hash_EnumNext __P((Hash_Search *));
-extern Hash_Entry * Hash_CreateEntry();
-extern void Hash_DeleteTable();
-extern void Hash_DeleteEntry();
-extern void Hash_DeleteTable();
-extern Hash_Entry * Hash_EnumFirst();
-extern Hash_Entry * Hash_EnumNext();
-extern Hash_Entry * Hash_FindEntry();
-extern void Hash_InitTable();
-extern void Hash_PrintStats();
-
-#endif _HASH
+#endif /* _HASH */
diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c
index 547c253816a..32f81b6ced2 100644
--- a/usr.bin/make/job.c
+++ b/usr.bin/make/job.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)job.c 5.15 (Berkeley) 3/1/91";*/
-static char rcsid[] = "$Id: job.c,v 1.4 1994/01/13 21:01:51 jtc Exp $";
+/* from: static char sccsid[] = "@(#)job.c 5.15 (Berkeley) 3/1/91"; */
+static char *rcsid = "$Id: job.c,v 1.5 1994/03/05 00:34:48 cgd Exp $";
#endif /* not lint */
/*-
@@ -97,7 +97,7 @@ static char rcsid[] = "$Id: job.c,v 1.4 1994/01/13 21:01:51 jtc Exp $";
* Job_Wait Wait for all currently-running jobs to finish.
*/
-#include "make.h"
+#include <sys/types.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <sys/file.h>
@@ -106,10 +106,11 @@ static char rcsid[] = "$Id: job.c,v 1.4 1994/01/13 21:01:51 jtc Exp $";
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
-#include <ctype.h>
-#include <unistd.h>
+#include <signal.h>
+#include "make.h"
+#include "hash.h"
+#include "dir.h"
#include "job.h"
#include "pathnames.h"
@@ -118,8 +119,8 @@ extern int errno;
/*
* error handling variables
*/
-int errors = 0; /* number of errors reported */
-int aborting = 0; /* why is the make aborting? */
+static int errors = 0; /* number of errors reported */
+static int aborting = 0; /* why is the make aborting? */
#define ABORT_ERROR 1 /* Because of an error */
#define ABORT_INTERRUPT 2 /* Because it was interrupted */
#define ABORT_WAIT 3 /* Waiting for jobs to finish */
@@ -189,22 +190,22 @@ static Shell shells[] = {
(char *)0, (char *)0,
}
};
-Shell *commandShell = &shells[DEFSHELL]; /* this is the shell to
+static Shell *commandShell = &shells[DEFSHELL];/* this is the shell to
* which we pass all
* commands in the Makefile.
* It is set by the
* Job_ParseShell function */
-char *shellPath = (char *) NULL, /* full pathname of
+static char *shellPath = (char *) NULL, /* full pathname of
* executable image */
*shellName; /* last component of shell */
static int maxJobs; /* The most children we can run at once */
static int maxLocal; /* The most local ones we can have */
-int nJobs; /* The number of children currently running */
-int nLocal; /* The number of local children */
-Lst jobs; /* The structures that describe them */
-Boolean jobFull; /* Flag to tell when the job table is full. It
+int nJobs; /* The number of children currently running */
+int nLocal; /* The number of local children */
+Lst jobs; /* The structures that describe them */
+Boolean jobFull; /* Flag to tell when the job table is full. It
* is set TRUE when (1) the total number of
* running jobs equals the maximum allowed or
* (2) a job can only be run locally, but
@@ -214,9 +215,9 @@ static fd_set outputs; /* Set of descriptors of pipes connected to
* the output channels of children */
#endif
-GNode *lastNode; /* The node for which output was most recently
+GNode *lastNode; /* The node for which output was most recently
* produced. */
-char *targFmt; /* Format string to use to head output from a
+char *targFmt; /* Format string to use to head output from a
* job when it's not the most-recent job heard
* from */
#define TARG_FMT "--- %s ---\n" /* Default format */
@@ -227,20 +228,34 @@ char *targFmt; /* Format string to use to head output from a
* been migrated home, the job is placed on the stoppedJobs queue to be run
* when the next job finishes.
*/
-Lst stoppedJobs; /* Lst of Job structures describing
+Lst stoppedJobs; /* Lst of Job structures describing
* jobs that were stopped due to concurrency
* limits or migration home */
+#if defined(USE_PGRP) && defined(SYSV)
+#define KILL(pid,sig) killpg (-(pid),(sig))
+#else
# if defined(USE_PGRP)
-#define KILL(pid,sig) killpg((pid),(sig))
+#define KILL(pid,sig) killpg ((pid),(sig))
# else
-#define KILL(pid,sig) kill((pid),(sig))
+#define KILL(pid,sig) kill ((pid),(sig))
# endif
+#endif
-static void JobRestart();
-static int JobStart();
-static void JobInterrupt();
+static int JobCondPassSig __P((Job *, int));
+static void JobPassSig __P((int));
+static int JobCmpPid __P((Job *, int));
+static int JobPrintCommand __P((char *, Job *));
+static int JobSaveCommand __P((char *, GNode *));
+static void JobFinish __P((Job *, union wait));
+static void JobExec __P((Job *, char **));
+static void JobMakeArgv __P((Job *, char **));
+static void JobRestart __P((Job *));
+static int JobStart __P((GNode *, int, Job *));
+static void JobDoOutput __P((Job *, Boolean));
+static Shell *JobMatchShell __P((char *));
+static void JobInterrupt __P((int));
/*-
*-----------------------------------------------------------------------
@@ -314,7 +329,7 @@ JobPassSig(signo)
* Leave gracefully if SIGQUIT, rather than core dumping.
*/
if (signo == SIGQUIT) {
- Finish();
+ Finish(0);
}
/*
@@ -406,6 +421,7 @@ JobPrintCommand (cmd, job)
noSpecials = (noExecute && ! (job->node->type & OP_MAKE));
if (strcmp (cmd, "...") == 0) {
+ job->node->type |= OP_SAVE_CMDS;
if ((job->flags & JOB_IGNDOTS) == 0) {
job->tailCmds = Lst_Succ (Lst_Member (job->node->commands,
(ClientData)cmd));
@@ -423,7 +439,7 @@ JobPrintCommand (cmd, job)
* the variables in the command.
*/
cmdNode = Lst_Member (job->node->commands, (ClientData)cmd);
- cmdStart = cmd = Var_Subst (cmd, job->node, FALSE);
+ cmdStart = cmd = Var_Subst (NULL, cmd, job->node, FALSE);
Lst_Replace (cmdNode, (ClientData)cmdStart);
cmdTemplate = "%s\n";
@@ -440,7 +456,8 @@ JobPrintCommand (cmd, job)
cmd++;
}
- while (isspace(*cmd)) cmd++;
+ while (isspace((unsigned char) *cmd))
+ cmd++;
if (shutUp) {
if (! (job->flags & JOB_SILENT) && !noSpecials &&
@@ -542,7 +559,7 @@ JobSaveCommand (cmd, gn)
char *cmd;
GNode *gn;
{
- cmd = Var_Subst (cmd, gn, FALSE);
+ cmd = Var_Subst (NULL, cmd, gn, FALSE);
(void)Lst_AtEnd (postCommands->commands, (ClientData)cmd);
return (0);
}
@@ -571,7 +588,7 @@ JobSaveCommand (cmd, gn)
*-----------------------------------------------------------------------
*/
/*ARGSUSED*/
-void
+static void
JobFinish (job, status)
Job *job; /* job to finish */
union wait status; /* sub-why job went away */
@@ -822,7 +839,6 @@ Job_Touch (gn, silent)
{
int streamID; /* ID of stream opened to do the touch */
struct timeval times[2]; /* Times for utimes() call */
- struct stat attr; /* Attributes of the file */
if (gn->type & (OP_JOIN|OP_USE|OP_EXEC|OP_OPTIONAL)) {
/*
@@ -888,7 +904,8 @@ Boolean
Job_CheckCommands (gn, abortProc)
GNode *gn; /* The target whose commands need
* verifying */
- void (*abortProc)(); /* Function to abort with message */
+ void (*abortProc) __P((char *, ...));
+ /* Function to abort with message */
{
if (OP_NOP(gn->type) && Lst_IsEmpty (gn->commands) &&
(gn->type & OP_LIB) == 0) {
@@ -1078,7 +1095,7 @@ JobExec(job, argv)
}
if (job->flags & JOB_REMOTE) {
- job->rmtID = (char *)0;
+ job->rmtID = 0;
} else {
nLocal += 1;
/*
@@ -1091,7 +1108,9 @@ JobExec(job, argv)
}
}
+#ifdef RMT_NO_EXEC
jobExecFinish:
+#endif
/*
* Now the job is actually running, add it to the table.
*/
@@ -1349,7 +1368,6 @@ JobStart (gn, flags, previous)
{
register Job *job; /* new job descriptor */
char *argv[4]; /* Argument vector to shell */
- char args[5]; /* arguments to shell */
static int jobno = 0; /* job number of catching output in a file */
Boolean cmdsOK; /* true if the nodes commands were all right */
Boolean local; /* Set true if the job was run locally */
@@ -1423,7 +1441,7 @@ JobStart (gn, flags, previous)
* used to be backwards; replace when start doing multiple commands
* per shell.
*/
- if (1) {
+ if (compatMake) {
/*
* Be compatible: If this is the first time for this node,
* verify its commands are ok and open the commands list for
@@ -1653,7 +1671,7 @@ JobStart (gn, flags, previous)
* curPos may be shifted as may the contents of outBuf.
*-----------------------------------------------------------------------
*/
-void
+static void
JobDoOutput (job, finish)
register Job *job; /* the job whose output needs printing */
Boolean finish; /* TRUE if this is the last time we'll be
@@ -1665,7 +1683,6 @@ JobDoOutput (job, finish)
register int max; /* limit for i (end of current data) */
int nRead; /* (Temporary) number of bytes read */
- char c; /* character after noPrint string */
FILE *oFILE; /* Stream pointer to shell's output file */
char inLine[132];
@@ -1799,9 +1816,8 @@ end_loop:
fflush (stdout);
}
if (i < max - 1) {
- bcopy (&job->outBuf[i + 1], /* shift the remaining */
- job->outBuf, /* characters down */
- max - (i + 1));
+ /* shift the remaining characters down */
+ memcpy ( job->outBuf, &job->outBuf[i + 1], max - (i + 1));
job->curPos = max - (i + 1);
} else {
@@ -1987,7 +2003,9 @@ Job_CatchOutput ()
fd_set readfds;
register LstNode ln;
register Job *job;
+#ifdef RMT_WILL_WATCH
int pnJobs; /* Previous nJobs */
+#endif
fflush(stdout);
#ifdef RMT_WILL_WATCH
@@ -2018,7 +2036,7 @@ Job_CatchOutput ()
timeout.tv_sec = SEL_SEC;
timeout.tv_usec = SEL_USEC;
- if ((nfds = select (FD_SETSIZE, &readfds, (int *) 0, (int *) 0, &timeout)) < 0)
+ if ((nfds = select (FD_SETSIZE, &readfds, (fd_set *) 0, (fd_set *) 0, &timeout)) < 0)
{
return;
} else {
@@ -2336,7 +2354,7 @@ Job_ParseShell (line)
}
words = brk_string (line, &wordCount);
- bzero ((Address)&newShell, sizeof(newShell));
+ memset ((Address)&newShell, 0, sizeof(newShell));
/*
* Parse the specification by keyword
@@ -2400,7 +2418,7 @@ Job_ParseShell (line)
* path the user gave for the shell.
*/
shellPath = path;
- path = rindex (path, '/');
+ path = strrchr (path, '/');
if (path == (char *)NULL) {
path = shellPath;
} else {
@@ -2472,13 +2490,10 @@ JobInterrupt (runINTERRUPT)
char *file = (job->node->path == (char *)NULL ?
job->node->name :
job->node->path);
- /* Don't unlink directories */ /* 10 Aug 92*/
- struct stat sbuf;
- stat (file, &sbuf);
- if (!(sbuf.st_mode & S_IFDIR)) {
- if (unlink (file) == 0) {
- Error ("*** %s removed", file);
- }
+ struct stat st;
+ if (lstat(file, &st) != -1 && !S_ISDIR(st.st_mode) &&
+ unlink(file) != -1) {
+ Error ("*** %s removed", file);
}
}
#ifdef RMT_WANTS_SIGNALS
@@ -2640,8 +2655,7 @@ Job_AbortAll ()
/*
* Catch as many children as want to report in at first, then give up
*/
- while (wait3(&foo, WNOHANG, (struct rusage *)0) > 0) {
- ;
- }
+ while (wait3(&foo, WNOHANG, (struct rusage *)0) > 0)
+ continue;
(void) unlink (tfile);
}
diff --git a/usr.bin/make/job.h b/usr.bin/make/job.h
index 1442cab7b5b..d4ba49d93c6 100644
--- a/usr.bin/make/job.h
+++ b/usr.bin/make/job.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)job.h 5.3 (Berkeley) 6/1/90
- * $Id: job.h,v 1.2 1993/08/01 18:12:03 mycroft Exp $
+ * $Id: job.h,v 1.3 1994/03/05 00:34:49 cgd Exp $
*/
/*-
@@ -98,7 +98,7 @@ typedef struct Job {
* saved when the job has been run */
FILE *cmdFILE; /* When creating the shell script, this is
* where the commands go */
- char *rmtID; /* ID returned from Rmt module */
+ int rmtID; /* ID returned from Rmt module */
short flags; /* Flags to control treatment of job */
#define JOB_IGNERR 0x001 /* Ignore non-zero exits */
#define JOB_SILENT 0x002 /* no output */
@@ -216,18 +216,19 @@ extern Lst stoppedJobs; /* List of jobs that are stopped or didn't
* quite get started */
extern Boolean jobFull; /* Non-zero if no more jobs should/will start*/
-/*
- * These functions should be used only by an intelligent Rmt module, hence
- * their names do *not* include an underscore as they are not fully exported,
- * if you see what I mean.
- */
-extern void JobDoOutput(/* job, final? */); /* Funnel output from
- * job->outPipe to the screen,
- * filtering out echo-off
- * strings etc. */
-extern void JobFinish(/* job, status */); /* Finish out a job. If
- * status indicates job has
- * just stopped, not finished,
- * the descriptor is placed on
- * the stoppedJobs list. */
+
+void Job_Touch __P((GNode *, Boolean));
+Boolean Job_CheckCommands __P((GNode *, void (*abortProc )(char *, ...)));
+void Job_CatchChildren __P((Boolean));
+void Job_CatchOutput __P((void));
+void Job_Make __P((GNode *));
+void Job_Init __P((int, int));
+Boolean Job_Full __P((void));
+Boolean Job_Empty __P((void));
+ReturnStatus Job_ParseShell __P((char *));
+int Job_End __P((void));
+void Job_Wait __P((void));
+void Job_AbortAll __P((void));
+void JobFlagForMigration __P((int));
+
#endif /* _JOB_H_ */
diff --git a/usr.bin/make/list.h b/usr.bin/make/list.h
index 520d2cba660..d89b9ed7748 100644
--- a/usr.bin/make/list.h
+++ b/usr.bin/make/list.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)list.h 5.3 (Berkeley) 6/1/90
- * $Id: list.h,v 1.2 1993/08/01 18:12:02 mycroft Exp $
+ * $Id: list.h,v 1.3 1994/03/05 00:34:51 cgd Exp $
*/
/*
@@ -296,4 +296,4 @@ void List_Move(); /* move an element elsewhere in a list */
#define LIST_ATREAR(headerPtr) (((List_Links *) headerPtr)->prevPtr)
-#endif _LIST
+#endif /* _LIST */
diff --git a/usr.bin/make/lst.h b/usr.bin/make/lst.h
index 1dace269679..d1bd1c37f32 100644
--- a/usr.bin/make/lst.h
+++ b/usr.bin/make/lst.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)lst.h 5.3 (Berkeley) 6/1/90
- * $Id: lst.h,v 1.2 1993/08/01 18:12:01 mycroft Exp $
+ * $Id: lst.h,v 1.3 1994/03/05 00:34:52 cgd Exp $
*/
/*-
@@ -47,6 +47,9 @@
#define _LST_H_
#include <sprite.h>
+#if __STDC__
+#include <stdlib.h>
+#endif
/*
* basic typedef. This is what the Lst_ functions handle
diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c
index 0499d3ddabb..dac66b7ddee 100644
--- a/usr.bin/make/main.c
+++ b/usr.bin/make/main.c
@@ -43,8 +43,8 @@ char copyright[] =
#endif /* not lint */
#ifndef lint
-/*static char sccsid[] = "from: @(#)main.c 5.25 (Berkeley) 4/1/91";*/
-static char rcsid[] = "$Id: main.c,v 1.12 1994/01/13 21:01:55 jtc Exp $";
+/* from: static char sccsid[] = "@(#)main.c 5.25 (Berkeley) 4/1/91"; */
+static char *rcsid = "$Id: main.c,v 1.13 1994/03/05 00:34:53 cgd Exp $";
#endif /* not lint */
/*-
@@ -74,16 +74,24 @@ static char rcsid[] = "$Id: main.c,v 1.12 1994/01/13 21:01:55 jtc Exp $";
* exiting.
*/
+#include <sys/types.h>
+#include <sys/time.h>
#include <sys/param.h>
+#include <sys/resource.h>
#include <sys/signal.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
-#include <stdlib.h>
+#if __STDC__
+#include <stdarg.h>
+#else
#include <varargs.h>
-#include <unistd.h>
+#endif
#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
#include "pathnames.h"
#ifndef DEFMAXLOCAL
@@ -99,8 +107,9 @@ Boolean allPrecious; /* .PRECIOUS given on line by itself */
static Boolean noBuiltins; /* -r flag */
static Lst makefiles; /* ordered list of makefiles to read */
-int maxJobs; /* -j argument */
+int maxJobs; /* -J argument */
static int maxLocal; /* -L argument */
+Boolean compatMake; /* -B argument */
Boolean debug; /* -d flag */
Boolean noExecute; /* -n flag */
Boolean keepgoing; /* -k flag */
@@ -114,9 +123,10 @@ Boolean checkEnvFirst; /* -e flag */
static Boolean jobsRunning; /* TRUE if the jobs might be running */
static Boolean ReadMakefile();
+static void usage();
-static char *curdir; /* pathname of dir where make ran */
-static int obj_is_elsewhere; /* if chdir'd for an architecture */
+static char *curdir; /* startup directory */
+static char *objdir; /* where we chdir'ed to */
/*-
* MainParseArgs --
@@ -139,12 +149,15 @@ MainParseArgs(argc, argv)
{
extern int optind;
extern char *optarg;
- register int i;
- register char *cp;
char c;
optind = 1; /* since we're called more than once */
-rearg: while((c = getopt(argc, argv, "D:I:Sd:ef:ij:knqrst")) != EOF) {
+#ifdef notyet
+# define OPTFLAGS "BD:I:L:PSd:ef:ij:knqrst"
+#else
+# define OPTFLAGS "D:I:d:ef:ij:knqrst"
+#endif
+rearg: while((c = getopt(argc, argv, OPTFLAGS)) != EOF) {
switch(c) {
case 'D':
Var_Set(optarg, "1", VAR_GLOBAL);
@@ -156,7 +169,10 @@ rearg: while((c = getopt(argc, argv, "D:I:Sd:ef:ij:knqrst")) != EOF) {
Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL);
Var_Append(MAKEFLAGS, optarg, VAR_GLOBAL);
break;
-#ifdef notdef
+#ifdef notyet
+ case 'B':
+ compatMake = TRUE;
+ break;
case 'L':
maxLocal = atoi(optarg);
Var_Append(MAKEFLAGS, "-L", VAR_GLOBAL);
@@ -166,11 +182,11 @@ rearg: while((c = getopt(argc, argv, "D:I:Sd:ef:ij:knqrst")) != EOF) {
usePipes = FALSE;
Var_Append(MAKEFLAGS, "-P", VAR_GLOBAL);
break;
-#endif
case 'S':
keepgoing = FALSE;
Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL);
break;
+#endif
case 'd': {
char *modules = optarg;
@@ -188,6 +204,9 @@ rearg: while((c = getopt(argc, argv, "D:I:Sd:ef:ij:knqrst")) != EOF) {
case 'd':
debug |= DEBUG_DIR;
break;
+ case 'f':
+ debug |= DEBUG_FOR;
+ break;
case 'g':
if (modules[1] == '1') {
debug |= DEBUG_GRAPH1;
@@ -284,10 +303,10 @@ rearg: while((c = getopt(argc, argv, "D:I:Sd:ef:ij:knqrst")) != EOF) {
if (!**argv)
Punt("illegal (null) argument.");
if (**argv == '-') {
-/* 17 Mar 92*/ if ((*argv)[1])
-/* 17 Mar 92*/ optind = 0; /* -flag... */
-/* 17 Mar 92*/ else
-/* 17 Mar 92*/ optind = 1; /* - */
+ if ((*argv)[1])
+ optind = 0; /* -flag... */
+ else
+ optind = 1; /* - */
goto rearg;
}
(void)Lst_AtEnd(create, (ClientData)*argv);
@@ -318,7 +337,8 @@ Main_ParseArgLine(line)
if (line == NULL)
return;
- for (; *line == ' '; ++line);
+ for (; *line == ' '; ++line)
+ continue;
if (!*line)
return;
@@ -343,15 +363,42 @@ Main_ParseArgLine(line)
* Side Effects:
* The program exits when done. Targets are created. etc. etc. etc.
*/
+int
main(argc, argv)
int argc;
char **argv;
{
Lst targs; /* target nodes to create -- passed to Make_Init */
- Boolean outOfDate; /* FALSE if all targets up to date */
- struct stat sb;
- char mdpath[MAXPATHLEN + 1], *p, *path, *getenv();
- char objpath[MAXPATHLEN + 1];
+ Boolean outOfDate = TRUE; /* FALSE if all targets up to date */
+ struct stat sb, sa;
+ char *p, *path, *pwd, *getenv(), *getwd();
+ char mdpath[MAXPATHLEN + 1];
+ char obpath[MAXPATHLEN + 1];
+ char cdpath[MAXPATHLEN + 1];
+
+ /*
+ * Find where we are and take care of PWD for the automounter...
+ * All this code is so that we know where we are when we start up
+ * on a different machine with pmake.
+ */
+ curdir = cdpath;
+ if (getwd(curdir) == NULL) {
+ (void)fprintf(stderr, "make: %s.\n", curdir);
+ exit(2);
+ }
+
+ if (stat(curdir, &sa) == -1) {
+ (void)fprintf(stderr, "make: %s: %s.\n",
+ curdir, strerror(errno));
+ exit(2);
+ }
+
+ if ((pwd = getenv("PWD")) != NULL) {
+ if (stat(pwd, &sb) == 0 && sa.st_ino == sb.st_ino &&
+ sa.st_dev == sb.st_dev)
+ (void) strcpy(curdir, pwd);
+ }
+
/*
* if the MAKEOBJDIR (or by default, the _PATH_OBJDIR) directory
@@ -362,34 +409,51 @@ main(argc, argv)
*/
if (!(path = getenv("MAKEOBJDIR"))) {
path = _PATH_OBJDIR;
- snprintf(mdpath, MAXPATHLEN + 1, "%s.%s", path, MACHINE);
- } else {
- strncpy(mdpath, path, MAXPATHLEN + 1);
+ (void) sprintf(mdpath, "%s.%s", path, MACHINE);
}
+ else
+ (void) strncpy(mdpath, path, MAXPATHLEN + 1);
+
+ if (stat(mdpath, &sb) == 0 && S_ISDIR(sb.st_mode)) {
- curdir = emalloc((u_int)MAXPATHLEN + 1);
- if (!getwd(curdir)) {
- (void)fprintf(stderr, "make: %s.\n", curdir);
- exit(2);
- }
- if (!lstat(mdpath, &sb)) {
- snprintf(objpath, MAXPATHLEN + 1, "%s/%s", curdir, mdpath);
- if (chdir(mdpath))
+ if (chdir(mdpath)) {
(void)fprintf(stderr, "make warning: %s: %s.\n",
- mdpath, strerror(errno));
- else
- obj_is_elsewhere = 1;
- } else {
- if (!lstat(path, &sb)) {
- snprintf(objpath, MAXPATHLEN + 1, "%s/%s", curdir, path);
- if (chdir(path))
- (void)fprintf(stderr, "make warning: %s: %s.\n",
- path, strerror(errno));
+ mdpath, strerror(errno));
+ objdir = curdir;
+ }
+ else {
+ if (mdpath[0] != '/') {
+ (void) sprintf(obpath, "%s/%s", curdir, mdpath);
+ objdir = obpath;
+ }
else
- obj_is_elsewhere = 1;
+ objdir = mdpath;
+ }
+ }
+ else {
+ if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+
+ if (chdir(path)) {
+ (void)fprintf(stderr, "make warning: %s: %s.\n",
+ path, strerror(errno));
+ objdir = curdir;
+ }
+ else {
+ if (path[0] != '/') {
+ (void) sprintf(obpath, "%s/%s", curdir,
+ path);
+ objdir = obpath;
+ }
+ else
+ objdir = obpath;
+ }
}
+ else
+ objdir = curdir;
}
+ setenv("PWD", objdir, 1);
+
create = Lst_Init(FALSE);
makefiles = Lst_Init(FALSE);
beSilent = FALSE; /* Print commands as executed */
@@ -406,7 +470,13 @@ main(argc, argv)
maxJobs = DEFMAXJOBS; /* Set default max concurrency */
maxLocal = DEFMAXLOCAL; /* Set default local max concurrency */
+#ifdef notyet
+ compatMake = FALSE; /* No compat mode */
+#else
+ compatMake = TRUE; /* No compat mode */
+#endif
+
/*
* Initialize the parsing, directory and variable modules to prepare
* for the reading of inclusion paths and variable settings on the
@@ -418,11 +488,10 @@ main(argc, argv)
* directories */
Var_Init(); /* As well as the lists of variables for
* parsing arguments */
-
- if (obj_is_elsewhere)
+ if (objdir != curdir)
Dir_AddDir(dirSearchPath, curdir);
Var_Set(".CURDIR", curdir, VAR_GLOBAL);
- Var_Set(".OBJDIR", obj_is_elsewhere?objpath:curdir, VAR_GLOBAL);
+ Var_Set(".OBJDIR", objdir, VAR_GLOBAL);
/*
* Initialize various variables.
@@ -433,8 +502,12 @@ main(argc, argv)
Var_Set("MAKE", argv[0], VAR_GLOBAL);
Var_Set(MAKEFLAGS, "", VAR_GLOBAL);
Var_Set("MFLAGS", "", VAR_GLOBAL);
+#ifdef MACHINE
Var_Set("MACHINE", MACHINE, VAR_GLOBAL);
+#endif
+#ifdef MACHINE_ARCH
Var_Set("MACHINE_ARCH", MACHINE_ARCH, VAR_GLOBAL);
+#endif
/*
* First snag any flags out of the MAKE environment variable.
@@ -499,7 +572,7 @@ main(argc, argv)
Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL), VAR_GLOBAL);
/* Install all the flags into the MAKE envariable. */
- if ((p = Var_Value(MAKEFLAGS, VAR_GLOBAL)) && *p)
+ if (((p = Var_Value(MAKEFLAGS, VAR_GLOBAL)) != NULL) && *p)
#ifdef POSIX
setenv("MAKEFLAGS", p, 1);
#else
@@ -521,11 +594,12 @@ main(argc, argv)
*/
static char VPATH[] = "${VPATH}";
- vpath = Var_Subst(VPATH, VAR_CMD, FALSE);
+ vpath = Var_Subst(NULL, VPATH, VAR_CMD, FALSE);
path = vpath;
do {
/* skip to end of directory */
- for (cp = path; *cp != ':' && *cp != '\0'; cp++);
+ for (cp = path; *cp != ':' && *cp != '\0'; cp++)
+ continue;
/* Save terminator character so know when to stop */
savec = *cp;
*cp = '\0';
@@ -561,7 +635,7 @@ main(argc, argv)
* this was original amMake -- want to allow parallelism, so put this
* back in, eventually.
*/
- if (0) {
+ if (!compatMake) {
/*
* Initialize job module before traversing the graph, now that
* any .BEGIN and .END targets have been read. This is done
@@ -589,9 +663,9 @@ main(argc, argv)
Targ_PrintGraph(2);
if (queryFlag && outOfDate)
- exit(1);
+ return(1);
else
- exit(0);
+ return(0);
}
/*-
@@ -616,12 +690,12 @@ ReadMakefile(fname)
Parse_File("(stdin)", stdin);
Var_Set("MAKEFILE", "", VAR_GLOBAL);
} else {
- if (stream = fopen(fname, "r"))
+ if ((stream = fopen(fname, "r")) != NULL)
goto found;
/* if we've chdir'd, rebuild the path name */
- if (obj_is_elsewhere && *fname != '/') {
+ if (curdir != objdir && *fname != '/') {
(void)sprintf(path, "%s/%s", curdir, fname);
- if (stream = fopen(path, "r")) {
+ if ((stream = fopen(path, "r")) != NULL) {
fname = path;
goto found;
}
@@ -657,14 +731,22 @@ found: Var_Set("MAKEFILE", fname, VAR_GLOBAL);
*/
/* VARARGS */
void
+#if __STDC__
+Error(char *fmt, ...)
+#else
Error(va_alist)
va_dcl
+#endif
{
va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
char *fmt;
va_start(ap);
fmt = va_arg(ap, char *);
+#endif
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
@@ -684,17 +766,25 @@ Error(va_alist)
*/
/* VARARGS */
void
+#if __STDC__
+Fatal(char *fmt, ...)
+#else
Fatal(va_alist)
va_dcl
+#endif
{
va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
char *fmt;
+ va_start(ap);
+ fmt = va_arg(ap, char *);
+#endif
if (jobsRunning)
Job_Wait();
- va_start(ap);
- fmt = va_arg(ap, char *);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
@@ -718,15 +808,24 @@ Fatal(va_alist)
*/
/* VARARGS */
void
+#if __STDC__
+Punt(char *fmt, ...)
+#else
Punt(va_alist)
va_dcl
+#endif
{
va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
char *fmt;
- (void)fprintf(stderr, "make: ");
va_start(ap);
fmt = va_arg(ap, char *);
+#endif
+
+ (void)fprintf(stderr, "make: ");
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
@@ -792,6 +891,7 @@ emalloc(len)
* enomem --
* die when out of memory.
*/
+void
enomem()
{
(void)fprintf(stderr, "make: %s.\n", strerror(errno));
@@ -802,6 +902,7 @@ enomem()
* usage --
* exit with usage message
*/
+static void
usage()
{
(void)fprintf(stderr,
diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1
index ea0521674d9..e2722c15cb6 100644
--- a/usr.bin/make/make.1
+++ b/usr.bin/make/make.1
@@ -30,7 +30,7 @@
.\" SUCH DAMAGE.
.\"
.\" from: @(#)make.1 5.7 (Berkeley) 7/24/91
-.\" $Id: make.1,v 1.5 1994/02/10 18:25:01 jtc Exp $
+.\" $Id: make.1,v 1.6 1994/03/05 00:34:56 cgd Exp $
.\"
.Dd July 24, 1991
.Dt MAKE 1
@@ -75,8 +75,7 @@ and makefiles, please refer to
The options are as follows:
.Bl -tag -width Ds
.It Fl D Ar variable
-Define
-.Ar variable
+Define Ar variable
to be 1, in the global context.
.It Fl d Ar flags
Turn on debugging, and specify which portions of
@@ -164,9 +163,9 @@ to
.Ar value .
.El
.Pp
-There are six different types of lines in a makefile: file dependency
+There are seven different types of lines in a makefile: file dependency
specifications, shell commands, variable assignments, include statements,
-conditional directives, and comments.
+conditional directives, for loops, and comments.
.Pp
In general, lines may be continued from one line to the next by ending
them with a backslash
@@ -488,13 +487,24 @@ This is the
.At V
style variable substitution.
It must be the last modifier specified.
-.Ar Old_string
-is anchored at the end of each word, so only suffixes or entire
-words may be replaced.
+If
+.Ar old_string
+or
+.Ar new_string
+do not contain the pattern matching character
+.Ar %
+then it is assumed that they are
+anchored at the end of each word, so only suffixes or entire
+words may be replaced. Otherwise
+.Ar %
+is the substring of
+.Ar old_string
+to be replaced in
+.Ar new_string
.El
-.Sh INCLUDE STATEMENTS AND CONDITIONALS
-Makefile inclusion and conditional structures reminiscent of the C
-programming language are provided in
+.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS
+Makefile inclusion, conditional structures and for loops reminiscent
+of the C programming language are provided in
.Nm make .
All such structures are identified by a line beginning with a single
dot
@@ -655,20 +665,18 @@ has been defined.
.El
.Pp
.Ar Expression
-may also be an arithmetic or string comparison, with the left-hand side
-being a variable expansion.
-The standard C relational operators are all supported, and the usual
-number/base conversion is performed.
-Note, octal numbers are not supported.
-If the righthand value of a
+may also be an arithmetic or string comparison. Variable expansion is
+performed on both sides of the comparison, after which the integral
+values are compared. A value is interpreted as hexadecimal if it is
+preceded by 0x, otherwise it is decimal; octal numbers are not supported.
+The standard C relational operators are all supported. If after
+variable expansion, either the left or right hand side of a
.Ql Ic ==
or
.Ql Ic "!="
-operator begins with a
-quotation mark
-.Pq Ql \*q
-a string comparison is done between the expanded
-variable and the text between the quotation marks.
+operator is not an integral value, then
+string comparison is performed between the expanded
+variables.
If no relational operator is given, it is assumed that the expanded
variable is being compared against 0.
.Pp
@@ -697,6 +705,31 @@ In both cases this continues until a
or
.Ql Ic .endif
is found.
+.Pp
+For loops are typically used to apply a set of rules to a list of files.
+The syntax of a for loop is:
+.Bl -tag -width Ds
+.It Xo
+.Ic \&.for
+.Ar variable
+.Ic in
+.Ar expression
+.Xc
+.It Xo
+<make-rules>
+.Xc
+.It Xo
+.Ic \&.endfor
+.Xc
+.El
+After the for
+.Ic expression
+is evaluated, it is split into words. The
+iteration
+.Ic variable
+is successively set to each word, and substituted in the
+.Ic make-rules
+inside the body of the for loop.
.Sh COMMENTS
Comments begin with a hash
.Pq Ql \&#
diff --git a/usr.bin/make/make.c b/usr.bin/make/make.c
index 56125122387..0f57e69038d 100644
--- a/usr.bin/make/make.c
+++ b/usr.bin/make/make.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)make.c 5.3 (Berkeley) 6/1/90";*/
-static char rcsid[] = "$Id: make.c,v 1.3 1994/01/13 21:01:57 jtc Exp $";
+/* from: static char sccsid[] = "@(#)make.c 5.3 (Berkeley) 6/1/90"; */
+static char *rcsid = "$Id: make.c,v 1.4 1994/03/05 00:34:58 cgd Exp $";
#endif /* not lint */
/*-
@@ -73,8 +73,10 @@ static char rcsid[] = "$Id: make.c,v 1.3 1994/01/13 21:01:57 jtc Exp $";
* and perform the .USE actions if so.
*/
-#include <stdio.h>
#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
static Lst toBeMade; /* The current fringe of the graph. These
* are nodes which await examination by
@@ -85,6 +87,10 @@ static int numNodes; /* Number of nodes to be processed. If this
* is non-zero when Job_Empty() returns
* TRUE, there's a cycle in the graph */
+static int MakeAddChild __P((GNode *, Lst));
+static int MakeAddAllSrc __P((GNode *, GNode *));
+static Boolean MakeStartJobs __P((void));
+static int MakePrintStatus __P((GNode *, Boolean));
/*-
*-----------------------------------------------------------------------
* Make_TimeStamp --
@@ -427,7 +433,12 @@ Make_Update (cgn)
* little, so this stuff is commented out unless you're sure it's ok.
* -- ardeb 1/12/88
*/
- if (noExecute || Dir_MTime(cgn) == 0) {
+ /*
+ * Christos, 4/9/92: If we are saving commands pretend that
+ * the target is made now. Otherwise archives with ... rules
+ * don't work!
+ */
+ if (noExecute || (cgn->type & OP_SAVE_CMDS) || Dir_MTime(cgn) == 0) {
cgn->mtime = now;
}
if (DEBUG(MAKE)) {
diff --git a/usr.bin/make/make.h b/usr.bin/make/make.h
index e1a8cdcd521..28e2d070325 100644
--- a/usr.bin/make/make.h
+++ b/usr.bin/make/make.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)make.h 5.13 (Berkeley) 3/1/91
- * $Id: make.h,v 1.2 1993/08/01 18:12:00 mycroft Exp $
+ * $Id: make.h,v 1.3 1994/03/05 00:35:00 cgd Exp $
*/
/*-
@@ -48,11 +48,18 @@
#define _MAKE_H_
#include <sys/types.h>
+#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#include <sys/cdefs.h>
+#if __STDC__
+#include <stdlib.h>
+#include <unistd.h>
+#endif
#include "sprite.h"
#include "lst.h"
#include "config.h"
+#include "buf.h"
/*-
* The structure for an individual graph node. Each node has several
@@ -215,7 +222,7 @@ typedef struct GNode {
* case, it ought to be a power of two simply because most storage allocation
* schemes allocate in powers of two.
*/
-#define BSIZE 256 /* starting size for expandable buffers */
+#define MAKE_BSIZE 256 /* starting size for expandable buffers */
/*
* These constants are all used by the Str_Concat function to decide how the
@@ -272,6 +279,7 @@ extern Lst create; /* The list of target names specified on the
extern Lst dirSearchPath; /* The list of directories to search when
* looking for targets */
+extern Boolean compatMake; /* True if we are make compatible */
extern Boolean ignoreErrors; /* True if should ignore all errors */
extern Boolean beSilent; /* True if should print no commands */
extern Boolean noExecute; /* True if should execute nothing */
@@ -323,6 +331,7 @@ extern int debug;
#define DEBUG_SUFF 0x0080
#define DEBUG_TARG 0x0100
#define DEBUG_VAR 0x0200
+#define DEBUG_FOR 0x0400
#ifdef __STDC__
#define CONCAT(a,b) a##b
@@ -339,4 +348,11 @@ extern int debug;
*/
#include "nonints.h"
-#endif _MAKE_H_
+int Make_TimeStamp __P((GNode *, GNode *));
+Boolean Make_OODate __P((GNode *));
+int Make_HandleUse __P((GNode *, GNode *));
+void Make_Update __P((GNode *));
+void Make_DoAllVar __P((GNode *));
+Boolean Make_Run __P((Lst));
+
+#endif /* _MAKE_H_ */
diff --git a/usr.bin/make/nonints.h b/usr.bin/make/nonints.h
index 91a6a5a31ca..d1b77a9a44d 100644
--- a/usr.bin/make/nonints.h
+++ b/usr.bin/make/nonints.h
@@ -36,94 +36,98 @@
* SUCH DAMAGE.
*
* from: @(#)nonints.h 5.6 (Berkeley) 4/18/91
- * $Id: nonints.h,v 1.2 1993/08/01 18:11:59 mycroft Exp $
+ * $Id: nonints.h,v 1.3 1994/03/05 00:35:02 cgd Exp $
*/
-char **brk_string(), *emalloc(), *str_concat();
+/* arch.c */
+ReturnStatus Arch_ParseArchive __P((char **, Lst, GNode *));
+void Arch_Touch __P((GNode *));
+void Arch_TouchLib __P((GNode *));
+int Arch_MTime __P((GNode *));
+int Arch_MemMTime __P((GNode *));
+void Arch_FindLib __P((GNode *, Lst));
+Boolean Arch_LibOODate __P((GNode *));
+void Arch_Init __P((void));
-ReturnStatus Arch_ParseArchive ();
-void Arch_Touch ();
-void Arch_TouchLib ();
-int Arch_MTime ();
-int Arch_MemMTime ();
-void Arch_FindLib ();
-Boolean Arch_LibOODate ();
-void Arch_Init ();
-void Compat_Run();
-void Dir_Init ();
-Boolean Dir_HasWildcards ();
-void Dir_Expand ();
-char * Dir_FindFile ();
-int Dir_MTime ();
-void Dir_AddDir ();
-ClientData Dir_CopyDir ();
-char * Dir_MakeFlags ();
-void Dir_Destroy ();
-void Dir_ClearPath ();
-void Dir_Concat ();
-int Make_TimeStamp ();
-Boolean Make_OODate ();
-int Make_HandleUse ();
-void Make_Update ();
-void Make_DoAllVar ();
-Boolean Make_Run ();
-void Job_Touch ();
-Boolean Job_CheckCommands ();
-void Job_CatchChildren ();
-void Job_CatchOutput ();
-void Job_Make ();
-void Job_Init ();
-Boolean Job_Full ();
-Boolean Job_Empty ();
-ReturnStatus Job_ParseShell ();
-int Job_End ();
-void Job_Wait();
-void Job_AbortAll ();
-void Main_ParseArgLine ();
-void Error ();
-void Fatal ();
-void Punt ();
-void DieHorribly ();
-void Finish ();
-void Parse_Error ();
-Boolean Parse_IsVar ();
-void Parse_DoVar ();
-void Parse_AddIncludeDir ();
-void Parse_File();
-Lst Parse_MainName();
-void Suff_ClearSuffixes ();
-Boolean Suff_IsTransform ();
-GNode * Suff_AddTransform ();
-void Suff_AddSuffix ();
-int Suff_EndTransform ();
-Lst Suff_GetPath ();
-void Suff_DoPaths();
-void Suff_AddInclude ();
-void Suff_AddLib ();
-void Suff_FindDeps ();
-void Suff_SetNull();
-void Suff_Init ();
-void Targ_Init ();
-GNode * Targ_NewGN ();
-GNode * Targ_FindNode ();
-Lst Targ_FindList ();
-Boolean Targ_Ignore ();
-Boolean Targ_Silent ();
-Boolean Targ_Precious ();
-void Targ_SetMain ();
-int Targ_PrintCmd ();
-char * Targ_FmtTime ();
-void Targ_PrintType ();
-char * Str_Concat ();
-int Str_Match();
-void Var_Delete();
-void Var_Set ();
-void Var_Append ();
-Boolean Var_Exists();
-char * Var_Value ();
-char * Var_Parse ();
-char * Var_Subst ();
-char * Var_GetTail();
-char * Var_GetHead();
-void Var_Init ();
-char * Str_FindSubstring();
+/* compat.c */
+void Compat_Run __P((Lst));
+
+/* cond.c */
+int Cond_Eval __P((char *));
+void Cond_End __P((void));
+
+/* for.c */
+int For_Eval __P((char *));
+void For_Run __P((void));
+
+/* main.c */
+void Main_ParseArgLine __P((char *));
+int main __P((int, char **));
+void Error __P((char *, ...));
+void Fatal __P((char *, ...));
+void Punt __P((char *, ...));
+void DieHorribly __P((void));
+void Finish __P((int));
+char *emalloc __P((u_int));
+void enomem __P((void));
+
+/* parse.c */
+void Parse_Error __P((int, char *, ...));
+Boolean Parse_AnyExport __P((void));
+Boolean Parse_IsVar __P((char *));
+void Parse_DoVar __P((char *, GNode *));
+void Parse_AddIncludeDir __P((char *));
+void Parse_File __P((char *, FILE *));
+void Parse_Init __P((void));
+void Parse_FromString __P((char *));
+Lst Parse_MainName __P((void));
+
+/* str.c */
+char *str_concat __P((char *, char *, int));
+char **brk_string __P((char *, int *));
+char *Str_FindSubstring __P((char *, char *));
+int Str_Match __P((char *, char *));
+char *Str_SYSVMatch __P((char *, char *, int *len));
+void Str_SYSVSubst __P((Buffer, char *, char *, int));
+
+/* suff.c */
+void Suff_ClearSuffixes __P((void));
+Boolean Suff_IsTransform __P((char *));
+GNode *Suff_AddTransform __P((char *));
+int Suff_EndTransform __P((GNode *));
+void Suff_AddSuffix __P((char *));
+Lst Suff_GetPath __P((char *));
+void Suff_DoPaths __P((void));
+void Suff_AddInclude __P((char *));
+void Suff_AddLib __P((char *));
+void Suff_FindDeps __P((GNode *));
+void Suff_SetNull __P((char *));
+void Suff_Init __P((void));
+void Suff_PrintAll __P((void));
+
+/* targ.c */
+void Targ_Init __P((void));
+GNode *Targ_NewGN __P((char *));
+GNode *Targ_FindNode __P((char *, int));
+Lst Targ_FindList __P((Lst, int));
+Boolean Targ_Ignore __P((GNode *));
+Boolean Targ_Silent __P((GNode *));
+Boolean Targ_Precious __P((GNode *));
+void Targ_SetMain __P((GNode *));
+int Targ_PrintCmd __P((char *));
+char *Targ_FmtTime __P((time_t));
+void Targ_PrintType __P((int));
+void Targ_PrintGraph __P((int));
+
+/* var.c */
+void Var_Delete __P((char *, GNode *));
+void Var_Set __P((char *, char *, GNode *));
+void Var_Append __P((char *, char *, GNode *));
+Boolean Var_Exists __P((char *, GNode *));
+char *Var_Value __P((char *, GNode *));
+char *Var_Parse __P((char *, GNode *, Boolean, int *, Boolean *));
+char *Var_Subst __P((char *, char *, GNode *, Boolean));
+char *Var_GetTail __P((char *));
+char *Var_GetHead __P((char *));
+void Var_Init __P((void));
+void Var_Dump __P((GNode *));
diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c
index 2aff1048d40..a933a6262f9 100644
--- a/usr.bin/make/parse.c
+++ b/usr.bin/make/parse.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)parse.c 5.18 (Berkeley) 2/19/91";*/
-static char rcsid[] = "$Id: parse.c,v 1.4 1994/01/13 21:01:59 jtc Exp $";
+/* from: static char sccsid[] = "@(#)parse.c 5.18 (Berkeley) 2/19/91"; */
+static char *rcsid = "$Id: parse.c,v 1.5 1994/03/05 00:35:04 cgd Exp $";
#endif /* not lint */
/*-
@@ -81,13 +81,19 @@ static char rcsid[] = "$Id: parse.c,v 1.4 1994/01/13 21:01:59 jtc Exp $";
* Parse_MainName Returns a Lst of the main target to create.
*/
+#if __STDC__
+#include <stdarg.h>
+#else
#include <varargs.h>
-#include <ctype.h>
+#endif
#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
+#include <ctype.h>
+#include <errno.h>
#include <sys/wait.h>
#include "make.h"
+#include "hash.h"
+#include "dir.h"
+#include "job.h"
#include "buf.h"
#include "pathnames.h"
@@ -98,15 +104,19 @@ static char rcsid[] = "$Id: parse.c,v 1.4 1994/01/13 21:01:59 jtc Exp $";
*/
#define CONTINUE 1
#define DONE 0
-static int ParseEOF();
-
static Lst targets; /* targets we're working on */
static Boolean inLine; /* true if currently in a dependency
* line or its commands */
+typedef struct {
+ char *str;
+ char *ptr;
+} PTR;
static char *fname; /* name of current file (for errors) */
static int lineno; /* line number in current file */
-static FILE *curFILE; /* current makefile */
+static FILE *curFILE = NULL; /* current makefile */
+
+static PTR *curPTR = NULL; /* current makefile */
static int fatals = 0;
@@ -119,8 +129,9 @@ static GNode *mainNode; /* The main target to create. This is the
typedef struct IFile {
char *fname; /* name of previous file */
int lineno; /* saved line number */
- FILE * F; /* the open stream */
-} IFile;
+ FILE * F; /* the open stream */
+ PTR * p; /* the char pointer */
+} IFile;
static Lst includes; /* stack of IFiles generated by
* #includes */
@@ -144,20 +155,21 @@ typedef enum {
MFlags, /* .MFLAGS or .MAKEFLAGS */
Main, /* .MAIN and we don't have anything user-specified to
* make */
+ NoExport, /* .NOEXPORT */
Not, /* Not special */
NotParallel, /* .NOTPARALELL */
Null, /* .NULL */
Order, /* .ORDER */
- Path, /* .PATH */
+ ExPath, /* .PATH */
Precious, /* .PRECIOUS */
- Shell, /* .SHELL */
+ ExShell, /* .SHELL */
Silent, /* .SILENT */
SingleShell, /* .SINGLESHELL */
Suffixes, /* .SUFFIXES */
- Attribute, /* Generic attribute */
+ Attribute /* Generic attribute */
} ParseSpecial;
-ParseSpecial specType;
+static ParseSpecial specType;
/*
* Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER
@@ -196,16 +208,37 @@ static struct {
{ ".NOTPARALLEL", NotParallel, 0 },
{ ".NULL", Null, 0 },
{ ".ORDER", Order, 0 },
-{ ".PATH", Path, 0 },
+{ ".PATH", ExPath, 0 },
{ ".PRECIOUS", Precious, OP_PRECIOUS },
{ ".RECURSIVE", Attribute, OP_MAKE },
-{ ".SHELL", Shell, 0 },
+{ ".SHELL", ExShell, 0 },
{ ".SILENT", Silent, OP_SILENT },
{ ".SINGLESHELL", SingleShell, 0 },
{ ".SUFFIXES", Suffixes, 0 },
{ ".USE", Attribute, OP_USE },
};
+static int ParseFindKeyword __P((char *));
+static int ParseLinkSrc __P((GNode *, GNode *));
+static int ParseDoOp __P((GNode *, int));
+static void ParseDoSrc __P((int, char *));
+static int ParseFindMain __P((GNode *));
+static int ParseAddDir __P((Lst, char *));
+static int ParseClearPath __P((Lst));
+static void ParseDoDependency __P((char *));
+static int ParseAddCmd __P((GNode *, char *));
+static int ParseReadc __P((void));
+static void ParseUnreadc __P((int));
+static int ParseHasCommands __P((GNode *));
+static void ParseDoInclude __P((char *));
+#ifdef SYSVINCLUDE
+static void ParseTraditionalInclude __P((char *));
+#endif
+static int ParseEOF __P((int));
+static char *ParseReadLine __P((void));
+static char *ParseSkipLine __P((int));
+static void ParseFinishLine __P((void));
+
/*-
*----------------------------------------------------------------------
* ParseFindKeyword --
@@ -259,18 +292,28 @@ ParseFindKeyword (str)
*/
/* VARARGS */
void
-Parse_Error(type, va_alist)
- int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */
+#if __STDC__
+Parse_Error(int type, char *fmt, ...)
+#else
+Parse_Error(va_alist)
va_dcl
+#endif
{
va_list ap;
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */
char *fmt;
+ va_start(ap);
+ type = va_arg(ap, int);
+ fmt = va_arg(ap, char *);
+#endif
+
(void)fprintf(stderr, "\"%s\", line %d: ", fname, lineno);
if (type == PARSE_WARNING)
(void)fprintf(stderr, "warning: ");
- va_start(ap);
- fmt = va_arg(ap, char *);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
(void)fprintf(stderr, "\n");
@@ -678,7 +721,7 @@ ParseDoDependency (line)
*/
int keywd = ParseFindKeyword(line);
if (keywd != -1) {
- if (specType == Path && parseKeywords[keywd].spec != Path) {
+ if (specType == ExPath && parseKeywords[keywd].spec != ExPath) {
Parse_Error(PARSE_FATAL, "Mismatched special targets");
return;
}
@@ -714,7 +757,7 @@ ParseDoDependency (line)
* .ORDER Must set initial predecessor to NIL
*/
switch (specType) {
- case Path:
+ case ExPath:
if (paths == NULL) {
paths = Lst_Init(FALSE);
}
@@ -746,11 +789,13 @@ ParseDoDependency (line)
break;
}
case SingleShell:
- /* backwards = 1; */
+ compatMake = 1;
break;
case Order:
predecessor = NILGNODE;
break;
+ default:
+ break;
}
} else if (strncmp (line, ".PATH", 5) == 0) {
/*
@@ -760,7 +805,7 @@ ParseDoDependency (line)
*/
Lst path;
- specType = Path;
+ specType = ExPath;
path = Suff_GetPath (&line[5]);
if (path == NILLST) {
Parse_Error (PARSE_FATAL,
@@ -812,7 +857,7 @@ ParseDoDependency (line)
(void)Lst_AtEnd (targets, (ClientData)gn);
}
- } else if (specType == Path && *line != '.' && *line != '\0') {
+ } else if (specType == ExPath && *line != '.' && *line != '\0') {
Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);
}
@@ -821,7 +866,7 @@ ParseDoDependency (line)
* If it is a special type and not .PATH, it's the only target we
* allow on this line...
*/
- if (specType != Not && specType != Path) {
+ if (specType != Not && specType != ExPath) {
Boolean warn = FALSE;
while ((*cp != '!') && (*cp != ':') && *cp) {
@@ -920,9 +965,11 @@ ParseDoDependency (line)
case Silent:
beSilent = TRUE;
break;
- case Path:
+ case ExPath:
Lst_ForEach(paths, ParseClearPath, (ClientData)NULL);
break;
+ default:
+ break;
}
} else if (specType == MFlags) {
/*
@@ -932,7 +979,7 @@ ParseDoDependency (line)
*/
Main_ParseArgLine (line);
*line = '\0';
- } else if (specType == Shell) {
+ } else if (specType == ExShell) {
if (Job_ParseShell (line) != SUCCESS) {
Parse_Error (PARSE_FATAL, "improper shell specification");
return;
@@ -945,7 +992,7 @@ ParseDoDependency (line)
/*
* NOW GO FOR THE SOURCES
*/
- if ((specType == Suffixes) || (specType == Path) ||
+ if ((specType == Suffixes) || (specType == ExPath) ||
(specType == Includes) || (specType == Libs) ||
(specType == Null))
{
@@ -984,7 +1031,7 @@ ParseDoDependency (line)
case Suffixes:
Suff_AddSuffix (line);
break;
- case Path:
+ case ExPath:
Lst_ForEach(paths, ParseAddDir, (ClientData)line);
break;
case Includes:
@@ -996,6 +1043,8 @@ ParseDoDependency (line)
case Null:
Suff_SetNull (line);
break;
+ default:
+ break;
}
*cp = savec;
if (savec != '\0') {
@@ -1259,13 +1308,12 @@ Parse_DoVar (line, ctxt)
Boolean oldOldVars = oldVars;
oldVars = FALSE;
- cp = Var_Subst(cp, ctxt, FALSE);
+ cp = Var_Subst(NULL, cp, ctxt, FALSE);
oldVars = oldOldVars;
Var_Set(line, cp, ctxt);
free(cp);
} else if (type == VAR_SHELL) {
- char result[BUFSIZ]; /* Result of command */
char *args[4]; /* Args for invoking the shell */
int fds[2]; /* Pipe streams */
int cpid; /* Child PID */
@@ -1273,18 +1321,19 @@ Parse_DoVar (line, ctxt)
Boolean freeCmd; /* TRUE if the command needs to be freed, i.e.
* if any variable expansion was performed */
+
/*
* Set up arguments for shell
*/
args[0] = "sh";
args[1] = "-c";
- if (index(cp, '$') != (char *)NULL) {
+ if (strchr(cp, '$') != (char *)NULL) {
/*
* There's a dollar sign in the command, so perform variable
* expansion on the whole thing. The resulting string will need
* freeing when we're done, so set freeCmd to TRUE.
*/
- args[2] = Var_Subst(cp, VAR_CMD, TRUE);
+ args[2] = Var_Subst(NULL, cp, VAR_CMD, TRUE);
freeCmd = TRUE;
} else {
args[2] = cp;
@@ -1326,34 +1375,44 @@ Parse_DoVar (line, ctxt)
} else {
int status;
int cc;
+ Buffer buf;
+ char *res;
/*
* No need for the writing half
*/
close(fds[1]);
+ buf = Buf_Init (MAKE_BSIZE);
+
+ do {
+ char result[BUFSIZ];
+ cc = read(fds[0], result, sizeof(result));
+ if (cc > 0)
+ Buf_AddBytes(buf, cc, (unsigned char *) result);
+ }
+ while (cc > 0 || (cc == -1 && errno == EINTR));
+
/*
- * Wait for the process to exit.
- *
- * XXX: If the child writes more than a pipe's worth, we will
- * deadlock.
+ * Close the input side of the pipe.
*/
- while(((pid = wait(&status)) != cpid) && (pid >= 0)) {
- ;
- }
+ close(fds[0]);
/*
- * Read all the characters the child wrote.
+ * Wait for the process to exit.
*/
- cc = read(fds[0], result, sizeof(result));
+ while(((pid = wait(&status)) != cpid) && (pid >= 0))
+ continue;
+
+ res = (char *)Buf_GetAll (buf, &cc);
+ Buf_Destroy (buf, FALSE);
- if (cc < 0) {
+ if (cc == 0) {
/*
* Couldn't read the child's output -- tell the user and
* set the variable to null
*/
Parse_Error(PARSE_WARNING, "Couldn't read shell's output");
- cc = 0;
}
if (status) {
@@ -1363,12 +1422,13 @@ Parse_DoVar (line, ctxt)
*/
Parse_Error(PARSE_WARNING, "\"%s\" returned non-zero", cp);
}
+
/*
* Null-terminate the result, convert newlines to spaces and
* install it in the variable.
*/
- result[cc] = '\0';
- cp = &result[cc] - 1;
+ res[cc] = '\0';
+ cp = &res[cc] - 1;
if (*cp == '\n') {
/*
@@ -1376,18 +1436,15 @@ Parse_DoVar (line, ctxt)
*/
*cp-- = '\0';
}
- while (cp >= result) {
+ while (cp >= res) {
if (*cp == '\n') {
*cp = ' ';
}
cp--;
}
- Var_Set(line, result, ctxt);
+ Var_Set(line, res, ctxt);
+ free(res);
- /*
- * Close the input side of the pipe.
- */
- close(fds[0]);
}
if (freeCmd) {
free(args[2]);
@@ -1410,7 +1467,7 @@ Parse_DoVar (line, ctxt)
* Side Effects:
* A new element is added to the commands list of the node.
*/
-static
+static int
ParseAddCmd(gn, cmd)
GNode *gn; /* the node to which the command is to be added */
char *cmd; /* the command to add */
@@ -1492,7 +1549,6 @@ ParseDoInclude (file)
{
char *fullname; /* full pathname of file */
IFile *oldFile; /* state associated with current file */
- Lst path; /* the path to use to find the file */
char endc; /* the character which ends the file spec */
char *cp; /* current position in file spec */
Boolean isSystem; /* TRUE if makefile is a system makefile */
@@ -1532,7 +1588,8 @@ ParseDoInclude (file)
if (*cp != endc) {
Parse_Error (PARSE_FATAL,
- "Unclosed .include filename. '%c' expected", endc);
+ "Unclosed %cinclude filename. '%c' expected",
+ '.', endc);
return;
}
*cp = '\0';
@@ -1541,7 +1598,7 @@ ParseDoInclude (file)
* Substitute for any variables in the file name before trying to
* find the thing.
*/
- file = Var_Subst (file, VAR_CMD, FALSE);
+ file = Var_Subst (NULL, file, VAR_CMD, FALSE);
/*
* Now we know the file's name and its search path, we attempt to
@@ -1558,7 +1615,7 @@ ParseDoInclude (file)
*/
char *prefEnd;
- prefEnd = rindex (fname, '/');
+ prefEnd = strrchr (fname, '/');
if (prefEnd != (char *)NULL) {
char *newName;
@@ -1615,6 +1672,7 @@ ParseDoInclude (file)
oldFile->fname = fname;
oldFile->F = curFILE;
+ oldFile->p = curPTR;
oldFile->lineno = lineno;
(void) Lst_AtFront (includes, (ClientData)oldFile);
@@ -1629,6 +1687,7 @@ ParseDoInclude (file)
lineno = 0;
curFILE = fopen (fullname, "r");
+ curPTR = NULL;
if (curFILE == (FILE * ) NULL) {
Parse_Error (PARSE_FATAL, "Cannot open %s", fullname);
/*
@@ -1638,6 +1697,189 @@ ParseDoInclude (file)
}
}
+
+/*-
+ *---------------------------------------------------------------------
+ * Parse_FromString --
+ * Start Parsing from the given string
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * A structure is added to the includes Lst and readProc, lineno,
+ * fname and curFILE are altered for the new file
+ *---------------------------------------------------------------------
+ */
+void
+Parse_FromString(str)
+ char *str;
+{
+ IFile *oldFile; /* state associated with this file */
+
+ if (DEBUG(FOR))
+ (void) fprintf(stderr, "%s\n----\n", str);
+
+ oldFile = (IFile *) emalloc (sizeof (IFile));
+ oldFile->lineno = lineno;
+ oldFile->fname = fname;
+ oldFile->F = curFILE;
+ oldFile->p = curPTR;
+
+ (void) Lst_AtFront (includes, (ClientData)oldFile);
+
+ curFILE = NULL;
+ curPTR = (PTR *) emalloc (sizeof (PTR));
+ curPTR->str = curPTR->ptr = str;
+ lineno = 0;
+ fname = strdup(fname);
+}
+
+
+#ifdef SYSVINCLUDE
+/*-
+ *---------------------------------------------------------------------
+ * ParseTraditionalInclude --
+ * Push to another file.
+ *
+ * The input is the line minus the "include". The file name is
+ * the string following the "include".
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * A structure is added to the includes Lst and readProc, lineno,
+ * fname and curFILE are altered for the new file
+ *---------------------------------------------------------------------
+ */
+static void
+ParseTraditionalInclude (file)
+ char *file; /* file specification */
+{
+ char *fullname; /* full pathname of file */
+ IFile *oldFile; /* state associated with current file */
+ char *cp; /* current position in file spec */
+ char *prefEnd;
+
+ /*
+ * Skip over whitespace
+ */
+ while ((*file == ' ') || (*file == '\t')) {
+ file++;
+ }
+
+ if (*file == '\0') {
+ Parse_Error (PARSE_FATAL,
+ "Filename missing from \"include\"");
+ return;
+ }
+
+ /*
+ * Skip to end of line or next whitespace
+ */
+ for (cp = file; *cp && *cp != '\n' && *cp != '\t' && *cp != ' '; cp++) {
+ continue;
+ }
+
+ *cp = '\0';
+
+ /*
+ * Substitute for any variables in the file name before trying to
+ * find the thing.
+ */
+ file = Var_Subst (NULL, file, VAR_CMD, FALSE);
+
+ /*
+ * Now we know the file's name, we attempt to find the durn thing.
+ * A return of NULL indicates the file don't exist.
+ *
+ * Include files are first searched for relative to the including
+ * file's location. We don't want to cd there, of course, so we
+ * just tack on the old file's leading path components and call
+ * Dir_FindFile to see if we can locate the beast.
+ * XXX - this *does* search in the current directory, right?
+ */
+
+ prefEnd = strrchr (fname, '/');
+ if (prefEnd != (char *)NULL) {
+ char *newName;
+
+ *prefEnd = '\0';
+ newName = str_concat (fname, file, STR_ADDSLASH);
+ fullname = Dir_FindFile (newName, parseIncPath);
+ if (fullname == (char *)NULL) {
+ fullname = Dir_FindFile(newName, dirSearchPath);
+ }
+ free (newName);
+ *prefEnd = '/';
+ } else {
+ fullname = (char *)NULL;
+ }
+
+ if (fullname == (char *)NULL) {
+ /*
+ * System makefile or makefile wasn't found in same directory as
+ * included makefile. Search for it first on the -I search path,
+ * then on the .PATH search path, if not found in a -I directory.
+ * XXX: Suffix specific?
+ */
+ fullname = Dir_FindFile (file, parseIncPath);
+ if (fullname == (char *)NULL) {
+ fullname = Dir_FindFile(file, dirSearchPath);
+ }
+ }
+
+ if (fullname == (char *)NULL) {
+ /*
+ * Still haven't found the makefile. Look for it on the system
+ * path as a last resort.
+ */
+ fullname = Dir_FindFile(file, sysIncPath);
+ }
+
+ if (fullname == (char *) NULL) {
+ Parse_Error (PARSE_FATAL, "Could not find %s", file);
+ return;
+ }
+
+ /*
+ * Once we find the absolute path to the file, we get to save all the
+ * state from the current file before we can start reading this
+ * include file. The state is stored in an IFile structure which
+ * is placed on a list with other IFile structures. The list makes
+ * a very nice stack to track how we got here...
+ */
+ oldFile = (IFile *) emalloc (sizeof (IFile));
+ oldFile->fname = fname;
+
+ oldFile->F = curFILE;
+ oldFile->p = curPTR;
+ oldFile->lineno = lineno;
+
+ (void) Lst_AtFront (includes, (ClientData)oldFile);
+
+ /*
+ * Once the previous state has been saved, we can get down to reading
+ * the new file. We set up the name of the file to be the absolute
+ * name of the include file so error messages refer to the right
+ * place. Naturally enough, we start reading at line number 0.
+ */
+ fname = fullname;
+ lineno = 0;
+
+ curFILE = fopen (fullname, "r");
+ curPTR = NULL;
+ if (curFILE == (FILE * ) NULL) {
+ Parse_Error (PARSE_FATAL, "Cannot open %s", fullname);
+ /*
+ * Pop to previous file
+ */
+ (void) ParseEOF(1);
+ }
+}
+#endif
+
/*-
*---------------------------------------------------------------------
* ParseEOF --
@@ -1664,12 +1906,17 @@ ParseEOF (opened)
}
ifile = (IFile *) Lst_DeQueue (includes);
- free (fname);
+ free ((Address) fname);
fname = ifile->fname;
lineno = ifile->lineno;
- if (opened)
+ if (opened && curFILE)
(void) fclose (curFILE);
+ if (curPTR) {
+ free((Address) curPTR->str);
+ free((Address) curPTR);
+ }
curFILE = ifile->F;
+ curPTR = ifile->p;
free ((Address)ifile);
return (CONTINUE);
}
@@ -1677,24 +1924,110 @@ ParseEOF (opened)
/*-
*---------------------------------------------------------------------
* ParseReadc --
- * Read a character from the current file and update the line number
- * counter as necessary
+ * Read a character from the current file
*
* Results:
* The character that was read
*
* Side Effects:
- * The lineno counter is incremented if the character is a newline
*---------------------------------------------------------------------
*/
-#ifdef notdef
-static int parseReadChar;
+static int
+ParseReadc()
+{
+ if (curFILE)
+ return fgetc(curFILE);
+
+ if (curPTR && *curPTR->ptr)
+ return *curPTR->ptr++;
+ return EOF;
+}
-#define ParseReadc() (((parseReadChar = getc(curFILE)) == '\n') ? \
- (lineno++, '\n') : parseReadChar)
-#else
-#define ParseReadc() (getc(curFILE))
-#endif /* notdef */
+
+/*-
+ *---------------------------------------------------------------------
+ * ParseUnreadc --
+ * Put back a character to the current file
+ *
+ * Results:
+ * None.
+ *
+ * Side Effects:
+ *---------------------------------------------------------------------
+ */
+static void
+ParseUnreadc(c)
+ int c;
+{
+ if (curFILE) {
+ ungetc(c, curFILE);
+ return;
+ }
+ if (curPTR) {
+ *--(curPTR->ptr) = c;
+ return;
+ }
+}
+
+
+/* ParseSkipLine():
+ * Grab the next line
+ */
+static char *
+ParseSkipLine(skip)
+ int skip; /* Skip lines that don't start with . */
+{
+ char *line;
+ int c, lastc = '\0', lineLength;
+ Buffer buf;
+
+ c = ParseReadc();
+
+ if (skip) {
+ /*
+ * Skip lines until get to one that begins with a
+ * special char.
+ */
+ while ((c != '.') && (c != EOF)) {
+ while (((c != '\n') || (lastc == '\\')) && (c != EOF))
+ {
+ /*
+ * Advance to next unescaped newline
+ */
+ if ((lastc = c) == '\n') {
+ lineno++;
+ }
+ c = ParseReadc();
+ }
+ lineno++;
+
+ lastc = c;
+ c = ParseReadc ();
+ }
+ }
+
+ if (c == EOF) {
+ Parse_Error (PARSE_FATAL, "Unclosed conditional/for loop");
+ return ((char *)NULL);
+ }
+
+ /*
+ * Read the entire line into buf
+ */
+ buf = Buf_Init (MAKE_BSIZE);
+ if (c != '\n') {
+ do {
+ Buf_AddByte (buf, (Byte)c);
+ c = ParseReadc();
+ } while ((c != '\n') && (c != EOF));
+ }
+ lineno++;
+
+ Buf_AddByte (buf, (Byte)'\0');
+ line = (char *)Buf_GetAll (buf, &lineLength);
+ Buf_Destroy (buf, FALSE);
+ return line;
+}
/*-
@@ -1739,20 +2072,17 @@ ParseReadLine ()
* semi-colons as semi-colons (by leaving semiNL FALSE). This also
* discards completely blank lines.
*/
- while(1) {
+ for (;;) {
c = ParseReadc();
if (c == '\t') {
ignComment = ignDepOp = TRUE;
break;
- } else if (c == '.') {
- ignComment = TRUE;
- break;
} else if (c == '\n') {
lineno++;
} else if (c == '#') {
- ungetc(c, curFILE);
- break;
+ ParseUnreadc(c);
+ break;
} else {
/*
* Anything else breaks out without doing anything
@@ -1763,7 +2093,7 @@ ParseReadLine ()
if (c != EOF) {
lastc = c;
- buf = Buf_Init(BSIZE);
+ buf = Buf_Init(MAKE_BSIZE);
while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) &&
(c != EOF))
@@ -1789,11 +2119,13 @@ test_char:
} else {
/*
* Check for comments, semiNL's, etc. -- easier than
- * ungetc(c, curFILE); continue;
+ * ParseUnreadc(c); continue;
*/
goto test_char;
}
+ /*NOTREACHED*/
break;
+
case ';':
/*
* Semi-colon: Need to see if it should be interpreted as a
@@ -1808,7 +2140,7 @@ test_char:
* harm, since the newline remains in the buffer and the
* whole line is ignored.
*/
- ungetc('\t', curFILE);
+ ParseUnreadc('\t');
goto line_read;
}
break;
@@ -1836,6 +2168,7 @@ test_char:
break;
case '#':
if (!ignComment) {
+ if (compatMake || (lastc != '\\')) {
/*
* If the character is a hash mark and it isn't escaped
* (or we're being compatible), the thing is a comment.
@@ -1845,6 +2178,14 @@ test_char:
c = ParseReadc();
} while ((c != '\n') && (c != EOF));
goto line_read;
+ } else {
+ /*
+ * Don't add the backslash. Just let the # get copied
+ * over.
+ */
+ lastc = c;
+ continue;
+ }
}
break;
case ':':
@@ -1883,62 +2224,47 @@ test_char:
*/
switch (Cond_Eval (line)) {
case COND_SKIP:
+ /*
+ * Skip to next conditional that evaluates to COND_PARSE.
+ */
do {
- /*
- * Skip to next conditional that evaluates to COND_PARSE.
- */
free (line);
- c = ParseReadc();
- /*
- * Skip lines until get to one that begins with a
- * special char.
- */
- while ((c != '.') && (c != EOF)) {
- while (((c != '\n') || (lastc == '\\')) &&
- (c != EOF))
- {
- /*
- * Advance to next unescaped newline
- */
- if ((lastc = c) == '\n') {
- lineno++;
- }
- c = ParseReadc();
- }
- lineno++;
-
- lastc = c;
- c = ParseReadc ();
- }
-
- if (c == EOF) {
- Parse_Error (PARSE_FATAL, "Unclosed conditional");
- return ((char *)NULL);
- }
-
- /*
- * Read the entire line into buf
- */
- buf = Buf_Init (BSIZE);
- do {
- Buf_AddByte (buf, (Byte)c);
- c = ParseReadc();
- } while ((c != '\n') && (c != EOF));
- lineno++;
-
- Buf_AddByte (buf, (Byte)'\0');
- line = (char *)Buf_GetAll (buf, &lineLength);
- Buf_Destroy (buf, FALSE);
- } while (Cond_Eval(line) != COND_PARSE);
+ line = ParseSkipLine(1);
+ } while (line && Cond_Eval(line) != COND_PARSE);
+ if (line == NULL)
+ break;
/*FALLTHRU*/
case COND_PARSE:
- free (line);
+ free ((Address) line);
line = ParseReadLine();
break;
+ case COND_INVALID:
+ if (For_Eval(line)) {
+ int ok;
+ free(line);
+ do {
+ /*
+ * Skip after the matching end
+ */
+ line = ParseSkipLine(0);
+ if (line == NULL) {
+ Parse_Error (PARSE_FATAL,
+ "Unexpected end of file in for loop.\n");
+ break;
+ }
+ ok = For_Eval(line);
+ free(line);
+ }
+ while (ok);
+ if (line != NULL)
+ For_Run();
+ line = ParseReadLine();
+ }
+ break;
}
}
-
return (line);
+
} else {
/*
* Hit end-of-file, so return a NULL line to indicate this.
@@ -2003,7 +2329,7 @@ Parse_File(name, stream)
fatals = 0;
do {
- while (line = ParseReadLine ()) {
+ while ((line = ParseReadLine ()) != NULL) {
if (*line == '.') {
/*
* Lines that begin with the special character are either
@@ -2036,10 +2362,15 @@ Parse_File(name, stream)
goto nextLine;
}
- if (*line == '\t') {
+ if (*line == '\t'
+#ifdef POSIX
+ || *line == ' '
+#endif
+ )
+ {
/*
- * If a line starts with a tab, it can only hope to be
- * a creation command.
+ * If a line starts with a tab (or space in POSIX-land), it
+ * can only hope to be a creation command.
*/
shellCommand:
for (cp = line + 1; isspace (*cp); cp++) {
@@ -2060,6 +2391,15 @@ Parse_File(name, stream)
cp);
}
}
+#ifdef SYSVINCLUDE
+ } else if (strncmp (line, "include", 7) == 0 &&
+ strchr(line, ':') == NULL) {
+ /*
+ * It's an S3/S5-style "include".
+ */
+ ParseTraditionalInclude (line + 7);
+ goto nextLine;
+#endif
} else if (Parse_IsVar (line)) {
ParseFinishLine();
Parse_DoVar (line, VAR_GLOBAL);
@@ -2103,7 +2443,7 @@ Parse_File(name, stream)
#endif
ParseFinishLine();
- cp = Var_Subst (line, VAR_CMD, TRUE);
+ cp = Var_Subst (NULL, line, VAR_CMD, TRUE);
free (line);
line = cp;
@@ -2151,9 +2491,10 @@ Parse_File(name, stream)
* the parseIncPath list is initialized...
*---------------------------------------------------------------------
*/
+void
Parse_Init ()
{
- char *cp, *start;
+ char *cp = NULL, *start;
/* avoid faults on read-only strings */
static char syspath[] = _PATH_DEFSYSPATH;
@@ -2167,9 +2508,8 @@ Parse_Init ()
* as dir1:...:dirn) to the system include path.
*/
for (start = syspath; *start != '\0'; start = cp) {
- for (cp = start; *cp != '\0' && *cp != ':'; cp++) {
- ;
- }
+ for (cp = start; *cp != '\0' && *cp != ':'; cp++)
+ continue;
if (*cp == '\0') {
Dir_AddDir(sysIncPath, start);
} else {
@@ -2204,8 +2544,10 @@ Parse_MainName()
Punt ("make: no target to make.\n");
/*NOTREACHED*/
} else if (mainNode->type & OP_DOUBLEDEP) {
+ (void) Lst_AtEnd (main, (ClientData)mainNode);
Lst_Concat(main, mainNode->cohorts, LST_CONCNEW);
}
- (void) Lst_AtEnd (main, (ClientData)mainNode);
+ else
+ (void) Lst_AtEnd (main, (ClientData)mainNode);
return (main);
}
diff --git a/usr.bin/make/sprite.h b/usr.bin/make/sprite.h
index 5ae51adac58..d9bf882a569 100644
--- a/usr.bin/make/sprite.h
+++ b/usr.bin/make/sprite.h
@@ -36,7 +36,7 @@
* SUCH DAMAGE.
*
* from: @(#)sprite.h 5.3 (Berkeley) 6/1/90
- * $Id: sprite.h,v 1.2 1993/08/01 18:11:57 mycroft Exp $
+ * $Id: sprite.h,v 1.3 1994/03/05 00:35:07 cgd Exp $
*/
/*
@@ -87,7 +87,7 @@ typedef int ReturnStatus;
* by user processes.
*/
-#define NIL 0xFFFFFFFF
+#define NIL ~0
#define USER_NIL 0
#ifndef NULL
#define NULL 0
diff --git a/usr.bin/make/str.c b/usr.bin/make/str.c
index c88a60cf647..5021365f092 100644
--- a/usr.bin/make/str.c
+++ b/usr.bin/make/str.c
@@ -37,11 +37,10 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)str.c 5.8 (Berkeley) 6/1/90";*/
-static char rcsid[] = "$Id: str.c,v 1.3 1994/01/13 21:02:03 jtc Exp $";
+/* from: static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; */
+static char *rcsid = "$Id: str.c,v 1.4 1994/03/05 00:35:08 cgd Exp $";
#endif /* not lint */
-#include <stdlib.h>
#include "make.h"
/*-
@@ -68,7 +67,7 @@ str_concat(s1, s2, flags)
result = emalloc((u_int)(len1 + len2 + 2));
/* copy first string into place */
- bcopy(s1, result, len1);
+ memcpy(result, s1, len1);
/* add separator character */
if (flags & STR_ADDSPACE) {
@@ -80,7 +79,7 @@ str_concat(s1, s2, flags)
}
/* copy second string plus EOS into place */
- bcopy(s2, result + len1, len2 + 1);
+ memcpy(result + len1, s2, len2 + 1);
/* free original strings */
if (flags & STR_DOFREE) {
@@ -117,8 +116,9 @@ brk_string(str, store_argc)
argv[0] = Var_Value(".MAKE", VAR_GLOBAL);
}
- /* skip leading space chars.
- for (; *str == ' ' || *str == '\t'; ++str);
+ /* skip leading space chars. */
+ for (; *str == ' ' || *str == '\t'; ++str)
+ continue;
/* allocate room for a copy of the string */
if ((len = strlen(str) + 1) > curlen)
@@ -136,11 +136,11 @@ brk_string(str, store_argc)
case '\'':
if (inquote)
if (inquote == ch)
- inquote = NULL;
+ inquote = '\0';
else
break;
else
- inquote = ch;
+ inquote = (char) ch;
continue;
case ' ':
case '\t':
@@ -195,7 +195,7 @@ brk_string(str, store_argc)
}
if (!start)
start = t;
- *t++ = ch;
+ *t++ = (char) ch;
}
done: argv[argc] = (char *)NULL;
*store_argc = argc;
@@ -251,6 +251,7 @@ Str_FindSubstring(string, substring)
*
* Side effects: None.
*/
+int
Str_Match(string, pattern)
register char *string; /* String */
register char *pattern; /* Pattern */
@@ -339,3 +340,98 @@ thisCharOK: ++pattern;
++string;
}
}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Str_SYSVMatch --
+ * Check word against pattern for a match (% is wild),
+ *
+ * Results:
+ * Returns the beginning position of a match or null. The number
+ * of characters matched is returned in len.
+ *
+ * Side Effects:
+ * None
+ *
+ *-----------------------------------------------------------------------
+ */
+char *
+Str_SYSVMatch(word, pattern, len)
+ char *word; /* Word to examine */
+ char *pattern; /* Pattern to examine against */
+ int *len; /* Number of characters to substitute */
+{
+ char *p = pattern;
+ char *w = word;
+ char *m;
+
+ if (*p == '\0')
+ return NULL;
+
+ if ((m = strchr(p, '%')) != NULL) {
+ /* check that the prefix matches */
+ for (; p != m && *w && *w == *p; w++, p++)
+ continue;
+
+ if (p != m)
+ return NULL; /* No match */
+
+ if (*++p == '\0') {
+ /* No more pattern, return the rest of the string */
+ *len = strlen(w);
+ return w;
+ }
+ }
+
+ m = w;
+
+ /* Find a matching tail */
+ do
+ if (strcmp(p, w) == 0) {
+ *len = w - m;
+ return m;
+ }
+ while (*w++ != '\0');
+
+ return NULL;
+}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * Str_SYSVSubst --
+ * Substitute '%' on the pattern with len characters from src.
+ * If the pattern does not contain a '%' prepend len characters
+ * from src.
+ *
+ * Results:
+ * None
+ *
+ * Side Effects:
+ * Places result on buf
+ *
+ *-----------------------------------------------------------------------
+ */
+void
+Str_SYSVSubst(buf, pat, src, len)
+ Buffer buf;
+ char *pat;
+ char *src;
+ int len;
+{
+ char *m;
+
+ if ((m = strchr(pat, '%')) != NULL) {
+ /* Copy the prefix */
+ Buf_AddBytes(buf, m - pat, (Byte *) pat);
+ /* skip the % */
+ pat = m + 1;
+ }
+
+ /* Copy the pattern */
+ Buf_AddBytes(buf, len, (Byte *) src);
+
+ /* append the rest */
+ Buf_AddBytes(buf, strlen(pat), (Byte *) pat);
+}
diff --git a/usr.bin/make/suff.c b/usr.bin/make/suff.c
index 652a45c3620..8ac46c6eea2 100644
--- a/usr.bin/make/suff.c
+++ b/usr.bin/make/suff.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)suff.c 5.6 (Berkeley) 6/1/90";*/
-static char rcsid[] = "$Id: suff.c,v 1.3 1994/01/13 21:02:06 jtc Exp $";
+/* from: static char sccsid[] = "@(#)suff.c 5.6 (Berkeley) 6/1/90"; */
+static char *rcsid = "$Id: suff.c,v 1.4 1994/03/05 00:35:11 cgd Exp $";
#endif /* not lint */
/*-
@@ -91,8 +91,9 @@ static char rcsid[] = "$Id: suff.c,v 1.3 1994/01/13 21:02:06 jtc Exp $";
*/
#include <stdio.h>
-#include <stdlib.h>
#include "make.h"
+#include "hash.h"
+#include "dir.h"
#include "bit.h"
static Lst sufflist; /* Lst of suffixes */
@@ -130,10 +131,43 @@ typedef struct _Src {
* this thing too early or never nuke it) */
} Src;
+/*
+ * A structure for passing more than one argument to the Lst-library-invoked
+ * function...
+ */
+typedef struct {
+ Lst l;
+ Src *s;
+} LstSrc;
+
static Suff *suffNull; /* The NULL suffix for this run */
static Suff *emptySuff; /* The empty suffix required for POSIX
* single-suffix transformation rules */
+
+static char *SuffStrIsPrefix __P((char *, char *));
+static char *SuffSuffIsSuffix __P((Suff *, char *));
+static int SuffSuffIsSuffixP __P((Suff *, char *));
+static int SuffSuffHasNameP __P((Suff *, char *));
+static int SuffSuffIsPrefix __P((Suff *, char *));
+static int SuffGNHasNameP __P((GNode *, char *));
+static void SuffFree __P((Suff *));
+static void SuffInsert __P((Lst, Suff *));
+static Boolean SuffParseTransform __P((char *, Suff **, Suff **));
+static int SuffRebuildGraph __P((GNode *, Suff *));
+static int SuffAddSrc __P((Suff *, LstSrc *));
+static void SuffAddLevel __P((Lst, Src *));
+static void SuffFreeSrc __P((Src *));
+static Src *SuffFindThem __P((Lst));
+static Src *SuffFindCmds __P((Src *));
+static int SuffExpandChildren __P((GNode *, GNode *));
+static Boolean SuffApplyTransform __P((GNode *, GNode *, Suff *, Suff *));
+static void SuffFindArchiveDeps __P((GNode *));
+static void SuffFindNormalDeps __P((GNode *));
+static int SuffPrintName __P((Suff *));
+static int SuffPrintSuff __P((Suff *));
+static int SuffPrintTrans __P((GNode *));
+
/*************** Lst Predicates ****************/
/*-
*-----------------------------------------------------------------------
@@ -207,6 +241,7 @@ SuffSuffIsSuffix (s, str)
*
*-----------------------------------------------------------------------
*/
+static int
SuffSuffIsSuffixP(s, str)
Suff *s;
char *str;
@@ -321,7 +356,7 @@ SuffInsert (l, s)
Suff *s; /* the suffix to insert */
{
LstNode ln; /* current element in l we're examining */
- Suff *s2; /* the suffix descriptor in this element */
+ Suff *s2 = NULL; /* the suffix descriptor in this element */
if (Lst_Open (l) == FAILURE) {
return;
@@ -404,7 +439,7 @@ SuffParseTransform(str, srcPtr, targPtr)
register char *str2; /* Extra pointer (maybe target suffix) */
LstNode singleLn; /* element in suffix list of any suffix
* that exactly matches str */
- Suff *single; /* Source of possible transformation to
+ Suff *single = NULL;/* Source of possible transformation to
* null suffix */
srcLn = NILLNODE;
@@ -416,7 +451,7 @@ SuffParseTransform(str, srcPtr, targPtr)
* we can find two that meet these criteria, we've successfully
* parsed the string.
*/
- while (1) {
+ for (;;) {
if (srcLn == NILLNODE) {
srcLn = Lst_Find(sufflist, (ClientData)str, SuffSuffIsPrefix);
} else {
@@ -870,14 +905,6 @@ Suff_AddLib (sname)
}
/********** Implicit Source Search Functions *********/
-/*
- * A structure for passing more than one argument to the Lst-library-invoked
- * function...
- */
-typedef struct {
- Lst l;
- Src *s;
-} LstSrc;
/*-
*-----------------------------------------------------------------------
@@ -1069,7 +1096,7 @@ SuffFindCmds (targ)
while ((ln = Lst_Next (t->children)) != NILLNODE) {
s = (GNode *)Lst_Datum (ln);
- cp = rindex (s->name, '/');
+ cp = strrchr (s->name, '/');
if (cp == (char *)NULL) {
cp = s->name;
} else {
@@ -1158,11 +1185,11 @@ SuffExpandChildren(cgn, pgn)
* to later since the resulting words are tacked on to the end of
* the children list.
*/
- if (index(cgn->name, '$') != (char *)NULL) {
+ if (strchr(cgn->name, '$') != (char *)NULL) {
if (DEBUG(SUFF)) {
printf("Expanding \"%s\"...", cgn->name);
}
- cp = Var_Subst(cgn->name, pgn, TRUE);
+ cp = Var_Subst(NULL, cgn->name, pgn, TRUE);
if (cp != (char *)NULL) {
Lst members = Lst_Init(FALSE);
@@ -1187,9 +1214,8 @@ SuffExpandChildren(cgn, pgn)
char *start;
char *initcp = cp; /* For freeing... */
- for (start = cp; *start == ' ' || *start == '\t'; start++) {
- ;
- }
+ for (start = cp; *start == ' ' || *start == '\t'; start++)
+ continue;
for (cp = start; *cp != '\0'; cp++) {
if (*cp == ' ' || *cp == '\t') {
/*
@@ -1496,7 +1522,6 @@ SuffFindArchiveDeps(gn)
};
char *vals[sizeof(copy)/sizeof(copy[0])];
int i; /* Index into copy and vals */
- char *cp; /* Suffix for member */
Suff *ms; /* Suffix descriptor for member */
char *name; /* Start of member's name */
@@ -1504,8 +1529,8 @@ SuffFindArchiveDeps(gn)
* The node is an archive(member) pair. so we must find a
* suffix for both of them.
*/
- eoarch = index (gn->name, '(');
- eoname = index (eoarch, ')');
+ eoarch = strchr (gn->name, '(');
+ eoname = strchr (eoarch, ')');
*eoname = '\0'; /* Nuke parentheses during suffix search */
*eoarch = '\0'; /* So a suffix can be found */
@@ -1625,7 +1650,6 @@ SuffFindNormalDeps(gn)
{
char *eoname; /* End of name */
char *sopref; /* Start of prefix */
- Suff *s; /* Current suffix */
LstNode ln; /* Next suffix node to check */
Lst srcs; /* List of sources at which to look */
Lst targs; /* List of targets to which things can be
@@ -1684,6 +1708,7 @@ SuffFindNormalDeps(gn)
targ->suff = (Suff *)Lst_Datum(ln);
targ->node = gn;
targ->parent = (Src *)NULL;
+ targ->children = 0;
/*
* Allocate room for the prefix, whose end is found by subtracting
@@ -1691,7 +1716,7 @@ SuffFindNormalDeps(gn)
*/
prefLen = (eoname - targ->suff->nameLen) - sopref;
targ->pref = emalloc(prefLen + 1);
- bcopy(sopref, targ->pref, prefLen);
+ memcpy(targ->pref, sopref, prefLen);
targ->pref[prefLen] = '\0';
/*
@@ -1724,6 +1749,7 @@ SuffFindNormalDeps(gn)
targ->suff = suffNull;
targ->node = gn;
targ->parent = (Src *)NULL;
+ targ->children = 0;
targ->pref = strdup(sopref);
SuffAddLevel(srcs, targ);
@@ -1751,9 +1777,8 @@ SuffFindNormalDeps(gn)
* Work up the transformation path to find the suffix of the
* target to which the transformation was made.
*/
- for (targ = bottom; targ->parent != NULL; targ = targ->parent) {
- ;
- }
+ for (targ = bottom; targ->parent != NULL; targ = targ->parent)
+ continue;
}
/*
@@ -2083,6 +2108,7 @@ Suff_Init ()
suffNull->name = strdup ("");
suffNull->nameLen = 0;
suffNull->searchPath = Lst_Init (FALSE);
+ Dir_Concat(suffNull->searchPath, dirSearchPath);
suffNull->children = Lst_Init (FALSE);
suffNull->parents = Lst_Init (FALSE);
suffNull->sNum = sNum++;
@@ -2120,19 +2146,19 @@ SuffPrintSuff (s)
printf ("LIBRARY");
break;
}
- putc(flags ? '|' : ')', stdout);
+ fputc(flags ? '|' : ')', stdout);
}
}
- putc ('\n', stdout);
+ fputc ('\n', stdout);
printf ("#\tTo: ");
Lst_ForEach (s->parents, SuffPrintName, (ClientData)0);
- putc ('\n', stdout);
+ fputc ('\n', stdout);
printf ("#\tFrom: ");
Lst_ForEach (s->children, SuffPrintName, (ClientData)0);
- putc ('\n', stdout);
+ fputc ('\n', stdout);
printf ("#\tSearch Path: ");
Dir_PrintPath (s->searchPath);
- putc ('\n', stdout);
+ fputc ('\n', stdout);
return (0);
}
@@ -2144,12 +2170,13 @@ SuffPrintTrans (t)
printf ("%-16s: ", t->name);
Targ_PrintType (t->type);
- putc ('\n', stdout);
+ fputc ('\n', stdout);
Lst_ForEach (t->commands, Targ_PrintCmd, (ClientData)0);
- putc ('\n', stdout);
+ fputc ('\n', stdout);
return(0);
}
+void
Suff_PrintAll()
{
printf ("#*** Suffixes:\n");
diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c
index 8501ca6aaf0..e880ecc0c9b 100644
--- a/usr.bin/make/targ.c
+++ b/usr.bin/make/targ.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)targ.c 5.9 (Berkeley) 3/1/91";*/
-static char rcsid[] = "$Id: targ.c,v 1.2 1993/08/01 18:11:39 mycroft Exp $";
+/* from: static char sccsid[] = "@(#)targ.c 5.9 (Berkeley) 3/1/91"; */
+static char *rcsid = "$Id: targ.c,v 1.3 1994/03/05 00:35:13 cgd Exp $";
#endif /* not lint */
/*-
@@ -84,6 +84,7 @@ static char rcsid[] = "$Id: targ.c,v 1.2 1993/08/01 18:11:39 mycroft Exp $";
#include <time.h>
#include "make.h"
#include "hash.h"
+#include "dir.h"
static Lst allTargets; /* the list of all targets found so far */
static Hash_Table targets; /* a hash table of same */
@@ -149,6 +150,7 @@ Targ_NewGN (name)
gn->preds = Lst_Init(FALSE);
gn->context = Lst_Init (FALSE);
gn->commands = Lst_Init (FALSE);
+ gn->suffix = NULL;
return (gn);
}
@@ -342,6 +344,7 @@ Targ_SetMain (gn)
}
static int
+/*ARGSUSED*/
TargPrintName (gn, ppath)
GNode *gn;
int ppath;
@@ -356,7 +359,7 @@ TargPrintName (gn, ppath)
printf ("(MAIN NAME) ");
}
}
-#endif notdef
+#endif /* notdef */
return (0);
}
@@ -496,13 +499,13 @@ TargPrintNode (gn, pass)
if (!Lst_IsEmpty (gn->iParents)) {
printf("# implicit parents: ");
Lst_ForEach (gn->iParents, TargPrintName, (ClientData)0);
- putc ('\n', stdout);
+ fputc ('\n', stdout);
}
}
if (!Lst_IsEmpty (gn->parents)) {
printf("# parents: ");
Lst_ForEach (gn->parents, TargPrintName, (ClientData)0);
- putc ('\n', stdout);
+ fputc ('\n', stdout);
}
printf("%-16s", gn->name);
@@ -516,7 +519,7 @@ TargPrintNode (gn, pass)
}
Targ_PrintType (gn->type);
Lst_ForEach (gn->children, TargPrintName, (ClientData)0);
- putc ('\n', stdout);
+ fputc ('\n', stdout);
Lst_ForEach (gn->commands, Targ_PrintCmd, (ClientData)0);
printf("\n\n");
if (gn->type & OP_DOUBLEDEP) {
@@ -562,6 +565,7 @@ TargPrintOnlySrc(gn)
* lots o' output
*-----------------------------------------------------------------------
*/
+void
Targ_PrintGraph (pass)
int pass; /* Which pass this is. 1 => no processing
* 2 => processing done */
diff --git a/usr.bin/make/tutorial.ms b/usr.bin/make/tutorial.ms
new file mode 100644
index 00000000000..55c9371c581
--- /dev/null
+++ b/usr.bin/make/tutorial.ms
@@ -0,0 +1,3385 @@
+'\"
+'\" This file contains a tutorial for pmake.
+'\"
+'\" $Id: tutorial.ms,v 1.1 1994/03/05 00:35:14 cgd Exp $
+'\"
+'\" xH is a macro to provide numbered headers that are automatically stuffed
+'\" into a table-of-contents, properly indented, etc. If the first argument
+'\" is numeric, it is taken as the depth for numbering (as for .NH), else
+'\" the default (1) is assumed.
+'\"
+.de xH
+.ds @Q
+.ie \\$1 \{\
+.ds @Q \\$1
+.ds @R \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.\}
+.el .ds @R \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.nr @S \\*(@Q-1*5
+.if \\*(@S==-5 .nr @S 0
+.NH \\*(@Q
+\\*(@R
+.XS \\n(PN \\n(@S
+\\*(SN \\*(@R
+.XE
+..
+'\" CW is used to place a string in fixed-width or switch to a
+'\" fixed-width font.
+'\" C is a typewriter font for a laserwriter. Use something else if
+'\" you don't have one...
+.de CW
+.ie !\\n(.$ .ft C
+.el \&\\$3\fC\\$1\fP\\$2
+..
+'\" Anything I put in a display I want to be in fixed-width
+.am DS
+.CW
+..
+'\" The stuff in .No produces a little stop sign in the left margin
+'\" that says NOTE in it. Unfortunately, it does cause a break, but
+'\" hey. Can't have everything. In case you're wondering how I came
+'\" up with such weird commands, they came from running grn on a
+'\" gremlin file...
+.de No
+.br
+.ne 0.5i
+.po -0.5i
+.br
+.mk
+.nr g3 \\n(.f
+.nr g4 \\n(.s
+.sp -1
+.st cf
+\D's -1u'\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'
+.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'
+.sp -1
+.ft R
+.ps 6
+.nr g8 \\n(.d
+.ds g9 "NOTE
+.sp 74u
+\h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9
+.sp |\\n(g8u
+.sp 166u
+\D't 3u'\D's -1u'
+.br
+.po
+.rt
+.ft \\n(g3
+.ps \\n(g4
+..
+.de Bp
+.ie !\\n(.$ .IP \(bu 2
+.el .IP "\&" 2
+..
+.po +.3i
+.RP
+.TL
+PMake \*- A Tutorial
+.AU
+Adam de Boor
+.AI
+University of California, Berkeley
+Computer Science Department
+Berkeley, CA 94720
+(415) 642-8282
+.LP
+.xH Introduction
+.PP
+PMake is a program for creating other programs, or anything else you
+can think of for it to do. The basic idea behind PMake is that, for
+any given system, be it a program or a document or whatever, there
+will be some files that depend on the state of other files (on when
+they were last modified). PMake takes these dependencies, which you
+must specify, and uses them to build whatever it is you want it to
+build.
+.PP
+PMake is almost fully-compatible with Make, with which you may already
+be familiar. PMake's most important feature is its ability to run
+several different jobs at once, making the creation of systems
+considerably faster. It also has a great deal more functionality than
+Make. Throughout the text, whenever something is mentioned that is an
+important difference between PMake and Make (i.e. something that will
+cause a makefile to fail if you don't do something about it), or is
+simply important, it will be flagged with a little sign in the left
+margin, like this:
+.No
+.PP
+This tutorial is divided into three main sections corresponding to basic,
+intermediate and advanced PMake usage. If you already know Make well,
+you will only need to skim chapter 2 (there are some aspects of
+PMake that I consider basic to its use that didn't exist in Make).
+Things in chapter 3 make life much easier, while those in chapter 4
+are strictly for those who know what they are doing. Chapter 5 has
+definitions for the jargon I use and chapter 6 contains possible
+solutions to the problems presented throughout the tutorial.
+.xH The Basics of PMake
+.PP
+PMake takes as input a file that tells a) which files depend on which
+other files to be complete and b) what to do about files that are
+``out-of-date.'' This file is known as a ``makefile'' and is usually
+.Ix 0 def makefile
+kept in the top-most directory of the system to be built. While you
+can call the makefile anything you want, PMake will look for
+.CW Makefile
+and
+.CW makefile
+(in that order) in the current directory if you don't tell it
+otherwise.
+.Ix 0 def makefile default
+To specify a different makefile, use the
+.B \-f
+flag (e.g.
+.CW "pmake -f program.mk" ''). ``
+.Ix 0 ref flags -f
+.Ix 0 ref makefile other
+.PP
+A makefile has four different types of lines in it:
+.RS
+.IP \(bu 2
+File dependency specifications
+.IP \(bu 2
+Creation commands
+.IP \(bu 2
+Variable assignments
+.IP \(bu 2
+Comments, include statements and conditional directives
+.RE
+.LP
+Any line may be continued over multiple lines by ending it with a
+backslash.
+.Ix 0 def "continuation line"
+The backslash, following newline and any initial whitespace
+on the following line are compressed into a single space before the
+input line is examined by PMake.
+.xH 2 Dependency Lines
+.PP
+As mentioned in the introduction, in any system, there are
+dependencies between the files that make up the system. For instance,
+in a program made up of several C source files and one header file,
+the C files will need to be re-compiled should the header file be
+changed. For a document of several chapters and one macro file, the
+chapters will need to be reprocessed if any of the macros changes.
+.Ix 0 def "dependency"
+These are dependencies and are specified by means of dependency lines in
+the makefile.
+.PP
+.Ix 0 def "dependency line"
+On a dependency line, there are targets and sources, separated by a
+one- or two-character operator.
+The targets ``depend'' on the sources and are usually created from
+them.
+.Ix 0 def target
+.Ix 0 def source
+.Ix 0 ref operator
+Any number of targets and sources may be specified on a dependency line.
+All the targets in the line are made to depend on all the sources.
+Targets and sources need not be actual files, but every source must be
+either an actual file or another target in the makefile.
+If you run out of room, use a backslash at the end of the line to continue onto
+the next one.
+.PP
+Any file may be a target and any file may be a source, but the
+relationship between the two (or however many) is determined by the
+``operator'' that separates them.
+.Ix 0 def operator
+Three types of operators exist: one specifies that the datedness of a
+target is determined by the state of its sources, while another
+specifies other files (the sources) that need to be dealt with before
+the target can be re-created. The third operator is very similar to
+the first, with the additional condition that the target is
+out-of-date if it has no sources. These operations are represented by
+the colon, the exclamation point and the double-colon, respectively, and are
+mutually exclusive. Their exact semantics are as follows:
+.IP ":"
+.Ix 0 def operator colon
+.Ix 0 def :
+If a colon is used, a target on the line is considered to be
+``out-of-date'' (and in need of creation) if
+.RS
+.IP \(bu 2
+any of the sources is out-of-date, or
+.IP \(bu 2
+any of the sources has been modified more recently than the target, or
+.IP \(bu 2
+the target doesn't exist.
+.RE
+.Ix 0 def out-of-date
+.IP "\&"
+Under this operation, steps will be taken to re-create the target only
+if it is found to be out-of-date by using these three rules.
+.IP "!"
+.Ix 0 def operator force
+.Ix 0 def !
+If an exclamation point is used, the target will always be re-created,
+but this will not happen until all of its sources have been examined
+and re-created, if necessary.
+.IP "::"
+.Ix 0 def operator double-colon
+.Ix 0 def ::
+If a double-colon is used, a target is out-of-date if:
+.RS
+.IP \(bu 2
+any of the sources is out-of-date, or
+.IP \(bu 2
+any of the sources has been modified more recently than the target, or
+.IP \(bu 2
+the target doesn't exist, or
+.IP \(bu 2
+the target has no sources.
+.RE
+.IP "\&"
+If the target is out-of-date according to these rules, it will be re-created.
+This operator also does something else to the targets, but I'll go
+into that in the next section (``Shell Commands'').
+.PP
+Enough words, now for an example. Take that C program I mentioned
+earlier. Say there are three C files
+.CW a.c , (
+.CW b.c
+and
+.CW c.c )
+each of which
+includes the file
+.CW defs.h .
+The dependencies between the files could then be expressed as follows:
+.DS
+program : a.o b.o c.o
+a.o b.o c.o : defs.h
+a.o : a.c
+b.o : b.c
+c.o : c.c
+.DE
+.LP
+You may be wondering at this point, where
+.CW a.o ,
+.CW b.o
+and
+.CW c.o
+came in and why
+.I they
+depend on
+.CW defs.h
+and the C files don't. The reason is quite simple:
+.CW program
+cannot be made by linking together .c files \*- it must be
+made from .o files. Likewise, if you change
+.CW defs.h ,
+it isn't the .c files that need to be re-created, it's the .o files.
+If you think of dependencies in these terms \*- which files (targets)
+need to be created from which files (sources) \*- you should have no problems.
+.PP
+An important thing to notice about the above example, is that all the
+\&.o files appear as targets on more than one line. This is perfectly
+all right: the target is made to depend on all the sources mentioned
+on all the dependency lines. E.g.
+.CW a.o
+depends on both
+.CW defs.h
+and
+.CW a.c .
+.Ix 0 ref dependency
+.No
+.PP
+The order of the dependency lines in the makefile is
+important: the first target on the first dependency line in the
+makefile will be the one that gets made if you don't say otherwise.
+That's why
+.CW program
+comes first in the example makefile, above.
+.xH 2 Shell Commands
+.PP
+``Isn't that nice,'' you say to yourself, ``but how are files
+actually `re-created,' as he likes to spell it?''
+The re-creation is accomplished by commands you place in the makefile.
+These commands are passed to the Bourne shell (better known as
+``/bin/sh'') to be executed and are
+.Ix 0 ref shell
+.Ix 0 ref re-creation
+.Ix 0 ref update
+expected to do what's necessary to update the target file (PMake
+doesn't actually check to see if the target was created. It just
+assumes it's there).
+.Ix 0 ref target
+.PP
+Shell commands in a makefile look a lot like shell commands you would
+type at a terminal, with one important exception: each command in a
+makefile
+.I must
+be preceeded by at least one tab.
+.PP
+Each target has associated with it a shell script made up of
+one or more of these shell commands. The creation script for a target
+should immediately follow the dependency line for that target. While
+any given target may appear on more than one dependency line, only one
+of these dependency lines may be followed by a creation script, unless
+the `::' operator was used on the dependency line.
+.Ix 0 ref operator double-colon
+.Ix 0 ref ::
+.No
+.PP
+If the double-colon was used, then each dependency line for the target
+may be followed by a shell script. That script will only be executed
+if the target on the associated dependency line is out-of-date with
+respect to the sources on that line, according to the rules I gave
+earlier.
+I'll give you a good example of this later on.
+.PP
+To expand on the earlier makefile, you might add commands as follows:
+.DS
+program : a.o b.o c.o
+ cc a.o b.o c.o \-o program
+a.o b.o c.o : defs.h
+a.o : a.c
+ cc \-c a.c
+b.o : b.c
+ cc \-c b.c
+c.o : c.c
+ cc \-c c.c
+.DE
+.LP
+Something you should remember when writing a makefile is the
+commands will be executed if the
+.I target
+on the dependency line is out-of-date, not the sources.
+.Ix 0 ref target
+.Ix 0 ref source
+.Ix 0 ref out-of-date
+In this example, the command
+.CW "cc \-c a.c" '' ``
+will be executed if
+.CW a.o
+is out-of-date. Because of the `:' operator,
+.Ix 0 ref :
+.Ix 0 ref operator colon
+this means that should
+.CW a.c
+.I or
+.CW defs.h
+have been modified more recently than
+.CW a.o ,
+the command will be executed
+.CW a.o "\&" (
+will be considered out-of-date).
+.Ix 0 ref out-of-date
+.PP
+Remember how I said the only difference between a makefile shell
+command and a regular shell command was the leading tab? I lied. There
+is another way in which makefile commands differ from regular ones.
+The first two characters after the initial whitespace are treated
+specially.
+If they are any combination of `@' and `\-', they cause PMake to do
+different things.
+.PP
+In most cases, shell commands are printed before they're
+actually executed. This is to keep you informed of what's going on. If
+an `@' appears, however, this echoing is suppressed. In the case of an
+.CW echo
+command, say
+.CW "echo Linking index" ,'' ``
+it would be
+rather silly to see
+.DS
+echo Linking index
+Linking index
+.DE
+.LP
+so PMake allows you to place an `@' before the command
+.CW "@echo Linking index" '') (``
+to prevent the command from being printed.
+.PP
+The other special character is the `\-'. In case you didn't know,
+shell commands finish with a certain ``exit status.'' This status is
+made available by the operating system to whatever program invoked the
+command. Normally this status will be 0 if everything went ok and
+non-zero if something went wrong. For this reason, PMake will consider
+an error to have occurred if one of the shells it invokes returns a non-zero
+status. When it detects an error, PMake's usual action is to abort
+whatever it's doing and exit with a non-zero status itself (any other
+targets that were being created will continue being made, but nothing
+new will be started. PMake will exit after the last job finishes).
+This behavior can be altered, however, by placing a `\-' at the front
+of a command
+.CW "\-mv index index.old" ''), (``
+certain command-line arguments,
+or doing other things, to be detailed later. In such
+a case, the non-zero status is simply ignored and PMake keeps chugging
+along.
+.No
+.PP
+Because all the commands are given to a single shell to execute, such
+things as setting shell variables, changing directories, etc., last
+beyond the command in which they are found. This also allows shell
+compound commands (like
+.CW for
+loops) to be entered in a natural manner.
+Since this could cause problems for some makefiles that depend on
+each command being executed by a single shell, PMake has a
+.B \-B
+.Ix 0 ref compatibility
+.Ix 0 ref flags -B
+flag (it stands for backwards-compatible) that forces each command to
+be given to a separate shell. It also does several other things, all
+of which I discourage since they are now old-fashioned.\|.\|.\|.
+.No
+.PP
+A target's shell script is fed to the shell on its (the shell's) input stream.
+This means that any commands, such as
+.CW ci
+that need to get input from the terminal won't work right \*- they'll
+get the shell's input, something they probably won't find to their
+liking. A simple way around this is to give a command like this:
+.DS
+ci $(SRCS) < /dev/tty
+.DE
+This would force the program's input to come from the terminal. If you
+can't do this for some reason, your only other alternative is to use
+PMake in its fullest compatibility mode. See
+.B Compatibility
+in chapter 4.
+.Ix 0 ref compatibility
+.PP
+.xH 2 Variables
+.PP
+PMake, like Make before it, has the ability to save text in variables
+to be recalled later at your convenience. Variables in PMake are used
+much like variables in the shell and, by tradition, consist of
+all upper-case letters (you don't
+.I have
+to use all upper-case letters.
+In fact there's nothing to stop you from calling a variable
+.CW @^&$%$ .
+Just tradition). Variables are assigned-to using lines of the form
+.Ix 0 def variable assignment
+.DS
+VARIABLE = value
+.DE
+.Ix 0 def variable assignment
+appended-to by
+.DS
+VARIABLE += value
+.DE
+.Ix 0 def variable appending
+.Ix 0 def variable assignment appended
+.Ix 0 def +=
+conditionally assigned-to (if the variable isn't already defined) by
+.DS
+VARIABLE ?= value
+.DE
+.Ix 0 def variable assignment conditional
+.Ix 0 def ?=
+and assigned-to with expansion (i.e. the value is expanded (see below)
+before being assigned to the variable\*-useful for placing a value at
+the beginning of a variable, or other things) by
+.DS
+VARIABLE := value
+.DE
+.Ix 0 def variable assignment expanded
+.Ix 0 def :=
+.LP
+Any whitespace before
+.I value
+is stripped off. When appending, a space is placed between the old
+value and the stuff being appended.
+The value of a variable may be retrieved by enclosing the variable
+name in parentheses or curly braces and preceeding the whole thing
+with a dollar sign.
+.PP
+For example, to set the variable CFLAGS to the string
+.CW "\-I/sprite/src/lib/libc \-O" ,'' ``
+you would place a line
+.DS
+CFLAGS = \-I/sprite/src/lib/libc \-O
+.DE
+in the makefile and use the word
+.CW "$(CFLAGS)"
+wherever you would like the string
+.CW "\-I/sprite/src/lib/libc \-O"
+to appear. This is called variable expansion.
+.Ix 0 def variable expansion
+.No
+.PP
+Unlike Make, PMake will not expand a variable unless it knows
+the variable exists. E.g. if you have a
+.CW "${i}"
+in a shell command and you have not assigned a value to the variable
+.CW i
+(the empty string is considered a value, by the way), where Make would have
+substituted the empty string, PMake will leave the
+.CW "${i}"
+alone.
+To keep PMake from substituting for a variable it knows, precede the
+dollar sign with another dollar sign.
+(e.g. to pass
+.CW "${HOME}"
+to the shell, use
+.CW "$${HOME}" ).
+This causes PMake, in effect, to expand the
+.CW $
+macro, which expands to a single
+.CW $ .
+For compatibility, Make's style of variable expansion will be used
+if you invoke PMake with any of the compatibility flags (\c
+.B \-V ,
+.B \-B
+or
+.B \-M .
+The
+.B \-V
+flag alters just the variable expansion).
+.Ix 0 ref flags -V
+.Ix 0 ref flags -B
+.Ix 0 ref compatibility
+.PP
+.Ix 0 ref variable expansion
+There are two different times at which variable expansion occurs:
+When parsing a dependency line, the expansion occurs immediately
+upon reading the line. If any variable used on a dependency line is
+undefined, PMake will print a message and exit.
+Variables in shell commands are expanded when the command is
+executed.
+Variables used inside another variable are expanded whenever the outer
+variable is expanded (the expansion of an inner variable has no effect
+on the outer variable. I.e. if the outer variable is used on a dependency
+line and in a shell command, and the inner variable changes value
+between when the dependency line is read and the shell command is
+executed, two different values will be substituted for the outer
+variable).
+.Ix 0 def variable types
+.PP
+Variables come in four flavors, though they are all expanded the same
+and all look about the same. They are (in order of expanding scope):
+.RS
+.IP \(bu 2
+Local variables.
+.Ix 0 ref variable local
+.IP \(bu 2
+Command-line variables.
+.Ix 0 ref variable command-line
+.IP \(bu 2
+Global variables.
+.Ix 0 ref variable global
+.IP \(bu 2
+Environment variables.
+.Ix 0 ref variable environment
+.RE
+.LP
+The classification of variables doesn't matter much, except that the
+classes are searched from the top (local) to the bottom (environment)
+when looking up a variable. The first one found wins.
+.xH 3 Local Variables
+.PP
+.Ix 0 def variable local
+Each target can have as many as seven local variables. These are
+variables that are only ``visible'' within that target's shell script
+and contain such things as the target's name, all of its sources (from
+all its dependency lines), those sources that were out-of-date, etc.
+Four local variables are defined for all targets. They are:
+.RS
+.IP ".TARGET"
+.Ix 0 def variable local .TARGET
+.Ix 0 def .TARGET
+The name of the target.
+.IP ".OODATE"
+.Ix 0 def variable local .OODATE
+.Ix 0 def .OODATE
+The list of the sources for the target that were considered out-of-date.
+The order in the list is not guaranteed to be the same as the order in
+which the dependencies were given.
+.IP ".ALLSRC"
+.Ix 0 def variable local .ALLSRC
+.Ix 0 def .ALLSRC
+The list of all sources for this target in the order in which they
+were given.
+.IP ".PREFIX"
+.Ix 0 def variable local .PREFIX
+.Ix 0 def .PREFIX
+The target without its suffix and without any leading path. E.g. for
+the target
+.CW ../../lib/compat/fsRead.c ,
+this variable would contain
+.CW fsRead .
+.RE
+.LP
+Three other local variables are set only for certain targets under
+special circumstances. These are the ``.IMPSRC,''
+.Ix 0 ref variable local .IMPSRC
+.Ix 0 ref .IMPSRC
+``.ARCHIVE,''
+.Ix 0 ref variable local .ARCHIVE
+.Ix 0 ref .ARCHIVE
+and ``.MEMBER''
+.Ix 0 ref variable local .MEMBER
+.ix 0 ref .MEMBER
+variables. When they are set and how they are used is described later.
+.xH 3 Command-line Variables
+.PP
+.Ix 0 def variable command-line
+Command-line variables are set when PMake is first invoked by giving a
+variable assignment as one of the arguments. For example,
+.DS
+pmake "CFLAGS = -I/sprite/src/lib/libc -O"
+.DE
+would make
+.CW CFLAGS
+be a command-line variable with the given value. Any assignments to
+.CW CFLAGS
+in the makefile will have no effect, because once it
+is set, there is (almost) nothing you can do to change a command-line
+variable (the search order, you see). Command-line variables may be
+set using any of the four assignment operators, though only
+.CW =
+and
+.CW ?=
+behave as you would expect them to, mostly because assignments to
+command-line variables are performed before the makefile is read, thus
+the values set in the makefile are unavailable at the time.
+.CW +=
+.Ix 0 ref +=
+.Ix 0 ref variable assignment appended
+is the same as
+.CW = ,
+because the old value of the variable is sought only on the scope in
+which the assignment is taking place (for reasons of efficiency that I
+won't get into here).
+.CW :=
+and
+.CW ?=
+.Ix 0 ref :=
+.Ix 0 ref ?=
+.Ix 0 ref variable assignment expanded
+.Ix 0 ref variable assignment conditional
+will work if the only variables used are in the environment.
+.xH 3 Global Variables
+.PP
+.Ix 0 def variable global
+Global variables are those set or appended-to in the makefile.
+There are two classes of global variables: those you set and those PMake sets.
+As I said before, the ones you set can have any name you want them to have,
+except they may not contain a colon or an exclamation point.
+The variables PMake sets (almost) always begin with a
+period and always contain upper-case letters, only. The variables are
+as follows:
+.RS
+.IP .PMAKE
+.Ix 0 def variable global .PMAKE
+.Ix 0 def .PMAKE
+.Ix 0 def variable global MAKE
+.Ix 0 def MAKE
+The name by which PMake was invoked is stored in this variable. For
+compatibility, the name is also stored in the MAKE variable.
+.IP .MAKEFLAGS
+.Ix 0 def variable global .MAKEFLAGS
+.Ix 0 def .MAKEFLAGS variable
+.Ix 0 def variable global MFLAGS
+.Ix 0 def MFLAGS
+All the relevant flags with which PMake was invoked. This does not
+include such things as
+.B \-f
+or variable assignments. Again for compatibility, this value is stored
+in the MFLAGS variable as well.
+.RE
+.LP
+Two other variables, ``.INCLUDES'' and ``.LIBS,'' are covered in the
+section on special targets in chapter 3.
+.Ix 0 ref variable global .INCLUDES
+.Ix 0 ref variable global .LIBS
+.PP
+Global variables may be deleted using lines of the form:
+.Ix 0 def #undef
+.Ix 0 def variable deletion
+.DS
+#undef \fIvariable\fP
+.DE
+The
+.CW # ' `
+must be the first character on the line. Note that this may only be
+done on global variables.
+.xH 3 Environment Variables
+.PP
+.Ix 0 def variable environment
+Environment variables are passed by the shell that invoked PMake and
+are given by PMake to each shell it invokes. They are expanded like
+any other variable, but they cannot be altered in any way.
+.PP
+One special environment variable,
+.CW PMAKE ,
+.Ix 0 def variable environment PMAKE
+is examined by PMake for command-line flags, variable assignments,
+etc., it should always use. This variable is examined before the
+actual arguments to PMake are. In addition, all flags given to PMake,
+either through the
+.CW PMAKE
+variable or on the command line, are placed in this environment
+variable and exported to each shell PMake executes. Thus recursive
+invocations of PMake automatically receive the same flags as the
+top-most one.
+.PP
+Using all these variables, you can compress the sample makefile even more:
+.DS
+OBJS = a.o b.o c.o
+program : $(OBJS)
+ cc $(.ALLSRC) \-o $(.TARGET)
+$(OBJS) : defs.h
+a.o : a.c
+ cc \-c a.c
+b.o : b.c
+ cc \-c b.c
+c.o : c.c
+ cc \-c c.c
+.DE
+.Ix 0 ref variable local .ALLSRC
+.Ix 0 ref .ALLSRC
+.Ix 0 ref variable local .TARGET
+.Ix 0 ref .TARGET
+.xH 2 Comments
+.PP
+.Ix 0 def comments
+Comments in a makefile start with a `#' character and extend to the
+end of the line. They may appear
+anywhere you want them, except in a shell command (though the shell
+will treat it as a comment, too). If, for some reason, you need to use the `#'
+in a variable or on a dependency line, put a backslash in front of it.
+PMake will compress the two into a single `#'.
+.xH 2 Parallelism
+.No
+.PP
+PMake was specifically designed to re-create several targets at once,
+when possible. You do not have to do anything special to cause this to
+happen (unless PMake was configured to not act in parallel, in which
+case you will have to make use of the
+.B \-L
+and
+.B \-J
+flags (see below).),
+.Ix 0 ref flags -L
+.Ix 0 ref flags -J
+but you do have to be careful at times.
+.PP
+There are several problems you are likely to encounter. One is
+that some makefiles (and programs) are written in such a way that it is
+impossible for two targets to be made at once. The program
+.CW xstr ,
+for example,
+always modifies the files
+.CW strings
+and
+.CW x.c .
+There is no way to change it. Thus you cannot run two of them at once
+without something being trashed. Similarly, if you have commands
+in the makefile that always send output to the same file, you will not
+be able to make more than one target at once unless you change the
+file you use. You can, for instance, add a
+.CW $$$$
+to the end of the file name to tack on the process ID of the shell
+executing the command (each
+.CW $$
+expands to a single
+.CW $ ,
+thus giving you the shell variable
+.CW $$ ).
+Since only one shell is used for all the
+commands, you'll get the same file name for each command in the
+script.
+.PP
+The other problem comes from improperly-specified dependencies that
+worked in Make because of its sequential, depth-first way of examining
+them. While I don't want to go into depth on how PMake
+works (look in chapter 4 if you're interested), I will warn you that
+files in two different ``levels'' of the dependency tree may be
+examined in a different order in PMake than they were in Make. For
+example, given the makefile
+.DS
+a : b c
+b : d
+.DE
+PMake will examine the targets in the order
+.CW c ,
+.CW d ,
+.CW b ,
+.CW a .
+If the makefile's author expected PMake to abort before making
+.CW c
+if an error occurred while making
+.CW b ,
+or if
+.CW b
+needed to exist before
+.CW c
+was made,
+s/he will be sorely disappointed. The dependencies are
+incomplete, since in both these cases,
+.CW c
+would depend on
+.CW b .
+So watch out.
+.PP
+Another problem you may face is that, while PMake is set up to handle the
+output from multiple jobs in a graceful fashion, the same is not so for input.
+It has no way to regulate input to different jobs,
+so if you use the redirection from
+.CW /dev/tty
+I mentioned earlier, you must be careful not to run two of the jobs at once.
+.xH 2 Writing and Debugging a Makefile
+.PP
+Now you know most of what's in a makefile, what do you do next? There
+are two choices: (1) use one of the uncommonly-available makefile
+generators or (2) write your own makefile (I leave out the third choice of
+ignoring PMake and doing everything by hand as being beyond the bounds
+of common sense).
+.PP
+When faced with the writing of a makefile, it is usually best to start
+from first principles: just what
+.I are
+you trying to do? What do you want to makefile finally to produce?
+.PP
+To begin with a somewhat traditional example, let's say you need to
+write a makefile to create a program,
+.CW expr ,
+that takes standard infix expressions and converts them to prefix form (for
+no readily apparent reason). You've got three source files, in C, that
+make up the program:
+.CW main.c ,
+.CW parse.c ,
+and
+.CW output.c .
+Harking back to my pithy advice about dependency lines, you write the
+first line of the file:
+.DS
+expr : main.o parse.o output.o
+.DE
+because you remember
+.CW expr
+is made from
+.CW .o
+files, not
+.CW .c
+files. Similarly for the
+.CW .o
+files you produce the lines:
+.DS
+main.o : main.c
+parse.o : parse.c
+output.o : output.c
+main.o parse.o output.o : defs.h
+.DE
+.PP
+Great. You've now got the dependencies specified. What you need now is
+commands. These commands, remember, must produce the target on the
+dependency line, usually by using the sources you've listed.
+You remember about local variables? Good, so it should come
+to you as no surprise when you write
+.DS
+expr : main.o parse.o output.o
+ cc -o $(.TARGET) $(.ALLSRC)
+.DE
+Why use the variables? If your program grows to produce postfix
+expressions too (which, of course, requires a name change or two), it
+is one fewer place you have to change the file. You cannot do this for
+the object files, however, because they depend on their corresponding
+source files
+.I and
+.CW defs.h ,
+thus if you said
+.DS
+ cc -c $(.ALLSRC)
+.DE
+you'd get (for
+.CW main.o ):
+.DS
+ cc -c main.c defs.h
+.DE
+which is wrong. So you round out the makefile with these lines:
+.DS
+main.o : main.c
+ cc -c main.c
+parse.o : parse.c
+ cc -c parse.c
+output.o : output.c
+ cc -c output.c
+.DE
+.PP
+The makefile is now complete and will, in fact, create the program you
+want it to without unnecessary compilations or excessive typing on
+your part. There are two things wrong with it, however (aside from it
+being altogether too long, something I'll address in chapter 3):
+.IP 1)
+The string
+.CW "main.o parse.o output.o" '' ``
+is repeated twice, necessitating two changes when you add postfix
+(you were planning on that, weren't you?). This is in direct violation
+of de Boor's first rule of writing makefiles:
+.QP
+.I
+Anything that needs to be written more than once
+should be placed in a variable.
+.IP "\&"
+I cannot emphasize this enough as being very important to the
+maintenance of a makefile and its program.
+.IP 2)
+There is no way to alter the way compilations are performed short of
+editing the makefile and making the change in all places. This is evil
+and violates de Boor's second rule, which follows directly from the
+first:
+.QP
+.I
+Any flags or programs used inside a makefile should be placed in a variable so
+they may be changed, temporarily or permanently, with the greatest ease.
+.PP
+The makefile should more properly read:
+.DS
+OBJS = main.o parse.o output.o
+expr : $(OBJS)
+ $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC)
+main.o : main.c
+ $(CC) $(CFLAGS) -c main.c
+parse.o : parse.c
+ $(CC) $(CFLAGS) -c parse.c
+output.o : output.c
+ $(CC) $(CFLAGS) -c output.c
+$(OBJS) : defs.h
+.DE
+These two rules lead to de Boor's first corrolary:
+.QP
+.I
+Variables are your friends.
+.PP
+Once you've written the makefile comes the sometimes-difficult task of
+.Ix 0 ref debugging
+making sure the darn thing works. Your most helpful tool to make sure
+the makefile is at least syntactically correct is the
+.B \-n
+.Ix 0 ref flags -n
+flag, which allows you to see if PMake will choke on the makefile. The
+second thing the
+.B \-n
+flag lets you do is see what PMake would do without it actually doing
+it, thus you can make sure the right commands would be executed were
+you to give PMake its head.
+.PP
+When you find your makefile isn't behaving as you hoped, the first
+question that comes to mind (after ``What time is it, anyway?'') is
+``Why not?'' In answering this, two flags will serve you well:
+.CW "-d m" '' ``
+.Ix 0 ref flags -d
+and
+.CW "-p 2" .'' ``
+.Ix 0 ref flags -p
+The first causes PMake to tell you as it examines each target in the
+makefile and indicate why it is deciding whatever it is deciding. You
+can then use the information printed for other targets to see where
+you went wrong. The
+.CW "-p 2" '' ``
+flag makes PMake print out its internal state when it is done,
+allowing you to see that you forgot to make that one chapter depend on
+that file of macros you just got a new version of. The output from
+.CW "-p 2" '' ``
+is intended to resemble closely a real makefile, but with additional
+information provided and with variables expanded in those commands
+PMake actually printed or executed.
+.PP
+Something to be especially careful about is circular dependencies.
+.Ix 0 def dependency circular
+E.g.
+.DS
+a : b
+b : c d
+d : a
+.DE
+In this case, because of how PMake works,
+.CW c
+is the only thing PMake will examine, because
+.CW d
+and
+.CW a
+will effectively fall off the edge of the universe, making it
+impossible to examine
+.CW b
+(or them, for that matter).
+The sad thing is, this all happens quite
+silently. PMake will complain about the circularity (and tell you
+where it is) if you run it as
+.CW make '' ``
+or give it the
+.B \-M
+.Ix 0 ref flags -M
+flag. Otherwise, you will suffer in silence. It's the nature of the
+beast (see chapter 4 for why).
+.xH 2 Invoking PMake
+.PP
+.Ix 0 ref flags
+.Ix 0 ref arguments
+.Ix 0 ref usage
+PMake comes with a wide variety of flags to choose from.
+They may appear in any order, interspersed with command-line variable
+assignments and targets to create.
+The flags are as follows:
+.IP "\fB\-d\fP \fIwhat\fP"
+.Ix 0 def flags -d
+.Ix 0 ref debugging
+This causes PMake to spew out debugging information that
+may prove useful to you. If you can't
+figure out why PMake is doing what it's doing, you might try using
+this flag. The
+.I what
+parameter is a string of single characters that tell PMake what
+aspects you are interested in. Most of what I describe will make
+little sense to you, unless you've dealt with Make before. Just
+remember where this table is and come back to it as you read on.
+The characters and the information they produce are as follows:
+.RS
+.IP a
+Archive searching and caching.
+.IP c
+Conditional evaluation.
+.IP d
+The searching and caching of directories.
+.IP j
+Various snippets of information related to the running of the multiple
+shells. Not particularly interesting.
+.IP m
+The making of each target: what target is being examined; when it was
+last modified; whether it is out-of-date; etc.
+.IP p
+Makefile parsing.
+.IP r
+Remote execution.
+.IP s
+The application of suffix-transformation rules. (See chapter 3)
+.IP t
+The maintenance of the list of targets.
+.IP v
+Variable assignment.
+.RE
+.IP "\&"
+Of these all, the
+.CW m
+and
+.CW s
+letters will be most useful to you.
+If the
+.B \-d
+is the final argument or the argument from which it would get these
+key letters (see below) begins with a
+.B \- ,
+all of these debugging flags will be set, resulting in massive amounts
+of output.
+.IP "\fB\-f\fP \fImakefile\fP"
+.Ix 0 def flags -f
+Specify a makefile to read different from the standard makefiles
+.CW Makefile "\&" (
+or
+.CW makefile ).
+.Ix 0 ref makefile default
+.Ix 0 ref makefile other
+If
+.I makefile
+is ``\-'', PMake uses the standard input. This is useful for making
+quick and dirty makefiles.\|.\|.
+.Ix 0 ref makefile "quick and dirty"
+.IP \fB\-h\fP
+.Ix 0 def flags -h
+Prints out a summary of the various flags PMake accepts. It can also
+be used to find out what level of concurrency was compiled into the
+version of PMake you are using (look at
+.B \-J
+and
+.B \-L )
+and various other information on how PMake was configured.
+.Ix 0 ref configuration
+.Ix 0 ref makefile system
+.IP \fB\-i\fP
+.Ix 0 def flags -i
+If you give this flag, PMake will ignore non-zero status returned
+by any of its shells. It's like placing a `\-' before all the commands
+in the makefile.
+.IP \fB\-k\fP
+.Ix 0 def flags -k
+This is similar to
+.B \-i
+in that it allows PMake to continue when it sees an error, but unlike
+.B \-i ,
+where PMake continues blithely as if nothing went wrong,
+.B \-k
+causes it to recognize the error and only continue work on those
+things that don't depend on the target, either directly or indirectly (through
+depending on something that depends on it), whose creation returned the error.
+The `k' is for ``keep going''.\|.\|.
+.Ix 0 ref target
+.IP \fB\-l\fP
+.Ix 0 def flags -l
+This turns off directory locking. Normally when PMake is invoked, it
+checks for the existence of a certain file (``LOCK.make,'' if you must
+know) that indicates that someone else is executing PMake in that
+directory. Because two people doing the same thing in the same place
+can be disastrous for the final product, PMake will refuse to do
+anything until the lockfile is removed (unless you own the file. If
+you're doing the same thing twice, it's you're fault if neither turns
+out right). If you give the
+.B \-l
+flag, however, none of this checking occurs.
+.IP \fB\-n\fP
+.Ix 0 def flags -n
+This flag tells PMake not to execute the commands needed to update the
+out-of-date targets in the makefile. Rather, PMake will simply print
+the commands it would have executed and exit. This is particularly
+useful for checking the correctness of a makefile. If PMake doesn't do
+what you expect it to, it's a good chance the makefile is wrong.
+.IP "\fB\-p\fP \fInumber\fP"
+.Ix 0 def flags -p
+.Ix 0 ref debugging
+This causes PMake to print its input in a reasonable form, though
+not necessarily one that would make immediate sense to anyone but me. The
+.I number
+is a bitwise-or of 1 and 2 where 1 means it should print the input
+before doing any processing and 2 says it should print it after
+everything has been re-created. Thus
+.CW "\-p 3"
+would print it twice\*-once before processing and once after (you
+might find the difference between the two interesting). This is mostly
+useful to me, but you may find it informative in some bizarre circumstances.
+.IP \fB\-q\fP
+.Ix 0 def flags -q
+If you give PMake this flag, it will not try to re-create anything. It
+will just see if anything is out-of-date and exit non-zero if so.
+.IP \fB\-r\fP
+.Ix 0 def flags -r
+When PMake starts up, it reads a default makefile that tells it what
+sort of system it's on and gives it some idea of what to do if you
+don't tell it anything. I'll tell you about it in chapter 3. If you
+give this flag, PMake won't read the default makefile.
+.IP \fB\-s\fP
+.Ix 0 def flags -s
+This causes PMake to not print commands before they're executed. It
+is the equivalent of putting an `@' before every command in the
+makefile.
+.IP \fB\-t\fP
+.Ix 0 def flags -t
+Rather than try to re-create a target, PMake will simply ``touch'' it
+so as to make it appear up-to-date. If the target didn't exist before,
+it will when PMake finishes, but if the target did exist, it will
+appear to have been updated.
+.IP \fB\-B\fP
+.Ix 0 ref compatibility
+.Ix 0 def flags -B
+Forces PMake to be as backwards-compatible with Make as possible while
+still being itself.
+This includes:
+.RS
+.IP \(bu 2
+Executing one shell per shell command
+.IP \(bu 2
+Expanding anything that looks even vaguely like a variable, with the
+empty string replacing any variable PMake doesn't know.
+.IP \(bu 2
+Refusing to allow you to escape a `#' with a backslash.
+.IP \(bu 2
+Permitting undefined variables on dependency lines and conditionals
+(see below). Normally this causes PMake to abort.
+.RE
+.IP \fB\-C\fP
+.Ix 0 def flags -C
+This nullifies any and all compatibility mode flags you may have given
+or implied. It is useful mostly in a makefile that you wrote for PMake
+to avoid bad things happening when someone runs PMake as
+.CW make '' ``
+or has things set in the environment that tell it to be compatible.
+.B \-C
+is
+.I not
+placed in the
+.CW PMAKE
+environment variable or the
+.CW .MAKEFLAGS
+or
+.CW MFLAGS
+global variables.
+.Ix 0 ref variable environment PMAKE
+.Ix 0 ref variable global .MAKEFLAGS
+.Ix 0 ref variable global MFLAGS
+.Ix 0 ref .MAKEFLAGS variable
+.Ix 0 ref MFLAGS
+.IP "\fB\-D\fP \fIvariable\fP"
+.Ix 0 def flags -D
+Allows you to define a variable to have the empty string as its value.
+The variable is a global variable, not a command-line variable. This
+is useful mostly for people who are used to the C compiler arguments
+and those using conditionals, which I'll get into in chapter 4.
+.IP "\fB\-I\fP \fIdirectory\fP"
+.Ix 0 def flags -I
+Tells PMake another place to search for included makefiles. Yet
+another thing to be explained in chapter 3.
+.IP "\fB\-J\fP \fInumber\fP"
+.Ix 0 def flags -J
+Gives the absolute maximum number of targets to create at once on both
+local and remote machines.
+.IP "\fB\-L\fP \fInumber\fP"
+.Ix 0 def flags -L
+This specifies the maximum number of targets to create on the local
+machine at once. This may be 0, though you should be wary of doing
+this, as PMake will hang until a remote machine becomes available, if
+one is not available when it is started.
+.IP \fB\-M\fP
+.Ix 0 ref compatibility
+.Ix 0 def flags -M
+This is the flag that provides absolute, complete, full compatibility
+with Make. It still allows you to use all but a few of the features of
+PMake, but it is non-parallel. This is the mode PMake enters if you
+call it
+.CW make .'' ``
+.IP \fB\-P\fP
+.Ix 0 def flags -P
+.Ix 0 ref "output control"
+When creating targets in parallel, several shells are executing at
+once, each wanting to write its own two cent's-worth to the screen.
+This output must be captured by PMake in some way in order to prevent
+the screen from being filled with garbage even more indecipherable
+than you usually see. PMake has two ways of doing this, one of which
+provides for much cleaner output and a clear separation between the
+output of different jobs, the other of which provides a more immediate
+response so one can tell what is really happpening. The former is done
+by notifying you when the creation of a target starts, capturing the
+output and transferring it to the screen all at once when the job
+finishes. The latter is done by catching the output of the shell (and
+its children) and buffering it until an entire line is received, then
+printing that line preceeded by an indication of which job produced
+the output. Since I prefer this second method, it is the one used by
+default. The first method will be used if you give the
+.B \-P
+flag to PMake.
+.IP \fB\-V\fP
+.Ix 0 def flags -V
+As mentioned before, the
+.B \-V
+flag tells PMake to use Make's style of expanding variables,
+substituting the empty string for any variable it doesn't know.
+.IP \fB\-W\fP
+.Ix 0 def flags -W
+There are several times when PMake will print a message at you that is
+only a warning, i.e. it can continue to work in spite of your having
+done something silly (such as forgotten a leading tab for a shell
+command). Sometimes you are well aware of silly things you have done
+and would like PMake to stop bothering you. This flag tells it to shut
+up about anything non-fatal.
+.IP \fB\-X\fP
+.Ix 0 def flags -X
+This flag causes PMake to not attempt to export any jobs to another
+machine (unless the
+.B \-M
+flag is in effect, or PMake was invoked as
+.CW make ,'' ``
+in which case it means
+.I do
+attempt to export jobs to another machine.
+Why? Because exportation slows the process down substantially when
+only a single target is made at a time).
+.PP
+Several flags may follow a single `\-'. Those flags that require
+arguments take them from successive parameters. E.g.
+.DS
+pmake -fDnI server.mk DEBUG /chip2/X/server/include
+.DE
+will cause PMake to read
+.CW server.mk
+as the input makefile, define the variable
+.CW DEBUG
+as a global variable and look for included makefiles in the directory
+.CW /chip2/X/server/include .
+.xH 2 Summary
+.PP
+A makefile is made of four types of lines:
+.RS
+.IP \(bu 2
+Dependency lines
+.IP \(bu 2
+Creation commands
+.IP \(bu 2
+Variable assignments
+.IP \(bu 2
+Comments, include statements and conditional directives
+.RE
+.PP
+A dependency line is a list of one or more targets, an operator
+.CW : ', (`
+.CW :: ', `
+or
+.CW ! '), `
+and a list of zero or more sources.
+.PP
+A creation command is a regular shell command preceeded by a tab. In
+addition, if the first two characters after the tab (and other
+whitespace) are a combination of
+.CW @ ' `
+or
+.CW - ', `
+PMake will cause the command to not be printed (if the character is
+.CW @ ') `
+or errors from it to be ignored (if
+.CW - '). `
+A blank line, dependency line or variable assignment terminates a
+creation script. There may be only one creation script for each target
+with a
+.CW : ' `
+or
+.CW ! ' `
+operator.
+.PP
+Variables are places to store text. They may be unconditionally
+assigned-to using the
+.CW = ' `
+operator, appended-to using the
+.CW += ' `
+operator, conditionally (if the variable is undefined) assigned-to
+with the
+.CW ?= ' `
+operator, and assigned-to with variable expansion with the
+.CW := ' `
+operator. They may be expanded (their value inserted) by enclosing
+their name in parentheses or curly braces, prceeded by a dollar sign.
+A dollar sign may be escaped with another dollar sign. Variables are
+not expanded if PMake doesn't know about them. There are seven local
+variables:
+.CW .TARGET ,
+.CW .ALLSRC ,
+.CW .OODATE ,
+.CW .PREFIX ,
+.CW .IMPSRC ,
+.CW .ARCHIVE ,
+and
+.CW .MEMBER .
+Variables are good. Know them. Love them. Live them.
+.PP
+Debugging of makefiles is best accomplished using the
+.B \-n ,
+.B "\-d m" ,
+and
+.B "\-p 2"
+flags.
+.xH 2 Exercises
+.ce
+\s+4\fBTBA\fP\s0
+.xH Short-cuts and Other Nice Things
+.PP
+Based on what I've told you so far, you may have gotten the impression
+that PMake is just a way of storing away commands and making sure you
+don't forget to compile something. Good. That's just what it is.
+However, the ways I've described have been inelegant, at best, and
+painful, at worst.
+This chapter contains things that make the
+writing of makefiles easier and the makefiles themselves shorter and
+easier to modify (and, occasionally, simpler). In this chapter, I
+assume you are somewhat more
+familiar with Sprite (or UNIX, if that's what you're using) than I did
+in chapter 2, just so you're on your toes.
+So without further ado...
+.xH 2 Transformation Rules
+.PP
+As you know, a file's name consists of two parts: a base name, which
+gives some hint as to the contents of the file, and a suffix, which
+usually indicates the format of the file.
+Over the years, as
+.UX
+has developed,
+naming conventions, with regards to suffixes, have also developed that have
+become almost as incontrovertible as Law. E.g. a file ending in
+.CW .c
+is assumed to contain C source code; one with a
+.CW .o
+suffix is assumed to be a compiled, relocatable object file that may
+be linked into any program; a file with a
+.CW .ms
+suffix is usually a text file to be processed by Troff with the \-ms
+macro package, and so on.
+One of the best aspects of both Make and PMake comes from their
+understanding of how the suffix of a file pertains to its contents and
+their ability to do things with a file based soley on its suffix. This
+ability comes from something known as a transformation rule. A
+transformation rule specifies how to change a file with one suffix
+into a file with another suffix.
+.PP
+A transformation rule looks much like a dependency line, except the
+target is made of two known suffixes stuck together. Suffixes are made
+known to PMake by placing them as sources on a dependency line whose
+target is the special target
+.CW .SUFFIXES .
+E.g.
+.DS
+\&.SUFFIXES : .o .c
+\&.c.o :
+ $(CC) $(CFLAGS) -c $(.IMPSRC)
+.DE
+The creation script attached to the target is used to transform a file with
+the first suffix (in this case,
+.CW .c )
+into a file with the second suffix (here,
+.CW .o ).
+In addition, the target inherits whatever attributes have been applied
+to the transformation rule.
+The simple rule given above says that to transform a C source file
+into an object file, you compile it using
+.CW cc
+with the
+.CW \-c
+flag.
+This rule is taken straight from the system makefile. Many
+transformation rules (and suffixes) are defined there, and I refer you
+to it for more examples (type
+.CW "pmake -h" '' ``
+to find out where it is).
+.PP
+There are several things to note about the transformation rule given
+above:
+.RS
+.IP 1)
+The
+.CW .IMPSRC
+variable.
+.Ix 0 def variable local .IMPSRC
+.Ix 0 def .IMPSRC
+This variable is set to the ``implied source'' (the file from which
+the target is being created; the one with the first suffix) which, in this
+case, is the .c file.
+.IP 2)
+The
+.CW CFLAGS
+variable. Almost all of the transformation rules in the system
+makefile are set up using variables that you can alter in your
+makefile to tailor the rule to your needs. In this case, if you want
+all your C files to be compiled with the
+.B \-g
+flag, to provide information for
+.CW dbx ,
+you would set the
+.CW CFLAGS
+variable to contain
+.CW -g
+.CW "CFLAGS = -g" '') (``
+and PMake would take care of the rest.
+.RE
+.PP
+To give you a quick example, the first makefile in chapter 2 could be
+changed to this:
+.DS
+OBJS = a.o b.o c.o
+program : $(OBJS)
+ $(CC) -o $(.TARGET) $(.ALLSRC)
+$(OBJS) : defs.h
+.DE
+The transformation rule I gave above takes the place of the 6 lines
+.DS
+a.o : a.c
+ cc -c a.c
+b.o : b.c
+ cc -c b.c
+c.o : c.c
+ cc -c c.c
+.DE
+.PP
+Now you may be wondering about the dependency between the
+.CW .o
+and
+.CW .c
+files \*- it's not mentioned anywhere in the new makefile. This is
+because it isn't needed: one of the effects of applying a
+transformation rule is the target comes to depend on the implied
+source. That's why it's called the implied
+.I source .
+.PP
+For a more detailed example. Say you have a makefile like this:
+.DS
+a.out : a.o b.o
+ $(CC) $(.ALLSRC)
+.DE
+and a directory set up like this:
+.DS
+total 4
+-rw-rw-r-- 1 deboor 34 Sep 7 00:43 Makefile
+-rw-rw-r-- 1 deboor 119 Oct 3 19:39 a.c
+-rw-rw-r-- 1 deboor 201 Sep 7 00:43 a.o
+-rw-rw-r-- 1 deboor 69 Sep 7 00:43 b.c
+.DE
+While just typing
+.CW pmake '' ``
+will do the right thing, it's much more informative to type
+.CW "pmake -d s" ''. ``
+This will show you what PMake is up to as it processes the files. In
+this case, PMake prints the following:
+.DS
+Suff_FindDeps (a.out)
+ using existing source a.o
+ applying .o -> .out to "a.o"
+Suff_FindDeps (a.o)
+ trying a.c...got it
+ applying .c -> .o to "a.c"
+Suff_FindDeps (b.o)
+ trying b.c...got it
+ applying .c -> .o to "b.c"
+Suff_FindDeps (a.c)
+ trying a.y...not there
+ trying a.l...not there
+ trying a.c,v...not there
+ trying a.y,v...not there
+ trying a.l,v...not there
+Suff_FindDeps (b.c)
+ trying b.y...not there
+ trying b.l...not there
+ trying b.c,v...not there
+ trying b.y,v...not there
+ trying b.l,v...not there
+--- a.o ---
+cc -c a.c
+--- b.o ---
+cc -c b.c
+--- a.out ---
+cc a.o b.o
+.DE
+.PP
+.CW Suff_FindDeps
+is the name of a function in PMake that is called to check for implied
+sources for a target using transformation rules.
+The transformations it tries are, naturally
+enough, limited to the ones that have been defined (a transformation
+may be defined multiple times, by the way, but only the most recent
+one will be used). You will notice, however, that there is a definite
+order to the suffixes that are tried. This order is set by the
+relative positions of the suffixes on the
+.CW .SUFFIXES
+line \*- the earlier a suffix appears, the earlier it is checked as
+the source of a transformation. Once a suffix has been defined, the
+only way to change its position in the pecking order is to remove all
+the suffixes (by having a
+.CW .SUFFIXES
+dependency line with no sources) and redefine them in the order you
+want. (Previously-defined transformation rules will be automatically
+redefined as the suffixes they involve are re-entered.)
+.PP
+Another way to affect the search order is to make the dependency
+explicit. In the above example,
+.CW a.out
+depends on
+.CW a.o
+and
+.CW b.o .
+Since a transformation exists from
+.CW .o
+to
+.CW .out ,
+PMake uses that, as indicated by the
+.CW "using existing source a.o" '' ``
+message.
+.PP
+The search for a transformation starts from the suffix of the target
+and continues through all the defined transformations, in the order
+dictated by the suffix ranking, until an existing file with the same
+base (the target name minus the suffix and any leading directories) is
+found. At that point, one or more transformation rules will have been
+found to change the one existing file into the target.
+.PP
+For example, ignoring what's in the system makefile for now, say you
+have a makefile like this:
+.DS
+\&.SUFFIXES : .out .o .c .y .l
+\&.l.c :
+ lex $(.IMPSRC)
+ mv lex.yy.c $(.TARGET)
+\&.y.c :
+ yacc $(.IMPSRC)
+ mv y.tab.c $(.TARGET)
+\&.c.o :
+ cc -c $(.IMPSRC)
+\&.o.out :
+ cc -o $(.TARGET) $(.IMPSRC)
+.DE
+and the single file
+.CW jive.l .
+If you were to type
+.CW "pmake -rd jive.out ms" ,'' ``
+you would get the following output for
+.CW jive.out :
+.DS
+Suff_FindDeps (jive.out)
+ trying jive.o...not there
+ trying jive.c...not there
+ trying jive.y...not there
+ trying jive.l...got it
+ applying .l -> .c to "jive.l"
+ applying .c -> .o to "jive.c"
+ applying .o -> .out to "jive.o"
+.DE
+and this is why: PMake starts with the target
+.CW jive.out ,
+figures out its suffix
+.CW .out ) (
+and looks for things it can transform to a
+.CW .out
+file. In this case, it only finds
+.CW .o ,
+so it looks for the file
+.CW jive.o .
+It fails to find it, so it looks for transformations into a
+.CW .o
+file. Again it has only one choice:
+.CW .c .
+So it looks for
+.CW jive.c
+and, as you know, fails to find it. At this point it has two choices:
+it can create the
+.CW .c
+file from either a
+.CW .y
+file or a
+.CW .l
+file. Since
+.CW .y
+came first on the
+.CW .SUFFIXES
+line, it checks for
+.CW jive.y
+first, but can't find it, so it looks for
+.CW jive.l
+and, lo and behold, there it is.
+At this point, it has defined a transformation path as follows:
+.CW .l
+\(->
+.CW .c
+\(->
+.CW .o
+\(->
+.CW .out
+and applies the transformation rules accordingly. For completeness,
+and to give you a better idea of what PMake actually did with this
+three-step transformation, this is what PMake printed for the rest of
+the process:
+.DS
+Suff_FindDeps (jive.o)
+ using existing source jive.c
+ applying .c -> .o to "jive.c"
+Suff_FindDeps (jive.c)
+ using existing source jive.l
+ applying .l -> .c to "jive.l"
+Suff_FindDeps (jive.l)
+Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date
+Examining jive.c...non-existent...out-of-date
+--- jive.c ---
+lex jive.l
+.\|.\|. meaningless lex output deleted .\|.\|.
+mv lex.yy.c jive.c
+Examining jive.o...non-existent...out-of-date
+--- jive.o ---
+cc -c jive.c
+Examining jive.out...non-existent...out-of-date
+--- jive.out ---
+cc -o jive.out jive.o
+.DE
+.PP
+One final question remains: what does PMake do with targets that have
+no known suffix? PMake simply pretends it actually has a known suffix
+and searches for transformations accordingly.
+The suffix it chooses is the source for the
+.CW .NULL
+.Ix 0 ref .NULL
+target mentioned later. In the system makefile,
+.CW .out
+is chosen as the ``null suffix''
+.Ix 0 def suffix null
+.Ix 0 def "null suffix"
+because most people use PMake to create programs. You are, however,
+free and welcome to change it to a suffix of your own choosing.
+The null suffix is ignored, however, when PMake is in compatibility
+mode (see chapter 4).
+.xH 2 Including Other Makefiles
+.Ix 0 def makefile inclusion
+.PP
+Just as for programs, it is often useful to extract certain parts of a
+makefile into another file and just include it in other makefiles
+somehow. Many compilers allow you say something like
+.DS
+#include "defs.h"
+.DE
+to include the contents of
+.CW defs.h
+in the source file. PMake allows you to do the same thing for
+makefiles, with the added ability to use variables in the filenames.
+An include directive in a makefile looks either like this:
+.DS
+#include <file>
+.DE
+or this
+.DS
+#include "file"
+.DE
+The difference between the two is where PMake searches for the file:
+the first way, PMake will look for
+the file only in the system makefile directory (to find out what that
+directory is, give PMake the
+.B \-h
+flag).
+.Ix 0 ref flags -h
+For files in double-quotes, the search is more complex:
+.RS
+.IP 1)
+The directory of the makefile that's including the file.
+.IP 2)
+The current directory (the one in which you invoked PMake).
+.IP 3)
+The directories given by you using
+.B \-I
+flags, in the order in which you gave them.
+.IP 4)
+Directories given by
+.CW .PATH
+dependency lines (see chapter 4).
+.IP 5)
+The system makefile directory.
+.RE
+.LP
+in that order.
+.PP
+You are free to use PMake variables in the filename\*-PMake will
+expand them before searching for the file. You must specify the
+searching method with either angle brackets or double-quotes
+.I outside
+of a variable expansion. I.e. the following
+.DS
+SYSTEM = <command.mk>
+
+#include $(SYSTEM)
+.DE
+won't work.
+.xH 2 Saving Commands
+.PP
+.Ix 0 def ...
+There may come a time when you will want to save certain commands to
+be executed when everything else is done. For instance: you're
+making several different libraries at one time and you want to create the
+members in parallel. Problem is,
+.CW ranlib
+is another one of those programs that can't be run more than once in
+the same directory at the same time (each one creates a file called
+.CW __.SYMDEF
+into which it stuffs information for the linker to use. Two of them
+running at once will overwrite each other's file and the result will
+be garbage for both parties). You might want a way to save the ranlib
+commands til the end so they can be run one after the other, thus
+keeping them from trashing each other's file. PMake allows you to do
+this by inserting an ellipsis (``.\|.\|.'') as a command between
+commands to be run at once and those to be run later.
+.PP
+So for the
+.CW ranlib
+case above, you might do this:
+.DS
+lib1.a : $(LIB1OBJS)
+ rm -f $(.TARGET)
+ ar cr $(.TARGET) $(.ALLSRC)
+ ...
+ ranlib $(.TARGET)
+
+lib2.a : $(LIB2OBJS)
+ rm -f $(.TARGET)
+ ar cr $(.TARGET) $(.ALLSRC)
+ ...
+ ranlib $(.TARGET)
+.DE
+.Ix 0 ref variable local .TARGET
+.Ix 0 ref variable local .ALLSRC
+This would save both
+.DS
+ranlib $(.TARGET)
+.DE
+commands until the end, when they would run one after the other
+(using the correct value for the
+.CW .TARGET
+variable, of course).
+.PP
+Commands saved in this manner are only executed if PMake manages to
+re-create everything without an error. In addition, any `@' or `\-'
+characters that precede the command are ignored.
+.xH 2 Target Attributes
+.PP
+PMake allows you to give attributes to targets by means of special
+sources. Like everything else PMake uses, these sources begin with a
+period and are made up of all upper-case letters. There are various
+reasons for using them, and I will try to give examples for most of
+them. Others you'll have to find uses for yourself. Think of it as ``an
+exercise for the reader.'' By placing one (or more) of these as a source on a
+dependency line, you are ``marking the target(s) with that
+attribute.'' That's just the way I phrase it, so you know.
+.PP
+Any attributes given as sources for a transformation rule are applied
+to the target of the transformation rule when the rule is applied.
+.Ix 0 def attributes
+.Ix 0 ref source
+.Ix 0 ref target
+.nr pw \w'.EXPORTSAME 'u
+.IP .DONTCARE \n(pwu
+.Ix 0 def attributes .DONTCARE
+.Ix 0 def .DONTCARE
+If a target is marked with this attribute and PMake can't figure out
+how to create it, it will ignore this fact and assume the file isn't
+really needed or actually exists and PMake just can't find it. This may prove
+wrong, but the error will be noted later on, not when PMake tries to create
+the target so marked.
+.IP .EXEC \n(pwu
+.Ix 0 def attributes .EXEC
+.Ix 0 def .EXEC
+This attribute causes its shell script to be executed while having no
+effect on targets that depend on it. This makes the target into a sort
+of subroutine. An example. Say you have some LISP files that need to
+be compiled and loaded into a LISP process. To do this, you echo LISP
+commands into a file and execute a LISP with this file as its input
+when everything's done. Say also that you have to load other files
+from another system before you can compile your files and further,
+that you don't want to go through the loading and dumping unless one
+of .I your files has changed. Your makefile might look a little bit
+like this (remember this is an educational example):
+.DS
+system : init a.fasl b.fasl c.fasl
+ for i in $(.ALLSRC);
+ do
+ echo -n '(load "' >> input
+ echo -n ${i} >> input
+ echo '")' >> input
+ done
+ echo '(dump "system")' >> input
+ lisp < input
+
+a.fasl : a.l init COMPILE
+b.fasl : b.l init COMPILE
+c.fasl : c.l init COMPILE
+COMPILE : .USE
+ echo '(compile "$(.ALLSRC)")' >> input
+init : .EXEC
+ echo '(load-system)' > input
+.DE
+.Ix 0 ref .USE
+.Ix 0 ref attributes .USE
+.Ix 0 ref variable local .ALLSRC
+.IP "\&"
+.CW .EXEC
+sources, don't appear in the local variables of targets that depend on
+them. Note that all the rules, not just that for
+.CW system ,
+include
+.CW init
+as a source. This is because none of the other targets can be made
+until
+.CW init
+has been made, thus they depend on it.
+.IP .EXPORT \n(pwu
+.Ix 0 def attributes .EXPORT
+.Ix 0 def .EXPORT
+This is used to mark those targets whose creation should be sent to
+another machine if at all possible. This is may be used by some
+exportation schemes if the exportation is expensive. You should ask
+your administrator if it is necessary.
+.IP .EXPORTSAME \n(pwu
+.Ix 0 def attributes .EXPORTSAME
+.Ix 0 def .EXPORTSAME
+Tells the export system that the job should be exported to a machine
+of the same architecture as the current one. Certain operations (e.g.
+running text through
+.CW nroff )
+can be performed the same on any architecture (CPU and
+operating system type), while others (e.g. compiling a program with
+.CW cc )
+must be performed on a machine with the same architecture. Not all
+export systems will support this attribute.
+.IP .IGNORE \n(pwu
+.Ix 0 def attributes .IGNORE
+.Ix 0 def .IGNORE attribute
+Giving a target the
+.CW .IGNORE
+attribute causes PMake to ignore errors from any of the target's commands, as
+if they all had `\-' before them.
+.IP .INVISIBLE \n(pwu
+.Ix 0 def attributes .INVISIBLE
+.Ix 0 def .INVISIBLE
+This allows you to specify one target as a source for another without
+the one affecting the other's local variables. Useful if, say, you
+have a makefile that creates two programs, one of which is used to
+create the other, so it must exist before the other is created. You
+could say
+.DS
+prog1 : $(PROG1OBJS) prog2 MAKEINSTALL
+prog2 : $(PROG2OBJS) .INVISIBLE MAKEINSTALL
+.DE
+where
+.CW MAKEINSTALL
+is some complex .USE rule (see below) that depends on the
+.Ix 0 ref .USE
+.CW .ALLSRC
+variable containing the right things. Without the
+.CW .INVISIBLE
+attribute for
+.CW prog2 ,
+the
+.CW MAKEINSTALL
+rule couldn't be applied. This is not as useful as it should be, and
+the semantics may change (or the whole thing go away) in the
+not-too-distant future.
+.IP .JOIN \n(pwu
+.Ix 0 def attributes .JOIN
+.Ix 0 def .JOIN
+This is another way to avoid performing some operations in parallel
+while permitting everything else to be done so. Specifically it
+forces the target's shell script to be executed only if one or more of the
+sources was out-of-date. In addition, the target's name,
+in both its
+.CW .TARGET
+variable and all the local variables of any target that depends on it,
+is replaced by the value of its
+.CW .ALLSRC
+variable.
+As an example, suppose you have a program that has four libraries that
+compile in the same directory along with, and at the same time as, the
+program. You again have the problem with
+.CW ranlib
+that I mentioned earlier, only this time it's more severe: you
+can't just sort of put the ranlib off to the end since the program
+will need those libraries before it can be re-created. You can do
+something like this:
+.DS
+program : $(OBJS) libraries
+ cc -o $(.TARGET) $(.ALLSRC)
+
+libraries : lib1.a lib2.a lib3.a lib4.a .JOIN
+ ranlib $(.OODATE)
+.DE
+.Ix 0 ref variable local .TARGET
+.Ix 0 ref variable local .ALLSRC
+.Ix 0 ref variable local .OODATE
+.Ix 0 ref .TARGET
+.Ix 0 ref .ALLSRC
+.Ix 0 ref .OODATE
+In this case, PMake will re-create the
+.CW $(OBJS)
+as necessary, along with
+.CW lib1.a ,
+.CW lib2.a ,
+.CW lib3.a
+and
+.CW lib4.a .
+It will then execute
+.CW ranlib
+on any library that was changed and set
+.CW program 's
+.CW .ALLSRC
+variable to contain what's in
+.CW $(OBJS)
+followed by
+.CW "lib1.a lib2.a lib3.a lib4.a" .'' ``
+In case you're wondering, it's called
+.CW .JOIN
+because it joins together different threads of the ``input graph'' at
+the target marked with the attribute.
+Another aspect of the .JOIN attribute is it keeps the target from
+being created if the
+.B \-t
+flag was given.
+.Ix 0 ref flags -t
+.IP .MAKE \n(pwu
+.Ix 0 def attributes .MAKE
+.Ix 0 def .MAKE
+The
+.CW .MAKE
+attribute marks its target as being a recursive invocation of PMake.
+What this does is force PMake to execute the script associated with
+the target (if it's out-of-date) even if you gave the
+.B \-n
+or
+.B \-t
+flag. By doing this, you can start at the top of a system and type
+.DS
+pmake -n
+.DE
+and have it descend the directory tree (if your makefiles are set up
+correctly), printing what it would have executed if you hadn't
+included the
+.B \-n
+flag.
+.IP .NOEXPORT \n(pwu
+.Ix 0 def attributes .NOEXPORT
+.Ix 0 def .NOEXPORT attribute
+If possible, PMake will attempt to export the creation of all targets to
+another machine (this depends on how PMake was configured). Sometimes,
+the creation is so simple, it is pointless to send it to another
+machine. If you give the target the
+.CW .NOEXPORT
+attribute, it will be run locally, even if you've given PMake the
+.B "\-L 0"
+flag.
+.IP .NOTMAIN \n(pwu
+.Ix 0 def attributes .NOTMAIN
+.Ix 0 def .NOTMAIN
+Normally, if you do not specify a target to make in any other way,
+PMake will take the first target on the first dependency line of a
+makefile as the target to create. That target is known as the ``Main
+Target'' and is labelled as such if you print the dependencies out
+using the
+.B \-p
+flag.
+.Ix 0 ref flags -p
+Giving a target this attribute tells PMake that the target is
+definitely
+.I not
+the Main Target.
+This allows you to place targets in an included makefile and
+have PMake create something else by default.
+.IP .PRECIOUS \n(pwu
+.Ix 0 def attributes .PRECIOUS
+.Ix 0 def .PRECIOUS attribute
+When PMake is interrupted (you type control-C at the keyboard), it
+will attempt to clean up after itself by removing any half-made
+targets. If a target has the
+.CW .PRECIOUS
+attribute, however, PMake will leave it alone. An additional side
+effect of the `::' operator is to mark the targets as
+.CW .PRECIOUS .
+.Ix 0 ref operator double-colon
+.Ix 0 ref ::
+.IP .SILENT \n(pwu
+.Ix 0 def attributes .SILENT
+.Ix 0 def .SILENT attribute
+Marking a target with this attribute keeps its commands from being
+printed when they're executed, just as if they had an `@' in front of them.
+.IP .USE \n(pwu
+.Ix 0 def attributes .USE
+.Ix 0 def .USE
+By giving a target this attribute, you turn the target into PMake's equivalent
+of a macro. When the target is used as a source for another target,
+the other target acquires the commands, sources and attributes (except
+.CW .USE )
+of the source.
+If the target already has commands, the
+.CW .USE
+target's commands are added to the end. If more than one .USE-marked
+source is given to a target, the rules are applied sequentially.
+.IP "\&" \n(pwu
+The typical .USE rule (as I call them) will use the sources of the
+target to which it is applied (as stored in the
+.CW .ALLSRC
+variable for the target) as its ``arguments,'' if you will.
+For example, you probably noticed that the commands for creating
+.CW lib1.a
+and
+.CW lib2.a
+in the example above were exactly the same. You can use the
+.CW .USE
+attribute to eliminate the repetition, like so:
+.DS
+lib1.a : $(LIB1OBJS) MAKELIB
+lib2.a : $(LIB2OBJS) MAKELIB
+
+MAKELIB : .USE
+ rm -f $(.TARGET)
+ ar cr $(.TARGET) $(.ALLSRC)
+ ...
+ ranlib $(.TARGET)
+.DE
+.Ix 0 ref variable local .TARGET
+.Ix 0 ref variable local .ALLSRC
+.IP "\&" \n(pwu
+Several system makefiles (not to be confused with The System Makefile)
+exist that make use of these .USE rules to make your
+life easier. (They're in the default, system makefile directory. Take a look.)
+Note that the .USE rule source itself
+.CW MAKELIB ) (
+does not appear in any of the targets's local variables.
+There is no limit to the number of times I could use the
+.CW MAKELIB
+rule. If there were more libraries, I could continue with
+.CW "lib3.a : $(LIB3OBJS) MAKELIB" '' ``
+and so on and so forth.
+.xH 2 Special Targets
+.PP
+As there were in Make, so there are certain targets that have special
+meaning to PMake. When you use one on a dependency line, it is the
+only target that may appear on the left-hand-side of the operator.
+.Ix 0 ref target
+.Ix 0 ref operator
+As for the attributes and variables, all the special targets
+begin with a period and consist of upper-case letters only.
+I won't describe them all in detail because some of them are rather
+complex and I'll describe them in more detail than you'll want in
+chapter 4.
+The targets are as follows:
+.nr pw \w'.MAKEFLAGS 'u
+.IP .BEGIN \n(pwu
+.Ix 0 def .BEGIN
+Any commands attached to this target are executed before anything else
+is done. You can use it for any initialization that needs doing.
+Errors are checked as for any other target, but the only way to turn
+off error checking is to use the
+.CW .IGNORE
+.Ix 0 ref .IGNORE attribute
+.Ix 0 ref attributes .IGNORE
+attribute. This may change.
+.IP .DEFAULT \n(pwu
+.Ix 0 def .DEFAULT
+This is sort of a .USE rule for any target (that was used only as a
+source) that PMake can't figure out any other way to create. It's only
+``sort of'' a .USE rule because only the shell script attached to the
+.CW .DEFAULT
+target is used. The
+.CW .IMPSRC
+variable of a target that inherits
+.CW .DEFAULT 's
+commands is set to the target's own name.
+.Ix 0 ref .IMPSRC
+.Ix 0 ref variable local .IMPSRC
+.IP .END \n(pwu
+.Ix 0 def .END
+This serves a function similar to
+.CW .BEGIN ,
+in that commands attached to it are executed once everything has been
+re-created (so long as no errors occurred). It also serves the extra
+function of being a place on which PMake can hang commands you put off
+to the end. Thus the script for this target will be executed before
+any of the commands you save with the ``.\|.\|.''. There is no echo
+control or error ignoring allowed, save with
+.CW .SILENT
+and
+.CW .IGNORE
+attributes.
+.Ix 0 ref ...
+.IP .EXPORT \n(pwu
+The sources for this target are passed to the exportation system compiled
+into PMake. Some systems will use these sources to configure
+themselves. You should ask your system administrator about this.
+.IP .IGNORE \n(pwu
+.Ix 0 def .IGNORE target
+.Ix 0 ref .IGNORE attribute
+.Ix 0 ref attributes .IGNORE
+This target marks each of its sources with the
+.CW .IGNORE
+attribute. If you don't give it any sources, then it is like
+giving the
+.B \-i
+flag when you invoke PMake \*- errors are ignored for all commands.
+.Ix 0 ref flags -i
+.IP .INCLUDES \n(pwu
+.Ix 0 def .INCLUDES target
+.Ix 0 def variable global .INCLUDES
+.Ix 0 def .INCLUDES variable
+The sources for this target are taken to be suffixes that indicate a
+file that can be included in a program source file.
+The suffix must have already been declared with
+.CW .SUFFIXES
+(see below).
+Any suffix so marked will have the directories on its search path
+(see
+.CW .PATH ,
+below) placed in the
+.CW .INCLUDES
+variable, each preceeded by a
+.B \-I
+flag. This variable can then be used as an argument for the compiler
+in the normal fashion. The
+.CW .h
+suffix is already marked in this way in the system makefile.
+.Ix 0 ref makefile system
+E.g. if you have
+.DS
+.SUFFIXES : .bitmap
+.PATH.bitmap : /usr/local/X/lib/bitmaps
+.INCLUDES : .bitmap
+.DE
+PMake will place
+.CW "-I/usr/local/X/lib/bitmaps" '' ``
+in the
+.CW .INCLUDES
+variable and you can then say
+.DS
+cc $(.INCLUDES) -c xprogram.c
+.DE
+(Note: the
+.CW .INCLUDES
+variable is not actually filled in until the entire makefile has been read.)
+.IP .INTERRUPT \n(pwu
+.Ix 0 def .INTERRUPT
+When PMake is interrupted,
+it will execute the commands in the script for this target, if it
+exists. There is neither error nor echo control for the commands
+attached to the
+.CW .INTERRUPT
+target.
+.IP .LIBS \n(pwu
+.Ix 0 def .LIBS target
+.Ix 0 def .LIBS variable
+.Ix 0 def variable global .LIBS
+This does for libraries what
+.CW .INCLUDES
+does for include files, except the flag used is
+.B \-L ,
+as required by those linkers that allow you to tell them where to find
+libraries. The variable used is
+.CW .LIBS .
+Be forewarned that PMake may not have been compiled to do this if the
+linker on your system doesn't accept the
+.B \-L
+flag, though the
+.CW .LIBS
+variable will always be defined once the makefile has been read.
+.IP .MAIN \n(pwu
+.Ix 0 def .MAIN
+If you didn't give a target (or targets) to create when you invoked
+PMake, it will take the sources of this target as the targets to
+create.
+.IP .MAKEFLAGS \n(pwu
+.Ix 0 def .MAKEFLAGS target
+This target provides a way for you to always specify flags for PMake
+when the makefile is used. The flags are just as they would be typed
+to the shell (except you can't use shell variables unless they're in
+the environment),
+though the
+.B \-f
+and
+.B \-r
+flags have no effect.
+.IP .NULL \n(pwu
+.Ix 0 def .NULL
+.Ix 0 ref suffix null
+.Ix 0 ref "null suffix"
+This allows you to specify what suffix PMake should pretend a file has
+if, in fact, it has no known suffix. Only one suffix may be so
+designated. The last source on the dependency line is the suffix that
+is used (you should, however, only give one suffix.\|.\|.).
+.IP .PATH \n(pwu
+.Ix 0 def .PATH
+If you give sources for this target, PMake will take them as
+directories to search for files it cannot find in the current
+directory. If you give no sources, it will clear out any directories
+added to the search path before. Since the effects of this all get
+very complex, I'll leave it til chapter four to give you a complete
+explanation.
+.IP .PATH\fIsuffix\fP \n(pwu
+.Ix 0 ref .PATH
+This does a similar thing to
+.CW .PATH ,
+but it does it only for files with the given suffix. The suffix must
+have been defined already. Look at
+.B "Search Paths"
+in chapter 4 for more information.
+.IP .PRECIOUS \n(pwu
+.Ix 0 def .PRECIOUS target
+.Ix 0 ref .PRECIOUS attribute
+.Ix 0 ref attributes .PRECIOUS
+Similar to
+.CW .IGNORE ,
+this gives the
+.CW .PRECIOUS
+attribute to each source on the dependency line, unless there are no
+sources, in which case the
+.CW .PRECIOUS
+attribute is given to every target in the file.
+.IP .RECURSIVE \n(pwu
+.Ix 0 def .RECURSIVE
+.Ix 0 ref attributes .MAKE
+.Ix 0 ref .MAKE
+This target applies the
+.CW .MAKE
+attribute to all its sources. It does nothing if you don't give it any sources.
+.IP .SHELL \n(pwu
+.Ix 0 def .SHELL
+PMake is not constrained to only using the Bourne shell to execute
+the commands you put in the makefile. You can tell it some other shell
+to use with this target. Check out
+.B "A Shell is a Shell is a Shell"
+in chapter 4 for more information.
+.IP .SILENT \n(pwu
+.Ix 0 def .SILENT target
+.Ix 0 ref .SILENT attribute
+.Ix 0 ref attributes .SILENT
+When you use
+.CW .SILENT
+as a target, it applies the
+.CW .SILENT
+attribute to each of its sources. If there are no sources on the
+dependency line, then it is as if you gave PMake the
+.B \-s
+flag and no commands will be echoed.
+.IP .SUFFIXES \n(pwu
+.Ix 0 def .SUFFIXES
+This is used to give new file suffixes for PMake to handle. Each
+source is a suffix PMake should recognize. If you give a
+.CW .SUFFIXES
+dependency line with no sources, PMake will forget about all the
+suffixes it knew (this also nukes the null suffix).
+For those targets that need to have suffixes defined, this is how you do it.
+.PP
+In addition to these targets, a line of the form
+.DS
+\fIattribute\fP : \fIsources\fP
+.DE
+applies the
+.I attribute
+to all the targets listed as
+.I sources .
+.xH 2 Modifying Variable Expansion
+.PP
+.Ix 0 def variable expansion modified
+.Ix 0 ref variable expansion
+.Ix 0 def variable modifiers
+Variables need not always be expanded verbatim. PMake defines several
+modifiers that may be applied to a variable's value before it is
+expanded. You apply a modifier by placing it after the variable name
+with a colon between the two, like so:
+.DS
+${\fIVARIABLE\fP:\fImodifier\fP}
+.DE
+Each modifier is a single character followed by something specific to
+the modifier itself.
+You may apply as many modifiers as you want \*- each one is applied to
+the result of the previous and is separated from the previous by
+another colon.
+.PP
+There are seven ways to modify a variable's expansion, most of which
+come from the C shell variable modification characters:
+.RS
+.IP "M\fIpattern\fP"
+.Ix 0 def :M
+.Ix 0 def modifier match
+This is used to select only those words (a word is a series of
+characters that are neither spaces nor tabs) that match the given
+.I pattern .
+The pattern is a wildcard pattern like that used by the shell, where
+.CW *
+means 0 or more characters of any sort;
+.CW ?
+is any single character;
+.CW [abcd]
+matches any single character that is either `a', `b', `c' or `d'
+(there may be any number of characters between the brackets);
+.CW [0-9]
+matches any single character that is between `0' and `9' (i.e. any
+digit. This form may be freely mixed with the other bracket form), and
+`\\' is used to escape any of the characters `*', `?', `[' or `:',
+leaving them as regular characters to match themselves in a word.
+For example, the system makefile
+.CW <makedepend.mk>
+uses
+.CW "$(CFLAGS:M-[ID]*)" '' ``
+to extract all the
+.CW \-I
+and
+.CW \-D
+flags that would be passed to the C compiler. This allows it to
+properly locate include files and generate the correct dependencies.
+.IP "N\fIpattern\fP"
+.Ix 0 def :N
+.Ix 0 def modifier nomatch
+This is identical to
+.CW :M
+except it substitutes all words that don't match the given pattern.
+.IP "S/\fIsearch-string\fP/\fIreplacement-string\fP/[g]"
+.Ix 0 def :S
+.Ix 0 def modifier substitute
+Causes the first occurrence of
+.I search-string
+in the variable to be replaced by
+.I replacement-string ,
+unless the
+.CW g
+flag is given at the end, in which case all occurences of the string
+are replaced. The substitution is performed on each word in the
+variable in turn. If
+.I search-string
+begins with a
+.CW ^ ,
+the string must match starting at the beginning of the word. If
+.I search-string
+ends with a
+.CW $ ,
+the string must match to the end of the word (these two may be
+combined to force an exact match). If a backslash preceeds these two
+characters, however, they lose their special meaning. Variable
+expansion also occurs in the normal fashion inside both the
+.I search-string
+and the
+.I replacement-string ,
+.B except
+that a backslash is used to prevent the expansion of a
+.CW $ ,
+not another dollar sign, as is usual.
+Note that
+.I search-string
+is just a string, not a pattern, so none of the usual
+regular-expression/wildcard characters have any special meaning save
+.CW ^
+and
+.CW $ .
+In the replacement string,
+the
+.CW &
+character is replaced by the
+.I search-string
+unless it is preceeded by a backslash.
+You are allowed to use any character except
+colon or exclamation point to separate the two strings. This so-called
+delimiter character may be placed in either string by preceeding it
+with a backslash.
+.IP T
+.Ix 0 def :T
+.Ix 0 def modifier tail
+Replaces each word in the variable expansion by its last
+component (its ``tail''). For example, given
+.DS
+OBJS = ../lib/a.o b /usr/lib/libm.a
+TAILS = $(OBJS:T)
+.DE
+the variable
+.CW TAILS
+would expand to
+.CW "a.o b libm.a" .'' ``
+.IP H
+.Ix 0 def :H
+.Ix 0 def modifier head
+This is similar to
+.CW :T ,
+except that every word is replaced by everything but the tail (the
+``head''). Using the same definition of
+.CW OBJS ,
+the string
+.CW "$(OBJS:H)" '' ``
+would expand to
+.CW "../lib /usr/lib" .'' ``
+Note that the final slash on the heads is removed and
+anything without a head is replaced by the empty string.
+.IP E
+.Ix 0 def :E
+.Ix 0 def modifier extension
+.Ix 0 def modifier suffix
+.CW :E
+replaces each word by its suffix (``extension''). So
+.CW "$(OBJS:E)" '' ``
+would give you
+.CW ".o .a" .'' ``
+.IP R
+.Ix 0 def :R
+.Ix 0 def modifier root
+.Ix 0 def modifier base
+This replaces each word by everything but the suffix (the ``root'' of
+the word).
+.CW "$(OBJS:R)" '' ``
+expands to ``
+.CW "../lib/a b /usr/lib/libm" .''
+.RE
+.xH 2 More on Debugging
+.xH 2 More Exercises
+.xH PMake for Gods
+.PP
+This chapter is devoted to those facilities in PMake that allow you to
+do a great deal in a makefile with very little work, as well as do
+some things you couldn't do in Make without a great deal of work (and
+perhaps the use of other programs). The problem with these features,
+is they must be handled with care, or you will end up with a mess.
+.PP
+Once more, I assume a greater familiarity with
+.UX
+or Sprite than I did in the previous two chapters.
+.xH 2 Search Paths
+.PP
+PMake supports the dispersal of files into multiple directories by
+allowing you to specify places to look for sources with
+.CW .PATH
+targets in the makefile. The directories you give as sources for these
+targets make up a ``search path.'' There are two types of search paths
+in PMake: one is used for all types of files (including included
+makefiles) and is specified with a plain
+.CW .PATH
+target (e.g.
+.CW ".PATH : RCS" ''), ``
+while the other is specific to a certain type of file, as indicated by
+the file's suffix. A specific search path is indicated by immediately following
+the
+.CW .PATH
+with the suffix of the file. For instance
+.DS
+\&.PATH.h : /sprite/lib/include /sprite/att/lib/include
+.DE
+would tell PMake to look in the directories
+.CW /sprite/lib/include
+and
+.CW /sprite/att/lib/include
+for any files whose suffix is
+.CW .h .
+.PP
+The current directory is always consulted first to see if a file
+exists. Only if it cannot be found there are the directories in the
+specific search path, followed by those in the general search path,
+consulted. This searching is only performed for those targets in the
+makefile that are used exclusively as sources, that is only for those
+files that aren't created by the makefile.
+.PP
+When a file is found in some directory other than the current one, all
+local variables that would have contained the target's name
+.CW .ALLSRC , (
+and
+.CW .IMPSRC )
+will instead contain the path to the file, as found by PMake.
+Thus if you have a file
+.CW ../lib/mumble.c
+and a makefile
+.DS
+\&.PATH.c : ../lib
+mumble : mumble.c
+ $(CC) -o $(.TARGET) $(.ALLSRC)
+.DE
+the command executed to create
+.CW mumble
+would be
+.CW "cc -o mumble ../lib/mumble.c" .'' ``
+(As an aside, the command in this case isn't strictly necessary, since
+it will be found using transformation rules if it isn't given. This is because
+.CW .out
+is the null suffix by default and a transformation exists from
+.CW .c
+to
+.CW .out .
+Just thought I'd throw that in.)
+.PP
+If a file exists in two directories on the same search path, the file
+in the first directory on the path will be the one PMake uses. So if
+you have a large system spread over many directories, it would behoove
+you to follow a naming convention that avoids such conflicts.
+.PP
+Something you should know about the way search paths are implemented
+is that each directory is read, and its contents cached, exactly once
+\&\*- when it is first encountered \*- so any changes to the
+directories while PMake is running will not be noted when searching
+for implicit sources, nor will they be found when PMake attempts to
+discover when the file was last modified, unless the file was created in the
+current directory. While people have suggested that PMake should read
+the directories each time, my experience suggests that the caching seldom
+causes problems.
+.xH 2 Archives and Libraries
+.PP
+.UX
+and Sprite allow you to merge files into an archive using the
+.CW ar
+command. Further, if the files are relocatable object files, you can
+run
+.CW ranlib
+on the archive and get yourself a library that you can link into any
+program you want. The main problem with archives is they double the
+space you need to store the archived files, since there's one copy in
+the archive and one copy out by itself. The problem with libraries is
+you usually think of them as
+.CW -lm
+rather than
+.CW /usr/lib/libm.a
+and the linker thinks they're out-of-date if you so much as look at
+them.
+.PP
+PMake solves the problem with archives by allowing you to tell it to
+examine the files in the archives (so you can remove the individual
+files without having to regenerate them later). To handle the problem
+with libraries, PMake adds an additional way of deciding if a library
+is out-of-date:
+.IP \(bu 2
+If the table of contents is older than the library, or is missing, the
+library is out-of-date.
+.LP
+A library is any target that looks like
+.CW \-l name'' ``
+or that ends in a suffix that was marked as a library using the
+.CW .LIBS
+target.
+.CW .a
+is so marked in the system makefile.
+.PP
+Members of an archive are specified as
+``\fIarchive\fP(\fImember\fP[ \fImember\fP...])''.
+Thus
+.CW libdix.a(window.o) '' ``'
+specifies the file
+.CW window.o
+in the archive
+.CW libdix.a .
+.PP
+A file that is a member of an archive is treated specially. If the
+file doesn't exist, but it is in the archive, the modification time
+recorded in the archive is used for the file when determining if the
+file is out-of-date. When figuring out how to make an archived member target
+(not the file itself, but the file in the archive \*- the
+\fIarchive\fP(\fImember\fP) target), special care is
+taken with the transformation rules, as follows:
+.IP \(bu 2
+\&\fIarchive\fP(\fImember\fP) is made to depend on \fImember\fP.
+.IP \(bu 2
+The transformation from the \fImember\fP's suffix to the
+\fIarchive\fP's suffix is applied to the \fIarchive\fP(\fImember\fP) target.
+.IP \(bu 2
+The \fIarchive\fP(\fImember\fP)'s
+.CW .TARGET
+variable is set to the name of the \fImember\fP if \fImember\fP is
+actually a target or the path to the member file if \fImember\fP is
+only a source.
+.IP \(bu 2
+The
+.CW .ARCHIVE
+variable for the \fIarchive\fP(\fImember\fP) target is set to the path
+of the \fIarchive\fP.
+.Ix 0 def variable local .ARCHIVE
+.Ix 0 def .ARCHIVE
+.IP \(bu 2
+The
+.CW .MEMBER
+variable is set to the actual string inside the parentheses. In most
+cases, this will be the same as the
+.CW .TARGET
+variable.
+.Ix 0 def variable local .MEMBER
+.Ix 0 def .MEMBER
+.IP \(bu 2
+The \fIarchive\fP(\fImember\fP)'s place in the local variables of the
+targets that depend on it is taken by the value of its
+.CW .TARGET
+variable.
+.LP
+Thus, a program library could be created with the following makefile:
+.DS
+\&.o.a :
+ ...
+ rm -f $(.TARGET:T)
+OBJS = obj1.o obj2.o obj3.o
+libprog.a : libprog.a($(OBJS))
+ ar cru $(.TARGET) $(.OODATE)
+ ranlib $(.TARGET)
+.DE
+This will cause the three object files to be compiled (if the
+corresponding source files were modified after the object file or, if
+that doesn't exist, the archived object file), the out-of-date ones
+archived in
+.CW libprog.a ,
+a table of contents placed in the archive and the newly-archived
+object files to be removed.
+.PP
+All this is used in the
+.CW makelib.mk
+system makefile to create a single library with ease. This makefile
+looks like this:
+.DS
+#
+# Rules for making libraries. The object files which make up the library are
+# removed once they are archived.
+#
+# To make several libararies in parallel, you should define the variable
+# "many_libraries". This will serialize the invocations of ranlib.
+#
+# To use, do something like this:
+#
+# OBJECTS = <files in the library>
+#
+# fish.a: fish.a($(OBJECTS)) MAKELIB
+#
+# $Header: /cvsroot/src/usr.bin/make/Attic/tutorial.ms,v 1.1 1994/03/05 00:35:14 cgd Exp $ SPRITE (Berkeley)
+#
+
+#ifndef _MAKELIB_MK
+_MAKELIB_MK =
+
+#include <po.mk>
+
+.po.a .o.a :
+ ...
+ rm -f $(.MEMBER)
+
+ARFLAGS ?= crl
+
+#
+# Re-archive the out-of-date members and recreate the library's table of
+# contents using ranlib. If many_libraries is defined, put the ranlib off
+# til the end so many libraries can be made at once.
+#
+MAKELIB : .USE .PRECIOUS
+ ar $(ARFLAGS) $(.TARGET) $(.OODATE)
+#ifndef no_ranlib
+# ifdef many_libraries
+ ...
+# endif many_libraries
+ ranlib $(.TARGET)
+#endif no_ranlib
+
+#endif _MAKELIB_MK
+.DE
+.xH 2 On the Condition...
+.PP
+Like the C compiler before it, PMake allows you to configure the makefile,
+based on the current environment, using conditional statements. A
+conditional looks like this:
+.DS
+#if \fIboolean expression\fP
+\fIlines\fP
+#elif \fIanother boolean expression\fP
+\fImore lines\fP
+#else
+\fIstill more lines\fP
+#endif
+.DE
+They may be nested to a maximum depth of 30 and may occur anywhere
+(except in a comment, of course). The
+.CW # '' ``
+must the very first character on the line.
+.PP
+Each
+.I "boolean expression"
+is made up of terms that look like function calls, the standard C
+boolean operators
+.CW && ,
+.CW || ,
+and
+.CW ! ,
+and the standard arithmetic operators
+.CW == ,
+.CW != ,
+.CW > ,
+.CW >= ,
+.CW < ,
+and
+.CW <= ,
+with
+.CW ==
+and
+.CW !=
+being overloaded to allow string comparisons as well.
+.CW &&
+represents logical AND;
+.CW ||
+is logical OR and
+.CW !
+is logical NOT. The arithmetic and string operators take precedence
+over all three of these operators, while NOT takes precedence over
+AND, which takes precedence over OR. This precedence may be
+overridden with parentheses, and an expression may be parenthesized to
+your heart's content. Each term looks like a call on one of four
+functions:
+.nr pw \w'defined 'u
+.Ix 0 def make
+.Ix 0 def conditional make
+.Ix 0 def if make
+.IP make \n(pwu
+The syntax is
+.CW make( \fItarget\fP\c
+.CW )
+where
+.I target
+is a target in the makefile. This is true if the given target was
+specified on the command line, or as the source for a
+.CW .MAIN
+target (note that the sources for
+.CW .MAIN
+are only used if no targets were given on the command line).
+.IP defined \n(pwu
+.Ix 0 def defined
+.Ix 0 def conditional defined
+.Ix 0 def if defined
+The syntax is
+.CW defined( \fIvariable\fP\c
+.CW )
+and is true if
+.I variable
+is defined. Certain variables are defined in the system makefile that
+identify the system on which PMake is being run.
+.IP exists \n(pwu
+.Ix 0 def exists
+.Ix 0 def conditional exists
+.Ix 0 def if exists
+The syntax is
+.CW exists( \fIfile\fP\c
+.CW )
+and is true if the file can be found on the global search path (i.e.
+that defined by
+.CW .PATH
+targets, not by
+.CW .PATH \fIsuffix\fP
+targets).
+.IP empty \n(pwu
+.Ix 0 def empty
+.Ix 0 def conditional empty
+.Ix 0 def if empty
+This syntax is much like the others, except the string inside the
+parentheses is of the same form as you would put between parentheses
+when expanding a variable, complete with modifiers and everything. The
+function returns true if the resulting string is empty (NOTE: an undefined
+variable in this context will cause at the very least a warning
+message about a malformed conditional, and at the worst will cause the
+process to stop once it has read the makefile. If you want to check
+for a variable being defined or empty, use the expression
+.CW !defined( \fIvar\fP\c ``
+.CW ") || empty(" \fIvar\fP\c
+.CW ) ''
+as the definition of
+.CW ||
+will prevent the
+.CW empty()
+from being evaluated and causing an error, if the variable is
+undefined). This can be used to see if a variable contains a given
+word, for example:
+.DS
+#if !empty(\fIvar\fP:M\fIword\fP)
+.DE
+.PP
+The arithmetic and string operators may only be used to test the value
+of a variable. The lefthand side must contain the variable expansion,
+while the righthand side contains either a string, enclosed in
+double-quotes, or a number. The standard C numeric conventions (except
+for specifying an octal number) apply to both sides. E.g.
+.DS
+#if $(OS) == 4.3
+
+#if $(MACHINE) == "sun3"
+
+#if $(LOAD_ADDR) < 0xc000
+.DE
+are all valid conditionals. In addition, the numeric value of a
+variable can be tested as a boolean as follows:
+.DS
+#if $(LOAD)
+.DE
+would see if
+.CW LOAD
+contains a non-zero value and
+.DS
+#if !$(LOAD)
+.DE
+would test if
+.CW LOAD
+contains a zero value.
+.PP
+In addition to the bare
+.CW #if ,'' ``
+there are other forms that apply one of the first two functions to each
+term. They are as follows:
+.DS
+ ifdef \fRdefined\fP
+ ifndef \fR!defined\fP
+ ifmake \fRmake\fP
+ ifnmake \fR!make\fP
+.DE
+There are also the ``else if'' forms:
+.CW elif ,
+.CW elifdef ,
+.CW elifndef ,
+.CW elifmake ,
+and
+.CW elifnmake .
+.PP
+For instance, if you wish to create two versions of a program, one of which
+is optimized (the production version) and the other of which is for debugging
+(has symbols for dbx), you have two choices: you can create two
+makefiles, one of which uses the
+.CW \-g
+flag for the compilation, while the other uses the
+.CW \-O
+flag, or you can use another target (call it
+.CW debug )
+to create the debug version. The construct below will take care of
+this for you. I have also made it so defining the variable
+.CW DEBUG
+(say with
+.CW "pmake -D DEBUG" )
+will also cause the debug version to be made.
+.DS
+#if defined(DEBUG) || make(debug)
+CFLAGS += -g
+#else
+CFLAGS += -O
+#endif
+.DE
+There are, of course, problems with this approach. The most glaring
+annoyance is that if you want to go from making a debug version to
+making a production version, you have to remove all the object files,
+or you will get some optimized and some debug versions in the same
+program. Another annoyance is you have to be careful not to make two
+targets that ``conflict'' because of some conditionals in the
+makefile. For instance
+.DS
+#if make(print)
+FORMATTER = ditroff -Plaser_printer
+#endif
+#if make(draft)
+FORMATTER = nroff -Pdot_matrix_printer
+#endif
+.DE
+would wreak havok if you tried
+.CW "pmake draft print" '' ``
+since you would use the same formatter for each target. As I said,
+this all gets somewhat complicated.
+.xH 2 A Shell is a Shell is a Shell
+.PP
+In normal operation, the Bourne Shell (better known as
+.CW sh '') ``
+is used to execute the commands to re-create targets. PMake also allows you
+to specify a different shell for it to use when executing these
+commands. There are several things PMake must know about the shell you
+wish to use. These things are specified as the sources for the
+.CW .SHELL
+.Ix 0 ref .SHELL
+.Ix 0 ref target .SHELL
+target by keyword, as follows:
+.IP "\fBpath=\fP\fIpath\fP"
+PMake needs to know where the shell actually resides, so it can
+execute it. If you specify this and nothing else, PMake will use the
+last component of the path and look in its table of the shells it
+knows and use the specification it finds, if any. Use this if you just
+want to use a different version of the Bourne or C Shell (PMake knows
+how to use the C Shell too).
+.IP "\fBname=\fP\fIname\fP"
+This is the name by which the shell is to be known. It is a single
+word and, if no other keywords are specified (other than
+.B path ),
+it is the name by which PMake attempts to find a specification for the
+it (as mentioned above). You can use this if you would just rather use
+the C Shell than the Bourne Shell
+.CW ".SHELL: name=csh" '' (``
+will do it).
+.IP "\fBquiet=\fP\fIecho-off command\fP"
+As mentioned before, PMake actually controls whether commands are
+printed by introducing commands into the shell's input stream. This
+keyword, and the next two, control what those commands are. The
+.B quiet
+keyword is the command used to turn echoing off. Once it is turned
+off, echoing is expected to remain off until the echo-on command is given.
+.IP "\fBecho=\fP\fIecho-on command\fP"
+The command PMake should give to turn echoing back on again.
+.IP "\fBfilter=\fP\fIprinted echo-off command\fP"
+Many shells will echo the echo-off command when it is given. This
+keyword tells PMake in what format the shell actually prints the
+echo-off command. Where ever PMake sees this string in the shell's
+output, it will delete it and any following whitespace, up to and
+including the next newline. See the example at the end of this section
+for more details.
+.IP "\fBechoFlag=\fP\fIflag to turn echoing on\fP"
+Unless a target has been marked
+.CW .SILENT ,
+PMake wants to start the shell running with echoing on. To do this, it
+passes this flag to the shell as one of its arguments. If either this
+or the next flag begins with a `\-', the flags will be passed to the
+shell as separate arguments. Otherwise, the two will be concatenated
+(if they are used at the same time, of course).
+.IP "\fBerrFlag=\fP\fIflag to turn error checking on\fP"
+Likewise, unless a target is marked
+.CW .IGNORE ,
+PMake wishes error-checking to be on from the very start. To this end,
+it will pass this flag to the shell as an argument. The same rules for
+an initial `\-' apply as for the
+.B echoFlag .
+.IP "\fBcheck=\fP\fIcommand to turn error checking on\fP"
+Just as for echo-control, error-control is achieved by inserting
+commands into the shell's input stream. This is the command to make
+the shell check for errors. It also serves another purpose if the
+shell doesn't have error-control as commands, but I'll get into that
+in a minute. Again, once error checking has been turned on, it is
+expected to remain on until it is turned off again.
+.IP "\fBignore=\fP\fIcommand to turn error checking off\fP"
+This is the command PMake uses to turn error checking off. It has
+another use if the shell doesn't do error-control, but I'll tell you
+about that.\|.\|.\|now.
+.IP "\fBhasErrCtl=\fP\fIyes or no\fP"
+This takes a value that is either
+.B yes
+or
+.B no .
+Now you might think that the existence of the
+.B check
+and
+.B ignore
+keywords would be enough to tell PMake if the shell can do
+error-control, but you'd be wrong. If
+.B hasErrCtl
+is
+.B yes ,
+PMake uses the check and ignore commands in a straight-forward manner.
+If this is
+.B no ,
+however, their use is rather different. In this case, the check
+command is used as a template, in which the string
+.B %s
+is replaced by the command that's about to be executed, to produce a
+command for the shell that will echo the command to be executed. The
+ignore command is also used as a template, again with
+.B %s
+replaced by the command to be executed, to produce a command that will
+execute the command to be executed and ignore any error it returns.
+When these strings are used as templates, you must provide newline(s)
+.CW \en '') (``
+in the appropriate place(s).
+.PP
+The strings that follow these keywords may be enclosed in single or
+double quotes (the quotes will be stripped off) and may contain the
+usual C backslash-characters (\en is newline, \er is return, \eb is
+backspace, \e' escapes a single-quote inside single-quotes, \e"
+escapes a double-quote inside double-quotes). Now for an example.
+.PP
+This is actually the contents of the
+.CW <shx.mk>
+system makefile, and causes PMake to use the Bourne Shell in such a
+way that each command is printed as it is executed. That is, if more
+than one command is given on a line, each will be printed separately.
+Similarly, each time the body of a loop is executed, the commands
+within that loop will be printed, etc. The specification runs like
+this:
+.DS
+#
+# This is a shell specification to have the bourne shell echo
+# the commands just before executing them, rather than when it reads
+# them. Useful if you want to see how variables are being expanded, etc.
+#
+\&.SHELL : path=/bin/sh \e
+ quiet="set -" \e
+ echo="set -x" \e
+ filter="+ set - " \e
+ echoFlag=x \e
+ errFlag=e \e
+ hasErrCtl=yes \e
+ check="set -e" \e
+ ignore="set +e"
+.DE
+.LP
+It tells PMake the following:
+.Bp
+The shell is located in the file
+.CW /bin/sh .
+It need not tell PMake that the name of the shell is
+.CW sh
+as PMake can figure that out for itself (it's the last component of
+the path).
+.Bp
+The command to stop echoing is
+.CW "set -" .
+.Bp
+The command to start echoing is
+.CW "set -x" .
+.Bp
+When the echo off command is executed, the shell will print
+.CW "+ set - "
+(The `+' comes from using the
+.CW \-x
+flag (rather than the
+.CW \-v
+flag PMake usually uses)). PMake will remove all occurences of this
+string from the output, so you don't notice extra commands you didn't
+put there.
+.Bp
+The flag the Bourne Shell will take to start echoing in this way is
+the
+.CW \-x
+flag. The Bourne Shell will only take its flag arguments concatenated
+as its first argument, so neither this nor the
+.B errFlag
+specification begins with a \-.
+.Bp
+The flag to use to turn error-checking on from the start is
+.CW \-e .
+.Bp
+The shell can turn error-checking on and off, and the commands to do
+so are
+.CW "set +e"
+and
+.CW "set -e" ,
+respectively.
+.PP
+I should note that this specification is for Bourne Shells that are
+not part of Berkeley
+.UX ,
+as shells from Berkeley don't do error control. You can get a similar
+effect, however, by changing the last three lines to be:
+.DS
+ hasErrCtl=no \e
+ check="echo \e"+ %s\e"\en" \e
+ ignore="sh -c '%s || exit 0\en"
+.DE
+.LP
+This will cause PMake to execute the two commands
+.DS
+echo "+ \fIcmd\fP"
+sh -c '\fIcmd\fP || true'
+.DE
+for each command for which errors are to be ignored. (In case you are
+wondering, the thing for
+.CW ignore
+tells the shell to execute another shell without error checking on and
+always exit 0, since the
+.B ||
+causes the
+.CW "exit 0"
+to be executed only if the first command exited non-zero, and if the
+first command exited zero, the shell will also exit zero, since that's
+the last command it executed).
+.xH 2 Compatibility
+.Ix 0 ref compatibility
+.PP
+There are three levels of backwards-compatibility built into PMake.
+Most makefiles will need none at all. Some may need a little bit of
+work to operate correctly when run in parallel. Each level encompasses
+the previous levels (e.g.
+.B \-B
+(one shell per command) implies
+.B \-V )
+The three levels are described in the following three sections.
+.xH 3 DEFCON 3 \*- Variable Expansion
+.Ix 0 ref compatibility
+.PP
+As noted before, PMake will not expand a variable unless it knows of a
+value for it. This can cause problems for makefiles that expect to
+leave variables undefined except in special circumstances (e.g. if
+more flags need to be passed to the C compiler or the output from a
+text processor should be sent to a different printer). If the
+variables are enclosed in curly braces
+.CW ${PRINTER} ''), (``
+the shell will let them pass. If they are enclosed in parentheses,
+however, the shell will declare a syntax error and the make will come
+to a grinding halt.
+.PP
+You have two choices: change the makefile to define the variables
+(their values can be overridden on the command line, since that's
+where they would have been set if you used Make, anyway) or always give the
+.B \-V
+flag (this can be done with the
+.CW .MAKEFLAGS
+target, if you want).
+.xH 3 DEFCON 2 \*- The Number of the Beast
+.Ix 0 ref compatibility
+.PP
+Then there are the makefiles that expect certain commands, such as
+changing to a different directory, to not affect other commands in a
+target's creation script. You can solve this is either by going
+back to executing one shell per command (which is what the
+.B \-B
+flag forces PMake to do), which slows the process down a good bit and
+requires you to use semicolons and escaped newlines for shell constructs, or
+by changing the makefile to execute the offending command(s) in a subshell
+(by placing the line inside parentheses), like so:
+.DS
+install :: .MAKE
+ (cd src; $(.PMAKE) install)
+ (cd lib; $(.PMAKE) install)
+ (cd man; $(.PMAKE) install)
+.DE
+.Ix 0 ref operator double-colon
+.Ix 0 ref variable global .PMAKE
+.Ix 0 ref .PMAKE
+.Ix 0 ref .MAKE
+.Ix 0 ref attribute .MAKE
+This will always execute the three makes (even if the
+.B \-n
+flag was given) because of the combination of the ``::'' operator and
+the
+.CW .MAKE
+attribute. Each command will change to the proper directory to perform
+the install, leaving the main shell in the directory in which it started.
+.xH 3 "DEFCON 1 \*- Imitation is the Not the Highest Form of Flattery"
+.Ix 0 ref compatibility
+.PP
+The final category of makefile is the one where every command requires
+input, the dependencies are incompletely specified, or you simply
+cannot create more than one target at a time, as mentioned earlier. In
+addition, you may not have the time or desire to upgrade the makefile
+to run smoothly with PMake. If you are the conservative sort, this is
+the compatibility mode for you. It is entered either by giving PMake
+the
+.B \-M
+flag (for Make), or by executing PMake as
+.CW make .'' ``
+In either case, PMake performs things exactly like Make, while still
+supporting most of the nice new features PMake provides. This
+includes:
+.IP \(bu 2
+No parallel execution.
+.IP \(bu 2
+Targets are made in the exact order specified by the makefile. The
+sources for each target are made in strict left-to-right order, etc.
+.IP \(bu 2
+A single Bourne shell is used to execute each command, thus the
+shell's
+.CW $$
+variable is useless, changing directories doesn't work across command
+lines, etc.
+.IP \(bu 2
+If no special characters exist in a command line, PMake will break the
+command into words itself and execute the command directly, without
+executing a shell first. The characters that cause PMake to execute a
+shell are:
+.CW # ,
+.CW = ,
+.CW | ,
+.CW ^ ,
+.CW ( ,
+.CW ) ,
+.CW { ,
+.CW } ,
+.CW ; ,
+.CW & ,
+.CW < ,
+.CW > ,
+.CW * ,
+.CW ? ,
+.CW [ ,
+.CW ] ,
+.CW : ,
+.CW $ ,
+.CW ` ,
+and
+.CW \e .
+You should notice that these are all the characters that are given
+special meaning by the shell.
+.IP \(bu 2
+The use of the null suffix is turned off.
+.Ix 0 ref "null suffix"
+.Ix 0 ref suffix null
+.xH 2 The Way Things Work
+.PP
+When PMake reads the makefile, it parses sources and targets into
+nodes in a graph. The graph is directed only in the sense that PMake
+knows which way is up. Each node contains not only links to all its
+parents and children (the nodes that depend on it and those on which
+it depends, respectively), but also a count of the number of its
+children that have already been processed.
+.PP
+The most important thing to know about how PMake uses this graph is
+that the traversal is breadth-first and occurs in two passes.
+.PP
+After PMake has parsed the makefile, it begins with the nodes the user
+has told it to make (either on the command line, or via a
+.CW .MAIN
+target, or by the target being the first in the file not labelled with
+the
+.CW .NOTMAIN
+attribute) placed in a queue. It continues to take the node of the
+front of the queue, mark it as something that needs to be made, pass
+the node to
+.CW Suff_FindDeps
+(mentioned earlier) to find any implicit sources for the node, and
+place all the node's children that have yet to be marked at the end of
+the queue. If any of the children is a
+.CW .USE
+rule, its children are linked to its parent and the parent's unmade
+children counter is decremented (since the
+.CW .USE
+node has been processed). You will note that this allows a
+.CW .USE
+node to have children that are
+.CW .USE
+nodes and the rules will be applied in sequence.
+If the node has no children, it is placed at the end of
+another queue to be examined in the second pass. This process
+continues until the queue is empty.
+.PP
+At this point, all the leaves of the graph are in the examination
+queue. PMake removes the node at the head of the queue and sees if it
+is out-of-date. If it is, it is passed to a function that will execute
+the commands for the node asynchronously. When the commands have
+completed, all the node's parents have their unmade children counter
+decremented and, if the counter is then 0, they are placed on the
+examination queue. Likewise, if the node is up-to-date. Only those
+parents that were marked on the downward pass are processed in this
+way. Thus PMake traverses the graph back up to the nodes the user
+instructed it to create. When the examination queue is empty and no
+shells are running to create a target, PMake is finished.
+.xH Answers to Exercises
+.xH Glossary of Jargon
+.de Gp
+.XP
+\&\fB\\$1:\fP
+..
+.Gp "attribute"
+A property given to a target that causes PMake to treat it differently.
+.Gp "command script"
+The lines immediately following a dependency line that specify
+commands to execute to create each of the targets on the dependency
+line. Each line in the command script must begin with a tab.
+.Gp "command-line variable"
+A variable defined in an argument when PMake is first executed.
+Overrides all assignments to the same variable name in the makefile.
+.Gp "conditional"
+A construct much like that used in C that allows a makefile to be
+configured on the fly based on the local environment, or on what is being
+made by that invocation of PMake.
+.Gp "creation script"
+Commands used to create a target. See ``command script.''
+.Gp "dependency"
+The relationship between a source and a target.
+'\" Elaborate on this
+.Gp "global variable"
+Any variable defined in a makefile. Takes precedence over variables
+defined in the environment, but not over command-line or local variables.
+.Gp "input graph"
+What PMake constructs from a makefile. Consists of nodes made of the
+targets in the makefile, and the links between them (the
+dependencies). The links are directed (from source to target) and
+there may not be any cycles (loops) in the graph.
+.Gp "local variable"
+A variable defined by PMake visible only in a target's shell script.
+There are seven local variables, not all of which are defined for
+every target:
+.CW .TARGET ,
+.CW .ALLSRC ,
+.CW .OODATE ,
+.CW .PREFIX ,
+.CW .IMPSRC ,
+.CW .ARCHIVE ,
+and
+.CW .MEMBER .
+.Gp "makefile"
+A file that describes how a system is built. If you don't know what it
+is after reading this tutorial.\|.\|.\|.
+.Gp "modifier"
+A letter, following a colon, used to alter how a variable is expanded.
+It has no effect on the variable itself.
+.Gp "operator"
+What separates a source from a target (on a dependency line) and specifies
+the relationship between the two. There are three:
+.CW : ', `
+.CW :: ', `
+and
+.CW ! '. `
+.Gp "search path"
+A list of directories in which a file should be sought. PMake's view
+of the contents of directories in a search path does not change once
+the makefile has been read. A file is sought on a search path only if
+it is exclusively a source.
+.Gp "shell"
+A program to which commands are passed in order to create targets.
+.Gp "source"
+Anything to the right of an operator on a dependency line. Targets on
+the dependency line are usually created from the sources.
+.Gp "special target"
+A target that causes PMake to do special things when it's encountered.
+.Gp "suffix"
+The tail end of a file name. Usually begins with a period,
+.CW .c
+or
+.CW .ms ,
+e.g.
+.Gp "target"
+A word to the left of the operator on a dependency line. More
+generally, any file that PMake might create. A file may be (and often
+is) both a target and a source (what it is depends on how PMake is
+looking at it at the time).
+.Gp "transformation rule"
+A special construct in a makefile that specifies how to create a file
+of one type from a file of another, as indicated by their suffixes.
+.Gp "variable expansion"
+The process of substituting the value of a variable for a reference to
+it. Expansion may be altered by means of modifiers.
+.Gp "variable"
+A place in which to store text that may be retrieved later. Also used
+to define the local environment. Conditionals exist that test whether
+a variable is defined or not.
+'\" Output table of contents last, with an entry for the index, making
+'\" sure to save and restore the last real page number for the index...
+.nr @n \n(PN+1
+.XS \n(@n 0
+Index
+.XE
+.nr %% \n%
+.TC
+.nr % \n(%%
diff --git a/usr.bin/make/util.c b/usr.bin/make/util.c
new file mode 100644
index 00000000000..6e2f777dba8
--- /dev/null
+++ b/usr.bin/make/util.c
@@ -0,0 +1,325 @@
+/*
+ * Missing stuff from OS's
+ *
+ * $Id: util.c,v 1.1 1994/03/05 00:35:16 cgd Exp $
+ */
+#include <stdio.h>
+#include <sys/cdefs.h>
+
+#if !__STDC__
+# ifndef const
+# define const
+# endif
+#endif
+
+#ifdef sun
+
+
+
+extern int errno, sys_nerr;
+extern char *sys_errlist[];
+
+char *
+strerror(e)
+ int e;
+{
+ static char buf[100];
+ if (e < 0 || e >= sys_nerr) {
+ sprintf(buf, "Unknown error %d", e);
+ return buf;
+ }
+ else
+ return sys_errlist[e];
+}
+#endif
+
+#if defined(sun) || defined(__hpux)
+
+int
+setenv(name, value, dum)
+ const char *name;
+ const char *value;
+ int dum;
+{
+ register char *p;
+ int len = strlen(name) + strlen(value) + 2; /* = \0 */
+ char *ptr = (char*) malloc(len);
+
+ (void) dum;
+
+ if (ptr == NULL)
+ return -1;
+
+ p = ptr;
+
+ while (*name)
+ *p++ = *name++;
+
+ *p++ = '=';
+
+ while (*value)
+ *p++ = *value++;
+
+ *p = '\0';
+
+ len = putenv(ptr);
+/* free(ptr); */
+ return len;
+}
+#endif
+
+#ifdef __hpux
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syscall.h>
+#include <sys/signal.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <sys/time.h>
+#include <time.h>
+#include <unistd.h>
+
+
+int
+killpg(pid, sig)
+ int pid, sig;
+{
+ return kill(-pid, sig);
+}
+
+void
+srandom(seed)
+ long seed;
+{
+ srand48(seed);
+}
+
+long
+random()
+{
+ return lrand48();
+}
+
+int
+setpriority(which, who, niceval)
+ int which, who, niceval;
+{
+#ifdef SYS_setpriority
+ return syscall(SYS_setpriority, which, who, niceval);
+#else
+ extern int errno;
+ errno = ENOSYS;
+ return -1;
+#endif
+}
+
+int
+setreuid(euid, ruid)
+ int euid, ruid;
+{
+ return setresuid(euid, ruid, -1);
+}
+
+int
+setregid(egid, rgid)
+ int egid, rgid;
+{
+ return setresgid(egid, rgid, -1);
+}
+
+/* turn into bsd signals */
+void (*
+signal(s, a)) ()
+ int s;
+ void (*a)();
+{
+ struct sigvec osv, sv;
+
+ (void) sigvector(s, (struct sigvec *) 0, &osv);
+ sv = osv;
+ sv.sv_handler = a;
+#ifdef SV_BSDSIG
+ sv.sv_flags = SV_BSDSIG;
+#endif
+
+ if (sigvector(s, &sv, (struct sigvec *) 0) == -1)
+ return (BADSIG);
+ return (osv.sv_handler);
+}
+
+#if !defined(BSD) && !defined(d_fileno)
+# define d_fileno d_ino
+#endif
+
+#ifndef DEV_DEV_COMPARE
+# define DEV_DEV_COMPARE(a, b) ((a) == (b))
+#endif
+#define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/')))
+#define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1])))
+
+
+/* strrcpy():
+ * Like strcpy, going backwards and returning the new pointer
+ */
+static char *
+strrcpy(ptr, str)
+ register char *ptr, *str;
+{
+ register int len = strlen(str);
+
+ while (len)
+ *--ptr = str[--len];
+
+ return (ptr);
+} /* end strrcpy */
+
+
+char *
+getwd(pathname)
+ char *pathname;
+{
+ DIR *dp;
+ struct dirent *d;
+ extern int errno;
+
+ struct stat st_root, st_cur, st_next, st_dotdot;
+ char pathbuf[MAXPATHLEN], nextpathbuf[MAXPATHLEN * 2];
+ char *pathptr, *nextpathptr, *cur_name_add;
+
+ /* find the inode of root */
+ if (stat("/", &st_root) == -1) {
+ (void) sprintf(pathname,
+ "getwd: Cannot stat \"/\" (%s)", strerror(errno));
+ return (NULL);
+ }
+ pathbuf[MAXPATHLEN - 1] = '\0';
+ pathptr = &pathbuf[MAXPATHLEN - 1];
+ nextpathbuf[MAXPATHLEN - 1] = '\0';
+ cur_name_add = nextpathptr = &nextpathbuf[MAXPATHLEN - 1];
+
+ /* find the inode of the current directory */
+ if (lstat(".", &st_cur) == -1) {
+ (void) sprintf(pathname,
+ "getwd: Cannot stat \".\" (%s)", strerror(errno));
+ return (NULL);
+ }
+ nextpathptr = strrcpy(nextpathptr, "../");
+
+ /* Descend to root */
+ for (;;) {
+
+ /* look if we found root yet */
+ if (st_cur.st_ino == st_root.st_ino &&
+ DEV_DEV_COMPARE(st_cur.st_dev, st_root.st_dev)) {
+ (void) strcpy(pathname, *pathptr != '/' ? "/" : pathptr);
+ return (pathname);
+ }
+
+ /* open the parent directory */
+ if (stat(nextpathptr, &st_dotdot) == -1) {
+ (void) sprintf(pathname,
+ "getwd: Cannot stat directory \"%s\" (%s)",
+ nextpathptr, strerror(errno));
+ return (NULL);
+ }
+ if ((dp = opendir(nextpathptr)) == NULL) {
+ (void) sprintf(pathname,
+ "getwd: Cannot open directory \"%s\" (%s)",
+ nextpathptr, strerror(errno));
+ return (NULL);
+ }
+
+ /* look in the parent for the entry with the same inode */
+ if (DEV_DEV_COMPARE(st_dotdot.st_dev, st_cur.st_dev)) {
+ /* Parent has same device. No need to stat every member */
+ for (d = readdir(dp); d != NULL; d = readdir(dp))
+ if (d->d_fileno == st_cur.st_ino)
+ break;
+ }
+ else {
+ /*
+ * Parent has a different device. This is a mount point so we
+ * need to stat every member
+ */
+ for (d = readdir(dp); d != NULL; d = readdir(dp)) {
+ if (ISDOT(d->d_name) || ISDOTDOT(d->d_name))
+ continue;
+ (void) strcpy(cur_name_add, d->d_name);
+ if (lstat(nextpathptr, &st_next) == -1) {
+ (void) sprintf(pathname, "getwd: Cannot stat \"%s\" (%s)",
+ d->d_name, strerror(errno));
+ (void) closedir(dp);
+ return (NULL);
+ }
+ /* check if we found it yet */
+ if (st_next.st_ino == st_cur.st_ino &&
+ DEV_DEV_COMPARE(st_next.st_dev, st_cur.st_dev))
+ break;
+ }
+ }
+ if (d == NULL) {
+ (void) sprintf(pathname, "getwd: Cannot find \".\" in \"..\"");
+ (void) closedir(dp);
+ return (NULL);
+ }
+ st_cur = st_dotdot;
+ pathptr = strrcpy(pathptr, d->d_name);
+ pathptr = strrcpy(pathptr, "/");
+ nextpathptr = strrcpy(nextpathptr, "../");
+ (void) closedir(dp);
+ *cur_name_add = '\0';
+ }
+} /* end getwd */
+
+
+char *sys_siglist[] = {
+ "Signal 0",
+ "Hangup", /* SIGHUP */
+ "Interrupt", /* SIGINT */
+ "Quit", /* SIGQUIT */
+ "Illegal instruction", /* SIGILL */
+ "Trace/BPT trap", /* SIGTRAP */
+ "IOT trap", /* SIGIOT */
+ "EMT trap", /* SIGEMT */
+ "Floating point exception", /* SIGFPE */
+ "Killed", /* SIGKILL */
+ "Bus error", /* SIGBUS */
+ "Segmentation fault", /* SIGSEGV */
+ "Bad system call", /* SIGSYS */
+ "Broken pipe", /* SIGPIPE */
+ "Alarm clock", /* SIGALRM */
+ "Terminated", /* SIGTERM */
+ "User defined signal 1", /* SIGUSR1 */
+ "User defined signal 2", /* SIGUSR2 */
+ "Child exited", /* SIGCLD */
+ "Power-fail restart", /* SIGPWR */
+ "Virtual timer expired", /* SIGVTALRM */
+ "Profiling timer expired", /* SIGPROF */
+ "I/O possible", /* SIGIO */
+ "Window size changes", /* SIGWINDOW */
+ "Stopped (signal)", /* SIGSTOP */
+ "Stopped", /* SIGTSTP */
+ "Continued", /* SIGCONT */
+ "Stopped (tty input)", /* SIGTTIN */
+ "Stopped (tty output)", /* SIGTTOU */
+ "Urgent I/O condition", /* SIGURG */
+ "Remote lock lost (NFS)", /* SIGLOST */
+ "Signal 31", /* reserved */
+ "DIL signal" /* SIGDIL */
+};
+
+int
+utimes(file, tvp)
+ char *file;
+ struct timeval tvp[2];
+{
+ struct utimbuf t;
+
+ t.actime = tvp[0].tv_sec;
+ t.modtime = tvp[1].tv_sec;
+ return(utime(file, &t));
+}
+
+
+#endif /* __hpux */
diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c
index 3212cba3111..8a16b300136 100644
--- a/usr.bin/make/var.c
+++ b/usr.bin/make/var.c
@@ -37,8 +37,8 @@
*/
#ifndef lint
-/*static char sccsid[] = "from: @(#)var.c 5.7 (Berkeley) 6/1/90";*/
-static char rcsid[] = "$Id: var.c,v 1.4 1994/01/13 21:02:09 jtc Exp $";
+/* from: static char sccsid[] = "@(#)var.c 5.7 (Berkeley) 6/1/90"; */
+static char *rcsid = "$Id: var.c,v 1.5 1994/03/05 00:35:17 cgd Exp $";
#endif /* not lint */
/*-
@@ -62,7 +62,8 @@ static char rcsid[] = "$Id: var.c,v 1.4 1994/01/13 21:02:09 jtc Exp $";
* Var_Value Return the value of a variable in a context or
* NULL if the variable is undefined.
*
- * Var_Subst Substitute for all variables in a string using
+ * Var_Subst Substitute named variable, or all variables if
+ * NULL in a string using
* the given context as the top-most one. If the
* third argument is non-zero, Parse_Error is
* called if any variables are undefined.
@@ -82,8 +83,6 @@ static char rcsid[] = "$Id: var.c,v 1.4 1994/01/13 21:02:09 jtc Exp $";
* XXX: There's a lot of duplication in these functions.
*/
-#include <stdio.h>
-#include <stdlib.h>
#include <ctype.h>
#include "make.h"
#include "buf.h"
@@ -100,7 +99,7 @@ char var_Error[] = "";
* set false. Why not just use a constant? Well, gcc likes to condense
* identical string instances...
*/
-char varNoError[] = "";
+static char varNoError[] = "";
/*
* Internally, variables are contained in four different contexts.
@@ -138,6 +137,32 @@ typedef struct Var {
* modified variables */
} Var;
+typedef struct {
+ char *lhs; /* String to match */
+ int leftLen; /* Length of string */
+ char *rhs; /* Replacement string (w/ &'s removed) */
+ int rightLen; /* Length of replacement */
+ int flags;
+#define VAR_SUB_GLOBAL 1 /* Apply substitution globally */
+#define VAR_MATCH_START 2 /* Match at start of word */
+#define VAR_MATCH_END 4 /* Match at end of word */
+#define VAR_NO_SUB 8 /* Substitution is non-global and already done */
+} VarPattern;
+
+static int VarCmp __P((Var *, char *));
+static Var *VarFind __P((char *, GNode *, int));
+static void VarAdd __P((char *, char *, GNode *));
+static Boolean VarHead __P((char *, Boolean, Buffer));
+static Boolean VarTail __P((char *, Boolean, Buffer));
+static Boolean VarSuffix __P((char *, Boolean, Buffer));
+static Boolean VarRoot __P((char *, Boolean, Buffer));
+static Boolean VarMatch __P((char *, Boolean, Buffer, char *));
+static Boolean VarSYSVMatch __P((char *, Boolean, Buffer, VarPattern *));
+static Boolean VarNoMatch __P((char *, Boolean, Buffer, char *));
+static Boolean VarSubstitute __P((char *, Boolean, Buffer, VarPattern *));
+static char *VarModify __P((char *, Boolean (*modProc )(), ClientData));
+static int VarPrintVar __P((Var *));
+
/*-
*-----------------------------------------------------------------------
* VarCmp --
@@ -291,7 +316,7 @@ VarFind (name, ctxt, flags)
* safely be freed.
*-----------------------------------------------------------------------
*/
-static
+static void
VarAdd (name, val, ctxt)
char *name; /* name of variable to add */
char *val; /* value to set it to */
@@ -304,7 +329,7 @@ VarAdd (name, val, ctxt)
v->name = strdup (name);
- len = strlen(val);
+ len = val ? strlen(val) : 0;
v->val = Buf_Init(len+1);
Buf_AddBytes(v->val, len, (Byte *)val);
@@ -434,7 +459,6 @@ Var_Append (name, val, ctxt)
GNode *ctxt; /* Context in which this should occur */
{
register Var *v;
- register char *cp;
v = VarFind (name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0);
@@ -446,7 +470,7 @@ Var_Append (name, val, ctxt)
if (DEBUG(VAR)) {
printf("%s:%s = %s\n", ctxt->name, name,
- Buf_GetAll(v->val, (int *)NULL));
+ (char *) Buf_GetAll(v->val, (int *)NULL));
}
if (v->flags & VAR_FROM_ENV) {
@@ -544,7 +568,7 @@ VarHead (word, addSpace, buf)
{
register char *slash;
- slash = rindex (word, '/');
+ slash = strrchr (word, '/');
if (slash != (char *)NULL) {
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
@@ -594,7 +618,7 @@ VarTail (word, addSpace, buf)
Buf_AddByte (buf, (Byte)' ');
}
- slash = rindex (word, '/');
+ slash = strrchr (word, '/');
if (slash != (char *)NULL) {
*slash++ = '\0';
Buf_AddBytes (buf, strlen(slash), (Byte *)slash);
@@ -628,7 +652,7 @@ VarSuffix (word, addSpace, buf)
{
register char *dot;
- dot = rindex (word, '.');
+ dot = strrchr (word, '.');
if (dot != (char *)NULL) {
if (addSpace) {
Buf_AddByte (buf, (Byte)' ');
@@ -670,7 +694,7 @@ VarRoot (word, addSpace, buf)
Buf_AddByte (buf, (Byte)' ');
}
- dot = rindex (word, '.');
+ dot = strrchr (word, '.');
if (dot != (char *)NULL) {
*dot = '\0';
Buf_AddBytes (buf, strlen (word), (Byte *)word);
@@ -715,6 +739,50 @@ VarMatch (word, addSpace, buf, pattern)
return(addSpace);
}
+
+
+/*-
+ *-----------------------------------------------------------------------
+ * VarSYSVMatch --
+ * Place the word in the buffer if it matches the given pattern.
+ * Callback function for VarModify to implement the System V %
+ * modifiers.
+ *
+ * Results:
+ * TRUE if a space should be placed in the buffer before the next
+ * word.
+ *
+ * Side Effects:
+ * The word may be copied to the buffer.
+ *
+ *-----------------------------------------------------------------------
+ */
+static Boolean
+VarSYSVMatch (word, addSpace, buf, pat)
+ char *word; /* Word to examine */
+ Boolean addSpace; /* TRUE if need to add a space to the
+ * buffer before adding the word, if it
+ * matches */
+ Buffer buf; /* Buffer in which to store it */
+ VarPattern *pat; /* Pattern the word must match */
+{
+ int len;
+ char *ptr;
+
+ if (addSpace)
+ Buf_AddByte(buf, (Byte)' ');
+
+ addSpace = TRUE;
+
+ if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL)
+ Str_SYSVSubst(buf, pat->rhs, ptr, len);
+ else
+ Buf_AddBytes(buf, strlen(word), (Byte *) word);
+
+ return(addSpace);
+}
+
+
/*-
*-----------------------------------------------------------------------
* VarNoMatch --
@@ -749,17 +817,6 @@ VarNoMatch (word, addSpace, buf, pattern)
return(addSpace);
}
-typedef struct {
- char *lhs; /* String to match */
- int leftLen; /* Length of string */
- char *rhs; /* Replacement string (w/ &'s removed) */
- int rightLen; /* Length of replacement */
- int flags;
-#define VAR_SUB_GLOBAL 1 /* Apply substitution globally */
-#define VAR_MATCH_START 2 /* Match at start of word */
-#define VAR_MATCH_END 4 /* Match at end of word */
-#define VAR_NO_SUB 8 /* Substitution is non-global and already done */
-} VarPattern;
/*-
*-----------------------------------------------------------------------
@@ -968,16 +1025,15 @@ VarModify (str, modProc, datum)
cp = str;
addSpace = FALSE;
- while (1) {
+ for (;;) {
/*
* Skip to next word and place cp at its end.
*/
while (isspace (*str)) {
str++;
}
- for (cp = str; *cp != '\0' && !isspace (*cp); cp++) {
- /* void */ ;
- }
+ for (cp = str; *cp != '\0' && !isspace (*cp); cp++)
+ continue;
if (cp == str) {
/*
* If we didn't go anywhere, we must be done!
@@ -1258,8 +1314,8 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr)
* return.
*/
str = (char *)Buf_GetAll(v->val, (int *)NULL);
- if (index (str, '$') != (char *)NULL) {
- str = Var_Subst(str, ctxt, err);
+ if (strchr (str, '$') != (char *)NULL) {
+ str = Var_Subst(NULL, str, ctxt, err);
*freePtr = TRUE;
}
@@ -1353,8 +1409,6 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr)
VarPattern pattern;
register char delim;
Buffer buf; /* Buffer for patterns */
- register char *cp2;
- int lefts;
pattern.flags = 0;
delim = tstr[1];
@@ -1580,9 +1634,8 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr)
* Now we break this sucker into the lhs and
* rhs. We must null terminate them of course.
*/
- for (cp = tstr; *cp != '='; cp++) {
- ;
- }
+ for (cp = tstr; *cp != '='; cp++)
+ continue;
pattern.lhs = tstr;
pattern.leftLen = cp - tstr;
*cp++ = '\0';
@@ -1598,9 +1651,7 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr)
* SYSV modifications happen through the whole
* string. Note the pattern is anchored at the end.
*/
- pattern.flags |= VAR_SUB_GLOBAL|VAR_MATCH_END;
-
- newStr = VarModify(str, VarSubstitute,
+ newStr = VarModify(str, VarSYSVMatch,
(ClientData)&pattern);
/*
@@ -1613,9 +1664,8 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr)
Error ("Unknown modifier '%c'\n", *tstr);
for (cp = tstr+1;
*cp != ':' && *cp != endc && *cp != '\0';
- cp++) {
- ;
- }
+ cp++)
+ continue;
termc = *cp;
newStr = var_Error;
}
@@ -1700,8 +1750,9 @@ Var_Parse (str, ctxt, err, lengthPtr, freePtr)
*-----------------------------------------------------------------------
*/
char *
-Var_Subst (str, ctxt, undefErr)
- register char *str; /* the string in which to substitute */
+Var_Subst (var, str, ctxt, undefErr)
+ char *var; /* Named variable || NULL for all */
+ char *str; /* the string in which to substitute */
GNode *ctxt; /* the context wherein to find variables */
Boolean undefErr; /* TRUE if undefineds are an error */
{
@@ -1713,11 +1764,11 @@ Var_Subst (str, ctxt, undefErr)
* been reported to prevent a plethora
* of messages when recursing */
- buf = Buf_Init (BSIZE);
+ buf = Buf_Init (MAKE_BSIZE);
errorReported = FALSE;
while (*str) {
- if ((*str == '$') && (str[1] == '$')) {
+ if (var == NULL && (*str == '$') && (str[1] == '$')) {
/*
* A dollar sign may be escaped either with another dollar sign.
* In such a case, we skip over the escape character and store the
@@ -1733,11 +1784,65 @@ Var_Subst (str, ctxt, undefErr)
*/
char *cp;
- for (cp = str++; *str != '$' && *str != '\0'; str++) {
- ;
- }
+ for (cp = str++; *str != '$' && *str != '\0'; str++)
+ continue;
Buf_AddBytes(buf, str - cp, (Byte *)cp);
} else {
+ if (var != NULL) {
+ int expand;
+ for (;;) {
+ if (str[1] != '(' && str[1] != '{') {
+ if (str[1] != *var) {
+ Buf_AddBytes(buf, 2, (Byte *) str);
+ str += 2;
+ expand = FALSE;
+ }
+ else
+ expand = TRUE;
+ break;
+ }
+ else {
+ char *p;
+
+ /*
+ * Scan up to the end of the variable name.
+ */
+ for (p = &str[2]; *p &&
+ *p != ':' && *p != ')' && *p != '}'; p++)
+ if (*p == '$')
+ break;
+ /*
+ * A variable inside the variable. We cannot expand
+ * the external variable yet, so we try again with
+ * the nested one
+ */
+ if (*p == '$') {
+ Buf_AddBytes(buf, p - str, (Byte *) str);
+ str = p;
+ continue;
+ }
+
+ if (strncmp(var, str + 2, p - str - 2) != 0 ||
+ var[p - str - 2] != '\0') {
+ /*
+ * Not the variable we want to expand, scan
+ * until the next variable
+ */
+ for (;*p != '$' && *p != '\0'; p++)
+ continue;
+ Buf_AddBytes(buf, p - str, (Byte *) str);
+ str = p;
+ expand = FALSE;
+ }
+ else
+ expand = TRUE;
+ break;
+ }
+ }
+ if (!expand)
+ continue;
+ }
+
val = Var_Parse (str, ctxt, undefErr, &length, &doFree);
/*
@@ -1860,11 +1965,11 @@ Var_Init ()
}
/****************** PRINT DEBUGGING INFO *****************/
-static
+static int
VarPrintVar (v)
Var *v;
{
- printf ("%-16s = %s\n", v->name, Buf_GetAll(v->val, (int *)NULL));
+ printf ("%-16s = %s\n", v->name, (char *) Buf_GetAll(v->val, (int *)NULL));
return (0);
}
@@ -1874,6 +1979,7 @@ VarPrintVar (v)
* print all variables in a context
*-----------------------------------------------------------------------
*/
+void
Var_Dump (ctxt)
GNode *ctxt;
{