From 8b6f73e37baf5c37946844ec335a84856b1a9033 Mon Sep 17 00:00:00 2001 From: "Simon J. Gerraty" Date: Wed, 10 Feb 2021 17:51:11 -0800 Subject: [PATCH] Import bmake-20210206 Changes of interest o unit-tests: use private TMPDIR to avoid errors from other users o avoid strdup in mkTempFile o always use vfork o job.c: do not create empty shell files in jobs mode reduce unnecessary calls to waitpid o cond.c: fix debug output for comparison operators in conditionals --- ChangeLog | 73 ++ FILES | 6 + VERSION | 2 +- arch.c | 24 +- bmake.1 | 6 +- bmake.cat1 | 4 +- buf.c | 63 +- buf.h | 25 +- compat.c | 18 +- cond.c | 645 ++++++++-------- config.h.in | 3 + configure | 36 +- configure.in | 15 +- dir.c | 454 ++++++----- dir.h | 10 +- enum.c | 16 +- enum.h | 33 +- filemon/filemon.h | 4 +- filemon/filemon_dev.c | 6 +- filemon/filemon_ktrace.c | 13 +- for.c | 253 +++--- hash.c | 13 +- job.c | 260 +++---- job.h | 3 +- lst.c | 17 +- lst.h | 4 +- main.c | 316 ++++---- make.1 | 6 +- make.c | 154 ++-- make.h | 104 +-- make_malloc.c | 6 +- make_malloc.h | 4 +- meta.c | 87 ++- metachar.c | 7 +- metachar.h | 6 +- mk/ChangeLog | 9 + mk/dirdeps.mk | 10 +- mk/install-mk | 4 +- mk/meta.stage.mk | 7 +- nonints.h | 58 +- parse.c | 306 ++++---- str.c | 22 +- suff.c | 66 +- targ.c | 30 +- trace.c | 10 +- trace.h | 6 +- unit-tests/Makefile | 80 +- unit-tests/cmdline-redirect-stdin.exp | 1 + unit-tests/cmdline-redirect-stdin.mk | 34 + unit-tests/cmdline.exp | 4 +- unit-tests/cmdline.mk | 4 +- unit-tests/cond-cmp-numeric-eq.exp | 1 - unit-tests/cond-cmp-numeric.exp | 8 +- unit-tests/cond-cmp-string.exp | 5 +- unit-tests/cond-cmp-string.mk | 30 +- unit-tests/cond-func-defined.exp | 6 +- unit-tests/cond-func.exp | 9 +- unit-tests/cond-op-not.exp | 5 +- unit-tests/cond-op-not.mk | 9 +- unit-tests/cond-op-parentheses.exp | 6 +- unit-tests/cond-op-parentheses.mk | 19 +- unit-tests/cond-op.exp | 4 + unit-tests/cond-op.mk | 40 +- unit-tests/cond-token-plain.exp | 31 +- unit-tests/cond-token-plain.mk | 97 ++- unit-tests/cond-token-string.exp | 20 +- unit-tests/cond-token-string.mk | 47 +- unit-tests/cond1.exp | 2 +- unit-tests/dir.mk | 4 +- unit-tests/directive-error.exp | 5 +- unit-tests/directive-error.mk | 13 +- unit-tests/directive-export-impl.exp | 8 +- unit-tests/directive-for-escape.exp | 43 +- unit-tests/directive-for-escape.mk | 28 +- unit-tests/directive-ifdef.exp | 2 + unit-tests/directive-ifdef.mk | 17 +- unit-tests/directive-include-fatal.mk | 4 +- unit-tests/envfirst.mk | 4 +- unit-tests/export.exp | 1 + unit-tests/gnode-submake.exp | 16 +- unit-tests/include-main.exp | 9 +- unit-tests/include-main.mk | 6 +- unit-tests/include-subsub.mk | 4 +- unit-tests/jobs-empty-commands.exp | 2 + unit-tests/jobs-empty-commands.mk | 18 + unit-tests/lint.mk | 13 +- unit-tests/objdir-writable.exp | 6 +- unit-tests/opt-debug-graph1.exp | 16 +- unit-tests/opt-debug-graph2.exp | 92 ++- unit-tests/opt-debug-graph2.mk | 22 +- unit-tests/opt-debug-graph3.exp | 92 ++- unit-tests/opt-debug-graph3.mk | 22 +- unit-tests/opt-no-action-touch.exp | 11 + unit-tests/opt-no-action-touch.mk | 48 ++ unit-tests/opt-touch-jobs.mk | 8 +- unit-tests/opt-warnings-as-errors.exp | 4 +- unit-tests/opt-warnings-as-errors.mk | 7 +- unit-tests/suff-incomplete.exp | 4 +- unit-tests/suff-main-several.exp | 16 +- unit-tests/suff-transform-debug.exp | 2 +- unit-tests/var-op-append.exp | 6 +- unit-tests/var-op-append.mk | 14 +- unit-tests/var-op-assign.exp | 2 +- unit-tests/var-op-shell.exp | 4 +- unit-tests/var-op-shell.mk | 13 +- unit-tests/vardebug.exp | 36 +- unit-tests/vardebug.mk | 4 +- unit-tests/varmisc.mk | 10 +- unit-tests/varmod-assign.mk | 3 +- unit-tests/varmod-gmtime.exp | 20 +- unit-tests/varmod-gmtime.mk | 19 +- unit-tests/varmod-ifelse.mk | 4 +- unit-tests/varmod-indirect.exp | 32 +- unit-tests/varmod-localtime.exp | 20 +- unit-tests/varmod-localtime.mk | 21 +- unit-tests/varmod-loop.mk | 6 +- unit-tests/varmod-match-escape.exp | 16 +- unit-tests/varmod-match-escape.mk | 20 +- unit-tests/varname-dot-shell.mk | 6 +- unit-tests/varname-empty.exp | 38 +- unit-tests/varname-empty.mk | 9 +- .../varname-make_print_var_on_error-jobs.mk | 4 +- unit-tests/varname-make_print_var_on_error.mk | 4 +- unit-tests/varname.exp | 10 +- unit-tests/varparse-dynamic.mk | 6 +- util.c | 10 +- var.c | 731 ++++++++++-------- 127 files changed, 3162 insertions(+), 2139 deletions(-) create mode 100644 unit-tests/cmdline-redirect-stdin.exp create mode 100644 unit-tests/cmdline-redirect-stdin.mk create mode 100644 unit-tests/jobs-empty-commands.exp create mode 100644 unit-tests/jobs-empty-commands.mk create mode 100644 unit-tests/opt-no-action-touch.exp create mode 100644 unit-tests/opt-no-action-touch.mk diff --git a/ChangeLog b/ChangeLog index 5cf7f1f45384..5cf6f9d8fe57 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,76 @@ +2021-02-06 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210206 + Merge with NetBSD make, pick up + o unit-tests: use private TMPDIR to avoid errors from other users + +2021-02-05 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210205 + Merge with NetBSD make, pick up + o avoid strdup in mkTempFile + o always use vfork + o rename context and ctxt to scope + o rename some VAR constants to SCOPE + o Var_ functions, move the scope to the front + o use shortcut functions Global_Set and Global_Append + o add shortcut Global_Delete for deleting a global variable + o rename Var_Delete to Var_DeleteExpand, Var_DeleteVar to Var_Delete + o compat.c: when exiting due to an error, print graph information + o enum.c: remove overengineered Enum_ValueToString + o make.c: remove unused INTERNAL flag + remove unused return type of MakeBuildParent + o parse.c: replace parse error "Need an operator" with better message + o var.c: improve documentation about variable scopes + rename Var_ValueDirect to GNode_ValueDirect + rename old Var_SetWithFlags to Var_SetExpandWithFlags + merge SetVar into Var_SetWithFlags + split Var_Exists into plain Var_Exists and Var_ExistsExpand + split Var_Append into Var_Append and Var_AppendExpand + replace enum bit-set with bit-field + o unit-tests/var-op-shell: use kill rather than kill -14 + which broke on darwin with recent update. + +2021-02-01 Simon J Gerraty + + * configure.in: check for sig_atomic_t and define it as 'int' + if missing. + + * VERSION (_MAKE_VERSION): 20210201 + Merge with NetBSD make, pick up + o use sig_atomic_t for caught_sigchld + +2021-01-30 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210130 + Merge with NetBSD make, pick up + o more unit tests + o convert SearchPath to struct + o split Buf_Destroy into Buf_Done and Buf_DoneData + o for.c: split For_Eval into separate functions + rename struct For to struct ForLoop + o job.c: do not create empty shell files in jobs mode + rename JobOpenTmpFile to JobWriteShellCommands + reduce unnecessary calls to waitpid + o parse.c: in -dp mode, print stack trace with each diagnostic + +2021-01-23 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210123 + Merge with NetBSD make, pick up + o rename Dir_Expand to SearchPath_Expand + o rename Dir_AddDir, reorder parameters of SearchPath_ToFlags + o cond.c: fix debug output for comparison operators in conditionals + o dir.c: split Dir_FindFile into separate functions + +2021-01-20 Simon J Gerraty + + * VERSION (_MAKE_VERSION): 20210120 + Merge with NetBSD make, pick up + o fix some more lint nits + o refine some unit tests for portability + o cond.c: rework parsing + 2021-01-10 Simon J Gerraty * VERSION (_MAKE_VERSION): 20210110 diff --git a/FILES b/FILES index 8e2513e1c183..5e7b8301da87 100644 --- a/FILES +++ b/FILES @@ -83,6 +83,8 @@ unit-tests/cmd-errors.exp unit-tests/cmd-errors.mk unit-tests/cmd-interrupt.exp unit-tests/cmd-interrupt.mk +unit-tests/cmdline-redirect-stdin.exp +unit-tests/cmdline-redirect-stdin.mk unit-tests/cmdline-undefined.exp unit-tests/cmdline-undefined.mk unit-tests/cmdline.exp @@ -397,6 +399,8 @@ unit-tests/job-flags.exp unit-tests/job-flags.mk unit-tests/job-output-long-lines.exp unit-tests/job-output-long-lines.mk +unit-tests/jobs-empty-commands.exp +unit-tests/jobs-empty-commands.mk unit-tests/jobs-error-indirect.exp unit-tests/jobs-error-indirect.mk unit-tests/jobs-error-nested-make.exp @@ -501,6 +505,8 @@ unit-tests/opt-no-action-at-all.exp unit-tests/opt-no-action-at-all.mk unit-tests/opt-no-action-runflags.exp unit-tests/opt-no-action-runflags.mk +unit-tests/opt-no-action-touch.exp +unit-tests/opt-no-action-touch.mk unit-tests/opt-no-action.exp unit-tests/opt-no-action.mk unit-tests/opt-query.exp diff --git a/VERSION b/VERSION index 70e0f68a60b0..0af794962680 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ # keep this compatible with sh and make -_MAKE_VERSION=20210110 +_MAKE_VERSION=20210206 diff --git a/arch.c b/arch.c index 037f6bc548cf..e5c0a5e4ac0f 100644 --- a/arch.c +++ b/arch.c @@ -1,4 +1,4 @@ -/* $NetBSD: arch.c,v 1.193 2021/01/09 16:06:09 rillig Exp $ */ +/* $NetBSD: arch.c,v 1.197 2021/02/05 05:15:12 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -147,7 +147,7 @@ struct ar_hdr { #include "dir.h" /* "@(#)arch.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: arch.c,v 1.193 2021/01/09 16:06:09 rillig Exp $"); +MAKE_RCSID("$NetBSD: arch.c,v 1.197 2021/02/05 05:15:12 rillig Exp $"); typedef struct List ArchList; typedef struct ListNode ArchListNode; @@ -225,14 +225,14 @@ ArchFree(void *ap) * Input: * pp The start of the specification. * gns The list on which to place the nodes. - * ctxt The context in which to expand variables. + * scope The scope in which to expand variables. * * Output: * return TRUE if it was a valid specification. * *pp Points to the first non-space after the archive spec. */ Boolean -Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt) +Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope) { char *cp; /* Pointer into line */ GNode *gn; /* New node */ @@ -255,7 +255,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt) Boolean isError; /* XXX: is expanded twice: once here and once below */ - (void)Var_Parse(&nested_p, ctxt, + (void)Var_Parse(&nested_p, scope, VARE_WANTRES | VARE_UNDEFERR, &result); /* TODO: handle errors */ isError = result.str == var_Error; @@ -272,7 +272,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt) *cp++ = '\0'; if (expandLibName) { char *expanded; - (void)Var_Subst(libName.str, ctxt, + (void)Var_Subst(libName.str, scope, VARE_WANTRES | VARE_UNDEFERR, &expanded); /* TODO: handle errors */ libName = MFStr_InitOwn(expanded); @@ -298,7 +298,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt) Boolean isError; const char *nested_p = cp; - (void)Var_Parse(&nested_p, ctxt, + (void)Var_Parse(&nested_p, scope, VARE_WANTRES | VARE_UNDEFERR, &result); /* TODO: handle errors */ @@ -355,7 +355,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt) char *p; char *unexpandedMemName = memName; - (void)Var_Subst(memName, ctxt, + (void)Var_Subst(memName, scope, VARE_WANTRES | VARE_UNDEFERR, &memName); /* TODO: handle errors */ @@ -379,7 +379,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt) gn->type |= OP_ARCHV; Lst_Append(gns, gn); - } else if (!Arch_ParseArchive(&p, gns, ctxt)) { + } else if (!Arch_ParseArchive(&p, gns, scope)) { /* Error in nested call. */ free(fullName); /* XXX: does unexpandedMemName leak? */ @@ -390,7 +390,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *ctxt) } else if (Dir_HasWildcards(memName)) { StringList members = LST_INIT; - Dir_Expand(memName, &dirSearchPath, &members); + SearchPath_Expand(&dirSearchPath, memName, &members); while (!Lst_IsEmpty(&members)) { char *member = Lst_Dequeue(&members); @@ -1022,9 +1022,9 @@ Arch_FindLib(GNode *gn, SearchPath *path) free(libName); #ifdef LIBRARIES - Var_Set(TARGET, gn->name, gn); + Var_Set(gn, TARGET, gn->name); #else - Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn); + Var_Set(gn, TARGET, GNode_Path(gn)); #endif } diff --git a/bmake.1 b/bmake.1 index f35c14cc1eee..e0d8267994c1 100644 --- a/bmake.1 +++ b/bmake.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.295 2020/12/23 13:49:12 rillig Exp $ +.\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -99,7 +99,7 @@ is equivalent to .It Fl D Ar variable Define .Ar variable -to be 1, in the global context. +to be 1, in the global scope. .It Fl d Ar [-]flags Turn on debugging, and specify which portions of .Nm @@ -355,7 +355,7 @@ Do not build any targets. Multiple instances of this option may be specified; the variables will be printed one per line, with a blank line for each null or undefined variable. -The value printed is extracted from the global context after all +The value printed is extracted from the global scope after all makefiles have been read. By default, the raw variable contents (which may include additional unexpanded variable references) are shown. diff --git a/bmake.cat1 b/bmake.cat1 index 9ed1dcdd9018..d9723b35cf7f 100644 --- a/bmake.cat1 +++ b/bmake.cat1 @@ -37,7 +37,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) -C /etc. -D variable - Define variable to be 1, in the global context. + Define variable to be 1, in the global scope. -d [-]flags Turn on debugging, and specify which portions of bmake are to @@ -224,7 +224,7 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1) instances of this option may be specified; the variables will be printed one per line, with a blank line for each null or unde- fined variable. The value printed is extracted from the global - context after all makefiles have been read. By default, the raw + scope after all makefiles have been read. By default, the raw variable contents (which may include additional unexpanded vari- able references) are shown. If variable contains a `$' then the value will be recursively expanded to its complete resultant text diff --git a/buf.c b/buf.c index b6a2e9192b60..cef082247278 100644 --- a/buf.c +++ b/buf.c @@ -1,4 +1,4 @@ -/* $NetBSD: buf.c,v 1.47 2020/12/30 10:03:16 rillig Exp $ */ +/* $NetBSD: buf.c,v 1.51 2021/01/30 21:18:14 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -75,7 +75,7 @@ #include "make.h" /* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: buf.c,v 1.47 2020/12/30 10:03:16 rillig Exp $"); +MAKE_RCSID("$NetBSD: buf.c,v 1.51 2021/01/30 21:18:14 rillig Exp $"); /* Make space in the buffer for adding at least 16 more bytes. */ void @@ -92,7 +92,7 @@ Buf_AddBytes(Buffer *buf, const char *bytes, size_t bytes_len) size_t old_len = buf->len; char *end; - if (__predict_false(old_len + bytes_len >= buf->cap)) { + if (old_len + bytes_len >= buf->cap) { size_t minIncr = bytes_len + 16; buf->cap += buf->cap > minIncr ? buf->cap : minIncr; buf->data = bmake_realloc(buf->data, buf->cap); @@ -135,21 +135,6 @@ Buf_AddInt(Buffer *buf, int n) Buf_AddBytes(buf, str, len); } -/* - * Get the data (usually a string) from the buffer. - * The returned data is valid until the next modifying operation - * on the buffer. - * - * Returns the data and optionally the length of the data. - */ -char * -Buf_GetAll(Buffer *buf, size_t *out_len) -{ - if (out_len != NULL) - *out_len = buf->len; - return buf->data; -} - /* Mark the buffer as empty, so it can be filled with data again. */ void Buf_Empty(Buffer *buf) @@ -175,22 +160,35 @@ Buf_Init(Buffer *buf) } /* - * Reset the buffer. - * If freeData is TRUE, the data from the buffer is freed as well. - * Otherwise it is kept and returned. + * Free the data from the buffer. + * Leave the buffer itself in an indeterminate state. */ -char * -Buf_Destroy(Buffer *buf, Boolean freeData) +void +Buf_Done(Buffer *buf) { - char *data = buf->data; - if (freeData) { - free(data); - data = NULL; - } + free(buf->data); +#ifdef CLEANUP buf->cap = 0; buf->len = 0; buf->data = NULL; +#endif +} + +/* + * Return the data from the buffer. + * Leave the buffer itself in an indeterminate state. + */ +char * +Buf_DoneData(Buffer *buf) +{ + char *data = buf->data; + +#ifdef CLEANUP + buf->cap = 0; + buf->len = 0; + buf->data = NULL; +#endif return data; } @@ -200,22 +198,23 @@ Buf_Destroy(Buffer *buf, Boolean freeData) #endif /* - * Reset the buffer and return its data. + * Return the data from the buffer. + * Leave the buffer itself in an indeterminate state. * * If the buffer size is much greater than its content, * a new buffer will be allocated and the old one freed. */ char * -Buf_DestroyCompact(Buffer *buf) +Buf_DoneDataCompact(Buffer *buf) { #if BUF_COMPACT_LIMIT > 0 if (buf->cap - buf->len >= BUF_COMPACT_LIMIT) { /* We trust realloc to be smart */ char *data = bmake_realloc(buf->data, buf->len + 1); data[buf->len] = '\0'; /* XXX: unnecessary */ - Buf_Destroy(buf, FALSE); + Buf_DoneData(buf); return data; } #endif - return Buf_Destroy(buf, FALSE); + return Buf_DoneData(buf); } diff --git a/buf.h b/buf.h index 942b115c76e6..594e9651dbfb 100644 --- a/buf.h +++ b/buf.h @@ -1,4 +1,4 @@ -/* $NetBSD: buf.h,v 1.38 2020/12/28 15:42:53 rillig Exp $ */ +/* $NetBSD: buf.h,v 1.42 2021/01/30 21:25:10 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -81,16 +81,11 @@ /* An automatically growing null-terminated buffer of characters. */ typedef struct Buffer { - size_t cap; /* Allocated size of the buffer, including the null */ - size_t len; /* Number of bytes in buffer, excluding the null */ + size_t cap; /* Allocated size of the buffer, including the '\0' */ + size_t len; /* Number of bytes in buffer, excluding the '\0' */ char *data; /* The buffer itself (always null-terminated) */ } Buffer; -/* If we aren't on NetBSD, __predict_false() might not be defined. */ -#ifndef __predict_false -#define __predict_false(x) (x) -#endif - void Buf_Expand(Buffer *); /* Buf_AddByte adds a single byte to a buffer. */ @@ -99,19 +94,13 @@ Buf_AddByte(Buffer *buf, char byte) { size_t old_len = buf->len++; char *end; - if (__predict_false(old_len + 1 >= buf->cap)) + if (old_len + 1 >= buf->cap) Buf_Expand(buf); end = buf->data + old_len; end[0] = byte; end[1] = '\0'; } -MAKE_INLINE size_t -Buf_Len(const Buffer *buf) -{ - return buf->len; -} - MAKE_INLINE Boolean Buf_EndsWith(const Buffer *buf, char ch) { @@ -122,11 +111,11 @@ void Buf_AddBytes(Buffer *, const char *, size_t); void Buf_AddBytesBetween(Buffer *, const char *, const char *); void Buf_AddStr(Buffer *, const char *); void Buf_AddInt(Buffer *, int); -char *Buf_GetAll(Buffer *, size_t *); void Buf_Empty(Buffer *); void Buf_Init(Buffer *); void Buf_InitSize(Buffer *, size_t); -char *Buf_Destroy(Buffer *, Boolean); -char *Buf_DestroyCompact(Buffer *); +void Buf_Done(Buffer *); +char *Buf_DoneData(Buffer *); +char *Buf_DoneDataCompact(Buffer *); #endif /* MAKE_BUF_H */ diff --git a/compat.c b/compat.c index 6c7238318959..59190d8c4354 100644 --- a/compat.c +++ b/compat.c @@ -1,4 +1,4 @@ -/* $NetBSD: compat.c,v 1.219 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: compat.c,v 1.224 2021/02/05 05:15:12 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -69,7 +69,7 @@ * SUCH DAMAGE. */ -/*- +/* * compat.c -- * The routines in this file implement the full-compatibility * mode of PMake. Most of the special functionality of PMake @@ -99,7 +99,7 @@ #include "pathnames.h" /* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: compat.c,v 1.219 2021/01/10 21:20:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: compat.c,v 1.224 2021/02/05 05:15:12 rillig Exp $"); static GNode *curTarg = NULL; static pid_t compatChild; @@ -260,7 +260,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln) /* * Append the expanded command, to prevent the * local variables from being interpreted in the - * context of the .END node. + * scope of the .END node. * * A probably unintended side effect of this is that * the expanded command will be expanded again in the @@ -360,7 +360,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln) /* * Fork and execute the single command. If the fork fails, we abort. */ - compatChild = cpid = vFork(); + compatChild = cpid = vfork(); if (cpid < 0) { Fatal("Could not fork"); } @@ -512,7 +512,7 @@ MakeUnmade(GNode *gn, GNode *pgn) } if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) - Var_Set(IMPSRC, GNode_VarTarget(gn), pgn); + Var_Set(pgn, IMPSRC, GNode_VarTarget(gn)); /* * All the children were made ok. Now youngestChild->mtime contains the @@ -605,7 +605,7 @@ MakeOther(GNode *gn, GNode *pgn) if (Lst_FindDatum(&gn->implicitParents, pgn) != NULL) { const char *target = GNode_VarTarget(gn); - Var_Set(IMPSRC, target != NULL ? target : "", pgn); + Var_Set(pgn, IMPSRC, target != NULL ? target : ""); } switch (gn->made) { @@ -752,6 +752,10 @@ Compat_Run(GNodeList *targs) } if (errorNode != NULL) { + if (DEBUG(GRAPH2)) + Targ_PrintGraph(2); + else if (DEBUG(GRAPH3)) + Targ_PrintGraph(3); PrintOnError(errorNode, "\nStop."); exit(1); } diff --git a/cond.c b/cond.c index 1a8aba637fe9..8f36fda22f12 100644 --- a/cond.c +++ b/cond.c @@ -1,4 +1,4 @@ -/* $NetBSD: cond.c,v 1.235 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -95,53 +95,65 @@ #include "dir.h" /* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: cond.c,v 1.235 2021/01/10 21:20:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: cond.c,v 1.256 2021/02/05 05:15:12 rillig Exp $"); /* * The parsing of conditional expressions is based on this grammar: - * E -> F || E - * E -> F - * F -> T && F - * F -> T - * T -> defined(variable) - * T -> make(target) - * T -> exists(file) - * T -> empty(varspec) - * T -> target(name) - * T -> commands(name) - * T -> symbol - * T -> $(varspec) op value - * T -> $(varspec) == "string" - * T -> $(varspec) != "string" - * T -> "string" - * T -> ( E ) - * T -> ! T - * op -> == | != | > | < | >= | <= + * Or -> And '||' Or + * Or -> And + * And -> Term '&&' And + * And -> Term + * Term -> Function '(' Argument ')' + * Term -> Leaf Operator Leaf + * Term -> Leaf + * Term -> '(' Or ')' + * Term -> '!' Term + * Leaf -> "string" + * Leaf -> Number + * Leaf -> VariableExpression + * Leaf -> Symbol + * Operator -> '==' | '!=' | '>' | '<' | '>=' | '<=' * - * 'symbol' is some other symbol to which the default function is applied. + * 'Symbol' is an unquoted string literal to which the default function is + * applied. * * The tokens are scanned by CondToken, which returns: - * TOK_AND for '&' or '&&' - * TOK_OR for '|' or '||' + * TOK_AND for '&&' + * TOK_OR for '||' * TOK_NOT for '!' * TOK_LPAREN for '(' * TOK_RPAREN for ')' + * * Other terminal symbols are evaluated using either the default function or * the function given in the terminal, they return either TOK_TRUE or * TOK_FALSE. - * - * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons. - * - * All non-terminal functions (CondParser_Expr, CondParser_Factor and - * CondParser_Term) return either TOK_FALSE, TOK_TRUE, or TOK_ERROR on error. */ typedef enum Token { - TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT, + TOK_FALSE, TOK_TRUE, TOK_AND, TOK_OR, TOK_NOT, TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR } Token; +typedef enum CondResult { + CR_FALSE, CR_TRUE, CR_ERROR +} CondResult; + +typedef enum ComparisonOp { + LT, LE, GT, GE, EQ, NE +} ComparisonOp; + typedef struct CondParser { - const struct If *if_info; /* Info for current statement */ + + /* + * The plain '.if ${VAR}' evaluates to true if the value of the + * expression has length > 0. The other '.if' variants delegate + * to evalBare instead. + */ + Boolean plain; + + /* The function to apply on unquoted bare words. */ + Boolean (*evalBare)(size_t, const char *); + Boolean negateEvalBare; + const char *p; /* The remaining condition to parse */ Token curr; /* Single push-back token used in parsing */ @@ -154,11 +166,13 @@ typedef struct CondParser { Boolean printedError; } CondParser; -static Token CondParser_Expr(CondParser *par, Boolean); +static CondResult CondParser_Or(CondParser *par, Boolean); static unsigned int cond_depth = 0; /* current .if nesting level */ static unsigned int cond_min_depth = 0; /* depth at makefile open */ +static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" }; + /* * Indicate when we should be strict about lhs of comparisons. * In strict mode, the lhs must be a variable expression or a string literal @@ -214,7 +228,7 @@ CondParser_SkipWhitespace(CondParser *par) * Return the length of the argument, or 0 on error. */ static size_t -ParseFuncArg(const char **pp, Boolean doEval, const char *func, +ParseFuncArg(CondParser *par, const char **pp, Boolean doEval, const char *func, char **out_arg) { const char *p = *pp; @@ -254,7 +268,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func, ? VARE_WANTRES | VARE_UNDEFERR : VARE_NONE; FStr nestedVal; - (void)Var_Parse(&p, VAR_CMDLINE, eflags, &nestedVal); + (void)Var_Parse(&p, SCOPE_CMDLINE, eflags, &nestedVal); /* TODO: handle errors */ Buf_AddStr(&argBuf, nestedVal.str); FStr_Done(&nestedVal); @@ -268,16 +282,15 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func, p++; } - *out_arg = Buf_GetAll(&argBuf, &argLen); - Buf_Destroy(&argBuf, FALSE); + argLen = argBuf.len; + *out_arg = Buf_DoneData(&argBuf); cpp_skip_hspace(&p); if (func != NULL && *p++ != ')') { - Parse_Error(PARSE_WARNING, - "Missing closing parenthesis for %s()", - func); - /* The PARSE_FATAL follows in CondEvalExpression. */ + Parse_Error(PARSE_FATAL, + "Missing closing parenthesis for %s()", func); + par->printedError = TRUE; return 0; } @@ -290,7 +303,7 @@ ParseFuncArg(const char **pp, Boolean doEval, const char *func, static Boolean FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg) { - FStr value = Var_Value(arg, VAR_CMDLINE); + FStr value = Var_Value(SCOPE_CMDLINE, arg); Boolean result = value.str != NULL; FStr_Done(&value); return result; @@ -390,7 +403,70 @@ is_separator(char ch) return ch == '\0' || ch_isspace(ch) || strchr("!=><)", ch) != NULL; } -/*- +/* + * In a quoted or unquoted string literal or a number, parse a variable + * expression. + * + * Example: .if x${CENTER}y == "${PREFIX}${SUFFIX}" || 0x${HEX} + */ +static Boolean +CondParser_StringExpr(CondParser *par, const char *start, + Boolean const doEval, Boolean const quoted, + Buffer *buf, FStr *const inout_str) +{ + VarEvalFlags eflags; + const char *nested_p; + Boolean atStart; + VarParseResult parseResult; + + /* if we are in quotes, an undefined variable is ok */ + eflags = doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR + : doEval ? VARE_WANTRES + : VARE_NONE; + + nested_p = par->p; + atStart = nested_p == start; + parseResult = Var_Parse(&nested_p, SCOPE_CMDLINE, eflags, inout_str); + /* TODO: handle errors */ + if (inout_str->str == var_Error) { + if (parseResult == VPR_ERR) { + /* + * FIXME: Even if an error occurs, there is no + * guarantee that it is reported. + * + * See cond-token-plain.mk $$$$$$$$. + */ + par->printedError = TRUE; + } + /* + * XXX: Can there be any situation in which a returned + * var_Error requires freeIt? + */ + FStr_Done(inout_str); + /* + * Even if !doEval, we still report syntax errors, which is + * what getting var_Error back with !doEval means. + */ + *inout_str = FStr_InitRefer(NULL); + return FALSE; + } + par->p = nested_p; + + /* + * If the '$' started the string literal (which means no quotes), and + * the variable expression is followed by a space, looks like a + * comparison operator or is the end of the expression, we are done. + */ + if (atStart && is_separator(par->p[0])) + return FALSE; + + Buf_AddStr(buf, inout_str->str); + FStr_Done(inout_str); + *inout_str = FStr_InitRefer(NULL); /* not finished yet */ + return TRUE; +} + +/* * Parse a string from a variable reference or an optionally quoted * string. This is called for the lhs and rhs of string comparisons. * @@ -399,19 +475,14 @@ is_separator(char ch) * Sets out_quoted if the string was quoted. * Sets out_freeIt. */ -/* coverity:[+alloc : arg-*4] */ static void CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS, FStr *out_str, Boolean *out_quoted) { Buffer buf; FStr str; - Boolean atStart; - const char *nested_p; Boolean quoted; const char *start; - VarEvalFlags eflags; - VarParseResult parseResult; Buf_Init(&buf); str = FStr_InitRefer(NULL); @@ -430,12 +501,10 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS, } continue; case '"': - if (quoted) { - par->p++; /* skip the closing quote */ - goto got_str; - } - Buf_AddByte(&buf, par->p[0]); /* likely? */ par->p++; + if (quoted) + goto got_str; /* skip the closing quote */ + Buf_AddByte(&buf, '"'); continue; case ')': /* see is_separator */ case '!': @@ -450,47 +519,9 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS, par->p++; continue; case '$': - /* if we are in quotes, an undefined variable is ok */ - eflags = - doEval && !quoted ? VARE_WANTRES | VARE_UNDEFERR : - doEval ? VARE_WANTRES : - VARE_NONE; - - nested_p = par->p; - atStart = nested_p == start; - parseResult = Var_Parse(&nested_p, VAR_CMDLINE, eflags, - &str); - /* TODO: handle errors */ - if (str.str == var_Error) { - if (parseResult == VPR_ERR) - par->printedError = TRUE; - /* - * XXX: Can there be any situation in which - * a returned var_Error requires freeIt? - */ - FStr_Done(&str); - /* - * Even if !doEval, we still report syntax - * errors, which is what getting var_Error - * back with !doEval means. - */ - str = FStr_InitRefer(NULL); + if (!CondParser_StringExpr(par, + start, doEval, quoted, &buf, &str)) goto cleanup; - } - par->p = nested_p; - - /* - * If the '$' started the string literal (which means - * no quotes), and the variable expression is followed - * by a space, looks like a comparison operator or is - * the end of the expression, we are done. - */ - if (atStart && is_separator(par->p[0])) - goto cleanup; - - Buf_AddStr(&buf, str.str); - FStr_Done(&str); - str = FStr_InitRefer(NULL); /* not finished yet */ continue; default: if (strictLHS && !quoted && *start != '$' && @@ -508,38 +539,17 @@ CondParser_String(CondParser *par, Boolean doEval, Boolean strictLHS, } } got_str: - str = FStr_InitOwn(Buf_GetAll(&buf, NULL)); + str = FStr_InitOwn(buf.data); cleanup: - Buf_Destroy(&buf, FALSE); + Buf_DoneData(&buf); *out_str = str; } -struct If { - const char *form; /* Form of if */ - size_t formlen; /* Length of form */ - Boolean doNot; /* TRUE if default function should be negated */ - /* The default function to apply on unquoted bare words. */ - Boolean (*defProc)(size_t, const char *); -}; - -/* The different forms of .if directives. */ -static const struct If ifs[] = { - { "def", 3, FALSE, FuncDefined }, - { "ndef", 4, TRUE, FuncDefined }, - { "make", 4, FALSE, FuncMake }, - { "nmake", 5, TRUE, FuncMake }, - { "", 0, FALSE, FuncDefined }, - { NULL, 0, FALSE, NULL } -}; -enum { - PLAIN_IF_INDEX = 4 -}; - static Boolean -If_Eval(const struct If *if_info, const char *arg, size_t arglen) +If_Eval(const CondParser *par, const char *arg, size_t arglen) { - Boolean res = if_info->defProc(arglen, arg); - return if_info->doNot ? !res : res; + Boolean res = par->evalBare(arglen, arg); + return par->negateEvalBare ? !res : res; } /* @@ -563,69 +573,96 @@ EvalNotEmpty(CondParser *par, const char *value, Boolean quoted) * the evaluation function from that .if variant, which would test * whether a variable of the given name were defined. */ /* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */ - if (par->if_info->form[0] == '\0') + if (par->plain) return value[0] != '\0'; - /* For the other variants of .ifxxx ${...}, use its default function. */ - return If_Eval(par->if_info, value, strlen(value)); + return If_Eval(par, value, strlen(value)); } /* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */ -static Token -EvalCompareNum(double lhs, const char *op, double rhs) +static Boolean +EvalCompareNum(double lhs, ComparisonOp op, double rhs) { - DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, op); + DEBUG3(COND, "lhs = %f, rhs = %f, op = %.2s\n", lhs, rhs, opname[op]); - switch (op[0]) { - case '!': - if (op[1] != '=') { - Parse_Error(PARSE_WARNING, "Unknown operator"); - /* The PARSE_FATAL follows in CondEvalExpression. */ - return TOK_ERROR; - } - return ToToken(lhs != rhs); - case '=': - if (op[1] != '=') { - Parse_Error(PARSE_WARNING, "Unknown operator"); - /* The PARSE_FATAL follows in CondEvalExpression. */ - return TOK_ERROR; - } - return ToToken(lhs == rhs); - case '<': - return ToToken(op[1] == '=' ? lhs <= rhs : lhs < rhs); - case '>': - return ToToken(op[1] == '=' ? lhs >= rhs : lhs > rhs); + switch (op) { + case LT: + return lhs < rhs; + case LE: + return lhs <= rhs; + case GT: + return lhs > rhs; + case GE: + return lhs >= rhs; + case NE: + return lhs != rhs; + default: + return lhs == rhs; } - return TOK_ERROR; } static Token -EvalCompareStr(const char *lhs, const char *op, const char *rhs) +EvalCompareStr(CondParser *par, const char *lhs, + ComparisonOp op, const char *rhs) { - if (!((op[0] == '!' || op[0] == '=') && op[1] == '=')) { - Parse_Error(PARSE_WARNING, - "String comparison operator " - "must be either == or !="); - /* The PARSE_FATAL follows in CondEvalExpression. */ + if (op != EQ && op != NE) { + Parse_Error(PARSE_FATAL, + "String comparison operator must be either == or !="); + par->printedError = TRUE; return TOK_ERROR; } - DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", lhs, rhs, op); - return ToToken((*op == '=') == (strcmp(lhs, rhs) == 0)); + DEBUG3(COND, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", + lhs, rhs, opname[op]); + return ToToken((op == EQ) == (strcmp(lhs, rhs) == 0)); } /* Evaluate a comparison, such as "${VAR} == 12345". */ static Token -EvalCompare(const char *lhs, Boolean lhsQuoted, const char *op, - const char *rhs, Boolean rhsQuoted) +EvalCompare(CondParser *par, const char *lhs, Boolean lhsQuoted, + ComparisonOp op, const char *rhs, Boolean rhsQuoted) { double left, right; if (!rhsQuoted && !lhsQuoted) if (TryParseNumber(lhs, &left) && TryParseNumber(rhs, &right)) - return EvalCompareNum(left, op, right); + return ToToken(EvalCompareNum(left, op, right)); - return EvalCompareStr(lhs, op, rhs); + return EvalCompareStr(par, lhs, op, rhs); +} + +static Boolean +CondParser_ComparisonOp(CondParser *par, ComparisonOp *out_op) +{ + const char *p = par->p; + + if (p[0] == '<' && p[1] == '=') { + *out_op = LE; + goto length_2; + } else if (p[0] == '<') { + *out_op = LT; + goto length_1; + } else if (p[0] == '>' && p[1] == '=') { + *out_op = GE; + goto length_2; + } else if (p[0] == '>') { + *out_op = GT; + goto length_1; + } else if (p[0] == '=' && p[1] == '=') { + *out_op = EQ; + goto length_2; + } else if (p[0] == '!' && p[1] == '=') { + *out_op = NE; + goto length_2; + } + return FALSE; + +length_2: + par->p = p + 2; + return TRUE; +length_1: + par->p = p + 1; + return TRUE; } /* @@ -641,7 +678,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval) { Token t = TOK_ERROR; FStr lhs, rhs; - const char *op; + ComparisonOp op; Boolean lhsQuoted, rhsQuoted; /* @@ -654,18 +691,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval) CondParser_SkipWhitespace(par); - op = par->p; - switch (par->p[0]) { - case '!': - case '=': - case '<': - case '>': - if (par->p[1] == '=') - par->p += 2; - else - par->p++; - break; - default: + if (!CondParser_ComparisonOp(par, &op)) { /* Unknown operator, compare against an empty string or 0. */ t = ToToken(doEval && EvalNotEmpty(par, lhs.str, lhsQuoted)); goto done_lhs; @@ -674,9 +700,9 @@ CondParser_Comparison(CondParser *par, Boolean doEval) CondParser_SkipWhitespace(par); if (par->p[0] == '\0') { - Parse_Error(PARSE_WARNING, - "Missing right-hand-side of operator"); - /* The PARSE_FATAL follows in CondEvalExpression. */ + Parse_Error(PARSE_FATAL, + "Missing right-hand-side of operator '%s'", opname[op]); + par->printedError = TRUE; goto done_lhs; } @@ -689,7 +715,7 @@ CondParser_Comparison(CondParser *par, Boolean doEval) goto done_rhs; } - t = EvalCompare(lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); + t = EvalCompare(par, lhs.str, lhsQuoted, op, rhs.str, rhsQuoted); done_rhs: FStr_Done(&rhs); @@ -704,8 +730,9 @@ CondParser_Comparison(CondParser *par, Boolean doEval) */ /*ARGSUSED*/ static size_t -ParseEmptyArg(const char **pp, Boolean doEval, - const char *func MAKE_ATTR_UNUSED, char **out_arg) +ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp, + Boolean doEval, const char *func MAKE_ATTR_UNUSED, + char **out_arg) { FStr val; size_t magic_res; @@ -714,7 +741,7 @@ ParseEmptyArg(const char **pp, Boolean doEval, *out_arg = NULL; (*pp)--; /* Make (*pp)[1] point to the '('. */ - (void)Var_Parse(pp, VAR_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE, + (void)Var_Parse(pp, SCOPE_CMDLINE, doEval ? VARE_WANTRES : VARE_NONE, &val); /* TODO: handle errors */ /* If successful, *pp points beyond the closing ')' now. */ @@ -753,8 +780,8 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token) static const struct fn_def { const char *fn_name; size_t fn_name_len; - size_t (*fn_parse)(const char **, Boolean, const char *, - char **); + size_t (*fn_parse)(CondParser *, const char **, Boolean, + const char *, char **); Boolean (*fn_eval)(size_t, const char *); } fns[] = { { "defined", 7, ParseFuncArg, FuncDefined }, @@ -779,7 +806,7 @@ CondParser_Func(CondParser *par, Boolean doEval, Token *out_token) if (*cp != '(') break; - arglen = fn->fn_parse(&cp, doEval, fn->fn_name, &arg); + arglen = fn->fn_parse(par, &cp, doEval, fn->fn_name, &arg); if (arglen == 0 || arglen == (size_t)-1) { par->p = cp; *out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR; @@ -825,7 +852,7 @@ CondParser_LeafToken(CondParser *par, Boolean doEval) * syntax would be invalid if we did "defined(a)" - so instead treat * as an expression. */ - arglen = ParseFuncArg(&cp, doEval, NULL, &arg); + arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg); cp1 = cp; cpp_skip_whitespace(&cp1); if (*cp1 == '=' || *cp1 == '!') @@ -838,7 +865,7 @@ CondParser_LeafToken(CondParser *par, Boolean doEval) * after .if must have been taken literally, so the argument cannot * be empty - even if it contained a variable expansion. */ - t = ToToken(!doEval || If_Eval(par->if_info, arg, arglen)); + t = ToToken(!doEval || If_Eval(par, arg, arglen)); free(arg); return t; } @@ -909,148 +936,113 @@ CondParser_Token(CondParser *par, Boolean doEval) } /* - * Parse a single term in the expression. This consists of a terminal symbol - * or TOK_NOT and a term (not including the binary operators): - * - * T -> defined(variable) | make(target) | exists(file) | symbol - * T -> ! T | ( E ) - * - * Results: - * TOK_TRUE, TOK_FALSE or TOK_ERROR. + * Term -> '(' Or ')' + * Term -> '!' Term + * Term -> Leaf Operator Leaf + * Term -> Leaf */ -static Token +static CondResult CondParser_Term(CondParser *par, Boolean doEval) { + CondResult res; Token t; t = CondParser_Token(par, doEval); + if (t == TOK_TRUE) + return CR_TRUE; + if (t == TOK_FALSE) + return CR_FALSE; - if (t == TOK_EOF) { - /* - * If we reached the end of the expression, the expression - * is malformed... - */ - t = TOK_ERROR; - } else if (t == TOK_LPAREN) { - /* - * T -> ( E ) - */ - t = CondParser_Expr(par, doEval); - if (t != TOK_ERROR) { - if (CondParser_Token(par, doEval) != TOK_RPAREN) { - t = TOK_ERROR; - } - } - } else if (t == TOK_NOT) { - t = CondParser_Term(par, doEval); - if (t == TOK_TRUE) { - t = TOK_FALSE; - } else if (t == TOK_FALSE) { - t = TOK_TRUE; - } + if (t == TOK_LPAREN) { + res = CondParser_Or(par, doEval); + if (res == CR_ERROR) + return CR_ERROR; + if (CondParser_Token(par, doEval) != TOK_RPAREN) + return CR_ERROR; + return res; } - return t; + + if (t == TOK_NOT) { + res = CondParser_Term(par, doEval); + if (res == CR_TRUE) + res = CR_FALSE; + else if (res == CR_FALSE) + res = CR_TRUE; + return res; + } + + return CR_ERROR; } /* - * Parse a conjunctive factor (nice name, wot?) - * - * F -> T && F | T - * - * Results: - * TOK_TRUE, TOK_FALSE or TOK_ERROR + * And -> Term '&&' And + * And -> Term */ -static Token -CondParser_Factor(CondParser *par, Boolean doEval) +static CondResult +CondParser_And(CondParser *par, Boolean doEval) { - Token l, o; + CondResult res; + Token op; - l = CondParser_Term(par, doEval); - if (l != TOK_ERROR) { - o = CondParser_Token(par, doEval); + res = CondParser_Term(par, doEval); + if (res == CR_ERROR) + return CR_ERROR; - if (o == TOK_AND) { - /* - * F -> T && F - * - * If T is TOK_FALSE, the whole thing will be - * TOK_FALSE, but we have to parse the r.h.s. anyway - * (to throw it away). If T is TOK_TRUE, the result - * is the r.h.s., be it a TOK_ERROR or not. - */ - if (l == TOK_TRUE) { - l = CondParser_Factor(par, doEval); - } else { - (void)CondParser_Factor(par, FALSE); - } - } else { - /* - * F -> T - */ - CondParser_PushBack(par, o); - } + op = CondParser_Token(par, doEval); + if (op == TOK_AND) { + if (res == CR_TRUE) + return CondParser_And(par, doEval); + if (CondParser_And(par, FALSE) == CR_ERROR) + return CR_ERROR; + return res; } - return l; + + CondParser_PushBack(par, op); + return res; } /* - * Main expression production. - * - * E -> F || E | F - * - * Results: - * TOK_TRUE, TOK_FALSE or TOK_ERROR. + * Or -> And '||' Or + * Or -> And */ -static Token -CondParser_Expr(CondParser *par, Boolean doEval) +static CondResult +CondParser_Or(CondParser *par, Boolean doEval) { - Token l, o; + CondResult res; + Token op; - l = CondParser_Factor(par, doEval); - if (l != TOK_ERROR) { - o = CondParser_Token(par, doEval); + res = CondParser_And(par, doEval); + if (res == CR_ERROR) + return CR_ERROR; - if (o == TOK_OR) { - /* - * E -> F || E - * - * A similar thing occurs for ||, except that here - * we make sure the l.h.s. is TOK_FALSE before we - * bother to evaluate the r.h.s. Once again, if l - * is TOK_FALSE, the result is the r.h.s. and once - * again if l is TOK_TRUE, we parse the r.h.s. to - * throw it away. - */ - if (l == TOK_FALSE) { - l = CondParser_Expr(par, doEval); - } else { - (void)CondParser_Expr(par, FALSE); - } - } else { - /* - * E -> F - */ - CondParser_PushBack(par, o); - } + op = CondParser_Token(par, doEval); + if (op == TOK_OR) { + if (res == CR_FALSE) + return CondParser_Or(par, doEval); + if (CondParser_Or(par, FALSE) == CR_ERROR) + return CR_ERROR; + return res; } - return l; + + CondParser_PushBack(par, op); + return res; } static CondEvalResult -CondParser_Eval(CondParser *par, Boolean *value) +CondParser_Eval(CondParser *par, Boolean *out_value) { - Token res; + CondResult res; DEBUG1(COND, "CondParser_Eval: %s\n", par->p); - res = CondParser_Expr(par, TRUE); - if (res != TOK_FALSE && res != TOK_TRUE) + res = CondParser_Or(par, TRUE); + if (res == CR_ERROR) return COND_INVALID; if (CondParser_Token(par, FALSE) != TOK_EOF) return COND_INVALID; - *value = res == TOK_TRUE; + *out_value = res == CR_TRUE; return COND_PARSE; } @@ -1066,7 +1058,8 @@ CondParser_Eval(CondParser *par, Boolean *value) * (*value) is set to the boolean value of the condition */ static CondEvalResult -CondEvalExpression(const struct If *info, const char *cond, Boolean *value, +CondEvalExpression(const char *cond, Boolean *out_value, Boolean plain, + Boolean (*evalBare)(size_t, const char *), Boolean negate, Boolean eprint, Boolean strictLHS) { CondParser par; @@ -1076,12 +1069,14 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value, cpp_skip_hspace(&cond); - par.if_info = info != NULL ? info : ifs + PLAIN_IF_INDEX; + par.plain = plain; + par.evalBare = evalBare; + par.negateEvalBare = negate; par.p = cond; par.curr = TOK_NONE; par.printedError = FALSE; - rval = CondParser_Eval(&par, value); + rval = CondParser_Eval(&par, out_value); if (rval == COND_INVALID && eprint && !par.printedError) Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", cond); @@ -1096,7 +1091,8 @@ CondEvalExpression(const struct If *info, const char *cond, Boolean *value, CondEvalResult Cond_EvalCondition(const char *cond, Boolean *out_value) { - return CondEvalExpression(NULL, cond, out_value, FALSE, FALSE); + return CondEvalExpression(cond, out_value, TRUE, + FuncDefined, FALSE, FALSE, FALSE); } static Boolean @@ -1106,6 +1102,43 @@ IsEndif(const char *p) p[3] == 'i' && p[4] == 'f' && !ch_isalpha(p[5]); } +static Boolean +DetermineKindOfConditional(const char **pp, Boolean *out_plain, + Boolean (**out_evalBare)(size_t, const char *), + Boolean *out_negate) +{ + const char *p = *pp; + + p += 2; + *out_plain = FALSE; + *out_evalBare = FuncDefined; + *out_negate = FALSE; + if (*p == 'n') { + p++; + *out_negate = TRUE; + } + if (is_token(p, "def", 3)) { /* .ifdef and .ifndef */ + p += 3; + } else if (is_token(p, "make", 4)) { /* .ifmake and .ifnmake */ + p += 4; + *out_evalBare = FuncMake; + } else if (is_token(p, "", 0) && !*out_negate) { /* plain .if */ + *out_plain = TRUE; + } else { + /* + * TODO: Add error message about unknown directive, + * since there is no other known directive that starts + * with 'el' or 'if'. + * + * Example: .elifx 123 + */ + return FALSE; + } + + *pp = p; + return TRUE; +} + /* * Evaluate the conditional directive in the line, which is one of: * @@ -1159,7 +1192,9 @@ Cond_EvalLine(const char *line) static enum IfState *cond_states = NULL; static unsigned int cond_states_cap = 128; - const struct If *ifp; + Boolean plain; + Boolean (*evalBare)(size_t, const char *); + Boolean negate; Boolean isElif; Boolean value; IfState state; @@ -1242,29 +1277,8 @@ Cond_EvalLine(const char *line) return COND_INVALID; /* Not an ifxxx or elifxxx line */ } - /* - * Figure out what sort of conditional it is -- what its default - * function is, etc. -- by looking in the table of valid "ifs" - */ - p += 2; - for (ifp = ifs;; ifp++) { - if (ifp->form == NULL) { - /* - * TODO: Add error message about unknown directive, - * since there is no other known directive that starts - * with 'el' or 'if'. - * - * Example: .elifx 123 - */ - return COND_INVALID; - } - if (is_token(p, ifp->form, ifp->formlen)) { - p += ifp->formlen; - break; - } - } - - /* Now we know what sort of 'if' it is... */ + if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate)) + return COND_INVALID; if (isElif) { if (cond_depth == cond_min_depth) { @@ -1308,7 +1322,8 @@ Cond_EvalLine(const char *line) } /* And evaluate the conditional expression */ - if (CondEvalExpression(ifp, p, &value, TRUE, TRUE) == COND_INVALID) { + if (CondEvalExpression(p, &value, plain, evalBare, negate, + TRUE, TRUE) == COND_INVALID) { /* Syntax error in conditional, error message already output. */ /* Skip everything to matching .endif */ /* XXX: An extra '.else' is not detected in this case. */ diff --git a/config.h.in b/config.h.in index 6ceb16bc141a..18f07aeda37f 100644 --- a/config.h.in +++ b/config.h.in @@ -333,6 +333,9 @@ /* Define to `int' if does not define. */ #undef pid_t +/* type that signal handlers can safely frob */ +#undef sig_atomic_t + /* Define to `unsigned int' if does not define. */ #undef size_t diff --git a/configure b/configure index bf729ee239ae..6dbf5ad31a00 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for bmake 20201112. +# Generated by GNU Autoconf 2.69 for bmake 20210201. # # Report bugs to . # @@ -580,8 +580,8 @@ MAKEFLAGS= # Identity of this package. PACKAGE_NAME='bmake' PACKAGE_TARNAME='bmake' -PACKAGE_VERSION='20201112' -PACKAGE_STRING='bmake 20201112' +PACKAGE_VERSION='20210201' +PACKAGE_STRING='bmake 20210201' PACKAGE_BUGREPORT='sjg@NetBSD.org' PACKAGE_URL='' @@ -1255,7 +1255,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures bmake 20201112 to adapt to many kinds of systems. +\`configure' configures bmake 20210201 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1316,7 +1316,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of bmake 20201112:";; + short | recursive ) echo "Configuration of bmake 20210201:";; esac cat <<\_ACEOF @@ -1422,7 +1422,7 @@ fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -bmake configure 20201112 +bmake configure 20210201 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. @@ -2002,7 +2002,7 @@ cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by bmake $as_me 20201112, which was +It was created by bmake $as_me 20210201, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -5322,6 +5322,24 @@ $as_echo "#define TM_IN_SYS_TIME 1" >>confdefs.h fi + +ac_fn_c_check_type "$LINENO" "sig_atomic_t" "ac_cv_type_sig_atomic_t" " +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include + +" +if test "x$ac_cv_type_sig_atomic_t" = xyes; then : + +else + +$as_echo "#define sig_atomic_t int" >>confdefs.h + + +fi + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking return type of signal handlers" >&5 $as_echo_n "checking return type of signal handlers... " >&6; } if ${ac_cv_type_signal+:} false; then : @@ -6666,7 +6684,7 @@ cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by bmake $as_me 20201112, which was +This file was extended by bmake $as_me 20210201, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6728,7 +6746,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -bmake config.status 20201112 +bmake config.status 20210201 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" diff --git a/configure.in b/configure.in index c52546068198..877493594a20 100644 --- a/configure.in +++ b/configure.in @@ -1,11 +1,11 @@ dnl dnl RCSid: -dnl $Id: configure.in,v 1.69 2020/11/14 07:40:43 sjg Exp $ +dnl $Id: configure.in,v 1.70 2021/02/01 18:29:26 sjg Exp $ dnl dnl Process this file with autoconf to produce a configure script dnl AC_PREREQ(2.50) -AC_INIT([bmake], [20201112], [sjg@NetBSD.org]) +AC_INIT([bmake], [20210201], [sjg@NetBSD.org]) AC_CONFIG_HEADERS(config.h) dnl make srcdir absolute @@ -185,6 +185,17 @@ AC_DECL_SYS_SIGLIST AC_HEADER_TIME AC_STRUCT_TM +dnl we need sig_atomic_t +AH_TEMPLATE([sig_atomic_t],[type that signal handlers can safely frob]) +AC_CHECK_TYPE(sig_atomic_t,,[ +AC_DEFINE([sig_atomic_t],[int],) +],[ +#ifdef HAVE_SYS_TYPES_H +#include +#endif +#include +]) + dnl Checks for library functions. AC_TYPE_SIGNAL AC_FUNC_VFORK diff --git a/dir.c b/dir.c index 230b66b3baf3..026983e3ec10 100644 --- a/dir.c +++ b/dir.c @@ -1,4 +1,4 @@ -/* $NetBSD: dir.c,v 1.255 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: dir.c,v 1.270 2021/02/05 05:48:19 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -89,8 +89,9 @@ * Returns TRUE if the name given it needs to * be wildcard-expanded. * - * Dir_Expand Given a pattern and a path, return a Lst of names - * which match the pattern on the search path. + * SearchPath_Expand + * Expand a filename pattern to find all matching files + * from the search path. * * Dir_FindFile Searches for a file on a given search path. * If it exists, the entire path is returned. @@ -105,7 +106,7 @@ * Update the modification time and path of a node with * data from the file corresponding to the node. * - * Dir_AddDir Add a directory to a search path. + * SearchPath_Add Add a directory to a search path. * * SearchPath_ToFlags * Given a search path and a command flag, create @@ -137,7 +138,7 @@ #include "job.h" /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: dir.c,v 1.255 2021/01/10 21:20:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: dir.c,v 1.270 2021/02/05 05:48:19 rillig Exp $"); /* * A search path is a list of CachedDir structures. A CachedDir has in it the @@ -252,7 +253,7 @@ typedef enum CachedStatsFlags { } CachedStatsFlags; -SearchPath dirSearchPath = LST_INIT; /* main search path */ +SearchPath dirSearchPath = { LST_INIT }; /* main search path */ static OpenDirs openDirs; /* all cached directories */ @@ -484,18 +485,18 @@ Dir_Init(void) * Called by Dir_InitDir and whenever .CURDIR is assigned to. */ void -Dir_InitCur(const char *cdname) +Dir_InitCur(const char *newCurdir) { CachedDir *dir; - if (cdname == NULL) + if (newCurdir == NULL) return; /* * Our build directory is not the same as our source directory. * Keep this one around too. */ - dir = Dir_AddDir(NULL, cdname); + dir = SearchPath_Add(NULL, newCurdir); if (dir == NULL) return; @@ -511,7 +512,7 @@ Dir_InitDot(void) { CachedDir *dir; - dir = Dir_AddDir(NULL, "."); + dir = SearchPath_Add(NULL, "."); if (dir == NULL) { Error("Cannot open `.' (%s)", strerror(errno)); exit(2); /* Not 1 so -q can distinguish error */ @@ -548,37 +549,37 @@ Dir_SetPATH(void) CachedDirListNode *ln; Boolean seenDotLast = FALSE; /* true if we should search '.' last */ - Var_Delete(".PATH", VAR_GLOBAL); + Global_Delete(".PATH"); - if ((ln = dirSearchPath.first) != NULL) { + if ((ln = dirSearchPath.dirs.first) != NULL) { CachedDir *dir = ln->datum; if (dir == dotLast) { seenDotLast = TRUE; - Var_Append(".PATH", dotLast->name, VAR_GLOBAL); + Global_Append(".PATH", dotLast->name); } } if (!seenDotLast) { if (dot != NULL) - Var_Append(".PATH", dot->name, VAR_GLOBAL); + Global_Append(".PATH", dot->name); if (cur != NULL) - Var_Append(".PATH", cur->name, VAR_GLOBAL); + Global_Append(".PATH", cur->name); } - for (ln = dirSearchPath.first; ln != NULL; ln = ln->next) { + for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; if (dir == dotLast) continue; if (dir == dot && seenDotLast) continue; - Var_Append(".PATH", dir->name, VAR_GLOBAL); + Global_Append(".PATH", dir->name); } if (seenDotLast) { if (dot != NULL) - Var_Append(".PATH", dot->name, VAR_GLOBAL); + Global_Append(".PATH", dot->name); if (cur != NULL) - Var_Append(".PATH", cur->name, VAR_GLOBAL); + Global_Append(".PATH", cur->name); } } @@ -633,7 +634,9 @@ Dir_HasWildcards(const char *name) * This is incomplete -- wildcards are only expanded in the final path * component, but not in directories like src/lib*c/file*.c, but it * will do for now (now being 1993 until at least 2020). To expand these, - * use the ':sh' variable modifier such as in ${:!echo src/lib*c/file*.c!}. + * delegate the work to the shell, using the '!=' variable assignment + * operator, the ':sh' variable modifier or the ':!...!' variable modifier, + * such as in ${:!echo src/lib*c/file*.c!}. * * Input: * pattern Pattern to look for @@ -667,7 +670,8 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions) * directory cache. * * This means that the pattern '[a-z.]*' does not find - * '.file', which is consistent with bash, NetBSD sh and csh. + * '.file', which is consistent with NetBSD sh, NetBSD ksh, + * bash, dash, csh and probably many other shells as well. */ if (base[0] == '.' && pattern[0] != '.') continue; @@ -797,7 +801,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path, suffix, suffix_len); if (contains_wildcard(file)) { - Dir_Expand(file, path, expansions); + SearchPath_Expand(path, file, expansions); free(file); } else { Lst_Append(expansions, file); @@ -814,7 +818,7 @@ static void DirExpandPath(const char *word, SearchPath *path, StringList *expansions) { SearchPathNode *ln; - for (ln = path->first; ln != NULL; ln = ln->next) { + for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; DirMatchFiles(word, dir, expansions); } @@ -834,112 +838,121 @@ PrintExpansions(StringList *expansions) } /* - * Expand the given word into a list of words by globbing it, looking in the - * directories on the given search path. + * The wildcard isn't in the first component. + * Find all the components up to the one with the wildcard. + */ +static void +SearchPath_ExpandMiddle(SearchPath *path, const char *pattern, + const char *wildcardComponent, StringList *expansions) +{ + char *prefix, *dirpath, *end; + SearchPath *partPath; + + prefix = bmake_strsedup(pattern, wildcardComponent + 1); + /* + * XXX: Check the "the directory is added to the path" part. + * It is probably surprising that the directory before a + * wildcard gets added to the path. + */ + /* + * XXX: Only the first match of the prefix in the path is + * taken, any others are ignored. The expectation may be + * that the pattern is expanded in the whole path. + */ + dirpath = Dir_FindFile(prefix, path); + free(prefix); + + /* + * dirpath is null if can't find the leading component + * + * XXX: Dir_FindFile won't find internal components. i.e. if the + * path contains ../Etc/Object and we're looking for Etc, it won't + * be found. Ah well. Probably not important. + * + * XXX: Check whether the above comment is still true. + */ + if (dirpath == NULL) + return; + + end = &dirpath[strlen(dirpath) - 1]; + /* XXX: What about multiple trailing slashes? */ + if (*end == '/') + *end = '\0'; + + partPath = SearchPath_New(); + (void)SearchPath_Add(partPath, dirpath); + DirExpandPath(wildcardComponent + 1, partPath, expansions); + SearchPath_Free(partPath); +} + +/* + * Expand the given pattern into a list of existing filenames by globbing it, + * looking in each directory from the search path. * * Input: - * word the word to expand * path the directories in which to find the files + * pattern the pattern to expand * expansions the list on which to place the results */ void -Dir_Expand(const char *word, SearchPath *path, StringList *expansions) +SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions) { - const char *cp; + const char *brace, *slash, *wildcard, *wildcardComponent; assert(path != NULL); assert(expansions != NULL); - DEBUG1(DIR, "Expanding \"%s\"... ", word); + DEBUG1(DIR, "Expanding \"%s\"... ", pattern); - cp = strchr(word, '{'); - if (cp != NULL) { - DirExpandCurly(word, cp, path, expansions); + brace = strchr(pattern, '{'); + if (brace != NULL) { + DirExpandCurly(pattern, brace, path, expansions); goto done; } - /* At this point, the word does not contain '{'. */ + /* At this point, the pattern does not contain '{'. */ + + slash = strchr(pattern, '/'); + if (slash == NULL) { + /* The pattern has no directory component. */ - cp = strchr(word, '/'); - if (cp == NULL) { - /* The word has no directory component. */ /* First the files in dot. */ - DirMatchFiles(word, dot, expansions); - + DirMatchFiles(pattern, dot, expansions); /* Then the files in every other directory on the path. */ - DirExpandPath(word, path, expansions); + DirExpandPath(pattern, path, expansions); goto done; } - /* At this point, the word has a directory component. */ + /* At this point, the pattern has a directory component. */ - /* Find the first wildcard in the word. */ - for (cp = word; *cp != '\0'; cp++) - if (*cp == '?' || *cp == '[' || *cp == '*') + /* Find the first wildcard in the pattern. */ + for (wildcard = pattern; *wildcard != '\0'; wildcard++) + if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*') break; - if (*cp == '\0') { + if (*wildcard == '\0') { /* * No directory component and no wildcard at all -- this * should never happen as in such a simple case there is no * need to expand anything. */ - DirExpandPath(word, path, expansions); + DirExpandPath(pattern, path, expansions); goto done; } /* Back up to the start of the component containing the wildcard. */ /* XXX: This handles '///' and '/' differently. */ - while (cp > word && *cp != '/') - cp--; + wildcardComponent = wildcard; + while (wildcardComponent > pattern && *wildcardComponent != '/') + wildcardComponent--; - if (cp == word) { + if (wildcardComponent == pattern) { /* The first component contains the wildcard. */ /* Start the search from the local directory */ - DirExpandPath(word, path, expansions); - goto done; - } - - { - char *prefix = bmake_strsedup(word, cp + 1); - /* - * The wildcard isn't in the first component. - * Find all the components up to the one with the wildcard. - */ - /* - * XXX: Check the "the directory is added to the path" part. - * It is probably surprising that the directory before a - * wildcard gets added to the path. - */ - /* - * XXX: Only the first match of the prefix in the path is - * taken, any others are ignored. The expectation may be - * that the pattern is expanded in the whole path. - */ - char *dirpath = Dir_FindFile(prefix, path); - free(prefix); - - /* - * dirpath is null if can't find the leading component - * XXX: Dir_FindFile won't find internal components. - * i.e. if the path contains ../Etc/Object and we're - * looking for Etc, it won't be found. Ah well. - * Probably not important. - * XXX: Check whether the above comment is still true. - */ - if (dirpath != NULL) { - SearchPath *partPath; - - char *end = &dirpath[strlen(dirpath) - 1]; - /* XXX: What about multiple trailing slashes? */ - if (*end == '/') - *end = '\0'; - - partPath = SearchPath_New(); - (void)Dir_AddDir(partPath, dirpath); - DirExpandPath(cp + 1, partPath, expansions); - SearchPath_Free(partPath); - } + DirExpandPath(pattern, path, expansions); + } else { + SearchPath_ExpandMiddle(path, pattern, wildcardComponent, + expansions); } done: @@ -1051,6 +1064,115 @@ DirFindDot(const char *name, const char *base) return NULL; } +static Boolean +FindFileRelative(SearchPath *path, Boolean seenDotLast, + const char *name, char **out_file) +{ + SearchPathNode *ln; + Boolean checkedDot = FALSE; + char *file; + + DEBUG0(DIR, " Trying subdirectories...\n"); + + if (!seenDotLast) { + if (dot != NULL) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + goto found; + } + if (cur != NULL && + (file = DirLookupSubdir(cur, name)) != NULL) + goto found; + } + + for (ln = path->dirs.first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + if (dir == dotLast) + continue; + if (dir == dot) { + if (checkedDot) + continue; + checkedDot = TRUE; + } + if ((file = DirLookupSubdir(dir, name)) != NULL) + goto found; + } + + if (seenDotLast) { + if (dot != NULL && !checkedDot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + goto found; + } + if (cur != NULL && + (file = DirLookupSubdir(cur, name)) != NULL) + goto found; + } + + if (checkedDot) { + /* + * Already checked by the given name, since . was in + * the path, so no point in proceeding. + */ + DEBUG0(DIR, " Checked . already, returning NULL\n"); + file = NULL; + goto found; + } + + return FALSE; + +found: + *out_file = file; + return TRUE; +} + +static Boolean +FindFileAbsolute(SearchPath *path, Boolean const seenDotLast, + const char *const name, const char *const base, + char **out_file) +{ + char *file; + SearchPathNode *ln; + + /* + * For absolute names, compare directory path prefix against + * the the directory path of each member on the search path + * for an exact match. If we have an exact match on any member + * of the search path, use the cached contents of that member + * to lookup the final file component. If that lookup fails we + * can safely assume that the file does not exist at all. + * This is signified by DirLookupAbs() returning an empty + * string. + */ + DEBUG0(DIR, " Trying exact path matches...\n"); + + if (!seenDotLast && cur != NULL && + ((file = DirLookupAbs(cur, name, base)) != NULL)) + goto found; + + for (ln = path->dirs.first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + if (dir == dotLast) + continue; + if ((file = DirLookupAbs(dir, name, base)) != NULL) + goto found; + } + + if (seenDotLast && cur != NULL && + ((file = DirLookupAbs(cur, name, base)) != NULL)) + goto found; + + return FALSE; + +found: + if (file[0] == '\0') { + free(file); + file = NULL; + } + *out_file = file; + return TRUE; +} + /* * Find the file with the given name along the given search path. * @@ -1085,8 +1207,8 @@ Dir_FindFile(const char *name, SearchPath *path) return NULL; } - if (path->first != NULL) { - CachedDir *dir = path->first->datum; + if (path->dirs.first != NULL) { + CachedDir *dir = path->dirs.first->datum; if (dir == dotLast) { seenDotLast = TRUE; DEBUG0(DIR, "[dot last]..."); @@ -1105,7 +1227,7 @@ Dir_FindFile(const char *name, SearchPath *path) /* * We look through all the directories on the path seeking one * which contains the final component of the given name. If - * such a beast is found, we concatenate the directory name + * such a file is found, we concatenate the directory name * and the final component and return the resulting string. * If we don't find any such thing, we go on to phase two. * @@ -1119,7 +1241,7 @@ Dir_FindFile(const char *name, SearchPath *path) if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) return file; - for (ln = path->first; ln != NULL; ln = ln->next) { + for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; if (dir == dotLast) continue; @@ -1139,7 +1261,7 @@ Dir_FindFile(const char *name, SearchPath *path) * path. (eg. /usr/include and sys/types.h. The above search would * fail to turn up types.h in /usr/include, but it *is* in * /usr/include/sys/types.h). - * [ This no longer applies: If we find such a beast, we assume there + * [ This no longer applies: If we find such a file, we assume there * will be more (what else can we assume?) and add all but the last * component of the resulting name onto the search path (at the * end).] @@ -1157,100 +1279,11 @@ Dir_FindFile(const char *name, SearchPath *path) } if (name[0] != '/') { - SearchPathNode *ln; - Boolean checkedDot = FALSE; - - DEBUG0(DIR, " Trying subdirectories...\n"); - - if (!seenDotLast) { - if (dot != NULL) { - checkedDot = TRUE; - if ((file = DirLookupSubdir(dot, name)) != NULL) - return file; - } - if (cur != NULL && - (file = DirLookupSubdir(cur, name)) != NULL) - return file; - } - - for (ln = path->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - if (dir == dotLast) - continue; - if (dir == dot) { - if (checkedDot) - continue; - checkedDot = TRUE; - } - if ((file = DirLookupSubdir(dir, name)) != NULL) - return file; - } - - if (seenDotLast) { - if (dot != NULL && !checkedDot) { - checkedDot = TRUE; - if ((file = DirLookupSubdir(dot, name)) != NULL) - return file; - } - if (cur != NULL && - (file = DirLookupSubdir(cur, name)) != NULL) - return file; - } - - if (checkedDot) { - /* - * Already checked by the given name, since . was in - * the path, so no point in proceeding. - */ - DEBUG0(DIR, " Checked . already, returning NULL\n"); - return NULL; - } - - } else { /* name[0] == '/' */ - SearchPathNode *ln; - - /* - * For absolute names, compare directory path prefix against - * the the directory path of each member on the search path - * for an exact match. If we have an exact match on any member - * of the search path, use the cached contents of that member - * to lookup the final file component. If that lookup fails we - * can safely assume that the file does not exist at all. - * This is signified by DirLookupAbs() returning an empty - * string. - */ - DEBUG0(DIR, " Trying exact path matches...\n"); - - if (!seenDotLast && cur != NULL && - ((file = DirLookupAbs(cur, name, base)) != NULL)) { - if (file[0] == '\0') { - free(file); - return NULL; - } + if (FindFileRelative(path, seenDotLast, name, &file)) return file; - } - - for (ln = path->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - if (dir == dotLast) - continue; - if ((file = DirLookupAbs(dir, name, base)) != NULL) { - if (file[0] == '\0') { - free(file); - return NULL; - } - return file; - } - } - - if (seenDotLast && cur != NULL && - ((file = DirLookupAbs(cur, name, base)) != NULL)) { - if (file[0] == '\0') { - free(file); - return NULL; - } + } else { + if (FindFileAbsolute(path, seenDotLast, name, base, &file)) return file; - } } /* @@ -1281,7 +1314,7 @@ Dir_FindFile(const char *name, SearchPath *path) base++; } prefix = bmake_strsedup(name, base - 1); - (void)Dir_AddDir(path, prefix); + (void)SearchPath_Add(path, prefix); free(prefix); bigmisses++; @@ -1511,7 +1544,7 @@ CacheNewDir(const char *name, SearchPath *path) OpenDirs_Add(&openDirs, dir); if (path != NULL) - Lst_Append(path, CachedDir_Ref(dir)); + Lst_Append(&path->dirs, CachedDir_Ref(dir)); DEBUG1(DIR, "Caching %s done\n", name); return dir; @@ -1536,28 +1569,28 @@ CacheNewDir(const char *name, SearchPath *path) * Lst_Append and CachedDir_Ref. */ CachedDir * -Dir_AddDir(SearchPath *path, const char *name) +SearchPath_Add(SearchPath *path, const char *name) { if (path != NULL && strcmp(name, ".DOTLAST") == 0) { SearchPathNode *ln; /* XXX: Linear search gets slow with thousands of entries. */ - for (ln = path->first; ln != NULL; ln = ln->next) { + for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *pathDir = ln->datum; if (strcmp(pathDir->name, name) == 0) return pathDir; } - Lst_Prepend(path, CachedDir_Ref(dotLast)); + Lst_Prepend(&path->dirs, CachedDir_Ref(dotLast)); } if (path != NULL) { /* XXX: Why is OpenDirs only checked if path != NULL? */ CachedDir *dir = OpenDirs_Find(&openDirs, name); if (dir != NULL) { - if (Lst_FindDatum(path, dir) == NULL) - Lst_Append(path, CachedDir_Ref(dir)); + if (Lst_FindDatum(&path->dirs, dir) == NULL) + Lst_Append(&path->dirs, CachedDir_Ref(dir)); return dir; } } @@ -1574,9 +1607,9 @@ Dir_CopyDirSearchPath(void) { SearchPath *path = SearchPath_New(); SearchPathNode *ln; - for (ln = dirSearchPath.first; ln != NULL; ln = ln->next) { + for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; - Lst_Append(path, CachedDir_Ref(dir)); + Lst_Append(&path->dirs, CachedDir_Ref(dir)); } return path; } @@ -1596,7 +1629,7 @@ Dir_CopyDirSearchPath(void) * don't go well. */ char * -SearchPath_ToFlags(const char *flag, SearchPath *path) +SearchPath_ToFlags(SearchPath *path, const char *flag) { Buffer buf; SearchPathNode *ln; @@ -1604,7 +1637,7 @@ SearchPath_ToFlags(const char *flag, SearchPath *path) Buf_Init(&buf); if (path != NULL) { - for (ln = path->first; ln != NULL; ln = ln->next) { + for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; Buf_AddStr(&buf, " "); Buf_AddStr(&buf, flag); @@ -1612,7 +1645,7 @@ SearchPath_ToFlags(const char *flag, SearchPath *path) } } - return Buf_Destroy(&buf, FALSE); + return Buf_DoneData(&buf); } /* Free the search path and all directories mentioned in it. */ @@ -1621,11 +1654,12 @@ SearchPath_Free(SearchPath *path) { SearchPathNode *ln; - for (ln = path->first; ln != NULL; ln = ln->next) { + for (ln = path->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; CachedDir_Unref(dir); } - Lst_Free(path); + Lst_Done(&path->dirs); + free(path); } /* @@ -1635,8 +1669,8 @@ SearchPath_Free(SearchPath *path) void SearchPath_Clear(SearchPath *path) { - while (!Lst_IsEmpty(path)) { - CachedDir *dir = Lst_Dequeue(path); + while (!Lst_IsEmpty(&path->dirs)) { + CachedDir *dir = Lst_Dequeue(&path->dirs); CachedDir_Unref(dir); } } @@ -1651,10 +1685,10 @@ SearchPath_AddAll(SearchPath *dst, SearchPath *src) { SearchPathNode *ln; - for (ln = src->first; ln != NULL; ln = ln->next) { + for (ln = src->dirs.first; ln != NULL; ln = ln->next) { CachedDir *dir = ln->datum; - if (Lst_FindDatum(dst, dir) == NULL) - Lst_Append(dst, CachedDir_Ref(dir)); + if (Lst_FindDatum(&dst->dirs, dir) == NULL) + Lst_Append(&dst->dirs, CachedDir_Ref(dir)); } } @@ -1685,11 +1719,11 @@ Dir_PrintDirectories(void) } void -SearchPath_Print(SearchPath *path) +SearchPath_Print(const SearchPath *path) { SearchPathNode *ln; - for (ln = path->first; ln != NULL; ln = ln->next) { + for (ln = path->dirs.first; ln != NULL; ln = ln->next) { const CachedDir *dir = ln->datum; debug_printf("%s ", dir->name); } diff --git a/dir.h b/dir.h index 9d9002b166be..f4bf32ab5b83 100644 --- a/dir.h +++ b/dir.h @@ -1,4 +1,4 @@ -/* $NetBSD: dir.h,v 1.40 2020/12/01 19:28:32 rillig Exp $ */ +/* $NetBSD: dir.h,v 1.43 2021/02/05 05:48:19 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -83,16 +83,16 @@ void Dir_InitDot(void); void Dir_End(void); void Dir_SetPATH(void); Boolean Dir_HasWildcards(const char *); -void Dir_Expand(const char *, SearchPath *, StringList *); +void SearchPath_Expand(SearchPath *, const char *, StringList *); char *Dir_FindFile(const char *, SearchPath *); char *Dir_FindHereOrAbove(const char *, const char *); void Dir_UpdateMTime(GNode *, Boolean); -CachedDir *Dir_AddDir(SearchPath *, const char *); -char *SearchPath_ToFlags(const char *, SearchPath *); +CachedDir *SearchPath_Add(SearchPath *, const char *); +char *SearchPath_ToFlags(SearchPath *, const char *); void SearchPath_Clear(SearchPath *); void SearchPath_AddAll(SearchPath *, SearchPath *); void Dir_PrintDirectories(void); -void SearchPath_Print(SearchPath *); +void SearchPath_Print(const SearchPath *); SearchPath *Dir_CopyDirSearchPath(void); /* Stripped-down variant of struct stat. */ diff --git a/enum.c b/enum.c index f08b4edd978d..a0ae8569b322 100755 --- a/enum.c +++ b/enum.c @@ -1,4 +1,4 @@ -/* $NetBSD: enum.c,v 1.14 2021/01/09 16:06:09 rillig Exp $ */ +/* $NetBSD: enum.c,v 1.15 2021/02/02 17:56:31 rillig Exp $ */ /* Copyright (c) 2020 Roland Illig @@ -29,7 +29,7 @@ #include "make.h" -MAKE_RCSID("$NetBSD: enum.c,v 1.14 2021/01/09 16:06:09 rillig Exp $"); +MAKE_RCSID("$NetBSD: enum.c,v 1.15 2021/02/02 17:56:31 rillig Exp $"); /* * Convert a bitset into a string representation, showing the names of the @@ -78,15 +78,3 @@ Enum_FlagsToString(char *buf, size_t buf_size, buf[0] = '\0'; return buf_start; } - -/* Convert a fixed-value enum into a string representation. */ -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; - } - abort(/* unknown enum value */); - /*NOTREACHED*/ -} diff --git a/enum.h b/enum.h index 6707214e8b0d..b5f4630414ef 100755 --- a/enum.h +++ b/enum.h @@ -1,4 +1,4 @@ -/* $NetBSD: enum.h,v 1.14 2020/12/30 10:03:16 rillig Exp $ */ +/* $NetBSD: enum.h,v 1.18 2021/02/02 21:26:51 rillig Exp $ */ /* Copyright (c) 2020 Roland Illig @@ -39,8 +39,9 @@ typedef struct EnumToStringSpec { const char *es_name; } EnumToStringSpec; + const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *); -const char *Enum_ValueToString(int, const EnumToStringSpec *); + /* For Enum_FlagsToString, the separator between flags. */ #define ENUM__SEP "|" @@ -100,14 +101,16 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *); #define ENUM__SPECS_5(part1, part2, part3, part4, part5) \ { part1, part2, part3, part4, part5, { 0, "" } } -/* Declare the necessary data structures for calling Enum_ValueToString. */ -#define ENUM__VALUE_RTTI(typnam, specs) \ - static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs /* Declare the necessary data structures for calling Enum_FlagsToString. */ #define ENUM__FLAGS_RTTI(typnam, specs, joined) \ static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \ - enum { typnam ## _ ## ToStringSize = sizeof joined } + enum { typnam ## _ ## ToStringSize = sizeof (joined) }; \ + MAKE_INLINE const char *typnam ## _ToString(char *buf, typnam value) \ + { return Enum_FlagsToString(buf, typnam ## _ ## ToStringSize, \ + value, typnam ## _ ## ToStringSpecs); \ + } \ + extern void enum_flags_rtti_dummy(void) /* * Declare the necessary data structures for calling Enum_FlagsToString @@ -174,28 +177,18 @@ const char *Enum_ValueToString(int, const EnumToStringSpec *); ENUM__JOIN_STR_4(v1, v2, v3, v4), \ ENUM__JOIN_STR_4(v5, v6, v7, v8))) -/* - * Declare the necessary data structures for calling Enum_ValueToString - * for an enum with 8 constants. - */ -#define ENUM_VALUE_RTTI_8(typnam, v1, v2, v3, v4, v5, v6, v7, v8) \ - ENUM__VALUE_RTTI(typnam, \ - ENUM__SPECS_2( \ - ENUM__SPEC_4(v1, v2, v3, v4), \ - ENUM__SPEC_4(v5, v6, v7, v8))) - /* * Declare the necessary data structures for calling Enum_FlagsToString - * for an enum with 10 flags. + * for an enum with 9 flags. */ -#define ENUM_FLAGS_RTTI_10(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10) \ +#define ENUM_FLAGS_RTTI_9(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9) \ ENUM__FLAGS_RTTI(typnam, \ ENUM__SPECS_2( \ ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8), \ - ENUM__SPEC_2(v9, v10)), \ + ENUM__SPEC_1(v9)), \ ENUM__JOIN_2( \ ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \ - ENUM__JOIN_STR_2(v9, v10))) + ENUM__JOIN_STR_1(v9))) /* * Declare the necessary data structures for calling Enum_FlagsToString diff --git a/filemon/filemon.h b/filemon/filemon.h index 4bd11bbde608..139d62f1a1f6 100644 --- a/filemon/filemon.h +++ b/filemon/filemon.h @@ -1,6 +1,6 @@ -/* $NetBSD: filemon.h,v 1.4 2020/11/29 09:27:40 rillig Exp $ */ +/* $NetBSD: filemon.h,v 1.5 2021/01/19 20:51:46 rillig Exp $ */ -/*- +/* * Copyright (c) 2019 The NetBSD Foundation, Inc. * All rights reserved. * diff --git a/filemon/filemon_dev.c b/filemon/filemon_dev.c index e4c583417b51..728d84c1f492 100644 --- a/filemon/filemon_dev.c +++ b/filemon/filemon_dev.c @@ -1,6 +1,6 @@ -/* $NetBSD: filemon_dev.c,v 1.6 2020/11/29 09:27:40 rillig Exp $ */ +/* $NetBSD: filemon_dev.c,v 1.8 2021/02/01 21:09:25 rillig Exp $ */ -/*- +/* * Copyright (c) 2020 The NetBSD Foundation, Inc. * All rights reserved. * @@ -39,7 +39,7 @@ #include #ifdef HAVE_FILEMON_H -# include +# include #endif #ifndef _PATH_FILEMON diff --git a/filemon/filemon_ktrace.c b/filemon/filemon_ktrace.c index 699d66bad9f3..1abef7e78af1 100644 --- a/filemon/filemon_ktrace.c +++ b/filemon/filemon_ktrace.c @@ -1,6 +1,6 @@ -/* $NetBSD: filemon_ktrace.c,v 1.12 2021/01/10 23:59:53 rillig Exp $ */ +/* $NetBSD: filemon_ktrace.c,v 1.14 2021/02/01 21:34:41 rillig Exp $ */ -/*- +/* * Copyright (c) 2019 The NetBSD Foundation, Inc. * All rights reserved. * @@ -761,6 +761,8 @@ filemon_sys_chdir(struct filemon *F, const struct filemon_key *key, return syscall_enter(key, call, 1, &show_chdir); } +/* TODO: monitor fchdir as well */ + /*ARGSUSED*/ static struct filemon_state * filemon_sys_execve(struct filemon *F, const struct filemon_key *key, @@ -832,6 +834,11 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key, const register_t *args = (const void *)&call[1]; int flags, fd; + /* + * XXX: In the .meta log, the base directory is missing, which makes + * all references to relative pathnames useless. + */ + if (call->ktr_argsize < 3) return NULL; fd = (int)args[0]; @@ -860,6 +867,8 @@ filemon_sys_openat(struct filemon *F, const struct filemon_key *key, } } +/* TODO: monitor the other *at syscalls as well, not only openat. */ + /*ARGSUSED*/ static struct filemon_state * filemon_sys_symlink(struct filemon *F, const struct filemon_key *key, diff --git a/for.c b/for.c index af55179dee8d..5705d9c5de0d 100644 --- a/for.c +++ b/for.c @@ -1,4 +1,4 @@ -/* $NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $ */ /* * Copyright (c) 1992, The Regents of the University of California. @@ -29,7 +29,7 @@ * SUCH DAMAGE. */ -/*- +/* * Handling of .for/.endfor loops in a makefile. * * For loops have the form: @@ -58,9 +58,8 @@ #include "make.h" /* "@(#)for.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: for.c,v 1.134 2021/01/10 21:20:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: for.c,v 1.141 2021/02/04 21:33:13 rillig Exp $"); -static int forLevel = 0; /* Nesting level */ /* One of the variables to the left of the "in" in a .for loop. */ typedef struct ForVar { @@ -68,10 +67,7 @@ typedef struct ForVar { size_t nameLen; } ForVar; -/* - * State of a for loop. - */ -typedef struct For { +typedef struct ForLoop { Buffer body; /* Unexpanded body of the loop */ Vector /* of ForVar */ vars; /* Iteration variables */ Words items; /* Substitution items */ @@ -81,22 +77,33 @@ typedef struct For { * only ${V} and $(V). */ Boolean short_var; unsigned int sub_next; /* Where to continue iterating */ -} For; +} ForLoop; -static For *accumFor; /* Loop being accumulated */ -static void -ForAddVar(For *f, const char *name, size_t len) +static ForLoop *accumFor; /* Loop being accumulated */ +static int forLevel = 0; /* Nesting level */ + + +static ForLoop * +ForLoop_New(void) { - ForVar *var = Vector_Push(&f->vars); - var->name = bmake_strldup(name, len); - var->nameLen = len; + ForLoop *f = bmake_malloc(sizeof *f); + + Buf_Init(&f->body); + Vector_Init(&f->vars, sizeof(ForVar)); + f->items.words = NULL; + f->items.freeIt = NULL; + Buf_Init(&f->curBody); + f->short_var = FALSE; + f->sub_next = 0; + + return f; } static void -For_Free(For *f) +ForLoop_Free(ForLoop *f) { - Buf_Destroy(&f->body, TRUE); + Buf_Done(&f->body); while (f->vars.len > 0) { ForVar *var = Vector_Pop(&f->vars); @@ -105,11 +112,89 @@ For_Free(For *f) Vector_Done(&f->vars); Words_Free(f->items); - Buf_Destroy(&f->curBody, TRUE); + Buf_Done(&f->curBody); free(f); } +static void +ForLoop_AddVar(ForLoop *f, const char *name, size_t len) +{ + ForVar *var = Vector_Push(&f->vars); + var->name = bmake_strldup(name, len); + var->nameLen = len; +} + +static Boolean +ForLoop_ParseVarnames(ForLoop *f, const char **pp) +{ + const char *p = *pp; + + for (;;) { + size_t len; + + cpp_skip_whitespace(&p); + if (*p == '\0') { + Parse_Error(PARSE_FATAL, "missing `in' in for"); + return FALSE; + } + + /* + * XXX: This allows arbitrary variable names; + * see directive-for.mk. + */ + for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) + continue; + + if (len == 2 && p[0] == 'i' && p[1] == 'n') { + p += 2; + break; + } + if (len == 1) + f->short_var = TRUE; + + ForLoop_AddVar(f, p, len); + p += len; + } + + if (f->vars.len == 0) { + Parse_Error(PARSE_FATAL, "no iteration variables in for"); + return FALSE; + } + + *pp = p; + return TRUE; +} + +static Boolean +ForLoop_ParseItems(ForLoop *f, const char *p) +{ + char *items; + + cpp_skip_whitespace(&p); + + if (Var_Subst(p, SCOPE_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { + Parse_Error(PARSE_FATAL, "Error in .for loop items"); + return FALSE; + } + + f->items = Str_Words(items, FALSE); + free(items); + + if (f->items.len == 1 && f->items.words[0][0] == '\0') + f->items.len = 0; /* .for var in ${:U} */ + + if (f->items.len != 0 && f->items.len % f->vars.len != 0) { + Parse_Error(PARSE_FATAL, + "Wrong number of words (%u) in .for " + "substitution list with %u variables", + (unsigned)f->items.len, (unsigned)f->vars.len); + return FALSE; + } + + return TRUE; +} + static Boolean IsFor(const char *p) { @@ -138,7 +223,7 @@ IsEndfor(const char *p) int For_Eval(const char *line) { - For *f; + ForLoop *f; const char *p; p = line + 1; /* skip the '.' */ @@ -153,113 +238,41 @@ For_Eval(const char *line) } p += 3; - /* - * we found a for loop, and now we are going to parse it. - */ + f = ForLoop_New(); - f = bmake_malloc(sizeof *f); - Buf_Init(&f->body); - Vector_Init(&f->vars, sizeof(ForVar)); - f->items.words = NULL; - f->items.freeIt = NULL; - Buf_Init(&f->curBody); - f->short_var = FALSE; - f->sub_next = 0; - - /* Grab the variables. Terminate on "in". */ - for (;;) { - size_t len; - - cpp_skip_whitespace(&p); - if (*p == '\0') { - Parse_Error(PARSE_FATAL, "missing `in' in for"); - For_Free(f); - return -1; - } - - /* - * XXX: This allows arbitrary variable names; - * see directive-for.mk. - */ - for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++) - continue; - - if (len == 2 && p[0] == 'i' && p[1] == 'n') { - p += 2; - break; - } - if (len == 1) - f->short_var = TRUE; - - ForAddVar(f, p, len); - p += len; - } - - if (f->vars.len == 0) { - Parse_Error(PARSE_FATAL, "no iteration variables in for"); - For_Free(f); + if (!ForLoop_ParseVarnames(f, &p)) { + ForLoop_Free(f); return -1; } - cpp_skip_whitespace(&p); - - { - char *items; - if (Var_Subst(p, VAR_GLOBAL, VARE_WANTRES, &items) != VPR_OK) { - Parse_Error(PARSE_FATAL, "Error in .for loop items"); - f->items.len = 0; - goto done; - } - - f->items = Str_Words(items, FALSE); - free(items); - - if (f->items.len == 1 && f->items.words[0][0] == '\0') - f->items.len = 0; /* .for var in ${:U} */ + if (!ForLoop_ParseItems(f, p)) { + /* Continue parsing the .for loop, but don't iterate. */ + f->items.len = 0; } - { - size_t nitems, nvars; - - if ((nitems = f->items.len) > 0 && - nitems % (nvars = f->vars.len) != 0) { - Parse_Error(PARSE_FATAL, - "Wrong number of words (%u) in .for " - "substitution list with %u variables", - (unsigned)nitems, (unsigned)nvars); - /* - * Return 'success' so that the body of the .for loop - * is accumulated. - * Remove all items so that the loop doesn't iterate. - */ - f->items.len = 0; - } - } - -done: accumFor = f; forLevel = 1; return 1; } /* - * Add another line to a .for loop. + * Add another line to the .for loop that is being built up. * Returns FALSE when the matching .endfor is reached. */ Boolean For_Accum(const char *line) { - const char *ptr = line; + const char *p = line; - if (*ptr == '.') { - ptr++; - cpp_skip_whitespace(&ptr); + if (*p == '.') { + p++; + cpp_skip_whitespace(&p); - if (IsEndfor(ptr)) { + if (IsEndfor(p)) { DEBUG1(FOR, "For: end for %d\n", forLevel); if (--forLevel <= 0) return FALSE; - } else if (IsFor(ptr)) { + } else if (IsFor(p)) { forLevel++; DEBUG1(FOR, "For: new loop %d\n", forLevel); } @@ -307,11 +320,11 @@ for_var_len(const char *var) * that characters that break this syntax must be backslash-escaped. */ static Boolean -NeedsEscapes(const char *word, char endc) +NeedsEscapes(const char *value, char endc) { const char *p; - for (p = word; *p != '\0'; p++) { + for (p = value; *p != '\0'; p++) { if (*p == ':' || *p == '$' || *p == '\\' || *p == endc) return TRUE; } @@ -356,8 +369,8 @@ Buf_AddEscaped(Buffer *cmds, const char *item, char endc) * expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue". */ static void -SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc, - const char **inout_mark) +ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd, + char endc, const char **inout_mark) { size_t i; const char *p = *pp; @@ -397,7 +410,7 @@ SubstVarLong(For *f, const char **pp, const char *bodyEnd, char endc, * variable expressions like $i with their ${:U...} expansion. */ static void -SubstVarShort(For *f, const char *p, const char **inout_mark) +ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark) { const char ch = *p; ForVar *vars; @@ -432,12 +445,12 @@ SubstVarShort(For *f, const char *p, const char **inout_mark) * This code assumes that the variable with the empty name will never be * defined, see unit-tests/varname-empty.mk for more details. * - * The detection of substitutions of the loop control variable is naive. - * Many of the modifiers use \ to escape $ (not $) so it is possible + * The detection of substitutions of the loop control variables is naive. + * Many of the modifiers use '\' to escape '$' (not '$'), so it is possible * to contrive a makefile where an unwanted substitution happens. */ static void -ForSubstBody(For *f) +ForLoop_SubstBody(ForLoop *f) { const char *p, *bodyEnd; const char *mark; /* where the last replacement left off */ @@ -449,10 +462,10 @@ ForSubstBody(For *f) for (p = mark; (p = strchr(p, '$')) != NULL;) { if (p[1] == '{' || p[1] == '(') { p += 2; - SubstVarLong(f, &p, bodyEnd, p[-1] == '{' ? '}' : ')', - &mark); + ForLoop_SubstVarLong(f, &p, bodyEnd, + p[-1] == '{' ? '}' : ')', &mark); } else if (p[1] != '\0') { - SubstVarShort(f, p + 1, &mark); + ForLoop_SubstVarShort(f, p + 1, &mark); p += 2; } else break; @@ -468,15 +481,15 @@ ForSubstBody(For *f) static char * ForReadMore(void *v_arg, size_t *out_len) { - For *f = v_arg; + ForLoop *f = v_arg; if (f->sub_next == f->items.len) { /* No more iterations */ - For_Free(f); + ForLoop_Free(f); return NULL; } - ForSubstBody(f); + ForLoop_SubstBody(f); DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data); f->sub_next += (unsigned int)f->vars.len; @@ -488,7 +501,7 @@ ForReadMore(void *v_arg, size_t *out_len) void For_Run(int lineno) { - For *f = accumFor; + ForLoop *f = accumFor; accumFor = NULL; if (f->items.len == 0) { @@ -496,7 +509,7 @@ For_Run(int lineno) * Nothing to expand - possibly due to an earlier syntax * error. */ - For_Free(f); + ForLoop_Free(f); return; } diff --git a/hash.c b/hash.c index 82735f59c6fd..3afc4ac7ec4e 100644 --- a/hash.c +++ b/hash.c @@ -1,4 +1,4 @@ -/* $NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $ */ +/* $NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -74,7 +74,7 @@ #include "make.h" /* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $"); +MAKE_RCSID("$NetBSD: hash.c,v 1.61 2021/02/01 17:32:10 rillig Exp $"); /* * The ratio of # entries to # buckets at which we rebuild the table to @@ -82,7 +82,7 @@ MAKE_RCSID("$NetBSD: hash.c,v 1.60 2020/12/30 10:03:16 rillig Exp $"); */ #define rebuildLimit 3 -/* This hash function matches Gosling's emacs and java.lang.String. */ +/* This hash function matches Gosling's Emacs and java.lang.String. */ static unsigned int hash(const char *key, size_t *out_keylen) { @@ -142,7 +142,10 @@ HashTable_Init(HashTable *t) t->maxchain = 0; } -/* Remove everything from the hash table and frees up the memory. */ +/* + * Remove everything from the hash table and free up the memory for the keys + * of the hash table, but not for the values associated to these keys. + */ void HashTable_Done(HashTable *t) { @@ -157,8 +160,8 @@ HashTable_Done(HashTable *t) he = next; } } - free(t->buckets); + free(t->buckets); #ifdef CLEANUP t->buckets = NULL; #endif diff --git a/job.c b/job.c index 2a43a64e9749..3ba67c69540f 100644 --- a/job.c +++ b/job.c @@ -1,4 +1,4 @@ -/* $NetBSD: job.c,v 1.397 2021/01/10 23:59:53 rillig Exp $ */ +/* $NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -69,14 +69,14 @@ * SUCH DAMAGE. */ -/*- +/* * job.c -- * handle the creation etc. of our child processes. * * Interface: * Job_Init Called to initialize this module. In addition, - * any commands attached to the .BEGIN target - * are executed before this function returns. + * the .BEGIN target is made including all of its + * dependencies before this function returns. * Hence, the makefiles must have been parsed * before this function is called. * @@ -99,9 +99,9 @@ * a time given by the SEL_* constants, below, * or until output is ready. * - * Job_ParseShell Given the line following a .SHELL target, parse - * the line as a shell specification. Returns - * FALSE if the spec was incorrect. + * Job_ParseShell Given a special dependency line with target '.SHELL', + * define the shell that is used for the creation + * commands in jobs mode. * * Job_Finish Perform any final processing which needs doing. * This includes the execution of any commands @@ -109,10 +109,9 @@ * target. It should only be called when the * job table is empty. * - * Job_AbortAll Abort all currently running jobs. It doesn't - * handle output or do anything for the jobs, - * just kills them. It should only be called in - * an emergency. + * Job_AbortAll Abort all currently running jobs. Do not handle + * output or do anything for the jobs, just kill them. + * Should only be called in an emergency. * * Job_CheckCommands * Verify that the commands for a target are @@ -156,7 +155,7 @@ #include "trace.h" /* "@(#)job.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: job.c,v 1.397 2021/01/10 23:59:53 rillig Exp $"); +MAKE_RCSID("$NetBSD: job.c,v 1.420 2021/02/05 22:15:44 sjg Exp $"); /* * A shell defines how the commands are run. All commands for a target are @@ -177,15 +176,15 @@ MAKE_RCSID("$NetBSD: job.c,v 1.397 2021/01/10 23:59:53 rillig Exp $"); * The error checking for individual commands is controlled using hasErrCtl, * errOn, errOff and runChkTmpl. * - * If a shell doesn't have error control, echoTmpl becomes a printf template - * for echoing the command, should echoing be on; runIgnTmpl becomes - * another printf template for executing the command while ignoring the return + * In case a shell doesn't have error control, echoTmpl is a printf template + * for echoing the command, should echoing be on; runIgnTmpl is another + * printf template for executing the command while ignoring the return * status. Finally runChkTmpl is a printf template for running the command and * causing the shell to exit on error. If any of these strings are empty when * hasErrCtl is FALSE, the command will be executed anyway as is, and if it * causes an error, so be it. Any templates set up to echo the command will - * escape any '$ ` \ "' characters in the command string to avoid common - * problems with echo "%s\n" as a template. + * escape any '$ ` \ "' characters in the command string to avoid unwanted + * shell code injection, the escaped command is safe to use in double quotes. * * The command-line flags "echo" and "exit" also control the behavior. The * "echo" flag causes the shell to start echoing commands right away. The @@ -230,7 +229,7 @@ typedef struct Shell { } Shell; typedef struct CommandFlags { - /* Whether to echo the command before running it. */ + /* Whether to echo the command before or instead of running it. */ Boolean echo; /* Run the command even in -n or -N mode. */ @@ -266,6 +265,7 @@ typedef enum AbortReason { /* why is the make aborting? */ ABORT_ERROR, /* Because of an error */ ABORT_INTERRUPT, /* Because it was interrupted */ ABORT_WAIT /* Waiting for jobs to finish */ + /* XXX: "WAIT" is not a _reason_ for aborting, it's rather a status. */ } AbortReason; static AbortReason aborting = ABORT_NONE; #define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */ @@ -420,7 +420,7 @@ static Shell *shell = &shells[DEFSHELL_INDEX]; const char *shellPath = NULL; /* full pathname of executable image */ const char *shellName = NULL; /* last component of shellPath */ char *shellErrFlag = NULL; -static char *shellArgv = NULL; /* Custom shell args */ +static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */ static Job *job_table; /* The structures that describe them */ @@ -434,8 +434,8 @@ static Boolean make_suspended = FALSE; /* Whether we've seen a SIGTSTP (etc) */ * the output channels of children */ static struct pollfd *fds = NULL; -static Job **allJobs = NULL; -static nfds_t nJobs = 0; +static Job **jobByFdIndex = NULL; +static nfds_t fdsLen = 0; static void watchfd(Job *); static void clearfd(Job *); static Boolean readyfd(Job *); @@ -452,6 +452,7 @@ enum { }; static sigset_t caught_signals; /* Set of signals we handle */ +static volatile sig_atomic_t caught_sigchld; static void JobDoOutput(Job *, Boolean); static void JobInterrupt(Boolean, int) MAKE_ATTR_DEAD; @@ -616,6 +617,7 @@ JobCondPassSig(int signo) static void JobChildSig(int signo MAKE_ATTR_UNUSED) { + caught_sigchld = 1; while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && errno == EAGAIN) continue; @@ -1002,7 +1004,7 @@ JobPrintCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd) * Print all commands to the shell file that is later executed. * * The special command "..." stops printing and saves the remaining commands - * to be executed later. + * to be executed later, when the target '.END' is made. * * Return whether at least one command was written to the shell file. */ @@ -1029,7 +1031,10 @@ JobPrintCommands(Job *job) return seen; } -/* Save the delayed commands, to be executed when everything else is done. */ +/* + * Save the delayed commands (those after '...'), to be executed later in + * the '.END' node, when everything else is done. + */ static void JobSaveCommands(Job *job) { @@ -1038,9 +1043,11 @@ JobSaveCommands(Job *job) for (ln = job->tailCmds; ln != NULL; ln = ln->next) { const char *cmd = ln->datum; char *expanded_cmd; - /* XXX: This Var_Subst is only intended to expand the dynamic + /* + * XXX: This Var_Subst is only intended to expand the dynamic * variables such as .TARGET, .IMPSRC. It is not intended to - * expand the other variables as well; see deptgt-end.mk. */ + * expand the other variables as well; see deptgt-end.mk. + */ (void)Var_Subst(cmd, job->node, VARE_WANTRES, &expanded_cmd); /* TODO: handle errors */ Lst_Append(&Targ_GetEndNode()->commands, expanded_cmd); @@ -1322,7 +1329,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) * .DEFAULT itself. */ Make_HandleUse(defaultNode, gn); - Var_Set(IMPSRC, GNode_VarTarget(gn), gn); + Var_Set(gn, IMPSRC, GNode_VarTarget(gn)); return TRUE; } @@ -1404,7 +1411,7 @@ JobExec(Job *job, char **argv) Var_ReexportVars(); - cpid = vFork(); + cpid = vfork(); if (cpid == -1) Punt("Cannot vfork: %s", strerror(errno)); @@ -1479,12 +1486,12 @@ JobExec(Job *job, char **argv) #if defined(HAVE_SETPGID) (void)setpgid(0, getpid()); #else -#if defined(HAVE_SETSID) +# if defined(HAVE_SETSID) /* XXX: dsl - I'm sure this should be setpgrp()... */ (void)setsid(); -#else +# else (void)setpgrp(0, getpid()); -#endif +# endif #endif (void)execv(shellPath, argv); @@ -1515,9 +1522,7 @@ JobExec(Job *job, char **argv) job->cmdFILE = NULL; } - /* - * Now the job is actually running, add it to the table. - */ + /* Now that the job is actually running, add it to the table. */ if (DEBUG(JOB)) { debug_printf("JobExec(%s): pid %d added to jobs table\n", job->node->name, job->pid); @@ -1572,15 +1577,14 @@ JobMakeArgv(Job *job, char **argv) } static void -JobOpenTmpFile(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run) +JobWriteShellCommands(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run) { /* * tfile is the name of a file into which all shell commands * are put. It is removed before the child shell is executed, * unless DEBUG(SCRIPT) is set. */ - char *tfile; - sigset_t mask; + char tfile[MAXPATHLEN]; int tfd; /* File descriptor to the temp file */ /* @@ -1592,22 +1596,13 @@ JobOpenTmpFile(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run) DieHorribly(); } - JobSigLock(&mask); - tfd = mkTempFile(TMPPAT, &tfile); - if (!DEBUG(SCRIPT)) - (void)eunlink(tfile); - JobSigUnlock(&mask); + tfd = Job_TempFile(TMPPAT, tfile, sizeof tfile); job->cmdFILE = fdopen(tfd, "w+"); if (job->cmdFILE == NULL) Punt("Could not fdopen %s", tfile); (void)fcntl(fileno(job->cmdFILE), F_SETFD, FD_CLOEXEC); - /* - * Send the commands to the command file, flush all its - * buffers then rewind and remove the thing. - */ - *out_run = TRUE; #ifdef USE_META if (useMeta) { @@ -1617,11 +1612,7 @@ JobOpenTmpFile(Job *job, GNode *gn, Boolean cmdsOK, Boolean *out_run) } #endif - /* We can do all the commands at once. hooray for sanity */ - if (!JobPrintCommands(job)) - *out_run = FALSE; - - free(tfile); + *out_run = JobPrintCommands(job); } /* @@ -1675,46 +1666,36 @@ JobStart(GNode *gn, Boolean special) cmdsOK = Job_CheckCommands(gn, Error); job->inPollfd = NULL; - /* - * If the -n flag wasn't given, we open up OUR (not the child's) - * temporary file to stuff commands in it. The thing is rd/wr so - * we don't need to reopen it to feed it to the shell. If the -n - * flag *was* given, we just set the file to be stdout. Cute, huh? - */ - if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) || + + if (Lst_IsEmpty(&gn->commands)) { + job->cmdFILE = stdout; + run = FALSE; + } else if (((gn->type & OP_MAKE) && !opts.noRecursiveExecute) || (!opts.noExecute && !opts.touchFlag)) { - JobOpenTmpFile(job, gn, cmdsOK, &run); + /* + * The above condition looks very similar to + * GNode_ShouldExecute but is subtly different. It prevents + * that .MAKE targets are touched since these are usually + * virtual targets. + */ + + JobWriteShellCommands(job, gn, cmdsOK, &run); + (void)fflush(job->cmdFILE); } else if (!GNode_ShouldExecute(gn)) { /* - * Not executing anything -- just print all the commands to - * stdout in one fell swoop. This will still set up - * job->tailCmds correctly. + * Just print all the commands to stdout in one fell swoop. + * This still sets up job->tailCmds correctly. */ SwitchOutputTo(gn); job->cmdFILE = stdout; - /* - * Only print the commands if they're ok, but don't die if - * they're not -- just let the user know they're bad and - * keep going. It doesn't do any harm in this case and may - * do some good. - */ if (cmdsOK) JobPrintCommands(job); - /* Don't execute the shell, thank you. */ run = FALSE; + (void)fflush(job->cmdFILE); } else { - /* - * Just touch the target and note that no shell should be - * executed. Set cmdFILE to stdout to make life easier. - * Check the commands, too, but don't die if they're no - * good -- it does no harm to keep working up the graph. - */ - job->cmdFILE = stdout; Job_Touch(gn, job->echo); run = FALSE; } - /* Just in case it isn't already... */ - (void)fflush(job->cmdFILE); /* If we're not supposed to execute a shell, don't. */ if (!run) { @@ -1955,11 +1936,11 @@ JobRun(GNode *targ) * Running these jobs in compat mode also guarantees that these * jobs do not overlap with other unrelated jobs. */ - List *lst = Lst_New(); - Lst_Append(lst, targ); - (void)Make_Run(lst); - Lst_Destroy(lst, NULL); - JobStart(targ, JOB_SPECIAL); + GNodeList lst = LST_INIT; + Lst_Append(&lst, targ); + (void)Make_Run(&lst); + Lst_Done(&lst); + JobStart(targ, TRUE); while (jobTokensRunning != 0) { Job_CatchOutput(); } @@ -1993,6 +1974,11 @@ Job_CatchChildren(void) if (jobTokensRunning == 0) return; + /* Have we received SIGCHLD since last call? */ + if (caught_sigchld == 0) + return; + caught_sigchld = 0; + while ((pid = waitpid((pid_t)-1, &status, WNOHANG | WUNTRACED)) > 0) { DEBUG2(JOB, "Process %d exited/stopped status %x.\n", pid, WAIT_STATUS(status)); @@ -2068,7 +2054,7 @@ Job_CatchOutput(void) /* The first fd in the list is the job token pipe */ do { - nready = poll(fds + 1 - wantToken, nJobs - 1 + wantToken, + nready = poll(fds + 1 - wantToken, fdsLen - 1 + wantToken, POLL_MSEC); } while (nready < 0 && errno == EINTR); @@ -2079,24 +2065,17 @@ Job_CatchOutput(void) char token = 0; ssize_t count; count = read(childExitJob.inPipe, &token, 1); - switch (count) { - case 0: - Punt("unexpected eof on token pipe"); - /*NOTREACHED*/ - case -1: - Punt("token pipe read: %s", strerror(errno)); - /*NOTREACHED*/ - case 1: + if (count == 1) { if (token == DO_JOB_RESUME[0]) /* * Complete relay requested from our SIGCONT * handler */ JobRestartJobs(); - break; - default: - abort(); - } + } else if (count == 0) + Punt("unexpected eof on token pipe"); + else + Punt("token pipe read: %s", strerror(errno)); nready--; } @@ -2104,10 +2083,10 @@ Job_CatchOutput(void) if (nready == 0) return; - for (i = npseudojobs * nfds_per_job(); i < nJobs; i++) { + for (i = npseudojobs * nfds_per_job(); i < fdsLen; i++) { if (fds[i].revents == 0) continue; - job = allJobs[i]; + job = jobByFdIndex[i]; if (job->status == JOB_ST_RUNNING) JobDoOutput(job, FALSE); #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) @@ -2159,7 +2138,7 @@ Shell_Init(void) if (shellPath == NULL) InitShellNameAndPath(); - Var_SetWithFlags(".SHELL", shellPath, VAR_CMDLINE, VAR_SET_READONLY); + Var_SetWithFlags(SCOPE_CMDLINE, ".SHELL", shellPath, VAR_SET_READONLY); if (shell->errFlag == NULL) shell->errFlag = ""; if (shell->echoFlag == NULL) @@ -2199,12 +2178,12 @@ Job_SetPrefix(void) { if (targPrefix != NULL) { free(targPrefix); - } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) { - Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL); + } else if (!Var_Exists(SCOPE_GLOBAL, MAKE_JOB_PREFIX)) { + Global_Set(MAKE_JOB_PREFIX, "---"); } (void)Var_Subst("${" MAKE_JOB_PREFIX "}", - VAR_GLOBAL, VARE_WANTRES, &targPrefix); + SCOPE_GLOBAL, VARE_WANTRES, &targPrefix); /* TODO: handle errors */ } @@ -2227,6 +2206,7 @@ Job_Init(void) memset(job_table, 0, (size_t)opts.maxJobs * sizeof *job_table); job_table_end = job_table + opts.maxJobs; wantToken = 0; + caught_sigchld = 0; aborting = ABORT_NONE; job_errors = 0; @@ -2254,13 +2234,13 @@ Job_Init(void) JobCreatePipe(&childExitJob, 3); - /* Preallocate enough for the maximum number of jobs. */ - fds = bmake_malloc(sizeof *fds * - (npseudojobs + (size_t)opts.maxJobs) * - nfds_per_job()); - allJobs = bmake_malloc(sizeof *allJobs * - (npseudojobs + (size_t)opts.maxJobs) * - nfds_per_job()); + { + /* Preallocate enough for the maximum number of jobs. */ + size_t nfds = (npseudojobs + (size_t)opts.maxJobs) * + nfds_per_job(); + fds = bmake_malloc(sizeof *fds * nfds); + jobByFdIndex = bmake_malloc(sizeof *jobByFdIndex * nfds); + } /* These are permanent entries and take slots 0 and 1 */ watchfd(&tokenWaitJob); @@ -2394,7 +2374,7 @@ Job_ParseShell(char *line) /* XXX: don't use line as an iterator variable */ pp_skip_whitespace(&line); - free(shellArgv); + free(shell_freeIt); memset(&newShell, 0, sizeof newShell); @@ -2409,7 +2389,7 @@ Job_ParseShell(char *line) Error("Unterminated quoted string [%s]", line); return FALSE; } - shellArgv = path; + shell_freeIt = path; for (path = NULL, argv = words; argc != 0; argc--, argv++) { char *arg = *argv; @@ -2620,7 +2600,7 @@ void Job_End(void) { #ifdef CLEANUP - free(shellArgv); + free(shell_freeIt); #endif } @@ -2715,17 +2695,17 @@ watchfd(Job *job) if (job->inPollfd != NULL) Punt("Watching watched job"); - fds[nJobs].fd = job->inPipe; - fds[nJobs].events = POLLIN; - allJobs[nJobs] = job; - job->inPollfd = &fds[nJobs]; - nJobs++; + fds[fdsLen].fd = job->inPipe; + fds[fdsLen].events = POLLIN; + jobByFdIndex[fdsLen] = job; + job->inPollfd = &fds[fdsLen]; + fdsLen++; #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) if (useMeta) { - fds[nJobs].fd = meta_job_fd(job); - fds[nJobs].events = fds[nJobs].fd == -1 ? 0 : POLLIN; - allJobs[nJobs] = job; - nJobs++; + fds[fdsLen].fd = meta_job_fd(job); + fds[fdsLen].events = fds[fdsLen].fd == -1 ? 0 : POLLIN; + jobByFdIndex[fdsLen] = job; + fdsLen++; } #endif } @@ -2737,7 +2717,7 @@ clearfd(Job *job) if (job->inPollfd == NULL) Punt("Unwatching unwatched job"); i = (size_t)(job->inPollfd - fds); - nJobs--; + fdsLen--; #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) if (useMeta) { /* @@ -2747,20 +2727,20 @@ clearfd(Job *job) assert(nfds_per_job() == 2); if (i % 2 != 0) Punt("odd-numbered fd with meta"); - nJobs--; + fdsLen--; } #endif /* * Move last job in table into hole made by dead job. */ - if (nJobs != i) { - fds[i] = fds[nJobs]; - allJobs[i] = allJobs[nJobs]; - allJobs[i]->inPollfd = &fds[i]; + if (fdsLen != i) { + fds[i] = fds[fdsLen]; + jobByFdIndex[i] = jobByFdIndex[fdsLen]; + jobByFdIndex[i]->inPollfd = &fds[i]; #if defined(USE_FILEMON) && !defined(USE_FILEMON_DEV) if (useMeta) { - fds[i + 1] = fds[nJobs + 1]; - allJobs[i + 1] = allJobs[nJobs + 1]; + fds[i + 1] = fds[fdsLen + 1]; + jobByFdIndex[i + 1] = jobByFdIndex[fdsLen + 1]; } #endif } @@ -2794,6 +2774,22 @@ JobTokenAdd(void) continue; } +/* Get a temp file */ +int +Job_TempFile(const char *pattern, char *tfile, size_t tfile_sz) +{ + int fd; + sigset_t mask; + + JobSigLock(&mask); + fd = mkTempFile(pattern, tfile, tfile_sz); + if (tfile != NULL && !DEBUG(SCRIPT)) + unlink(tfile); + JobSigUnlock(&mask); + + return fd; +} + /* Prep the job token pipe in the root make process. */ void Job_ServerStart(int max_tokens, int jp_0, int jp_1) @@ -2815,8 +2811,8 @@ Job_ServerStart(int max_tokens, int jp_0, int jp_1) snprintf(jobarg, sizeof jobarg, "%d,%d", tokenWaitJob.inPipe, tokenWaitJob.outPipe); - Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); - Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-J"); + Global_Append(MAKEFLAGS, jobarg); /* * Preload the job pipe with one token per job, save the one @@ -2915,7 +2911,7 @@ Job_RunTarget(const char *target, const char *fname) return FALSE; if (fname != NULL) - Var_Set(ALLSRC, fname, gn); + Var_Set(gn, ALLSRC, fname); JobRun(gn); /* XXX: Replace with GNode_IsError(gn) */ diff --git a/job.h b/job.h index 9bb5f149766f..0d7d7313b9d4 100644 --- a/job.h +++ b/job.h @@ -1,4 +1,4 @@ -/* $NetBSD: job.h,v 1.71 2020/12/30 10:03:16 rillig Exp $ */ +/* $NetBSD: job.h,v 1.72 2021/02/05 19:19:17 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -205,5 +205,6 @@ void Job_ServerStart(int, int, int); void Job_SetPrefix(void); Boolean Job_RunTarget(const char *, const char *); void Job_FlagsToString(const Job *, char *, size_t); +int Job_TempFile(const char *, char *, size_t); #endif /* MAKE_JOB_H */ diff --git a/lst.c b/lst.c index 8d660b37ff52..8f3c32ef72e6 100644 --- a/lst.c +++ b/lst.c @@ -1,4 +1,4 @@ -/* $NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $ */ +/* $NetBSD: lst.c,v 1.104 2021/02/01 19:39:31 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -34,7 +34,7 @@ #include "make.h" -MAKE_RCSID("$NetBSD: lst.c,v 1.102 2020/12/30 10:03:16 rillig Exp $"); +MAKE_RCSID("$NetBSD: lst.c,v 1.104 2021/02/01 19:39:31 rillig Exp $"); static ListNode * LstNodeNew(ListNode *prev, ListNode *next, void *datum) @@ -89,17 +89,6 @@ Lst_Free(List *list) free(list); } -/* - * Destroy a list and free all its resources. The freeProc is called with the - * datum from each node in turn before the node is freed. - */ -void -Lst_Destroy(List *list, LstFreeProc freeProc) -{ - Lst_DoneCall(list, freeProc); - free(list); -} - /* Insert a new node with the datum before the given node. */ void Lst_InsertBefore(List *list, ListNode *ln, void *datum) @@ -287,7 +276,7 @@ Vector_Push(Vector *v) } /* - * Return the pointer to the last item in the vector. + * Remove the last item from the vector, return the pointer to it. * The returned data is valid until the next modifying operation. */ void * diff --git a/lst.h b/lst.h index 30c28ed7096d..fe854a7647a8 100644 --- a/lst.h +++ b/lst.h @@ -1,4 +1,4 @@ -/* $NetBSD: lst.h,v 1.95 2021/01/03 21:12:03 rillig Exp $ */ +/* $NetBSD: lst.h,v 1.96 2021/02/01 18:55:15 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -116,8 +116,6 @@ void Lst_Done(List *); void Lst_DoneCall(List *, LstFreeProc); /* Free the list, leaving the node data unmodified. */ void Lst_Free(List *); -/* Free the list, freeing the node data using the given function. */ -void Lst_Destroy(List *, LstFreeProc); #define LST_INIT { NULL, NULL } diff --git a/main.c b/main.c index f2813d263c2a..d25bb00d3716 100644 --- a/main.c +++ b/main.c @@ -1,4 +1,4 @@ -/* $NetBSD: main.c,v 1.512 2021/01/10 23:59:53 rillig Exp $ */ +/* $NetBSD: main.c,v 1.533 2021/02/05 19:19:17 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -73,18 +73,19 @@ * * Utility functions defined in this file: * - * Main_ParseArgLine Parse and process command line arguments from - * a single string. Used to implement the - * special targets .MFLAGS and .MAKEFLAGS. + * Main_ParseArgLine + * Parse and process command line arguments from a + * single string. Used to implement the special targets + * .MFLAGS and .MAKEFLAGS. * - * Error Print a tagged error message. + * Error Print a tagged error message. * - * Fatal Print an error message and exit. + * Fatal Print an error message and exit. * - * Punt Abort all jobs and exit with a message. + * Punt Abort all jobs and exit with a message. * - * Finish Finish things up by printing the number of - * errors which occurred, and exit. + * Finish Finish things up by printing the number of errors + * that occurred, and exit. */ #include @@ -110,7 +111,7 @@ #include "trace.h" /* "@(#)main.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: main.c,v 1.512 2021/01/10 23:59:53 rillig Exp $"); +MAKE_RCSID("$NetBSD: main.c,v 1.533 2021/02/05 19:19:17 sjg Exp $"); #if defined(MAKE_NATIVE) && !defined(lint) __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 " "The Regents of the University of California. " @@ -200,7 +201,7 @@ usage(void) } static void -parse_debug_option_F(const char *modules) +MainParseArgDebugFile(const char *arg) { const char *mode; size_t len; @@ -209,24 +210,24 @@ parse_debug_option_F(const char *modules) if (opts.debug_file != stdout && opts.debug_file != stderr) fclose(opts.debug_file); - if (*modules == '+') { - modules++; + if (*arg == '+') { + arg++; mode = "a"; } else mode = "w"; - if (strcmp(modules, "stdout") == 0) { + if (strcmp(arg, "stdout") == 0) { opts.debug_file = stdout; return; } - if (strcmp(modules, "stderr") == 0) { + if (strcmp(arg, "stderr") == 0) { opts.debug_file = stderr; return; } - len = strlen(modules); + len = strlen(arg); fname = bmake_malloc(len + 20); - memcpy(fname, modules, len + 1); + memcpy(fname, arg, len + 1); /* Let the filename be modified by the pid */ if (strcmp(fname + len - 3, ".%d") == 0) @@ -242,12 +243,12 @@ parse_debug_option_F(const char *modules) } static void -parse_debug_options(const char *argvalue) +MainParseArgDebug(const char *argvalue) { const char *modules; DebugFlags debug = opts.debug; - for (modules = argvalue; *modules != '\0'; ++modules) { + for (modules = argvalue; *modules != '\0'; modules++) { switch (*modules) { case '0': /* undocumented, only intended for tests */ debug = DEBUG_NONE; @@ -325,7 +326,7 @@ parse_debug_options(const char *argvalue) debug |= DEBUG_SHELL; break; case 'F': - parse_debug_option_F(modules + 1); + MainParseArgDebugFile(modules + 1); goto debug_setbuf; default: (void)fprintf(stderr, @@ -348,11 +349,9 @@ parse_debug_options(const char *argvalue) } } -/* - * does path contain any relative components - */ +/* Is path relative, or does it contain any relative component "." or ".."? */ static Boolean -is_relpath(const char *path) +IsRelativePath(const char *path) { const char *cp; @@ -383,7 +382,7 @@ MainParseArgChdir(const char *argvalue) (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); exit(2); } - if (!is_relpath(argvalue) && + if (!IsRelativePath(argvalue) && stat(argvalue, &sa) != -1 && stat(curdir, &sb) != -1 && sa.st_ino == sb.st_ino && @@ -414,8 +413,8 @@ MainParseArgJobsInternal(const char *argvalue) jp_1 = -1; opts.compatMake = TRUE; } else { - Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); - Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-J"); + Global_Append(MAKEFLAGS, argvalue); } } @@ -432,9 +431,9 @@ MainParseArgJobs(const char *argvalue) progname); exit(2); /* Not 1 so -q can distinguish error */ } - Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); - Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); - Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-j"); + Global_Append(MAKEFLAGS, argvalue); + Global_Set(".MAKE.JOBS", argvalue); maxJobTokens = opts.maxJobs; } @@ -446,13 +445,13 @@ MainParseArgSysInc(const char *argvalue) char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4); if (found_path == NULL) return; - (void)Dir_AddDir(sysIncPath, found_path); + (void)SearchPath_Add(sysIncPath, found_path); free(found_path); } else { - (void)Dir_AddDir(sysIncPath, argvalue); + (void)SearchPath_Add(sysIncPath, argvalue); } - Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); - Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-m"); + Global_Append(MAKEFLAGS, argvalue); } static Boolean @@ -463,22 +462,22 @@ MainParseArg(char c, const char *argvalue) break; case 'B': opts.compatMake = TRUE; - Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); - Var_Set(MAKE_MODE, "compat", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-B"); + Global_Set(MAKE_MODE, "compat"); break; case 'C': MainParseArgChdir(argvalue); break; case 'D': if (argvalue[0] == '\0') return FALSE; - Var_Set(argvalue, "1", VAR_GLOBAL); - Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); - Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Global_SetExpand(argvalue, "1"); + Global_Append(MAKEFLAGS, "-D"); + Global_Append(MAKEFLAGS, argvalue); break; case 'I': Parse_AddIncludeDir(argvalue); - Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); - Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-I"); + Global_Append(MAKEFLAGS, argvalue); break; case 'J': MainParseArgJobsInternal(argvalue); @@ -486,24 +485,24 @@ MainParseArg(char c, const char *argvalue) case 'N': opts.noExecute = TRUE; opts.noRecursiveExecute = TRUE; - Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-N"); break; case 'S': opts.keepgoing = FALSE; - Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-S"); break; case 'T': tracefile = bmake_strdup(argvalue); - Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); - Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-T"); + Global_Append(MAKEFLAGS, argvalue); break; case 'V': case 'v': opts.printVars = c == 'v' ? PVM_EXPANDED : PVM_UNEXPANDED; Lst_Append(&opts.variables, bmake_strdup(argvalue)); /* XXX: Why always -V? */ - Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); - Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-V"); + Global_Append(MAKEFLAGS, argvalue); break; case 'W': opts.parseWarnFatal = TRUE; @@ -511,35 +510,35 @@ MainParseArg(char c, const char *argvalue) break; case 'X': opts.varNoExportEnv = TRUE; - Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-X"); break; case 'd': /* If '-d-opts' don't pass to children */ if (argvalue[0] == '-') argvalue++; else { - Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); - Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-d"); + Global_Append(MAKEFLAGS, argvalue); } - parse_debug_options(argvalue); + MainParseArgDebug(argvalue); break; case 'e': opts.checkEnvFirst = TRUE; - Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-e"); break; case 'f': Lst_Append(&opts.makefiles, bmake_strdup(argvalue)); break; case 'i': opts.ignoreErrors = TRUE; - Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-i"); break; case 'j': MainParseArgJobs(argvalue); break; case 'k': opts.keepgoing = TRUE; - Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-k"); break; case 'm': MainParseArgSysInc(argvalue); @@ -547,28 +546,28 @@ MainParseArg(char c, const char *argvalue) break; case 'n': opts.noExecute = TRUE; - Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-n"); break; case 'q': opts.queryFlag = TRUE; /* Kind of nonsensical, wot? */ - Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-q"); break; case 'r': opts.noBuiltins = TRUE; - Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-r"); break; case 's': opts.beSilent = TRUE; - Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-s"); break; case 't': opts.touchFlag = TRUE; - Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-t"); break; case 'w': opts.enterFlag = TRUE; - Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-w"); break; default: case '?': @@ -657,10 +656,10 @@ MainParseArgs(int argc, char **argv) * 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) { + for (; argc > 1; argv++, argc--) { VarAssign var; if (Parse_IsVar(argv[1], &var)) { - Parse_DoVar(&var, VAR_CMDLINE); + Parse_DoVar(&var, SCOPE_CMDLINE); } else { if (argv[1][0] == '\0') Punt("illegal (null) argument."); @@ -692,7 +691,7 @@ Main_ParseArgLine(const char *line) if (line == NULL) return; /* XXX: don't use line as an iterator variable */ - for (; *line == ' '; ++line) + for (; *line == ' '; line++) continue; if (line[0] == '\0') return; @@ -712,7 +711,7 @@ Main_ParseArgLine(const char *line) } #endif { - FStr argv0 = Var_Value(".MAKE", VAR_GLOBAL); + FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE"); buf = str_concat3(argv0.str, " ", line); FStr_Done(&argv0); } @@ -756,7 +755,7 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...) progname, path, strerror(errno)); } else { snprintf(objdir, sizeof objdir, "%s", path); - Var_Set(".OBJDIR", objdir, VAR_GLOBAL); + Global_Set(".OBJDIR", objdir); setenv("PWD", objdir, 1); Dir_InitDot(); purge_relative_cached_realpaths(); @@ -772,7 +771,7 @@ Main_SetObjdir(Boolean writable, const char *fmt, ...) static Boolean SetVarObjdir(Boolean writable, const char *var, const char *suffix) { - FStr path = Var_Value(var, VAR_CMDLINE); + FStr path = Var_Value(SCOPE_CMDLINE, var); FStr xpath; if (path.str == NULL || path.str[0] == '\0') { @@ -784,7 +783,7 @@ SetVarObjdir(Boolean writable, const char *var, const char *suffix) xpath = FStr_InitRefer(path.str); if (strchr(path.str, '$') != 0) { char *expanded; - (void)Var_Subst(path.str, VAR_GLOBAL, VARE_WANTRES, &expanded); + (void)Var_Subst(path.str, SCOPE_GLOBAL, VARE_WANTRES, &expanded); /* TODO: handle errors */ xpath = FStr_InitOwn(expanded); } @@ -835,28 +834,23 @@ siginfo(int signo MAKE_ATTR_UNUSED) static void MakeMode(void) { - FStr mode = FStr_InitRefer(NULL); + char *mode; - if (mode.str == NULL) { - char *expanded; - (void)Var_Subst("${" MAKE_MODE ":tl}", - VAR_GLOBAL, VARE_WANTRES, &expanded); - /* TODO: handle errors */ - mode = FStr_InitOwn(expanded); - } + (void)Var_Subst("${" MAKE_MODE ":tl}", SCOPE_GLOBAL, VARE_WANTRES, &mode); + /* TODO: handle errors */ - if (mode.str[0] != '\0') { - if (strstr(mode.str, "compat") != NULL) { + if (mode[0] != '\0') { + if (strstr(mode, "compat") != NULL) { opts.compatMake = TRUE; forceJobs = FALSE; } #if USE_META - if (strstr(mode.str, "meta") != NULL) - meta_mode_init(mode.str); + if (strstr(mode, "meta") != NULL) + meta_mode_init(mode); #endif } - FStr_Done(&mode); + free(mode); } static void @@ -864,7 +858,7 @@ PrintVar(const char *varname, Boolean expandVars) { if (strchr(varname, '$') != NULL) { char *evalue; - (void)Var_Subst(varname, VAR_GLOBAL, VARE_WANTRES, &evalue); + (void)Var_Subst(varname, SCOPE_GLOBAL, VARE_WANTRES, &evalue); /* TODO: handle errors */ printf("%s\n", evalue); bmake_free(evalue); @@ -872,14 +866,14 @@ PrintVar(const char *varname, Boolean expandVars) } else if (expandVars) { char *expr = str_concat3("${", varname, "}"); char *evalue; - (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &evalue); + (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &evalue); /* TODO: handle errors */ free(expr); printf("%s\n", evalue); bmake_free(evalue); } else { - FStr value = Var_Value(varname, VAR_GLOBAL); + FStr value = Var_Value(SCOPE_GLOBAL, varname); printf("%s\n", value.str != NULL ? value.str : ""); FStr_Done(&value); } @@ -899,7 +893,7 @@ GetBooleanVar(const char *varname, Boolean fallback) char *value; Boolean res; - (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &value); + (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &value); /* TODO: handle errors */ res = ParseBoolean(value, fallback); free(value); @@ -966,7 +960,7 @@ runTargets(void) Compat_Run(&targs); outOfDate = FALSE; } - Lst_Done(&targs); /* Don't free the nodes. */ + Lst_Done(&targs); /* Don't free the targets themselves. */ return outOfDate; } @@ -981,13 +975,13 @@ InitVarTargets(void) StringListNode *ln; if (Lst_IsEmpty(&opts.create)) { - Var_Set(".TARGETS", "", VAR_GLOBAL); + Global_Set(".TARGETS", ""); return; } for (ln = opts.create.first; ln != NULL; ln = ln->next) { - char *name = ln->datum; - Var_Append(".TARGETS", name, VAR_GLOBAL); + const char *name = ln->datum; + Global_Append(".TARGETS", name); } } @@ -1001,7 +995,7 @@ InitRandom(void) } static const char * -InitVarMachine(const struct utsname *utsname) +InitVarMachine(const struct utsname *utsname MAKE_ATTR_UNUSED) { #ifdef FORCE_MACHINE return FORCE_MACHINE; @@ -1062,6 +1056,9 @@ InitVarMachineArch(void) * All this code is so that we know where we are when we start up * on a different machine with pmake. * + * XXX: Make no longer has "local" and "remote" mode. Is this code still + * necessary? + * * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX * since the value of curdir can vary depending on how we got * here. Ie sitting at a shell prompt (shell that provides $PWD) @@ -1081,13 +1078,13 @@ HandlePWD(const struct stat *curdir_st) if (ignorePWD || (pwd = getenv("PWD")) == NULL) return; - prefix = Var_Value("MAKEOBJDIRPREFIX", VAR_CMDLINE); + prefix = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIRPREFIX"); if (prefix.str != NULL) { FStr_Done(&prefix); return; } - makeobjdir = Var_Value("MAKEOBJDIR", VAR_CMDLINE); + makeobjdir = Var_Value(SCOPE_CMDLINE, "MAKEOBJDIR"); if (makeobjdir.str != NULL && strchr(makeobjdir.str, '$') != NULL) goto ignore_pwd; @@ -1146,7 +1143,7 @@ CmdOpts_Init(void) { opts.compatMake = FALSE; opts.debug = DEBUG_NONE; - /* opts.debug_file has been initialized earlier */ + /* opts.debug_file has already been initialized earlier */ opts.strict = FALSE; opts.debugVflag = FALSE; opts.checkEnvFirst = FALSE; @@ -1189,8 +1186,8 @@ InitVarMake(const char *argv0) make = abspath; } - Var_Set("MAKE", make, VAR_GLOBAL); - Var_Set(".MAKE", make, VAR_GLOBAL); + Global_Set("MAKE", make); + Global_Set(".MAKE", make); } /* @@ -1223,11 +1220,11 @@ InitDefSysIncPath(char *syspath) if (strncmp(start, ".../", 4) == 0) { char *dir = Dir_FindHereOrAbove(curdir, start + 4); if (dir != NULL) { - (void)Dir_AddDir(defSysIncPath, dir); + (void)SearchPath_Add(defSysIncPath, dir); free(dir); } } else { - (void)Dir_AddDir(defSysIncPath, start); + (void)SearchPath_Add(defSysIncPath, start); } } @@ -1239,25 +1236,26 @@ static void ReadBuiltinRules(void) { StringListNode *ln; - StringList sysMkPath = LST_INIT; + StringList sysMkFiles = LST_INIT; - Dir_Expand(_PATH_DEFSYSMK, - Lst_IsEmpty(sysIncPath) ? defSysIncPath : sysIncPath, - &sysMkPath); - if (Lst_IsEmpty(&sysMkPath)) + SearchPath_Expand( + Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath, + _PATH_DEFSYSMK, + &sysMkFiles); + if (Lst_IsEmpty(&sysMkFiles)) Fatal("%s: no system rules (%s).", progname, _PATH_DEFSYSMK); - for (ln = sysMkPath.first; ln != NULL; ln = ln->next) + for (ln = sysMkFiles.first; ln != NULL; ln = ln->next) if (ReadMakefile(ln->datum) == 0) break; if (ln == NULL) Fatal("%s: cannot open %s.", - progname, (const char *)sysMkPath.first->datum); + progname, (const char *)sysMkFiles.first->datum); - /* Free the list but not the actual filenames since these may still - * be used in GNodes. */ - Lst_Done(&sysMkPath); + /* Free the list nodes but not the actual filenames since these may + * still be used in GNodes. */ + Lst_Done(&sysMkFiles); } static void @@ -1267,10 +1265,10 @@ InitMaxJobs(void) int n; if (forceJobs || opts.compatMake || - !Var_Exists(".MAKE.JOBS", VAR_GLOBAL)) + !Var_Exists(SCOPE_GLOBAL, ".MAKE.JOBS")) return; - (void)Var_Subst("${.MAKE.JOBS}", VAR_GLOBAL, VARE_WANTRES, &value); + (void)Var_Subst("${.MAKE.JOBS}", SCOPE_GLOBAL, VARE_WANTRES, &value); /* TODO: handle errors */ n = (int)strtol(value, NULL, 0); if (n < 1) { @@ -1282,8 +1280,8 @@ InitMaxJobs(void) } if (n != opts.maxJobs) { - Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); - Var_Append(MAKEFLAGS, value, VAR_GLOBAL); + Global_Append(MAKEFLAGS, "-j"); + Global_Append(MAKEFLAGS, value); } opts.maxJobs = n; @@ -1302,10 +1300,10 @@ static void InitVpath(void) { char *vpath, savec, *path; - if (!Var_Exists("VPATH", VAR_CMDLINE)) + if (!Var_Exists(SCOPE_CMDLINE, "VPATH")) return; - (void)Var_Subst("${VPATH}", VAR_CMDLINE, VARE_WANTRES, &vpath); + (void)Var_Subst("${VPATH}", SCOPE_CMDLINE, VARE_WANTRES, &vpath); /* TODO: handle errors */ path = vpath; do { @@ -1317,7 +1315,7 @@ InitVpath(void) savec = *cp; *cp = '\0'; /* Add directory to search path */ - (void)Dir_AddDir(&dirSearchPath, path); + (void)SearchPath_Add(&dirSearchPath, path); *cp = savec; path = cp + 1; } while (savec == ':'); @@ -1343,7 +1341,7 @@ ReadFirstDefaultMakefile(void) char *prefs; (void)Var_Subst("${" MAKE_MAKEFILE_PREFERENCE "}", - VAR_CMDLINE, VARE_WANTRES, &prefs); + SCOPE_CMDLINE, VARE_WANTRES, &prefs); /* TODO: handle errors */ /* XXX: This should use a local list instead of opts.makefiles @@ -1412,21 +1410,21 @@ main_Init(int argc, char **argv) */ Targ_Init(); Var_Init(); - Var_Set(".MAKE.OS", utsname.sysname, VAR_GLOBAL); - Var_Set("MACHINE", machine, VAR_GLOBAL); - Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL); + Global_Set(".MAKE.OS", utsname.sysname); + Global_Set("MACHINE", machine); + Global_Set("MACHINE_ARCH", machine_arch); #ifdef MAKE_VERSION - Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL); + Global_Set("MAKE_VERSION", MAKE_VERSION); #endif - Var_Set(".newline", "\n", VAR_GLOBAL); /* handy for :@ loops */ + Global_Set(".newline", "\n"); /* handy for :@ loops */ /* * This is the traditional preference for makefiles. */ #ifndef MAKEFILE_PREFERENCE_LIST # define MAKEFILE_PREFERENCE_LIST "makefile Makefile" #endif - Var_Set(MAKE_MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, VAR_GLOBAL); - Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL); + Global_Set(MAKE_MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST); + Global_Set(MAKE_DEPENDFILE, ".depend"); CmdOpts_Init(); allPrecious = FALSE; /* Remove targets when interrupted */ @@ -1450,12 +1448,12 @@ main_Init(int argc, char **argv) */ Parse_Init(); InitVarMake(argv[0]); - Var_Set(MAKEFLAGS, "", VAR_GLOBAL); - Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL); - Var_Set("MFLAGS", "", VAR_GLOBAL); - Var_Set(".ALLTARGETS", "", VAR_GLOBAL); + Global_Set(MAKEFLAGS, ""); + Global_Set(MAKEOVERRIDES, ""); + Global_Set("MFLAGS", ""); + Global_Set(".ALLTARGETS", ""); /* some makefiles need to know this */ - Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMDLINE); + Var_Set(SCOPE_CMDLINE, MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV); /* Set some other useful variables. */ { @@ -1465,15 +1463,15 @@ main_Init(int argc, char **argv) if (makelevel < 0) makelevel = 0; snprintf(tmp, sizeof tmp, "%d", makelevel); - Var_Set(MAKE_LEVEL, tmp, VAR_GLOBAL); + Global_Set(MAKE_LEVEL, tmp); snprintf(tmp, sizeof tmp, "%u", myPid); - Var_Set(".MAKE.PID", tmp, VAR_GLOBAL); + Global_Set(".MAKE.PID", tmp); snprintf(tmp, sizeof tmp, "%u", getppid()); - Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL); + Global_Set(".MAKE.PPID", tmp); snprintf(tmp, sizeof tmp, "%u", getuid()); - Var_Set(".MAKE.UID", tmp, VAR_GLOBAL); + Global_Set(".MAKE.UID", tmp); snprintf(tmp, sizeof tmp, "%u", getgid()); - Var_Set(".MAKE.GID", tmp, VAR_GLOBAL); + Global_Set(".MAKE.GID", tmp); } if (makelevel > 0) { char pn[1024]; @@ -1528,7 +1526,7 @@ main_Init(int argc, char **argv) #ifndef NO_PWD_OVERRIDE HandlePWD(&sa); #endif - Var_Set(".CURDIR", curdir, VAR_GLOBAL); + Global_Set(".CURDIR", curdir); InitObjdir(machine, machine_arch); @@ -1574,7 +1572,7 @@ main_PrepareMaking(void) /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ if (!opts.noBuiltins || opts.printVars == PVM_NONE) { (void)Var_Subst("${.MAKE.DEPENDFILE}", - VAR_CMDLINE, VARE_WANTRES, &makeDependfile); + SCOPE_CMDLINE, VARE_WANTRES, &makeDependfile); if (makeDependfile[0] != '\0') { /* TODO: handle errors */ doing_depend = TRUE; @@ -1589,8 +1587,8 @@ main_PrepareMaking(void) MakeMode(); { - FStr makeflags = Var_Value(MAKEFLAGS, VAR_GLOBAL); - Var_Append("MFLAGS", makeflags.str, VAR_GLOBAL); + FStr makeflags = Var_Value(SCOPE_GLOBAL, MAKEFLAGS); + Global_Append("MFLAGS", makeflags.str); FStr_Done(&makeflags); } @@ -1720,7 +1718,7 @@ ReadMakefile(const char *fname) if (strcmp(fname, "-") == 0) { Parse_File(NULL /*stdin*/, -1); - Var_Set("MAKEFILE", "", VAR_INTERNAL); + Var_Set(SCOPE_INTERNAL, "MAKEFILE", ""); } else { /* if we've chdir'd, rebuild the path name */ if (strcmp(curdir, objdir) != 0 && *fname != '/') { @@ -1747,7 +1745,7 @@ ReadMakefile(const char *fname) /* look in -I and system include directories. */ name = Dir_FindFile(fname, parseIncPath); if (name == NULL) { - SearchPath *sysInc = Lst_IsEmpty(sysIncPath) + SearchPath *sysInc = Lst_IsEmpty(&sysIncPath->dirs) ? defSysIncPath : sysIncPath; name = Dir_FindFile(fname, sysInc); } @@ -1764,14 +1762,14 @@ ReadMakefile(const char *fname) */ found: if (!doing_depend) - Var_Set("MAKEFILE", fname, VAR_INTERNAL); + Var_Set(SCOPE_INTERNAL, "MAKEFILE", fname); Parse_File(fname, fd); } free(path); return 0; } -/*- +/* * Cmd_Exec -- * Execute the command in cmd, and return the output of that command * in a string. In the output, newlines are replaced with spaces. @@ -1824,7 +1822,7 @@ Cmd_Exec(const char *cmd, const char **errfmt) /* * Fork */ - switch (cpid = vFork()) { + switch (cpid = vfork()) { case 0: (void)close(pipefds[0]); /* Close input side of pipe */ @@ -1866,8 +1864,8 @@ Cmd_Exec(const char *cmd, const char **errfmt) while ((pid = waitpid(cpid, &status, 0)) != cpid && pid >= 0) JobReapChild(pid, status, FALSE); - res_len = Buf_Len(&buf); - res = Buf_Destroy(&buf, FALSE); + res_len = buf.len; + res = Buf_DoneData(&buf); if (savederr != 0) *errfmt = "Couldn't read shell's output for \"%s\""; @@ -2051,9 +2049,9 @@ execDie(const char *af, const char *av) Buf_AddStr(&buf, strerror(errno)); Buf_AddStr(&buf, ")\n"); - write_all(STDERR_FILENO, Buf_GetAll(&buf, NULL), Buf_Len(&buf)); + write_all(STDERR_FILENO, buf.data, buf.len); - Buf_Destroy(&buf, TRUE); + Buf_Done(&buf); _exit(1); } @@ -2122,7 +2120,7 @@ shouldDieQuietly(GNode *gn, int bf) else quietly = (gn != NULL && (gn->type & OP_MAKE)) ? 1 : 0; } - return quietly; + return quietly != 0; } static void @@ -2133,15 +2131,15 @@ SetErrorVars(GNode *gn) /* * We can print this even if there is no .ERROR target. */ - Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL); - Var_Delete(".ERROR_CMD", VAR_GLOBAL); + Global_Set(".ERROR_TARGET", gn->name); + Global_Delete(".ERROR_CMD"); for (ln = gn->commands.first; ln != NULL; ln = ln->next) { const char *cmd = ln->datum; if (cmd == NULL) break; - Var_Append(".ERROR_CMD", cmd, VAR_GLOBAL); + Global_Append(".ERROR_CMD", cmd); } } @@ -2176,7 +2174,7 @@ PrintOnError(GNode *gn, const char *msg) { char *errorVarsValues; (void)Var_Subst("${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", - VAR_GLOBAL, VARE_WANTRES, &errorVarsValues); + SCOPE_GLOBAL, VARE_WANTRES, &errorVarsValues); /* TODO: handle errors */ printf("%s", errorVarsValues); free(errorVarsValues); @@ -2206,7 +2204,7 @@ Main_ExportMAKEFLAGS(Boolean first) once = FALSE; expr = "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}"; - (void)Var_Subst(expr, VAR_CMDLINE, VARE_WANTRES, &s); + (void)Var_Subst(expr, SCOPE_CMDLINE, VARE_WANTRES, &s); /* TODO: handle errors */ if (s[0] != '\0') { #ifdef POSIX @@ -2228,7 +2226,7 @@ getTmpdir(void) /* Honor $TMPDIR but only if it is valid. Ensure it ends with '/'. */ (void)Var_Subst("${TMPDIR:tA:U" _PATH_TMP "}/", - VAR_GLOBAL, VARE_WANTRES, &tmpdir); + SCOPE_GLOBAL, VARE_WANTRES, &tmpdir); /* TODO: handle errors */ if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { @@ -2244,27 +2242,29 @@ getTmpdir(void) * Otherwise unlink the file once open. */ int -mkTempFile(const char *pattern, char **out_fname) +mkTempFile(const char *pattern, char *tfile, size_t tfile_sz) { static char *tmpdir = NULL; - char tfile[MAXPATHLEN]; + char tbuf[MAXPATHLEN]; int fd; if (pattern == NULL) pattern = TMPPAT; if (tmpdir == NULL) tmpdir = getTmpdir(); + if (tfile == NULL) { + tfile = tbuf; + tfile_sz = sizeof tbuf; + } if (pattern[0] == '/') { - snprintf(tfile, sizeof tfile, "%s", pattern); + snprintf(tfile, tfile_sz, "%s", pattern); } else { - snprintf(tfile, sizeof tfile, "%s%s", tmpdir, pattern); + snprintf(tfile, tfile_sz, "%s%s", tmpdir, pattern); } if ((fd = mkstemp(tfile)) < 0) Punt("Could not create temporary file %s: %s", tfile, strerror(errno)); - if (out_fname != NULL) { - *out_fname = bmake_strdup(tfile); - } else { + if (tfile == tbuf) { unlink(tfile); /* we just want the descriptor */ } return fd; diff --git a/make.1 b/make.1 index c711b38a9731..0412aaab61fd 100644 --- a/make.1 +++ b/make.1 @@ -1,4 +1,4 @@ -.\" $NetBSD: make.1,v 1.295 2020/12/23 13:49:12 rillig Exp $ +.\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $ .\" .\" Copyright (c) 1990, 1993 .\" The Regents of the University of California. All rights reserved. @@ -99,7 +99,7 @@ is equivalent to .It Fl D Ar variable Define .Ar variable -to be 1, in the global context. +to be 1, in the global scope. .It Fl d Ar [-]flags Turn on debugging, and specify which portions of .Nm @@ -355,7 +355,7 @@ Do not build any targets. Multiple instances of this option may be specified; the variables will be printed one per line, with a blank line for each null or undefined variable. -The value printed is extracted from the global context after all +The value printed is extracted from the global scope after all makefiles have been read. By default, the raw variable contents (which may include additional unexpanded variable references) are shown. diff --git a/make.c b/make.c index 7745e038c814..1cd7299a76c5 100644 --- a/make.c +++ b/make.c @@ -1,4 +1,4 @@ -/* $NetBSD: make.c,v 1.234 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: make.c,v 1.242 2021/02/05 05:15:12 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -78,8 +78,8 @@ * Make_Update After a target is made, update all its parents. * Perform various bookkeeping chores like the updating * of the youngestChild field of the parent, filling - * of the IMPSRC context variable, etc. Place the parent - * on the toBeMade queue if it should be. + * of the IMPSRC variable, etc. Place the parent on the + * toBeMade queue if it should be. * * GNode_UpdateYoungestChild * Update the node's youngestChild field based on the @@ -103,7 +103,7 @@ #include "job.h" /* "@(#)make.c 8.1 (Berkeley) 6/6/93" */ -MAKE_RCSID("$NetBSD: make.c,v 1.234 2021/01/10 21:20:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: make.c,v 1.242 2021/02/05 05:15:12 rillig Exp $"); /* Sequence # to detect recursion. */ static unsigned int checked_seqno = 1; @@ -137,10 +137,6 @@ make_abort(GNode *gn, int lineno) abort(); } -ENUM_VALUE_RTTI_8(GNodeMade, - UNMADE, DEFERRED, REQUESTED, BEINGMADE, - MADE, UPTODATE, ERROR, ABORTED); - ENUM_FLAGS_RTTI_31(GNodeType, OP_DEPENDS, OP_FORCE, OP_DOUBLEDEP, /* OP_OPMASK is omitted since it combines other flags */ @@ -152,10 +148,10 @@ ENUM_FLAGS_RTTI_31(GNodeType, OP_TRANSFORM, OP_MEMBER, OP_LIB, OP_ARCHV, OP_HAS_COMMANDS, OP_SAVE_CMDS, OP_DEPS_FOUND, OP_MARK); -ENUM_FLAGS_RTTI_10(GNodeFlags, +ENUM_FLAGS_RTTI_9(GNodeFlags, REMAKE, CHILDMADE, FORCE, DONE_WAIT, DONE_ORDER, FROM_DEPEND, DONE_ALLSRC, CYCLE, - DONECYCLE, INTERNAL); + DONECYCLE); void GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn, @@ -164,13 +160,11 @@ GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn, char type_buf[GNodeType_ToStringSize]; char flags_buf[GNodeFlags_ToStringSize]; - fprintf(f, "%smade %s, type %s, flags %s%s", + fprintf(f, "%s%s, type %s, flags %s%s", prefix, - Enum_ValueToString(gn->made, GNodeMade_ToStringSpecs), - Enum_FlagsToString(type_buf, sizeof type_buf, - gn->type, GNodeType_ToStringSpecs), - Enum_FlagsToString(flags_buf, sizeof flags_buf, - gn->flags, GNodeFlags_ToStringSpecs), + GNodeMade_Name(gn->made), + GNodeType_ToString(type_buf, gn->type), + GNodeFlags_ToString(flags_buf, gn->flags), suffix); } @@ -574,9 +568,9 @@ UpdateImplicitParentsVars(GNode *cgn, const char *cname) for (ln = cgn->implicitParents.first; ln != NULL; ln = ln->next) { GNode *pgn = ln->datum; if (pgn->flags & REMAKE) { - Var_Set(IMPSRC, cname, pgn); + Var_Set(pgn, IMPSRC, cname); if (cpref != NULL) - Var_Set(PREFIX, cpref, pgn); + Var_Set(pgn, PREFIX, cpref); } } } @@ -601,7 +595,7 @@ IsWaitingForOrder(GNode *gn) return FALSE; } -static int MakeBuildParent(GNode *, GNodeListNode *); +static void MakeBuildParent(GNode *, GNodeListNode *); static void ScheduleOrderSuccessors(GNode *gn) @@ -610,8 +604,7 @@ ScheduleOrderSuccessors(GNode *gn) GNodeListNode *ln; for (ln = gn->order_succ.first; ln != NULL; ln = ln->next) - if (MakeBuildParent(ln->datum, toBeMadeNext) != 0) - break; + MakeBuildParent(ln->datum, toBeMadeNext); } /* @@ -810,50 +803,53 @@ UnmarkChildren(GNode *gn) static void MakeAddAllSrc(GNode *cgn, GNode *pgn) { + const char *child, *allsrc; + if (cgn->type & OP_MARK) return; cgn->type |= OP_MARK; - if (!(cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE | OP_INVISIBLE))) { - const char *child, *allsrc; + if (cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE | OP_INVISIBLE)) + return; - if (cgn->type & OP_ARCHV) - child = GNode_VarMember(cgn); - else - child = GNode_Path(cgn); - if (cgn->type & OP_JOIN) { - allsrc = GNode_VarAllsrc(cgn); - } else { - allsrc = child; - } - if (allsrc != NULL) - Var_Append(ALLSRC, allsrc, pgn); - if (pgn->type & OP_JOIN) { - if (cgn->made == MADE) { - Var_Append(OODATE, child, pgn); - } - } else if ((pgn->mtime < cgn->mtime) || - (cgn->mtime >= now && cgn->made == MADE)) { - /* - * It goes in the OODATE variable if the parent is - * younger than the child or if the child has been - * modified more recently than the start of the make. - * This is to keep pmake from getting confused if - * something else updates the parent after the make - * starts (shouldn't happen, I know, but sometimes it - * does). In such a case, if we've updated the child, - * the parent is likely to have a modification time - * later than that of the child and anything that - * relies on the OODATE variable will be hosed. - * - * XXX: This will cause all made children to go in - * the OODATE variable, even if they're not touched, - * if RECHECK isn't defined, since cgn->mtime is set - * to now in Make_Update. According to some people, - * this is good... - */ - Var_Append(OODATE, child, pgn); - } + if (cgn->type & OP_ARCHV) + child = GNode_VarMember(cgn); + else + child = GNode_Path(cgn); + + if (cgn->type & OP_JOIN) + allsrc = GNode_VarAllsrc(cgn); + else + allsrc = child; + + if (allsrc != NULL) + Var_Append(pgn, ALLSRC, allsrc); + + if (pgn->type & OP_JOIN) { + if (cgn->made == MADE) + Var_Append(pgn, OODATE, child); + + } else if ((pgn->mtime < cgn->mtime) || + (cgn->mtime >= now && cgn->made == MADE)) { + /* + * It goes in the OODATE variable if the parent is + * younger than the child or if the child has been + * modified more recently than the start of the make. + * This is to keep pmake from getting confused if + * something else updates the parent after the make + * starts (shouldn't happen, I know, but sometimes it + * does). In such a case, if we've updated the child, + * the parent is likely to have a modification time + * later than that of the child and anything that + * relies on the OODATE variable will be hosed. + * + * XXX: This will cause all made children to go in + * the OODATE variable, even if they're not touched, + * if RECHECK isn't defined, since cgn->mtime is set + * to now in Make_Update. According to some people, + * this is good... + */ + Var_Append(pgn, OODATE, child); } } @@ -883,17 +879,17 @@ Make_DoAllVar(GNode *gn) for (ln = gn->children.first; ln != NULL; ln = ln->next) MakeAddAllSrc(ln->datum, gn); - if (!Var_Exists(OODATE, gn)) - Var_Set(OODATE, "", gn); - if (!Var_Exists(ALLSRC, gn)) - Var_Set(ALLSRC, "", gn); + if (!Var_Exists(gn, OODATE)) + Var_Set(gn, OODATE, ""); + if (!Var_Exists(gn, ALLSRC)) + Var_Set(gn, ALLSRC, ""); if (gn->type & OP_JOIN) - Var_Set(TARGET, GNode_VarAllsrc(gn), gn); + Var_Set(gn, TARGET, GNode_VarAllsrc(gn)); gn->flags |= DONE_ALLSRC; } -static int +static Boolean MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext) { @@ -903,13 +899,13 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext) GNode_FprintDetails(opts.debug_file, "", cn, "\n"); } if (GNode_IsReady(cn)) - return 0; + return FALSE; /* If this node is on the RHS of a .ORDER, check LHSs. */ if (IsWaitingForOrder(cn)) { /* Can't build this (or anything else in this child list) yet */ cn->made = DEFERRED; - return 0; /* but keep looking */ + return FALSE; /* but keep looking */ } DEBUG2(MAKE, "MakeBuildChild: schedule %s%s\n", @@ -925,7 +921,7 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext) ListNode *ln; for (ln = cn->cohorts.first; ln != NULL; ln = ln->next) - if (MakeBuildChild(ln->datum, toBeMadeNext) != 0) + if (MakeBuildChild(ln->datum, toBeMadeNext)) break; } @@ -937,18 +933,16 @@ MakeBuildChild(GNode *cn, GNodeListNode *toBeMadeNext) } /* When a .ORDER LHS node completes, we do this on each RHS. */ -static int +static void MakeBuildParent(GNode *pn, GNodeListNode *toBeMadeNext) { if (pn->made != DEFERRED) - return 0; + return; - if (MakeBuildChild(pn, toBeMadeNext) == 0) { + if (!MakeBuildChild(pn, toBeMadeNext)) { /* When this node is built, reschedule its parents. */ pn->flags |= DONE_ORDER; } - - return 0; } static void @@ -958,7 +952,7 @@ MakeChildren(GNode *gn) GNodeListNode *ln; for (ln = gn->children.first; ln != NULL; ln = ln->next) - if (MakeBuildChild(ln->datum, toBeMadeNext) != 0) + if (MakeBuildChild(ln->datum, toBeMadeNext)) break; } @@ -1038,9 +1032,9 @@ MakeStartJobs(void) if (gn->type & OP_JOIN) { /* * Even for an up-to-date .JOIN node, we - * need it to have its context variables so + * need it to have its local variables so * references to it get the correct value - * for .TARGET when building up the context + * for .TARGET when building up the local * variables of its parent(s)... */ Make_DoAllVar(gn); @@ -1243,14 +1237,14 @@ Make_ExpandUse(GNodeList *targs) continue; *eoa = '\0'; *eon = '\0'; - Var_Set(MEMBER, eoa + 1, gn); - Var_Set(ARCHIVE, gn->name, gn); + Var_Set(gn, MEMBER, eoa + 1); + Var_Set(gn, ARCHIVE, gn->name); *eoa = '('; *eon = ')'; } Dir_UpdateMTime(gn, FALSE); - Var_Set(TARGET, GNode_Path(gn), gn); + Var_Set(gn, TARGET, GNode_Path(gn)); UnmarkChildren(gn); HandleUseNodes(gn); diff --git a/make.h b/make.h index 230700a8dd9d..da5f60534f31 100644 --- a/make.h +++ b/make.h @@ -1,4 +1,4 @@ -/* $NetBSD: make.h,v 1.242 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: make.h,v 1.256 2021/02/05 19:19:17 sjg Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -72,7 +72,7 @@ * from: @(#)make.h 8.3 (Berkeley) 6/13/95 */ -/*- +/* * make.h -- * The global definitions for pmake */ @@ -350,25 +350,23 @@ typedef enum GNodeType { typedef enum GNodeFlags { GNF_NONE = 0, /* this target needs to be (re)made */ - REMAKE = 0x0001, + REMAKE = 1 << 0, /* children of this target were made */ - CHILDMADE = 0x0002, + CHILDMADE = 1 << 1, /* children don't exist, and we pretend made */ - FORCE = 0x0004, + FORCE = 1 << 2, /* Set by Make_ProcessWait() */ - DONE_WAIT = 0x0008, + DONE_WAIT = 1 << 3, /* Build requested by .ORDER processing */ - DONE_ORDER = 0x0010, + DONE_ORDER = 1 << 4, /* Node created from .depend */ - FROM_DEPEND = 0x0020, + FROM_DEPEND = 1 << 5, /* We do it once only */ - DONE_ALLSRC = 0x0040, + DONE_ALLSRC = 1 << 6, /* Used by MakePrintStatus */ - CYCLE = 0x1000, + CYCLE = 1 << 12, /* Used by MakePrintStatus */ - DONECYCLE = 0x2000, - /* Internal use only */ - INTERNAL = 0x4000 + DONECYCLE = 1 << 13 } GNodeFlags; typedef struct List StringList; @@ -377,7 +375,9 @@ typedef struct ListNode StringListNode; typedef struct List GNodeList; typedef struct ListNode GNodeListNode; -typedef struct List /* of CachedDir */ SearchPath; +typedef struct SearchPath { + List /* of CachedDir */ dirs; +} SearchPath; /* * A graph node represents a target that can possibly be made, including its @@ -429,7 +429,10 @@ typedef struct GNode { * this node, in the normal sense. */ GNodeList order_succ; - /* Other nodes of the same name, for the '::' dependency operator. */ + /* + * Other nodes of the same name, for targets that were defined using + * the '::' dependency operator (OP_DOUBLEDEP). + */ GNodeList cohorts; /* The "#n" suffix for this cohort, or "" for other nodes */ char cohort_num[8]; @@ -442,11 +445,14 @@ typedef struct GNode { /* Last time (sequence number) we tried to make this node */ unsigned int checked_seqno; - /* The "local" variables that are specific to this target and this + /* + * The "local" variables that are specific to this target and this * target only, such as $@, $<, $?. * - * Also used for the global variable scopes VAR_GLOBAL, VAR_CMDLINE, - * VAR_INTERNAL, which contain variables with arbitrary names. */ + * Also used for the global variable scopes SCOPE_GLOBAL, + * SCOPE_CMDLINE, SCOPE_INTERNAL, which contain variables with + * arbitrary names. + */ HashTable /* of Var pointer */ vars; /* The commands to be given to a shell to create this target. */ @@ -509,11 +515,11 @@ extern GNode *defaultNode; * Variables defined internally by make which should not override those set * by makefiles. */ -extern GNode *VAR_INTERNAL; -/* Variables defined in a global context, e.g in the Makefile itself. */ -extern GNode *VAR_GLOBAL; +extern GNode *SCOPE_INTERNAL; +/* Variables defined in a global scope, e.g in the makefile itself. */ +extern GNode *SCOPE_GLOBAL; /* Variables defined on the command line. */ -extern GNode *VAR_CMDLINE; +extern GNode *SCOPE_CMDLINE; /* * Value returned by Var_Parse when an error is encountered. It actually @@ -532,8 +538,8 @@ extern SearchPath dirSearchPath; /* Used for .include "...". */ extern SearchPath *parseIncPath; /* - * Used for .include <...>, for the built-in sys.mk and makefiles from the - * command line arguments. + * Used for .include <...>, for the built-in sys.mk and for makefiles from + * the command line arguments. */ extern SearchPath *sysIncPath; /* The default for sysIncPath. */ @@ -543,18 +549,12 @@ extern SearchPath *defSysIncPath; extern char curdir[]; /* The basename of the program name, suffixed with [n] for sub-makes. */ extern const char *progname; +extern int makelevel; /* Name of the .depend makefile */ extern char *makeDependfile; /* If we replaced environ, this will be non-NULL. */ extern char **savedEnv; -extern int makelevel; - -/* - * We cannot vfork() in a child of vfork(). - * Most systems do not enforce this but some do. - */ -#define vFork() ((getpid() == myPid) ? vfork() : fork()) extern pid_t myPid; #define MAKEFLAGS ".MAKEFLAGS" @@ -606,7 +606,7 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); do { \ if (DEBUG(module)) \ debug_printf args; \ - } while (/*CONSTCOND*/ 0) + } while (/*CONSTCOND*/FALSE) #define DEBUG0(module, text) \ DEBUG_IMPL(module, ("%s", text)) @@ -671,11 +671,13 @@ typedef struct CmdOpts { /* -n: execute almost no commands from the targets */ Boolean noExecute; - /* -q: if true, we aren't supposed to really make anything, just see - * if the targets are out-of-date */ + /* + * -q: if true, do not really make anything, just see if the targets + * are out-of-date + */ Boolean queryFlag; - /* -r: raw mode, without loading the builtin rules. */ + /* -r: raw mode, do not load the builtin rules. */ Boolean noBuiltins; /* -s: don't echo the shell commands before executing them */ @@ -693,7 +695,7 @@ typedef struct CmdOpts { /* -W: if true, makefile parsing warnings are treated as errors */ Boolean parseWarnFatal; - /* -w: print Entering and Leaving for submakes */ + /* -w: print 'Entering' and 'Leaving' for submakes */ Boolean enterFlag; /* -X: if true, do not export variables set on the command line to the @@ -722,7 +724,7 @@ Boolean shouldDieQuietly(GNode *, int); void PrintOnError(GNode *, const char *); void Main_ExportMAKEFLAGS(Boolean); Boolean Main_SetObjdir(Boolean, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); -int mkTempFile(const char *, char **); +int mkTempFile(const char *, char *, size_t); int str2Lst_Append(StringList *, char *); void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *); Boolean GNode_ShouldExecute(GNode *gn); @@ -765,19 +767,19 @@ GNode_IsError(const GNode *gn) } MAKE_INLINE const char * -GNode_VarTarget(GNode *gn) { return Var_ValueDirect(TARGET, gn); } +GNode_VarTarget(GNode *gn) { return GNode_ValueDirect(gn, TARGET); } MAKE_INLINE const char * -GNode_VarOodate(GNode *gn) { return Var_ValueDirect(OODATE, gn); } +GNode_VarOodate(GNode *gn) { return GNode_ValueDirect(gn, OODATE); } MAKE_INLINE const char * -GNode_VarAllsrc(GNode *gn) { return Var_ValueDirect(ALLSRC, gn); } +GNode_VarAllsrc(GNode *gn) { return GNode_ValueDirect(gn, ALLSRC); } MAKE_INLINE const char * -GNode_VarImpsrc(GNode *gn) { return Var_ValueDirect(IMPSRC, gn); } +GNode_VarImpsrc(GNode *gn) { return GNode_ValueDirect(gn, IMPSRC); } MAKE_INLINE const char * -GNode_VarPrefix(GNode *gn) { return Var_ValueDirect(PREFIX, gn); } +GNode_VarPrefix(GNode *gn) { return GNode_ValueDirect(gn, PREFIX); } MAKE_INLINE const char * -GNode_VarArchive(GNode *gn) { return Var_ValueDirect(ARCHIVE, gn); } +GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); } MAKE_INLINE const char * -GNode_VarMember(GNode *gn) { return Var_ValueDirect(MEMBER, gn); } +GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); } #ifdef __GNUC__ #define UNCONST(ptr) ({ \ @@ -851,18 +853,18 @@ pp_skip_hspace(char **pp) } #if defined(lint) -# define MAKE_RCSID(id) extern void do_not_define_rcsid(void) +# define MAKE_RCSID(id) extern void do_not_define_rcsid(void) #elif defined(MAKE_NATIVE) -# include -# define MAKE_RCSID(id) __RCSID(id) +# include +# define MAKE_RCSID(id) __RCSID(id) #elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__) -# define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y) -# define MAKE_RCSID(id) static volatile char \ +# define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y) +# define MAKE_RCSID(id) static volatile char \ MAKE_RCSID_CONCAT(rcsid_, __COUNTER__)[] = id #elif defined(MAKE_ALL_IN_ONE) -# define MAKE_RCSID(id) extern void do_not_define_rcsid(void) +# define MAKE_RCSID(id) extern void do_not_define_rcsid(void) #else -# define MAKE_RCSID(id) static volatile char rcsid[] = id +# define MAKE_RCSID(id) static volatile char rcsid[] = id #endif #endif /* MAKE_MAKE_H */ diff --git a/make_malloc.c b/make_malloc.c index 62cbb0a5cc07..42a69f43704f 100644 --- a/make_malloc.c +++ b/make_malloc.c @@ -1,6 +1,6 @@ -/* $NetBSD: make_malloc.c,v 1.24 2020/12/07 22:37:18 rillig Exp $ */ +/* $NetBSD: make_malloc.c,v 1.25 2021/01/19 20:51:46 rillig Exp $ */ -/*- +/* * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * @@ -30,7 +30,7 @@ #include "make.h" -MAKE_RCSID("$NetBSD: make_malloc.c,v 1.24 2020/12/07 22:37:18 rillig Exp $"); +MAKE_RCSID("$NetBSD: make_malloc.c,v 1.25 2021/01/19 20:51:46 rillig Exp $"); #ifndef USE_EMALLOC diff --git a/make_malloc.h b/make_malloc.h index 4dacc924c40f..72f6c11fd882 100644 --- a/make_malloc.h +++ b/make_malloc.h @@ -1,6 +1,6 @@ -/* $NetBSD: make_malloc.h,v 1.15 2020/12/30 10:03:16 rillig Exp $ */ +/* $NetBSD: make_malloc.h,v 1.16 2021/01/19 20:51:46 rillig Exp $ */ -/*- +/* * Copyright (c) 2009 The NetBSD Foundation, Inc. * All rights reserved. * diff --git a/meta.c b/meta.c index feff26846e4d..b7f1831b3cdf 100644 --- a/meta.c +++ b/meta.c @@ -1,4 +1,4 @@ -/* $NetBSD: meta.c,v 1.168 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: meta.c,v 1.177 2021/02/05 19:19:17 sjg Exp $ */ /* * Implement 'meta' mode. @@ -144,7 +144,10 @@ meta_open_filemon(BuildMon *pbm) * cwd causing getcwd to do a lot more work. * We only care about the descriptor. */ - pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); + if (!opts.compatMake) + pbm->mon_fd = Job_TempFile("filemon.XXXXXX", NULL, 0); + else + pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL, 0); if ((dupfd = dup(pbm->mon_fd)) == -1) { err(1, "Could not dup filemon output!"); } @@ -327,7 +330,7 @@ is_submake(const char *cmd, GNode *gn) Boolean rc = FALSE; if (p_make == NULL) { - p_make = Var_Value(".MAKE", gn).str; + p_make = Var_Value(gn, ".MAKE").str; p_len = strlen(p_make); } cp = strchr(cmd, '$'); @@ -407,7 +410,7 @@ printCMDs(GNode *gn, FILE *fp) } \ return FALSE; \ } \ -} while (/*CONSTCOND*/0) +} while (/*CONSTCOND*/FALSE) /* @@ -483,7 +486,7 @@ meta_create(BuildMon *pbm, GNode *gn) fp = NULL; - dname = Var_Value(".OBJDIR", gn); + dname = Var_Value(gn, ".OBJDIR"); tname = GNode_VarTarget(gn); /* if this succeeds objdir_realpath is realpath of dname */ @@ -538,8 +541,8 @@ meta_create(BuildMon *pbm, GNode *gn) fprintf(fp, "-- command output --\n"); fflush(fp); - Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); - Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); + Global_Append(".MAKE.META.FILES", fname); + Global_Append(".MAKE.META.CREATED", fname); gn->type |= OP_META; /* in case anyone wants to know */ if (metaSilent) { @@ -573,7 +576,7 @@ meta_init(void) { #ifdef USE_FILEMON /* this allows makefiles to test if we have filemon support */ - Var_Set(".MAKE.PATH_FILEMON", filemon_path(), VAR_GLOBAL); + Global_Set(".MAKE.PATH_FILEMON", filemon_path()); #endif } @@ -613,14 +616,15 @@ meta_mode_init(const char *make_mode) get_mode_bf(metaMissing, "missing-meta="); get_mode_bf(metaSilent, "silent="); } - if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { + if (metaVerbose && !Var_Exists(SCOPE_GLOBAL, MAKE_META_PREFIX)) { /* * The default value for MAKE_META_PREFIX * prints the absolute path of the target. * This works be cause :H will generate '.' if there is no / * and :tA will resolve that to cwd. */ - Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL); + Global_Set(MAKE_META_PREFIX, + "Building ${.TARGET:H:tA}/${.TARGET:T}"); } if (once) return; @@ -630,28 +634,28 @@ meta_mode_init(const char *make_mode) * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} */ (void)Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}", - VAR_GLOBAL, VARE_WANTRES, &metaBailiwickStr); + SCOPE_GLOBAL, VARE_WANTRES, &metaBailiwickStr); /* TODO: handle errors */ str2Lst_Append(&metaBailiwick, metaBailiwickStr); /* * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} */ - Var_Append(MAKE_META_IGNORE_PATHS, - "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); + Global_Append(MAKE_META_IGNORE_PATHS, + "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}"); (void)Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}", - VAR_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr); + SCOPE_GLOBAL, VARE_WANTRES, &metaIgnorePathsStr); /* TODO: handle errors */ str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr); /* * We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS} */ - value = Var_Value(MAKE_META_IGNORE_PATTERNS, VAR_GLOBAL); + value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_PATTERNS); if (value.str != NULL) { metaIgnorePatterns = TRUE; FStr_Done(&value); } - value = Var_Value(MAKE_META_IGNORE_FILTER, VAR_GLOBAL); + value = Var_Value(SCOPE_GLOBAL, MAKE_META_IGNORE_FILTER); if (value.str != NULL) { metaIgnoreFilter = TRUE; FStr_Done(&value); @@ -786,13 +790,12 @@ meta_job_error(Job *job, GNode *gn, Boolean ignerr, int status) fprintf(pbm->mfp, "\n*** Error code %d%s\n", status, ignerr ? "(ignored)" : ""); } - if (gn != NULL) { - Var_Set(".ERROR_TARGET", GNode_Path(gn), VAR_GLOBAL); - } + if (gn != NULL) + Global_Set(".ERROR_TARGET", GNode_Path(gn)); getcwd(cwd, sizeof cwd); - Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL); + Global_Set(".ERROR_CWD", cwd); if (pbm->meta_fname[0] != '\0') { - Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL); + Global_Set(".ERROR_META_FILE", pbm->meta_fname); } meta_job_finish(job); } @@ -816,7 +819,7 @@ meta_job_output(Job *job, char *cp, const char *nl) char *cp2; (void)Var_Subst("${" MAKE_META_PREFIX "}", - VAR_GLOBAL, VARE_WANTRES, &meta_prefix); + SCOPE_GLOBAL, VARE_WANTRES, &meta_prefix); /* TODO: handle errors */ if ((cp2 = strchr(meta_prefix, '$')) != NULL) meta_prefix_len = (size_t)(cp2 - meta_prefix); @@ -993,7 +996,13 @@ meta_ignore(GNode *gn, const char *p) const char *expr; char *pm; - Var_Set(".p.", p, gn); + /* + * XXX: This variable is set on a target GNode but is not one of + * the usual local variables. It should be deleted afterwards. + * Ideally it would not be created in the first place, just like + * in a .for loop. + */ + Var_Set(gn, ".p.", p); expr = "${" MAKE_META_IGNORE_PATTERNS ":@m@${.p.:M$m}@}"; (void)Var_Subst(expr, gn, VARE_WANTRES, &pm); /* TODO: handle errors */ @@ -1093,7 +1102,7 @@ meta_oodate(GNode *gn, Boolean oodate) if (oodate) return oodate; /* we're done */ - dname = Var_Value(".OBJDIR", gn); + dname = Var_Value(gn, ".OBJDIR"); tname = GNode_VarTarget(gn); /* if this succeeds fname3 is realpath of dname */ @@ -1146,7 +1155,7 @@ meta_oodate(GNode *gn, Boolean oodate) } /* we want to track all the .meta we read */ - Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); + Global_Append(".MAKE.META.FILES", fname); cmdNode = gn->commands.first; while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { @@ -1198,7 +1207,7 @@ meta_oodate(GNode *gn, Boolean oodate) /* * We need to track pathnames per-process. * - * Each process run by make, starts off in the 'CWD' + * Each process run by make starts off in the 'CWD' * recorded in the .meta file, if it chdirs ('C') * elsewhere we need to track that - but only for * that process. If it forks ('F'), we initialize @@ -1221,18 +1230,18 @@ meta_oodate(GNode *gn, Boolean oodate) if (lastpid > 0) { /* We need to remember these. */ - Var_Set(lcwd_vname, lcwd, VAR_GLOBAL); - Var_Set(ldir_vname, latestdir, VAR_GLOBAL); + Global_SetExpand(lcwd_vname, lcwd); + Global_SetExpand(ldir_vname, latestdir); } snprintf(lcwd_vname, sizeof lcwd_vname, LCWD_VNAME_FMT, pid); snprintf(ldir_vname, sizeof ldir_vname, LDIR_VNAME_FMT, pid); lastpid = pid; - ldir = Var_Value(ldir_vname, VAR_GLOBAL); + ldir = Var_Value(SCOPE_GLOBAL, ldir_vname); if (ldir.str != NULL) { strlcpy(latestdir, ldir.str, sizeof latestdir); FStr_Done(&ldir); } - ldir = Var_Value(lcwd_vname, VAR_GLOBAL); + ldir = Var_Value(SCOPE_GLOBAL, lcwd_vname); if (ldir.str != NULL) { strlcpy(lcwd, ldir.str, sizeof lcwd); FStr_Done(&ldir); @@ -1255,8 +1264,8 @@ meta_oodate(GNode *gn, Boolean oodate) /* Process according to record type. */ switch (buf[0]) { case 'X': /* eXit */ - Var_Delete(lcwd_vname, VAR_GLOBAL); - Var_Delete(ldir_vname, VAR_GLOBAL); + Var_DeleteExpand(SCOPE_GLOBAL, lcwd_vname); + Var_DeleteExpand(SCOPE_GLOBAL, ldir_vname); lastpid = 0; /* no need to save ldir_vname */ break; @@ -1268,9 +1277,9 @@ meta_oodate(GNode *gn, Boolean oodate) child = atoi(p); if (child > 0) { snprintf(cldir, sizeof cldir, LCWD_VNAME_FMT, child); - Var_Set(cldir, lcwd, VAR_GLOBAL); + Global_SetExpand(cldir, lcwd); snprintf(cldir, sizeof cldir, LDIR_VNAME_FMT, child); - Var_Set(cldir, latestdir, VAR_GLOBAL); + Global_SetExpand(cldir, latestdir); #ifdef DEBUG_META_MODE if (DEBUG(META)) debug_printf( @@ -1286,8 +1295,8 @@ meta_oodate(GNode *gn, Boolean oodate) /* Update lcwd and latest directory. */ strlcpy(latestdir, p, sizeof latestdir); strlcpy(lcwd, p, sizeof lcwd); - Var_Set(lcwd_vname, lcwd, VAR_GLOBAL); - Var_Set(ldir_vname, lcwd, VAR_GLOBAL); + Global_SetExpand(lcwd_vname, lcwd); + Global_SetExpand(ldir_vname, lcwd); #ifdef DEBUG_META_MODE DEBUG4(META, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd); @@ -1605,8 +1614,8 @@ meta_oodate(GNode *gn, Boolean oodate) * We have decided it is oodate, so .OODATE needs to be set. * All we can sanely do is set it to .ALLSRC. */ - Var_Delete(OODATE, gn); - Var_Set(OODATE, GNode_VarAllsrc(gn), gn); + Var_Delete(gn, OODATE); + Var_Set(gn, OODATE, GNode_VarAllsrc(gn)); } oodate_out: @@ -1699,7 +1708,7 @@ meta_compat_parent(pid_t child) fflush(stdout); buf[nread] = '\0'; meta_job_output(NULL, buf, ""); - } while (/*CONSTCOND*/0); + } while (/*CONSTCOND*/FALSE); if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) { if (meta_job_event(NULL) <= 0) metafd = -1; diff --git a/metachar.c b/metachar.c index e510eeb66609..dcb049dff44d 100644 --- a/metachar.c +++ b/metachar.c @@ -1,6 +1,6 @@ -/* $NetBSD: metachar.c,v 1.8 2020/10/30 19:14:20 rillig Exp $ */ +/* $NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $ */ -/*- +/* * Copyright (c) 2015 The NetBSD Foundation, Inc. * All rights reserved. * @@ -39,7 +39,7 @@ #include "metachar.h" -MAKE_RCSID("$NetBSD: metachar.c,v 1.8 2020/10/30 19:14:20 rillig Exp $"); +MAKE_RCSID("$NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $"); /* * The following array is used to make a fast determination of which @@ -82,4 +82,3 @@ unsigned char _metachar[128] = { /* x y z { | } ~ del */ 0, 0, 0, 1, 1, 1, 1, 0, }; - diff --git a/metachar.h b/metachar.h index 3c0780a31b8b..04f967229109 100644 --- a/metachar.h +++ b/metachar.h @@ -1,6 +1,6 @@ -/* $NetBSD: metachar.h,v 1.13 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: metachar.h,v 1.15 2021/01/19 20:51:46 rillig Exp $ */ -/*- +/* * Copyright (c) 2015 The NetBSD Foundation, Inc. * All rights reserved. * @@ -37,7 +37,7 @@ extern unsigned char _metachar[]; #define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0) -MAKE_INLINE int +MAKE_INLINE Boolean needshell(const char *cmd) { while (!is_shell_metachar(*cmd) && *cmd != ':' && *cmd != '=') diff --git a/mk/ChangeLog b/mk/ChangeLog index 4c74e668faf1..fa6ea9b9d337 100644 --- a/mk/ChangeLog +++ b/mk/ChangeLog @@ -1,3 +1,12 @@ +2021-01-30 Simon J Gerraty + + * install-mk (MK_VERSION): 20210130 + + * dirdeps.mk: expr 2 - 1 - 1 exits with a bad status + we need to guard against this in DIRDEP_LOADAVG_REPORT. + + * dirdeps.mk: restore respect for TARGET_MACHINE + 2021-01-06 Simon J Gerraty * install-mk (MK_VERSION): 20210101 diff --git a/mk/dirdeps.mk b/mk/dirdeps.mk index c8f03df01c9c..4eb20cedfec7 100644 --- a/mk/dirdeps.mk +++ b/mk/dirdeps.mk @@ -1,4 +1,4 @@ -# $Id: dirdeps.mk,v 1.131 2021/01/07 00:57:51 sjg Exp $ +# $Id: dirdeps.mk,v 1.133 2021/01/31 04:39:22 sjg Exp $ # Copyright (c) 2010-2021, Simon J. Gerraty # Copyright (c) 2010-2018, Juniper Networks, Inc. @@ -265,8 +265,8 @@ N_notmachine := ${.MAKE.DEPENDFILE_PREFERENCE:E:N*${MACHINE}*:${M_ListToSkip}} # if we were included recursively _DEP_TARGET_SPEC should be valid. .if empty(_DEP_TARGET_SPEC) -# if not, just use TARGET_SPEC -_DEP_TARGET_SPEC := ${TARGET_SPEC} +DEP_MACHINE = ${TARGET_MACHINE:U${MACHINE}} +_DEP_TARGET_SPEC := ${DEP_TARGET_SPEC} .if ${.INCLUDEDFROMFILE:U:M${.MAKE.DEPENDFILE_PREFIX}*} != "" # record that we've read dependfile for this _dirdeps_checked.${_CURDIR}.${TARGET_SPEC}: @@ -386,8 +386,10 @@ DIRDEP_LOADAVG_LAST = 0 # yes the expression here is a bit complicated, # the trick is to only eval ${DIRDEP_LOADAVG_LAST::=${now_utc}} # when we want to report. +# Note: expr(1) will exit 1 if the expression evaluates to 0 +# hence the || true DIRDEP_LOADAVG_REPORT = \ - test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST}:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \ + test -z "${"${expr ${now_utc} - ${DIRDEP_LOADAVG_INTEVAL:U60} - ${DIRDEP_LOADAVG_LAST} || true:L:sh:N-*}":?yes${DIRDEP_LOADAVG_LAST::=${now_utc}}:}" || \ echo "${TRACER}`${DIRDEP_LOADAVG_CMD}`" # we suppress SUBDIR when visiting the leaves diff --git a/mk/install-mk b/mk/install-mk index aa3f360b43ae..b014728ca450 100755 --- a/mk/install-mk +++ b/mk/install-mk @@ -55,7 +55,7 @@ # Simon J. Gerraty # RCSid: -# $Id: install-mk,v 1.190 2021/01/07 00:58:42 sjg Exp $ +# $Id: install-mk,v 1.191 2021/01/30 23:16:42 sjg Exp $ # # @(#) Copyright (c) 1994 Simon J. Gerraty # @@ -70,7 +70,7 @@ # sjg@crufty.net # -MK_VERSION=20210101 +MK_VERSION=20210130 OWNER= GROUP= MODE=444 diff --git a/mk/meta.stage.mk b/mk/meta.stage.mk index c78685760c11..463521b79595 100644 --- a/mk/meta.stage.mk +++ b/mk/meta.stage.mk @@ -1,4 +1,4 @@ -# $Id: meta.stage.mk,v 1.60 2020/08/19 17:51:53 sjg Exp $ +# $Id: meta.stage.mk,v 1.61 2021/01/31 04:43:12 sjg Exp $ # # @(#) Copyright (c) 2011-2017, Simon J. Gerraty # @@ -30,8 +30,11 @@ _dirdep ?= ${RELDIR} CLEANFILES+= .dirdep # this allows us to trace dependencies back to their src dir -.dirdep: .NOPATH +.dirdep: .NOPATH +.if !commands(.dirdep) +.dirdep: @echo '${_dirdep}' > $@ +.endif .if defined(NO_POSIX_SHELL) || ${type printf:L:sh:Mbuiltin} == "" _stage_file_basename = `basename $$f` diff --git a/nonints.h b/nonints.h index 41194f104f13..38ac0c85518b 100644 --- a/nonints.h +++ b/nonints.h @@ -1,6 +1,6 @@ -/* $NetBSD: nonints.h,v 1.186 2020/12/28 00:46:24 rillig Exp $ */ +/* $NetBSD: nonints.h,v 1.202 2021/02/05 05:15:12 rillig Exp $ */ -/*- +/* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * @@ -34,7 +34,7 @@ * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94 */ -/*- +/* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * @@ -107,7 +107,11 @@ str_basename(const char *pathname) MAKE_INLINE SearchPath * SearchPath_New(void) -{ return Lst_New(); } +{ + SearchPath *path = bmake_malloc(sizeof *path); + Lst_Init(&path->dirs); + return path; +} void SearchPath_Free(SearchPath *); @@ -282,6 +286,7 @@ const char *Targ_FmtTime(time_t); void Targ_PrintType(int); void Targ_PrintGraph(int); void Targ_Propagate(void); +const char *GNodeMade_Name(GNodeMade); /* var.c */ void Var_Init(void); @@ -290,26 +295,28 @@ void Var_End(void); typedef enum VarEvalFlags { VARE_NONE = 0, - /* Expand and evaluate variables during parsing. + /* + * Expand and evaluate variables during parsing. * * TODO: Document what Var_Parse and Var_Subst return when this flag - * is not set. */ + * is not set. + */ VARE_WANTRES = 1 << 0, - /* Treat undefined variables as errors. - * Must only be used in combination with VARE_WANTRES. */ + /* + * Treat undefined variables as errors. + * Must only be used in combination with VARE_WANTRES. + */ VARE_UNDEFERR = 1 << 1, - /* Keep '$$' as '$$' instead of reducing it to a single '$'. + /* + * Keep '$$' as '$$' instead of reducing it to a single '$'. * * Used in variable assignments using the ':=' operator. It allows * multiple such assignments to be chained without accidentally * expanding '$$file' to '$file' in the first assignment and * interpreting it as '${f}' followed by 'ile' in the next assignment. - * - * See also preserveUndefined, which preserves subexpressions that are - * based on undefined variables; maybe that can be converted to a flag - * as well. */ + */ VARE_KEEP_DOLLAR = 1 << 2, /* @@ -369,15 +376,19 @@ typedef enum VarExportMode { VEM_LITERAL } VarExportMode; -void Var_DeleteVar(const char *, GNode *); -void Var_Delete(const char *, GNode *); +void Var_Delete(GNode *, const char *); +void Var_DeleteExpand(GNode *, const char *); void Var_Undef(const char *); -void Var_Set(const char *, const char *, GNode *); -void Var_SetWithFlags(const char *, const char *, GNode *, VarSetFlags); -void Var_Append(const char *, const char *, GNode *); -Boolean Var_Exists(const char *, GNode *); -FStr Var_Value(const char *, GNode *); -const char *Var_ValueDirect(const char *, GNode *); +void Var_Set(GNode *, const char *, const char *); +void Var_SetExpand(GNode *, const char *, const char *); +void Var_SetWithFlags(GNode *, const char *, const char *, VarSetFlags); +void Var_SetExpandWithFlags(GNode *, const char *, const char *, VarSetFlags); +void Var_Append(GNode *, const char *, const char *); +void Var_AppendExpand(GNode *, const char *, const char *); +Boolean Var_Exists(GNode *, const char *); +Boolean Var_ExistsExpand(GNode *, const char *); +FStr Var_Value(GNode *, const char *); +const char *GNode_ValueDirect(GNode *, const char *); VarParseResult Var_Parse(const char **, GNode *, VarEvalFlags, FStr *); VarParseResult Var_Subst(const char *, GNode *, VarEvalFlags, char **); void Var_Stats(void); @@ -387,6 +398,11 @@ void Var_Export(VarExportMode, const char *); void Var_ExportVars(const char *); void Var_UnExport(Boolean, const char *); +void Global_Set(const char *, const char *); +void Global_SetExpand(const char *, const char *); +void Global_Append(const char *, const char *); +void Global_Delete(const char *); + /* util.c */ typedef void (*SignalProc)(int); SignalProc bmake_signal(int, SignalProc); diff --git a/parse.c b/parse.c index aca1015ab30d..d14c7c46547a 100644 --- a/parse.c +++ b/parse.c @@ -1,4 +1,4 @@ -/* $NetBSD: parse.c,v 1.526 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: parse.c,v 1.549 2021/02/05 05:46:27 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -84,7 +84,7 @@ * Parse_End Clean up the module * * Parse_File Parse a top-level makefile. Included files are - * handled by Parse_include_file though. + * handled by IncludeFile instead. * * Parse_IsVar Return TRUE if the given line is a variable * assignment. Used by MainParseArgs to determine if @@ -124,7 +124,7 @@ #include "pathnames.h" /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: parse.c,v 1.526 2021/01/10 21:20:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: parse.c,v 1.549 2021/02/05 05:46:27 rillig Exp $"); /* types and constants */ @@ -240,44 +240,11 @@ static int fatals = 0; */ /* - * The include chain of makefiles. At the bottom is the top-level makefile - * from the command line, and on top of that, there are the included files or - * .for loops, up to and including the current file. + * The include chain of makefiles. At index 0 is the top-level makefile from + * the command line, followed by the included files or .for loops, up to and + * including the current file. * - * This data could be used to print stack traces on parse errors. As of - * 2020-09-14, this is not done though. It seems quite simple to print the - * tuples (fname:lineno:fromForLoop), from top to bottom. This simple idea is - * made complicated by the fact that the .for loops also use this stack for - * storing information. - * - * The lineno fields of the IFiles with fromForLoop == TRUE look confusing, - * which is demonstrated by the test 'include-main.mk'. They seem sorted - * backwards since they tell the number of completely parsed lines, which for - * a .for loop is right after the terminating .endfor. To compensate for this - * confusion, there is another field first_lineno pointing at the start of the - * .for loop, 1-based for human consumption. - * - * To make the stack trace intuitive, the entry below the first .for loop must - * be ignored completely since neither its lineno nor its first_lineno is - * useful. Instead, the topmost of each chain of .for loop needs to be - * printed twice, once with its first_lineno and once with its lineno. - * - * As of 2020-10-28, using the above rules, the stack trace for the .info line - * in include-subsub.mk would be: - * - * includes[5]: include-subsub.mk:4 - * (lineno, from an .include) - * includes[4]: include-sub.mk:32 - * (lineno, from a .for loop below an .include) - * includes[4]: include-sub.mk:31 - * (first_lineno, from a .for loop, lineno == 32) - * includes[3]: include-sub.mk:30 - * (first_lineno, from a .for loop, lineno == 33) - * includes[2]: include-sub.mk:29 - * (first_lineno, from a .for loop, lineno == 34) - * includes[1]: include-sub.mk:35 - * (not printed since it is below a .for loop) - * includes[0]: include-main.mk:27 + * See PrintStackTrace for how to interpret the data. */ static Vector /* of IFile */ includes; @@ -295,8 +262,8 @@ CurFile(void) } /* include paths */ -SearchPath *parseIncPath; /* dirs for "..." includes */ -SearchPath *sysIncPath; /* dirs for <...> includes */ +SearchPath *parseIncPath; /* directories for "..." includes */ +SearchPath *sysIncPath; /* directories for <...> includes */ SearchPath *defSysIncPath; /* default for sysIncPath */ /* parser tables */ @@ -503,12 +470,61 @@ loadfile(const char *path, int fd) { struct loadedfile *lf = loadedfile_create(path, buf.data, buf.len); - Buf_Destroy(&buf, FALSE); + Buf_DoneData(&buf); return lf; } } -/* old code */ +static void +PrintStackTrace(void) +{ + const IFile *entries; + size_t i, n; + + if (!(DEBUG(PARSE))) + return; + + entries = GetInclude(0); + n = includes.len; + if (n == 0) + return; + n--; /* This entry is already in the diagnostic. */ + + /* + * For the IFiles with fromForLoop, lineno seems to be sorted + * backwards. This is because lineno is the number of completely + * parsed lines, which for a .for loop is right after the + * corresponding .endfor. The intuitive line number comes from + * first_lineno instead, which points at the start of the .for loop. + * + * To make the stack trace intuitive, the entry below each chain of + * .for loop entries must be ignored completely since neither its + * lineno nor its first_lineno is useful. Instead, the topmost of + * each chain of .for loop entries needs to be printed twice, once + * with its first_lineno and once with its lineno. + */ + + for (i = n; i-- > 0;) { + const IFile *entry = entries + i; + const char *fname = entry->fname; + Boolean printLineno; + char dirbuf[MAXPATHLEN + 1]; + + if (fname[0] != '/' && strcmp(fname, "(stdin)") != 0) + fname = realpath(fname, dirbuf); + + printLineno = !entry->fromForLoop; + if (i + 1 < n && entries[i + 1].fromForLoop == printLineno) + printLineno = entry->fromForLoop; + + if (printLineno) + debug_printf("\tin .include from %s:%d\n", + fname, entry->lineno); + if (entry->fromForLoop) + debug_printf("\tin .for loop from %s:%d\n", + fname, entry->first_lineno); + } +} /* Check if the current character is escaped on the current line. */ static Boolean @@ -575,13 +591,13 @@ PrintLocation(FILE *f, const char *fname, size_t lineno) /* Find out which makefile is the culprit. * We try ${.PARSEDIR} and apply realpath(3) if not absolute. */ - dir = Var_Value(".PARSEDIR", VAR_GLOBAL); + dir = Var_Value(SCOPE_GLOBAL, ".PARSEDIR"); if (dir.str == NULL) dir.str = "."; if (dir.str[0] != '/') dir.str = realpath(dir.str, dirbuf); - base = Var_Value(".PARSEFILE", VAR_GLOBAL); + base = Var_Value(SCOPE_GLOBAL, ".PARSEFILE"); if (base.str == NULL) base.str = str_basename(fname); @@ -609,13 +625,17 @@ ParseVErrorInternal(FILE *f, const char *fname, size_t lineno, (void)fflush(f); if (type == PARSE_INFO) - return; - if (type == PARSE_FATAL || opts.parseWarnFatal) - fatals++; - if (opts.parseWarnFatal && !fatal_warning_error_printed) { + goto print_stack_trace; + if (type == PARSE_WARNING && !opts.parseWarnFatal) + goto print_stack_trace; + fatals++; + if (type == PARSE_WARNING && !fatal_warning_error_printed) { Error("parsing warnings being treated as errors"); fatal_warning_error_printed = TRUE; } + +print_stack_trace: + PrintStackTrace(); } static void @@ -676,7 +696,7 @@ Parse_Error(ParseErrorLevel type, const char *fmt, ...) /* - * Parse and handle a .info, .warning or .error directive. + * Parse and handle an .info, .warning or .error directive. * For an .error directive, immediately exit. */ static void @@ -690,7 +710,7 @@ ParseMessage(ParseErrorLevel level, const char *levelName, const char *umsg) return; } - (void)Var_Subst(umsg, VAR_CMDLINE, VARE_WANTRES, &xmsg); + (void)Var_Subst(umsg, SCOPE_CMDLINE, VARE_WANTRES, &xmsg); /* TODO: handle errors */ Parse_Error(level, "%s", xmsg); @@ -867,11 +887,9 @@ static void ParseDependencySourceMain(const char *src) { /* - * In a line like ".MAIN: source1 source2", it means we need to add - * the sources of said target to the list of things to create. - * - * Note that this will only be invoked if the user didn't specify a - * target on the command line and the .MAIN occurs for the first time. + * In a line like ".MAIN: source1 source2", add all sources to the + * list of things to create, but only if the user didn't specify a + * target on the command line and .MAIN occurs for the first time. * * See ParseDoDependencyTargetSpecial, branch SP_MAIN. * See unit-tests/cond-func-make-main.mk. @@ -881,7 +899,7 @@ ParseDependencySourceMain(const char *src) * Add the name to the .TARGETS variable as well, so the user can * employ that, if desired. */ - Var_Append(".TARGETS", src, VAR_GLOBAL); + Global_Append(".TARGETS", src); } static void @@ -918,12 +936,11 @@ ParseDependencySourceOther(const char *src, GNodeType tOp, GNode *gn; /* - * 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. + * The source is not an attribute, so find/create a node for it. + * After that, 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, + * 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. @@ -1013,7 +1030,7 @@ ParseErrorNoDependency(const char *lstart) Parse_Error(PARSE_FATAL, "Unknown directive \"%.*s\"", (int)(dirend - dirstart), dirstart); } else - Parse_Error(PARSE_FATAL, "Need an operator"); + Parse_Error(PARSE_FATAL, "Invalid line type"); } static void @@ -1040,7 +1057,7 @@ ParseDependencyTargetWord(const char **pp, const char *lstart) const char *nested_p = cp; FStr nested_val; - (void)Var_Parse(&nested_p, VAR_CMDLINE, VARE_NONE, + (void)Var_Parse(&nested_p, SCOPE_CMDLINE, VARE_NONE, &nested_val); /* TODO: handle errors */ FStr_Done(&nested_val); @@ -1055,7 +1072,7 @@ ParseDependencyTargetWord(const char **pp, const char *lstart) /* Handle special targets like .PATH, .DEFAULT, .BEGIN, .ORDER. */ static void ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType, - const char *line, /* XXX: bad name */ + const char *targetName, SearchPathList **inout_paths) { switch (*inout_specType) { @@ -1077,7 +1094,7 @@ ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType, case SP_STALE: case SP_ERROR: case SP_INTERRUPT: { - GNode *gn = Targ_GetNode(line); + GNode *gn = Targ_GetNode(targetName); if (doing_depend) ParseMark(gn); gn->type |= OP_NOTMAIN | OP_SPECIAL; @@ -1120,15 +1137,15 @@ ParseDoDependencyTargetSpecial(ParseSpecial *inout_specType, * Call on the suffix module to give us a path to modify. */ static Boolean -ParseDoDependencyTargetPath(const char *line, /* XXX: bad name */ +ParseDoDependencyTargetPath(const char *suffixName, SearchPathList **inout_paths) { SearchPath *path; - path = Suff_GetPath(&line[5]); + path = Suff_GetPath(suffixName); if (path == NULL) { Parse_Error(PARSE_FATAL, - "Suffix '%s' not defined (yet)", &line[5]); + "Suffix '%s' not defined (yet)", suffixName); return FALSE; } @@ -1143,20 +1160,20 @@ ParseDoDependencyTargetPath(const char *line, /* XXX: bad name */ * See if it's a special target and if so set specType to match it. */ static Boolean -ParseDoDependencyTarget(const char *line, /* XXX: bad name */ +ParseDoDependencyTarget(const char *targetName, ParseSpecial *inout_specType, GNodeType *out_tOp, SearchPathList **inout_paths) { int keywd; - if (!(line[0] == '.' && ch_isupper(line[1]))) + if (!(targetName[0] == '.' && ch_isupper(targetName[1]))) return TRUE; /* * See if the target is a special target that must have it * or its sources handled specially. */ - keywd = ParseFindKeyword(line); + keywd = ParseFindKeyword(targetName); if (keywd != -1) { if (*inout_specType == SP_PATH && parseKeywords[keywd].spec != SP_PATH) { @@ -1167,22 +1184,21 @@ ParseDoDependencyTarget(const char *line, /* XXX: bad name */ *inout_specType = parseKeywords[keywd].spec; *out_tOp = parseKeywords[keywd].op; - ParseDoDependencyTargetSpecial(inout_specType, line, + ParseDoDependencyTargetSpecial(inout_specType, targetName, inout_paths); - } else if (strncmp(line, ".PATH", 5) == 0) { + } else if (strncmp(targetName, ".PATH", 5) == 0) { *inout_specType = SP_PATH; - if (!ParseDoDependencyTargetPath(line, inout_paths)) + if (!ParseDoDependencyTargetPath(targetName + 5, inout_paths)) return FALSE; } return TRUE; } static void -ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */ - StringList *curTargs) +ParseDoDependencyTargetMundane(char *targetName, StringList *curTargs) { - if (Dir_HasWildcards(line)) { + if (Dir_HasWildcards(targetName)) { /* * Targets are to be sought only in the current directory, * so create an empty path for the thing. Note we need to @@ -1191,7 +1207,7 @@ ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */ */ SearchPath *emptyPath = SearchPath_New(); - Dir_Expand(line, emptyPath, curTargs); + SearchPath_Expand(emptyPath, targetName, curTargs); SearchPath_Free(emptyPath); } else { @@ -1199,7 +1215,7 @@ ParseDoDependencyTargetMundane(char *line, /* XXX: bad name */ * No wildcards, but we want to avoid code duplication, * so create a list with the word on it. */ - Lst_Append(curTargs, line); + Lst_Append(curTargs, targetName); } /* Apply the targets. */ @@ -1323,7 +1339,7 @@ ParseDoDependencySourcesEmpty(ParseSpecial specType, SearchPathList *paths) break; #ifdef POSIX case SP_POSIX: - Var_Set("%POSIX", "1003.2", VAR_GLOBAL); + Global_Set("%POSIX", "1003.2"); break; #endif default: @@ -1337,7 +1353,7 @@ AddToPaths(const char *dir, SearchPathList *paths) if (paths != NULL) { SearchPathListNode *ln; for (ln = paths->first; ln != NULL; ln = ln->next) - (void)Dir_AddDir(ln->datum, dir); + (void)SearchPath_Add(ln->datum, dir); } } @@ -1440,7 +1456,7 @@ ParseDoDependencyTargets(char **inout_cp, * there was an error in the specification. On error, * line should remain untouched. */ - if (!Arch_ParseArchive(&tgt, targets, VAR_CMDLINE)) { + if (!Arch_ParseArchive(&tgt, targets, SCOPE_CMDLINE)) { Parse_Error(PARSE_FATAL, "Error in archive specification: \"%s\"", tgt); @@ -1545,7 +1561,8 @@ ParseDoDependencySourcesMundane(char *start, char *end, if (*end == '(') { GNodeList sources = LST_INIT; - if (!Arch_ParseArchive(&start, &sources, VAR_CMDLINE)) { + if (!Arch_ParseArchive(&start, &sources, + SCOPE_CMDLINE)) { Parse_Error(PARSE_FATAL, "Error in source archive spec \"%s\"", start); @@ -1866,13 +1883,13 @@ Parse_IsVar(const char *p, VarAssign *out_var) * Check for syntax errors such as unclosed expressions or unknown modifiers. */ static void -VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt) +VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *scope) { if (opts.strict) { if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) { char *expandedValue; - (void)Var_Subst(uvalue, ctxt, VARE_NONE, + (void)Var_Subst(uvalue, scope, VARE_NONE, &expandedValue); /* TODO: handle errors */ free(expandedValue); @@ -1881,31 +1898,32 @@ VarCheckSyntax(VarAssignOp type, const char *uvalue, GNode *ctxt) } static void -VarAssign_EvalSubst(const char *name, const char *uvalue, GNode *ctxt, +VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue, FStr *out_avalue) { - const char *avalue; char *evalue; /* * make sure that we set the variable the first time to nothing - * so that it gets substituted! + * so that it gets substituted. + * + * TODO: Add a test that demonstrates why this code is needed, + * apart from making the debug log longer. */ - if (!Var_Exists(name, ctxt)) - Var_Set(name, "", ctxt); + if (!Var_ExistsExpand(scope, name)) + Var_SetExpand(scope, name, ""); - (void)Var_Subst(uvalue, ctxt, + (void)Var_Subst(uvalue, scope, VARE_WANTRES | VARE_KEEP_DOLLAR | VARE_KEEP_UNDEF, &evalue); /* TODO: handle errors */ - avalue = evalue; - Var_Set(name, avalue, ctxt); + Var_SetExpand(scope, name, evalue); - *out_avalue = (FStr){ avalue, evalue }; + *out_avalue = FStr_InitOwn(evalue); } static void -VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt, +VarAssign_EvalShell(const char *name, const char *uvalue, GNode *scope, FStr *out_avalue) { FStr cmd; @@ -1915,14 +1933,14 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt, cmd = FStr_InitRefer(uvalue); if (strchr(cmd.str, '$') != NULL) { char *expanded; - (void)Var_Subst(cmd.str, VAR_CMDLINE, + (void)Var_Subst(cmd.str, SCOPE_CMDLINE, VARE_WANTRES | VARE_UNDEFERR, &expanded); /* TODO: handle errors */ cmd = FStr_InitOwn(expanded); } cmdOut = Cmd_Exec(cmd.str, &errfmt); - Var_Set(name, cmdOut, ctxt); + Var_SetExpand(scope, name, cmdOut); *out_avalue = FStr_InitOwn(cmdOut); if (errfmt != NULL) @@ -1934,31 +1952,32 @@ VarAssign_EvalShell(const char *name, const char *uvalue, GNode *ctxt, /* * Perform a variable assignment. * - * The actual value of the variable is returned in *out_avalue and - * *out_avalue_freeIt. Especially for VAR_SUBST and VAR_SHELL this can differ - * from the literal value. + * The actual value of the variable is returned in *out_TRUE_avalue. + * Especially for VAR_SUBST and VAR_SHELL this can differ from the literal + * value. * - * Return whether the assignment was actually done. The assignment is only - * skipped if the operator is '?=' and the variable already exists. + * Return whether the assignment was actually performed, which is usually + * the case. It is only skipped if the operator is '?=' and the variable + * already exists. */ static Boolean VarAssign_Eval(const char *name, VarAssignOp op, const char *uvalue, - GNode *ctxt, FStr *out_TRUE_avalue) + GNode *scope, FStr *out_TRUE_avalue) { FStr avalue = FStr_InitRefer(uvalue); if (op == VAR_APPEND) - Var_Append(name, uvalue, ctxt); + Var_AppendExpand(scope, name, uvalue); else if (op == VAR_SUBST) - VarAssign_EvalSubst(name, uvalue, ctxt, &avalue); + VarAssign_EvalSubst(scope, name, uvalue, &avalue); else if (op == VAR_SHELL) - VarAssign_EvalShell(name, uvalue, ctxt, &avalue); + VarAssign_EvalShell(name, uvalue, scope, &avalue); else { - if (op == VAR_DEFAULT && Var_Exists(name, ctxt)) + if (op == VAR_DEFAULT && Var_ExistsExpand(scope, name)) return FALSE; /* Normal assignment -- just do it. */ - Var_Set(name, uvalue, ctxt); + Var_SetExpand(scope, name, uvalue); } *out_TRUE_avalue = avalue; @@ -1984,14 +2003,14 @@ VarAssignSpecial(const char *name, const char *avalue) Var_ExportVars(avalue); } -/* Perform the variable variable assignment in the given context. */ +/* Perform the variable variable assignment in the given scope. */ void -Parse_DoVar(VarAssign *var, GNode *ctxt) +Parse_DoVar(VarAssign *var, GNode *scope) { FStr avalue; /* actual value (maybe expanded) */ - VarCheckSyntax(var->op, var->value, ctxt); - if (VarAssign_Eval(var->varname, var->op, var->value, ctxt, &avalue)) { + VarCheckSyntax(var->op, var->value, scope); + if (VarAssign_Eval(var->varname, var->op, var->value, scope, &avalue)) { VarAssignSpecial(var->varname, avalue.str); FStr_Done(&avalue); } @@ -2086,7 +2105,7 @@ ParseAddCmd(GNode *gn, char *cmd) void Parse_AddIncludeDir(const char *dir) { - (void)Dir_AddDir(parseIncPath, dir); + (void)SearchPath_Add(parseIncPath, dir); } /* @@ -2100,7 +2119,7 @@ Parse_AddIncludeDir(const char *dir) * line options. */ static void -Parse_include_file(char *file, Boolean isSystem, Boolean depinc, Boolean silent) +IncludeFile(char *file, Boolean isSystem, Boolean depinc, Boolean silent) { struct loadedfile *lf; char *fullname; /* full pathname of file */ @@ -2175,8 +2194,8 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, Boolean silent) /* * Look for it on the system path */ - SearchPath *path = Lst_IsEmpty(sysIncPath) ? defSysIncPath - : sysIncPath; + SearchPath *path = Lst_IsEmpty(&sysIncPath->dirs) + ? defSysIncPath : sysIncPath; fullname = Dir_FindFile(file, path); } @@ -2206,12 +2225,12 @@ Parse_include_file(char *file, Boolean isSystem, Boolean depinc, Boolean silent) } static void -ParseDoInclude(char *line /* XXX: bad name */) +ParseDoInclude(char *directive) { char endc; /* the character which ends the file spec */ char *cp; /* current position in file spec */ - Boolean silent = line[0] != 'i'; - char *file = line + (silent ? 8 : 7); + Boolean silent = directive[0] != 'i'; + char *file = directive + (silent ? 8 : 7); /* Skip to delimiter character so we know where to look */ pp_skip_hspace(&file); @@ -2248,10 +2267,10 @@ ParseDoInclude(char *line /* XXX: bad name */) * Substitute for any variables in the filename before trying to * find the file. */ - (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &file); + (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &file); /* TODO: handle errors */ - Parse_include_file(file, endc == '>', line[0] == 'd', silent); + IncludeFile(file, endc == '>', directive[0] == 'd', silent); free(file); } @@ -2275,8 +2294,8 @@ SetFilenameVars(const char *filename, const char *dirvar, const char *filevar) basename = slash + 1; } - Var_Set(dirvar, dirname, VAR_GLOBAL); - Var_Set(filevar, basename, VAR_GLOBAL); + Global_SetExpand(dirvar, dirname); + Global_SetExpand(filevar, basename); DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n", __func__, dirvar, dirname, filevar, basename); @@ -2314,8 +2333,8 @@ ParseSetParseFile(const char *filename) SetFilenameVars(including, ".INCLUDEDFROMDIR", ".INCLUDEDFROMFILE"); } else { - Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); - Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); + Global_Delete(".INCLUDEDFROMDIR"); + Global_Delete(".INCLUDEDFROMFILE"); } } @@ -2352,7 +2371,7 @@ StrContainsWord(const char *str, const char *word) static Boolean VarContainsWord(const char *varname, const char *word) { - FStr val = Var_Value(varname, VAR_GLOBAL); + FStr val = Var_Value(SCOPE_GLOBAL, varname); Boolean found = val.str != NULL && StrContainsWord(val.str, word); FStr_Done(&val); return found; @@ -2369,7 +2388,7 @@ static void ParseTrackInput(const char *name) { if (!VarContainsWord(MAKE_MAKEFILES, name)) - Var_Append(MAKE_MAKEFILES, name, VAR_GLOBAL); + Global_Append(MAKE_MAKEFILES, name); } @@ -2486,7 +2505,7 @@ ParseTraditionalInclude(char *line) * Substitute for any variables in the file name before trying to * find the thing. */ - (void)Var_Subst(file, VAR_CMDLINE, VARE_WANTRES, &all_files); + (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &all_files); /* TODO: handle errors */ if (*file == '\0') { @@ -2504,7 +2523,7 @@ ParseTraditionalInclude(char *line) else done = TRUE; - Parse_include_file(file, FALSE, FALSE, silent); + IncludeFile(file, FALSE, FALSE, silent); } out: free(all_files); @@ -2536,7 +2555,7 @@ ParseGmakeExport(char *line) /* * Expand the value before putting it in the environment. */ - (void)Var_Subst(value, VAR_CMDLINE, VARE_WANTRES, &value); + (void)Var_Subst(value, SCOPE_CMDLINE, VARE_WANTRES, &value); /* TODO: handle errors */ setenv(variable, value, 1); @@ -2581,16 +2600,16 @@ ParseEOF(void) } /* Dispose of curFile info */ - /* Leak curFile->fname because all the gnodes have pointers to it. */ + /* Leak curFile->fname because all the GNodes have pointers to it. */ free(curFile->buf_freeIt); Vector_Pop(&includes); if (includes.len == 0) { /* We've run out of input */ - Var_Delete(".PARSEDIR", VAR_GLOBAL); - Var_Delete(".PARSEFILE", VAR_GLOBAL); - Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); - Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); + Global_Delete(".PARSEDIR"); + Global_Delete(".PARSEFILE"); + Global_Delete(".INCLUDEDFROMDIR"); + Global_Delete(".INCLUDEDFROMFILE"); return FALSE; } @@ -2781,7 +2800,6 @@ ParseGetLine(GetLineMode mode) char *firstBackslash; char *firstComment; - /* Loop through blank lines and comment lines */ for (;;) { ParseRawLineResult res = ParseRawLine(curFile, &line, &line_end, &firstBackslash, &firstComment); @@ -3048,7 +3066,7 @@ ParseVarassign(const char *line) return FALSE; FinishDependencyGroup(); - Parse_DoVar(&var, VAR_GLOBAL); + Parse_DoVar(&var, SCOPE_GLOBAL); return TRUE; } @@ -3130,7 +3148,7 @@ ParseDependency(char *line) * It simply returns the special empty string var_Error, * which cannot be detected in the result of Var_Subst. */ eflags = opts.strict ? VARE_WANTRES : VARE_WANTRES | VARE_UNDEFERR; - (void)Var_Subst(line, VAR_CMDLINE, eflags, &expanded_line); + (void)Var_Subst(line, SCOPE_CMDLINE, eflags, &expanded_line); /* TODO: handle errors */ /* Need a fresh list for the target nodes */ @@ -3274,13 +3292,11 @@ Parse_MainName(GNodeList *mainList) if (mainNode == NULL) Punt("no target to make."); - if (mainNode->type & OP_DOUBLEDEP) { - Lst_Append(mainList, mainNode); + Lst_Append(mainList, mainNode); + if (mainNode->type & OP_DOUBLEDEP) Lst_AppendAll(mainList, &mainNode->cohorts); - } else - Lst_Append(mainList, mainNode); - Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); + Global_Append(".TARGETS", mainNode->name); } int diff --git a/str.c b/str.c index c2954ec5326d..c486df6d3d84 100644 --- a/str.c +++ b/str.c @@ -1,6 +1,6 @@ -/* $NetBSD: str.c,v 1.78 2021/01/10 23:59:53 rillig Exp $ */ +/* $NetBSD: str.c,v 1.81 2021/02/01 22:36:28 rillig Exp $ */ -/*- +/* * Copyright (c) 1988, 1989, 1990, 1993 * The Regents of the University of California. All rights reserved. * @@ -32,7 +32,7 @@ * SUCH DAMAGE. */ -/*- +/* * Copyright (c) 1989 by Berkeley Softworks * All rights reserved. * @@ -71,7 +71,7 @@ #include "make.h" /* "@(#)str.c 5.8 (Berkeley) 6/1/90" */ -MAKE_RCSID("$NetBSD: str.c,v 1.78 2021/01/10 23:59:53 rillig Exp $"); +MAKE_RCSID("$NetBSD: str.c,v 1.81 2021/02/01 22:36:28 rillig Exp $"); /* Return the concatenation of s1 and s2, freshly allocated. */ char * @@ -156,7 +156,7 @@ Str_Words(const char *str, Boolean expand) inquote = '\0'; word_start = words_buf; word_end = words_buf; - for (str_p = str;; ++str_p) { + for (str_p = str;; str_p++) { char ch = *str_p; switch (ch) { case '"': @@ -324,9 +324,15 @@ Str_Match(const char *str, const char *pat) break; return FALSE; } - /* XXX: This naive comparison makes the parser - * for the pattern dependent on the actual of - * the string. This is unpredictable. */ + /* + * XXX: This naive comparison makes the + * control flow of the pattern parser + * dependent on the actual value of the + * string. This is unpredictable. It may be + * though that the code only looks wrong but + * actually all code paths result in the same + * behavior. This needs further tests. + */ if (*pat == *str) break; if (pat[1] == '-') { diff --git a/suff.c b/suff.c index db9ad982ecef..91e8bc613eb8 100644 --- a/suff.c +++ b/suff.c @@ -1,4 +1,4 @@ -/* $NetBSD: suff.c,v 1.335 2021/01/10 21:20:46 rillig Exp $ */ +/* $NetBSD: suff.c,v 1.345 2021/02/05 05:15:12 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -114,7 +114,7 @@ #include "dir.h" /* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */ -MAKE_RCSID("$NetBSD: suff.c,v 1.335 2021/01/10 21:20:46 rillig Exp $"); +MAKE_RCSID("$NetBSD: suff.c,v 1.345 2021/02/05 05:15:12 rillig Exp $"); typedef List SuffixList; typedef ListNode SuffixListNode; @@ -885,7 +885,7 @@ Suff_DoPaths(void) for (ln = sufflist.first; ln != NULL; ln = ln->next) { Suffix *suff = ln->datum; - if (!Lst_IsEmpty(suff->searchPath)) { + if (!Lst_IsEmpty(&suff->searchPath->dirs)) { #ifdef INCLUDES if (suff->flags & SUFF_INCLUDE) SearchPath_AddAll(includesPath, @@ -902,12 +902,12 @@ Suff_DoPaths(void) } } - flags = SearchPath_ToFlags("-I", includesPath); - Var_Set(".INCLUDES", flags, VAR_GLOBAL); + flags = SearchPath_ToFlags(includesPath, "-I"); + Global_Set(".INCLUDES", flags); free(flags); - flags = SearchPath_ToFlags("-L", libsPath); - Var_Set(".LIBS", flags, VAR_GLOBAL); + flags = SearchPath_ToFlags(libsPath, "-L"); + Global_Set(".LIBS", flags); free(flags); SearchPath_Free(includesPath); @@ -1176,14 +1176,14 @@ FindCmds(Candidate *targ, CandidateSearcher *cs) GNode *tgn; /* Target GNode */ GNode *sgn; /* Source GNode */ size_t prefLen; /* The length of the defined prefix */ - Suffix *suff; /* Suffix on matching beastie */ + Suffix *suff; /* Suffix of the matching candidate */ Candidate *ret; /* Return value */ tgn = targ->node; prefLen = strlen(targ->prefix); for (gln = tgn->children.first; gln != NULL; gln = gln->next) { - const char *cp; + const char *base; sgn = gln->datum; @@ -1198,11 +1198,11 @@ FindCmds(Candidate *targ, CandidateSearcher *cs) continue; } - cp = str_basename(sgn->name); - if (strncmp(cp, targ->prefix, prefLen) != 0) + base = str_basename(sgn->name); + if (strncmp(base, targ->prefix, prefLen) != 0) continue; /* The node matches the prefix, see if it has a known suffix. */ - suff = FindSuffixByName(cp + prefLen); + suff = FindSuffixByName(base + prefLen); if (suff == NULL) continue; @@ -1246,7 +1246,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn) * Expand the word along the chosen path */ Lst_Init(&expansions); - Dir_Expand(cgn->name, Suff_FindPath(cgn), &expansions); + SearchPath_Expand(Suff_FindPath(cgn), cgn->name, &expansions); while (!Lst_IsEmpty(&expansions)) { GNode *gn; @@ -1388,9 +1388,9 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn) if (cgn->type & OP_ARCHV) { /* - * Node was an archive(member) target, so we want to + * Node was an 'archive(member)' target, so * call on the Arch module to find the nodes for us, - * expanding variables in the parent's context. + * expanding variables in the parent's scope. */ char *p = cp; (void)Arch_ParseArchive(&p, &members, pgn); @@ -1626,8 +1626,8 @@ FindDepsArchive(GNode *gn, CandidateSearcher *cs) gn->unmade++; /* Copy in the variables from the member node to this one. */ - Var_Set(PREFIX, GNode_VarPrefix(mem), gn); - Var_Set(TARGET, GNode_VarTarget(mem), gn); + Var_Set(gn, PREFIX, GNode_VarPrefix(mem)); + Var_Set(gn, TARGET, GNode_VarTarget(mem)); memSuff = mem->suffix; if (memSuff == NULL) { /* Didn't know what it was. */ @@ -1637,10 +1637,10 @@ FindDepsArchive(GNode *gn, CandidateSearcher *cs) /* Set the other two local variables required for this target. */ - Var_Set(MEMBER, name, gn); - Var_Set(ARCHIVE, gn->name, gn); + Var_Set(gn, MEMBER, name); + Var_Set(gn, ARCHIVE, gn->name); /* Set $@ for compatibility with other makes. */ - Var_Set(TARGET, gn->name, gn); + Var_Set(gn, TARGET, gn->name); /* * Now we've got the important local variables set, expand any sources @@ -1691,7 +1691,7 @@ FindDepsLib(GNode *gn) Arch_FindLib(gn, suff->searchPath); } else { Suffix_Unassign(&gn->suffix); - Var_Set(TARGET, gn->name, gn); + Var_Set(gn, TARGET, gn->name); } /* @@ -1699,7 +1699,7 @@ FindDepsLib(GNode *gn) * filesystem conventions, we don't set the regular variables for * the thing. .PREFIX is simply made empty. */ - Var_Set(PREFIX, "", gn); + Var_Set(gn, PREFIX, ""); } static void @@ -1776,7 +1776,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ) if (gn->path == NULL) return; - Var_Set(TARGET, gn->path, gn); + Var_Set(gn, TARGET, gn->path); if (targ != NULL) { /* @@ -1791,7 +1791,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ) savec = gn->path[savep]; gn->path[savep] = '\0'; - Var_Set(PREFIX, str_basename(gn->path), gn); + Var_Set(gn, PREFIX, str_basename(gn->path)); gn->path[savep] = savec; } else { @@ -1800,7 +1800,7 @@ FindDepsRegularPath(GNode *gn, Candidate *targ) * known suffix. */ Suffix_Unassign(&gn->suffix); - Var_Set(PREFIX, str_basename(gn->path), gn); + Var_Set(gn, PREFIX, str_basename(gn->path)); } } @@ -1890,8 +1890,8 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs) } } - Var_Set(TARGET, GNode_Path(gn), gn); - Var_Set(PREFIX, targ != NULL ? targ->prefix : gn->name, gn); + Var_Set(gn, TARGET, GNode_Path(gn)); + Var_Set(gn, PREFIX, targ != NULL ? targ->prefix : gn->name); /* * Now we've got the important local variables set, expand any sources @@ -1981,8 +1981,8 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs) * we need to do is set the standard variables. */ targ->node->type |= OP_DEPS_FOUND; - Var_Set(PREFIX, targ->prefix, targ->node); - Var_Set(TARGET, targ->node->name, targ->node); + Var_Set(targ->node, PREFIX, targ->prefix); + Var_Set(targ->node, TARGET, targ->node->name); } } @@ -2048,8 +2048,8 @@ FindDeps(GNode *gn, CandidateSearcher *cs) gn->type |= OP_DEPS_FOUND; /* Make sure we have these set, may get revised below. */ - Var_Set(TARGET, GNode_Path(gn), gn); - Var_Set(PREFIX, gn->name, gn); + Var_Set(gn, TARGET, GNode_Path(gn)); + Var_Set(gn, PREFIX, gn->name); DEBUG1(SUFF, "SuffFindDeps \"%s\"\n", gn->name); @@ -2137,9 +2137,7 @@ Suffix_Print(Suffix *suff) char flags_buf[SuffixFlags_ToStringSize]; debug_printf(" (%s)", - Enum_FlagsToString(flags_buf, sizeof flags_buf, - suff->flags, - SuffixFlags_ToStringSpecs)); + SuffixFlags_ToString(flags_buf, suff->flags)); } debug_printf("\n"); diff --git a/targ.c b/targ.c index a5194f95381f..2b02f233ac48 100644 --- a/targ.c +++ b/targ.c @@ -1,4 +1,4 @@ -/* $NetBSD: targ.c,v 1.160 2021/01/10 23:59:53 rillig Exp $ */ +/* $NetBSD: targ.c,v 1.165 2021/02/04 21:42:46 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -113,7 +113,7 @@ #include "dir.h" /* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: targ.c,v 1.160 2021/01/10 23:59:53 rillig Exp $"); +MAKE_RCSID("$NetBSD: targ.c,v 1.165 2021/02/04 21:42:46 rillig Exp $"); /* * All target nodes that appeared on the left-hand side of one of the @@ -243,14 +243,14 @@ GNode_Free(void *gnp) * by this node. * * XXX: For the nodes that represent targets or sources (and not - * VAR_GLOBAL), it should be safe to free the variables as well, + * SCOPE_GLOBAL), it should be safe to free the variables as well, * since each node manages the memory for all its variables itself. * - * XXX: The GNodes that are only used as variable contexts (VAR_CMD, - * VAR_GLOBAL, VAR_INTERNAL) are not freed at all (see Var_End, where - * they are not mentioned). These might be freed at all, if their - * variable values are indeed not used anywhere else (see Trace_Init - * for the only suspicious use). + * XXX: The GNodes that are only used as variable scopes (VAR_CMD, + * SCOPE_GLOBAL, SCOPE_INTERNAL) are not freed at all (see Var_End, + * where they are not mentioned). These might be freed at all, if + * their variable values are indeed not used anywhere else (see + * Trace_Init for the only suspicious use). */ HashTable_Done(&gn->vars); @@ -305,7 +305,7 @@ GNode * Targ_NewInternalNode(const char *name) { GNode *gn = GNode_New(name); - Var_Append(".ALLTARGETS", name, VAR_GLOBAL); + Global_Append(".ALLTARGETS", name); Lst_Append(&allTargets, gn); DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name); if (doing_depend) @@ -450,8 +450,8 @@ Targ_PrintType(int type) } } -static const char * -made_name(GNodeMade made) +const char * +GNodeMade_Name(GNodeMade made) { switch (made) { case UNMADE: return "unmade"; @@ -505,10 +505,10 @@ Targ_PrintNode(GNode *gn, int pass) if (gn->mtime != 0) { debug_printf("# last modified %s: %s\n", Targ_FmtTime(gn->mtime), - made_name(gn->made)); + GNodeMade_Name(gn->made)); } else if (gn->made != UNMADE) { debug_printf("# nonexistent (maybe): %s\n", - made_name(gn->made)); + GNodeMade_Name(gn->made)); } else debug_printf("# unmade\n"); } @@ -577,10 +577,10 @@ Targ_PrintGraph(int pass) PrintOnlySources(); debug_printf("#*** Global Variables:\n"); - Var_Dump(VAR_GLOBAL); + Var_Dump(SCOPE_GLOBAL); debug_printf("#*** Command-line Variables:\n"); - Var_Dump(VAR_CMDLINE); + Var_Dump(SCOPE_CMDLINE); debug_printf("\n"); Dir_PrintDirectories(); diff --git a/trace.c b/trace.c index 7506adf51727..840c7995e5c1 100644 --- a/trace.c +++ b/trace.c @@ -1,6 +1,6 @@ -/* $NetBSD: trace.c,v 1.25 2020/12/20 14:32:13 rillig Exp $ */ +/* $NetBSD: trace.c,v 1.28 2021/02/05 05:15:12 rillig Exp $ */ -/*- +/* * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/*- +/* * trace.c -- * handle logging of trace events generated by various parts of make. * @@ -48,7 +48,7 @@ #include "job.h" #include "trace.h" -MAKE_RCSID("$NetBSD: trace.c,v 1.25 2020/12/20 14:32:13 rillig Exp $"); +MAKE_RCSID("$NetBSD: trace.c,v 1.28 2021/02/05 05:15:12 rillig Exp $"); static FILE *trfile; static pid_t trpid; @@ -71,7 +71,7 @@ Trace_Init(const char *pathname) trpid = getpid(); /* XXX: This variable may get overwritten later, which * would make trwd point to undefined behavior. */ - curDir = Var_Value(".CURDIR", VAR_GLOBAL); + curDir = Var_Value(SCOPE_GLOBAL, ".CURDIR"); trwd = curDir.str; trfile = fopen(pathname, "a"); diff --git a/trace.h b/trace.h index 5fd79acab5aa..063fb26ab08c 100644 --- a/trace.h +++ b/trace.h @@ -1,6 +1,6 @@ -/* $NetBSD: trace.h,v 1.5 2020/11/28 08:41:53 rillig Exp $ */ +/* $NetBSD: trace.h,v 1.6 2021/01/19 20:51:46 rillig Exp $ */ -/*- +/* * Copyright (c) 2000 The NetBSD Foundation, Inc. * All rights reserved. * @@ -29,7 +29,7 @@ * POSSIBILITY OF SUCH DAMAGE. */ -/*- +/* * trace.h -- * Definitions pertaining to the tracing of jobs in parallel mode. */ diff --git a/unit-tests/Makefile b/unit-tests/Makefile index 87ed4ef212d4..d649c552a03a 100644 --- a/unit-tests/Makefile +++ b/unit-tests/Makefile @@ -1,6 +1,6 @@ -# $Id: Makefile,v 1.138 2021/01/01 22:55:09 sjg Exp $ +# $Id: Makefile,v 1.143 2021/02/06 18:31:30 sjg Exp $ # -# $NetBSD: Makefile,v 1.260 2020/12/31 03:05:12 rillig Exp $ +# $NetBSD: Makefile,v 1.269 2021/02/06 18:26:03 sjg Exp $ # # Unit tests for make(1) # @@ -30,6 +30,8 @@ # src/tests/usr.bin/make/t_make.sh. # +.MAIN: all + # we use these below but we might be an older make .MAKE.OS?= ${uname -s:L:sh} .MAKE.UID?= ${id -u:L:sh} @@ -45,6 +47,7 @@ TESTS+= cmd-errors-jobs TESTS+= cmd-errors-lint TESTS+= cmd-interrupt TESTS+= cmdline +TESTS+= cmdline-redirect-stdin TESTS+= cmdline-undefined TESTS+= comment TESTS+= compat-error @@ -200,6 +203,7 @@ TESTS+= impsrc TESTS+= include-main TESTS+= job-flags #TESTS+= job-output-long-lines +TESTS+= jobs-empty-commands TESTS+= jobs-error-indirect TESTS+= jobs-error-nested TESTS+= jobs-error-nested-make @@ -256,6 +260,7 @@ TESTS+= opt-m-include-dir TESTS+= opt-no-action TESTS+= opt-no-action-at-all TESTS+= opt-no-action-runflags +TESTS+= opt-no-action-touch TESTS+= opt-query TESTS+= opt-raw TESTS+= opt-silent @@ -439,16 +444,10 @@ TESTS+= varquote # escape-for-item.mk # posix-*.mk (see posix.mk and posix1.mk) -.if ${.OBJDIR} != ${.CURDIR} -RO_OBJDIR:= ${.OBJDIR}/roobj -.else -RO_OBJDIR:= ${TMPDIR:U/tmp}/roobj -.endif # Additional environment variables for some of the tests. # The base environment is -i PATH="$PATH". ENV.depsrc-optional+= TZ=UTC ENV.envfirst= FROM_ENV=value-from-env -ENV.objdir-writable+= RO_OBJDIR=${RO_OBJDIR} ENV.varmisc= FROM_ENV=env ENV.varmisc+= FROM_ENV_BEFORE=env ENV.varmisc+= FROM_ENV_AFTER=env @@ -485,8 +484,9 @@ SED_CMDS.job-output-long-lines= \ ${:D marker should always be at the beginning of the line. } \ -e '/^aa*--- job-b ---$$/d' \ -e '/^bb*--- job-a ---$$/d' -SED_CMDS.objdir-writable= -e 's,${RO_OBJDIR},OBJDIR/roobj,g' SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1} +SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2} +SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3} SED_CMDS.opt-debug-jobs= -e 's,([0-9][0-9]*),(),' SED_CMDS.opt-debug-jobs+= -e 's,pid [0-9][0-9]*,pid ,' SED_CMDS.opt-debug-jobs+= -e 's,Process [0-9][0-9]*,Process ,' @@ -506,11 +506,9 @@ SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output} SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1} SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1} -SED_CMDS.var-op-shell+= \ - -e 's,^${.SHELL:T}: [ 0-9:]*,,' \ - -e 's,^${.SHELL:T}: ,,' \ - -e '/command/s,No such.*,not found,' -SED_CMDS.vardebug+= -e 's,${.SHELL},,' +SED_CMDS.var-op-shell+= ${STD_SED_CMDS.shell} +SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,' +SED_CMDS.vardebug+= -e 's,${.SHELL},,' SED_CMDS.varmod-subst-regex+= \ -e 's,\(Regex compilation error:\).*,\1 (details omitted),' SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: ",' @@ -518,11 +516,13 @@ SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: ",g' +SED_CMDS.varname-empty+= -e '/\.PARSEDIR/d' +SED_CMDS.varname-empty+= -e '/\.SHELL/d' # Some tests need an additional round of postprocessing. POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/' POSTPROC.gnode-submake= awk '/Input graph/, /^$$/' -POSTPROC.varname-empty= ${TOOL_SED} -n -e '/^Var_Set/p' -e '/^out:/p' # Some tests reuse other tests, which makes them unnecessarily fragile. export-all.rawout: export.mk @@ -542,6 +542,11 @@ STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(\.MAKE\.[A-Z_]* *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(MACHINE[_ARCH]* *=\) .*,\1
,' STD_SED_CMDS.dg1+= -e 's,^\(MAKE *=\) .*,\1
,' +STD_SED_CMDS.dg1+= -e 's,^\(\.SHELL *=\) .*,\1
,' + +STD_SED_CMDS.dg2= ${STD_SED_CMDS.dg1} +STD_SED_CMDS.dg2+= -e 's,\(last modified\) ..:..:.. ... ..\, ....,\1 ,' +STD_SED_CMDS.dg3= ${STD_SED_CMDS.dg2} # Omit details such as process IDs from the output of the -dj option. STD_SED_CMDS.dj= \ @@ -558,9 +563,34 @@ STD_SED_CMDS.hide-from-output= \ -e 's,hide-from-output ,,' \ -e 's,hide-from-output,,' -# End of the configuration helpers section. +# Normalize the output for error messages from the shell. +# +# $shell -c '...' +# NetBSD sh ...: not found +# NetBSD ksh ksh: ...: not found +# bash 5.0.18 bash: ...: command not found +# bash 5.1.0 bash: line 1: ...: command not found +# dash dash: 1: ...: not found +# +# $shell -c '< /nonexistent' +# NetBSD sh sh: cannot open /nonexistent: no such file +# NetBSD ksh ksh: cannot open /nonexistent: No such file or directory +# bash 5.0.18 bash: /nonexistent: No such file or directory +# bash 5.1.0 bash: line 1: /nonexistent: No such file or directory +# dash dash: 1: cannot open /nonexistent: No such file +# +# echo '< /nonexistent' | $shell +# NetBSD sh sh: cannot open /nonexistent: no such file +# NetBSD ksh ksh: [1]: cannot open /nonexistent: No such file or directory +# bash 5.0.18 bash: line 1: /nonexistent: No such file or directory +# bash 5.1.0 bash: line 1: /nonexistent: No such file or directory +# dash dash: 1: cannot open /nonexistent: No such file +# +STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: line [0-9][0-9]*: ,,' +STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: [0-9][0-9]*: ,,' +STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: ,,' -.MAIN: all +# End of the configuration helpers section. .-include "Makefile.inc" .-include "Makefile.config" @@ -603,7 +633,22 @@ LANG= C _MKMSG_TEST= : .endif + +# for many tests we need a TMPDIR that will not collide +# with other users. +.if ${.OBJDIR} != ${.CURDIR} +# easy +TMPDIR:= ${.OBJDIR}/tmp +.else +TMPDIR:= ${TMPDIR:U/tmp}/uid${.MAKE.UID} +.endif +# make sure it exists +.if !exist(${TMPDIR}) +x!= echo; mkdir -p ${TMPDIR} +.endif + MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc +MAKE_TEST_ENV+= TMPDIR=${TMPDIR} .if ${.MAKE.OS} == "NetBSD" LIMIT_RESOURCES?= ulimit -v 200000 @@ -637,6 +682,7 @@ _SED_CMDS+= -e 's,${TEST_MAKE:T:S,.,\\.,g}[][0-9]* warning,make warning,' _SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,' # replace anything after 'stopped in' with unit-tests _SED_CMDS+= -e '/stopped/s, /.*, unit-tests,' +_SED_CMDS+= -e 's,${TMPDIR},TMPDIR,g' # strip ${.CURDIR}/ from the output _SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g' _SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g' diff --git a/unit-tests/cmdline-redirect-stdin.exp b/unit-tests/cmdline-redirect-stdin.exp new file mode 100644 index 000000000000..39a9383953dd --- /dev/null +++ b/unit-tests/cmdline-redirect-stdin.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/unit-tests/cmdline-redirect-stdin.mk b/unit-tests/cmdline-redirect-stdin.mk new file mode 100644 index 000000000000..994016b5ec87 --- /dev/null +++ b/unit-tests/cmdline-redirect-stdin.mk @@ -0,0 +1,34 @@ +# $NetBSD: cmdline-redirect-stdin.mk,v 1.1 2021/02/01 20:31:41 rillig Exp $ +# +# Demonstrate that the '!=' assignment operator can read individual lines +# from make's stdin. +# +# This edge case is an implementation detail that has no practical +# application. + +all: .PHONY + @printf '%s\n' "first line" "second line" \ + | ${MAKE} -f ${MAKEFILE} read-lines + +.if make(read-lines) +line1!= read line; echo "$$line" +line2!= read line; echo "$$line" + +.if ${line1} != "first line" +. error line1="${line1}" + +.elif ${line2} == "" +# If this branch is ever reached, the shell from the assignment to line1 +# probably buffers its input. Most shells use unbuffered stdin, and this +# is actually specified by POSIX, which says that "The read utility shall +# read a single line from standard input". This is the reason why the shell +# reads its input byte by byte, which makes it terribly slow for practical +# applications. +. error The shell's read command does not read a single line. + +.elif ${line2} != "second line" +. error line2="${line2}" +.endif + +read-lines: .PHONY +.endif diff --git a/unit-tests/cmdline.exp b/unit-tests/cmdline.exp index 8e981ba5248c..596281ab0a1f 100644 --- a/unit-tests/cmdline.exp +++ b/unit-tests/cmdline.exp @@ -1,5 +1,5 @@ makeobjdir-direct: -show-objdir: /tmp/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 +show-objdir: TMPDIR/6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 makeobjdir-indirect: -show-objdir: /tmp/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/ +show-objdir: TMPDIR/a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45/ exit status 0 diff --git a/unit-tests/cmdline.mk b/unit-tests/cmdline.mk index a3e2de984927..f82e7f967ef8 100644 --- a/unit-tests/cmdline.mk +++ b/unit-tests/cmdline.mk @@ -1,8 +1,8 @@ -# $NetBSD: cmdline.mk,v 1.2 2020/11/15 14:07:53 rillig Exp $ +# $NetBSD: cmdline.mk,v 1.3 2021/02/06 18:26:03 sjg Exp $ # # Tests for command line parsing and related special variables. -TMPBASE?= /tmp/uid${.MAKE.UID} +TMPBASE?= ${TMPDIR:U/tmp/uid${.MAKE.UID}} SUB1= a7b41170-53f8-4cc2-bc5c-e4c3dd93ec45 # just a random UUID SUB2= 6a8899d2-d227-4b55-9b6b-f3c8eeb83fd5 # just a random UUID MAKE_CMD= env TMPBASE=${TMPBASE}/${SUB1} ${.MAKE} -f ${MAKEFILE} -r diff --git a/unit-tests/cond-cmp-numeric-eq.exp b/unit-tests/cond-cmp-numeric-eq.exp index 64e383ef32a2..72d3d935755b 100644 --- a/unit-tests/cond-cmp-numeric-eq.exp +++ b/unit-tests/cond-cmp-numeric-eq.exp @@ -1,4 +1,3 @@ -make: "cond-cmp-numeric-eq.mk" line 67: warning: Unknown operator make: "cond-cmp-numeric-eq.mk" line 67: Malformed conditional (!(12345 = 12345)) make: "cond-cmp-numeric-eq.mk" line 74: Malformed conditional (!(12345 === 12345)) make: Fatal errors encountered -- cannot continue diff --git a/unit-tests/cond-cmp-numeric.exp b/unit-tests/cond-cmp-numeric.exp index 578d53228f6e..4a97b6879e7a 100644 --- a/unit-tests/cond-cmp-numeric.exp +++ b/unit-tests/cond-cmp-numeric.exp @@ -1,14 +1,10 @@ CondParser_Eval: !(${:UINF} > 1e100) -make: "cond-cmp-numeric.mk" line 11: warning: String comparison operator must be either == or != -make: "cond-cmp-numeric.mk" line 11: Malformed conditional (!(${:UINF} > 1e100)) +make: "cond-cmp-numeric.mk" line 11: String comparison operator must be either == or != CondParser_Eval: ${:UNaN} > NaN -make: "cond-cmp-numeric.mk" line 16: warning: String comparison operator must be either == or != -make: "cond-cmp-numeric.mk" line 16: Malformed conditional (${:UNaN} > NaN) +make: "cond-cmp-numeric.mk" line 16: String comparison operator must be either == or != CondParser_Eval: !(${:UNaN} == NaN) lhs = "NaN", rhs = "NaN", op = == CondParser_Eval: 123 ! 123 -lhs = 123.000000, rhs = 123.000000, op = ! -make: "cond-cmp-numeric.mk" line 34: warning: Unknown operator make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/unit-tests/cond-cmp-string.exp b/unit-tests/cond-cmp-string.exp index 6e6c218267bc..a10341ed2121 100644 --- a/unit-tests/cond-cmp-string.exp +++ b/unit-tests/cond-cmp-string.exp @@ -1,8 +1,11 @@ make: "cond-cmp-string.mk" line 18: Malformed conditional (str != str) make: "cond-cmp-string.mk" line 42: Malformed conditional ("string" != "str""ing") -make: "cond-cmp-string.mk" line 49: warning: String comparison operator must be either == or != make: "cond-cmp-string.mk" line 49: Malformed conditional (!("value" = "value")) make: "cond-cmp-string.mk" line 56: Malformed conditional (!("value" === "value")) +make: "cond-cmp-string.mk" line 113: String comparison operator must be either == or != +make: "cond-cmp-string.mk" line 120: String comparison operator must be either == or != +make: "cond-cmp-string.mk" line 127: String comparison operator must be either == or != +make: "cond-cmp-string.mk" line 134: String comparison operator must be either == or != make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-cmp-string.mk b/unit-tests/cond-cmp-string.mk index 6b5cba83bef7..9f3e731b2eb0 100644 --- a/unit-tests/cond-cmp-string.mk +++ b/unit-tests/cond-cmp-string.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-cmp-string.mk,v 1.13 2020/11/15 14:07:53 rillig Exp $ +# $NetBSD: cond-cmp-string.mk,v 1.14 2021/01/19 19:54:57 rillig Exp $ # # Tests for string comparisons in .if conditions. @@ -108,3 +108,31 @@ .if 12345.0 == "12345" . error .endif + +# Strings cannot be compared relationally, only for equality. +.if "string" < "string" +. error +.else +. error +.endif + +# Strings cannot be compared relationally, only for equality. +.if "string" <= "string" +. error +.else +. error +.endif + +# Strings cannot be compared relationally, only for equality. +.if "string" > "string" +. error +.else +. error +.endif + +# Strings cannot be compared relationally, only for equality. +.if "string" >= "string" +. error +.else +. error +.endif diff --git a/unit-tests/cond-func-defined.exp b/unit-tests/cond-func-defined.exp index caf66e39938f..878f56de2ecc 100644 --- a/unit-tests/cond-func-defined.exp +++ b/unit-tests/cond-func-defined.exp @@ -1,7 +1,5 @@ -make: "cond-func-defined.mk" line 23: warning: Missing closing parenthesis for defined() -make: "cond-func-defined.mk" line 23: Malformed conditional (!defined(A B)) -make: "cond-func-defined.mk" line 33: warning: Missing closing parenthesis for defined() -make: "cond-func-defined.mk" line 33: Malformed conditional (defined(DEF) +make: "cond-func-defined.mk" line 23: Missing closing parenthesis for defined() +make: "cond-func-defined.mk" line 33: Missing closing parenthesis for defined() make: "cond-func-defined.mk" line 45: In .for loops, variable expressions for the loop variables are make: "cond-func-defined.mk" line 46: substituted at evaluation time. There is no actual variable make: "cond-func-defined.mk" line 47: involved, even if it feels like it. diff --git a/unit-tests/cond-func.exp b/unit-tests/cond-func.exp index 73b6273d0b09..855b9e5210fd 100644 --- a/unit-tests/cond-func.exp +++ b/unit-tests/cond-func.exp @@ -1,9 +1,6 @@ -make: "cond-func.mk" line 36: warning: Missing closing parenthesis for defined() -make: "cond-func.mk" line 36: Malformed conditional (!defined(A B)) -make: "cond-func.mk" line 51: warning: Missing closing parenthesis for defined() -make: "cond-func.mk" line 51: Malformed conditional (!defined(A&B)) -make: "cond-func.mk" line 54: warning: Missing closing parenthesis for defined() -make: "cond-func.mk" line 54: Malformed conditional (!defined(A|B)) +make: "cond-func.mk" line 36: Missing closing parenthesis for defined() +make: "cond-func.mk" line 51: Missing closing parenthesis for defined() +make: "cond-func.mk" line 54: Missing closing parenthesis for defined() make: "cond-func.mk" line 94: The empty variable is never defined. make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...). make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...). diff --git a/unit-tests/cond-op-not.exp b/unit-tests/cond-op-not.exp index 37f57b7fdfa7..440670ca7249 100644 --- a/unit-tests/cond-op-not.exp +++ b/unit-tests/cond-op-not.exp @@ -3,4 +3,7 @@ make: "cond-op-not.mk" line 37: Not space evaluates to false. make: "cond-op-not.mk" line 41: Not 0 evaluates to true. make: "cond-op-not.mk" line 49: Not 1 evaluates to false. make: "cond-op-not.mk" line 55: Not word evaluates to false. -exit status 0 +make: "cond-op-not.mk" line 59: Malformed conditional (!) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/cond-op-not.mk b/unit-tests/cond-op-not.mk index 388c62d8898f..ffd5bc89e4bf 100644 --- a/unit-tests/cond-op-not.mk +++ b/unit-tests/cond-op-not.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op-not.mk,v 1.6 2020/11/15 14:58:14 rillig Exp $ +# $NetBSD: cond-op-not.mk,v 1.7 2021/01/19 17:49:13 rillig Exp $ # # Tests for the ! operator in .if conditions, which negates its argument. @@ -55,5 +55,12 @@ . info Not word evaluates to false. .endif +# A single exclamation mark is a parse error. +.if ! +. error +.else +. error +.endif + all: @:; diff --git a/unit-tests/cond-op-parentheses.exp b/unit-tests/cond-op-parentheses.exp index a0fa137af4ca..b44093304100 100644 --- a/unit-tests/cond-op-parentheses.exp +++ b/unit-tests/cond-op-parentheses.exp @@ -1,2 +1,6 @@ make: "cond-op-parentheses.mk" line 13: Parentheses can be nested at least to depth 112. -exit status 0 +make: "cond-op-parentheses.mk" line 19: Malformed conditional (() +make: "cond-op-parentheses.mk" line 29: Malformed conditional ()) +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/cond-op-parentheses.mk b/unit-tests/cond-op-parentheses.mk index 39ebc1607455..ca288cad5826 100644 --- a/unit-tests/cond-op-parentheses.mk +++ b/unit-tests/cond-op-parentheses.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op-parentheses.mk,v 1.3 2020/11/15 14:58:14 rillig Exp $ +# $NetBSD: cond-op-parentheses.mk,v 1.4 2021/01/19 17:49:13 rillig Exp $ # # Tests for parentheses in .if conditions. @@ -15,5 +15,22 @@ . error .endif +# An unbalanced opening parenthesis is a parse error. +.if ( +. error +.else +. error +.endif + +# An unbalanced closing parenthesis is a parse error. +# +# As of 2021-01-19, CondParser_Term returned TOK_RPAREN even though this +# function promised to only ever return TOK_TRUE, TOK_FALSE or TOK_ERROR. +.if ) +. error +.else +. error +.endif + all: @:; diff --git a/unit-tests/cond-op.exp b/unit-tests/cond-op.exp index 1a66e5ffeabd..28e8d48e2697 100644 --- a/unit-tests/cond-op.exp +++ b/unit-tests/cond-op.exp @@ -11,6 +11,10 @@ make: "cond-op.mk" line 93: 1 0 0 => 0 1 1 make: "cond-op.mk" line 93: 1 0 1 => 1 1 1 make: "cond-op.mk" line 93: 1 1 0 => 0 1 1 make: "cond-op.mk" line 93: 1 1 1 => 1 1 1 +make: "cond-op.mk" line 104: Malformed conditional (1 &&) +make: "cond-op.mk" line 112: Malformed conditional (0 &&) +make: "cond-op.mk" line 120: Malformed conditional (1 ||) +make: "cond-op.mk" line 129: Malformed conditional (0 ||) make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-op.mk b/unit-tests/cond-op.mk index 170355f6c0ad..2ed451c90391 100644 --- a/unit-tests/cond-op.mk +++ b/unit-tests/cond-op.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-op.mk,v 1.10 2020/11/15 14:58:14 rillig Exp $ +# $NetBSD: cond-op.mk,v 1.13 2021/01/19 18:20:30 rillig Exp $ # # Tests for operators like &&, ||, ! in .if conditions. # @@ -61,8 +61,8 @@ # As soon as the parser sees the '$', it knows that the condition will # be malformed. Therefore there is no point in evaluating it. # -# As of 2020-09-11, that part of the condition is evaluated nevertheless, -# since CondParser_Expr just requests the next token, without restricting +# As of 2021-01-20, that part of the condition is evaluated nevertheless, +# since CondParser_Or just requests the next token, without restricting # the token to the expected tokens. If the parser were to restrict the # valid follow tokens for the token "0" to those that can actually produce # a correct condition (which in this case would be comparison operators, @@ -98,5 +98,39 @@ . endfor .endfor +# This condition is obviously malformed. It is properly detected and also +# was properly detected before 2021-01-19, but only because the left hand +# side of the '&&' evaluated to true. +.if 1 && +. error +.else +. error +.endif + +# This obviously malformed condition was not detected as such before cond.c +# 1.238 from 2021-01-19. +.if 0 && +. error +.else +. error +.endif + +# This obviously malformed condition was not detected as such before cond.c +# 1.238 from 2021-01-19. +.if 1 || +. error +.else +. error +.endif + +# This condition is obviously malformed. It is properly detected and also +# was properly detected before 2021-01-19, but only because the left hand +# side of the '||' evaluated to false. +.if 0 || +. error +.else +. error +.endif + all: @:; diff --git a/unit-tests/cond-token-plain.exp b/unit-tests/cond-token-plain.exp index a87ce35186f3..24cfa6bcbc82 100644 --- a/unit-tests/cond-token-plain.exp +++ b/unit-tests/cond-token-plain.exp @@ -18,12 +18,37 @@ lhs = "yes", rhs = "yes", op = != CondParser_Eval: ${UNDEF:Uundefined}!=undefined lhs = "undefined", rhs = "undefined", op = != CondParser_Eval: ${UNDEF:U12345}>12345 -lhs = 12345.000000, rhs = 12345.000000, op = >1 +lhs = 12345.000000, rhs = 12345.000000, op = > CondParser_Eval: ${UNDEF:U12345}<12345 -lhs = 12345.000000, rhs = 12345.000000, op = <1 +lhs = 12345.000000, rhs = 12345.000000, op = < CondParser_Eval: (${UNDEF:U0})||0 CondParser_Eval: ${:Uvar}&&name != "var&&name" lhs = "var&&name", rhs = "var&&name", op = != CondParser_Eval: ${:Uvar}||name != "var||name" lhs = "var||name", rhs = "var||name", op = != -exit status 0 +CondParser_Eval: bare +make: "cond-token-plain.mk" line 102: A bare word is treated like defined(...), and the variable 'bare' is not defined. +CondParser_Eval: VAR +make: "cond-token-plain.mk" line 107: A bare word is treated like defined(...). +CondParser_Eval: V${:UA}R +make: "cond-token-plain.mk" line 114: ok +CondParser_Eval: V${UNDEF}AR +make: "cond-token-plain.mk" line 122: Undefined variables in bare words expand to an empty string. +CondParser_Eval: 0${:Ux00} +make: "cond-token-plain.mk" line 130: Numbers can be composed from literals and variable expressions. +CondParser_Eval: 0${:Ux01} +make: "cond-token-plain.mk" line 134: Numbers can be composed from literals and variable expressions. +CondParser_Eval: "" == +make: "cond-token-plain.mk" line 140: Missing right-hand-side of operator '==' +CondParser_Eval: == "" +make: "cond-token-plain.mk" line 148: Malformed conditional (== "") +CondParser_Eval: \\ +make: "cond-token-plain.mk" line 163: The variable '\\' is not defined. +CondParser_Eval: \\ +make: "cond-token-plain.mk" line 168: Now the variable '\\' is defined. +CondParser_Eval: "unquoted\"quoted" != unquoted"quoted +lhs = "unquoted"quoted", rhs = "unquoted"quoted", op = != +CondParser_Eval: $$$$$$$$ != "" +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/cond-token-plain.mk b/unit-tests/cond-token-plain.mk index a5ffa37a5c84..89f2247e077c 100644 --- a/unit-tests/cond-token-plain.mk +++ b/unit-tests/cond-token-plain.mk @@ -1,4 +1,4 @@ -# $NetBSD: cond-token-plain.mk,v 1.6 2020/11/15 14:58:14 rillig Exp $ +# $NetBSD: cond-token-plain.mk,v 1.10 2021/01/21 14:08:09 rillig Exp $ # # Tests for plain tokens (that is, string literals without quotes) # in .if conditions. @@ -93,5 +93,100 @@ . error .endif +# A bare word may appear alone in a condition, without any comparison +# operator. It is implicitly converted into defined(bare). +.if bare +. error +.else +. info A bare word is treated like defined(...), and the variable $\ + 'bare' is not defined. +.endif + +VAR= defined +.if VAR +. info A bare word is treated like defined(...). +.else +. error +.endif + +# Bare words may be intermixed with variable expressions. +.if V${:UA}R +. info ok +.else +. error +.endif + +# In bare words, even undefined variables are allowed. Without the bare +# words, undefined variables are not allowed. That feels inconsistent. +.if V${UNDEF}AR +. info Undefined variables in bare words expand to an empty string. +.else +. error +.endif + +.if 0${:Ux00} +. error +.else +. info Numbers can be composed from literals and variable expressions. +.endif + +.if 0${:Ux01} +. info Numbers can be composed from literals and variable expressions. +.else +. error +.endif + +# If the right-hand side is missing, it's a parse error. +.if "" == +. error +.else +. error +.endif + +# If the left-hand side is missing, it's a parse error as well, but without +# a specific error message. +.if == "" +. error +.else +. error +.endif + +# The '\\' is not a line continuation. Neither is it an unquoted string +# literal. Instead, it is parsed as a function argument (ParseFuncArg), +# and in that context, the backslash is just an ordinary character. The +# function argument thus stays '\\' (2 backslashes). This string is passed +# to FuncDefined, and since there is no variable named '\\', the condition +# evaluates to false. +.if \\ +. error +.else +. info The variable '\\' is not defined. +.endif + +${:U\\\\}= backslash +.if \\ +. info Now the variable '\\' is defined. +.else +. error +.endif + +# Anything that doesn't start with a double quote is considered a "bare word". +# Strangely, a bare word may contain double quotes inside. Nobody should ever +# depend on this since it may well be unintended. See CondParser_String. +.if "unquoted\"quoted" != unquoted"quoted +. error +.endif + +# FIXME: In CondParser_String, Var_Parse returns var_Error without a +# corresponding error message. +.if $$$$$$$$ != "" +. error +.else +. error +.endif + +# See cond-token-string.mk for similar tests where the condition is enclosed +# in "quotes". + all: @:; diff --git a/unit-tests/cond-token-string.exp b/unit-tests/cond-token-string.exp index 83009f420325..07b318caa81a 100644 --- a/unit-tests/cond-token-string.exp +++ b/unit-tests/cond-token-string.exp @@ -1,8 +1,18 @@ -make: "cond-token-string.mk" line 9: Unknown modifier 'Z' -make: "cond-token-string.mk" line 9: Malformed conditional ("" != "${:Uvalue:Z}") -make: "cond-token-string.mk" line 18: xvalue is not defined. -make: "cond-token-string.mk" line 24: Malformed conditional (x${:Uvalue} == "") -make: "cond-token-string.mk" line 33: Expected. +make: "cond-token-string.mk" line 13: Unknown modifier 'Z' +make: "cond-token-string.mk" line 13: Malformed conditional ("" != "${:Uvalue:Z}") +make: "cond-token-string.mk" line 22: xvalue is not defined. +make: "cond-token-string.mk" line 28: Malformed conditional (x${:Uvalue} == "") +make: "cond-token-string.mk" line 37: Expected. +CondParser_Eval: "UNDEF" +make: "cond-token-string.mk" line 46: The string literal "UNDEF" is not empty. +CondParser_Eval: " " +make: "cond-token-string.mk" line 55: The string literal " " is not empty, even though it consists of whitespace only. +CondParser_Eval: "${UNDEF}" +make: "cond-token-string.mk" line 64: An undefined variable in quotes expands to an empty string, which then evaluates to false. +CondParser_Eval: "${:Uvalue}" +make: "cond-token-string.mk" line 68: A nonempty variable expression evaluates to true. +CondParser_Eval: "${:U}" +make: "cond-token-string.mk" line 76: An empty variable evaluates to false. make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/cond-token-string.mk b/unit-tests/cond-token-string.mk index 7e110806d408..a92d3a896116 100644 --- a/unit-tests/cond-token-string.mk +++ b/unit-tests/cond-token-string.mk @@ -1,6 +1,10 @@ -# $NetBSD: cond-token-string.mk,v 1.3 2020/11/10 22:23:37 rillig Exp $ +# $NetBSD: cond-token-string.mk,v 1.4 2021/01/21 00:38:28 rillig Exp $ # -# Tests for quoted and unquoted string literals in .if conditions. +# Tests for quoted string literals in .if conditions. +# +# See also: +# cond-token-plain.mk +# Covers string literals without quotes (called "bare words"). # TODO: Implementation @@ -35,5 +39,44 @@ . error .endif +.MAKEFLAGS: -dc + +# A string in quotes is checked whether it is not empty. +.if "UNDEF" +. info The string literal "UNDEF" is not empty. +.else +. error +.endif + +# A space is not empty as well. +# This differs from many other places where whitespace is trimmed. +.if " " +. info The string literal " " is not empty, even though it consists of $\ + whitespace only. +.else +. error +.endif + +.if "${UNDEF}" +. error +.else +. info An undefined variable in quotes expands to an empty string, which $\ + then evaluates to false. +.endif + +.if "${:Uvalue}" +. info A nonempty variable expression evaluates to true. +.else +. error +.endif + +.if "${:U}" +. error +.else +. info An empty variable evaluates to false. +.endif + +.MAKEFLAGS: -d0 + all: @:; diff --git a/unit-tests/cond1.exp b/unit-tests/cond1.exp index b9db035833be..0acd935780a0 100644 --- a/unit-tests/cond1.exp +++ b/unit-tests/cond1.exp @@ -16,7 +16,7 @@ Passed: 4 is not prime 5 is prime -make: warning: String comparison operator must be either == or != +make: String comparison operator must be either == or != make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No OK diff --git a/unit-tests/dir.mk b/unit-tests/dir.mk index 55e22d88b803..36fe2baf978c 100644 --- a/unit-tests/dir.mk +++ b/unit-tests/dir.mk @@ -1,4 +1,4 @@ -# $NetBSD: dir.mk,v 1.8 2020/11/03 18:42:33 rillig Exp $ +# $NetBSD: dir.mk,v 1.9 2021/01/23 10:48:49 rillig Exp $ # # Tests for dir.c. @@ -79,7 +79,7 @@ single-word: # Demonstrate debug logging for filename expansion, especially curly braces. .MAKEFLAGS: -dd -# The below line does not call Dir_Expand yet. +# The below line does not call SearchPath_Expand yet. # It is expanded only when necessary, that is, when the 'debug' target is # indeed made. debug: {{thi,fou}r,fif}twen diff --git a/unit-tests/directive-error.exp b/unit-tests/directive-error.exp index 39a9383953dd..bad12326a514 100644 --- a/unit-tests/directive-error.exp +++ b/unit-tests/directive-error.exp @@ -1 +1,4 @@ -exit status 0 +make: "directive-error.mk" line 13: message + +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/directive-error.mk b/unit-tests/directive-error.mk index 50a0c6c0e84c..135db2159dd2 100644 --- a/unit-tests/directive-error.mk +++ b/unit-tests/directive-error.mk @@ -1,10 +1,13 @@ -# $NetBSD: directive-error.mk,v 1.3 2020/12/13 01:07:54 rillig Exp $ +# $NetBSD: directive-error.mk,v 1.5 2021/01/27 00:02:38 rillig Exp $ # # Tests for the .error directive, which prints an error message and exits # immediately, unlike other "fatal" parse errors, which continue to parse # until the end of the current top-level makefile. +# +# See also: +# opt-warnings-as-errors.mk -# TODO: Implementation - -all: - @:; +# Before parse.c 1.532 from 2021-01-27, the ".error" issued an irrelevant +# message saying "parsing warnings being treated as errors". +.MAKEFLAGS: -W +.error message diff --git a/unit-tests/directive-export-impl.exp b/unit-tests/directive-export-impl.exp index c3ac940d2df0..1a5cf34dbfb8 100644 --- a/unit-tests/directive-export-impl.exp +++ b/unit-tests/directive-export-impl.exp @@ -12,7 +12,7 @@ Result of ${UT_VAR:N*} is "" (VARE_UNDEFERR|VARE_WANTRES, VAR_EXPORTED|VAR_REEXP ParseDoDependency(: ) CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>" Var_Parse: ${:!echo "\$UT_VAR"!} != "<>" with VARE_UNDEFERR|VARE_WANTRES -Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) +Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) Modifier part: "echo "$UT_VAR"" Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none) @@ -21,7 +21,7 @@ Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none) Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none) Var_Parse: ${UT_VAR} with VARE_WANTRES Var_Parse: ${REF}> with VARE_WANTRES -Result of ${:!echo "\$UT_VAR"!} is "<>" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Result of ${:!echo "\$UT_VAR"!} is "<>" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) lhs = "<>", rhs = "<>", op = != ParseReadLine (49): ': ${UT_VAR:N*}' Var_Parse: ${UT_VAR:N*} with VARE_UNDEFERR|VARE_WANTRES @@ -35,7 +35,7 @@ ParseReadLine (53): 'REF= defined' Global:REF = defined CondParser_Eval: ${:!echo "\$UT_VAR"!} != "" Var_Parse: ${:!echo "\$UT_VAR"!} != "" with VARE_UNDEFERR|VARE_WANTRES -Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) +Applying ${:!...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) Modifier part: "echo "$UT_VAR"" Var_Parse: ${.MAKE.EXPORTED:O:u} with VARE_WANTRES Applying ${.MAKE.EXPORTED:O} to "UT_VAR" (VARE_WANTRES, none, none) @@ -44,7 +44,7 @@ Applying ${.MAKE.EXPORTED:u} to "UT_VAR" (VARE_WANTRES, none, none) Result of ${.MAKE.EXPORTED:u} is "UT_VAR" (VARE_WANTRES, none, none) Var_Parse: ${UT_VAR} with VARE_WANTRES Var_Parse: ${REF}> with VARE_WANTRES -Result of ${:!echo "\$UT_VAR"!} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Result of ${:!echo "\$UT_VAR"!} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) lhs = "", rhs = "", op = != ParseReadLine (61): 'all:' ParseDoDependency(all:) diff --git a/unit-tests/directive-for-escape.exp b/unit-tests/directive-for-escape.exp index 3d2d2ec744aa..89a8cbc2e229 100644 --- a/unit-tests/directive-for-escape.exp +++ b/unit-tests/directive-for-escape.exp @@ -27,25 +27,29 @@ make: "directive-for-escape.mk" line 41: value-with-modifier For: end for 1 For: loop body: . info ${:U\${UNDEF\:U\\$\\$} -make: "directive-for-escape.mk" line 52: ${UNDEF:U\$ +make: "directive-for-escape.mk" line 55: ${UNDEF:U\$ For: loop body: . info ${:U{{\}\}} -make: "directive-for-escape.mk" line 52: {{}} +make: "directive-for-escape.mk" line 55: {{}} For: loop body: . info ${:Uend\}} -make: "directive-for-escape.mk" line 52: end} +make: "directive-for-escape.mk" line 55: end} +For: end for 1 +For: loop body: +. info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end} +make: "directive-for-escape.mk" line 66: beginend For: end for 1 For: loop body: . info ${:U\$} -make: "directive-for-escape.mk" line 60: $ +make: "directive-for-escape.mk" line 74: $ For: end for 1 For: loop body: . info ${NUMBERS} ${:Ureplaced} -make: "directive-for-escape.mk" line 68: one two three replaced +make: "directive-for-escape.mk" line 82: one two three replaced For: end for 1 For: loop body: . info ${:Ureplaced} -make: "directive-for-escape.mk" line 78: replaced +make: "directive-for-escape.mk" line 92: replaced For: end for 1 For: loop body: . info . $$i: ${:Uinner} @@ -54,21 +58,18 @@ For: loop body: . info . $$(i): $(:Uinner) . info . $$(i:M*): $(:Uinner:M*) . info . $${i$${:U}}: ${i${:U}} -. info . $${i\}}: ${:Uinner\}} # XXX: unclear why SubstVarLong needs this +. info . $${i\}}: ${:Uinner\}} # XXX: unclear why ForLoop_SubstVarLong needs this . info . $${i2}: ${i2} . info . $${i,}: ${i,} . info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner} -make: "directive-for-escape.mk" line 86: . $i: inner -make: "directive-for-escape.mk" line 87: . ${i}: inner -make: "directive-for-escape.mk" line 88: . ${i:M*}: inner -make: "directive-for-escape.mk" line 89: . $(i): inner -make: "directive-for-escape.mk" line 90: . $(i:M*): inner -make: "directive-for-escape.mk" line 91: . ${i${:U}}: outer -make: "directive-for-escape.mk" line 92: . ${i\}}: inner} -make: "directive-for-escape.mk" line 93: . ${i2}: two -make: "directive-for-escape.mk" line 94: . ${i,}: comma -make: "directive-for-escape.mk" line 95: . adjacent: innerinnerinnerinner -make: no target to make. - -make: stopped in unit-tests -exit status 2 +make: "directive-for-escape.mk" line 100: . $i: inner +make: "directive-for-escape.mk" line 101: . ${i}: inner +make: "directive-for-escape.mk" line 102: . ${i:M*}: inner +make: "directive-for-escape.mk" line 103: . $(i): inner +make: "directive-for-escape.mk" line 104: . $(i:M*): inner +make: "directive-for-escape.mk" line 105: . ${i${:U}}: outer +make: "directive-for-escape.mk" line 106: . ${i\}}: inner} +make: "directive-for-escape.mk" line 107: . ${i2}: two +make: "directive-for-escape.mk" line 108: . ${i,}: comma +make: "directive-for-escape.mk" line 109: . adjacent: innerinnerinnerinner +exit status 0 diff --git a/unit-tests/directive-for-escape.mk b/unit-tests/directive-for-escape.mk index 0bd2af68625a..d61f05cc53cc 100644 --- a/unit-tests/directive-for-escape.mk +++ b/unit-tests/directive-for-escape.mk @@ -1,4 +1,4 @@ -# $NetBSD: directive-for-escape.mk,v 1.3 2020/12/31 14:26:37 rillig Exp $ +# $NetBSD: directive-for-escape.mk,v 1.6 2021/01/25 19:05:39 rillig Exp $ # # Test escaping of special characters in the iteration values of a .for loop. # These values get expanded later using the :U variable modifier, and this @@ -41,17 +41,31 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier) . info $i .endfor -# Cover the code for nested '{}' in for_var_len. +# Try to cover the code for nested '{}' in for_var_len, without success. # -# The value of VALUES is not a variable expression. Instead, it is meant to -# represent dollar, lbrace, "UNDEF:U", backslash, dollar, backslash, dollar, -# space, nested braces, space, "end}". +# The value of VALUES is not meant to be a variable expression. Instead, it +# is meant to represent dollar, lbrace, "UNDEF:U", backslash, dollar, +# backslash, dollar, space, nested braces, space, "end}". +# +# The .for loop splits ${VALUES} into 3 words, at the space characters, since +# these are not escaped. VALUES= $${UNDEF:U\$$\$$ {{}} end} # XXX: Where does the '\$$\$$' get converted into a single '\$'? .for i in ${VALUES} . info $i .endfor +# Second try to cover the code for nested '{}' in for_var_len. +# +# XXX: It is wrong that for_var_len requires the braces to be balanced. +# Each variable modifier has its own inconsistent way of parsing nested +# variable expressions, braces and parentheses. The only sensible thing +# to do is therefore to let Var_Parse do all the parsing work. +VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end +.for i in ${VALUES} +. info $i +.endfor + # A single trailing dollar doesn't happen in practice. # The dollar sign is correctly passed through to the body of the .for loop. # There, it is expanded by the .info directive, but even there a trailing @@ -89,8 +103,10 @@ i,= comma . info . $$(i): $(i) . info . $$(i:M*): $(i:M*) . info . $${i$${:U}}: ${i${:U}} -. info . $${i\}}: ${i\}} # XXX: unclear why SubstVarLong needs this +. info . $${i\}}: ${i\}} # XXX: unclear why ForLoop_SubstVarLong needs this . info . $${i2}: ${i2} . info . $${i,}: ${i,} . info . adjacent: $i${i}${i:M*}$i .endfor + +all: diff --git a/unit-tests/directive-ifdef.exp b/unit-tests/directive-ifdef.exp index b8453ecc4124..1a1358988f39 100644 --- a/unit-tests/directive-ifdef.exp +++ b/unit-tests/directive-ifdef.exp @@ -1,2 +1,4 @@ make: "directive-ifdef.mk" line 12: Function calls in .ifdef are possible. +make: "directive-ifdef.mk" line 23: String literals are tested for emptiness. +make: "directive-ifdef.mk" line 27: String literals are tested for emptiness. Whitespace is non-empty. exit status 0 diff --git a/unit-tests/directive-ifdef.mk b/unit-tests/directive-ifdef.mk index 516e095c464f..12f3648e8b2c 100644 --- a/unit-tests/directive-ifdef.mk +++ b/unit-tests/directive-ifdef.mk @@ -1,4 +1,4 @@ -# $NetBSD: directive-ifdef.mk,v 1.3 2020/11/08 22:38:28 rillig Exp $ +# $NetBSD: directive-ifdef.mk,v 1.4 2021/01/21 23:03:41 rillig Exp $ # # Tests for the .ifdef directive. @@ -14,5 +14,20 @@ DEFINED= defined . error .endif +# String literals are handled the same in all variants of the .if directive. +# They evaluate to true if they are not empty. Whitespace counts as non-empty +# as well. +.ifdef "" +. error +.else +. info String literals are tested for emptiness. +.endif + +.ifdef " " +. info String literals are tested for emptiness. Whitespace is non-empty. +.else +. error +.endif + all: @:; diff --git a/unit-tests/directive-include-fatal.mk b/unit-tests/directive-include-fatal.mk index 22b30ae909c2..6744f9e80e5c 100755 --- a/unit-tests/directive-include-fatal.mk +++ b/unit-tests/directive-include-fatal.mk @@ -1,4 +1,4 @@ -# $NetBSD: directive-include-fatal.mk,v 1.2 2020/09/13 10:20:11 rillig Exp $ +# $NetBSD: directive-include-fatal.mk,v 1.3 2021/02/01 22:16:57 rillig Exp $ # # Test for the .include directive combined with fatal errors. # @@ -6,7 +6,7 @@ # suspicious, as if it were possible to suppress fatal errors by including # another file. It was a false alarm though, since Parse_File only handles # the top-level makefiles from the command line. Any included files are -# handled by Parse_include_file instead, and that function does not reset +# handled by IncludeFile instead, and that function does not reset # the "fatals" counter. # Using an undefined variable in a condition generates a fatal error. diff --git a/unit-tests/envfirst.mk b/unit-tests/envfirst.mk index b5cfc4f5c578..60a331a5db64 100644 --- a/unit-tests/envfirst.mk +++ b/unit-tests/envfirst.mk @@ -1,4 +1,4 @@ -# $NetBSD: envfirst.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $ +# $NetBSD: envfirst.mk,v 1.5 2021/02/04 21:42:47 rillig Exp $ # # The -e option makes environment variables stronger than global variables. @@ -33,7 +33,7 @@ FROM_ENV?= default . error ${FROM_ENV} .endif -# Even .undef doesn't work since it only affects the global context, +# Even .undef doesn't work since it only affects the global scope, # which is independent from the environment variables. .undef FROM_ENV .if ${FROM_ENV} != value-from-env diff --git a/unit-tests/export.exp b/unit-tests/export.exp index b5ccf2168dfc..648d1283fb64 100644 --- a/unit-tests/export.exp +++ b/unit-tests/export.exp @@ -1,4 +1,5 @@ MAKELEVEL=1 +TMPDIR=TMPDIR UT_DOLLAR=This is $UT_FU UT_FOO=foobar is fubar UT_FU=fubar diff --git a/unit-tests/gnode-submake.exp b/unit-tests/gnode-submake.exp index dbce13efaefa..ea00e8d76c11 100644 --- a/unit-tests/gnode-submake.exp +++ b/unit-tests/gnode-submake.exp @@ -1,11 +1,11 @@ #*** Input graph: -# all, made UNMADE, type OP_DEPENDS, flags none -# makeinfo, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none -# make-index, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none -# braces-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none -# braces-no-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none -# braces-no-dot-modifier, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none -# parentheses-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none -# parentheses-no-dot, made UNMADE, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none +# all, unmade, type OP_DEPENDS, flags none +# makeinfo, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none +# make-index, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none +# braces-dot, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none +# braces-no-dot, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none +# braces-no-dot-modifier, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none +# parentheses-dot, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none +# parentheses-no-dot, unmade, type OP_DEPENDS|OP_SUBMAKE|OP_HAS_COMMANDS, flags none exit status 0 diff --git a/unit-tests/include-main.exp b/unit-tests/include-main.exp index f94ae722f095..61e716ad8ad7 100644 --- a/unit-tests/include-main.exp +++ b/unit-tests/include-main.exp @@ -2,7 +2,14 @@ make: "include-main.mk" line 14: main-before-ok make: "include-main.mk" line 21: main-before-for-ok make: "include-sub.mk" line 4: sub-before-ok make: "include-sub.mk" line 14: sub-before-for-ok -make: "include-subsub.mk" line 4: subsub-ok +ParseReadLine (5): '. info subsub-ok' +make: "include-subsub.mk" line 5: subsub-ok + in .for loop from include-sub.mk:31 + in .for loop from include-sub.mk:30 + in .for loop from include-sub.mk:29 + in .include from include-main.mk:27 +ParseReadLine (6): '.MAKEFLAGS: -d0' +ParseDoDependency(.MAKEFLAGS: -d0) make: "include-sub.mk" line 38: sub-after-ok make: "include-sub.mk" line 45: sub-after-for-ok make: "include-main.mk" line 30: main-after-ok diff --git a/unit-tests/include-main.mk b/unit-tests/include-main.mk index 0d3ab999843d..d3f122aef718 100644 --- a/unit-tests/include-main.mk +++ b/unit-tests/include-main.mk @@ -1,4 +1,4 @@ -# $NetBSD: include-main.mk,v 1.5 2020/09/05 18:18:05 rillig Exp $ +# $NetBSD: include-main.mk,v 1.6 2021/01/22 00:44:55 rillig Exp $ # # Until 2020-09-05, the .INCLUDEDFROMFILE magic variable did not behave # as described in the manual page. @@ -17,7 +17,7 @@ .endif .for i in once -. if !defined(${.INCLUDEDFROMFILE}) +. if !defined(.INCLUDEDFROMFILE) . info main-before-for-ok . else . warning main-before-for-fail(${.INCLUDEDFROMFILE}) @@ -33,7 +33,7 @@ .endif .for i in once -. if !defined(${.INCLUDEDFROMFILE}) +. if !defined(.INCLUDEDFROMFILE) . info main-after-for-ok . else . warning main-after-for-fail(${.INCLUDEDFROMFILE}) diff --git a/unit-tests/include-subsub.mk b/unit-tests/include-subsub.mk index 915d36d8a2a2..476d75f79556 100644 --- a/unit-tests/include-subsub.mk +++ b/unit-tests/include-subsub.mk @@ -1,7 +1,9 @@ -# $NetBSD: include-subsub.mk,v 1.3 2020/09/05 18:13:47 rillig Exp $ +# $NetBSD: include-subsub.mk,v 1.4 2021/01/26 23:44:56 rillig Exp $ .if ${.INCLUDEDFROMFILE} == "include-sub.mk" +.MAKEFLAGS: -dp . info subsub-ok +.MAKEFLAGS: -d0 .else . warning subsub-fail(${.INCLUDEDFROMFILE}) .endif diff --git a/unit-tests/jobs-empty-commands.exp b/unit-tests/jobs-empty-commands.exp new file mode 100644 index 000000000000..7fcf60109123 --- /dev/null +++ b/unit-tests/jobs-empty-commands.exp @@ -0,0 +1,2 @@ +action +exit status 0 diff --git a/unit-tests/jobs-empty-commands.mk b/unit-tests/jobs-empty-commands.mk new file mode 100644 index 000000000000..ec8b2a8c8d77 --- /dev/null +++ b/unit-tests/jobs-empty-commands.mk @@ -0,0 +1,18 @@ +# $NetBSD: jobs-empty-commands.mk,v 1.2 2021/01/30 12:46:38 rillig Exp $ +# +# In jobs mode, the shell commands for creating a target are written to a +# temporary file first, which is then run by the shell. In chains of +# dependencies, these files would end up empty. Since job.c 1.399 from +# 2021-01-29, these empty files are no longer created. +# +# https://mail-index.netbsd.org/current-users/2021/01/26/msg040215.html + +.MAKEFLAGS: -j1 +#.MAKEFLAGS: -dn # to see the created temporary files + +all: .PHONY step-1 +.for i i_plus_1 in ${:U:range=100:@i@$i $i@:[2..199]} +step-$i: .PHONY step-${i_plus_1} +.endfor +step-100: .PHONY + @echo 'action' diff --git a/unit-tests/lint.mk b/unit-tests/lint.mk index cfcb6fe7ba93..5db417639d0b 100755 --- a/unit-tests/lint.mk +++ b/unit-tests/lint.mk @@ -1,10 +1,13 @@ -# $NetBSD: lint.mk,v 1.3 2020/09/15 16:22:04 rillig Exp $ +# $NetBSD: lint.mk,v 1.4 2021/01/30 13:50:18 rillig Exp $ # -# Demonstrates stricter checks that are only enabled in the lint mode, -# using the -dL option. +# Demonstrates stricter checks that are only enabled in lint mode, using the +# option -dL. -# Ouch: as of 2020-08-03, make exits successfully even though the error -# message has been issued as PARSE_FATAL. +# Before main.c 1.421 from 2020-11-01, make exited successfully even though +# the error message had been issued as PARSE_FATAL. This was because back +# then, make checked for parse errors only after parsing each top-level +# makefile, in Parse_File. After that, when expanding variable expressions +# in shell commands, the parse errors were not checked again. # Ouch: as of 2020-08-03, the variable is malformed and parsing stops # for a moment, but is continued after the wrongly-guessed end of the diff --git a/unit-tests/objdir-writable.exp b/unit-tests/objdir-writable.exp index f15cf914a7f5..9c507f647f8c 100644 --- a/unit-tests/objdir-writable.exp +++ b/unit-tests/objdir-writable.exp @@ -1,5 +1,5 @@ -make warning: OBJDIR/roobj: Permission denied. +make warning: TMPDIR/roobj: Permission denied. /tmp -OBJDIR/roobj -OBJDIR/roobj +TMPDIR/roobj +TMPDIR/roobj exit status 0 diff --git a/unit-tests/opt-debug-graph1.exp b/unit-tests/opt-debug-graph1.exp index 45f403f75f1b..4d4aa0c3faea 100644 --- a/unit-tests/opt-debug-graph1.exp +++ b/unit-tests/opt-debug-graph1.exp @@ -1,12 +1,12 @@ #*** Input graph: -# all, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none -# made-target, made UNMADE, type OP_DEPENDS, flags none -# made-target-no-sources, made UNMADE, type OP_DEPENDS, flags none -# made-source, made UNMADE, type OP_DEPENDS, flags none -# unmade-target, made UNMADE, type OP_DEPENDS, flags none -# unmade-sources, made UNMADE, type none, flags none -# unmade-silent-source, made UNMADE, type OP_SILENT, flags none -# unmade-target-no-sources, made UNMADE, type OP_DEPENDS, flags none +# all, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none +# made-target, unmade, type OP_DEPENDS, flags none +# made-target-no-sources, unmade, type OP_DEPENDS, flags none +# made-source, unmade, type OP_DEPENDS, flags none +# unmade-target, unmade, type OP_DEPENDS, flags none +# unmade-sources, unmade, type none, flags none +# unmade-silent-source, unmade, type OP_SILENT, flags none +# unmade-target-no-sources, unmade, type OP_DEPENDS, flags none # diff --git a/unit-tests/opt-debug-graph2.exp b/unit-tests/opt-debug-graph2.exp index 39a9383953dd..03f02719618e 100644 --- a/unit-tests/opt-debug-graph2.exp +++ b/unit-tests/opt-debug-graph2.exp @@ -1 +1,91 @@ -exit status 0 +: 'Making made-target.' +false +*** Error code 1 (continuing) +false +*** Error code 1 (continuing) +`all' not remade because of errors. +#*** Input graph: +# made-target, made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC +# +# *** MAIN TARGET *** +# No unmade children +# last modified : made +# parents: all +made-target : + (null) + + +# error-target, error when made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC +# +# No unmade children +# nonexistent (maybe): error when made +# parents: all +error-target : + (null) + + +# aborted-target, aborted, type OP_DEPENDS|OP_PHONY|OP_DEPS_FOUND|OP_MARK, flags none +# aborted-target-dependency, error when made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC +# +# No unmade children +# nonexistent (maybe): error when made +# parents: aborted-target +aborted-target-dependency: + (null) + + +# all, aborted, type OP_DEPENDS|OP_DEPS_FOUND, flags CHILDMADE|FORCE +# +# 3 unmade children +# nonexistent (maybe): aborted +all : made-target error-target aborted-target + + +# .END, unmade, type OP_SPECIAL, flags none + + +# +# Files that are only sources: +# .END [.END] +#*** Global Variables: +.ALLTARGETS = made-target error-target aborted-target aborted-target-dependency all .END +.CURDIR = +.INCLUDES = +.LIBS = +.MAKE =
+.MAKE.DEPENDFILE =
+.MAKE.GID =
+.MAKE.LEVEL =
+.MAKE.MAKEFILES =
+.MAKE.MAKEFILE_PREFERENCE =
+.MAKE.OS =
+.MAKE.PID =
+.MAKE.PPID =
+.MAKE.UID =
+.MAKEFLAGS = -r -k -d g2 +.MAKEOVERRIDES = +.OBJDIR = +.PATH = . +.TARGETS = all +.newline = + +MACHINE =
+MACHINE_ARCH =
+MAKE =
+MFLAGS = -r -k -d g2 +#*** Command-line Variables: +.MAKE.LEVEL.ENV = MAKELEVEL +.SHELL =
+ +#*** Directory Cache: +# Stats: 0 hits 4 misses 0 near misses 0 losers (0%) +# refs hits directory +# 1 0 +# 1 0 . + +#*** Suffixes: +#*** Transformations: + +Stop. +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/opt-debug-graph2.mk b/unit-tests/opt-debug-graph2.mk index 92ffac6f7539..49e27569aa98 100644 --- a/unit-tests/opt-debug-graph2.mk +++ b/unit-tests/opt-debug-graph2.mk @@ -1,9 +1,23 @@ -# $NetBSD: opt-debug-graph2.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $ +# $NetBSD: opt-debug-graph2.mk,v 1.3 2021/02/02 17:47:56 rillig Exp $ # # Tests for the -dg2 command line option, which prints the input # graph after making everything, or before exiting on error. +# +# Before compat.c 1.222 from 2021-02-02, there was no debug output despite +# the error. -# TODO: Implementation +.MAKEFLAGS: -dg2 -all: - @:; +.MAIN: all + +made-target: .PHONY + : 'Making $@.' + +error-target: .PHONY + false + +aborted-target: .PHONY aborted-target-dependency +aborted-target-dependency: .PHONY + false + +all: made-target error-target aborted-target diff --git a/unit-tests/opt-debug-graph3.exp b/unit-tests/opt-debug-graph3.exp index 39a9383953dd..f2966442eb26 100644 --- a/unit-tests/opt-debug-graph3.exp +++ b/unit-tests/opt-debug-graph3.exp @@ -1 +1,91 @@ -exit status 0 +: 'Making made-target.' +false +*** Error code 1 (continuing) +false +*** Error code 1 (continuing) +`all' not remade because of errors. +#*** Input graph: +# made-target, made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC +# +# *** MAIN TARGET *** +# No unmade children +# last modified : made +# parents: all +made-target : + (null) + + +# error-target, error when made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC +# +# No unmade children +# nonexistent (maybe): error when made +# parents: all +error-target : + (null) + + +# aborted-target, aborted, type OP_DEPENDS|OP_PHONY|OP_DEPS_FOUND|OP_MARK, flags none +# aborted-target-dependency, error when made, type OP_DEPENDS|OP_PHONY|OP_HAS_COMMANDS|OP_DEPS_FOUND|OP_MARK, flags REMAKE|DONE_ALLSRC +# +# No unmade children +# nonexistent (maybe): error when made +# parents: aborted-target +aborted-target-dependency: + (null) + + +# all, aborted, type OP_DEPENDS|OP_DEPS_FOUND, flags CHILDMADE|FORCE +# +# 3 unmade children +# nonexistent (maybe): aborted +all : made-target error-target aborted-target + + +# .END, unmade, type OP_SPECIAL, flags none + + +# +# Files that are only sources: +# .END [.END] +#*** Global Variables: +.ALLTARGETS = made-target error-target aborted-target aborted-target-dependency all .END +.CURDIR = +.INCLUDES = +.LIBS = +.MAKE =
+.MAKE.DEPENDFILE =
+.MAKE.GID =
+.MAKE.LEVEL =
+.MAKE.MAKEFILES =
+.MAKE.MAKEFILE_PREFERENCE =
+.MAKE.OS =
+.MAKE.PID =
+.MAKE.PPID =
+.MAKE.UID =
+.MAKEFLAGS = -r -k -d g3 +.MAKEOVERRIDES = +.OBJDIR = +.PATH = . +.TARGETS = all +.newline = + +MACHINE =
+MACHINE_ARCH =
+MAKE =
+MFLAGS = -r -k -d g3 +#*** Command-line Variables: +.MAKE.LEVEL.ENV = MAKELEVEL +.SHELL =
+ +#*** Directory Cache: +# Stats: 0 hits 4 misses 0 near misses 0 losers (0%) +# refs hits directory +# 1 0 +# 1 0 . + +#*** Suffixes: +#*** Transformations: + +Stop. +make: stopped in unit-tests +exit status 1 diff --git a/unit-tests/opt-debug-graph3.mk b/unit-tests/opt-debug-graph3.mk index 28166313d833..8cb99c59b6a7 100644 --- a/unit-tests/opt-debug-graph3.mk +++ b/unit-tests/opt-debug-graph3.mk @@ -1,9 +1,23 @@ -# $NetBSD: opt-debug-graph3.mk,v 1.1 2020/09/05 06:20:51 rillig Exp $ +# $NetBSD: opt-debug-graph3.mk,v 1.3 2021/02/02 17:47:56 rillig Exp $ # # Tests for the -dg3 command line option, which prints the input # graph before exiting on error. +# +# Before compat.c 1.222 from 2021-02-02, there was no debug output despite +# the error. -# TODO: Implementation +.MAKEFLAGS: -dg3 -all: - @:; +.MAIN: all + +made-target: .PHONY + : 'Making $@.' + +error-target: .PHONY + false + +aborted-target: .PHONY aborted-target-dependency +aborted-target-dependency: .PHONY + false + +all: made-target error-target aborted-target diff --git a/unit-tests/opt-no-action-touch.exp b/unit-tests/opt-no-action-touch.exp new file mode 100644 index 000000000000..247bd7fb47fe --- /dev/null +++ b/unit-tests/opt-no-action-touch.exp @@ -0,0 +1,11 @@ +echo ": Making opt-touch-phony." +{ : Making opt-touch-phony. +} || exit $? +echo 'Making opt-touch-make.' +Making opt-touch-make. +echo ": Making opt-touch-regular." +{ : Making opt-touch-regular. +} || exit $? +`opt-touch-join' is up to date. +`opt-touch-use' is up to date. +exit status 0 diff --git a/unit-tests/opt-no-action-touch.mk b/unit-tests/opt-no-action-touch.mk new file mode 100644 index 000000000000..8bbdd970250d --- /dev/null +++ b/unit-tests/opt-no-action-touch.mk @@ -0,0 +1,48 @@ +# $NetBSD: opt-no-action-touch.mk,v 1.1 2021/01/30 12:46:38 rillig Exp $ +# +# Tests for combining the command line options -n (no action) and -t (touch). +# This combination is unusual and probably doesn't ever happen in practice, +# but still make needs to behave as expected. The option -n is stronger than +# -t, so instead of being touched, the commands of the targets are printed. +# +# See also: +# opt-touch-jobs.mk contains the same test without the option -n. + +.MAKEFLAGS: -j1 -n -t +.MAKEFLAGS: opt-touch-phony +.MAKEFLAGS: opt-touch-join +.MAKEFLAGS: opt-touch-use +.MAKEFLAGS: opt-touch-make +.MAKEFLAGS: opt-touch-regular + +# .PHONY targets are not touched since they do not represent actual files. +# See Job_Touch. +opt-touch-phony: .PHONY + : Making $@. + +# .JOIN targets are not touched since they do not represent actual files. +# See Job_Touch. +opt-touch-join: .JOIN + : Making $@. + +# .USE targets are not touched since they do not represent actual files. +# See Job_Touch. +opt-touch-use: .USE + : Making use of $@. + +# The attribute .MAKE is stronger than the command line option -n. Therefore +# this target is run as usual. It is not prefixed by '@', therefore it is +# printed before being run. +opt-touch-make: .MAKE + echo 'Making $@.' + +# Since the option -n is stronger than the option -t, this target is not +# touched either. Without the -n, it would be touched. +opt-touch-regular: + : Making $@. + +# Since none of the above targets are actually touched, the following command +# does not output anything. +.END: + @files=$$(ls opt-touch-* 2>/dev/null | grep -v -e '\.'); \ + [ -z "$$files" ] || { echo "created files: $$files" 1>&2; exit 1; } diff --git a/unit-tests/opt-touch-jobs.mk b/unit-tests/opt-touch-jobs.mk index 4402d76e9578..6005ab49d125 100644 --- a/unit-tests/opt-touch-jobs.mk +++ b/unit-tests/opt-touch-jobs.mk @@ -1,4 +1,4 @@ -# $NetBSD: opt-touch-jobs.mk,v 1.1 2020/11/14 15:35:20 rillig Exp $ +# $NetBSD: opt-touch-jobs.mk,v 1.2 2021/01/30 12:14:08 rillig Exp $ # # Tests for the -t command line option in jobs mode. @@ -9,12 +9,18 @@ .MAKEFLAGS: opt-touch-use .MAKEFLAGS: opt-touch-make +# .PHONY targets are not touched since they do not represent actual files. +# See Job_Touch. opt-touch-phony: .PHONY : Making $@. +# .JOIN targets are not touched since they do not represent actual files. +# See Job_Touch. opt-touch-join: .JOIN : Making $@. +# .USE targets are not touched since they do not represent actual files. +# See Job_Touch. opt-touch-use: .USE : Making use of $@. diff --git a/unit-tests/opt-warnings-as-errors.exp b/unit-tests/opt-warnings-as-errors.exp index 278c9469607f..1db56b753bed 100644 --- a/unit-tests/opt-warnings-as-errors.exp +++ b/unit-tests/opt-warnings-as-errors.exp @@ -1,6 +1,6 @@ -make: "opt-warnings-as-errors.mk" line 7: warning: message 1 +make: "opt-warnings-as-errors.mk" line 12: warning: message 1 make: parsing warnings being treated as errors -make: "opt-warnings-as-errors.mk" line 8: warning: message 2 +make: "opt-warnings-as-errors.mk" line 13: warning: message 2 parsing continues make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/unit-tests/opt-warnings-as-errors.mk b/unit-tests/opt-warnings-as-errors.mk index 2302dd4794b5..c29343f960a7 100644 --- a/unit-tests/opt-warnings-as-errors.mk +++ b/unit-tests/opt-warnings-as-errors.mk @@ -1,6 +1,11 @@ -# $NetBSD: opt-warnings-as-errors.mk,v 1.4 2020/11/09 20:50:56 rillig Exp $ +# $NetBSD: opt-warnings-as-errors.mk,v 1.5 2021/01/27 00:02:38 rillig Exp $ # # Tests for the -W command line option, which turns warnings into errors. +# +# Even in -W mode, a .warning is not completely equivalent to an .error. +# First, the word "warning" is still printed, and second, parsing continues +# after a failed warning, whereas it would stop immediately at the first +# .error. .MAKEFLAGS: -W diff --git a/unit-tests/suff-incomplete.exp b/unit-tests/suff-incomplete.exp index 721f20eb33db..23b959d4b4e5 100644 --- a/unit-tests/suff-incomplete.exp +++ b/unit-tests/suff-incomplete.exp @@ -18,8 +18,8 @@ defining transformation from `.a' to `.c' inserting ".a" (1) at end of list inserting ".c" (3) at end of list # LinkSource: added child .a.c - ${.PREFIX}.dependency -# .a.c, made UNMADE, type OP_DEPENDS|OP_TRANSFORM, flags none -# ${.PREFIX}.dependency, made UNMADE, type none, flags none +# .a.c, unmade, type OP_DEPENDS|OP_TRANSFORM, flags none +# ${.PREFIX}.dependency, unmade, type none, flags none ParseReadLine (23): '.DEFAULT:' transformation .a.c complete ParseDoDependency(.DEFAULT:) diff --git a/unit-tests/suff-main-several.exp b/unit-tests/suff-main-several.exp index c938820c1eaf..a494ddc68545 100644 --- a/unit-tests/suff-main-several.exp +++ b/unit-tests/suff-main-several.exp @@ -59,17 +59,17 @@ ParseReadLine (39): ' : Making ${.TARGET} out of nothing.' ParseReadLine (40): 'next-main: suff-main-several.{2,3,4}' ParseDoDependency(next-main: suff-main-several.{2,3,4}) # LinkSource: added child next-main - suff-main-several.{2,3,4} -# next-main, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none -# suff-main-several.{2,3,4}, made UNMADE, type none, flags none +# next-main, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none +# suff-main-several.{2,3,4}, unmade, type none, flags none ParseReadLine (42): '.MAKEFLAGS: -d0 -dg1' ParseDoDependency(.MAKEFLAGS: -d0 -dg1) #*** Input graph: -# .1.2, made UNMADE, type OP_TRANSFORM, flags none -# .1.3, made UNMADE, type OP_TRANSFORM, flags none -# .1.4, made UNMADE, type OP_TRANSFORM, flags none -# next-main, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none -# suff-main-several.1, made UNMADE, type OP_DEPENDS|OP_HAS_COMMANDS, flags none -# suff-main-several.{2,3,4}, made UNMADE, type none, flags none +# .1.2, unmade, type OP_TRANSFORM, flags none +# .1.3, unmade, type OP_TRANSFORM, flags none +# .1.4, unmade, type OP_TRANSFORM, flags none +# next-main, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none +# suff-main-several.1, unmade, type OP_DEPENDS|OP_HAS_COMMANDS, flags none +# suff-main-several.{2,3,4}, unmade, type none, flags none # diff --git a/unit-tests/suff-transform-debug.exp b/unit-tests/suff-transform-debug.exp index 70181be4b97f..0634ff616d0d 100644 --- a/unit-tests/suff-transform-debug.exp +++ b/unit-tests/suff-transform-debug.exp @@ -1,5 +1,5 @@ #*** Input graph: -# all, made UNMADE, type OP_DEPENDS, flags none +# all, unmade, type OP_DEPENDS, flags none # diff --git a/unit-tests/var-op-append.exp b/unit-tests/var-op-append.exp index d9cfe21bd84c..424ad37ccf63 100644 --- a/unit-tests/var-op-append.exp +++ b/unit-tests/var-op-append.exp @@ -1,7 +1,7 @@ Var_Parse: ${:U\$\$\$\$\$\$\$\$} with VARE_WANTRES -Applying ${:U...} to "" (VARE_WANTRES, none, VEF_UNDEF) -Result of ${:U\$\$\$\$\$\$\$\$} is "$$$$$$$$" (VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) -Global:VAR.$$$$ = dollars +Applying ${:U...} to "" (VARE_WANTRES, none, VES_UNDEF) +Result of ${:U\$\$\$\$\$\$\$\$} is "$$$$$$$$" (VARE_WANTRES, none, VES_DEF) +Global:VAR.$$$$$$$$ = dollars Global:.MAKEFLAGS = -r -k -d v -d Global:.MAKEFLAGS = -r -k -d v -d 0 exit status 0 diff --git a/unit-tests/var-op-append.mk b/unit-tests/var-op-append.mk index 009a469e31ef..deb4af6a7384 100644 --- a/unit-tests/var-op-append.mk +++ b/unit-tests/var-op-append.mk @@ -1,4 +1,4 @@ -# $NetBSD: var-op-append.mk,v 1.7 2020/10/30 20:36:33 rillig Exp $ +# $NetBSD: var-op-append.mk,v 1.8 2021/02/03 08:40:47 rillig Exp $ # # Tests for the += variable assignment operator, which appends to a variable, # creating it if necessary. @@ -32,17 +32,15 @@ C++= value . error .endif -# Try out how often the variable name is expanded when appending to a -# nonexistent variable. -# As of 2020-10-30, that's two times. -# XXX: That's one time too often. -# See Var_Append, the call to Var_Set. +# Before var.c 1.793 from 2021-02-03, the variable name of a newly created +# variable was expanded two times in a row, which was unexpected but +# irrelevant in practice since variable names containing dollars lead to +# strange side effects in several other places as well. .MAKEFLAGS: -dv VAR.${:U\$\$\$\$\$\$\$\$}+= dollars .MAKEFLAGS: -d0 -.if ${VAR.${:U\$\$\$\$}} != "dollars" +.if ${VAR.${:U\$\$\$\$\$\$\$\$}} != "dollars" . error .endif all: - @:; diff --git a/unit-tests/var-op-assign.exp b/unit-tests/var-op-assign.exp index 0562ffcda733..73e580403d78 100644 --- a/unit-tests/var-op-assign.exp +++ b/unit-tests/var-op-assign.exp @@ -1,5 +1,5 @@ this will be evaluated later -make: "var-op-assign.mk" line 59: Need an operator +make: "var-op-assign.mk" line 59: Invalid line type make: "var-op-assign.mk" line 93: Parsing still continues until here. make: Fatal errors encountered -- cannot continue make: stopped in unit-tests diff --git a/unit-tests/var-op-shell.exp b/unit-tests/var-op-shell.exp index caea85ab5daa..890bfa43c38e 100644 --- a/unit-tests/var-op-shell.exp +++ b/unit-tests/var-op-shell.exp @@ -1,7 +1,7 @@ make: "var-op-shell.mk" line 28: warning: "echo "failed"; false" returned non-zero status make: "var-op-shell.mk" line 34: warning: "false" returned non-zero status -make: "var-op-shell.mk" line 59: warning: "kill -14 $$" exited on a signal +make: "var-op-shell.mk" line 56: warning: "kill $$" exited on a signal /bin/no/such/command: not found -make: "var-op-shell.mk" line 65: warning: "/bin/no/such/command" returned non-zero status +make: "var-op-shell.mk" line 62: warning: "/bin/no/such/command" returned non-zero status stderr exit status 0 diff --git a/unit-tests/var-op-shell.mk b/unit-tests/var-op-shell.mk index 7b52513e1131..0fdc54fc6041 100644 --- a/unit-tests/var-op-shell.mk +++ b/unit-tests/var-op-shell.mk @@ -1,4 +1,4 @@ -# $NetBSD: var-op-shell.mk,v 1.3 2020/11/09 20:39:46 rillig Exp $ +# $NetBSD: var-op-shell.mk,v 1.4 2021/02/06 04:55:08 sjg Exp $ # # Tests for the != variable assignment operator, which runs its right-hand # side through the shell. @@ -50,13 +50,10 @@ OUTPUT!= echo "before"; false; echo "after" . error .endif -# NB: The signal number must be numeric since some shells (which ones?) don't -# accept symbolic signal names. 14 is typically SIGALRM. -# -# XXX: The number of the signal is not mentioned in the warning since that -# would have been difficult to implement; currently the errfmt is a format -# string containing a single %s conversion. -OUTPUT!= kill -14 $$$$ +# This should result in a warning about "exited on a signal". +# This used to be kill -14 (SIGALRM), but that stopped working on +# Darwin18 after recent update. +OUTPUT!= kill $$$$ .if ${OUTPUT} != "" . error .endif diff --git a/unit-tests/vardebug.exp b/unit-tests/vardebug.exp index cd89e81b3923..a9a00a11f5dd 100644 --- a/unit-tests/vardebug.exp +++ b/unit-tests/vardebug.exp @@ -6,12 +6,12 @@ Global:VAR = overwritten Global:delete VAR Global:delete VAR (not found) Var_Parse: ${:U} with VARE_WANTRES -Applying ${:U} to "" (VARE_WANTRES, none, VEF_UNDEF) -Result of ${:U} is "" (VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF) +Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF) Var_Set("${:U}", "empty name", ...) name expands to empty string - ignored Var_Parse: ${:U} with VARE_WANTRES -Applying ${:U} to "" (VARE_WANTRES, none, VEF_UNDEF) -Result of ${:U} is "" (VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF) +Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF) Var_Append("${:U}", "empty name", ...) name expands to empty string - ignored Global:FROM_CMDLINE = overwritten ignored! Global:VAR = 1 @@ -47,32 +47,32 @@ Result of ${VAR:tl} is "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none) Applying ${VAR:Q} to "1 2 3" (VARE_UNDEFERR|VARE_WANTRES, none, none) Result of ${VAR:Q} is "1\ 2\ 3" (VARE_UNDEFERR|VARE_WANTRES, none, none) Var_Parse: ${:Uvalue:${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:Uvalue} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:Uvalue} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Var_Parse: ${:UM*e}:Mvalu[e]} with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:UM*e} is "M*e" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:UM*e} is "M*e" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Indirect modifier "M*e" from "${:UM*e}" -Applying ${:M...} to "value" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:M...} to "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Pattern[] for [value] is [*e] ModifyWords: split "value" into 1 words VarMatch [value] [*e] -Result of ${:M*e} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) -Applying ${:M...} to "value" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Result of ${:M*e} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) +Applying ${:M...} to "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Pattern[] for [value] is [valu[e]] ModifyWords: split "value" into 1 words VarMatch [value] [valu[e]] -Result of ${:Mvalu[e]} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Result of ${:Mvalu[e]} is "value" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Var_Parse: ${:UVAR} with VARE_WANTRES -Applying ${:U...} to "" (VARE_WANTRES, none, VEF_UNDEF) -Result of ${:UVAR} is "VAR" (VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_WANTRES, none, VES_UNDEF) +Result of ${:UVAR} is "VAR" (VARE_WANTRES, none, VES_DEF) Global:delete VAR Var_Parse: ${:Uvariable:unknown} with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:Uvariable} is "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) -Applying ${:u...} to "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:Uvariable} is "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) +Applying ${:u...} to "variable" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) make: "vardebug.mk" line 44: Unknown modifier 'u' -Result of ${:unknown} is error (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Result of ${:unknown} is error (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) make: "vardebug.mk" line 44: Malformed conditional (${:Uvariable:unknown}) Var_Parse: ${UNDEFINED} with VARE_UNDEFERR|VARE_WANTRES make: "vardebug.mk" line 53: Malformed conditional (${UNDEFINED}) diff --git a/unit-tests/vardebug.mk b/unit-tests/vardebug.mk index 51c2f4b0839a..4a16a7f2797f 100644 --- a/unit-tests/vardebug.mk +++ b/unit-tests/vardebug.mk @@ -1,4 +1,4 @@ -# $NetBSD: vardebug.mk,v 1.6 2020/10/31 13:15:10 rillig Exp $ +# $NetBSD: vardebug.mk,v 1.7 2021/02/04 21:42:47 rillig Exp $ # # Demonstrates the debugging output for var.c. @@ -54,7 +54,7 @@ VAR+= 3 .endif # By default, .SHELL is not defined and thus can be set. As soon as it is -# accessed, it is initialized in the command line context (during VarFind), +# accessed, it is initialized in the command line scope (during VarFind), # where it is set to read-only. Assigning to it is ignored. .MAKEFLAGS: .SHELL=overwritten diff --git a/unit-tests/varmisc.mk b/unit-tests/varmisc.mk index 607c8d7e0ed3..e5ab375d8f39 100644 --- a/unit-tests/varmisc.mk +++ b/unit-tests/varmisc.mk @@ -1,5 +1,5 @@ -# $Id: varmisc.mk,v 1.22 2020/11/30 19:27:41 sjg Exp $ -# $NetBSD: varmisc.mk,v 1.29 2020/11/28 14:08:37 rillig Exp $ +# $Id: varmisc.mk,v 1.23 2021/02/05 20:02:30 sjg Exp $ +# $NetBSD: varmisc.mk,v 1.30 2021/02/04 21:42:47 rillig Exp $ # # Miscellaneous variable tests. @@ -135,7 +135,7 @@ VAR.${param}= ${param} .endif # Appending to a variable from the environment creates a copy of that variable -# in the global context. +# in the global scope. # The appended value is not exported automatically. # When a variable is exported, the exported value is taken at the time of the # .export directive. Later changes to the variable have no effect. @@ -153,8 +153,8 @@ export-appended: # begin parse-dynamic # # Demonstrate that the target-specific variables are not evaluated in -# the global context. They are preserved until there is a local context -# in which resolving them makes sense. +# the global scope. Their expressions are preserved until there is a local +# scope in which resolving them makes sense. # There are different code paths for short names ... ${:U>}= before diff --git a/unit-tests/varmod-assign.mk b/unit-tests/varmod-assign.mk index 773c348b19c8..e4cbc249df88 100644 --- a/unit-tests/varmod-assign.mk +++ b/unit-tests/varmod-assign.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-assign.mk,v 1.8 2020/10/18 21:37:24 rillig Exp $ +# $NetBSD: varmod-assign.mk,v 1.9 2021/01/22 22:54:53 rillig Exp $ # # Tests for the obscure ::= variable modifiers, which perform variable # assignments during evaluation, just like the = operator in C. @@ -88,7 +88,6 @@ mod-assign-shell-error: @${SH_OK::!= echo word; true } echo ok=${SH_OK} # If the command fails, the variable keeps its previous value. - # FIXME: the error message says: "previous" returned non-zero status @${SH_ERR::=previous} @${SH_ERR::!= echo word; false } echo err=${SH_ERR} diff --git a/unit-tests/varmod-gmtime.exp b/unit-tests/varmod-gmtime.exp index 1baa0215b6e6..5d5806b92d26 100644 --- a/unit-tests/varmod-gmtime.exp +++ b/unit-tests/varmod-gmtime.exp @@ -1,13 +1,13 @@ -make: "varmod-gmtime.mk" line 60: Invalid time value: ${:U1593536400}} != "mtime=11593536400}" -make: "varmod-gmtime.mk" line 60: Malformed conditional (${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}") -make: "varmod-gmtime.mk" line 70: Invalid time value: -1} != "" -make: "varmod-gmtime.mk" line 70: Malformed conditional (${:L:gmtime=-1} != "") -make: "varmod-gmtime.mk" line 79: Invalid time value: 1} != "" -make: "varmod-gmtime.mk" line 79: Malformed conditional (${:L:gmtime= 1} != "") -make: "varmod-gmtime.mk" line 118: Invalid time value: 10000000000000000000000000000000} != "" -make: "varmod-gmtime.mk" line 118: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "") -make: "varmod-gmtime.mk" line 129: Invalid time value: error} != "" -make: "varmod-gmtime.mk" line 129: Malformed conditional (${:L:gmtime=error} != "") +make: "varmod-gmtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}" +make: "varmod-gmtime.mk" line 57: Malformed conditional (${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}") +make: "varmod-gmtime.mk" line 67: Invalid time value: -1} != "" +make: "varmod-gmtime.mk" line 67: Malformed conditional (${:L:gmtime=-1} != "") +make: "varmod-gmtime.mk" line 76: Invalid time value: 1} != "" +make: "varmod-gmtime.mk" line 76: Malformed conditional (${:L:gmtime= 1} != "") +make: "varmod-gmtime.mk" line 119: Invalid time value: 10000000000000000000000000000000} != "" +make: "varmod-gmtime.mk" line 119: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "") +make: "varmod-gmtime.mk" line 130: Invalid time value: error} != "" +make: "varmod-gmtime.mk" line 130: Malformed conditional (${:L:gmtime=error} != "") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/varmod-gmtime.mk b/unit-tests/varmod-gmtime.mk index 3c5f04991c48..cb3d4e7eb241 100644 --- a/unit-tests/varmod-gmtime.mk +++ b/unit-tests/varmod-gmtime.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-gmtime.mk,v 1.9 2020/12/22 07:22:39 rillig Exp $ +# $NetBSD: varmod-gmtime.mk,v 1.10 2021/01/19 05:26:34 rillig Exp $ # # Tests for the :gmtime variable modifier, which formats a timestamp # using strftime(3) in UTC. @@ -54,9 +54,6 @@ # # If ApplyModifier_Gmtime were to pass its argument through # ParseModifierPart, this would work. -# -# XXX: Where does the empty line 4 in varmod-gmtime.exp come from? -# TODO: Remove the \n from "Invalid time value: %s\n" in var.c. .if ${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}" . error .endif @@ -74,8 +71,8 @@ .endif -# Spaces were allowed before var.c 1.631, not because it would make sense -# but just as a side-effect from using strtoul. +# Spaces were allowed before var.c 1.631 from 2020-10-31 21:40:20, not +# because it would make sense but just as a side-effect from using strtoul. .if ${:L:gmtime= 1} != "" . error .endif @@ -102,8 +99,12 @@ .if ${:L:gmtime=2147483648} == "Tue Jan 19 03:14:08 2038" # All systems that have unsigned time_t or 64-bit time_t. -.elif ${:L:gmtime=2147483648} != "Fri Dec 13 20:45:52 1901" -# FreeBSD-12.0-i386 still has 32-bit signed time_t. +.elif ${:L:gmtime=2147483648} == "Fri Dec 13 20:45:52 1901" +# FreeBSD-12.0-i386 still has 32-bit signed time_t, see +# sys/x86/include/_types.h, __LP64__. +# +# Linux on 32-bit systems may still have 32-bit signed time_t, see +# sysdeps/unix/sysv/linux/generic/bits/typesizes.h, __TIMESIZE. .else . error .endif @@ -122,7 +123,7 @@ .endif # Before var.c 1.631 from 2020-10-31, there was no error handling while -# parsing the :gmtime modifier, thus no error message is printed. Parsing +# parsing the :gmtime modifier, thus no error message was printed. Parsing # stopped after the '=', and the remaining string was parsed for more variable # modifiers. Because of the unknown modifier 'e' from the 'error', the whole # variable value was discarded and thus not printed. diff --git a/unit-tests/varmod-ifelse.mk b/unit-tests/varmod-ifelse.mk index 5e0ad04584be..ec6acdb2ee2f 100644 --- a/unit-tests/varmod-ifelse.mk +++ b/unit-tests/varmod-ifelse.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-ifelse.mk,v 1.8 2020/12/10 16:47:42 rillig Exp $ +# $NetBSD: varmod-ifelse.mk,v 1.9 2021/01/25 19:05:39 rillig Exp $ # # Tests for the ${cond:?then:else} variable modifier, which evaluates either # the then-expression or the else-expression, depending on the condition. @@ -103,7 +103,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign} # This hack does not work for variables from .for loops since these are # expanded at parse time to their corresponding ${:Uvalue} expressions. # Making the '$' of the '${VAR}' expression indirect hides this expression -# from the parser of the .for loop body. See SubstVarLong. +# from the parser of the .for loop body. See ForLoop_SubstVarLong. .MAKEFLAGS: -dc VAR= value .if ${ ${:U\$}{VAR} == value :?ok:bad} != "ok" diff --git a/unit-tests/varmod-indirect.exp b/unit-tests/varmod-indirect.exp index 8cdd49ad4f03..860da7781979 100644 --- a/unit-tests/varmod-indirect.exp +++ b/unit-tests/varmod-indirect.exp @@ -15,40 +15,40 @@ Global:_ = before ${UNDEF} after ParseReadLine (137): '_:= before ${UNDEF:${:US,a,a,}} after' Var_Parse: ${UNDEF:${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF Var_Parse: ${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF -Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) -Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) +Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF) Indirect modifier "S,a,a," from "${:US,a,a,}" -Applying ${UNDEF:S...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) +Applying ${UNDEF:S...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) Modifier part: "a" Modifier part: "a" ModifyWords: split "" into 1 words -Result of ${UNDEF:S,a,a,} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) +Result of ${UNDEF:S,a,a,} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) Var_Parse: ${:US,a,a,}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF -Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) -Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) +Result of ${:US,a,a,} is "S,a,a," (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF) Global:_ = before ${UNDEF:S,a,a,} after ParseReadLine (147): '_:= before ${UNDEF:${:U}} after' Var_Parse: ${UNDEF:${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF Var_Parse: ${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF -Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) -Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF) +Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) +Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF) Indirect modifier "" from "${:U}" Var_Parse: ${:U}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF -Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) -Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF) +Applying ${:U} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) +Result of ${:U} is "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF) Global:_ = before ${UNDEF:} after ParseReadLine (152): '_:= before ${UNDEF:${:UZ}} after' Var_Parse: ${UNDEF:${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF Var_Parse: ${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF -Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) -Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) +Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF) Indirect modifier "Z" from "${:UZ}" -Applying ${UNDEF:Z} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) +Applying ${UNDEF:Z} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) make: "varmod-indirect.mk" line 152: Unknown modifier 'Z' -Result of ${UNDEF:Z} is error (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) +Result of ${UNDEF:Z} is error (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) Var_Parse: ${:UZ}} after with VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF -Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF) -Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_UNDEF) +Result of ${:UZ} is "Z" (VARE_WANTRES|VARE_KEEP_DOLLAR|VARE_KEEP_UNDEF, none, VES_DEF) Global:_ = before ${UNDEF:Z} after ParseReadLine (154): '.MAKEFLAGS: -d0' ParseDoDependency(.MAKEFLAGS: -d0) diff --git a/unit-tests/varmod-localtime.exp b/unit-tests/varmod-localtime.exp index b58de7700466..ed4d4f053c61 100644 --- a/unit-tests/varmod-localtime.exp +++ b/unit-tests/varmod-localtime.exp @@ -1,13 +1,13 @@ -make: "varmod-localtime.mk" line 60: Invalid time value: ${:U1593536400}} != "mtime=11593536400}" -make: "varmod-localtime.mk" line 60: Malformed conditional (${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}") -make: "varmod-localtime.mk" line 70: Invalid time value: -1} != "" -make: "varmod-localtime.mk" line 70: Malformed conditional (${:L:localtime=-1} != "") -make: "varmod-localtime.mk" line 79: Invalid time value: 1} != "" -make: "varmod-localtime.mk" line 79: Malformed conditional (${:L:localtime= 1} != "") -make: "varmod-localtime.mk" line 118: Invalid time value: 10000000000000000000000000000000} != "" -make: "varmod-localtime.mk" line 118: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "") -make: "varmod-localtime.mk" line 129: Invalid time value: error} != "" -make: "varmod-localtime.mk" line 129: Malformed conditional (${:L:localtime=error} != "") +make: "varmod-localtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}" +make: "varmod-localtime.mk" line 57: Malformed conditional (${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}") +make: "varmod-localtime.mk" line 67: Invalid time value: -1} != "" +make: "varmod-localtime.mk" line 67: Malformed conditional (${:L:localtime=-1} != "") +make: "varmod-localtime.mk" line 76: Invalid time value: 1} != "" +make: "varmod-localtime.mk" line 76: Malformed conditional (${:L:localtime= 1} != "") +make: "varmod-localtime.mk" line 119: Invalid time value: 10000000000000000000000000000000} != "" +make: "varmod-localtime.mk" line 119: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "") +make: "varmod-localtime.mk" line 130: Invalid time value: error} != "" +make: "varmod-localtime.mk" line 130: Malformed conditional (${:L:localtime=error} != "") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 diff --git a/unit-tests/varmod-localtime.mk b/unit-tests/varmod-localtime.mk index 3ee2f0ac93fb..f2867b61f8e9 100644 --- a/unit-tests/varmod-localtime.mk +++ b/unit-tests/varmod-localtime.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-localtime.mk,v 1.7 2020/12/22 07:22:39 rillig Exp $ +# $NetBSD: varmod-localtime.mk,v 1.8 2021/01/19 05:26:34 rillig Exp $ # # Tests for the :localtime variable modifier, which formats a timestamp # using strftime(3) in local time. @@ -36,7 +36,7 @@ # If the modifier name is not matched exactly, fall back to the # :from=to modifier. -.if ${gmtime:L:gm%=local%} != "localtime" +.if ${localtime:L:local%=gm%} != "gmtime" . error .endif @@ -54,9 +54,6 @@ # # If ApplyModifier_Localtime were to pass its argument through # ParseModifierPart, this would work. -# -# XXX: Where does the empty line 4 in varmod-localtime.exp come from? -# TODO: Remove the \n from "Invalid time value: %s\n" in var.c. .if ${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}" . error .endif @@ -74,8 +71,8 @@ .endif -# Spaces were allowed before var.c 1.631, not because it would make sense -# but just as a side-effect from using strtoul. +# Spaces were allowed before var.c 1.631 from 2020-10-31 21:40:20, not +# because it would make sense but just as a side-effect from using strtoul. .if ${:L:localtime= 1} != "" . error .endif @@ -102,8 +99,12 @@ .if ${:L:localtime=2147483648} == "Tue Jan 19 04:14:08 2038" # All systems that have unsigned time_t or 64-bit time_t. -.elif ${:L:localtime=2147483648} != "Fri Dec 13 21:45:52 1901" -# FreeBSD-12.0-i386 still has 32-bit signed time_t. +.elif ${:L:localtime=2147483648} == "Fri Dec 13 21:45:52 1901" +# FreeBSD-12.0-i386 still has 32-bit signed time_t, see +# sys/x86/include/_types.h, __LP64__. +# +# Linux on 32-bit systems may still have 32-bit signed time_t, see +# sysdeps/unix/sysv/linux/generic/bits/typesizes.h, __TIMESIZE. .else . error .endif @@ -122,7 +123,7 @@ .endif # Before var.c 1.631 from 2020-10-31, there was no error handling while -# parsing the :localtime modifier, thus no error message is printed. Parsing +# parsing the :localtime modifier, thus no error message was printed. Parsing # stopped after the '=', and the remaining string was parsed for more variable # modifiers. Because of the unknown modifier 'e' from the 'error', the whole # variable value was discarded and thus not printed. diff --git a/unit-tests/varmod-loop.mk b/unit-tests/varmod-loop.mk index 654c449d7bfa..c109c775a492 100644 --- a/unit-tests/varmod-loop.mk +++ b/unit-tests/varmod-loop.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-loop.mk,v 1.8 2020/11/12 00:40:55 rillig Exp $ +# $NetBSD: varmod-loop.mk,v 1.9 2021/02/04 21:42:47 rillig Exp $ # # Tests for the :@var@...${var}...@ variable modifier. @@ -39,7 +39,7 @@ mod-loop-varname: @echo empty: :${:U1 2 3:@@x${}y@}: # The :@ modifier resolves the variables a little more often than expected. -# In particular, it resolves _all_ variables from the context, and not only +# In particular, it resolves _all_ variables from the scope, and not only # the loop variable (in this case v). # # The d means direct reference, the i means indirect reference. @@ -77,7 +77,7 @@ mod-loop-dollar: # for the loop variable. These modifiers influence each other. # # As of 2020-10-18, the :@ modifier is implemented by actually setting a -# variable in the context of the expression and deleting it again after the +# variable in the scope of the expression and deleting it again after the # loop. This is different from the .for loops, which substitute the variable # expression with ${:Uvalue}, leading to different unwanted side effects. # diff --git a/unit-tests/varmod-match-escape.exp b/unit-tests/varmod-match-escape.exp index 023fd7f23dfb..30c148075524 100755 --- a/unit-tests/varmod-match-escape.exp +++ b/unit-tests/varmod-match-escape.exp @@ -3,8 +3,8 @@ CondParser_Eval: ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}} Var_Parse: ${SPECIALS:M${:U}\:} != ${SPECIALS:M\:${:U}} with VARE_UNDEFERR|VARE_WANTRES Applying ${SPECIALS:M...} to "\: : \\ * \*" (VARE_UNDEFERR|VARE_WANTRES, none, none) Var_Parse: ${:U}\: with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Pattern[SPECIALS] for [\: : \\ * \*] is [\:] ModifyWords: split "\: : \\ * \*" into 5 words VarMatch [\:] [\:] @@ -16,8 +16,8 @@ Result of ${SPECIALS:M${:U}\:} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, none) Var_Parse: ${SPECIALS:M\:${:U}} with VARE_UNDEFERR|VARE_WANTRES Applying ${SPECIALS:M...} to "\: : \\ * \*" (VARE_UNDEFERR|VARE_WANTRES, none, none) Var_Parse: ${:U} with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Pattern[SPECIALS] for [\: : \\ * \*] is [:] ModifyWords: split "\: : \\ * \*" into 5 words VarMatch [\:] [:] @@ -32,8 +32,8 @@ CondParser_Eval: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:} Var_Parse: ${VALUES:M\:${:U\:}} != ${VALUES:M${:U\:}\:} with VARE_UNDEFERR|VARE_WANTRES Applying ${VALUES:M...} to ": :: :\:" (VARE_UNDEFERR|VARE_WANTRES, none, none) Var_Parse: ${:U:} with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:U} is "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Pattern[VALUES] for [: :: :\:] is [:] ModifyWords: split ": :: :\:" into 3 words VarMatch [:] [:] @@ -43,8 +43,8 @@ Result of ${VALUES:M\:${:U\:}} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, none) Var_Parse: ${VALUES:M${:U\:}\:} with VARE_UNDEFERR|VARE_WANTRES Applying ${VALUES:M...} to ": :: :\:" (VARE_UNDEFERR|VARE_WANTRES, none, none) Var_Parse: ${:U\:}\: with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:U\:} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:U\:} is ":" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Pattern[VALUES] for [: :: :\:] is [:\:] ModifyWords: split ": :: :\:" into 3 words VarMatch [:] [:\:] diff --git a/unit-tests/varmod-match-escape.mk b/unit-tests/varmod-match-escape.mk index 5115d1b565df..e62fbe8352b7 100755 --- a/unit-tests/varmod-match-escape.mk +++ b/unit-tests/varmod-match-escape.mk @@ -1,4 +1,4 @@ -# $NetBSD: varmod-match-escape.mk,v 1.5 2020/11/01 19:49:28 rillig Exp $ +# $NetBSD: varmod-match-escape.mk,v 1.6 2021/02/01 22:36:28 rillig Exp $ # # As of 2020-08-01, the :M and :N modifiers interpret backslashes differently, # depending on whether there was a variable expression somewhere before the @@ -68,5 +68,23 @@ VALUES= : :: :\: . error .endif +# The control flow of the pattern parser depends on the actual string that +# is being matched. There needs to be either a test that shows a difference +# in behavior, or a proof that the behavior does not depend on the actual +# string. +# +# TODO: Str_Match("a-z]", "[a-z]") +# TODO: Str_Match("012", "[0-]]") +# TODO: Str_Match("0]", "[0-]]") +# TODO: Str_Match("1]", "[0-]]") +# TODO: Str_Match("[", "[[]") +# TODO: Str_Match("]", "[]") +# TODO: Str_Match("]", "[[-]]") + +# In brackets, the backslash is just an ordinary character. +# Outside brackets, it is an escape character for a few special characters. +# TODO: Str_Match("\\", "[\\-]]") +# TODO: Str_Match("-]", "[\\-]]") + all: @:; diff --git a/unit-tests/varname-dot-shell.mk b/unit-tests/varname-dot-shell.mk index 2e67b12aefff..1a061b869495 100755 --- a/unit-tests/varname-dot-shell.mk +++ b/unit-tests/varname-dot-shell.mk @@ -1,4 +1,4 @@ -# $NetBSD: varname-dot-shell.mk,v 1.6 2020/10/30 16:09:56 rillig Exp $ +# $NetBSD: varname-dot-shell.mk,v 1.7 2021/02/04 21:42:47 rillig Exp $ # # Tests for the special .SHELL variable, which contains the shell used for # running the commands. @@ -22,8 +22,8 @@ ORIG_SHELL:= ${.SHELL} .endif # Trying to delete the variable. -# This has no effect since the variable is not defined in the global context, -# but in the command-line context. +# This has no effect since the variable is not defined in the global scope, +# but in the command-line scope. .undef .SHELL .SHELL= newly overwritten .if ${.SHELL} != ${ORIG_SHELL} diff --git a/unit-tests/varname-empty.exp b/unit-tests/varname-empty.exp index 24c596e1c568..28f55368fd19 100644 --- a/unit-tests/varname-empty.exp +++ b/unit-tests/varname-empty.exp @@ -1,13 +1,47 @@ +Var_Parse: ${:U} with VARE_WANTRES +Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF) +Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF) Var_Set("${:U}", "cmdline-u", ...) name expands to empty string - ignored Var_Set("", "cmdline-plain", ...) name expands to empty string - ignored +Global:.CURDIR = +Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE:U} with VARE_WANTRES +Applying ${MAKE_OBJDIR_CHECK_WRITABLE:U} to "" (VARE_WANTRES, none, VES_UNDEF) +Result of ${MAKE_OBJDIR_CHECK_WRITABLE:U} is "" (VARE_WANTRES, none, VES_DEF) +Global:.OBJDIR = +Global:delete .PATH (not found) +Global:.PATH = . +Global:.PATH = . +Global:.TARGETS = +Internal:MAKEFILE = varname-empty.mk +Global:.MAKE.MAKEFILES = varname-empty.mk +Global:.PARSEFILE = varname-empty.mk +Global:delete .INCLUDEDFROMDIR (not found) +Global:delete .INCLUDEDFROMFILE (not found) Var_Set("", "default", ...) name expands to empty string - ignored Var_Set("", "assigned", ...) name expands to empty string - ignored -Var_Set("", "appended", ...) name expands to empty string - ignored +SetVar: variable name is empty - ignored Var_Set("", "", ...) name expands to empty string - ignored Var_Set("", "subst", ...) name expands to empty string - ignored Var_Set("", "shell-output", ...) name expands to empty string - ignored +Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) +Var_Parse: ${:U} with VARE_WANTRES +Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF) +Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF) Var_Set("${:U}", "assigned indirectly", ...) name expands to empty string - ignored -Var_Set("", "assigned", ...) name expands to empty string - ignored +Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) +Var_Parse: ${:U} with VARE_WANTRES +Applying ${:U} to "" (VARE_WANTRES, none, VES_UNDEF) +Result of ${:U} is "" (VARE_WANTRES, none, VES_DEF) +Var_Append("${:U}", "appended indirectly", ...) name expands to empty string - ignored +Var_Parse: ${:Ufallback} != "fallback" with VARE_UNDEFERR|VARE_WANTRES +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:Ufallback} is "fallback" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) +Global:.MAKEFLAGS = -r -d v -d +Global:.MAKEFLAGS = -r -d v -d 0 out: fallback out: 1 2 3 exit status 0 diff --git a/unit-tests/varname-empty.mk b/unit-tests/varname-empty.mk index fd9aa05b6057..492f9f2618ba 100755 --- a/unit-tests/varname-empty.mk +++ b/unit-tests/varname-empty.mk @@ -1,4 +1,4 @@ -# $NetBSD: varname-empty.mk,v 1.7 2020/10/23 17:53:01 rillig Exp $ +# $NetBSD: varname-empty.mk,v 1.8 2021/02/03 08:34:15 rillig Exp $ # # Tests for the special variable with the empty name. # @@ -41,6 +41,13 @@ ${:U}= assigned indirectly . error .endif +${:U}+= appended indirectly +.if ${:Ufallback} != "fallback" +. error +.endif + +.MAKEFLAGS: -d0 + # Before 2020-08-22, the simple assignment operator '=' after an empty # variable name had an off-by-one bug in Parse_DoVar. The code that was # supposed to "skip to operator character" started its search _after_ the diff --git a/unit-tests/varname-make_print_var_on_error-jobs.mk b/unit-tests/varname-make_print_var_on_error-jobs.mk index d4ab4c8bb711..10a9647fbd1e 100644 --- a/unit-tests/varname-make_print_var_on_error-jobs.mk +++ b/unit-tests/varname-make_print_var_on_error-jobs.mk @@ -1,4 +1,4 @@ -# $NetBSD: varname-make_print_var_on_error-jobs.mk,v 1.2 2020/12/13 19:08:20 rillig Exp $ +# $NetBSD: varname-make_print_var_on_error-jobs.mk,v 1.3 2021/02/04 21:33:14 rillig Exp $ # # Tests for the special MAKE_PRINT_VAR_ON_ERROR variable, which prints the # values of selected variables on error. @@ -13,7 +13,7 @@ # See also: # compat-error.mk -# XXX: As of 2020-12-13, PrintOnError calls Var_Subst with VAR_GLOBAL, which +# XXX: As of 2021-02-04, PrintOnError calls Var_Subst with SCOPE_GLOBAL, which # does not expand the node-local variables like .TARGET. This results in the # double '${.TARGET}' in the output. diff --git a/unit-tests/varname-make_print_var_on_error.mk b/unit-tests/varname-make_print_var_on_error.mk index 3c498febc386..2c145eb5b350 100644 --- a/unit-tests/varname-make_print_var_on_error.mk +++ b/unit-tests/varname-make_print_var_on_error.mk @@ -1,4 +1,4 @@ -# $NetBSD: varname-make_print_var_on_error.mk,v 1.5 2020/12/13 19:08:20 rillig Exp $ +# $NetBSD: varname-make_print_var_on_error.mk,v 1.6 2021/02/04 21:33:14 rillig Exp $ # # Tests for the special MAKE_PRINT_VAR_ON_ERROR variable, which prints the # values of selected variables on error. @@ -11,7 +11,7 @@ # See also: # compat-error.mk -# XXX: As of 2020-12-13, PrintOnError calls Var_Subst with VAR_GLOBAL, which +# XXX: As of 2021-02-04, PrintOnError calls Var_Subst with SCOPE_GLOBAL, which # does not expand the node-local variables like .TARGET. This results in the # double '${.TARGET}' in the output. diff --git a/unit-tests/varname.exp b/unit-tests/varname.exp index 93962d7fb7b7..84f878a9f742 100644 --- a/unit-tests/varname.exp +++ b/unit-tests/varname.exp @@ -5,16 +5,16 @@ Var_Parse: ${VARNAME} with VARE_WANTRES Global:VAR((( = 3 open parentheses Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" with VARE_WANTRES Var_Parse: ${:UVAR(((}= try1 with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Global:.ALLTARGETS = VAR(((=) make: "varname.mk" line 30: No closing parenthesis in archive specification make: "varname.mk" line 30: Error in archive specification: "VAR" Var_Parse: ${:UVAR\(\(\(}= try2 with VARE_UNDEFERR|VARE_WANTRES -Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) -Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) +Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VES_UNDEF) +Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VES_DEF) Global:.ALLTARGETS = VAR(((=) VAR\(\(\(= -make: "varname.mk" line 35: Need an operator +make: "varname.mk" line 35: Invalid line type Var_Parse: ${VARNAME} with VARE_WANTRES Global:VAR((( = try3 Global:.MAKEFLAGS = -r -k -d v -d diff --git a/unit-tests/varparse-dynamic.mk b/unit-tests/varparse-dynamic.mk index 29051d31eeea..c65ba12e6149 100644 --- a/unit-tests/varparse-dynamic.mk +++ b/unit-tests/varparse-dynamic.mk @@ -1,4 +1,4 @@ -# $NetBSD: varparse-dynamic.mk,v 1.3 2020/11/21 15:48:05 rillig Exp $ +# $NetBSD: varparse-dynamic.mk,v 1.4 2021/02/04 21:42:47 rillig Exp $ # Before 2020-07-27, there was an off-by-one error in Var_Parse that skipped # the last character in the variable name. @@ -11,7 +11,7 @@ .endif # When a dynamic variable (such as .TARGET) is evaluated in the global -# context, it is not yet ready to be expanded. Therefore the complete +# scope, it is not yet ready to be expanded. Therefore the complete # expression is returned as the variable value, hoping that it can be # resolved at a later point. # @@ -21,7 +21,7 @@ . error .endif -# If a dynamic variable is expanded in a non-local context, the expression +# If a dynamic variable is expanded in a non-local scope, the expression # based on this variable is not expanded. But there may be nested variable # expressions in the modifiers, and these are kept unexpanded as well. .if ${.TARGET:M${:Ufallback}} != "\${.TARGET:M\${:Ufallback}}" diff --git a/util.c b/util.c index 6143e4c462d2..571d187c5fe1 100644 --- a/util.c +++ b/util.c @@ -1,9 +1,9 @@ -/* $NetBSD: util.c,v 1.73 2020/12/30 10:03:16 rillig Exp $ */ +/* $NetBSD: util.c,v 1.76 2021/02/03 08:00:36 rillig Exp $ */ /* * Missing stuff from OS's * - * $Id: util.c,v 1.45 2021/01/01 22:55:09 sjg Exp $ + * $Id: util.c,v 1.46 2021/02/05 20:02:29 sjg Exp $ */ #include @@ -13,7 +13,7 @@ #include "make.h" -MAKE_RCSID("$NetBSD: util.c,v 1.73 2020/12/30 10:03:16 rillig Exp $"); +MAKE_RCSID("$NetBSD: util.c,v 1.76 2021/02/03 08:00:36 rillig Exp $"); #if !defined(MAKE_NATIVE) && !defined(HAVE_STRERROR) extern int errno, sys_nerr; @@ -74,7 +74,7 @@ unsetenv(const char *name) } while (findenv(name, &offset)) { /* if set multiple times */ - for (p = &environ[offset];; ++p) + for (p = &environ[offset];; p++) if (!(*p = *(p + 1))) break; } @@ -122,7 +122,7 @@ setenv(const char *name, const char *value, int rewrite) environ = savedEnv; environ[offset + 1] = NULL; } - for (cc = name; *cc && *cc != '='; ++cc) /* no `=' in name */ + for (cc = name; *cc && *cc != '='; cc++) /* no `=' in name */ continue; size = cc - name; /* name + `=' + value */ diff --git a/var.c b/var.c index cecd217b47be..eddca9ef6daa 100644 --- a/var.c +++ b/var.c @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.781 2021/01/10 23:59:53 rillig Exp $ */ +/* $NetBSD: var.c,v 1.807 2021/02/05 05:42:39 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -80,14 +80,20 @@ * * Var_End Clean up the module. * - * Var_Set Set the value of the variable, creating it if + * Var_Set + * Var_SetExpand + * Set the value of the variable, creating it if * necessary. * - * Var_Append Append more characters to the variable, creating it if + * Var_Append + * Var_AppendExpand + * Append more characters to the variable, creating it if * necessary. A space is placed between the old value and * the new one. * - * Var_Exists See if a variable exists. + * Var_Exists + * Var_ExistsExpand + * See if a variable exists. * * Var_Value Return the unexpanded value of a variable, or NULL if * the variable is undefined. @@ -96,7 +102,9 @@ * * Var_Parse Parse a variable expression such as ${VAR:Mpattern}. * - * Var_Delete Delete a variable. + * Var_Delete + * Var_DeleteExpand + * Delete a variable. * * Var_ReexportVars * Export some or even all variables to the environment @@ -110,7 +118,7 @@ * Debugging: * Var_Stats Print out hashing statistics if in -dh mode. * - * Var_Dump Print out all variables defined in the given context. + * Var_Dump Print out all variables defined in the given scope. * * XXX: There's a lot of duplication in these functions. */ @@ -139,7 +147,7 @@ #include "metachar.h" /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: var.c,v 1.781 2021/01/10 23:59:53 rillig Exp $"); +MAKE_RCSID("$NetBSD: var.c,v 1.807 2021/02/05 05:42:39 rillig Exp $"); typedef enum VarFlags { VAR_NONE = 0, @@ -187,11 +195,11 @@ typedef enum VarFlags { * value can be queried by expressions such as $V, ${VAR}, or with modifiers * such as ${VAR:S,from,to,g:Q}. * - * There are 3 kinds of variables: context variables, environment variables, + * There are 3 kinds of variables: scope variables, environment variables, * undefined variables. * - * Context variables are stored in a GNode.context. The only way to undefine - * a context variable is using the .undef directive. In particular, it must + * Scope variables are stored in a GNode.scope. The only way to undefine + * a scope variable is using the .undef directive. In particular, it must * not be possible to undefine a variable during the evaluation of an * expression, or Var.name might point nowhere. * @@ -204,7 +212,7 @@ typedef enum VarFlags { typedef struct Var { /* * The name of the variable, once set, doesn't change anymore. - * For context variables, it aliases the corresponding HashEntry name. + * For scope variables, it aliases the corresponding HashEntry name. * For environment and undefined variables, it is allocated. */ FStr name; @@ -231,16 +239,16 @@ typedef enum UnexportWhat { } UnexportWhat; /* Flags for pattern matching in the :S and :C modifiers */ -typedef enum VarPatternFlags { - VARP_NONE = 0, +typedef struct VarPatternFlags { + /* Replace as often as possible ('g') */ - VARP_SUB_GLOBAL = 1 << 0, + Boolean subGlobal: 1; /* Replace only once ('1') */ - VARP_SUB_ONE = 1 << 1, + Boolean subOnce: 1; /* Match at start of word ('^') */ - VARP_ANCHOR_START = 1 << 2, + Boolean anchorStart: 1; /* Match at end of word ('$') */ - VARP_ANCHOR_END = 1 << 3 + Boolean anchorEnd: 1; } VarPatternFlags; /* SepBuf is a string being built from words, interleaved with separators. */ @@ -289,23 +297,29 @@ static char varUndefined[] = ""; static Boolean save_dollars = FALSE; /* - * Internally, variables are contained in four different contexts. - * 1) the environment. They cannot be changed. If an environment - * variable is appended to, the result is placed in the global - * context. - * 2) the global context. Variables set in the makefiles are located - * here. - * 3) the command-line context. All variables set on the command line - * are placed in this context. - * 4) the local context. Each target has associated with it a context - * list. On this list are located the structures describing such - * local variables as $(@) and $(*) - * The four contexts are searched in the reverse order from which they are - * listed (but see opts.checkEnvFirst). + * A scope collects variable names and their values. + * + * The main scope is SCOPE_GLOBAL, which contains the variables that are set + * in the makefiles. SCOPE_INTERNAL acts as a fallback for SCOPE_GLOBAL and + * contains some internal make variables. These internal variables can thus + * be overridden, they can also be restored by undefining the overriding + * variable. + * + * SCOPE_CMDLINE contains variables from the command line arguments. These + * override variables from SCOPE_GLOBAL. + * + * There is no scope for environment variables, these are generated on-the-fly + * whenever they are referenced. If there were such a scope, each change to + * environment variables would have to be reflected in that scope, which may + * be simpler or more complex than the current implementation. + * + * Each target has its own scope, containing the 7 target-local variables + * .TARGET, .ALLSRC, etc. No other variables are in these scopes. */ -GNode *VAR_INTERNAL; /* variables from make itself */ -GNode *VAR_GLOBAL; /* variables from the makefile */ -GNode *VAR_CMDLINE; /* variables defined on the command-line */ + +GNode *SCOPE_CMDLINE; +GNode *SCOPE_GLOBAL; +GNode *SCOPE_INTERNAL; ENUM_FLAGS_RTTI_6(VarFlags, VAR_IN_USE, VAR_FROM_ENV, @@ -372,18 +386,18 @@ CanonicalVarname(const char *name) } static Var * -GNode_FindVar(GNode *ctxt, const char *varname, unsigned int hash) +GNode_FindVar(GNode *scope, const char *varname, unsigned int hash) { - return HashTable_FindValueHash(&ctxt->vars, varname, hash); + return HashTable_FindValueHash(&scope->vars, varname, hash); } /* - * Find the variable in the context, and maybe in other contexts as well. + * Find the variable in the scope, and maybe in other scopes as well. * * Input: * name name to find, is not expanded any further - * ctxt context in which to look first - * elsewhere TRUE to look in other contexts as well + * scope scope in which to look first + * elsewhere TRUE to look in other scopes as well * * Results: * The found variable, or NULL if the variable does not exist. @@ -391,7 +405,7 @@ GNode_FindVar(GNode *ctxt, const char *varname, unsigned int hash) * VarFreeEnv after use. */ static Var * -VarFind(const char *name, GNode *ctxt, Boolean elsewhere) +VarFind(const char *name, GNode *scope, Boolean elsewhere) { Var *var; unsigned int nameHash; @@ -405,23 +419,23 @@ VarFind(const char *name, GNode *ctxt, Boolean elsewhere) name = CanonicalVarname(name); nameHash = Hash_Hash(name); - /* First look for the variable in the given context. */ - var = GNode_FindVar(ctxt, name, nameHash); + /* First look for the variable in the given scope. */ + var = GNode_FindVar(scope, name, nameHash); if (!elsewhere) return var; /* - * The variable was not found in the given context. - * Now look for it in the other contexts as well. + * The variable was not found in the given scope. + * Now look for it in the other scopes as well. */ - if (var == NULL && ctxt != VAR_CMDLINE) - var = GNode_FindVar(VAR_CMDLINE, name, nameHash); + if (var == NULL && scope != SCOPE_CMDLINE) + var = GNode_FindVar(SCOPE_CMDLINE, name, nameHash); - if (!opts.checkEnvFirst && var == NULL && ctxt != VAR_GLOBAL) { - var = GNode_FindVar(VAR_GLOBAL, name, nameHash); - if (var == NULL && ctxt != VAR_INTERNAL) { - /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ - var = GNode_FindVar(VAR_INTERNAL, name, nameHash); + if (!opts.checkEnvFirst && var == NULL && scope != SCOPE_GLOBAL) { + var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); + if (var == NULL && scope != SCOPE_INTERNAL) { + /* SCOPE_INTERNAL is subordinate to SCOPE_GLOBAL */ + var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); } } @@ -433,10 +447,10 @@ VarFind(const char *name, GNode *ctxt, Boolean elsewhere) return VarNew(FStr_InitOwn(varname), env, VAR_FROM_ENV); } - if (opts.checkEnvFirst && ctxt != VAR_GLOBAL) { - var = GNode_FindVar(VAR_GLOBAL, name, nameHash); - if (var == NULL && ctxt != VAR_INTERNAL) - var = GNode_FindVar(VAR_INTERNAL, name, + if (opts.checkEnvFirst && scope != SCOPE_GLOBAL) { + var = GNode_FindVar(SCOPE_GLOBAL, name, nameHash); + if (var == NULL && scope != SCOPE_INTERNAL) + var = GNode_FindVar(SCOPE_INTERNAL, name, nameHash); return var; } @@ -464,71 +478,73 @@ VarFreeEnv(Var *v, Boolean freeValue) return FALSE; FStr_Done(&v->name); - Buf_Destroy(&v->val, freeValue); + if (freeValue) + Buf_Done(&v->val); + else + Buf_DoneData(&v->val); free(v); return TRUE; } /* - * Add a new variable of the given name and value to the given context. + * Add a new variable of the given name and value to the given scope. * The name and val arguments are duplicated so they may safely be freed. */ static void -VarAdd(const char *name, const char *val, GNode *ctxt, VarSetFlags flags) +VarAdd(const char *name, const char *val, GNode *scope, VarSetFlags flags) { - HashEntry *he = HashTable_CreateEntry(&ctxt->vars, name, NULL); + HashEntry *he = HashTable_CreateEntry(&scope->vars, name, NULL); Var *v = VarNew(FStr_InitRefer(/* aliased to */ he->key), val, flags & VAR_SET_READONLY ? VAR_READONLY : VAR_NONE); HashEntry_Set(he, v); - if (!(ctxt->flags & INTERNAL)) - DEBUG3(VAR, "%s:%s = %s\n", ctxt->name, name, val); + DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val); } /* - * Remove a variable from a context, freeing all related memory as well. + * Remove a variable from a scope, freeing all related memory as well. * The variable name is kept as-is, it is not expanded. */ void -Var_DeleteVar(const char *varname, GNode *ctxt) +Var_Delete(GNode *scope, const char *varname) { - HashEntry *he = HashTable_FindEntry(&ctxt->vars, varname); + HashEntry *he = HashTable_FindEntry(&scope->vars, varname); Var *v; if (he == NULL) { - DEBUG2(VAR, "%s:delete %s (not found)\n", ctxt->name, varname); + DEBUG2(VAR, "%s:delete %s (not found)\n", scope->name, varname); return; } - DEBUG2(VAR, "%s:delete %s\n", ctxt->name, varname); + DEBUG2(VAR, "%s:delete %s\n", scope->name, varname); v = HashEntry_Get(he); if (v->flags & VAR_EXPORTED) unsetenv(v->name.str); if (strcmp(v->name.str, MAKE_EXPORTED) == 0) var_exportedVars = VAR_EXPORTED_NONE; assert(v->name.freeIt == NULL); - HashTable_DeleteEntry(&ctxt->vars, he); - Buf_Destroy(&v->val, TRUE); + HashTable_DeleteEntry(&scope->vars, he); + Buf_Done(&v->val); free(v); } /* - * Remove a variable from a context, freeing all related memory as well. + * Remove a variable from a scope, freeing all related memory as well. * The variable name is expanded once. */ void -Var_Delete(const char *name, GNode *ctxt) +Var_DeleteExpand(GNode *scope, const char *name) { FStr varname = FStr_InitRefer(name); if (strchr(varname.str, '$') != NULL) { char *expanded; - (void)Var_Subst(varname.str, VAR_GLOBAL, VARE_WANTRES, + (void)Var_Subst(varname.str, SCOPE_GLOBAL, VARE_WANTRES, &expanded); /* TODO: handle errors */ varname = FStr_InitOwn(expanded); } - Var_DeleteVar(varname.str, ctxt); + Var_Delete(scope, varname.str); FStr_Done(&varname); } @@ -550,7 +566,7 @@ Var_Undef(const char *arg) return; } - vpr = Var_Subst(arg, VAR_GLOBAL, VARE_WANTRES, &expanded); + vpr = Var_Subst(arg, SCOPE_GLOBAL, VARE_WANTRES, &expanded); if (vpr != VPR_OK) { Parse_Error(PARSE_FATAL, "Error in variable names to be undefined"); @@ -563,7 +579,7 @@ Var_Undef(const char *arg) for (i = 0; i < varnames.len; i++) { const char *varname = varnames.words[i]; - Var_DeleteVar(varname, VAR_GLOBAL); + Global_Delete(varname); } Words_Free(varnames); @@ -580,8 +596,8 @@ MayExport(const char *name) if (name[1] == '\0') { /* * A single char. - * If it is one of the vars that should only appear in - * local context, skip it, else we can get Var_Subst + * If it is one of the variables that should only appear in + * local scope, skip it, else we can get Var_Subst * into a loop. */ switch (name[0]) { @@ -621,7 +637,7 @@ ExportVarEnv(Var *v) /* XXX: name is injected without escaping it */ expr = str_concat3("${", name, "}"); - (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &val); + (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &val); /* TODO: handle errors */ setenv(name, val, 1); free(val); @@ -676,7 +692,7 @@ ExportVar(const char *name, VarExportMode mode) if (!MayExport(name)) return FALSE; - v = VarFind(name, VAR_GLOBAL, FALSE); + v = VarFind(name, SCOPE_GLOBAL, FALSE); if (v == NULL) return FALSE; @@ -714,7 +730,7 @@ Var_ReexportVars(void) HashIter hi; /* Ouch! Exporting all variables at once is crazy... */ - HashIter_Init(&hi, &VAR_GLOBAL->vars); + HashIter_Init(&hi, &SCOPE_GLOBAL->vars); while (HashIter_Next(&hi) != NULL) { Var *var = hi.entry->value; ExportVar(var->name.str, VEM_ENV); @@ -722,7 +738,7 @@ Var_ReexportVars(void) return; } - (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, VARE_WANTRES, + (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES, &xvarnames); /* TODO: handle errors */ if (xvarnames[0] != '\0') { @@ -754,7 +770,7 @@ ExportVars(const char *varnames, Boolean isExport, VarExportMode mode) var_exportedVars = VAR_EXPORTED_SOME; if (isExport && mode == VEM_PLAIN) - Var_Append(MAKE_EXPORTED, varname, VAR_GLOBAL); + Global_Append(MAKE_EXPORTED, varname); } Words_Free(words); } @@ -764,7 +780,7 @@ ExportVarsExpand(const char *uvarnames, Boolean isExport, VarExportMode mode) { char *xvarnames; - (void)Var_Subst(uvarnames, VAR_GLOBAL, VARE_WANTRES, &xvarnames); + (void)Var_Subst(uvarnames, SCOPE_GLOBAL, VARE_WANTRES, &xvarnames); /* TODO: handle errors */ ExportVars(xvarnames, isExport, mode); free(xvarnames); @@ -841,7 +857,7 @@ GetVarnamesToUnexport(Boolean isEnv, const char *arg, if (what != UNEXPORT_NAMED) { char *expanded; /* Using .MAKE.EXPORTED */ - (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", VAR_GLOBAL, + (void)Var_Subst("${" MAKE_EXPORTED ":O:u}", SCOPE_GLOBAL, VARE_WANTRES, &expanded); /* TODO: handle errors */ varnames = FStr_InitOwn(expanded); @@ -854,7 +870,7 @@ GetVarnamesToUnexport(Boolean isEnv, const char *arg, static void UnexportVar(const char *varname, UnexportWhat what) { - Var *v = VarFind(varname, VAR_GLOBAL, FALSE); + Var *v = VarFind(varname, SCOPE_GLOBAL, FALSE); if (v == NULL) { DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname); return; @@ -872,9 +888,9 @@ UnexportVar(const char *varname, UnexportWhat what) char *expr = str_concat3("${" MAKE_EXPORTED ":N", v->name.str, "}"); char *cp; - (void)Var_Subst(expr, VAR_GLOBAL, VARE_WANTRES, &cp); + (void)Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES, &cp); /* TODO: handle errors */ - Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL); + Global_Set(MAKE_EXPORTED, cp); free(cp); free(expr); } @@ -897,7 +913,7 @@ UnexportVars(FStr *varnames, UnexportWhat what) Words_Free(words); if (what != UNEXPORT_NAMED) - Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); + Global_Delete(MAKE_EXPORTED); } /* @@ -917,17 +933,24 @@ Var_UnExport(Boolean isEnv, const char *arg) } /* Set the variable to the value; the name is not expanded. */ -static void -SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags) +void +Var_SetWithFlags(GNode *scope, const char *name, const char *val, + VarSetFlags flags) { Var *v; - if (ctxt == VAR_GLOBAL) { - v = VarFind(name, VAR_CMDLINE, FALSE); + assert(val != NULL); + if (name[0] == '\0') { + DEBUG0(VAR, "SetVar: variable name is empty - ignored\n"); + return; + } + + if (scope == SCOPE_GLOBAL) { + v = VarFind(name, SCOPE_CMDLINE, FALSE); if (v != NULL) { if (v->flags & VAR_FROM_CMD) { DEBUG3(VAR, "%s:%s = %s ignored!\n", - ctxt->name, name, val); + scope->name, name, val); return; } VarFreeEnv(v, TRUE); @@ -935,32 +958,32 @@ SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags) } /* - * We only look for a variable in the given context since anything set - * here will override anything in a lower context, so there's not much + * Only look for a variable in the given scope since anything set + * here will override anything in a lower scope, so there's not much * point in searching them all just to save a bit of memory... */ - v = VarFind(name, ctxt, FALSE); + v = VarFind(name, scope, FALSE); if (v == NULL) { - if (ctxt == VAR_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) { + if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT)) { /* * This var would normally prevent the same name being - * added to VAR_GLOBAL, so delete it from there if + * added to SCOPE_GLOBAL, so delete it from there if * needed. Otherwise -V name may show the wrong value. */ /* XXX: name is expanded for the second time */ - Var_Delete(name, VAR_GLOBAL); + Var_DeleteExpand(SCOPE_GLOBAL, name); } - VarAdd(name, val, ctxt, flags); + VarAdd(name, val, scope, flags); } else { if ((v->flags & VAR_READONLY) && !(flags & VAR_SET_READONLY)) { DEBUG3(VAR, "%s:%s = %s ignored (read-only)\n", - ctxt->name, name, val); + scope->name, name, val); return; } Buf_Empty(&v->val); Buf_AddStr(&v->val, val); - DEBUG3(VAR, "%s:%s = %s\n", ctxt->name, name, val); + DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, val); if (v->flags & VAR_EXPORTED) ExportVar(name, VEM_PLAIN); } @@ -969,10 +992,10 @@ SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags) * to the environment (as per POSIX standard) * Other than internals. */ - if (ctxt == VAR_CMDLINE && !(flags & VAR_SET_NO_EXPORT) && + if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) && name[0] != '.') { if (v == NULL) - v = VarFind(name, ctxt, FALSE); /* we just added it */ + v = VarFind(name, scope, FALSE); /* we just added it */ v->flags |= VAR_FROM_CMD; /* @@ -984,7 +1007,7 @@ SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags) if (!opts.varNoExportEnv) setenv(name, val, 1); - Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); + Global_Append(MAKEOVERRIDES, name); } if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0) save_dollars = ParseBoolean(val, save_dollars); @@ -995,8 +1018,8 @@ SetVar(const char *name, const char *val, GNode *ctxt, VarSetFlags flags) /* See Var_Set for documentation. */ void -Var_SetWithFlags(const char *name, const char *val, GNode *ctxt, - VarSetFlags flags) +Var_SetExpandWithFlags(GNode *scope, const char *name, const char *val, + VarSetFlags flags) { const char *unexpanded_name = name; FStr varname = FStr_InitRefer(name); @@ -1005,7 +1028,7 @@ Var_SetWithFlags(const char *name, const char *val, GNode *ctxt, if (strchr(varname.str, '$') != NULL) { char *expanded; - (void)Var_Subst(varname.str, ctxt, VARE_WANTRES, &expanded); + (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded); /* TODO: handle errors */ varname = FStr_InitOwn(expanded); } @@ -1015,13 +1038,19 @@ Var_SetWithFlags(const char *name, const char *val, GNode *ctxt, "name expands to empty string - ignored\n", unexpanded_name, val); } else - SetVar(varname.str, val, ctxt, flags); + Var_SetWithFlags(scope, varname.str, val, flags); FStr_Done(&varname); } +void +Var_Set(GNode *scope, const char *name, const char *val) +{ + Var_SetWithFlags(scope, name, val, VAR_SET_NONE); +} + /* - * Set the variable name to the value val in the given context. + * Set the variable name to the value val in the given scope. * * If the variable doesn't yet exist, it is created. * Otherwise the new value overwrites and replaces the old value. @@ -1029,17 +1058,77 @@ Var_SetWithFlags(const char *name, const char *val, GNode *ctxt, * Input: * name name of the variable to set, is expanded once * val value to give to the variable - * ctxt context in which to set it + * scope scope in which to set it */ void -Var_Set(const char *name, const char *val, GNode *ctxt) +Var_SetExpand(GNode *scope, const char *name, const char *val) { - Var_SetWithFlags(name, val, ctxt, VAR_SET_NONE); + Var_SetExpandWithFlags(scope, name, val, VAR_SET_NONE); +} + +void +Global_Set(const char *name, const char *value) +{ + Var_Set(SCOPE_GLOBAL, name, value); +} + +void +Global_SetExpand(const char *name, const char *value) +{ + Var_SetExpand(SCOPE_GLOBAL, name, value); +} + +void +Global_Delete(const char *name) +{ + Var_Delete(SCOPE_GLOBAL, name); +} + +/* + * Append the value to the named variable. + * + * If the variable doesn't exist, it is created. Otherwise a single space + * and the given value are appended. + */ +void +Var_Append(GNode *scope, const char *name, const char *val) +{ + Var *v; + + v = VarFind(name, scope, scope == SCOPE_GLOBAL); + + if (v == NULL) { + Var_SetWithFlags(scope, name, val, VAR_SET_NONE); + } else if (v->flags & VAR_READONLY) { + DEBUG1(VAR, "Ignoring append to %s since it is read-only\n", + name); + } else if (scope == SCOPE_CMDLINE || !(v->flags & VAR_FROM_CMD)) { + Buf_AddByte(&v->val, ' '); + Buf_AddStr(&v->val, val); + + DEBUG3(VAR, "%s:%s = %s\n", scope->name, name, v->val.data); + + if (v->flags & VAR_FROM_ENV) { + /* + * If the original variable came from the environment, + * we have to install it in the global scope (we + * could place it in the environment, but then we + * should provide a way to export other variables...) + */ + v->flags &= ~(unsigned)VAR_FROM_ENV; + /* + * This is the only place where a variable is + * created whose v->name is not the same as + * scope->vars->key. + */ + HashTable_Set(&scope->vars, name, v); + } + } } /* * The variable of the given name has the given value appended to it in the - * given context. + * given scope. * * If the variable doesn't exist, it is created. Otherwise the strings are * concatenated, with a space in between. @@ -1047,30 +1136,30 @@ Var_Set(const char *name, const char *val, GNode *ctxt) * Input: * name name of the variable to modify, is expanded once * val string to append to it - * ctxt context in which this should occur + * scope scope in which this should occur * * Notes: - * Only if the variable is being sought in the global context is the + * Only if the variable is being sought in the global scope is the * environment searched. - * XXX: Knows its calling circumstances in that if called with ctxt - * an actual target, it will only search that context since only + * XXX: Knows its calling circumstances in that if called with scope + * an actual target, it will only search that scope since only * a local variable could be being appended to. This is actually * a big win and must be tolerated. */ void -Var_Append(const char *name, const char *val, GNode *ctxt) +Var_AppendExpand(GNode *scope, const char *name, const char *val) { char *name_freeIt = NULL; - Var *v; assert(val != NULL); if (strchr(name, '$') != NULL) { const char *unexpanded_name = name; - (void)Var_Subst(name, ctxt, VARE_WANTRES, &name_freeIt); + (void)Var_Subst(name, scope, VARE_WANTRES, &name_freeIt); /* TODO: handle errors */ name = name_freeIt; if (name[0] == '\0') { + /* TODO: update function name in the debug message */ DEBUG2(VAR, "Var_Append(\"%s\", \"%s\", ...) " "name expands to empty string - ignored\n", unexpanded_name, val); @@ -1079,63 +1168,21 @@ Var_Append(const char *name, const char *val, GNode *ctxt) } } - v = VarFind(name, ctxt, ctxt == VAR_GLOBAL); + Var_Append(scope, name, val); - if (v == NULL) { - /* XXX: name is expanded for the second time */ - Var_Set(name, val, ctxt); - } else if (v->flags & VAR_READONLY) { - DEBUG1(VAR, "Ignoring append to %s since it is read-only\n", - name); - } else if (ctxt == VAR_CMDLINE || !(v->flags & VAR_FROM_CMD)) { - Buf_AddByte(&v->val, ' '); - Buf_AddStr(&v->val, val); - - DEBUG3(VAR, "%s:%s = %s\n", - ctxt->name, name, Buf_GetAll(&v->val, NULL)); - - if (v->flags & VAR_FROM_ENV) { - /* - * If the original variable came from the environment, - * we have to install it in the global context (we - * could place it in the environment, but then we - * should provide a way to export other variables...) - */ - v->flags &= ~(unsigned)VAR_FROM_ENV; - /* - * This is the only place where a variable is - * created whose v->name is not the same as - * ctxt->context->key. - */ - HashTable_Set(&ctxt->vars, name, v); - } - } free(name_freeIt); } -/* - * See if the given variable exists, in the given context or in other - * fallback contexts. - * - * Input: - * name Variable to find, is expanded once - * ctxt Context in which to start search - */ -Boolean -Var_Exists(const char *name, GNode *ctxt) +void +Global_Append(const char *name, const char *value) { - FStr varname = FStr_InitRefer(name); - Var *v; + Var_Append(SCOPE_GLOBAL, name, value); +} - if (strchr(varname.str, '$') != NULL) { - char *expanded; - (void)Var_Subst(varname.str, ctxt, VARE_WANTRES, &expanded); - /* TODO: handle errors */ - varname = FStr_InitOwn(expanded); - } - - v = VarFind(varname.str, ctxt, TRUE); - FStr_Done(&varname); +Boolean +Var_Exists(GNode *scope, const char *name) +{ + Var *v = VarFind(name, scope, TRUE); if (v == NULL) return FALSE; @@ -1144,12 +1191,38 @@ Var_Exists(const char *name, GNode *ctxt) } /* - * Return the unexpanded value of the given variable in the given context, - * or the usual contexts. + * See if the given variable exists, in the given scope or in other + * fallback scopes. + * + * Input: + * name Variable to find, is expanded once + * scope Scope in which to start search + */ +Boolean +Var_ExistsExpand(GNode *scope, const char *name) +{ + FStr varname = FStr_InitRefer(name); + Boolean exists; + + if (strchr(varname.str, '$') != NULL) { + char *expanded; + (void)Var_Subst(varname.str, scope, VARE_WANTRES, &expanded); + /* TODO: handle errors */ + varname = FStr_InitOwn(expanded); + } + + exists = Var_Exists(scope, varname.str); + FStr_Done(&varname); + return exists; +} + +/* + * Return the unexpanded value of the given variable in the given scope, + * or the usual scopes. * * Input: * name name to find, is not expanded any further - * ctxt context in which to search for it + * scope scope in which to search for it * * Results: * The value if the variable exists, NULL if it doesn't. @@ -1157,15 +1230,15 @@ Var_Exists(const char *name, GNode *ctxt) * out_freeIt when the returned value is no longer needed. */ FStr -Var_Value(const char *name, GNode *ctxt) +Var_Value(GNode *scope, const char *name) { - Var *v = VarFind(name, ctxt, TRUE); + Var *v = VarFind(name, scope, TRUE); char *value; if (v == NULL) return FStr_InitRefer(NULL); - value = Buf_GetAll(&v->val, NULL); + value = v->val.data; return VarFreeEnv(v, FALSE) ? FStr_InitOwn(value) : FStr_InitRefer(value); @@ -1173,13 +1246,13 @@ Var_Value(const char *name, GNode *ctxt) /* * Return the unexpanded variable value from this node, without trying to look - * up the variable in any other context. + * up the variable in any other scope. */ const char * -Var_ValueDirect(const char *name, GNode *ctxt) +GNode_ValueDirect(GNode *gn, const char *name) { - Var *v = VarFind(name, ctxt, FALSE); - return v != NULL ? Buf_GetAll(&v->val, NULL) : NULL; + Var *v = VarFind(name, gn, FALSE); + return v != NULL ? v->val.data : NULL; } @@ -1222,9 +1295,9 @@ SepBuf_AddStr(SepBuf *buf, const char *str) } static char * -SepBuf_Destroy(SepBuf *buf, Boolean free_buf) +SepBuf_DoneData(SepBuf *buf) { - return Buf_Destroy(&buf->buf, free_buf); + return Buf_DoneData(&buf->buf); } @@ -1377,7 +1450,7 @@ SysVMatch(const char *word, const char *pattern, } struct ModifyWord_SYSVSubstArgs { - GNode *ctx; + GNode *scope; const char *lhs; const char *rhs; }; @@ -1404,7 +1477,7 @@ ModifyWord_SYSVSubst(const char *word, SepBuf *buf, void *data) * match, but only if the lhs had a '%' as well. */ - (void)Var_Subst(args->rhs, args->ctx, VARE_WANTRES, &rhs_expanded); + (void)Var_Subst(args->rhs, args->scope, VARE_WANTRES, &rhs_expanded); /* TODO: handle errors */ rhs = rhs_expanded; @@ -1446,15 +1519,15 @@ ModifyWord_Subst(const char *word, SepBuf *buf, void *data) struct ModifyWord_SubstArgs *args = data; const char *match; - if ((args->pflags & VARP_SUB_ONE) && args->matched) + if (args->pflags.subOnce && args->matched) goto nosub; - if (args->pflags & VARP_ANCHOR_START) { + if (args->pflags.anchorStart) { if (wordLen < args->lhsLen || memcmp(word, args->lhs, args->lhsLen) != 0) goto nosub; - if ((args->pflags & VARP_ANCHOR_END) && wordLen != args->lhsLen) + if ((args->pflags.anchorEnd) && wordLen != args->lhsLen) goto nosub; /* :S,^prefix,replacement, or :S,^whole$,replacement, */ @@ -1465,7 +1538,7 @@ ModifyWord_Subst(const char *word, SepBuf *buf, void *data) return; } - if (args->pflags & VARP_ANCHOR_END) { + if (args->pflags.anchorEnd) { const char *start; if (wordLen < args->lhsLen) @@ -1492,7 +1565,7 @@ ModifyWord_Subst(const char *word, SepBuf *buf, void *data) args->matched = TRUE; wordLen -= (size_t)(match - word) + args->lhsLen; word += (size_t)(match - word) + args->lhsLen; - if (wordLen == 0 || !(args->pflags & VARP_SUB_GLOBAL)) + if (wordLen == 0 || !args->pflags.subGlobal) break; } nosub: @@ -1533,7 +1606,7 @@ ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) int flags = 0; regmatch_t m[10]; - if ((args->pflags & VARP_SUB_ONE) && args->matched) + if (args->pflags.subOnce && args->matched) goto nosub; tryagain: @@ -1581,7 +1654,7 @@ ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) } wp += m[0].rm_eo; - if (args->pflags & VARP_SUB_GLOBAL) { + if (args->pflags.subGlobal) { flags |= REG_NOTBOL; if (m[0].rm_so == 0 && m[0].rm_eo == 0) { SepBuf_AddBytes(buf, wp, 1); @@ -1606,7 +1679,7 @@ ModifyWord_SubstRegex(const char *word, SepBuf *buf, void *data) struct ModifyWord_LoopArgs { - GNode *ctx; + GNode *scope; char *tvar; /* name of temporary variable */ char *str; /* string to expand */ VarEvalFlags eflags; @@ -1623,8 +1696,10 @@ ModifyWord_Loop(const char *word, SepBuf *buf, void *data) return; args = data; - Var_SetWithFlags(args->tvar, word, args->ctx, VAR_SET_NO_EXPORT); - (void)Var_Subst(args->str, args->ctx, args->eflags, &s); + /* XXX: The variable name should not be expanded here. */ + Var_SetExpandWithFlags(args->scope, args->tvar, word, + VAR_SET_NO_EXPORT); + (void)Var_Subst(args->str, args->scope, args->eflags, &s); /* TODO: handle errors */ DEBUG4(VAR, "ModifyWord_Loop: " @@ -1694,7 +1769,7 @@ VarSelectWords(char sep, Boolean oneBigWord, const char *str, int first, Words_Free(words); - return SepBuf_Destroy(&buf, FALSE); + return SepBuf_DoneData(&buf); } @@ -1739,7 +1814,7 @@ ModifyWords(const char *str, if (oneBigWord) { SepBuf_Init(&result, sep); modifyWord(str, &result, modifyWord_args); - return SepBuf_Destroy(&result, FALSE); + return SepBuf_DoneData(&result); } SepBuf_Init(&result, sep); @@ -1751,13 +1826,13 @@ ModifyWords(const char *str, for (i = 0; i < words.len; i++) { modifyWord(words.words[i], &result, modifyWord_args); - if (Buf_Len(&result.buf) > 0) + if (result.buf.len > 0) SepBuf_Sep(&result); } Words_Free(words); - return SepBuf_Destroy(&result, FALSE); + return SepBuf_DoneData(&result); } @@ -1779,7 +1854,7 @@ Words_JoinFree(Words words) Words_Free(words); - return Buf_Destroy(&buf, FALSE); + return Buf_DoneData(&buf); } /* Remove adjacent duplicate words. */ @@ -1826,7 +1901,7 @@ VarQuote(const char *str, Boolean quoteDollar) Buf_AddStr(&buf, "\\$"); } - return Buf_Destroy(&buf, FALSE); + return Buf_DoneData(&buf); } /* @@ -1977,21 +2052,24 @@ VarStrftime(const char *fmt, Boolean zulu, time_t tim) * Some modifiers need to free some memory. */ -typedef enum VarExprFlags { - VEF_NONE = 0, +typedef enum VarExprStatus { + /* The variable expression is based in a regular, defined variable. */ + VES_NONE, /* The variable expression is based on an undefined variable. */ - VEF_UNDEF = 0x01, + VES_UNDEF, /* * The variable expression started as an undefined expression, but one * of the modifiers (such as :D or :U) has turned the expression from * undefined to defined. */ - VEF_DEF = 0x02 -} VarExprFlags; - -ENUM_FLAGS_RTTI_2(VarExprFlags, - VEF_UNDEF, VEF_DEF); + VES_DEF +} VarExprStatus; +static const char * const VarExprStatus_Name[] = { + "none", + "VES_UNDEF", + "VES_DEF" +}; typedef struct ApplyModifiersState { /* '\0' or '{' or '(' */ @@ -1999,7 +2077,7 @@ typedef struct ApplyModifiersState { /* '\0' or '}' or ')' */ const char endc; Var *const var; - GNode *const ctxt; + GNode *const scope; const VarEvalFlags eflags; /* * The new value of the expression, after applying the modifier, @@ -2014,14 +2092,14 @@ typedef struct ApplyModifiersState { * big word, possibly containing spaces. */ Boolean oneBigWord; - VarExprFlags exprFlags; + VarExprStatus exprStatus; } ApplyModifiersState; static void ApplyModifiersState_Define(ApplyModifiersState *st) { - if (st->exprFlags & VEF_UNDEF) - st->exprFlags |= VEF_DEF; + if (st->exprStatus == VES_UNDEF) + st->exprStatus = VES_DEF; } typedef enum ApplyModifierResult { @@ -2099,7 +2177,7 @@ ParseModifierPartSubst( if (p[1] == delim) { /* Unescaped $ at end of pattern */ if (out_pflags != NULL) - *out_pflags |= VARP_ANCHOR_END; + out_pflags->anchorEnd = TRUE; else Buf_AddByte(&buf, *p); p++; @@ -2112,7 +2190,7 @@ ParseModifierPartSubst( VarEvalFlags nested_eflags = eflags & ~(unsigned)VARE_KEEP_DOLLAR; - (void)Var_Parse(&nested_p, st->ctxt, nested_eflags, + (void)Var_Parse(&nested_p, st->scope, nested_eflags, &nested_val); /* TODO: handle errors */ Buf_AddStr(&buf, nested_val.str); @@ -2168,11 +2246,11 @@ ParseModifierPartSubst( return VPR_ERR; } - *pp = ++p; + *pp = p + 1; if (out_length != NULL) - *out_length = Buf_Len(&buf); + *out_length = buf.len; - *out_part = Buf_Destroy(&buf, FALSE); + *out_part = Buf_DoneData(&buf); DEBUG1(VAR, "Modifier part: \"%s\"\n", *out_part); return VPR_OK; } @@ -2291,7 +2369,7 @@ ApplyModifier_Loop(const char **pp, const char *val, ApplyModifiersState *st) char prev_sep; VarParseResult res; - args.ctx = st->ctxt; + args.scope = st->scope; (*pp)++; /* Skip the first '@' */ res = ParseModifierPart(pp, '@', VARE_NONE, st, &args.tvar); @@ -2316,7 +2394,11 @@ ApplyModifier_Loop(const char **pp, const char *val, ApplyModifiersState *st) ModifyWords(val, ModifyWord_Loop, &args, st->oneBigWord, st->sep)); st->sep = prev_sep; /* XXX: Consider restoring the previous variable instead of deleting. */ - Var_Delete(args.tvar, st->ctxt); + /* + * XXX: The variable name should not be expanded here, see + * ModifyWord_Loop. + */ + Var_DeleteExpand(st->scope, args.tvar); free(args.tvar); free(args.str); return AMR_OK; @@ -2331,7 +2413,7 @@ ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st) VarEvalFlags eflags = VARE_NONE; if (st->eflags & VARE_WANTRES) - if ((**pp == 'D') == !(st->exprFlags & VEF_UNDEF)) + if ((**pp == 'D') == (st->exprStatus == VES_NONE)) eflags = st->eflags; Buf_Init(&buf); @@ -2357,7 +2439,7 @@ ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st) if (*p == '$') { FStr nested_val; - (void)Var_Parse(&p, st->ctxt, eflags, &nested_val); + (void)Var_Parse(&p, st->scope, eflags, &nested_val); /* TODO: handle errors */ Buf_AddStr(&buf, nested_val.str); FStr_Done(&nested_val); @@ -2373,10 +2455,10 @@ ApplyModifier_Defined(const char **pp, const char *val, ApplyModifiersState *st) ApplyModifiersState_Define(st); if (eflags & VARE_WANTRES) { - st->newVal = FStr_InitOwn(Buf_Destroy(&buf, FALSE)); + st->newVal = FStr_InitOwn(Buf_DoneData(&buf)); } else { st->newVal = FStr_InitRefer(val); - Buf_Destroy(&buf, TRUE); + Buf_Done(&buf); } return AMR_OK; } @@ -2571,7 +2653,7 @@ ApplyModifier_Range(const char **pp, const char *val, ApplyModifiersState *st) Buf_AddInt(&buf, 1 + (int)i); } - st->newVal = FStr_InitOwn(Buf_Destroy(&buf, FALSE)); + st->newVal = FStr_InitOwn(Buf_DoneData(&buf)); return AMR_OK; } @@ -2639,7 +2721,7 @@ ApplyModifier_Match(const char **pp, const char *val, ApplyModifiersState *st) if (needSubst) { char *old_pattern = pattern; - (void)Var_Subst(pattern, st->ctxt, st->eflags, &pattern); + (void)Var_Subst(pattern, st->scope, st->eflags, &pattern); /* TODO: handle errors */ free(old_pattern); } @@ -2672,7 +2754,7 @@ ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st) *pp += 2; - args.pflags = VARP_NONE; + args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE }; args.matched = FALSE; /* @@ -2680,7 +2762,7 @@ ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st) * start of the word -- skip over it and flag pattern. */ if (**pp == '^') { - args.pflags |= VARP_ANCHOR_START; + args.pflags.anchorStart = TRUE; (*pp)++; } @@ -2700,10 +2782,10 @@ ApplyModifier_Subst(const char **pp, const char *val, ApplyModifiersState *st) for (;; (*pp)++) { switch (**pp) { case 'g': - args.pflags |= VARP_SUB_GLOBAL; + args.pflags.subGlobal = TRUE; continue; case '1': - args.pflags |= VARP_SUB_ONE; + args.pflags.subOnce = TRUE; continue; case 'W': oneBigWord = TRUE; @@ -2751,16 +2833,16 @@ ApplyModifier_Regex(const char **pp, const char *val, ApplyModifiersState *st) return AMR_CLEANUP; } - args.pflags = VARP_NONE; + args.pflags = (VarPatternFlags){ FALSE, FALSE, FALSE, FALSE }; args.matched = FALSE; oneBigWord = st->oneBigWord; for (;; (*pp)++) { switch (**pp) { case 'g': - args.pflags |= VARP_SUB_GLOBAL; + args.pflags.subGlobal = TRUE; continue; case '1': - args.pflags |= VARP_SUB_ONE; + args.pflags.subOnce = TRUE; continue; case 'W': oneBigWord = TRUE; @@ -2996,7 +3078,7 @@ ApplyModifier_Words(const char **pp, const char *val, ApplyModifiersState *st) /* 3 digits + '\0' is usually enough */ Buf_InitSize(&buf, 4); Buf_AddInt(&buf, (int)ac); - st->newVal = FStr_InitOwn(Buf_Destroy(&buf, FALSE)); + st->newVal = FStr_InitOwn(Buf_DoneData(&buf)); } goto ok; } @@ -3188,7 +3270,7 @@ ApplyModifier_IfElse(const char **pp, ApplyModifiersState *st) static ApplyModifierResult ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) { - GNode *ctxt; + GNode *scope; char delim; char *val; VarParseResult res; @@ -3208,11 +3290,11 @@ ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) return AMR_BAD; } - ctxt = st->ctxt; /* context where v belongs */ - if (!(st->exprFlags & VEF_UNDEF) && st->ctxt != VAR_GLOBAL) { - Var *gv = VarFind(st->var->name.str, st->ctxt, FALSE); + scope = st->scope; /* scope where v belongs */ + if (st->exprStatus == VES_NONE && st->scope != SCOPE_GLOBAL) { + Var *gv = VarFind(st->var->name.str, st->scope, FALSE); if (gv == NULL) - ctxt = VAR_GLOBAL; + scope = SCOPE_GLOBAL; else VarFreeEnv(gv, TRUE); } @@ -3235,10 +3317,11 @@ ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) (*pp)--; + /* XXX: Expanding the variable name at this point sounds wrong. */ if (st->eflags & VARE_WANTRES) { switch (op[0]) { case '+': - Var_Append(st->var->name.str, val, ctxt); + Var_AppendExpand(scope, st->var->name.str, val); break; case '!': { const char *errfmt; @@ -3246,16 +3329,17 @@ ApplyModifier_Assign(const char **pp, ApplyModifiersState *st) if (errfmt != NULL) Error(errfmt, val); else - Var_Set(st->var->name.str, cmd_output, ctxt); + Var_SetExpand(scope, + st->var->name.str, cmd_output); free(cmd_output); break; } case '?': - if (!(st->exprFlags & VEF_UNDEF)) + if (st->exprStatus == VES_NONE) break; /* FALLTHROUGH */ default: - Var_Set(st->var->name.str, val, ctxt); + Var_SetExpand(scope, st->var->name.str, val); break; } } @@ -3279,11 +3363,11 @@ ApplyModifier_Remember(const char **pp, const char *val, if (mod[1] == '=') { size_t n = strcspn(mod + 2, ":)}"); char *name = bmake_strldup(mod + 2, n); - Var_Set(name, val, st->ctxt); + Var_SetExpand(st->scope, name, val); free(name); *pp = mod + 2 + n; } else { - Var_Set("_", val, st->ctxt); + Var_Set(st->scope, "_", val); *pp = mod + 1; } st->newVal = FStr_InitRefer(val); @@ -3363,7 +3447,7 @@ ApplyModifier_SysV(const char **pp, const char *val, ApplyModifiersState *st) if (lhs[0] == '\0' && val[0] == '\0') { st->newVal = FStr_InitRefer(val); /* special case */ } else { - struct ModifyWord_SYSVSubstArgs args = { st->ctxt, lhs, rhs }; + struct ModifyWord_SYSVSubstArgs args = { st->scope, lhs, rhs }; st->newVal = FStr_InitOwn( ModifyWords(val, ModifyWord_SYSVSubst, &args, st->oneBigWord, st->sep)); @@ -3402,7 +3486,6 @@ LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc, { char eflags_str[VarEvalFlags_ToStringSize]; char vflags_str[VarFlags_ToStringSize]; - char exprflags_str[VarExprFlags_ToStringSize]; Boolean is_single_char = mod[0] != '\0' && (mod[1] == endc || mod[1] == ':'); @@ -3410,13 +3493,9 @@ LogBeforeApply(const ApplyModifiersState *st, const char *mod, char endc, * be used since the end of the modifier is not yet known. */ debug_printf("Applying ${%s:%c%s} to \"%s\" (%s, %s, %s)\n", st->var->name.str, mod[0], is_single_char ? "" : "...", val, - Enum_FlagsToString(eflags_str, sizeof eflags_str, - st->eflags, VarEvalFlags_ToStringSpecs), - Enum_FlagsToString(vflags_str, sizeof vflags_str, - st->var->flags, VarFlags_ToStringSpecs), - Enum_FlagsToString(exprflags_str, sizeof exprflags_str, - st->exprFlags, - VarExprFlags_ToStringSpecs)); + VarEvalFlags_ToString(eflags_str, st->eflags), + VarFlags_ToString(vflags_str, st->var->flags), + VarExprStatus_Name[st->exprStatus]); } static void @@ -3424,20 +3503,15 @@ LogAfterApply(ApplyModifiersState *st, const char *p, const char *mod) { char eflags_str[VarEvalFlags_ToStringSize]; char vflags_str[VarFlags_ToStringSize]; - char exprflags_str[VarExprFlags_ToStringSize]; const char *quot = st->newVal.str == var_Error ? "" : "\""; const char *newVal = st->newVal.str == var_Error ? "error" : st->newVal.str; debug_printf("Result of ${%s:%.*s} is %s%s%s (%s, %s, %s)\n", st->var->name.str, (int)(p - mod), mod, quot, newVal, quot, - Enum_FlagsToString(eflags_str, sizeof eflags_str, - st->eflags, VarEvalFlags_ToStringSpecs), - Enum_FlagsToString(vflags_str, sizeof vflags_str, - st->var->flags, VarFlags_ToStringSpecs), - Enum_FlagsToString(exprflags_str, sizeof exprflags_str, - st->exprFlags, - VarExprFlags_ToStringSpecs)); + VarEvalFlags_ToString(eflags_str, st->eflags), + VarFlags_ToString(vflags_str, st->var->flags), + VarExprStatus_Name[st->exprStatus]); } static ApplyModifierResult @@ -3507,7 +3581,7 @@ ApplyModifier(const char **pp, const char *val, ApplyModifiersState *st) } static FStr ApplyModifiers(const char **, FStr, char, char, Var *, - VarExprFlags *, GNode *, VarEvalFlags); + VarExprStatus *, GNode *, VarEvalFlags); typedef enum ApplyModifiersIndirectResult { /* The indirect modifiers have been applied successfully. */ @@ -3541,7 +3615,7 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp, const char *p = *pp; FStr mods; - (void)Var_Parse(&p, st->ctxt, st->eflags, &mods); + (void)Var_Parse(&p, st->scope, st->eflags, &mods); /* TODO: handle errors */ if (mods.str[0] != '\0' && *p != '\0' && *p != ':' && *p != st->endc) { @@ -3555,7 +3629,7 @@ ApplyModifiersIndirect(ApplyModifiersState *st, const char **pp, if (mods.str[0] != '\0') { const char *modsp = mods.str; FStr newVal = ApplyModifiers(&modsp, *inout_value, '\0', '\0', - st->var, &st->exprFlags, st->ctxt, st->eflags); + st->var, &st->exprStatus, st->scope, st->eflags); *inout_value = newVal; if (newVal.str == var_Error || *modsp != '\0') { FStr_Done(&mods); @@ -3651,17 +3725,22 @@ ApplyModifiers( char startc, /* '(' or '{', or '\0' for indirect modifiers */ char endc, /* ')' or '}', or '\0' for indirect modifiers */ Var *v, - VarExprFlags *exprFlags, - GNode *ctxt, /* for looking up and modifying variables */ + VarExprStatus *exprStatus, + GNode *scope, /* for looking up and modifying variables */ VarEvalFlags eflags ) { ApplyModifiersState st = { - startc, endc, v, ctxt, eflags, + startc, endc, v, scope, eflags, +#if defined(lint) + /* lint cannot parse C99 struct initializers yet. */ + { var_Error, NULL }, +#else FStr_InitRefer(var_Error), /* .newVal */ +#endif ' ', /* .sep */ FALSE, /* .oneBigWord */ - *exprFlags /* .exprFlags */ + *exprStatus /* .exprStatus */ }; const char *p; const char *mod; @@ -3704,7 +3783,7 @@ ApplyModifiers( *pp = p; assert(value.str != NULL); /* Use var_Error or varUndefined instead. */ - *exprFlags = st.exprFlags; + *exprStatus = st.exprStatus; return value; bad_modifier: @@ -3715,7 +3794,7 @@ ApplyModifiers( cleanup: *pp = p; FStr_Done(&value); - *exprFlags = st.exprFlags; + *exprStatus = st.exprStatus; return FStr_InitRefer(var_Error); } @@ -3748,11 +3827,11 @@ VarnameIsDynamic(const char *name, size_t len) } static const char * -UndefinedShortVarValue(char varname, const GNode *ctxt) +UndefinedShortVarValue(char varname, const GNode *scope) { - if (ctxt == VAR_CMDLINE || ctxt == VAR_GLOBAL) { + if (scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL) { /* - * If substituting a local variable in a non-local context, + * If substituting a local variable in a non-local scope, * assume it's for dynamic source stuff. We have to handle * this specially and return the longhand for the variable * with the dollar sign escaped so it makes it back to the @@ -3780,7 +3859,7 @@ UndefinedShortVarValue(char varname, const GNode *ctxt) */ static char * ParseVarname(const char **pp, char startc, char endc, - GNode *ctxt, VarEvalFlags eflags, + GNode *scope, VarEvalFlags eflags, size_t *out_varname_len) { Buffer buf; @@ -3803,7 +3882,7 @@ ParseVarname(const char **pp, char startc, char endc, /* A variable inside a variable, expand. */ if (*p == '$') { FStr nested_val; - (void)Var_Parse(&p, ctxt, eflags, &nested_val); + (void)Var_Parse(&p, scope, eflags, &nested_val); /* TODO: handle errors */ Buf_AddStr(&buf, nested_val.str); FStr_Done(&nested_val); @@ -3813,8 +3892,8 @@ ParseVarname(const char **pp, char startc, char endc, } } *pp = p; - *out_varname_len = Buf_Len(&buf); - return Buf_Destroy(&buf, FALSE); + *out_varname_len = buf.len; + return Buf_DoneData(&buf); } static VarParseResult @@ -3851,7 +3930,7 @@ ValidShortVarname(char varname, const char *start) * Return whether to continue parsing. */ static Boolean -ParseVarnameShort(char startc, const char **pp, GNode *ctxt, +ParseVarnameShort(char startc, const char **pp, GNode *scope, VarEvalFlags eflags, VarParseResult *out_FALSE_res, const char **out_FALSE_val, Var **out_TRUE_var) @@ -3876,12 +3955,12 @@ ParseVarnameShort(char startc, const char **pp, GNode *ctxt, name[0] = startc; name[1] = '\0'; - v = VarFind(name, ctxt, TRUE); + v = VarFind(name, scope, TRUE); if (v == NULL) { const char *val; *pp += 2; - val = UndefinedShortVarValue(startc, ctxt); + val = UndefinedShortVarValue(startc, scope); if (val == NULL) val = eflags & VARE_UNDEFERR ? var_Error : varUndefined; @@ -3914,11 +3993,11 @@ ParseVarnameShort(char startc, const char **pp, GNode *ctxt, /* Find variables like @F or val, FALSE); + char *varValue = Buf_DoneData(&v->val); if (value == varValue) *out_val_freeIt = varValue; else @@ -4103,7 +4182,7 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value) * When parsing a condition in ParseEmptyArg, it may also * point to the "y" of "empty(VARNAME:Modifiers)", which * is syntactically the same. - * ctxt The context for finding variables + * scope The scope for finding variables * eflags Control the exact details of parsing * * Output: @@ -4131,7 +4210,7 @@ FreeEnvVar(void **out_val_freeIt, Var *v, const char *value) */ /* coverity[+alloc : arg-*4] */ VarParseResult -Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) +Var_Parse(const char **pp, GNode *scope, VarEvalFlags eflags, FStr *out_val) { const char *p = *pp; const char *const start = p; @@ -4143,7 +4222,7 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) char endc; /* * TRUE if the variable is local and we're expanding it in a - * non-local context. This is done to support dynamic sources. + * non-local scope. This is done to support dynamic sources. * The result is just the expression, unaltered. */ Boolean dynamic; @@ -4151,11 +4230,10 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) Var *v; FStr value; char eflags_str[VarEvalFlags_ToStringSize]; - VarExprFlags exprFlags = VEF_NONE; + VarExprStatus exprStatus = VES_NONE; DEBUG2(VAR, "Var_Parse: %s with %s\n", start, - Enum_FlagsToString(eflags_str, sizeof eflags_str, eflags, - VarEvalFlags_ToStringSpecs)); + VarEvalFlags_ToString(eflags_str, eflags)); *out_val = FStr_InitRefer(NULL); extramodifiers = NULL; /* extra modifiers to apply first */ @@ -4170,17 +4248,17 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) startc = p[1]; if (startc != '(' && startc != '{') { VarParseResult res; - if (!ParseVarnameShort(startc, pp, ctxt, eflags, &res, + if (!ParseVarnameShort(startc, pp, scope, eflags, &res, &out_val->str, &v)) return res; haveModifier = FALSE; p++; } else { VarParseResult res; - if (!ParseVarnameLong(p, startc, ctxt, eflags, + if (!ParseVarnameLong(p, startc, scope, eflags, pp, &res, out_val, &endc, &p, &v, &haveModifier, &extramodifiers, - &dynamic, &exprFlags)) + &dynamic, &exprStatus)) return res; } @@ -4196,7 +4274,7 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) * the then-current value of the variable. This might also invoke * undefined behavior. */ - value = FStr_InitRefer(Buf_GetAll(&v->val, NULL)); + value = FStr_InitRefer(v->val.data); /* * Before applying any modifiers, expand any nested expressions from @@ -4208,7 +4286,7 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) if (opts.strict) nested_eflags &= ~(unsigned)VARE_UNDEFERR; v->flags |= VAR_IN_USE; - (void)Var_Subst(value.str, ctxt, nested_eflags, &expanded); + (void)Var_Subst(value.str, scope, nested_eflags, &expanded); v->flags &= ~(unsigned)VAR_IN_USE; /* TODO: handle errors */ value = FStr_InitOwn(expanded); @@ -4218,14 +4296,14 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) if (extramodifiers != NULL) { const char *em = extramodifiers; value = ApplyModifiers(&em, value, '\0', '\0', - v, &exprFlags, ctxt, eflags); + v, &exprStatus, scope, eflags); } if (haveModifier) { p++; /* Skip initial colon. */ value = ApplyModifiers(&p, value, startc, endc, - v, &exprFlags, ctxt, eflags); + v, &exprStatus, scope, eflags); } } @@ -4237,8 +4315,8 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) if (v->flags & VAR_FROM_ENV) { FreeEnvVar(&value.freeIt, v, value.str); - } else if (exprFlags & VEF_UNDEF) { - if (!(exprFlags & VEF_DEF)) { + } else if (exprStatus != VES_NONE) { + if (exprStatus != VES_DEF) { FStr_Done(&value); if (dynamic) { value = FStr_InitOwn(bmake_strsedup(start, p)); @@ -4252,8 +4330,8 @@ Var_Parse(const char **pp, GNode *ctxt, VarEvalFlags eflags, FStr *out_val) ? var_Error : varUndefined); } } - if (value.str != Buf_GetAll(&v->val, NULL)) - Buf_Destroy(&v->val, TRUE); + if (value.str != v->val.data) + Buf_Done(&v->val); FStr_Done(&v->name); free(v); } @@ -4275,14 +4353,14 @@ VarSubstDollarDollar(const char **pp, Buffer *res, VarEvalFlags eflags) } static void -VarSubstExpr(const char **pp, Buffer *buf, GNode *ctxt, +VarSubstExpr(const char **pp, Buffer *buf, GNode *scope, VarEvalFlags eflags, Boolean *inout_errorReported) { const char *p = *pp; const char *nested_p = p; FStr val; - (void)Var_Parse(&nested_p, ctxt, eflags, &val); + (void)Var_Parse(&nested_p, scope, eflags, &val); /* TODO: handle errors */ if (val.str == var_Error || val.str == varUndefined) { @@ -4350,12 +4428,12 @@ VarSubstPlain(const char **pp, Buffer *res) * Input: * str The string in which the variable expressions are * expanded. - * ctxt The context in which to start searching for - * variables. The other contexts are searched as well. + * scope The scope in which to start searching for + * variables. The other scopes are searched as well. * eflags Special effects during expansion. */ VarParseResult -Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags, char **out_res) +Var_Subst(const char *str, GNode *scope, VarEvalFlags eflags, char **out_res) { const char *p = str; Buffer res; @@ -4372,12 +4450,12 @@ Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags, char **out_res) if (p[0] == '$' && p[1] == '$') VarSubstDollarDollar(&p, &res, eflags); else if (p[0] == '$') - VarSubstExpr(&p, &res, ctxt, eflags, &errorReported); + VarSubstExpr(&p, &res, scope, eflags, &errorReported); else VarSubstPlain(&p, &res); } - *out_res = Buf_DestroyCompact(&res); + *out_res = Buf_DoneDataCompact(&res); return VPR_OK; } @@ -4385,9 +4463,9 @@ Var_Subst(const char *str, GNode *ctxt, VarEvalFlags eflags, char **out_res) void Var_Init(void) { - VAR_INTERNAL = GNode_New("Internal"); - VAR_GLOBAL = GNode_New("Global"); - VAR_CMDLINE = GNode_New("Command"); + SCOPE_INTERNAL = GNode_New("Internal"); + SCOPE_GLOBAL = GNode_New("Global"); + SCOPE_CMDLINE = GNode_New("Command"); } /* Clean up the variables module. */ @@ -4400,12 +4478,12 @@ Var_End(void) void Var_Stats(void) { - HashTable_DebugStats(&VAR_GLOBAL->vars, "VAR_GLOBAL"); + HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables"); } -/* Print all variables in a context, sorted by name. */ +/* Print all variables in a scope, sorted by name. */ void -Var_Dump(GNode *ctxt) +Var_Dump(GNode *scope) { Vector /* of const char * */ vec; HashIter hi; @@ -4414,7 +4492,7 @@ Var_Dump(GNode *ctxt) Vector_Init(&vec, sizeof(const char *)); - HashIter_Init(&hi, &ctxt->vars); + HashIter_Init(&hi, &scope->vars); while (HashIter_Next(&hi) != NULL) *(const char **)Vector_Push(&vec) = hi.entry->key; varnames = vec.items; @@ -4423,9 +4501,8 @@ Var_Dump(GNode *ctxt) for (i = 0; i < vec.len; i++) { const char *varname = varnames[i]; - Var *var = HashTable_FindValue(&ctxt->vars, varname); - debug_printf("%-16s = %s\n", - varname, Buf_GetAll(&var->val, NULL)); + Var *var = HashTable_FindValue(&scope->vars, varname); + debug_printf("%-16s = %s\n", varname, var->val.data); } Vector_Done(&vec);