Module Name: src
Committed By: rillig
Date: Mon Oct 5 19:27:48 UTC 2020
Modified Files:
src/usr.bin/make: Makefile arch.c compat.c cond.c dir.c enum.c for.c
hash.c job.c main.c make.h make_malloc.c nonints.h parse.c str.c
suff.c targ.c trace.c util.c var.c
src/usr.bin/make/unit-tests: Makefile directive-export-literal.exp
directive-export-literal.mk directive-ifndef.exp
directive-ifndef.mk directive-ifnmake.exp directive-ifnmake.mk
make-exported.exp make-exported.mk opt-debug-file.mk
opt-debug-for.exp opt-debug-for.mk opt-debug-jobs.exp
opt-debug-jobs.mk opt-debug-lint.exp opt-debug-lint.mk
opt-debug-loud.exp opt-debug-loud.mk opt-debug.exp opt-debug.mk
var-op-append.mk varname-dot-curdir.mk
Log Message:
make(1): revert previous commit
It had accidentally reverted all the work from the past few days.
To generate a diff of this commit:
cvs rdiff -u -r1.101 -r1.102 src/usr.bin/make/Makefile
cvs rdiff -u -r1.131 -r1.132 src/usr.bin/make/arch.c
cvs rdiff -u -r1.164 -r1.165 src/usr.bin/make/compat.c
cvs rdiff -u -r1.159 -r1.160 src/usr.bin/make/cond.c
cvs rdiff -u -r1.157 -r1.158 src/usr.bin/make/dir.c
cvs rdiff -u -r1.11 -r1.12 src/usr.bin/make/enum.c
cvs rdiff -u -r1.91 -r1.92 src/usr.bin/make/for.c
cvs rdiff -u -r1.42 -r1.43 src/usr.bin/make/hash.c
cvs rdiff -u -r1.259 -r1.260 src/usr.bin/make/job.c
cvs rdiff -u -r1.368 -r1.369 src/usr.bin/make/main.c
cvs rdiff -u -r1.154 -r1.155 src/usr.bin/make/make.h
cvs rdiff -u -r1.22 -r1.23 src/usr.bin/make/make_malloc.c
cvs rdiff -u -r1.139 -r1.140 src/usr.bin/make/nonints.h
cvs rdiff -u -r1.367 -r1.368 src/usr.bin/make/parse.c
cvs rdiff -u -r1.67 -r1.68 src/usr.bin/make/str.c
cvs rdiff -u -r1.175 -r1.176 src/usr.bin/make/suff.c
cvs rdiff -u -r1.110 -r1.111 src/usr.bin/make/targ.c
cvs rdiff -u -r1.18 -r1.19 src/usr.bin/make/trace.c
cvs rdiff -u -r1.62 -r1.63 src/usr.bin/make/util.c
cvs rdiff -u -r1.566 -r1.567 src/usr.bin/make/var.c
cvs rdiff -u -r1.161 -r1.162 src/usr.bin/make/unit-tests/Makefile
cvs rdiff -u -r1.3 -r1.4 \
src/usr.bin/make/unit-tests/directive-export-literal.exp \
src/usr.bin/make/unit-tests/directive-ifndef.exp \
src/usr.bin/make/unit-tests/directive-ifnmake.exp \
src/usr.bin/make/unit-tests/opt-debug-file.mk \
src/usr.bin/make/unit-tests/opt-debug-for.exp \
src/usr.bin/make/unit-tests/opt-debug-for.mk \
src/usr.bin/make/unit-tests/opt-debug-jobs.exp \
src/usr.bin/make/unit-tests/opt-debug-jobs.mk \
src/usr.bin/make/unit-tests/opt-debug-loud.exp \
src/usr.bin/make/unit-tests/opt-debug-loud.mk \
src/usr.bin/make/unit-tests/opt-debug.exp
cvs rdiff -u -r1.4 -r1.5 \
src/usr.bin/make/unit-tests/directive-export-literal.mk \
src/usr.bin/make/unit-tests/directive-ifndef.mk \
src/usr.bin/make/unit-tests/directive-ifnmake.mk \
src/usr.bin/make/unit-tests/make-exported.exp \
src/usr.bin/make/unit-tests/opt-debug.mk \
src/usr.bin/make/unit-tests/var-op-append.mk
cvs rdiff -u -r1.5 -r1.6 src/usr.bin/make/unit-tests/make-exported.mk \
src/usr.bin/make/unit-tests/varname-dot-curdir.mk
cvs rdiff -u -r1.10 -r1.11 src/usr.bin/make/unit-tests/opt-debug-lint.exp
cvs rdiff -u -r1.9 -r1.10 src/usr.bin/make/unit-tests/opt-debug-lint.mk
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/usr.bin/make/Makefile
diff -u src/usr.bin/make/Makefile:1.101 src/usr.bin/make/Makefile:1.102
--- src/usr.bin/make/Makefile:1.101 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/Makefile Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.101 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: Makefile,v 1.102 2020/10/05 19:27:47 rillig Exp $
# @(#)Makefile 5.2 (Berkeley) 12/28/90
PROG= make
@@ -189,3 +189,10 @@ retest:
rm -f *.gcov *.gcda
.endif
${.MAKE} test
+
+# Just out of curiosity, during development.
+.SUFFIXES: .cpre .casm
+.c.cpre:
+ ${COMPILE.c:S,^-c$,-E,} ${.IMPSRC} -o ${.TARGET}
+.c.casm:
+ ${COMPILE.c:S,^-c$,-S,} ${.IMPSRC} -o ${.TARGET}
Index: src/usr.bin/make/arch.c
diff -u src/usr.bin/make/arch.c:1.131 src/usr.bin/make/arch.c:1.132
--- src/usr.bin/make/arch.c:1.131 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/arch.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: arch.c,v 1.131 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: arch.c,v 1.132 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -123,18 +123,14 @@
#include <sys/param.h>
#include <ar.h>
-#include <ctype.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <utime.h>
#include "make.h"
-#include "hash.h"
#include "dir.h"
#include "config.h"
/* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: arch.c,v 1.131 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: arch.c,v 1.132 2020/10/05 19:27:47 rillig Exp $");
#ifdef TARGET_MACHINE
#undef MAKE_MACHINE
@@ -151,11 +147,11 @@ typedef struct ListNode ArchListNode;
static ArchList *archives; /* The archives we've already examined */
typedef struct Arch {
- char *name; /* Name of archive */
- Hash_Table members; /* All the members of the archive described
- * by <name, struct ar_hdr *> key/value pairs */
- char *fnametab; /* Extended name table strings */
- size_t fnamesize; /* Size of the string table */
+ char *name; /* Name of archive */
+ Hash_Table members; /* All the members of the archive described
+ * by <name, struct ar_hdr *> key/value pairs */
+ char *fnametab; /* Extended name table strings */
+ size_t fnamesize; /* Size of the string table */
} Arch;
static FILE *ArchFindMember(const char *, const char *,
@@ -170,8 +166,8 @@ static void
ArchFree(void *ap)
{
Arch *a = (Arch *)ap;
- Hash_Search search;
- Hash_Entry *entry;
+ Hash_Search search;
+ Hash_Entry *entry;
/* Free memory from hash entries */
for (entry = Hash_EnumFirst(&a->members, &search);
@@ -258,7 +254,7 @@ Arch_ParseArchive(char **linePtr, GNodeL
* place and skip to the end of it (either white-space or
* a close paren).
*/
- Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */
+ Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */
while (*cp != '\0' && *cp != ')' && ch_isspace(*cp)) {
cp++;
@@ -270,7 +266,7 @@ Arch_ParseArchive(char **linePtr, GNodeL
* Variable spec, so call the Var module to parse the puppy
* so we can safely advance beyond it...
*/
- void *freeIt;
+ void *freeIt;
const char *result;
Boolean isError;
const char *nested_p = cp;
@@ -324,11 +320,11 @@ Arch_ParseArchive(char **linePtr, GNodeL
* later.
*/
if (doSubst) {
- char *buf;
- char *sacrifice;
- char *oldMemName = memName;
+ char *buf;
+ char *sacrifice;
+ char *oldMemName = memName;
- (void)Var_Subst(memName, ctxt, VARE_UNDEFERR | VARE_WANTRES,
+ (void)Var_Subst(memName, ctxt, VARE_UNDEFERR|VARE_WANTRES,
&memName);
/* TODO: handle errors */
@@ -413,15 +409,9 @@ Arch_ParseArchive(char **linePtr, GNodeL
free(libName);
}
- /*
- * We promised the pointer would be set up at the next non-space, so
- * we must advance cp there before setting *linePtr... (note that on
- * entrance to the loop, cp is guaranteed to point at a ')')
- */
- do {
- cp++;
- } while (*cp != '\0' && ch_isspace(*cp));
-
+ cp++; /* skip the ')' */
+ /* We promised that linePtr would be set up at the next non-space. */
+ pp_skip_whitespace(&cp);
*linePtr = cp;
return TRUE;
}
@@ -449,15 +439,15 @@ Arch_ParseArchive(char **linePtr, GNodeL
static struct ar_hdr *
ArchStatMember(const char *archive, const char *member, Boolean hash)
{
-#define AR_MAX_NAME_LEN (sizeof(arh.ar_name)-1)
- FILE * arch; /* Stream to archive */
- size_t size; /* Size of archive member */
- char magic[SARMAG];
+#define AR_MAX_NAME_LEN (sizeof(arh.ar_name) - 1)
+ FILE *arch; /* Stream to archive */
+ size_t size; /* Size of archive member */
+ char magic[SARMAG];
ArchListNode *ln;
- Arch *ar; /* Archive descriptor */
- struct ar_hdr arh; /* archive-member header for reading archive */
- char memName[MAXPATHLEN+1];
- /* Current member name while hashing. */
+ Arch *ar; /* Archive descriptor */
+ struct ar_hdr arh; /* archive-member header for reading archive */
+ char memName[MAXPATHLEN + 1];
+ /* Current member name while hashing. */
/*
* Because of space constraints and similar things, files are archived
@@ -486,7 +476,7 @@ ArchStatMember(const char *archive, cons
{
/* Try truncated name */
- char copy[AR_MAX_NAME_LEN+1];
+ char copy[AR_MAX_NAME_LEN + 1];
size_t len = strlen(member);
if (len > AR_MAX_NAME_LEN) {
@@ -506,11 +496,11 @@ ArchStatMember(const char *archive, cons
* no need to allocate extra room for the header we're returning,
* so just declare it static.
*/
- static struct ar_hdr sarh;
+ static struct ar_hdr sarh;
- arch = ArchFindMember(archive, member, &sarh, "r");
+ arch = ArchFindMember(archive, member, &sarh, "r");
- if (arch == NULL) {
+ if (arch == NULL) {
return NULL;
} else {
fclose(arch);
@@ -533,8 +523,8 @@ ArchStatMember(const char *archive, cons
*/
if ((fread(magic, SARMAG, 1, arch) != 1) ||
(strncmp(magic, ARMAG, SARMAG) != 0)) {
- fclose(arch);
- return NULL;
+ fclose(arch);
+ return NULL;
}
ar = bmake_malloc(sizeof(Arch));
@@ -545,7 +535,7 @@ ArchStatMember(const char *archive, cons
memName[AR_MAX_NAME_LEN] = '\0';
while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) {
- if (strncmp( arh.ar_fmag, ARFMAG, sizeof(arh.ar_fmag)) != 0) {
+ if (strncmp(arh.ar_fmag, ARFMAG, sizeof(arh.ar_fmag)) != 0) {
/*
* The header is bogus, so the archive is bad
* and there's no way we can recover...
@@ -560,7 +550,7 @@ ArchStatMember(const char *archive, cons
* boundary, so we need to extract the size of the file from the
* 'size' field of the header and round it up during the seek.
*/
- arh.ar_size[sizeof(arh.ar_size)-1] = '\0';
+ arh.ar_size[sizeof(arh.ar_size) - 1] = '\0';
size = (size_t)strtol(arh.ar_size, NULL, 10);
memcpy(memName, arh.ar_name, sizeof(arh.ar_name));
@@ -579,15 +569,14 @@ ArchStatMember(const char *archive, cons
* svr4 magic mode; handle it
*/
switch (ArchSVR4Entry(ar, memName, size, arch)) {
- case -1: /* Invalid data */
+ case -1: /* Invalid data */
goto badarch;
- case 0: /* List of files entry */
+ case 0: /* List of files entry */
continue;
- default: /* Got the entry */
+ default: /* Got the entry */
break;
}
- }
- else {
+ } else {
if (nameend[0] == '/')
nameend[0] = '\0';
}
@@ -601,23 +590,24 @@ ArchStatMember(const char *archive, cons
if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 &&
ch_isdigit(memName[sizeof(AR_EFMT1) - 1])) {
- int elen = atoi(&memName[sizeof(AR_EFMT1)-1]);
+ int elen = atoi(&memName[sizeof(AR_EFMT1) - 1]);
if ((unsigned int)elen > MAXPATHLEN)
- goto badarch;
+ goto badarch;
if (fread(memName, (size_t)elen, 1, arch) != 1)
- goto badarch;
+ goto badarch;
memName[elen] = '\0';
if (fseek(arch, -elen, SEEK_CUR) != 0)
- goto badarch;
+ goto badarch;
if (DEBUG(ARCH) || DEBUG(MAKE)) {
- debug_printf("ArchStat: Extended format entry for %s\n", memName);
+ debug_printf("ArchStat: Extended format entry for %s\n",
+ memName);
}
}
#endif
{
- Hash_Entry *he;
+ Hash_Entry *he;
he = Hash_CreateEntry(&ar->members, memName, NULL);
Hash_SetValue(he, bmake_malloc(sizeof(struct ar_hdr)));
memcpy(Hash_GetValue(he), &arh, sizeof(struct ar_hdr));
@@ -706,12 +696,12 @@ ArchSVR4Entry(Arch *ar, char *name, size
entry = (size_t)strtol(&name[1], &eptr, 0);
if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) {
- DEBUG1(ARCH, "Could not parse SVR4 name %s\n", name);
+ DEBUG1(ARCH, "Could not parse SVR4 name %s\n", name);
return 2;
}
if (entry >= ar->fnamesize) {
DEBUG2(ARCH, "SVR4 entry offset %s is greater than %lu\n",
- name, (unsigned long)ar->fnamesize);
+ name, (unsigned long)ar->fnamesize);
return 2;
}
@@ -746,13 +736,13 @@ ArchSVR4Entry(Arch *ar, char *name, size
*/
static FILE *
ArchFindMember(const char *archive, const char *member, struct ar_hdr *arhPtr,
- const char *mode)
+ const char *mode)
{
- FILE * arch; /* Stream to archive */
- int size; /* Size of archive member */
- char magic[SARMAG];
- size_t len, tlen;
- const char * base;
+ FILE *arch; /* Stream to archive */
+ int size; /* Size of archive member */
+ char magic[SARMAG];
+ size_t len, tlen;
+ const char *base;
arch = fopen(archive, mode);
if (arch == NULL) {
@@ -765,8 +755,8 @@ ArchFindMember(const char *archive, cons
*/
if ((fread(magic, SARMAG, 1, arch) != 1) ||
(strncmp(magic, ARMAG, SARMAG) != 0)) {
- fclose(arch);
- return NULL;
+ fclose(arch);
+ return NULL;
}
/*
@@ -785,13 +775,13 @@ ArchFindMember(const char *archive, cons
}
while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) {
- if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof(arhPtr->ar_fmag) ) != 0) {
- /*
- * The header is bogus, so the archive is bad
- * and there's no way we can recover...
- */
- fclose(arch);
- return NULL;
+ if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof(arhPtr->ar_fmag)) != 0) {
+ /*
+ * The header is bogus, so the archive is bad
+ * and there's no way we can recover...
+ */
+ fclose(arch);
+ return NULL;
} else if (strncmp(member, arhPtr->ar_name, tlen) == 0) {
/*
* If the member's name doesn't take up the entire 'name' field,
@@ -800,7 +790,8 @@ ArchFindMember(const char *archive, cons
* of the matched string is anything but a space, this isn't the
* member we sought.
*/
- if (tlen != sizeof(arhPtr->ar_name) && arhPtr->ar_name[tlen] != ' '){
+ if (tlen != sizeof(arhPtr->ar_name) &&
+ arhPtr->ar_name[tlen] != ' ') {
goto skip;
} else {
/*
@@ -866,7 +857,7 @@ skip:
* extract the size of the file from the 'size' field of the
* header and round it up during the seek.
*/
- arhPtr->ar_size[sizeof(arhPtr->ar_size)-1] = '\0';
+ arhPtr->ar_size[sizeof(arhPtr->ar_size) - 1] = '\0';
size = (int)strtol(arhPtr->ar_size, NULL, 10);
if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) {
fclose(arch);
@@ -901,8 +892,8 @@ skip:
void
Arch_Touch(GNode *gn)
{
- FILE * arch; /* Stream open to archive, positioned properly */
- struct ar_hdr arh; /* Current header describing member */
+ FILE *arch; /* Stream open to archive, positioned properly */
+ struct ar_hdr arh; /* Current header describing member */
char *p1, *p2;
arch = ArchFindMember(Var_Value(ARCHIVE, gn, &p1),
@@ -912,7 +903,7 @@ Arch_Touch(GNode *gn)
bmake_free(p1);
bmake_free(p2);
- snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long) now);
+ snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long)now);
if (arch != NULL) {
(void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch);
@@ -961,13 +952,13 @@ Arch_TouchLib(GNode *gn)
time_t
Arch_MTime(GNode *gn)
{
- struct ar_hdr *arhPtr; /* Header of desired member */
- time_t modTime; /* Modification time as an integer */
+ struct ar_hdr *arhPtr; /* Header of desired member */
+ time_t modTime; /* Modification time as an integer */
char *p1, *p2;
arhPtr = ArchStatMember(Var_Value(ARCHIVE, gn, &p1),
- Var_Value(MEMBER, gn, &p2),
- TRUE);
+ Var_Value(MEMBER, gn, &p2),
+ TRUE);
bmake_free(p1);
bmake_free(p2);
@@ -1039,8 +1030,8 @@ Arch_MemMTime(GNode *gn)
void
Arch_FindLib(GNode *gn, SearchPath *path)
{
- char *libName; /* file name for archive */
- size_t sz = strlen(gn->name) + 6 - 2;
+ char *libName; /* file name for archive */
+ size_t sz = strlen(gn->name) + 6 - 2;
libName = bmake_malloc(sz);
snprintf(libName, sz, "lib%s.a", &gn->name[2]);
Index: src/usr.bin/make/compat.c
diff -u src/usr.bin/make/compat.c:1.164 src/usr.bin/make/compat.c:1.165
--- src/usr.bin/make/compat.c:1.164 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/compat.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: compat.c,v 1.164 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: compat.c,v 1.165 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -82,26 +82,23 @@
* thems as need creatin'
*/
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/wait.h>
-
-#include <ctype.h>
-#include <errno.h>
-#include <signal.h>
-#include <stdio.h>
-
-#include "make.h"
-#include "hash.h"
-#include "dir.h"
-#include "job.h"
-#include "metachar.h"
-#include "pathnames.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <errno.h>
+#include <signal.h>
+
+#include "make.h"
+#include "dir.h"
+#include "job.h"
+#include "metachar.h"
+#include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: compat.c,v 1.164 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: compat.c,v 1.165 2020/10/05 19:27:47 rillig Exp $");
-static GNode *curTarg = NULL;
+static GNode *curTarg = NULL;
static pid_t compatChild;
static int compatSigno;
@@ -112,15 +109,15 @@ static int compatSigno;
static void
CompatDeleteTarget(GNode *gn)
{
- if ((gn != NULL) && !Targ_Precious (gn)) {
- char *p1;
- const char *file = Var_Value(TARGET, gn, &p1);
+ if (gn != NULL && !Targ_Precious(gn)) {
+ char *file_freeIt;
+ const char *file = Var_Value(TARGET, gn, &file_freeIt);
if (!noExecute && eunlink(file) != -1) {
Error("*** %s removed", file);
}
- bmake_free(p1);
+ bmake_free(file_freeIt);
}
}
@@ -139,7 +136,7 @@ CompatInterrupt(int signo)
CompatDeleteTarget(curTarg);
- if ((curTarg != NULL) && !Targ_Precious (curTarg)) {
+ if (curTarg != NULL && !Targ_Precious(curTarg)) {
/*
* Run .INTERRUPT only if hit with interrupt signal
*/
@@ -150,11 +147,13 @@ CompatInterrupt(int signo)
}
}
}
+
if (signo == SIGQUIT)
_exit(signo);
+
/*
- * If there is a child running, pass the signal on
- * we will exist after it has exited.
+ * If there is a child running, pass the signal on.
+ * We will exist after it has exited.
*/
compatSigno = signo;
if (compatChild > 0) {
@@ -165,11 +164,8 @@ CompatInterrupt(int signo)
}
}
-/*-
- *-----------------------------------------------------------------------
- * CompatRunCommand --
- * Execute the next command for a target. If the command returns an
- * error, the node's made field is set to ERROR and creation stops.
+/* Execute the next command for a target. If the command returns an error,
+ * the node's made field is set to ERROR and creation stops.
*
* Input:
* cmdp Command to execute
@@ -177,11 +173,6 @@ CompatInterrupt(int signo)
*
* Results:
* 0 if the command succeeded, 1 if an error occurred.
- *
- * Side Effects:
- * The node's 'made' field may be set to ERROR.
- *
- *-----------------------------------------------------------------------
*/
int
Compat_RunCommand(const char *cmdp, struct GNode *gn)
@@ -215,13 +206,6 @@ Compat_RunCommand(const char *cmdp, stru
(void)Var_Subst(cmd, gn, VARE_WANTRES, &cmdStart);
/* TODO: handle errors */
- /*
- * brk_string will return an argv with a NULL in av[0], thus causing
- * execvp to choke and die horribly. Besides, how can we execute a null
- * command? In any case, we warn the user that the command expanded to
- * nothing (is this the right thing to do?).
- */
-
if (*cmdStart == '\0') {
free(cmdStart);
return 0;
@@ -241,7 +225,7 @@ Compat_RunCommand(const char *cmdp, stru
return 0;
}
- while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) {
+ while (*cmd == '@' || *cmd == '-' || *cmd == '+') {
switch (*cmd) {
case '@':
silent = !DEBUG(LOUD);
@@ -439,8 +423,7 @@ Compat_RunCommand(const char *cmdp, stru
gn->made = ERROR;
if (keepgoing) {
/*
- * Abort the current target, but let others
- * continue.
+ * Abort the current target, but let others continue.
*/
printf(" (continuing)\n");
} else {
@@ -503,7 +486,7 @@ Compat_Make(GNode *gn, GNode *pgn)
{
if (!shellName) /* we came here from jobs */
Shell_Init();
- if (gn->made == UNMADE && (gn == pgn || (pgn->type & OP_MADE) == 0)) {
+ if (gn->made == UNMADE && (gn == pgn || !(pgn->type & OP_MADE))) {
/*
* First mark ourselves to be made, then apply whatever transformations
* the suffix module thinks are necessary. Once that's done, we can
@@ -514,19 +497,19 @@ Compat_Make(GNode *gn, GNode *pgn)
*/
gn->flags |= REMAKE;
gn->made = BEINGMADE;
- if ((gn->type & OP_MADE) == 0)
+ if (!(gn->type & OP_MADE))
Suff_FindDeps(gn);
MakeNodes(gn->children, gn);
- if ((gn->flags & REMAKE) == 0) {
+ if (!(gn->flags & REMAKE)) {
gn->made = ABORTED;
pgn->flags &= ~(unsigned)REMAKE;
goto cohorts;
}
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
- char *p1;
- Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn);
- bmake_free(p1);
+ char *target_freeIt;
+ Var_Set(IMPSRC, Var_Value(TARGET, gn, &target_freeIt), pgn);
+ bmake_free(target_freeIt);
}
/*
@@ -536,7 +519,7 @@ Compat_Make(GNode *gn, GNode *pgn)
* are defined by the Make_OODate function.
*/
DEBUG1(MAKE, "Examining %s...", gn->name);
- if (! Make_OODate(gn)) {
+ if (!Make_OODate(gn)) {
gn->made = UPTODATE;
DEBUG0(MAKE, "up-to-date.\n");
goto cohorts;
@@ -623,10 +606,10 @@ Compat_Make(GNode *gn, GNode *pgn)
pgn->flags &= ~(unsigned)REMAKE;
} else {
if (Lst_FindDatum(gn->implicitParents, pgn) != NULL) {
- char *p1;
- const char *target = Var_Value(TARGET, gn, &p1);
+ char *target_freeIt;
+ const char *target = Var_Value(TARGET, gn, &target_freeIt);
Var_Set(IMPSRC, target != NULL ? target : "", pgn);
- bmake_free(p1);
+ bmake_free(target_freeIt);
}
switch(gn->made) {
case BEINGMADE:
Index: src/usr.bin/make/cond.c
diff -u src/usr.bin/make/cond.c:1.159 src/usr.bin/make/cond.c:1.160
--- src/usr.bin/make/cond.c:1.159 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/cond.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: cond.c,v 1.159 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: cond.c,v 1.160 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -93,7 +93,7 @@
#include "dir.h"
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: cond.c,v 1.159 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: cond.c,v 1.160 2020/10/05 19:27:47 rillig Exp $");
/*
* The parsing of conditional expressions is based on this grammar:
@@ -186,8 +186,7 @@ CondParser_PushBack(CondParser *par, Tok
static void
CondParser_SkipWhitespace(CondParser *par)
{
- while (ch_isspace(par->p[0]))
- par->p++;
+ cpp_skip_whitespace(&par->p);
}
/* Parse the argument of a built-in function.
@@ -538,7 +537,7 @@ EvalNotEmpty(CondParser *par, const char
return lhs[0] != 0;
/* Otherwise action default test ... */
- return par->if_info->defProc(strlen(lhs), lhs) != par->if_info->doNot;
+ return par->if_info->defProc(strlen(lhs), lhs) == !par->if_info->doNot;
}
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
@@ -697,8 +696,7 @@ ParseEmptyArg(const char **linePtr, Bool
}
/* A variable is empty when it just contains spaces... 4/15/92, christos */
- while (ch_isspace(val[0]))
- val++;
+ cpp_skip_whitespace(&val);
/*
* For consistency with the other functions we can't generate the
@@ -745,8 +743,7 @@ CondParser_Func(CondParser *par, Boolean
continue;
cp += fn_def->fn_name_len;
/* There can only be whitespace before the '(' */
- while (ch_isspace(*cp))
- cp++;
+ cpp_skip_whitespace(&cp);
if (*cp != '(')
break;
@@ -776,8 +773,8 @@ CondParser_Func(CondParser *par, Boolean
* expression.
*/
arglen = ParseFuncArg(&cp, doEval, NULL, &arg);
- for (cp1 = cp; ch_isspace(*cp1); cp1++)
- continue;
+ cp1 = cp;
+ cpp_skip_whitespace(&cp1);
if (*cp1 == '=' || *cp1 == '!')
return CondParser_Comparison(par, doEval);
par->p = cp;
@@ -788,7 +785,7 @@ CondParser_Func(CondParser *par, Boolean
* after .if must have been taken literally, so the argument cannot
* be empty - even if it contained a variable expansion.
*/
- t = !doEval || par->if_info->defProc(arglen, arg) != par->if_info->doNot;
+ t = !doEval || par->if_info->defProc(arglen, arg) == !par->if_info->doNot;
free(arg);
return t;
}
Index: src/usr.bin/make/dir.c
diff -u src/usr.bin/make/dir.c:1.157 src/usr.bin/make/dir.c:1.158
--- src/usr.bin/make/dir.c:1.157 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/dir.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.c,v 1.157 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: dir.c,v 1.158 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -129,14 +129,13 @@
#include <dirent.h>
#include <errno.h>
-#include <stdio.h>
#include "make.h"
#include "dir.h"
#include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: dir.c,v 1.157 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: dir.c,v 1.158 2020/10/05 19:27:47 rillig Exp $");
#define DIR_DEBUG0(text) DEBUG0(DIR, text)
#define DIR_DEBUG1(fmt, arg1) DEBUG1(DIR, fmt, arg1)
@@ -219,7 +218,58 @@ typedef ListNode SearchPathNode;
SearchPath *dirSearchPath; /* main search path */
-static CachedDirList *openDirectories; /* the list of all open directories */
+/* A list of cached directories, with fast lookup by directory name. */
+typedef struct OpenDirs {
+ CachedDirList *list;
+ Hash_Table /* of CachedDirListNode */ table;
+} OpenDirs;
+
+static void
+OpenDirs_Init(OpenDirs *odirs)
+{
+ odirs->list = Lst_Init();
+ Hash_InitTable(&odirs->table);
+}
+
+static void MAKE_ATTR_UNUSED
+OpenDirs_Done(OpenDirs *odirs)
+{
+ Dir_ClearPath(odirs->list);
+ Lst_Free(odirs->list);
+ Hash_DeleteTable(&odirs->table);
+}
+
+static CachedDir *
+OpenDirs_Find(OpenDirs *odirs, const char *name)
+{
+ CachedDirListNode *ln = Hash_FindValue(&odirs->table, name);
+ return ln != NULL ? ln->datum : NULL;
+}
+
+static void
+OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir)
+{
+ Hash_Entry *he = Hash_FindEntry(&odirs->table, cdir->name);
+ if (he != NULL)
+ return;
+ he = Hash_CreateEntry(&odirs->table, cdir->name, NULL);
+ Lst_Append(odirs->list, cdir);
+ Hash_SetValue(he, odirs->list->last);
+}
+
+static void
+OpenDirs_Remove(OpenDirs *odirs, const char *name)
+{
+ Hash_Entry *he = Hash_FindEntry(&odirs->table, name);
+ CachedDirListNode *ln;
+ if (he == NULL)
+ return;
+ ln = Hash_GetValue(he);
+ Hash_DeleteEntry(&odirs->table, he);
+ Lst_Remove(odirs->list, ln);
+}
+
+static OpenDirs openDirs; /* the list of all open directories */
/*
* Variables for gathering statistics on the efficiency of the hashing
@@ -337,7 +387,7 @@ void
Dir_Init(void)
{
dirSearchPath = Lst_Init();
- openDirectories = Lst_Init();
+ OpenDirs_Init(&openDirs);
Hash_InitTable(&mtimes);
Hash_InitTable(&lmtimes);
}
@@ -387,11 +437,8 @@ void
Dir_InitDot(void)
{
if (dot != NULL) {
- CachedDirListNode *ln;
-
- /* Remove old entry from openDirectories, but do not destroy. */
- ln = Lst_FindDatum(openDirectories, dot);
- Lst_Remove(openDirectories, ln);
+ /* Remove old entry from openDirs, but do not destroy. */
+ OpenDirs_Remove(&openDirs, dot->name);
}
dot = Dir_AddDir(NULL, ".");
@@ -424,8 +471,7 @@ Dir_End(void)
Dir_Destroy(dot);
Dir_ClearPath(dirSearchPath);
Lst_Free(dirSearchPath);
- Dir_ClearPath(openDirectories);
- Lst_Free(openDirectories);
+ OpenDirs_Done(&openDirs);
Hash_DeleteTable(&mtimes);
#endif
}
@@ -1482,13 +1528,12 @@ Dir_MTime(GNode *gn, Boolean recheck)
CachedDir *
Dir_AddDir(SearchPath *path, const char *name)
{
- SearchPathNode *ln = NULL;
CachedDir *dir = NULL; /* the added directory */
DIR *d;
struct dirent *dp;
if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
- ln = Lst_Find(path, DirFindName, name);
+ SearchPathNode *ln = Lst_Find(path, DirFindName, name);
if (ln != NULL)
return LstNode_Datum(ln);
@@ -1497,9 +1542,8 @@ Dir_AddDir(SearchPath *path, const char
}
if (path != NULL)
- ln = Lst_Find(openDirectories, DirFindName, name);
- if (ln != NULL) {
- dir = LstNode_Datum(ln);
+ dir = OpenDirs_Find(&openDirs, name);
+ if (dir != NULL) {
if (Lst_FindDatum(path, dir) == NULL) {
dir->refCount++;
Lst_Append(path, dir);
@@ -1530,7 +1574,7 @@ Dir_AddDir(SearchPath *path, const char
(void)Hash_CreateEntry(&dir->files, dp->d_name, NULL);
}
(void)closedir(d);
- Lst_Append(openDirectories, dir);
+ OpenDirs_Add(&openDirs, dir);
if (path != NULL)
Lst_Append(path, dir);
}
@@ -1623,11 +1667,7 @@ Dir_Destroy(void *dirp)
dir->refCount--;
if (dir->refCount == 0) {
- CachedDirListNode *node;
-
- node = Lst_FindDatum(openDirectories, dir);
- if (node != NULL)
- Lst_Remove(openDirectories, node);
+ OpenDirs_Remove(&openDirs, dir->name);
Hash_DeleteTable(&dir->files);
free(dir->name);
@@ -1712,7 +1752,7 @@ Dir_PrintDirectories(void)
percentage(hits, hits + bigmisses + nearmisses));
debug_printf("# %-20s referenced\thits\n", "directory");
- for (ln = openDirectories->first; ln != NULL; ln = ln->next) {
+ for (ln = openDirs.list->first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
debug_printf("# %-20s %10d\t%4d\n", dir->name, dir->refCount,
dir->hits);
Index: src/usr.bin/make/enum.c
diff -u src/usr.bin/make/enum.c:1.11 src/usr.bin/make/enum.c:1.12
--- src/usr.bin/make/enum.c:1.11 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/enum.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: enum.c,v 1.11 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $ */
/*
Copyright (c) 2020 Roland Illig <[email protected]>
@@ -27,13 +27,9 @@
POSSIBILITY OF SUCH DAMAGE.
*/
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-
#include "make.h"
-MAKE_RCSID("$NetBSD: enum.c,v 1.11 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: enum.c,v 1.12 2020/10/05 19:27:47 rillig Exp $");
/* Convert a bitset into a string representation, showing the names of the
* individual bits.
@@ -52,7 +48,7 @@ Enum_FlagsToString(char *buf, size_t buf
size_t name_len;
if ((value & spec->es_value) != spec->es_value)
- continue;
+ continue;
value &= ~spec->es_value;
assert(buf_size >= sep_len + 1);
@@ -86,8 +82,8 @@ const char *
Enum_ValueToString(int value, const EnumToStringSpec *spec)
{
for (; spec->es_name[0] != '\0'; spec++) {
- if (value == spec->es_value)
- return spec->es_name;
+ if (value == spec->es_value)
+ return spec->es_name;
}
abort(/* unknown enum value */);
}
Index: src/usr.bin/make/for.c
diff -u src/usr.bin/make/for.c:1.91 src/usr.bin/make/for.c:1.92
--- src/usr.bin/make/for.c:1.91 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/for.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: for.c,v 1.91 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: for.c,v 1.92 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@@ -61,7 +61,7 @@
#include "strlist.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: for.c,v 1.91 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: for.c,v 1.92 2020/10/05 19:27:47 rillig Exp $");
typedef enum {
FOR_SUB_ESCAPE_CHAR = 0x0001,
@@ -119,8 +119,8 @@ For_Eval(const char *line)
Words words;
/* Skip the '.' and any following whitespace */
- for (ptr = line + 1; ch_isspace(*ptr); ptr++)
- continue;
+ ptr = line + 1;
+ cpp_skip_whitespace(&ptr);
/*
* If we are not in a for loop quickly determine if the statement is
@@ -152,8 +152,7 @@ For_Eval(const char *line)
while (TRUE) {
size_t len;
- while (ch_isspace(*ptr))
- ptr++;
+ cpp_skip_whitespace(&ptr);
if (*ptr == '\0') {
Parse_Error(PARSE_FATAL, "missing `in' in for");
For_Free(new_for);
@@ -179,8 +178,7 @@ For_Eval(const char *line)
return -1;
}
- while (ch_isspace(*ptr))
- ptr++;
+ cpp_skip_whitespace(&ptr);
/*
* Make a list with the remaining words.
@@ -267,9 +265,8 @@ For_Accum(const char *line)
const char *ptr = line;
if (*ptr == '.') {
-
- for (ptr++; *ptr && ch_isspace(*ptr); ptr++)
- continue;
+ ptr++;
+ cpp_skip_whitespace(&ptr);
if (strncmp(ptr, "endfor", 6) == 0 && (ch_isspace(ptr[6]) || !ptr[6])) {
DEBUG1(FOR, "For: end for %d\n", forLevel);
Index: src/usr.bin/make/hash.c
diff -u src/usr.bin/make/hash.c:1.42 src/usr.bin/make/hash.c:1.43
--- src/usr.bin/make/hash.c:1.42 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/hash.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.c,v 1.42 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: hash.c,v 1.43 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -79,38 +79,50 @@
#include "make.h"
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: hash.c,v 1.42 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: hash.c,v 1.43 2020/10/05 19:27:47 rillig Exp $");
/*
- * Forward references to local procedures that are used before they're
- * defined:
+ * The ratio of # entries to # buckets at which we rebuild the table to
+ * make it larger.
*/
+#define rebuildLimit 3
-static void RebuildTable(Hash_Table *);
+/* This hash function matches Gosling's emacs. */
+static unsigned int
+hash(const char *key, size_t *out_keylen)
+{
+ unsigned h = 0;
+ const char *p = key;
+ while (*p != '\0')
+ h = (h << 5) - h + (unsigned char)*p++;
+ if (out_keylen != NULL)
+ *out_keylen = (size_t)(p - key);
+ return h;
+}
-/*
- * The following defines the ratio of # entries to # buckets
- * at which we rebuild the table to make it larger.
- */
+static Hash_Entry *
+HashTable_Find(Hash_Table *t, unsigned int h, const char *key)
+{
+ Hash_Entry *e;
+ int chainlen = 0;
-#define rebuildLimit 3
+#ifdef DEBUG_HASH_LOOKUP
+ DEBUG4(HASH, "%s: %p h=%x key=%s\n", __func__, t, h, key);
+#endif
-/* The hash function(s) */
+ for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
+ chainlen++;
+ if (e->namehash == h && strcmp(e->name, key) == 0)
+ break;
+ }
-#ifndef HASH
-/* The default: this one matches Gosling's emacs */
-#define HASH(h, key, p) do { \
- for (h = 0, p = key; *p;) \
- h = (h << 5) - h + (unsigned char)*p++; \
- } while (0)
+ if (chainlen > t->maxchain)
+ t->maxchain = chainlen;
-#endif
+ return e;
+}
-/* Sets up the hash table.
- *
- * Input:
- * t Structure to to hold the table.
- */
+/* Sets up the hash table. */
void
Hash_InitTable(Hash_Table *t)
{
@@ -162,35 +174,47 @@ Hash_DeleteTable(Hash_Table *t)
Hash_Entry *
Hash_FindEntry(Hash_Table *t, const char *key)
{
- Hash_Entry *e;
- unsigned h;
- const char *p;
- int chainlen;
-
- if (t == NULL || t->buckets == NULL) {
- return NULL;
- }
- HASH(h, key, p);
- p = key;
- chainlen = 0;
-#ifdef DEBUG_HASH_LOOKUP
- DEBUG4(HASH, "%s: %p h=%x key=%s\n", __func__, t, h, key);
-#endif
- for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
- chainlen++;
- if (e->namehash == h && strcmp(e->name, p) == 0)
- break;
- }
- if (chainlen > t->maxchain)
- t->maxchain = chainlen;
- return e;
+ unsigned int h = hash(key, NULL);
+ return HashTable_Find(t, h, key);
}
void *
Hash_FindValue(Hash_Table *t, const char *key)
{
- Hash_Entry *he = Hash_FindEntry(t, key);
- return he != NULL ? he->value : NULL;
+ Hash_Entry *he = Hash_FindEntry(t, key);
+ return he != NULL ? he->value : NULL;
+}
+
+/* Makes a new hash table that is larger than the old one. The entire hash
+ * table is moved, so any bucket numbers from the old table become invalid. */
+static void
+RebuildTable(Hash_Table *t)
+{
+ Hash_Entry *e, *next = NULL, **hp, **xp;
+ int i, mask;
+ Hash_Entry **oldhp;
+ int oldsize;
+
+ oldhp = t->buckets;
+ oldsize = i = t->bucketsSize;
+ i <<= 1;
+ t->bucketsSize = i;
+ t->bucketsMask = mask = i - 1;
+ t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
+ while (--i >= 0)
+ *hp++ = NULL;
+ for (hp = oldhp, i = oldsize; --i >= 0;) {
+ for (e = *hp++; e != NULL; e = next) {
+ next = e->next;
+ xp = &t->buckets[e->namehash & mask];
+ e->next = *xp;
+ *xp = e;
+ }
+ }
+ free(oldhp);
+ DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
+ __func__, t, t->bucketsSize, t->numEntries, t->maxchain);
+ t->maxchain = 0;
}
/* Searches the hash table for an entry corresponding to the key.
@@ -207,34 +231,16 @@ Hash_CreateEntry(Hash_Table *t, const ch
{
Hash_Entry *e;
unsigned h;
- const char *p;
- int keylen;
- int chainlen;
+ size_t keylen;
struct Hash_Entry **hp;
- /*
- * Hash the key. As a side effect, save the length (strlen) of the
- * key in case we need to create the entry.
- */
- HASH(h, key, p);
- keylen = p - key;
- p = key;
- chainlen = 0;
-#ifdef DEBUG_HASH_LOOKUP
- DEBUG4(HASH, "%s: %p h=%x key=%s\n", __func__, t, h, key);
-#endif
- for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
- chainlen++;
- if (e->namehash == h && strcmp(e->name, p) == 0) {
- if (newPtr != NULL)
- *newPtr = FALSE;
- break;
- }
- }
- if (chainlen > t->maxchain)
- t->maxchain = chainlen;
- if (e)
+ h = hash(key, &keylen);
+ e = HashTable_Find(t, h, key);
+ if (e) {
+ if (newPtr != NULL)
+ *newPtr = FALSE;
return e;
+ }
/*
* The desired entry isn't there. Before allocating a new entry,
@@ -243,13 +249,14 @@ Hash_CreateEntry(Hash_Table *t, const ch
*/
if (t->numEntries >= rebuildLimit * t->bucketsSize)
RebuildTable(t);
+
e = bmake_malloc(sizeof(*e) + keylen);
hp = &t->buckets[h & t->bucketsMask];
e->next = *hp;
*hp = e;
Hash_SetValue(e, NULL);
e->namehash = h;
- (void)strcpy(e->name, p);
+ memcpy(e->name, key, keylen + 1);
t->numEntries++;
if (newPtr != NULL)
@@ -263,8 +270,6 @@ Hash_DeleteEntry(Hash_Table *t, Hash_Ent
{
Hash_Entry **hp, *p;
- if (e == NULL)
- return;
for (hp = &t->buckets[e->namehash & t->bucketsMask];
(p = *hp) != NULL; hp = &p->next) {
if (p == e) {
@@ -274,7 +279,6 @@ Hash_DeleteEntry(Hash_Table *t, Hash_Ent
return;
}
}
- (void)write(2, "bad call to Hash_DeleteEntry\n", 29);
abort();
}
@@ -330,38 +334,6 @@ Hash_EnumNext(Hash_Search *searchPtr)
return e;
}
-/* Makes a new hash table that is larger than the old one. The entire hash
- * table is moved, so any bucket numbers from the old table become invalid. */
-static void
-RebuildTable(Hash_Table *t)
-{
- Hash_Entry *e, *next = NULL, **hp, **xp;
- int i, mask;
- Hash_Entry **oldhp;
- int oldsize;
-
- oldhp = t->buckets;
- oldsize = i = t->bucketsSize;
- i <<= 1;
- t->bucketsSize = i;
- t->bucketsMask = mask = i - 1;
- t->buckets = hp = bmake_malloc(sizeof(*hp) * i);
- while (--i >= 0)
- *hp++ = NULL;
- for (hp = oldhp, i = oldsize; --i >= 0;) {
- for (e = *hp++; e != NULL; e = next) {
- next = e->next;
- xp = &t->buckets[e->namehash & mask];
- e->next = *xp;
- *xp = e;
- }
- }
- free(oldhp);
- DEBUG5(HASH, "%s: %p size=%d entries=%d maxchain=%d\n",
- __func__, t, t->bucketsSize, t->numEntries, t->maxchain);
- t->maxchain = 0;
-}
-
void
Hash_ForEach(Hash_Table *t, void (*action)(void *, void *), void *data)
{
@@ -377,6 +349,6 @@ Hash_ForEach(Hash_Table *t, void (*actio
void
Hash_DebugStats(Hash_Table *t, const char *name)
{
- DEBUG4(HASH, "Hash_Table %s: size=%d numEntries=%d maxchain=%d\n",
- name, t->bucketsSize, t->numEntries, t->maxchain);
+ DEBUG4(HASH, "Hash_Table %s: size=%d numEntries=%d maxchain=%d\n",
+ name, t->bucketsSize, t->numEntries, t->maxchain);
}
Index: src/usr.bin/make/job.c
diff -u src/usr.bin/make/job.c:1.259 src/usr.bin/make/job.c:1.260
--- src/usr.bin/make/job.c:1.259 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/job.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: job.c,v 1.259 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: job.c,v 1.260 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -143,7 +143,7 @@
#include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: job.c,v 1.259 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: job.c,v 1.260 2020/10/05 19:27:47 rillig Exp $");
# define STATIC static
@@ -264,7 +264,7 @@ static Shell shells[] = {
*/
{
"csh",
- TRUE, "unset verbose", "set verbose", "unset verbose", 10,
+ TRUE, "unset verbose", "set verbose", "unset verbose", 13,
FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"\n", "", "'\\\n'", '#',
"v", "e",
},
@@ -655,8 +655,7 @@ JobPrintCommand(void *cmdp, void *jobp)
cmd++;
}
- while (ch_isspace(*cmd))
- cmd++;
+ pp_skip_whitespace(&cmd);
/*
* If the shell doesn't have error control the alternate echo'ing will
@@ -1576,19 +1575,14 @@ JobStart(GNode *gn, int flags)
}
static char *
-JobOutput(Job *job, char *cp, char *endp, int msg)
+JobOutput(Job *job, char *cp, char *endp)
{
char *ecp;
- if (commandShell->noPrint) {
- ecp = Str_FindSubstring(cp, commandShell->noPrint);
- while (ecp != NULL) {
+ if (commandShell->noPrint && commandShell->noPrint[0] != '\0') {
+ while ((ecp = strstr(cp, commandShell->noPrint)) != NULL) {
if (cp != ecp) {
*ecp = '\0';
- if (!beSilent && msg && job->node != lastNode) {
- MESSAGE(stdout, job->node);
- lastNode = job->node;
- }
/*
* The only way there wouldn't be a newline after
* this line is if it were the last in the buffer.
@@ -1609,7 +1603,6 @@ JobOutput(Job *job, char *cp, char *endp
while (*cp == ' ' || *cp == '\t' || *cp == '\n') {
cp++;
}
- ecp = Str_FindSubstring(cp, commandShell->noPrint);
} else {
return cp;
}
@@ -1738,7 +1731,7 @@ end_loop:
if (i >= job->curPos) {
char *cp;
- cp = JobOutput(job, job->outBuf, &job->outBuf[i], FALSE);
+ cp = JobOutput(job, job->outBuf, &job->outBuf[i]);
/*
* There's still more in that thar buffer. This time, though,
@@ -2213,8 +2206,7 @@ Job_ParseShell(char *line)
Boolean fullSpec = FALSE;
Shell *sh;
- while (ch_isspace(*line))
- line++;
+ pp_skip_whitespace(&line);
free(shellArgv);
Index: src/usr.bin/make/main.c
diff -u src/usr.bin/make/main.c:1.368 src/usr.bin/make/main.c:1.369
--- src/usr.bin/make/main.c:1.368 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/main.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: main.c,v 1.368 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: main.c,v 1.369 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -106,16 +106,12 @@
#include <sys/utsname.h>
#include <sys/wait.h>
-#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
#include <time.h>
#include "make.h"
-#include "hash.h"
#include "dir.h"
#include "job.h"
#include "pathnames.h"
@@ -126,10 +122,11 @@
#endif
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: main.c,v 1.368 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: main.c,v 1.369 2020/10/05 19:27:47 rillig Exp $");
#if defined(MAKE_NATIVE) && !defined(lint)
-__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\
- The Regents of the University of California. All rights reserved.");
+__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
+ "The Regents of the University of California. "
+ "All rights reserved.");
#endif
#ifndef DEFMAXLOCAL
@@ -270,6 +267,9 @@ parse_debug_options(const char *argvalue
for (modules = argvalue; *modules; ++modules) {
switch (*modules) {
+ case '0': /* undocumented, only intended for tests */
+ debug &= DEBUG_LINT;
+ break;
case 'A':
debug = ~(0|DEBUG_LINT);
break;
@@ -673,9 +673,10 @@ rearg:
* perform them if so. Else take them to be targets and stuff them
* on the end of the "create" list.
*/
- for (; argc > 1; ++argv, --argc)
- if (Parse_IsVar(argv[1])) {
- Parse_DoVar(argv[1], VAR_CMD);
+ for (; argc > 1; ++argv, --argc) {
+ VarAssign var;
+ if (Parse_IsVar(argv[1], &var)) {
+ Parse_DoVar(&var, VAR_CMD);
} else {
if (!*argv[1])
Punt("illegal (null) argument.");
@@ -683,6 +684,7 @@ rearg:
goto rearg;
Lst_Append(create, bmake_strdup(argv[1]));
}
+ }
return;
noarg:
@@ -971,6 +973,60 @@ InitVarTargets(void)
}
}
+static const char *
+init_machine(const struct utsname *utsname)
+{
+ const char *machine = getenv("MACHINE");
+ if (machine != NULL)
+ return machine;
+
+#ifdef MAKE_NATIVE
+ return utsname->machine;
+#else
+#ifdef MAKE_MACHINE
+ return MAKE_MACHINE;
+#else
+ return "unknown";
+#endif
+#endif
+}
+
+static const char *
+init_machine_arch(void)
+{
+ const char *env = getenv("MACHINE_ARCH");
+ if (env != NULL)
+ return env;
+
+#ifdef MAKE_NATIVE
+ {
+ struct utsname utsname;
+ static char machine_arch_buf[sizeof(utsname.machine)];
+ const int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
+ size_t len = sizeof(machine_arch_buf);
+
+ if (sysctl(mib, __arraycount(mib), machine_arch_buf,
+ &len, NULL, 0) < 0) {
+ (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname,
+ strerror(errno));
+ exit(2);
+ }
+
+ return machine_arch_buf;
+ }
+#else
+#ifndef MACHINE_ARCH
+#ifdef MAKE_MACHINE_ARCH
+ return MAKE_MACHINE_ARCH;
+#else
+ return "unknown";
+#endif
+#else
+ return MACHINE_ARCH;
+#endif
+#endif
+}
+
/*-
* main --
* The main function, for obvious reasons. Initializes variables
@@ -995,8 +1051,8 @@ main(int argc, char **argv)
struct stat sb, sa;
char *p1, *path;
char mdpath[MAXPATHLEN];
- const char *machine = getenv("MACHINE");
- const char *machine_arch = getenv("MACHINE_ARCH");
+ const char *machine;
+ const char *machine_arch;
char *syspath = getenv("MAKESYSPATH");
StringList *sysMkPath; /* Path of sys.mk */
char *cp = NULL, *start;
@@ -1051,44 +1107,8 @@ main(int argc, char **argv)
* Note that both MACHINE and MACHINE_ARCH are decided at
* run-time.
*/
- if (!machine) {
-#ifdef MAKE_NATIVE
- machine = utsname.machine;
-#else
-#ifdef MAKE_MACHINE
- machine = MAKE_MACHINE;
-#else
- machine = "unknown";
-#endif
-#endif
- }
-
- if (!machine_arch) {
-#ifdef MAKE_NATIVE
- static char machine_arch_buf[sizeof(utsname.machine)];
- const int mib[2] = { CTL_HW, HW_MACHINE_ARCH };
- size_t len = sizeof(machine_arch_buf);
-
- if (sysctl(mib, __arraycount(mib), machine_arch_buf,
- &len, NULL, 0) < 0) {
- (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname,
- strerror(errno));
- exit(2);
- }
-
- machine_arch = machine_arch_buf;
-#else
-#ifndef MACHINE_ARCH
-#ifdef MAKE_MACHINE_ARCH
- machine_arch = MAKE_MACHINE_ARCH;
-#else
- machine_arch = "unknown";
-#endif
-#else
- machine_arch = MACHINE_ARCH;
-#endif
-#endif
- }
+ machine = init_machine(&utsname);
+ machine_arch = init_machine_arch();
myPid = getpid(); /* remember this for vFork() */
@@ -1715,16 +1735,10 @@ bad:
return bmake_strdup("");
}
-/*-
- * Error --
- * Print an error message given its format.
+/* Print a printf-style error message.
*
- * Results:
- * None.
- *
- * Side Effects:
- * The message is printed.
- */
+ * This error message has no consequences, in particular it does not affect
+ * the exit status. */
void
Error(const char *fmt, ...)
{
Index: src/usr.bin/make/make.h
diff -u src/usr.bin/make/make.h:1.154 src/usr.bin/make/make.h:1.155
--- src/usr.bin/make/make.h:1.154 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/make.h Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: make.h,v 1.154 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: make.h,v 1.155 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -259,7 +259,9 @@ typedef enum {
/* Already processed by Suff_FindDeps */
OP_DEPS_FOUND = 1 << 25,
/* Node found while expanding .ALLSRC */
- OP_MARK = 1 << 24
+ OP_MARK = 1 << 24,
+
+ OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
} GNodeType;
typedef enum {
@@ -358,15 +360,6 @@ typedef struct GNode {
int lineno;
} GNode;
-#define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute)
-/*
- * OP_NOP will return TRUE if the node with the given type was not the
- * object of a dependency operator
- */
-#define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000)
-
-#define OP_NOTARGET (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)
-
/*
* Error levels for parsing. PARSE_FATAL means the process cannot continue
* once the makefile has been parsed. PARSE_WARNING means it can. Passed
@@ -568,6 +561,22 @@ int mkTempFile(const char *, char **);
int str2Lst_Append(StringList *, char *, const char *);
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
+static Boolean MAKE_ATTR_UNUSED
+NoExecute(GNode *gn)
+{
+ return (gn->type & OP_MAKE) ? noRecursiveExecute : noExecute;
+}
+
+/*
+ * See if the node with the given type was not the object of a dependency
+ * operator.
+ */
+static Boolean MAKE_ATTR_UNUSED
+OP_NOP(GNodeType t)
+{
+ return !(t & OP_OPMASK);
+}
+
#ifdef __GNUC__
#define UNCONST(ptr) ({ \
union __unconst { \
@@ -616,6 +625,20 @@ static inline MAKE_ATTR_UNUSED char ch_t
static inline MAKE_ATTR_UNUSED char ch_toupper(char ch)
{ return (char)toupper((unsigned char)ch); }
+static inline MAKE_ATTR_UNUSED void
+cpp_skip_whitespace(const char **pp)
+{
+ while (ch_isspace(**pp))
+ (*pp)++;
+}
+
+static inline MAKE_ATTR_UNUSED void
+pp_skip_whitespace(char **pp)
+{
+ while (ch_isspace(**pp))
+ (*pp)++;
+}
+
#ifndef MAKE_NATIVE
#define MAKE_RCSID(id) static volatile char rcsid[] = id
#else
Index: src/usr.bin/make/make_malloc.c
diff -u src/usr.bin/make/make_malloc.c:1.22 src/usr.bin/make/make_malloc.c:1.23
--- src/usr.bin/make/make_malloc.c:1.22 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/make_malloc.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: make_malloc.c,v 1.22 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $ */
/*-
* Copyright (c) 2009 The NetBSD Foundation, Inc.
@@ -26,14 +26,11 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
#include <errno.h>
#include "make.h"
-MAKE_RCSID("$NetBSD: make_malloc.c,v 1.22 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: make_malloc.c,v 1.23 2020/10/05 19:27:47 rillig Exp $");
#ifndef USE_EMALLOC
Index: src/usr.bin/make/nonints.h
diff -u src/usr.bin/make/nonints.h:1.139 src/usr.bin/make/nonints.h:1.140
--- src/usr.bin/make/nonints.h:1.139 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/nonints.h Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: nonints.h,v 1.139 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: nonints.h,v 1.140 2020/10/05 19:27:47 rillig Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@@ -120,9 +120,27 @@ Boolean getBoolean(const char *, Boolean
char *cached_realpath(const char *, char *);
/* parse.c */
+
+typedef enum VarAssignOp {
+ VAR_NORMAL, /* = */
+ VAR_SUBST, /* := */
+ VAR_SHELL, /* != or :sh= */
+ VAR_APPEND, /* += */
+ VAR_DEFAULT /* ?= */
+} VarAssignOp;
+
+typedef struct VarAssign {
+ const char *nameStart; /* unexpanded */
+ const char *nameEndDraft; /* before operator adjustment */
+ char *varname;
+ const char *eq; /* the '=' of the assignment operator */
+ VarAssignOp op;
+ const char *value; /* unexpanded */
+} VarAssign;
+
void Parse_Error(int, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
-Boolean Parse_IsVar(const char *);
-void Parse_DoVar(char *, GNode *);
+Boolean Parse_IsVar(const char *, VarAssign *out_var);
+void Parse_DoVar(VarAssign *, GNode *);
void Parse_AddIncludeDir(const char *);
void Parse_File(const char *, int);
void Parse_Init(void);
@@ -147,7 +165,6 @@ Words_Free(Words w) {
char *str_concat2(const char *, const char *);
char *str_concat3(const char *, const char *, const char *);
char *str_concat4(const char *, const char *, const char *, const char *);
-char *Str_FindSubstring(const char *, const char *);
Boolean Str_Match(const char *, const char *);
/* suff.c */
@@ -193,10 +210,13 @@ void Targ_Propagate(void);
/* var.c */
typedef enum {
+ VARE_NONE = 0,
/* Treat undefined variables as errors. */
VARE_UNDEFERR = 0x01,
/* Expand and evaluate variables during parsing. */
VARE_WANTRES = 0x02,
+ /* In an assignment using the ':=' operator, keep '$$' as '$$' instead
+ * of reducing it to a single '$'. */
VARE_ASSIGN = 0x04
} VarEvalFlags;
Index: src/usr.bin/make/parse.c
diff -u src/usr.bin/make/parse.c:1.367 src/usr.bin/make/parse.c:1.368
--- src/usr.bin/make/parse.c:1.367 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/parse.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.367 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.368 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -116,7 +116,6 @@
#include <sys/stat.h>
#include <errno.h>
#include <stdarg.h>
-#include <stdio.h>
#include <stdint.h>
#ifndef MAP_FILE
@@ -132,7 +131,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.367 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.368 2020/10/05 19:27:47 rillig Exp $");
/* types and constants */
@@ -205,6 +204,8 @@ typedef enum {
Attribute /* Generic attribute */
} ParseSpecial;
+typedef List SearchPathList;
+
/* result data */
/*
@@ -585,21 +586,8 @@ ParseMark(GNode *gn)
gn->lineno = curFile->lineno;
}
-/*-
- *----------------------------------------------------------------------
- * ParseFindKeyword --
- * Look in the table of keywords for one matching the given string.
- *
- * Input:
- * str String to find
- *
- * Results:
- * The index of the keyword, or -1 if it isn't there.
- *
- * Side Effects:
- * None
- *----------------------------------------------------------------------
- */
+/* Look in the table of keywords for one matching the given string.
+ * Return the index of the keyword, or -1 if it isn't there. */
static int
ParseFindKeyword(const char *str)
{
@@ -660,8 +648,7 @@ PrintLocation(FILE *f, const char *filen
*
* Increment "fatals" if the level is PARSE_FATAL, and continue parsing
* until the end of the current top-level makefile, then exit (see
- * Parse_File).
- */
+ * Parse_File). */
static void
ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type,
const char *fmt, va_list ap)
@@ -768,8 +755,7 @@ ParseMessage(char *line)
line++;
if (!ch_isspace(*line))
return FALSE; /* not for us */
- while (ch_isspace(*line))
- line++;
+ pp_skip_whitespace(&line);
(void)Var_Subst(line, VAR_CMD, VARE_WANTRES, &line);
/* TODO: handle errors */
@@ -889,33 +875,12 @@ ApplyDependencyOperator(GNodeType op)
break;
}
-/*-
- *---------------------------------------------------------------------
- * ParseDoSrc --
- * Given the name of a source, figure out if it is an attribute
- * and apply it to the targets if it is. Else decide if there is
- * some attribute which should be applied *to* the source because
- * of some special target and apply it if so. Otherwise, make the
- * source be a child of the targets in the list 'targets'
- *
- * Input:
- * tOp operator (if any) from special targets
- * src name of the source to handle
- *
- * Results:
- * None
- *
- * Side Effects:
- * Operator bits may be added to the list of targets or to the source.
- * The targets may have a new source added to their lists of children.
- *---------------------------------------------------------------------
- */
-static void
-ParseDoSrc(int tOp, const char *src, ParseSpecial specType)
+static Boolean
+ParseDoSrcKeyword(const char *src, ParseSpecial specType)
{
- GNode *gn = NULL;
static int wait_number = 0;
char wait_src[16];
+ GNode *gn;
if (*src == '.' && ch_isupper(src[1])) {
int keywd = ParseFindKeyword(src);
@@ -923,7 +888,7 @@ ParseDoSrc(int tOp, const char *src, Par
int op = parseKeywords[keywd].op;
if (op != 0) {
ApplyDependencyOperator(op);
- return;
+ return TRUE;
}
if (parseKeywords[keywd].spec == Wait) {
/*
@@ -944,82 +909,114 @@ ParseDoSrc(int tOp, const char *src, Par
struct ParseLinkSrcArgs args = { gn, specType };
Lst_ForEach(targets, ParseLinkSrc, &args);
}
- return;
+ return TRUE;
}
}
}
+ return FALSE;
+}
- switch (specType) {
- case Main:
- /*
- * If we have noted the existence of a .MAIN, it means we need
- * to add the sources of said target to the list of things
- * to create. The string 'src' is likely to be free, so we
- * must make a new copy of it. Note that this will only be
- * invoked if the user didn't specify a target on the command
- * line. This is to allow #ifmake's to succeed, or something...
- */
- Lst_Append(create, bmake_strdup(src));
- /*
- * Add the name to the .TARGETS variable as well, so the user can
- * employ that, if desired.
- */
- Var_Append(".TARGETS", src, VAR_GLOBAL);
- return;
+static void
+ParseDoSrcMain(const char *src)
+{
+ /*
+ * If we have noted the existence of a .MAIN, it means we need
+ * to add the sources of said target to the list of things
+ * to create. The string 'src' is likely to be free, so we
+ * must make a new copy of it. Note that this will only be
+ * invoked if the user didn't specify a target on the command
+ * line. This is to allow #ifmake's to succeed, or something...
+ */
+ Lst_Append(create, bmake_strdup(src));
+ /*
+ * Add the name to the .TARGETS variable as well, so the user can
+ * employ that, if desired.
+ */
+ Var_Append(".TARGETS", src, VAR_GLOBAL);
+}
- case Order:
- /*
- * Create proper predecessor/successor links between the previous
- * source and the current one.
- */
- gn = Targ_GetNode(src);
- if (doing_depend)
- ParseMark(gn);
- if (predecessor != NULL) {
- Lst_Append(predecessor->order_succ, gn);
- Lst_Append(gn->order_pred, predecessor);
- if (DEBUG(PARSE)) {
- debug_printf("# %s: added Order dependency %s - %s\n",
- __func__, predecessor->name, gn->name);
- Targ_PrintNode(predecessor, 0);
- Targ_PrintNode(gn, 0);
- }
+static void
+ParseDoSrcOrder(const char *src)
+{
+ GNode *gn;
+ /*
+ * Create proper predecessor/successor links between the previous
+ * source and the current one.
+ */
+ gn = Targ_GetNode(src);
+ if (doing_depend)
+ ParseMark(gn);
+ if (predecessor != NULL) {
+ Lst_Append(predecessor->order_succ, gn);
+ Lst_Append(gn->order_pred, predecessor);
+ if (DEBUG(PARSE)) {
+ debug_printf("# %s: added Order dependency %s - %s\n",
+ __func__, predecessor->name, gn->name);
+ Targ_PrintNode(predecessor, 0);
+ Targ_PrintNode(gn, 0);
}
- /*
- * The current source now becomes the predecessor for the next one.
- */
- predecessor = gn;
- break;
+ }
+ /*
+ * The current source now becomes the predecessor for the next one.
+ */
+ predecessor = gn;
+}
- default:
- /*
- * If the source is not an attribute, we need to find/create
- * a node for it. After that we can apply any operator to it
- * from a special target or link it to its parents, as
- * appropriate.
- *
- * In the case of a source that was the object of a :: operator,
- * the attribute is applied to all of its instances (as kept in
- * the 'cohorts' list of the node) or all the cohorts are linked
- * to all the targets.
- */
+static void
+ParseDoSrcOther(const char *src, GNodeType tOp, ParseSpecial specType)
+{
+ GNode *gn;
- /* Find/create the 'src' node and attach to all targets */
- gn = Targ_GetNode(src);
- if (doing_depend)
- ParseMark(gn);
- if (tOp) {
- gn->type |= tOp;
- } else {
- {
- struct ParseLinkSrcArgs args = { gn, specType };
- Lst_ForEach(targets, ParseLinkSrc, &args);
- }
+ /*
+ * If the source is not an attribute, we need to find/create
+ * a node for it. After that we can apply any operator to it
+ * from a special target or link it to its parents, as
+ * appropriate.
+ *
+ * In the case of a source that was the object of a :: operator,
+ * the attribute is applied to all of its instances (as kept in
+ * the 'cohorts' list of the node) or all the cohorts are linked
+ * to all the targets.
+ */
+
+ /* Find/create the 'src' node and attach to all targets */
+ gn = Targ_GetNode(src);
+ if (doing_depend)
+ ParseMark(gn);
+ if (tOp) {
+ gn->type |= tOp;
+ } else {
+ {
+ struct ParseLinkSrcArgs args = { gn, specType };
+ Lst_ForEach(targets, ParseLinkSrc, &args);
}
- break;
}
}
+/* Given the name of a source in a dependency line, figure out if it is an
+ * attribute (such as .SILENT) and apply it to the targets if it is. Else
+ * decide if there is some attribute which should be applied *to* the source
+ * because of some special target (such as .PHONY) and apply it if so.
+ * Otherwise, make the source a child of the targets in the list 'targets'.
+ *
+ * Input:
+ * tOp operator (if any) from special targets
+ * src name of the source to handle
+ */
+static void
+ParseDoSrc(GNodeType tOp, const char *src, ParseSpecial specType)
+{
+ if (ParseDoSrcKeyword(src, specType))
+ return;
+
+ if (specType == Main)
+ ParseDoSrcMain(src);
+ else if (specType == Order)
+ ParseDoSrcOrder(src);
+ else
+ ParseDoSrcOther(src, tOp, specType);
+}
+
/* If we have yet to decide on a main target to make, in the absence of any
* user input, we want the first target on the first dependency line that is
* actually a real target (i.e. isn't a .USE or .EXEC rule) to be made. */
@@ -1071,8 +1068,7 @@ ParseErrorNoDependency(const char *lstar
else if (lstart[0] == '.') {
const char *dirstart = lstart + 1;
const char *dirend;
- while (ch_isspace(*dirstart))
- dirstart++;
+ cpp_skip_whitespace(&dirstart);
dirend = dirstart;
while (ch_isalnum(*dirend) || *dirend == '-')
dirend++;
@@ -1116,64 +1112,369 @@ ParseDependencyTargetWord(/*const*/ char
*pp = cp;
}
-/* Parse a dependency line consisting of targets, followed by a dependency
- * operator, optionally followed by sources.
- *
- * The nodes of the sources are linked as children to the nodes of the
- * targets. Nodes are created as necessary.
- *
- * The operator is applied to each node in the global 'targets' list,
- * which is where the nodes found for the targets are kept, by means of
- * the ParseDoOp function.
- *
- * The sources are parsed in much the same way as the targets, except
- * that they are expanded using the wildcarding scheme of the C-Shell,
- * and all instances of the resulting words in the list of all targets
- * are found. Each of the resulting nodes is then linked to each of the
- * targets as one of its children.
- *
- * Certain targets and sources such as .PHONY or .PRECIOUS are handled
- * specially. These are the ones detailed by the specType variable.
- *
- * The storing of transformation rules such as '.c.o' is also taken care of
- * here. A target is recognized as a transformation rule by calling
- * Suff_IsTransform. If it is a transformation rule, its node is gotten
- * from the suffix module via Suff_AddTransform rather than the standard
- * Targ_FindNode in the target module.
+/*
+ * Certain special targets have special semantics:
+ * .PATH Have to set the dirSearchPath
+ * variable too
+ * .MAIN Its sources are only used if
+ * nothing has been specified to
+ * create.
+ * .DEFAULT Need to create a node to hang
+ * commands on, but we don't want
+ * it in the graph, nor do we want
+ * it to be the Main Target, so we
+ * create it, set OP_NOTMAIN and
+ * add it to the list, setting
+ * DEFAULT to the new node for
+ * later use. We claim the node is
+ * A transformation rule to make
+ * life easier later, when we'll
+ * use Make_HandleUse to actually
+ * apply the .DEFAULT commands.
+ * .PHONY The list of targets
+ * .NOPATH Don't search for file in the path
+ * .STALE
+ * .BEGIN
+ * .END
+ * .ERROR
+ * .DELETE_ON_ERROR
+ * .INTERRUPT Are not to be considered the
+ * main target.
+ * .NOTPARALLEL Make only one target at a time.
+ * .SINGLESHELL Create a shell for each command.
+ * .ORDER Must set initial predecessor to NULL
+ */
+static void
+ParseDoDependencyTargetSpecial(ParseSpecial *const inout_specType,
+ const char *const line,
+ SearchPathList **const inout_paths)
+{
+ switch (*inout_specType) {
+ case ExPath:
+ if (*inout_paths == NULL) {
+ *inout_paths = Lst_Init();
+ }
+ Lst_Append(*inout_paths, dirSearchPath);
+ break;
+ case Main:
+ if (!Lst_IsEmpty(create)) {
+ *inout_specType = Not;
+ }
+ break;
+ case Begin:
+ case End:
+ case Stale:
+ case dotError:
+ case Interrupt: {
+ GNode *gn = Targ_GetNode(line);
+ if (doing_depend)
+ ParseMark(gn);
+ gn->type |= OP_NOTMAIN|OP_SPECIAL;
+ Lst_Append(targets, gn);
+ break;
+ }
+ case Default: {
+ GNode *gn = Targ_NewGN(".DEFAULT");
+ gn->type |= OP_NOTMAIN|OP_TRANSFORM;
+ Lst_Append(targets, gn);
+ DEFAULT = gn;
+ break;
+ }
+ case DeleteOnError:
+ deleteOnError = TRUE;
+ break;
+ case NotParallel:
+ maxJobs = 1;
+ break;
+ case SingleShell:
+ compatMake = TRUE;
+ break;
+ case Order:
+ predecessor = NULL;
+ break;
+ default:
+ break;
+ }
+}
+
+/*
+ * .PATH<suffix> has to be handled specially.
+ * Call on the suffix module to give us a path to modify.
*/
-static void
-ParseDoDependency(char *line)
+static Boolean
+ParseDoDependencyTargetPath(const char *const line,
+ SearchPathList **const inout_paths)
{
- typedef List SearchPathList;
+ SearchPath *path;
- char *cp; /* our current position */
- GNodeType op; /* the operator on the line */
- char savec; /* a place to save a character */
- SearchPathList *paths; /* search paths to alter when parsing
- * a list of .PATH targets */
- int tOp; /* operator from special target */
- GNodeList *sources; /* archive sources after expansion */
- StringList *curTargs; /* target names to be found and added
- * to the targets list */
- char *lstart = line;
+ path = Suff_GetPath(&line[5]);
+ if (path == NULL) {
+ Parse_Error(PARSE_FATAL,
+ "Suffix '%s' not defined (yet)",
+ &line[5]);
+ return FALSE;
+ } else {
+ if (*inout_paths == NULL) {
+ *inout_paths = Lst_Init();
+ }
+ Lst_Append(*inout_paths, path);
+ }
+ return TRUE;
+}
+
+/*
+ * See if it's a special target and if so set specType to match it.
+ */
+static Boolean
+ParseDoDependencyTarget(const char *const line,
+ ParseSpecial *const inout_specType,
+ GNodeType *out_tOp,
+ SearchPathList **inout_paths)
+{
+ int keywd;
+
+ if (!(*line == '.' && ch_isupper(line[1])))
+ return TRUE;
/*
- * specType contains the SPECial TYPE of the current target. It is Not
- * if the target is unspecial. If it *is* special, however, the children
- * are linked as children of the parent but not vice versa.
+ * See if the target is a special target that must have it
+ * or its sources handled specially.
*/
- ParseSpecial specType = Not;
+ keywd = ParseFindKeyword(line);
+ if (keywd != -1) {
+ if (*inout_specType == ExPath && parseKeywords[keywd].spec != ExPath) {
+ Parse_Error(PARSE_FATAL, "Mismatched special targets");
+ return FALSE;
+ }
- DEBUG1(PARSE, "ParseDoDependency(%s)\n", line);
- tOp = 0;
+ *inout_specType = parseKeywords[keywd].spec;
+ *out_tOp = parseKeywords[keywd].op;
- paths = NULL;
+ ParseDoDependencyTargetSpecial(inout_specType, line, inout_paths);
- curTargs = Lst_Init();
+ } else if (strncmp(line, ".PATH", 5) == 0) {
+ *inout_specType = ExPath;
+ if (!ParseDoDependencyTargetPath(line, inout_paths))
+ return FALSE;
+ }
+ return TRUE;
+}
- /*
- * First, grind through the targets.
- */
+static void
+ParseDoDependencyTargetMundane(char *const line,
+ StringList *const curTargs)
+{
+ if (Dir_HasWildcards(line)) {
+ /*
+ * Targets are to be sought only in the current directory,
+ * so create an empty path for the thing. Note we need to
+ * use Dir_Destroy in the destruction of the path as the
+ * Dir module could have added a directory to the path...
+ */
+ SearchPath *emptyPath = Lst_Init();
+
+ Dir_Expand(line, emptyPath, curTargs);
+
+ Lst_Destroy(emptyPath, Dir_Destroy);
+ } else {
+ /*
+ * No wildcards, but we want to avoid code duplication,
+ * so create a list with the word on it.
+ */
+ Lst_Append(curTargs, line);
+ }
+
+ /* Apply the targets. */
+
+ while(!Lst_IsEmpty(curTargs)) {
+ char *targName = Lst_Dequeue(curTargs);
+ GNode *gn = Suff_IsTransform(targName)
+ ? Suff_AddTransform(targName)
+ : Targ_GetNode(targName);
+ if (doing_depend)
+ ParseMark(gn);
+
+ Lst_Append(targets, gn);
+ }
+}
+
+static void
+ParseDoDependencyTargetExtraWarn(char **pp, const char *lstart)
+{
+ Boolean warning = FALSE;
+ char *cp = *pp;
+
+ while (*cp && (ParseIsEscaped(lstart, cp) ||
+ (*cp != '!' && *cp != ':'))) {
+ if (ParseIsEscaped(lstart, cp) ||
+ (*cp != ' ' && *cp != '\t')) {
+ warning = TRUE;
+ }
+ cp++;
+ }
+ if (warning) {
+ Parse_Error(PARSE_WARNING, "Extra target ignored");
+ }
+ *pp = cp;
+}
+
+static void
+ParseDoDependencyCheckSpec(ParseSpecial const specType)
+{
+ switch(specType) {
+ default:
+ Parse_Error(PARSE_WARNING,
+ "Special and mundane targets don't mix. Mundane ones ignored");
+ break;
+ case Default:
+ case Stale:
+ case Begin:
+ case End:
+ case dotError:
+ case Interrupt:
+ /*
+ * These four create nodes on which to hang commands, so
+ * targets shouldn't be empty...
+ */
+ case Not:
+ /*
+ * Nothing special here -- targets can be empty if it wants.
+ */
+ break;
+ }
+}
+
+static Boolean
+ParseDoDependencyParseOp(char **const pp, const char *const lstart,
+ GNodeType *const out_op)
+{
+ const char *cp = *pp;
+
+ if (*cp == '!') {
+ *out_op = OP_FORCE;
+ (*pp)++;
+ return TRUE;
+ }
+
+ if (*cp == ':') {
+ if (cp[1] == ':') {
+ *out_op = OP_DOUBLEDEP;
+ (*pp) += 2;
+ } else {
+ *out_op = OP_DEPENDS;
+ (*pp)++;
+ }
+ return TRUE;
+ }
+
+ {
+ const char *msg = lstart[0] == '.' ? "Unknown directive"
+ : "Missing dependency operator";
+ Parse_Error(PARSE_FATAL, "%s", msg);
+ return FALSE;
+ }
+}
+
+static void
+ParseDoDependencySourcesEmpty(ParseSpecial const specType,
+ SearchPathList *const paths)
+{
+ switch (specType) {
+ case Suffixes:
+ Suff_ClearSuffixes();
+ break;
+ case Precious:
+ allPrecious = TRUE;
+ break;
+ case Ignore:
+ ignoreErrors = TRUE;
+ break;
+ case Silent:
+ beSilent = TRUE;
+ break;
+ case ExPath:
+ if (paths != NULL)
+ Lst_ForEach(paths, ParseClearPath, NULL);
+ Dir_SetPATH();
+ break;
+#ifdef POSIX
+ case Posix:
+ Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
+ break;
+#endif
+ default:
+ break;
+ }
+}
+
+/*
+ * If the target was one that doesn't take files as its sources
+ * but takes something like suffixes, we take each
+ * space-separated word on the line as a something and deal
+ * with it accordingly.
+ *
+ * If the target was .SUFFIXES, we take each source as a
+ * suffix and add it to the list of suffixes maintained by the
+ * Suff module.
+ *
+ * If the target was a .PATH, we add the source as a directory
+ * to search on the search path.
+ *
+ * If it was .INCLUDES, the source is taken to be the suffix of
+ * files which will be #included and whose search path should
+ * be present in the .INCLUDES variable.
+ *
+ * If it was .LIBS, the source is taken to be the suffix of
+ * files which are considered libraries and whose search path
+ * should be present in the .LIBS variable.
+ *
+ * If it was .NULL, the source is the suffix to use when a file
+ * has no valid suffix.
+ *
+ * If it was .OBJDIR, the source is a new definition for .OBJDIR,
+ * and will cause make to do a new chdir to that path.
+ */
+static void
+ParseDoDependencySourceSpecial(ParseSpecial const specType, char *const line,
+ SearchPathList *const paths)
+{
+ switch (specType) {
+ case Suffixes:
+ Suff_AddSuffix(line, &mainNode);
+ break;
+ case ExPath:
+ if (paths != NULL)
+ Lst_ForEach(paths, ParseAddDir, line);
+ break;
+ case Includes:
+ Suff_AddInclude(line);
+ break;
+ case Libs:
+ Suff_AddLib(line);
+ break;
+ case Null:
+ Suff_SetNull(line);
+ break;
+ case ExObjdir:
+ Main_SetObjdir("%s", line);
+ break;
+ default:
+ break;
+ }
+}
+
+static Boolean
+ParseDoDependencyTargets(char **const inout_cp,
+ char **const inout_line,
+ const char *const lstart,
+ ParseSpecial *const inout_specType,
+ GNodeType *const inout_tOp,
+ SearchPathList **const inout_paths,
+ StringList *const curTargs)
+{
+ char *cp = *inout_cp;
+ char *line = *inout_line;
+ char savec;
for (;;) {
/*
@@ -1202,8 +1503,8 @@ ParseDoDependency(char *line)
*/
if (!Arch_ParseArchive(&line, targets, VAR_CMD)) {
Parse_Error(PARSE_FATAL,
- "Error in archive specification: \"%s\"", line);
- goto out;
+ "Error in archive specification: \"%s\"", line);
+ return FALSE;
} else {
/* Done with this word; on to the next. */
cp = line;
@@ -1213,207 +1514,180 @@ ParseDoDependency(char *line)
if (!*cp) {
ParseErrorNoDependency(lstart, line);
- goto out;
+ return FALSE;
}
/* Insert a null terminator. */
savec = *cp;
*cp = '\0';
+ if (!ParseDoDependencyTarget(line, inout_specType, inout_tOp,
+ inout_paths))
+ return FALSE;
+
/*
- * Got the word. See if it's a special target and if so set
- * specType to match it.
+ * Have word in line. Get or create its node and stick it at
+ * the end of the targets list
*/
- if (*line == '.' && ch_isupper(line[1])) {
- /*
- * See if the target is a special target that must have it
- * or its sources handled specially.
- */
- int keywd = ParseFindKeyword(line);
- if (keywd != -1) {
- if (specType == ExPath && parseKeywords[keywd].spec != ExPath) {
- Parse_Error(PARSE_FATAL, "Mismatched special targets");
- goto out;
- }
+ if (*inout_specType == Not && *line != '\0') {
+ ParseDoDependencyTargetMundane(line, curTargs);
+ } else if (*inout_specType == ExPath && *line != '.' && *line != '\0') {
+ Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);
+ }
- specType = parseKeywords[keywd].spec;
- tOp = parseKeywords[keywd].op;
+ /* Don't need the inserted null terminator any more. */
+ *cp = savec;
- /*
- * Certain special targets have special semantics:
- * .PATH Have to set the dirSearchPath
- * variable too
- * .MAIN Its sources are only used if
- * nothing has been specified to
- * create.
- * .DEFAULT Need to create a node to hang
- * commands on, but we don't want
- * it in the graph, nor do we want
- * it to be the Main Target, so we
- * create it, set OP_NOTMAIN and
- * add it to the list, setting
- * DEFAULT to the new node for
- * later use. We claim the node is
- * A transformation rule to make
- * life easier later, when we'll
- * use Make_HandleUse to actually
- * apply the .DEFAULT commands.
- * .PHONY The list of targets
- * .NOPATH Don't search for file in the path
- * .STALE
- * .BEGIN
- * .END
- * .ERROR
- * .DELETE_ON_ERROR
- * .INTERRUPT Are not to be considered the
- * main target.
- * .NOTPARALLEL Make only one target at a time.
- * .SINGLESHELL Create a shell for each command.
- * .ORDER Must set initial predecessor to NULL
- */
- switch (specType) {
- case ExPath:
- if (paths == NULL) {
- paths = Lst_Init();
- }
- Lst_Append(paths, dirSearchPath);
- break;
- case Main:
- if (!Lst_IsEmpty(create)) {
- specType = Not;
- }
- break;
- case Begin:
- case End:
- case Stale:
- case dotError:
- case Interrupt: {
- GNode *gn = Targ_GetNode(line);
- if (doing_depend)
- ParseMark(gn);
- gn->type |= OP_NOTMAIN|OP_SPECIAL;
- Lst_Append(targets, gn);
- break;
- }
- case Default: {
- GNode *gn = Targ_NewGN(".DEFAULT");
- gn->type |= OP_NOTMAIN|OP_TRANSFORM;
- Lst_Append(targets, gn);
- DEFAULT = gn;
- break;
- }
- case DeleteOnError:
- deleteOnError = TRUE;
- break;
- case NotParallel:
- maxJobs = 1;
- break;
- case SingleShell:
- compatMake = TRUE;
- break;
- case Order:
- predecessor = NULL;
- break;
- default:
- break;
- }
- } else if (strncmp(line, ".PATH", 5) == 0) {
- /*
- * .PATH<suffix> has to be handled specially.
- * Call on the suffix module to give us a path to
- * modify.
- */
- SearchPath *path;
+ /*
+ * If it is a special type and not .PATH, it's the only target we
+ * allow on this line...
+ */
+ if (*inout_specType != Not && *inout_specType != ExPath) {
+ ParseDoDependencyTargetExtraWarn(&cp, lstart);
+ } else {
+ pp_skip_whitespace(&cp);
+ }
+ line = cp;
+ if (*line == '\0')
+ break;
+ if ((*line == '!' || *line == ':') && !ParseIsEscaped(lstart, line))
+ break;
+ }
- specType = ExPath;
- path = Suff_GetPath(&line[5]);
- if (path == NULL) {
- Parse_Error(PARSE_FATAL,
- "Suffix '%s' not defined (yet)",
- &line[5]);
- goto out;
- } else {
- if (paths == NULL) {
- paths = Lst_Init();
- }
- Lst_Append(paths, path);
- }
- }
+ *inout_cp = cp;
+ *inout_line = line;
+ return TRUE;
+}
+
+static void
+ParseDoDependencySourcesSpecial(char *line, char *cp,
+ ParseSpecial specType, SearchPathList *paths)
+{
+ char savec;
+
+ while (*line) {
+ while (*cp && !ch_isspace(*cp)) {
+ cp++;
+ }
+ savec = *cp;
+ *cp = '\0';
+ ParseDoDependencySourceSpecial(specType, line, paths);
+ *cp = savec;
+ if (savec != '\0') {
+ cp++;
}
+ pp_skip_whitespace(&cp);
+ line = cp;
+ }
+}
+static Boolean
+ParseDoDependencySourcesMundane(char *line, char *cp,
+ ParseSpecial specType, GNodeType tOp)
+{
+ while (*line) {
/*
- * Have word in line. Get or create its node and stick it at
- * the end of the targets list
+ * The targets take real sources, so we must beware of archive
+ * specifications (i.e. things with left parentheses in them)
+ * and handle them accordingly.
*/
- if (specType == Not && *line != '\0') {
- if (Dir_HasWildcards(line)) {
+ for (; *cp && !ch_isspace(*cp); cp++) {
+ if (*cp == '(' && cp > line && cp[-1] != '$') {
/*
- * Targets are to be sought only in the current directory,
- * so create an empty path for the thing. Note we need to
- * use Dir_Destroy in the destruction of the path as the
- * Dir module could have added a directory to the path...
+ * Only stop for a left parenthesis if it isn't at the
+ * start of a word (that'll be for variable changes
+ * later) and isn't preceded by a dollar sign (a dynamic
+ * source).
*/
- SearchPath *emptyPath = Lst_Init();
+ break;
+ }
+ }
- Dir_Expand(line, emptyPath, curTargs);
+ if (*cp == '(') {
+ GNodeList *sources = Lst_Init();
+ if (!Arch_ParseArchive(&line, sources, VAR_CMD)) {
+ Parse_Error(PARSE_FATAL,
+ "Error in source archive spec \"%s\"", line);
+ return FALSE;
+ }
- Lst_Destroy(emptyPath, Dir_Destroy);
- } else {
- /*
- * No wildcards, but we want to avoid code duplication,
- * so create a list with the word on it.
- */
- Lst_Append(curTargs, line);
+ while (!Lst_IsEmpty(sources)) {
+ GNode *gn = Lst_Dequeue(sources);
+ ParseDoSrc(tOp, gn->name, specType);
+ }
+ Lst_Free(sources);
+ cp = line;
+ } else {
+ if (*cp) {
+ *cp = '\0';
+ cp++;
}
- /* Apply the targets. */
+ ParseDoSrc(tOp, line, specType);
+ }
+ pp_skip_whitespace(&cp);
+ line = cp;
+ }
+ return TRUE;
+}
+
+/* Parse a dependency line consisting of targets, followed by a dependency
+ * operator, optionally followed by sources.
+ *
+ * The nodes of the sources are linked as children to the nodes of the
+ * targets. Nodes are created as necessary.
+ *
+ * The operator is applied to each node in the global 'targets' list,
+ * which is where the nodes found for the targets are kept, by means of
+ * the ParseDoOp function.
+ *
+ * The sources are parsed in much the same way as the targets, except
+ * that they are expanded using the wildcarding scheme of the C-Shell,
+ * and all instances of the resulting words in the list of all targets
+ * are found. Each of the resulting nodes is then linked to each of the
+ * targets as one of its children.
+ *
+ * Certain targets and sources such as .PHONY or .PRECIOUS are handled
+ * specially. These are the ones detailed by the specType variable.
+ *
+ * The storing of transformation rules such as '.c.o' is also taken care of
+ * here. A target is recognized as a transformation rule by calling
+ * Suff_IsTransform. If it is a transformation rule, its node is gotten
+ * from the suffix module via Suff_AddTransform rather than the standard
+ * Targ_FindNode in the target module.
+ */
+static void
+ParseDoDependency(char *line)
+{
+ char *cp; /* our current position */
+ GNodeType op; /* the operator on the line */
+ SearchPathList *paths; /* search paths to alter when parsing
+ * a list of .PATH targets */
+ int tOp; /* operator from special target */
+ StringList *curTargs; /* target names to be found and added
+ * to the targets list */
+ char *lstart = line;
- while(!Lst_IsEmpty(curTargs)) {
- char *targName = Lst_Dequeue(curTargs);
- GNode *gn = Suff_IsTransform(targName)
- ? Suff_AddTransform(targName)
- : Targ_GetNode(targName);
- if (doing_depend)
- ParseMark(gn);
+ /*
+ * specType contains the SPECial TYPE of the current target. It is Not
+ * if the target is unspecial. If it *is* special, however, the children
+ * are linked as children of the parent but not vice versa.
+ */
+ ParseSpecial specType = Not;
- Lst_Append(targets, gn);
- }
- } else if (specType == ExPath && *line != '.' && *line != '\0') {
- Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line);
- }
+ DEBUG1(PARSE, "ParseDoDependency(%s)\n", line);
+ tOp = 0;
- /* Don't need the inserted null terminator any more. */
- *cp = savec;
+ paths = NULL;
- /*
- * If it is a special type and not .PATH, it's the only target we
- * allow on this line...
- */
- if (specType != Not && specType != ExPath) {
- Boolean warning = FALSE;
+ curTargs = Lst_Init();
- while (*cp && (ParseIsEscaped(lstart, cp) ||
- (*cp != '!' && *cp != ':'))) {
- if (ParseIsEscaped(lstart, cp) ||
- (*cp != ' ' && *cp != '\t')) {
- warning = TRUE;
- }
- cp++;
- }
- if (warning) {
- Parse_Error(PARSE_WARNING, "Extra target ignored");
- }
- } else {
- while (*cp && ch_isspace(*cp)) {
- cp++;
- }
- }
- line = cp;
- if (*line == '\0')
- break;
- if ((*line == '!' || *line == ':') && !ParseIsEscaped(lstart, line))
- break;
- }
+ /*
+ * First, grind through the targets.
+ */
+ if (!ParseDoDependencyTargets(&cp, &line, lstart, &specType, &tOp, &paths,
+ curTargs))
+ goto out;
/*
* Don't need the list of target names anymore...
@@ -1421,50 +1695,14 @@ ParseDoDependency(char *line)
Lst_Free(curTargs);
curTargs = NULL;
- if (!Lst_IsEmpty(targets)) {
- switch(specType) {
- default:
- Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored");
- break;
- case Default:
- case Stale:
- case Begin:
- case End:
- case dotError:
- case Interrupt:
- /*
- * These four create nodes on which to hang commands, so
- * targets shouldn't be empty...
- */
- case Not:
- /*
- * Nothing special here -- targets can be empty if it wants.
- */
- break;
- }
- }
+ if (!Lst_IsEmpty(targets))
+ ParseDoDependencyCheckSpec(specType);
/*
- * Have now parsed all the target names. Must parse the operator next. The
- * result is left in op .
+ * Have now parsed all the target names. Must parse the operator next.
*/
- if (*cp == '!') {
- op = OP_FORCE;
- } else if (*cp == ':') {
- if (cp[1] == ':') {
- op = OP_DOUBLEDEP;
- cp++;
- } else {
- op = OP_DEPENDS;
- }
- } else {
- Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive"
- : "Missing dependency operator");
- goto out;
- }
-
- /* Advance beyond the operator */
- cp++;
+ if (!ParseDoDependencyParseOp(&cp, lstart, &op))
+ goto out;
/*
* Apply the operator to the target. This is how we remember which
@@ -1479,9 +1717,7 @@ ParseDoDependency(char *line)
* LINE will now point to the first source word, if any, or the
* end of the string if not.
*/
- while (*cp && ch_isspace(*cp)) {
- cp++;
- }
+ pp_skip_whitespace(&cp);
line = cp;
/*
@@ -1494,32 +1730,7 @@ ParseDoDependency(char *line)
* a .PATH removes all directories from the search path(s).
*/
if (!*line) {
- switch (specType) {
- case Suffixes:
- Suff_ClearSuffixes();
- break;
- case Precious:
- allPrecious = TRUE;
- break;
- case Ignore:
- ignoreErrors = TRUE;
- break;
- case Silent:
- beSilent = TRUE;
- break;
- case ExPath:
- if (paths != NULL)
- Lst_ForEach(paths, ParseClearPath, NULL);
- Dir_SetPATH();
- break;
-#ifdef POSIX
- case Posix:
- Var_Set("%POSIX", "1003.2", VAR_GLOBAL);
- break;
-#endif
- default:
- break;
- }
+ ParseDoDependencySourcesEmpty(specType, paths);
} else if (specType == MFlags) {
/*
* Call on functions in main.c to deal with these arguments and
@@ -1546,71 +1757,7 @@ ParseDoDependency(char *line)
specType == Includes || specType == Libs ||
specType == Null || specType == ExObjdir)
{
- while (*line) {
- /*
- * If the target was one that doesn't take files as its sources
- * but takes something like suffixes, we take each
- * space-separated word on the line as a something and deal
- * with it accordingly.
- *
- * If the target was .SUFFIXES, we take each source as a
- * suffix and add it to the list of suffixes maintained by the
- * Suff module.
- *
- * If the target was a .PATH, we add the source as a directory
- * to search on the search path.
- *
- * If it was .INCLUDES, the source is taken to be the suffix of
- * files which will be #included and whose search path should
- * be present in the .INCLUDES variable.
- *
- * If it was .LIBS, the source is taken to be the suffix of
- * files which are considered libraries and whose search path
- * should be present in the .LIBS variable.
- *
- * If it was .NULL, the source is the suffix to use when a file
- * has no valid suffix.
- *
- * If it was .OBJDIR, the source is a new definition for .OBJDIR,
- * and will cause make to do a new chdir to that path.
- */
- while (*cp && !ch_isspace(*cp)) {
- cp++;
- }
- savec = *cp;
- *cp = '\0';
- switch (specType) {
- case Suffixes:
- Suff_AddSuffix(line, &mainNode);
- break;
- case ExPath:
- if (paths != NULL)
- Lst_ForEach(paths, ParseAddDir, line);
- break;
- case Includes:
- Suff_AddInclude(line);
- break;
- case Libs:
- Suff_AddLib(line);
- break;
- case Null:
- Suff_SetNull(line);
- break;
- case ExObjdir:
- Main_SetObjdir("%s", line);
- break;
- default:
- break;
- }
- *cp = savec;
- if (savec != '\0') {
- cp++;
- }
- while (*cp && ch_isspace(*cp)) {
- cp++;
- }
- line = cp;
- }
+ ParseDoDependencySourcesSpecial(line, cp, specType, paths);
if (paths) {
Lst_Free(paths);
paths = NULL;
@@ -1619,51 +1766,8 @@ ParseDoDependency(char *line)
Dir_SetPATH();
} else {
assert(paths == NULL);
- while (*line) {
- /*
- * The targets take real sources, so we must beware of archive
- * specifications (i.e. things with left parentheses in them)
- * and handle them accordingly.
- */
- for (; *cp && !ch_isspace(*cp); cp++) {
- if (*cp == '(' && cp > line && cp[-1] != '$') {
- /*
- * Only stop for a left parenthesis if it isn't at the
- * start of a word (that'll be for variable changes
- * later) and isn't preceded by a dollar sign (a dynamic
- * source).
- */
- break;
- }
- }
-
- if (*cp == '(') {
- sources = Lst_Init();
- if (!Arch_ParseArchive(&line, sources, VAR_CMD)) {
- Parse_Error(PARSE_FATAL,
- "Error in source archive spec \"%s\"", line);
- goto out;
- }
-
- while (!Lst_IsEmpty(sources)) {
- GNode *gn = Lst_Dequeue(sources);
- ParseDoSrc(tOp, gn->name, specType);
- }
- Lst_Free(sources);
- cp = line;
- } else {
- if (*cp) {
- *cp = '\0';
- cp++;
- }
-
- ParseDoSrc(tOp, line, specType);
- }
- while (*cp && ch_isspace(*cp)) {
- cp++;
- }
- line = cp;
- }
+ if (!ParseDoDependencySourcesMundane(line, cp, specType, tOp))
+ goto out;
}
FindMainTarget();
@@ -1675,42 +1779,37 @@ out:
Lst_Free(curTargs);
}
-/*-
- *---------------------------------------------------------------------
- * Parse_IsVar --
- * Return TRUE if the passed line is a variable assignment. A variable
- * assignment consists of a single word followed by optional whitespace
- * followed by either a += or an = operator.
- * This function is used both by the Parse_File function and main when
- * parsing the command-line arguments.
- *
- * Input:
- * line the line to check
+/* Parse a variable assignment, consisting of a single-word variable name,
+ * optional whitespace, an assignment operator, optional whitespace and the
+ * variable value.
*
- * Results:
- * TRUE if it is. FALSE if it ain't
- *
- * Side Effects:
- * none
- *---------------------------------------------------------------------
- */
+ * Used for both lines in a file and command line arguments. */
Boolean
-Parse_IsVar(const char *line)
+Parse_IsVar(const char *p, VarAssign *out_var)
{
- Boolean wasSpace = FALSE; /* set TRUE if found a space */
+ const char *firstSpace = NULL;
char ch;
int level = 0;
-#define ISEQOPERATOR(c) \
- (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!'))
- /*
- * Skip to variable name
- */
- while (*line == ' ' || *line == '\t')
- line++;
+ /* Skip to variable name */
+ while (*p == ' ' || *p == '\t')
+ p++;
+
+ /* During parsing, the '+' of the '+=' operator is initially parsed
+ * as part of the variable name. It is later corrected, as is the ':sh'
+ * modifier. Of these two (nameEnd and op), the earlier one determines the
+ * actual end of the variable name. */
+ out_var->nameStart = p;
+#ifdef CLEANUP
+ out_var->nameEndDraft = NULL;
+ out_var->varname = NULL;
+ out_var->eq = NULL;
+ out_var->op = VAR_NORMAL;
+ out_var->value = NULL;
+#endif
/* Scan for one of the assignment operators outside a variable expansion */
- while ((ch = *line++) != 0) {
+ while ((ch = *p++) != 0) {
if (ch == '(' || ch == '{') {
level++;
continue;
@@ -1719,158 +1818,123 @@ Parse_IsVar(const char *line)
level--;
continue;
}
+
if (level != 0)
continue;
- while (ch == ' ' || ch == '\t') {
- ch = *line++;
- wasSpace = TRUE;
- }
+
+ if (ch == ' ' || ch == '\t')
+ if (firstSpace == NULL)
+ firstSpace = p - 1;
+ while (ch == ' ' || ch == '\t')
+ ch = *p++;
+
#ifdef SUNSHCMD
- if (ch == ':' && strncmp(line, "sh", 2) == 0) {
- line += 2;
+ if (ch == ':' && strncmp(p, "sh", 2) == 0) {
+ p += 2;
continue;
}
#endif
- if (ch == '=')
+ if (ch == '=') {
+ out_var->eq = p - 1;
+ out_var->nameEndDraft = firstSpace != NULL ? firstSpace : p - 1;
+ out_var->op = VAR_NORMAL;
+ cpp_skip_whitespace(&p);
+ out_var->value = p;
return TRUE;
- if (*line == '=' && ISEQOPERATOR(ch))
+ }
+ if (*p == '=' && (ch == '+' || ch == ':' || ch == '?' || ch == '!')) {
+ out_var->eq = p;
+ out_var->nameEndDraft = firstSpace != NULL ? firstSpace : p;
+ out_var->op = ch == '+' ? VAR_APPEND :
+ ch == ':' ? VAR_SUBST :
+ ch == '?' ? VAR_DEFAULT : VAR_SHELL;
+ p++;
+ cpp_skip_whitespace(&p);
+ out_var->value = p;
return TRUE;
- if (wasSpace)
+ }
+ if (firstSpace != NULL)
return FALSE;
}
return FALSE;
}
-/*-
- *---------------------------------------------------------------------
- * Parse_DoVar --
- * Take the variable assignment in the passed line and do it in the
- * global context.
- *
- * Note: There is a lexical ambiguity with assignment modifier characters
- * in variable names. This routine interprets the character before the =
- * as a modifier. Therefore, an assignment like
- * C++=/usr/bin/CC
- * is interpreted as "C+ +=" instead of "C++ =".
- *
- * Input:
- * line a line guaranteed to be a variable assignment.
- * This reduces error checks
- * ctxt Context in which to do the assignment
- *
- * Results:
- * none
- *
- * Side Effects:
- * the variable structure of the given variable name is altered in the
- * global context.
- *---------------------------------------------------------------------
- */
-void
-Parse_DoVar(char *line, GNode *ctxt)
+/* Determine the assignment operator and adjust the end of the variable
+ * name accordingly. */
+static void
+ParseVarassignOp(VarAssign *var)
{
- char *cp; /* pointer into line */
- enum {
- VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL
- } type; /* Type of assignment */
- char *opc; /* ptr to operator character to
- * null-terminate the variable name */
- Boolean freeCp = FALSE; /* TRUE if cp needs to be freed,
- * i.e. if any variable expansion was
- * performed */
- int depth;
-
- /*
- * Skip to variable name
- */
- while (*line == ' ' || *line == '\t')
- line++;
-
- /*
- * Skip to operator character, nulling out whitespace as we go
- * XXX Rather than counting () and {} we should look for $ and
- * then expand the variable.
- */
- for (depth = 0, cp = line; depth > 0 || *cp != '='; cp++) {
- if (*cp == '(' || *cp == '{') {
- depth++;
- continue;
- }
- if (*cp == ')' || *cp == '}') {
- depth--;
- continue;
- }
- if (depth == 0 && ch_isspace(*cp)) {
- *cp = '\0';
- }
- }
- opc = cp-1; /* operator is the previous character */
- *cp++ = '\0'; /* nuke the = */
-
- /*
- * Check operator type
- */
- switch (*opc) {
- case '+':
- type = VAR_APPEND;
- *opc = '\0';
- break;
-
- case '?':
- /*
- * If the variable already has a value, we don't do anything.
- */
- *opc = '\0';
- if (Var_Exists(line, ctxt)) {
- return;
- } else {
- type = VAR_NORMAL;
- }
- break;
-
- case ':':
- type = VAR_SUBST;
- *opc = '\0';
- break;
-
- case '!':
- type = VAR_SHELL;
- *opc = '\0';
- break;
+ const char *op = var->eq;
+ const char * const name = var->nameStart;
+ VarAssignOp type;
+
+ if (op > name && op[-1] == '+') {
+ type = VAR_APPEND;
+ op--;
+
+ } else if (op > name && op[-1] == '?') {
+ op--;
+ type = VAR_DEFAULT;
+
+ } else if (op > name && op[-1] == ':') {
+ op--;
+ type = VAR_SUBST;
+
+ } else if (op > name && op[-1] == '!') {
+ op--;
+ type = VAR_SHELL;
- default:
+ } else {
+ type = VAR_NORMAL;
#ifdef SUNSHCMD
- while (opc > line && *opc != ':')
- opc--;
+ while (op > name && ch_isspace(op[-1]))
+ op--;
- if (strncmp(opc, ":sh", 3) == 0) {
- type = VAR_SHELL;
- *opc = '\0';
- break;
- }
+ if (op >= name + 3 && op[-3] == ':' && op[-2] == 's' && op[-1] == 'h') {
+ type = VAR_SHELL;
+ op -= 3;
+ }
#endif
- type = VAR_NORMAL;
- break;
}
- while (ch_isspace(*cp))
- cp++;
+ {
+ const char *nameEnd = var->nameEndDraft < op ? var->nameEndDraft : op;
+ var->varname = bmake_strsedup(var->nameStart, nameEnd);
+ var->op = type;
+ }
+}
+static void
+VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt)
+{
if (DEBUG(LINT)) {
- if (type != VAR_SUBST && strchr(cp, '$') != NULL) {
- /* sanity check now */
+ if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
+ /* Check for syntax errors such as unclosed expressions or
+ * unknown modifiers. */
char *expandedValue;
- (void)Var_Subst(cp, ctxt, VARE_ASSIGN, &expandedValue);
+ (void)Var_Subst(uvalue, ctxt, VARE_NONE, &expandedValue);
/* TODO: handle errors */
free(expandedValue);
}
}
+}
+
+static Boolean
+VarAssign_Eval(VarAssign *var, GNode *ctxt,
+ const char **out_avalue, void **out_avalue_freeIt)
+{
+ const char *uvalue = var->value;
+ const char *name = var->varname;
+ const VarAssignOp type = var->op;
+ const char *avalue = uvalue;
+ void *avalue_freeIt = NULL;
if (type == VAR_APPEND) {
- Var_Append(line, cp, ctxt);
+ Var_Append(name, uvalue, ctxt);
} else if (type == VAR_SUBST) {
+ char *evalue;
/*
* Allow variables in the old value to be undefined, but leave their
* expressions alone -- this is done by forcing oldVars to be false.
@@ -1890,59 +1954,99 @@ Parse_DoVar(char *line, GNode *ctxt)
* make sure that we set the variable the first time to nothing
* so that it gets substituted!
*/
- if (!Var_Exists(line, ctxt))
- Var_Set(line, "", ctxt);
+ if (!Var_Exists(name, ctxt))
+ Var_Set(name, "", ctxt);
- (void)Var_Subst(cp, ctxt, VARE_WANTRES|VARE_ASSIGN, &cp);
+ (void)Var_Subst(uvalue, ctxt, VARE_WANTRES|VARE_ASSIGN, &evalue);
/* TODO: handle errors */
oldVars = oldOldVars;
- freeCp = TRUE;
+ avalue = evalue;
+ avalue_freeIt = evalue;
- Var_Set(line, cp, ctxt);
+ Var_Set(name, avalue, ctxt);
} else if (type == VAR_SHELL) {
- char *res;
- const char *error;
-
- if (strchr(cp, '$') != 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.
- */
- (void)Var_Subst(cp, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES, &cp);
+ const char *cmd, *errfmt;
+ char *cmdOut;
+ void *cmd_freeIt = NULL;
+
+ cmd = uvalue;
+ if (strchr(cmd, '$') != NULL) {
+ char *ecmd;
+ (void)Var_Subst(cmd, VAR_CMD, VARE_UNDEFERR|VARE_WANTRES, &ecmd);
/* TODO: handle errors */
- freeCp = TRUE;
+ cmd = cmd_freeIt = ecmd;
}
- res = Cmd_Exec(cp, &error);
- Var_Set(line, res, ctxt);
- free(res);
+ cmdOut = Cmd_Exec(cmd, &errfmt);
+ Var_Set(name, cmdOut, ctxt);
+ avalue = avalue_freeIt = cmdOut;
+
+ if (errfmt)
+ Parse_Error(PARSE_WARNING, errfmt, cmd);
- if (error)
- Parse_Error(PARSE_WARNING, error, cp);
+ free(cmd_freeIt);
} else {
- /*
- * Normal assignment -- just do it.
- */
- Var_Set(line, cp, ctxt);
+ if (type == VAR_DEFAULT && Var_Exists(var->varname, ctxt)) {
+ *out_avalue_freeIt = NULL;
+ return FALSE;
+ }
+
+ /* Normal assignment -- just do it. */
+ Var_Set(name, uvalue, ctxt);
}
- if (strcmp(line, MAKEOVERRIDES) == 0)
+
+ *out_avalue = avalue;
+ *out_avalue_freeIt = avalue_freeIt;
+ return TRUE;
+}
+
+static void
+VarAssignSpecial(const char *name, const char *avalue)
+{
+ if (strcmp(name, MAKEOVERRIDES) == 0)
Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */
- else if (strcmp(line, ".CURDIR") == 0) {
+ else if (strcmp(name, ".CURDIR") == 0) {
/*
* Someone is being (too?) clever...
* Let's pretend they know what they are doing and
* re-initialize the 'cur' CachedDir.
*/
- Dir_InitCur(cp);
+ Dir_InitCur(avalue);
Dir_SetPATH();
- } else if (strcmp(line, MAKE_JOB_PREFIX) == 0) {
+ } else if (strcmp(name, MAKE_JOB_PREFIX) == 0) {
Job_SetPrefix();
- } else if (strcmp(line, MAKE_EXPORTED) == 0) {
- Var_Export(cp, FALSE);
+ } else if (strcmp(name, MAKE_EXPORTED) == 0) {
+ Var_Export(avalue, FALSE);
}
- if (freeCp)
- free(cp);
+}
+
+/* Take the variable assignment in the passed line and execute it.
+ *
+ * Note: There is a lexical ambiguity with assignment modifier characters
+ * in variable names. This routine interprets the character before the =
+ * as a modifier. Therefore, an assignment like
+ * C++=/usr/bin/CC
+ * is interpreted as "C+ +=" instead of "C++ =".
+ *
+ * Input:
+ * p A line guaranteed to be a variable assignment
+ * (see Parse_IsVar).
+ * ctxt Context in which to do the assignment
+ */
+void
+Parse_DoVar(VarAssign *var, GNode *ctxt)
+{
+ const char *avalue; /* actual value (maybe expanded) */
+ void *avalue_freeIt;
+
+ ParseVarassignOp(var);
+
+ VarCheckSyntax(var->op, var->value, ctxt);
+ if (VarAssign_Eval(var, ctxt, &avalue, &avalue_freeIt))
+ VarAssignSpecial(var->varname, avalue);
+
+ free(avalue_freeIt);
+ free(var->varname);
}
@@ -2408,11 +2512,7 @@ ParseTraditionalInclude(char *line)
DEBUG2(PARSE, "%s: %s\n", __func__, file);
- /*
- * Skip over whitespace
- */
- while (ch_isspace(*file))
- file++;
+ pp_skip_whitespace(&file);
/*
* Substitute for any variables in the file name before trying to
@@ -2454,11 +2554,7 @@ ParseGmakeExport(char *line)
DEBUG2(PARSE, "%s: %s\n", __func__, variable);
- /*
- * Skip over whitespace
- */
- while (ch_isspace(*variable))
- variable++;
+ pp_skip_whitespace(&variable);
for (value = variable; *value && *value != '='; value++)
continue;
@@ -2543,7 +2639,7 @@ ParseEOF(void)
#define PARSE_SKIP 2
static char *
-ParseGetLine(int flags, int *length)
+ParseGetLine(int flags)
{
IFile *cf = curFile;
char *ptr;
@@ -2635,7 +2731,6 @@ ParseGetLine(int flags, int *length)
if (flags & PARSE_RAW) {
/* Leave '\' (etc) in line buffer (eg 'for' lines) */
- *length = line_end - line;
return line;
}
@@ -2655,10 +2750,8 @@ ParseGetLine(int flags, int *length)
}
/* If we didn't see a '\\' then the in-situ data is fine */
- if (escaped == NULL) {
- *length = line_end - line;
+ if (escaped == NULL)
return line;
- }
/* Remove escapes from '\n' and '#' */
tp = ptr = escaped;
@@ -2701,7 +2794,6 @@ ParseGetLine(int flags, int *length)
tp--;
*tp = 0;
- *length = tp - line;
return line;
}
@@ -2717,12 +2809,11 @@ static char *
ParseReadLine(void)
{
char *line; /* Result */
- int lineLength; /* Length of result */
int lineno; /* Saved line # */
int rval;
for (;;) {
- line = ParseGetLine(0, &lineLength);
+ line = ParseGetLine(0);
if (line == NULL)
return NULL;
@@ -2737,7 +2828,7 @@ ParseReadLine(void)
case COND_SKIP:
/* Skip to next conditional that evaluates to COND_PARSE. */
do {
- line = ParseGetLine(PARSE_SKIP, &lineLength);
+ line = ParseGetLine(PARSE_SKIP);
} while (line && Cond_EvalLine(line) != COND_PARSE);
if (line == NULL)
break;
@@ -2757,7 +2848,7 @@ ParseReadLine(void)
lineno = curFile->lineno;
/* Accumulate loop lines until matching .endfor */
do {
- line = ParseGetLine(PARSE_RAW, &lineLength);
+ line = ParseGetLine(PARSE_RAW);
if (line == NULL) {
Parse_Error(PARSE_FATAL,
"Unexpected end of file in for loop.");
@@ -2793,9 +2884,7 @@ FinishDependencyGroup(void)
static void
ParseLine_ShellCommand(char *cp)
{
- for (; ch_isspace(*cp); cp++)
- continue;
-
+ pp_skip_whitespace(&cp);
if (*cp == '\0')
return; /* skip empty commands */
@@ -2853,16 +2942,16 @@ Parse_File(const char *name, int fd)
* On the other hand they can be suffix rules (.c.o: ...)
* or just dependencies for filenames that start '.'.
*/
- for (cp = line + 1; ch_isspace(*cp); cp++)
- continue;
+ cp = line + 1;
+ pp_skip_whitespace(&cp);
if (IsInclude(cp, FALSE)) {
ParseDoInclude(cp);
continue;
}
if (strncmp(cp, "undef", 5) == 0) {
const char *varname;
- for (cp += 5; ch_isspace(*cp); cp++)
- continue;
+ cp += 5;
+ pp_skip_whitespace(&cp);
varname = cp;
for (; !ch_isspace(*cp) && *cp != '\0'; cp++)
continue;
@@ -2872,8 +2961,8 @@ Parse_File(const char *name, int fd)
/* TODO: use Str_Words, like everywhere else */
continue;
} else if (strncmp(cp, "export", 6) == 0) {
- for (cp += 6; ch_isspace(*cp); cp++)
- continue;
+ cp += 6;
+ pp_skip_whitespace(&cp);
Var_Export(cp, TRUE);
continue;
} else if (strncmp(cp, "unexport", 8) == 0) {
@@ -2917,10 +3006,13 @@ Parse_File(const char *name, int fd)
continue;
}
#endif
- if (Parse_IsVar(line)) {
- FinishDependencyGroup();
- Parse_DoVar(line, VAR_GLOBAL);
- continue;
+ {
+ VarAssign var;
+ if (Parse_IsVar(line, &var)) {
+ FinishDependencyGroup();
+ Parse_DoVar(&var, VAR_GLOBAL);
+ continue;
+ }
}
#ifndef POSIX
@@ -2933,8 +3025,7 @@ Parse_File(const char *name, int fd)
*/
cp = line;
if (ch_isspace(line[0])) {
- while (ch_isspace(*cp))
- cp++;
+ pp_skip_whitespace(&cp);
while (*cp && (ParseIsEscaped(line, cp) ||
*cp != ':' && *cp != '!')) {
cp++;
Index: src/usr.bin/make/str.c
diff -u src/usr.bin/make/str.c:1.67 src/usr.bin/make/str.c:1.68
--- src/usr.bin/make/str.c:1.67 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/str.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: str.c,v 1.67 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: str.c,v 1.68 2020/10/05 19:27:47 rillig Exp $ */
/*-
* Copyright (c) 1988, 1989, 1990, 1993
@@ -71,7 +71,7 @@
#include "make.h"
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
-MAKE_RCSID("$NetBSD: str.c,v 1.67 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: str.c,v 1.68 2020/10/05 19:27:47 rillig Exp $");
/* Return the concatenation of s1 and s2, freshly allocated. */
char *
@@ -269,46 +269,6 @@ done:
}
/*
- * Str_FindSubstring -- See if a string contains a particular substring.
- *
- * Input:
- * string String to search.
- * substring Substring to find in string.
- *
- * Results: If string contains substring, the return value is the location of
- * the first matching instance of substring in string. If string doesn't
- * contain substring, the return value is NULL. Matching is done on an exact
- * character-for-character basis with no wildcards or special characters.
- *
- * Side effects: None.
- */
-char *
-Str_FindSubstring(const char *string, const char *substring)
-{
- const char *a, *b;
-
- /*
- * First scan quickly through the two strings looking for a single-
- * character match. When it's found, then compare the rest of the
- * substring.
- */
-
- for (b = substring; *string != 0; string++) {
- if (*string != *b)
- continue;
- a = string;
- for (;;) {
- if (*b == 0)
- return UNCONST(string);
- if (*a++ != *b++)
- break;
- }
- b = substring;
- }
- return NULL;
-}
-
-/*
* Str_Match -- Test if a string matches a pattern like "*.[ch]".
*
* XXX this function does not detect or report malformed patterns.
Index: src/usr.bin/make/suff.c
diff -u src/usr.bin/make/suff.c:1.175 src/usr.bin/make/suff.c:1.176
--- src/usr.bin/make/suff.c:1.175 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/suff.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: suff.c,v 1.175 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: suff.c,v 1.176 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -125,11 +125,11 @@
* find the node.
*/
-#include "make.h"
-#include "dir.h"
+#include "make.h"
+#include "dir.h"
/* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */
-MAKE_RCSID("$NetBSD: suff.c,v 1.175 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: suff.c,v 1.176 2020/10/05 19:27:47 rillig Exp $");
#define SUFF_DEBUG0(text) DEBUG0(SUFF, text)
#define SUFF_DEBUG1(fmt, arg1) DEBUG1(SUFF, fmt, arg1)
Index: src/usr.bin/make/targ.c
diff -u src/usr.bin/make/targ.c:1.110 src/usr.bin/make/targ.c:1.111
--- src/usr.bin/make/targ.c:1.110 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/targ.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: targ.c,v 1.110 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: targ.c,v 1.111 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -115,14 +115,13 @@
* something for suffixes, too, but...
*/
-#include <stdio.h>
-#include <time.h>
+#include <time.h>
-#include "make.h"
-#include "dir.h"
+#include "make.h"
+#include "dir.h"
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: targ.c,v 1.110 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: targ.c,v 1.111 2020/10/05 19:27:47 rillig Exp $");
static GNodeList *allTargets; /* the list of all targets found so far */
#ifdef CLEANUP
Index: src/usr.bin/make/trace.c
diff -u src/usr.bin/make/trace.c:1.18 src/usr.bin/make/trace.c:1.19
--- src/usr.bin/make/trace.c:1.18 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/trace.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: trace.c,v 1.18 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: trace.c,v 1.19 2020/10/05 19:27:47 rillig Exp $ */
/*-
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -44,14 +44,11 @@
#include <sys/time.h>
-#include <stdio.h>
-#include <unistd.h>
-
#include "make.h"
#include "job.h"
#include "trace.h"
-MAKE_RCSID("$NetBSD: trace.c,v 1.18 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: trace.c,v 1.19 2020/10/05 19:27:47 rillig Exp $");
static FILE *trfile;
static pid_t trpid;
Index: src/usr.bin/make/util.c
diff -u src/usr.bin/make/util.c:1.62 src/usr.bin/make/util.c:1.63
--- src/usr.bin/make/util.c:1.62 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/util.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: util.c,v 1.62 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: util.c,v 1.63 2020/10/05 19:27:47 rillig Exp $ */
/*
* Missing stuff from OS's
@@ -10,13 +10,12 @@
#include <sys/param.h>
#include <errno.h>
-#include <stdio.h>
#include <time.h>
#include <signal.h>
#include "make.h"
-MAKE_RCSID("$NetBSD: util.c,v 1.62 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: util.c,v 1.63 2020/10/05 19:27:47 rillig Exp $");
#if !defined(MAKE_NATIVE) && !defined(HAVE_STRERROR)
extern int errno, sys_nerr;
Index: src/usr.bin/make/var.c
diff -u src/usr.bin/make/var.c:1.566 src/usr.bin/make/var.c:1.567
--- src/usr.bin/make/var.c:1.566 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/var.c Mon Oct 5 19:27:47 2020
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.566 2020/10/05 19:24:29 rillig Exp $ */
+/* $NetBSD: var.c,v 1.567 2020/10/05 19:27:47 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -121,7 +121,7 @@
#include "metachar.h"
/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.566 2020/10/05 19:24:29 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.567 2020/10/05 19:27:47 rillig Exp $");
#define VAR_DEBUG1(fmt, arg1) DEBUG1(VAR, fmt, arg1)
#define VAR_DEBUG2(fmt, arg1, arg2) DEBUG2(VAR, fmt, arg1, arg2)
@@ -470,6 +470,8 @@ Var_Export1(const char *name, VarExportF
if (name[0] == '.')
return FALSE; /* skip internals */
+ if (name[0] == '-')
+ return FALSE; /* skip misnamed variables */
if (name[1] == '\0') {
/*
* A single char.
@@ -601,14 +603,14 @@ Var_Export(const char *str, Boolean isEx
return;
}
- flags = 0;
- if (strncmp(str, "-env", 4) == 0) {
+ if (isExport && strncmp(str, "-env", 4) == 0) {
str += 4;
- } else if (strncmp(str, "-literal", 8) == 0) {
+ flags = 0;
+ } else if (isExport && strncmp(str, "-literal", 8) == 0) {
str += 8;
- flags |= VAR_EXPORT_LITERAL;
+ flags = VAR_EXPORT_LITERAL;
} else {
- flags |= VAR_EXPORT_PARENT;
+ flags = VAR_EXPORT_PARENT;
}
(void)Var_Subst(str, VAR_GLOBAL, VARE_WANTRES, &val);
@@ -675,8 +677,7 @@ Var_UnExport(const char *str)
if (cp && *cp)
setenv(MAKE_LEVEL_ENV, cp, 1);
} else {
- for (; ch_isspace(*str); str++)
- continue;
+ cpp_skip_whitespace(&str);
if (str[0] != '\0')
varnames = str;
}
@@ -1290,8 +1291,11 @@ ModifyWord_Subst(const char *word, SepBu
return;
}
+ if (args->lhs[0] == '\0')
+ goto nosub;
+
/* unanchored case, may match more than once */
- while ((match = Str_FindSubstring(word, args->lhs)) != NULL) {
+ while ((match = strstr(word, args->lhs)) != NULL) {
SepBuf_AddBytesBetween(buf, word, match);
SepBuf_AddBytes(buf, args->rhs, args->rhsLen);
args->matched = TRUE;
@@ -2063,6 +2067,16 @@ ApplyModifier_Defined(const char **pp, A
return AMR_OK;
}
+/* :L */
+static ApplyModifierResult
+ApplyModifier_Literal(const char **pp, ApplyModifiersState *st)
+{
+ ApplyModifiersState_Define(st);
+ st->newVal = bmake_strdup(st->v->name);
+ (*pp)++;
+ return AMR_OK;
+}
+
/* :gmtime */
static ApplyModifierResult
ApplyModifier_Gmtime(const char **pp, ApplyModifiersState *st)
@@ -2925,6 +2939,17 @@ ApplyModifier_WordFunc(const char **pp,
return AMR_OK;
}
+static ApplyModifierResult
+ApplyModifier_Unique(const char **pp, ApplyModifiersState *st)
+{
+ if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
+ st->newVal = VarUniq(st->val);
+ (*pp)++;
+ return AMR_OK;
+ } else
+ return AMR_UNKNOWN;
+}
+
#ifdef SYSVVARSUB
/* :from=to */
static ApplyModifierResult
@@ -3063,10 +3088,7 @@ ApplyModifier(const char **pp, ApplyModi
case 'U':
return ApplyModifier_Defined(pp, st);
case 'L':
- ApplyModifiersState_Define(st);
- st->newVal = bmake_strdup(st->v->name);
- (*pp)++;
- return AMR_OK;
+ return ApplyModifier_Literal(pp, st);
case 'P':
return ApplyModifier_Path(pp, st);
case '!':
@@ -3108,12 +3130,7 @@ ApplyModifier(const char **pp, ApplyModi
case 'O':
return ApplyModifier_Order(pp, st);
case 'u':
- if ((*pp)[1] == st->endc || (*pp)[1] == ':') {
- st->newVal = VarUniq(st->val);
- (*pp)++;
- return AMR_OK;
- } else
- return AMR_UNKNOWN;
+ return ApplyModifier_Unique(pp, st);
#ifdef SUNSHCMD
case 's':
return ApplyModifier_SunShell(pp, st);
@@ -3167,13 +3184,21 @@ ApplyModifiers(
/* TODO: handle errors */
/*
- * If we have not parsed up to st.endc or ':',
- * we are not interested.
+ * If we have not parsed up to st.endc or ':', we are not
+ * interested. This means the expression ${VAR:${M_1}${M_2}}
+ * is not accepted, but ${VAR:${M_1}:${M_2}} is.
*/
if (rval[0] != '\0' &&
(c = *nested_p) != '\0' && c != ':' && c != st.endc) {
+ if (DEBUG(LINT))
+ Parse_Error(PARSE_FATAL,
+ "Missing delimiter ':' after indirect modifier \"%.*s\"",
+ (int)(nested_p - p), p);
+
free(freeIt);
/* XXX: apply_mods doesn't sound like "not interested". */
+ /* XXX: Why is the indirect modifier parsed again by
+ * apply_mods? If any, p should be advanced to nested_p. */
goto apply_mods;
}
@@ -3194,6 +3219,7 @@ ApplyModifiers(
}
}
free(freeIt);
+
if (*p == ':')
p++;
else if (*p == '\0' && endc != '\0') {
@@ -3250,6 +3276,10 @@ ApplyModifiers(
st.endc, st.v->name, st.val, *mod);
} else if (*p == ':') {
p++;
+ } else if (DEBUG(LINT) && *p != '\0' && *p != endc) {
+ Parse_Error(PARSE_FATAL,
+ "Missing delimiter ':' after modifier \"%.*s\"",
+ (int)(p - mod), mod);
}
}
out:
@@ -3738,11 +3768,7 @@ Var_Subst(const char *str, GNode *ctxt,
while (*p != '\0') {
if (p[0] == '$' && p[1] == '$') {
- /*
- * A dollar sign may be escaped with another dollar sign.
- * In such a case, we skip over the escape character and store the
- * dollar sign into the buffer directly.
- */
+ /* A dollar sign may be escaped with another dollar sign. */
if (save_dollars && (eflags & VARE_ASSIGN))
Buf_AddByte(&buf, '$');
Buf_AddByte(&buf, '$');
Index: src/usr.bin/make/unit-tests/Makefile
diff -u src/usr.bin/make/unit-tests/Makefile:1.161 src/usr.bin/make/unit-tests/Makefile:1.162
--- src/usr.bin/make/unit-tests/Makefile:1.161 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/Makefile Mon Oct 5 19:27:48 2020
@@ -1,4 +1,4 @@
-# $NetBSD: Makefile,v 1.161 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: Makefile,v 1.162 2020/10/05 19:27:48 rillig Exp $
#
# Unit tests for make(1)
#
@@ -168,6 +168,7 @@ TESTS+= export-env
TESTS+= export-variants
TESTS+= forloop
TESTS+= forsubst
+TESTS+= hanoi-include
TESTS+= impsrc
TESTS+= include-main
TESTS+= job-output-long-lines
@@ -229,6 +230,7 @@ TESTS+= opt-warnings-as-errors
TESTS+= opt-where-am-i
TESTS+= opt-x-reduce-exported
TESTS+= order
+TESTS+= parse-var
TESTS+= phony-end
TESTS+= posix
TESTS+= # posix1 # broken by reverting POSIX changes
@@ -244,6 +246,10 @@ TESTS+= sh-leading-plus
TESTS+= sh-meta-chars
TESTS+= sh-multi-line
TESTS+= sh-single-line
+TESTS+= shell-csh
+TESTS+= shell-custom
+TESTS+= shell-ksh
+TESTS+= shell-sh
TESTS+= # suffixes # runs into an endless loop (try -dA)
TESTS+= suff-rebuild
TESTS+= sunshcmd
@@ -264,6 +270,7 @@ TESTS+= var-op-assign
TESTS+= var-op-default
TESTS+= var-op-expand
TESTS+= var-op-shell
+TESTS+= var-op-sunsh
TESTS+= varcmd
TESTS+= vardebug
TESTS+= varfind
@@ -403,6 +410,10 @@ SED_CMDS.opt-debug-graph1= \
SED_CMDS.opt-debug-graph1+= \
-e '/Global Variables:/,/Suffixes:/d'
SED_CMDS.sh-dots= -e 's,^.*\.\.\.:.*,<normalized: ...: not found>,'
+SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(<pid>),'
+SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid <pid>,'
+SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process <pid>,'
+SED_CMDS.opt-debug-jobs+= -e 's,JobFinish: [0-9][0-9]*,JobFinish: <pid>,'
SED_CMDS.varmod-subst-regex+= \
-e 's,\(Regex compilation error:\).*,\1 (details omitted),'
SED_CMDS.varmod-edge+= -e 's, line [0-9]*:, line omitted:,'
Index: src/usr.bin/make/unit-tests/directive-export-literal.exp
diff -u src/usr.bin/make/unit-tests/directive-export-literal.exp:1.3 src/usr.bin/make/unit-tests/directive-export-literal.exp:1.4
--- src/usr.bin/make/unit-tests/directive-export-literal.exp:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/directive-export-literal.exp Mon Oct 5 19:27:48 2020
@@ -1 +1,2 @@
+value with ${UNEXPANDED} expression
exit status 0
Index: src/usr.bin/make/unit-tests/directive-ifndef.exp
diff -u src/usr.bin/make/unit-tests/directive-ifndef.exp:1.3 src/usr.bin/make/unit-tests/directive-ifndef.exp:1.4
--- src/usr.bin/make/unit-tests/directive-ifndef.exp:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/directive-ifndef.exp Mon Oct 5 19:27:48 2020
@@ -1 +1,2 @@
+make: "directive-ifndef.mk" line 10: guarded section
exit status 0
Index: src/usr.bin/make/unit-tests/directive-ifnmake.exp
diff -u src/usr.bin/make/unit-tests/directive-ifnmake.exp:1.3 src/usr.bin/make/unit-tests/directive-ifnmake.exp:1.4
--- src/usr.bin/make/unit-tests/directive-ifnmake.exp:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/directive-ifnmake.exp Mon Oct 5 19:27:48 2020
@@ -1 +1,3 @@
+Don't forget to run the tests (1)
+Running the tests
exit status 0
Index: src/usr.bin/make/unit-tests/opt-debug-file.mk
diff -u src/usr.bin/make/unit-tests/opt-debug-file.mk:1.3 src/usr.bin/make/unit-tests/opt-debug-file.mk:1.4
--- src/usr.bin/make/unit-tests/opt-debug-file.mk:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-file.mk Mon Oct 5 19:27:48 2020
@@ -1,9 +1,37 @@
-# $NetBSD: opt-debug-file.mk,v 1.3 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: opt-debug-file.mk,v 1.4 2020/10/05 19:27:48 rillig Exp $
#
# Tests for the -dF command line option, which redirects the debug log
# to a file instead of writing it to stderr.
-# TODO: Implementation
+# Enable debug logging for variable assignments and evaluation (-dv)
+# and redirect the debug logging to the given file.
+.MAKEFLAGS: -dvFopt-debug-file.debuglog
+
+# This output goes to the debug log file.
+VAR= value ${:Uexpanded}
+
+# Hide the logging output for the remaining actions.
+# As of 2020-10-03, it is not possible to disable debug logging again.
+.MAKEFLAGS: -dF/dev/null
+
+# Make sure that the debug logging file contains some logging.
+DEBUG_OUTPUT:= ${:!cat opt-debug-file.debuglog!}
+# Grmbl. Because of the := operator in the above line, the variable
+# value contains ${:Uexpanded}. This variable expression is expanded
+# upon further processing. Therefore, don't read from untrusted input.
+#.MAKEFLAGS: -dc -dFstderr
+.if !${DEBUG_OUTPUT:tW:M*VAR = value expanded*}
+. error ${DEBUG_OUTPUT}
+.endif
+
+# To get the unexpanded text that was actually written to the debug log
+# file, the content of that log file must not be stored in a variable.
+# XXX: In the :M modifier, a dollar is escaped as '$$', not '\$'.
+.if !${:!cat opt-debug-file.debuglog!:tW:M*VAR = value $${:Uexpanded}*}
+. error
+.endif
+
+_!= rm opt-debug-file.debuglog
all:
@:;
Index: src/usr.bin/make/unit-tests/opt-debug-for.exp
diff -u src/usr.bin/make/unit-tests/opt-debug-for.exp:1.3 src/usr.bin/make/unit-tests/opt-debug-for.exp:1.4
--- src/usr.bin/make/unit-tests/opt-debug-for.exp:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-for.exp Mon Oct 5 19:27:48 2020
@@ -1 +1,22 @@
+For: new loop 2
+For: end for 2
+For: end for 1
+For: loop body:
+. for inner in 1 2
+VAR.${:Ua}${inner}= value
+. endfor
+For: end for 1
+For: loop body:
+VAR.${:Ua}${:U1}= value
+For: loop body:
+VAR.${:Ua}${:U2}= value
+For: loop body:
+. for inner in 1 2
+VAR.${:Ub}${inner}= value
+. endfor
+For: end for 1
+For: loop body:
+VAR.${:Ub}${:U1}= value
+For: loop body:
+VAR.${:Ub}${:U2}= value
exit status 0
Index: src/usr.bin/make/unit-tests/opt-debug-for.mk
diff -u src/usr.bin/make/unit-tests/opt-debug-for.mk:1.3 src/usr.bin/make/unit-tests/opt-debug-for.mk:1.4
--- src/usr.bin/make/unit-tests/opt-debug-for.mk:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-for.mk Mon Oct 5 19:27:48 2020
@@ -1,9 +1,26 @@
-# $NetBSD: opt-debug-for.mk,v 1.3 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: opt-debug-for.mk,v 1.4 2020/10/05 19:27:48 rillig Exp $
#
# Tests for the -df command line option, which adds debug logging for
# parsing and evaluating .for loops.
-# TODO: Implementation
+.MAKEFLAGS: -df
+
+# XXX: In the debug log, the "new loop 2" appears out of context.
+# There should be a "begin loop 1" before, and all these messages should
+# contain line number information.
+#
+# XXX: The "loop body" should print the nesting level as well.
+#
+# XXX: It is hard to extract any information from the debug log since
+# the "begin" and "end" events are not balanced and the nesting level
+# is not printed consistently. It would also be helpful to mention the
+# actual substitutions, such as "For 1: outer=b".
+#
+.for outer in a b
+. for inner in 1 2
+VAR.${outer}${inner}= value
+. endfor
+.endfor
all:
@:;
Index: src/usr.bin/make/unit-tests/opt-debug-jobs.exp
diff -u src/usr.bin/make/unit-tests/opt-debug-jobs.exp:1.3 src/usr.bin/make/unit-tests/opt-debug-jobs.exp:1.4
--- src/usr.bin/make/unit-tests/opt-debug-jobs.exp:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-jobs.exp Mon Oct 5 19:27:48 2020
@@ -1 +1,25 @@
+job_pipe -1 -1, maxjobs 1, tokens 1, compat 0
+Job_TokenWithdraw(<pid>): aborting 0, running 0
+(<pid>) withdrew token
+echo ": expanded expression"
+{ : expanded expression
+} || exit $?
+echo ": variable"
+{ : variable
+} || exit $?
+echo ": 'single' and \"double\" quotes"
+{ : 'single' and "double" quotes
+} || exit $?
+Running all locally
+ Command: sh -q
+JobExec(all): pid <pid> added to jobs table
+job table @ job started
+job 0, status 3, flags 0, pid <pid>
+: expanded expression
+: variable
+: 'single' and "double" quotes
+Process <pid> exited/stopped status 0.
+JobFinish: <pid> [all], status 0
+Job_TokenWithdraw(<pid>): aborting 0, running 0
+(<pid>) withdrew token
exit status 0
Index: src/usr.bin/make/unit-tests/opt-debug-jobs.mk
diff -u src/usr.bin/make/unit-tests/opt-debug-jobs.mk:1.3 src/usr.bin/make/unit-tests/opt-debug-jobs.mk:1.4
--- src/usr.bin/make/unit-tests/opt-debug-jobs.mk:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-jobs.mk Mon Oct 5 19:27:48 2020
@@ -1,9 +1,26 @@
-# $NetBSD: opt-debug-jobs.mk,v 1.3 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: opt-debug-jobs.mk,v 1.4 2020/10/05 19:27:48 rillig Exp $
#
-# Tests for the -da command line option, which adds debug logging about
+# Tests for the -dj command line option, which adds debug logging about
# running jobs in multiple shells.
-# TODO: Implementation
+.MAKEFLAGS: -dj
+
+# Run in parallel mode since the debug logging is more interesting there
+# than in compat mode.
+.MAKEFLAGS: -j1
all:
- @:;
+ # Only the actual command is logged.
+ # To see the evaluation of the variable expressions, use -dv.
+ : ${:Uexpanded} expression
+
+ # Undefined variables expand to empty strings.
+ # Multiple spaces are preserved in the command, as they might be
+ # significant.
+ : ${UNDEF} variable
+
+ # In the debug output, single quotes are not escaped, even though
+ # the whole command is enclosed in single quotes as well.
+ # This allows to copy and paste the whole command, without having
+ # to unescape anything.
+ : 'single' and "double" quotes
Index: src/usr.bin/make/unit-tests/opt-debug-loud.exp
diff -u src/usr.bin/make/unit-tests/opt-debug-loud.exp:1.3 src/usr.bin/make/unit-tests/opt-debug-loud.exp:1.4
--- src/usr.bin/make/unit-tests/opt-debug-loud.exp:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-loud.exp Mon Oct 5 19:27:48 2020
@@ -1 +1,3 @@
+echo all-word
+all-word
exit status 0
Index: src/usr.bin/make/unit-tests/opt-debug-loud.mk
diff -u src/usr.bin/make/unit-tests/opt-debug-loud.mk:1.3 src/usr.bin/make/unit-tests/opt-debug-loud.mk:1.4
--- src/usr.bin/make/unit-tests/opt-debug-loud.mk:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-loud.mk Mon Oct 5 19:27:48 2020
@@ -1,13 +1,22 @@
-# $NetBSD: opt-debug-loud.mk,v 1.3 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: opt-debug-loud.mk,v 1.4 2020/10/05 19:27:48 rillig Exp $
#
# Tests for the -dl command line option, which prints the commands before
# running them, ignoring the command line option for silent mode (-s) as
# well as the .SILENT special source and target, as well as the '@' prefix
# for shell commands.
-# TODO: Implementation
+.MAKEFLAGS: -dl -s
+.SILENT:
-# TODO: What about ${:!cmd!}?
+# The -dl command line option does not affect commands that are run during
+# variable expansion, such as :!cmd! or :sh.
+.if ${:!echo word!} != "word"
+. error
+.endif
-all:
- @:;
+all: .SILENT
+ # Even though the command line option -s is given, .SILENT is set
+ # for all targets and for this target in particular, the command
+ # is still printed. The -dl debugging option is stronger than all
+ # of these.
+ @echo all-word
Index: src/usr.bin/make/unit-tests/opt-debug.exp
diff -u src/usr.bin/make/unit-tests/opt-debug.exp:1.3 src/usr.bin/make/unit-tests/opt-debug.exp:1.4
--- src/usr.bin/make/unit-tests/opt-debug.exp:1.3 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug.exp Mon Oct 5 19:27:48 2020
@@ -1 +1,4 @@
+Global:VAR = value
+Global:.MAKEFLAGS = -r -k -d v -d
+Global:.MAKEFLAGS = -r -k -d v -d 0
exit status 0
Index: src/usr.bin/make/unit-tests/directive-export-literal.mk
diff -u src/usr.bin/make/unit-tests/directive-export-literal.mk:1.4 src/usr.bin/make/unit-tests/directive-export-literal.mk:1.5
--- src/usr.bin/make/unit-tests/directive-export-literal.mk:1.4 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/directive-export-literal.mk Mon Oct 5 19:27:48 2020
@@ -1,8 +1,11 @@
-# $NetBSD: directive-export-literal.mk,v 1.4 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: directive-export-literal.mk,v 1.5 2020/10/05 19:27:48 rillig Exp $
#
-# Tests for the .export-literal directive.
+# Tests for the .export-literal directive, which exports a variable value
+# without expanding it.
-# TODO: Implementation
+UT_VAR= value with ${UNEXPANDED} expression
+
+.export-literal UT_VAR
all:
- @:;
+ @echo "$$UT_VAR"
Index: src/usr.bin/make/unit-tests/directive-ifndef.mk
diff -u src/usr.bin/make/unit-tests/directive-ifndef.mk:1.4 src/usr.bin/make/unit-tests/directive-ifndef.mk:1.5
--- src/usr.bin/make/unit-tests/directive-ifndef.mk:1.4 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/directive-ifndef.mk Mon Oct 5 19:27:48 2020
@@ -1,8 +1,24 @@
-# $NetBSD: directive-ifndef.mk,v 1.4 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: directive-ifndef.mk,v 1.5 2020/10/05 19:27:48 rillig Exp $
#
-# Tests for the .ifndef directive.
+# Tests for the .ifndef directive, which can be used for multiple-inclusion
+# guards. In contrast to C, where #ifndef and #define nicely line up the
+# macro name, there is no such syntax in make. Therefore, it is more
+# common to use .if !defined(GUARD) instead.
-# TODO: Implementation
+.ifndef GUARD
+GUARD= # defined
+.info guarded section
+.endif
+
+.ifndef GUARD
+GUARD= # defined
+.info guarded section
+.endif
+
+.if !defined(GUARD)
+GUARD= # defined
+.info guarded section
+.endif
all:
@:;
Index: src/usr.bin/make/unit-tests/directive-ifnmake.mk
diff -u src/usr.bin/make/unit-tests/directive-ifnmake.mk:1.4 src/usr.bin/make/unit-tests/directive-ifnmake.mk:1.5
--- src/usr.bin/make/unit-tests/directive-ifnmake.mk:1.4 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/directive-ifnmake.mk Mon Oct 5 19:27:48 2020
@@ -1,8 +1,22 @@
-# $NetBSD: directive-ifnmake.mk,v 1.4 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: directive-ifnmake.mk,v 1.5 2020/10/05 19:27:48 rillig Exp $
#
-# Tests for the .ifnmake directive.
-
-# TODO: Implementation
+# Tests for the .ifnmake directive, which evaluates to true if its argument
+# is _not_ listed in the command-line targets to be created.
all:
@:;
+
+.ifnmake(test)
+.BEGIN:
+ @echo "Don't forget to run the tests (1)"
+.endif
+
+.MAKEFLAGS: test
+
+.ifnmake(test)
+.BEGIN:
+ @echo "Don't forget to run the tests (2)"
+.endif
+
+test:
+ @echo "Running the tests"
Index: src/usr.bin/make/unit-tests/make-exported.exp
diff -u src/usr.bin/make/unit-tests/make-exported.exp:1.4 src/usr.bin/make/unit-tests/make-exported.exp:1.5
--- src/usr.bin/make/unit-tests/make-exported.exp:1.4 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/make-exported.exp Mon Oct 5 19:27:48 2020
@@ -1,3 +1,2 @@
--literal=make-exported-value
UT_VAR=
exit status 0
Index: src/usr.bin/make/unit-tests/opt-debug.mk
diff -u src/usr.bin/make/unit-tests/opt-debug.mk:1.4 src/usr.bin/make/unit-tests/opt-debug.mk:1.5
--- src/usr.bin/make/unit-tests/opt-debug.mk:1.4 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug.mk Mon Oct 5 19:27:48 2020
@@ -1,8 +1,14 @@
-# $NetBSD: opt-debug.mk,v 1.4 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: opt-debug.mk,v 1.5 2020/10/05 19:27:48 rillig Exp $
#
-# Tests for the -d command line option.
+# Tests for the -d command line option, which controls debug logging.
-# TODO: Implementation
+# Enable debug logging for the variables (var.c).
+.MAKEFLAGS: -dv
+
+VAR= value
+
+# Disable all debug logging again.
+.MAKEFLAGS: -d0 # -d0 is available since 2020-10-03
all:
@:;
Index: src/usr.bin/make/unit-tests/var-op-append.mk
diff -u src/usr.bin/make/unit-tests/var-op-append.mk:1.4 src/usr.bin/make/unit-tests/var-op-append.mk:1.5
--- src/usr.bin/make/unit-tests/var-op-append.mk:1.4 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/var-op-append.mk Mon Oct 5 19:27:48 2020
@@ -1,9 +1,36 @@
-# $NetBSD: var-op-append.mk,v 1.4 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: var-op-append.mk,v 1.5 2020/10/05 19:27:48 rillig Exp $
#
# Tests for the += variable assignment operator, which appends to a variable,
# creating it if necessary.
-# TODO: Implementation
+# Appending to an undefined variable is possible.
+# The variable is created, and no extra space is added before the value.
+VAR+= one
+.if ${VAR} != "one"
+. error
+.endif
+
+# Appending to an existing variable adds a single space and the value.
+VAR+= two
+.if ${VAR} != "one two"
+. error
+.endif
+
+# Appending an empty string nevertheless adds a single space.
+VAR+= # empty
+.if ${VAR} != "one two "
+. error
+.endif
+
+# Variable names may contain '+', and this character is also part of the
+# '+=' assignment operator. As far as possible, the '+' is interpreted as
+# part of the assignment operator.
+#
+# See Parse_DoVar
+C++=value
+.if ${C+} != "value" || defined(C++)
+. error
+.endif
all:
@:;
Index: src/usr.bin/make/unit-tests/make-exported.mk
diff -u src/usr.bin/make/unit-tests/make-exported.mk:1.5 src/usr.bin/make/unit-tests/make-exported.mk:1.6
--- src/usr.bin/make/unit-tests/make-exported.mk:1.5 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/make-exported.mk Mon Oct 5 19:27:48 2020
@@ -1,16 +1,25 @@
-# $NetBSD: make-exported.mk,v 1.5 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: make-exported.mk,v 1.6 2020/10/05 19:27:48 rillig Exp $
#
# As of 2020-08-09, the code in Var_Export is shared between the .export
# directive and the .MAKE.EXPORTED variable. This leads to non-obvious
# behavior for certain variable assignments.
--env= make-exported-value
--literal= make-exported-value
+-env= make-exported-value-env
+-literal= make-exported-value-literal
UT_VAR= ${UNEXPANDED}
-# The following behavior is probably not intended.
-.MAKE.EXPORTED= -env # like .export-env
-.MAKE.EXPORTED= -literal UT_VAR # like .export-literal PATH
+# Before 2020-10-03, the following line took the code path of .export-env,
+# which was surprising behavior. Since 2020-10-03 this line tries to
+# export the variable named "-env", but that is rejected because the
+# variable name starts with a hyphen.
+.MAKE.EXPORTED= -env
+
+# Before 2020-10-03, if the value of .MAKE.EXPORTED started with "-literal",
+# make behaved like a mixture of .export-literal and a regular .export.
+#
+# Since 2020-10-03, the "variable" named "-literal" is not exported anymore,
+# it is just ignored since its name starts with '-'.
+.MAKE.EXPORTED= -literal UT_VAR
all:
@env | sort | grep -E '^UT_|make-exported-value' || true
Index: src/usr.bin/make/unit-tests/varname-dot-curdir.mk
diff -u src/usr.bin/make/unit-tests/varname-dot-curdir.mk:1.5 src/usr.bin/make/unit-tests/varname-dot-curdir.mk:1.6
--- src/usr.bin/make/unit-tests/varname-dot-curdir.mk:1.5 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/varname-dot-curdir.mk Mon Oct 5 19:27:48 2020
@@ -1,8 +1,27 @@
-# $NetBSD: varname-dot-curdir.mk,v 1.5 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: varname-dot-curdir.mk,v 1.6 2020/10/05 19:27:48 rillig Exp $
#
# Tests for the special .CURDIR variable.
# TODO: Implementation
+# Until 2020-10-04, assigning the result of a shell assignment to .CURDIR
+# tried to add the shell command ("echo /") to the .PATH instead of the
+# output of the shell command ("/"). Since "echo /" does not exist, the
+# .PATH was left unmodified. See VarAssign_Eval.
+#
+# Since 2020-10-04, the output of the shell command is added to .PATH.
+.CURDIR!= echo /
+.if ${.PATH:M/} != "/"
+. error
+.endif
+
+# A normal assignment works fine, as does a substitution assignment.
+# Appending to .CURDIR does not make sense, therefore it doesn't matter that
+# this code path is buggy as well.
+.CURDIR= /
+.if ${.PATH:M/} != "/"
+. error
+.endif
+
all:
@:;
Index: src/usr.bin/make/unit-tests/opt-debug-lint.exp
diff -u src/usr.bin/make/unit-tests/opt-debug-lint.exp:1.10 src/usr.bin/make/unit-tests/opt-debug-lint.exp:1.11
--- src/usr.bin/make/unit-tests/opt-debug-lint.exp:1.10 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-lint.exp Mon Oct 5 19:27:48 2020
@@ -1,5 +1,9 @@
make: "opt-debug-lint.mk" line 19: Variable "X" is undefined
make: "opt-debug-lint.mk" line 41: Variable "UNDEF" is undefined
+make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "L"
+make: "opt-debug-lint.mk" line 61: Missing delimiter ':' after modifier "P"
+make: "opt-debug-lint.mk" line 67: Missing delimiter ':' after indirect modifier "${:UL}"
+make: Unknown modifier '$'
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
Index: src/usr.bin/make/unit-tests/opt-debug-lint.mk
diff -u src/usr.bin/make/unit-tests/opt-debug-lint.mk:1.9 src/usr.bin/make/unit-tests/opt-debug-lint.mk:1.10
--- src/usr.bin/make/unit-tests/opt-debug-lint.mk:1.9 Mon Oct 5 19:24:29 2020
+++ src/usr.bin/make/unit-tests/opt-debug-lint.mk Mon Oct 5 19:27:48 2020
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-lint.mk,v 1.9 2020/10/05 19:24:29 rillig Exp $
+# $NetBSD: opt-debug-lint.mk,v 1.10 2020/10/05 19:27:48 rillig Exp $
#
# Tests for the -dL command line option, which runs additional checks
# to catch common mistakes, such as unclosed variable expressions.
@@ -56,5 +56,17 @@ ${UNDEF}: ${UNDEF}
. error
.endif
+# Since 2020-10-03, in lint mode the variable modifier must be separated
+# by colons. See varparse-mod.mk.
+.if ${value:LPL} != "value"
+. error
+.endif
+
+# Since 2020-10-03, in lint mode the variable modifier must be separated
+# by colons. See varparse-mod.mk.
+.if ${value:${:UL}PL} != "LPL}" # FIXME: "LPL}" is unexpected here.
+. error ${value:${:UL}PL}
+.endif
+
all:
@:;