aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2024-01-14 01:16:25 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2024-01-14 01:16:25 +0000
commit7a05a7153af649605e1ebde33aac94a14ed2a4cd (patch)
treed18c7d07668e9b934a80ab50502bdfce284047d8
parent1012cf15f75d1e9048779abd07270a37cdba590a (diff)
downloadsrc-7a05a7153af649605e1ebde33aac94a14ed2a4cd.tar.gz
src-7a05a7153af649605e1ebde33aac94a14ed2a4cd.zip
Import bmake-20240108vendor/NetBSD/bmake/20240108
Interesting/relevant changes since bmake-20230909 * VERSION (_MAKE_VERSION): 20240106 Merge with NetBSD make, pick up o fix duplicate progname when reporting an unknown target o unit tests for Cmd_Exec using temp file * VERSION (_MAKE_VERSION): 20240105 Merge with NetBSD make, pick up o main.c: Cmd_Exec write cmd to a file if too big avoid blowing commandline/env limits * VERSION (_MAKE_VERSION): 20240101 o util.c: flesh out more of strftime * configure.in: add --with-bmake-strftime it is not a full implementation but enough to pass all the unit-tests. * parse.c: LoadFile do not append \n to empty buffer. * VERSION (_MAKE_VERSION): 20231230 Merge with NetBSD make, pick up o simplify memory allocation for string buffers o fix declared types of list nodes o suff.c: clean up freeing of suffixes o var.c: simplify debug message for the ':@var@...@' modifier clean up variable handling * VERSION (_MAKE_VERSION): 20231226 Merge with NetBSD make, pick up o compat.c: ensure make's output is correctly ordered with that of the target when not going to a tty o main.c: check for shellPath whether to call Shell_Init() * VERSION (_MAKE_VERSION): 20231224 Merge with NetBSD make, pick up o compat.c: check for shellPath whether to call Shell_Init() tweak the unit test to detect the bug thus fixed. o make.1: do not claim .SHELL is only used by jobs mode. * VERSION (_MAKE_VERSION): 20231220 Merge with NetBSD make, pick up o str.c: speed up pattern matching in the ':M' modifier o var.c: fix confusing debug logging when deleting a variable use consistent debug messages style when ignoring variables * VERSION (_MAKE_VERSION): 20231210 Merge with NetBSD make, pick up o var.c: avoid segfault on empty :C match expression explain in debug log why variable assignment is ignored. * VERSION (_MAKE_VERSION): 20231208 Merge with NetBSD make, pick up o var.c: ensure fromCmd is set correctly for variables set on command line. * VERSION (_MAKE_VERSION): 20231124 Merge with NetBSD make, pick up o main.c: cleanup processing of -j fix lint warning about strchr o var.c: more accurate error message for invalid ':mtime' argument cleanup :[...] modifier avoid reading beyond substring when comparing o unit-tests cover all cases of :mtime, test and explain exporting of variables o cleanup comments * bsd.after-import.mk (ECHO_TAG): FreeBSD no longer uses $FreeBSD$ tag, so avoid adding it. mk/ChangeLog since bmake-20230909 * dirdeps.mk: for MAKE_VERSION 20240105 we do not have the same limits on command line length, so skip export of lists to env. * jobs.mk: avoid C suffix in JOB_MAX_C if factor is floating point. This keeps JOB_MAX numeric incase another makefile does comparisons. * gendirdeps.mk: if META_XTRAS is passed to us, add to META_FILES
-rw-r--r--ChangeLog102
-rw-r--r--README17
-rw-r--r--VERSION2
-rw-r--r--arch.c10
-rw-r--r--bmake.16
-rw-r--r--bmake.cat16
-rwxr-xr-xboot-strap16
-rw-r--r--bsd.after-import.mk17
-rw-r--r--buf.c6
-rw-r--r--buf.h4
-rw-r--r--compat.c65
-rw-r--r--cond.c111
-rwxr-xr-xconfigure34
-rw-r--r--configure.in16
-rw-r--r--dir.c330
-rw-r--r--for.c14
-rw-r--r--hash.c28
-rw-r--r--hash.h21
-rw-r--r--job.c139
-rw-r--r--job.h6
-rw-r--r--lst.c22
-rw-r--r--lst.h8
-rw-r--r--main.c156
-rw-r--r--make.16
-rw-r--r--make.c28
-rw-r--r--make.h5
-rw-r--r--meta.c6
-rw-r--r--mk/ChangeLog42
-rw-r--r--mk/compiler.mk4
-rw-r--r--mk/dirdeps.mk17
-rw-r--r--mk/dpadd.mk14
-rw-r--r--mk/gendirdeps.mk6
-rw-r--r--mk/host-target.mk7
-rw-r--r--mk/init.mk8
-rwxr-xr-x[-rw-r--r--]mk/install-mk4
-rw-r--r--mk/jobs.mk5
-rw-r--r--mk/lib.mk25
-rw-r--r--mk/man.mk93
-rw-r--r--mk/meta.autodep.mk4
-rw-r--r--mk/own.mk3
-rw-r--r--mk/prog.mk5
-rw-r--r--mk/rst2htm.mk3
-rwxr-xr-x[-rw-r--r--]os.sh0
-rw-r--r--parse.c279
-rw-r--r--str.c77
-rw-r--r--str.h29
-rw-r--r--suff.c168
-rw-r--r--unit-tests/Makefile9
-rw-r--r--unit-tests/cmd-errors-jobs.exp2
-rw-r--r--unit-tests/cmd-errors-lint.exp2
-rw-r--r--unit-tests/cmd-errors.exp2
-rw-r--r--unit-tests/cmdline-undefined.mk4
-rw-r--r--unit-tests/comment.mk4
-rw-r--r--unit-tests/cond-cmp-string.mk14
-rwxr-xr-xunit-tests/cond-cmp-unary.mk8
-rw-r--r--unit-tests/cond-eof.mk4
-rw-r--r--unit-tests/cond-func-defined.exp2
-rw-r--r--unit-tests/cond-func-defined.mk8
-rw-r--r--unit-tests/cond-func-empty.mk16
-rw-r--r--unit-tests/cond-func-exists.mk6
-rw-r--r--unit-tests/cond-func.mk4
-rw-r--r--unit-tests/cond-late.exp2
-rw-r--r--unit-tests/cond-late.mk6
-rw-r--r--unit-tests/cond-op-and.mk8
-rw-r--r--unit-tests/cond-op-or.mk8
-rw-r--r--unit-tests/cond-short.exp2
-rw-r--r--unit-tests/cond-short.mk23
-rw-r--r--unit-tests/cond-token-number.mk6
-rw-r--r--unit-tests/cond-token-plain.exp4
-rw-r--r--unit-tests/cond-token-plain.mk16
-rw-r--r--unit-tests/cond-token-string.exp2
-rw-r--r--unit-tests/cond-token-string.mk10
-rw-r--r--unit-tests/cond-token-var.mk16
-rwxr-xr-xunit-tests/dep-var.exp2
-rwxr-xr-xunit-tests/dep-var.mk10
-rw-r--r--unit-tests/depsrc-ignore.exp2
-rw-r--r--unit-tests/deptgt-delete_on_error.exp2
-rw-r--r--unit-tests/deptgt-makeflags.exp2
-rw-r--r--unit-tests/deptgt-makeflags.mk6
-rw-r--r--unit-tests/deptgt.mk6
-rw-r--r--unit-tests/dir.mk4
-rw-r--r--unit-tests/directive-else.mk4
-rw-r--r--unit-tests/directive-export-gmake.exp1
-rw-r--r--unit-tests/directive-export-gmake.mk25
-rw-r--r--unit-tests/directive-export.exp6
-rw-r--r--unit-tests/directive-export.mk10
-rw-r--r--unit-tests/directive-for-empty.mk12
-rw-r--r--unit-tests/directive-for-errors.mk14
-rw-r--r--unit-tests/directive-for-escape.exp4
-rw-r--r--unit-tests/directive-for-escape.mk12
-rw-r--r--unit-tests/directive-for-if.mk4
-rwxr-xr-xunit-tests/directive-for.exp19
-rwxr-xr-xunit-tests/directive-for.mk27
-rw-r--r--unit-tests/directive-ifmake.mk4
-rw-r--r--unit-tests/directive-ifndef.mk36
-rw-r--r--unit-tests/directive-include-guard.exp16
-rw-r--r--unit-tests/directive-include-guard.mk188
-rw-r--r--unit-tests/directive-warning.mk4
-rw-r--r--unit-tests/directive.mk4
-rw-r--r--unit-tests/escape.exp36
-rw-r--r--unit-tests/escape.mk7
-rw-r--r--unit-tests/hanoi-include.mk7
-rw-r--r--unit-tests/jobs-error-indirect.exp2
-rw-r--r--unit-tests/jobs-error-nested-make.exp2
-rw-r--r--unit-tests/jobs-error-nested.exp4
-rwxr-xr-xunit-tests/lint.mk4
-rw-r--r--unit-tests/moderrs.exp20
-rw-r--r--unit-tests/moderrs.mk6
-rw-r--r--unit-tests/opt-debug-file.mk4
-rw-r--r--unit-tests/opt-debug-jobs.mk4
-rw-r--r--unit-tests/opt-debug-lint.mk6
-rw-r--r--unit-tests/opt-debug-loud.mk6
-rw-r--r--unit-tests/opt-debug-var.mk4
-rw-r--r--unit-tests/parse-var.mk6
-rw-r--r--unit-tests/recursive.exp4
-rw-r--r--unit-tests/recursive.mk15
-rwxr-xr-xunit-tests/sh-dots.mk6
-rw-r--r--unit-tests/sh-leading-hyphen.exp2
-rw-r--r--unit-tests/shell-sh.mk4
-rw-r--r--unit-tests/unexport.mk4
-rw-r--r--unit-tests/var-eval-short.mk4
-rw-r--r--unit-tests/var-op-append.mk33
-rw-r--r--unit-tests/var-op-assign.mk4
-rw-r--r--unit-tests/var-op-default.mk8
-rw-r--r--unit-tests/var-op-expand.mk6
-rw-r--r--unit-tests/var-op-shell.mk20
-rw-r--r--unit-tests/var-readonly.exp3
-rw-r--r--unit-tests/var-readonly.mk4
-rw-r--r--unit-tests/var-recursive.exp8
-rw-r--r--unit-tests/var-recursive.mk4
-rw-r--r--unit-tests/var-scope-cmdline.mk4
-rw-r--r--unit-tests/var-scope-local-legacy.exp2
-rw-r--r--unit-tests/var-scope-local-legacy.mk22
-rw-r--r--unit-tests/var-scope-local.exp4
-rw-r--r--unit-tests/var-scope-local.mk12
-rw-r--r--unit-tests/vardebug.exp20
-rw-r--r--unit-tests/vardebug.mk29
-rw-r--r--unit-tests/varmisc.exp5
-rw-r--r--unit-tests/varmisc.mk28
-rw-r--r--unit-tests/varmod-assign.exp32
-rw-r--r--unit-tests/varmod-assign.mk71
-rw-r--r--unit-tests/varmod-defined.exp2
-rw-r--r--unit-tests/varmod-defined.mk10
-rw-r--r--unit-tests/varmod-edge.exp2
-rw-r--r--unit-tests/varmod-edge.mk8
-rw-r--r--unit-tests/varmod-gmtime.mk15
-rw-r--r--unit-tests/varmod-ifelse.exp20
-rw-r--r--unit-tests/varmod-ifelse.mk36
-rw-r--r--unit-tests/varmod-indirect.mk24
-rw-r--r--unit-tests/varmod-l-name-to-value.mk4
-rw-r--r--unit-tests/varmod-localtime.mk4
-rw-r--r--unit-tests/varmod-loop-varname.mk4
-rw-r--r--unit-tests/varmod-loop.mk6
-rwxr-xr-xunit-tests/varmod-match-escape.mk20
-rw-r--r--unit-tests/varmod-match.exp33
-rw-r--r--unit-tests/varmod-match.mk317
-rw-r--r--unit-tests/varmod-mtime.exp16
-rw-r--r--unit-tests/varmod-mtime.mk45
-rw-r--r--unit-tests/varmod-order.exp6
-rw-r--r--unit-tests/varmod-range.exp21
-rw-r--r--unit-tests/varmod-range.mk26
-rw-r--r--unit-tests/varmod-subst-regex.mk57
-rw-r--r--unit-tests/varmod-subst.mk46
-rw-r--r--unit-tests/varmod-sysv.mk10
-rw-r--r--unit-tests/varmod-to-separator.mk4
-rw-r--r--unit-tests/varmod-undefined.mk6
-rw-r--r--unit-tests/varmod.mk6
-rwxr-xr-xunit-tests/varname-dot-shell.exp10
-rw-r--r--unit-tests/varname-dot-suffixes.exp20
-rw-r--r--unit-tests/varname-dot-suffixes.mk19
-rw-r--r--unit-tests/varname-empty.exp24
-rwxr-xr-xunit-tests/varname-empty.mk4
-rw-r--r--unit-tests/varname-make_print_var_on_error-jobs.mk4
-rw-r--r--unit-tests/varname.mk4
-rw-r--r--unit-tests/varparse-dynamic.mk4
-rw-r--r--unit-tests/varparse-errors.exp28
-rw-r--r--unit-tests/varparse-errors.mk8
-rw-r--r--unit-tests/varparse-mod.mk6
-rw-r--r--unit-tests/varparse-undef-partial.mk13
-rw-r--r--util.c96
-rw-r--r--var.c696
181 files changed, 2577 insertions, 2169 deletions
diff --git a/ChangeLog b/ChangeLog
index ec0be0f4027c..4bd41562fb30 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,105 @@
+2024-01-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240108
+ Merge with NetBSD make, pick up
+ o miscellaneous cleanups
+
+2024-01-06 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240106
+ Merge with NetBSD make, pick up
+ o fix duplicate progname when reporting an unknown target
+ o unit tests for Cmd_Exec using temp file
+
+2024-01-05 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240105
+ Merge with NetBSD make, pick up
+ o main.c: Cmd_Exec write cmd to a file if too big
+ avoid blowing commandline/env limits
+
+2024-01-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20240101
+ o util.c: flesh out more of strftime
+ * configure.in: add --with-bmake-strftime
+ it is not a full implementation but enough to pass all
+ the unit-tests.
+ * parse.c: LoadFile do not append \n to empty buffer.
+
+2023-12-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231230
+ Merge with NetBSD make, pick up
+ o simplify memory allocation for string buffers
+ o fix declared types of list nodes
+ o suff.c: clean up freeing of suffixes
+ o var.c: simplify debug message for the ':@var@...@' modifier
+ clean up variable handling
+
+2023-12-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231226
+ Merge with NetBSD make, pick up
+ o compat.c: ensure make's output is correctly ordered with that of
+ the target when not going to a tty
+ o main.c: check for shellPath whether to call Shell_Init()
+
+2023-12-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231224
+ Merge with NetBSD make, pick up
+ o compat.c: check for shellPath whether to call Shell_Init()
+ tweak the unit test to detect the bug thus fixed.
+ o make.1: do not claim .SHELL is only used by jobs mode.
+
+2023-12-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231220
+ Merge with NetBSD make, pick up
+ o str.c: speed up pattern matching in the ':M' modifier
+ o var.c: fix confusing debug logging when deleting a variable
+ use consistent debug messages style when ignoring variables
+
+2023-12-10 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231210
+ Merge with NetBSD make, pick up
+ o var.c: avoid segfault on empty :C match expression
+ explain in debug log why variable assignment is ignored.
+
+2023-12-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231208
+ Merge with NetBSD make, pick up
+ o var.c: ensure fromCmd is set correctly for variables set on
+ command line.
+
+2023-11-26 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * configure.in: disable generation of 'makefile' for
+ Darwin by default.
+
+ * boot-strap: docuement --without-makefile
+
+2023-11-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20231124
+ Merge with NetBSD make, pick up
+ o main.c: cleanup processing of -j
+ fix lint warning about strchr
+ o var.c: more accurate error message for invalid ':mtime' argument
+ cleanup :[...] modifier
+ avoid reading beyond substring when comparing
+ o unit-tests cover all cases of :mtime, test and explain exporting
+ of variables
+ o cleanup comments
+
+2023-09-17 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * bsd.after-import.mk (ECHO_TAG): FreeBSD no longer uses
+ $FreeBSD$ tag, so avoid adding it.
+
2023-09-09 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20230909
diff --git a/README b/README
index a782f6dfc5b5..bf59107db2d3 100644
--- a/README
+++ b/README
@@ -6,12 +6,12 @@ Since 1993 I have run it on AIX, BSDi, Darwin, FreeBSD, HP-UX, IRIX,
Linux, Minix, OSF, Solaris, SunOS and even UTS.
Others have run it on many more systems.
-Currently each release is tested on NetBSD, FreeBSD, Solaris and Linux.
+Currently each release is tested on Darwin, NetBSD, FreeBSD and Linux.
Since 2003 bmake switched to a date based version (first was 20030714)
which generally represents the date it was last merged with NetBSD's
make. Since then, NetBSD's make is imported within a week of any
-interesting changes, so that bmake tracks it very closely.
+*interesting* changes, so that bmake tracks it very closely.
Building
========
@@ -33,20 +33,27 @@ the GNU standard process of::
./configure; make; make install
+This will *not* work on Darwin or any other system with a case
+insensitive filesystem. It depends on a generated ``makefile`` which
+is disabled by default on Darwin.
+
To make much use of bmake you will need the bsd.*.mk macros or my
portable *.mk macros which are included with bmake since 20121212
and separately available from
-http://www.crufty.net/ftp/pub/sjg/mk.tar.gz
-which will be links to the latest versions.
+https://www.crufty.net/ftp/pub/sjg/mk.tar.gz
+both that and
+https://www.crufty.net/ftp/pub/sjg/bmake.tar.gz
+will be links to the latest versions.
Porting
=======
If you encounter a system that bmake does not build or work on *out of
the box*, I welcome patches.
+Even a report of unit tests which fail is appreciated.
If you can provide access to a suitable machine - even better.
-More info can be found at http://www.crufty.net/help/sjg/bmake.htm
+More info can be found at https://www.crufty.net/help/sjg/bmake.htm
--sjg <sjg@crufty.net>
diff --git a/VERSION b/VERSION
index 0cc67de7b22d..ca636f3ab1e2 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
# keep this compatible with sh and make
-_MAKE_VERSION=20230909
+_MAKE_VERSION=20240108
diff --git a/arch.c b/arch.c
index 6d2c6e0f1875..b90452149c99 100644
--- a/arch.c
+++ b/arch.c
@@ -1,4 +1,4 @@
-/* $NetBSD: arch.c,v 1.213 2023/02/14 21:08:00 rillig Exp $ */
+/* $NetBSD: arch.c,v 1.214 2023/11/19 22:50:11 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.213 2023/02/14 21:08:00 rillig Exp $");
+MAKE_RCSID("$NetBSD: arch.c,v 1.214 2023/11/19 22:50:11 rillig Exp $");
typedef struct List ArchList;
typedef struct ListNode ArchListNode;
@@ -253,7 +253,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
FStr lib; /* Library-part of specification */
FStr mem; /* Member-part of specification */
char saveChar; /* Ending delimiter of member-name */
- bool expandLib; /* Whether the parsed lib contains variable
+ bool expandLib; /* Whether the parsed lib contains
* expressions that need to be expanded */
spec = *pp;
@@ -262,7 +262,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
for (cp = lib.str; *cp != '(' && *cp != '\0';) {
if (*cp == '$') {
- /* Expand nested variable expressions. */
+ /* Expand nested expressions. */
/* XXX: This code can probably be shortened. */
const char *nested_p = cp;
FStr result;
@@ -299,7 +299,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
mem = FStr_InitRefer(cp);
while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
if (*cp == '$') {
- /* Expand nested variable expressions. */
+ /* Expand nested expressions. */
/*
* XXX: This code can probably be shortened.
*/
diff --git a/bmake.1 b/bmake.1
index a658eab6700e..55d910b3b204 100644
--- a/bmake.1
+++ b/bmake.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.371 2023/09/10 21:52:36 rillig Exp $
+.\" $NetBSD: make.1,v 1.372 2023/12/24 16:48:30 sjg Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd September 9, 2023
+.Dd December 24, 2023
.Dt BMAKE 1
.Os
.Sh NAME
@@ -2523,7 +2523,7 @@ set the read-only attribute on the global variables specified as sources.
.It Ic .SHELL
Sets the shell that
.Nm
-uses to execute commands in jobs mode.
+uses to execute commands.
The sources are a set of
.Ar field\| Ns Cm \&= Ns Ar value
pairs.
diff --git a/bmake.cat1 b/bmake.cat1
index bdd48dda11db..61138ea0f2b6 100644
--- a/bmake.cat1
+++ b/bmake.cat1
@@ -1599,8 +1599,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
set the read-only attribute on the global variables specified as
sources.
- .SHELL Sets the shell that bmake uses to execute commands in jobs mode.
- The sources are a set of field=value pairs.
+ .SHELL Sets the shell that bmake uses to execute commands. The sources
+ are a set of field=value pairs.
name This is the minimal specification, used to select
one of the built-in shell specs; sh, ksh, and csh.
@@ -1759,4 +1759,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
attempt to suppress a cascade of unnecessary errors, can result in a
seemingly unexplained `*** Error code 6'
-FreeBSD 13.0 September 9, 2023 FreeBSD 13.0
+FreeBSD 13.0 December 24, 2023 FreeBSD 13.0
diff --git a/boot-strap b/boot-strap
index f47f0c4fdecd..00fa04a0e08b 100755
--- a/boot-strap
+++ b/boot-strap
@@ -82,9 +82,23 @@
#
# Possibly useful configure_args:
#
+# --without-makefile
+# do not generate 'makefile'.
+#
+# 'makefile' is used to enable the classic
+# './configure; make; make install' dance, but on
+# systems with case insensitive filesystems it can lead
+# to infinite recursion.
+#
+# It is disabled by default on Darwin, and Cygwin.
+#
# --without-meta
# disable use of meta mode.
#
+# Even without filemon(9) meta mode is very useful
+# both for debugging build and improving reliability of
+# update builds.
+#
# --without-filemon
# disable use of filemon(9) which is currently only
# available for NetBSD and FreeBSD.
@@ -119,7 +133,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: boot-strap,v 1.58 2023/06/27 21:02:19 sjg Exp $
+# $Id: boot-strap,v 1.59 2023/11/26 18:19:43 sjg Exp $
#
# @(#) Copyright (c) 2001 Simon J. Gerraty
#
diff --git a/bsd.after-import.mk b/bsd.after-import.mk
index fec42c9deeed..418caeaa58a6 100644
--- a/bsd.after-import.mk
+++ b/bsd.after-import.mk
@@ -1,4 +1,4 @@
-# $Id: bsd.after-import.mk,v 1.17 2021/10/22 06:31:32 sjg Exp $
+# $Id: bsd.after-import.mk,v 1.18 2023/09/18 05:29:23 sjg Exp $
# This makefile is for use when integrating bmake into a BSD build
# system. Use this makefile after importing bmake.
@@ -68,18 +68,25 @@ MAKEFILE_SED = sed -e '/^MACHINE/d' \
# These are the simple files we want to capture
configured_files= config.h Makefile.config unit-tests/Makefile.config
+# FreeBSD has dropped their tag with svn
+.if ${HOST_OS:NFreeBSD} == ""
+ECHO_TAG= :
+.else
+ECHO_TAG?= echo
+.endif
+
after-import: bootstrap ${MAKEFILE}
.for f in ${configured_files:M*.[ch]}
@echo Capturing $f
@mkdir -p ${${.CURDIR}/$f:L:H}
- @(echo '/* $$${HOST_OS}$$ */'; cat ${HOST_OS}/$f) > ${.CURDIR}/$f
+ @(${ECHO_TAG} '/* $$${HOST_OS}$$ */'; cat ${HOST_OS}/$f) > ${.CURDIR}/$f
.endfor
.for f in ${configured_files:M*Makefile*}
@echo Capturing $f
@mkdir -p ${${.CURDIR}/$f:L:H}
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
- echo '#'; echo '# $$${HOST_OS}$$'; echo; \
+ echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; echo; \
echo 'SRCTOP?= $${.CURDIR:${${.CURDIR}/$f:L:H:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; echo; \
${MAKEFILE_SED} ${HOST_OS}/$f ) > ${.CURDIR}/$f
.endfor
@@ -89,7 +96,7 @@ _makefile: bootstrap ${MAKEFILE}
@echo Generating ${.CURDIR}/Makefile
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
- echo '#'; echo '# $$${HOST_OS}$$'; \
+ echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; \
echo; echo 'SRCTOP?= $${.CURDIR:${.CURDIR:S,${SRCTOP}/,,:C,[^/]+,H,g:S,/,:,g}}'; \
echo; echo '# look here first for config.h'; \
echo 'CFLAGS+= -I$${.CURDIR}'; echo; \
@@ -115,7 +122,7 @@ _utmakefile: bootstrap ${MAKEFILE}
@mkdir -p ${.CURDIR}/unit-tests
@(echo '# This is a generated file, do NOT edit!'; \
echo '# See ${_this:S,${SRCTOP}/,,}'; \
- echo '#'; echo '# $$${HOST_OS}$$'; \
+ echo '#'; ${ECHO_TAG} '# $$${HOST_OS}$$'; \
${MAKEFILE_SED} \
-e '/^UNIT_TESTS/s,=.*,= $${srcdir},' \
${BMAKE_SRC}/unit-tests/Makefile ) > ${.TARGET}
diff --git a/buf.c b/buf.c
index 845a68c46330..fdc6c8ec2b60 100644
--- a/buf.c
+++ b/buf.c
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.c,v 1.56 2023/06/01 07:44:10 rillig Exp $ */
+/* $NetBSD: buf.c,v 1.57 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,13 +69,13 @@
* SUCH DAMAGE.
*/
-/* Automatically-expanding null-terminated character buffers. */
+/* Automatically growing null-terminated buffers of characters. */
#include <limits.h>
#include "make.h"
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: buf.c,v 1.56 2023/06/01 07:44:10 rillig Exp $");
+MAKE_RCSID("$NetBSD: buf.c,v 1.57 2023/12/19 19:33:39 rillig Exp $");
/* Make space in the buffer for adding at least 16 more bytes. */
void
diff --git a/buf.h b/buf.h
index ccd7d513b211..c5e7d539de9e 100644
--- a/buf.h
+++ b/buf.h
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.h,v 1.48 2023/06/01 07:44:10 rillig Exp $ */
+/* $NetBSD: buf.h,v 1.49 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -96,7 +96,7 @@ Buf_Clear(Buffer *buf)
buf->data[0] = '\0';
}
-/* Buf_AddByte adds a single byte to a buffer. */
+/* Adds a single byte to a buffer. */
MAKE_INLINE void
Buf_AddByte(Buffer *buf, char byte)
{
diff --git a/compat.c b/compat.c
index 221eb64959e6..6f55880cbc54 100644
--- a/compat.c
+++ b/compat.c
@@ -1,4 +1,4 @@
-/* $NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg Exp $ */
+/* $NetBSD: compat.c,v 1.252 2024/01/05 23:22:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -94,7 +94,7 @@
#include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: compat.c,v 1.247 2023/05/04 22:31:17 sjg Exp $");
+MAKE_RCSID("$NetBSD: compat.c,v 1.252 2024/01/05 23:22:06 rillig Exp $");
static GNode *curTarg = NULL;
static pid_t compatChild;
@@ -110,10 +110,8 @@ CompatDeleteTarget(GNode *gn)
if (gn != NULL && !GNode_IsPrecious(gn) &&
(gn->type & OP_PHONY) == 0) {
const char *file = GNode_VarTarget(gn);
-
- if (!opts.noExecute && unlink_file(file) == 0) {
+ if (!opts.noExecute && unlink_file(file) == 0)
Error("*** %s removed", file);
- }
}
}
@@ -132,14 +130,11 @@ CompatInterrupt(int signo)
CompatDeleteTarget(curTarg);
if (curTarg != NULL && !GNode_IsPrecious(curTarg)) {
- /*
- * Run .INTERRUPT only if hit with interrupt signal
- */
+ /* Run .INTERRUPT only if hit with interrupt signal. */
if (signo == SIGINT) {
GNode *gn = Targ_FindNode(".INTERRUPT");
- if (gn != NULL) {
+ if (gn != NULL)
Compat_Make(gn, gn);
- }
}
}
@@ -280,11 +275,9 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
silent = !DEBUG(LOUD);
else if (*cmd == '-')
errCheck = false;
- else if (*cmd == '+') {
+ else if (*cmd == '+')
doIt = true;
- if (shellName == NULL) /* we came here from jobs */
- Shell_Init();
- } else if (!ch_isspace(*cmd))
+ else if (!ch_isspace(*cmd))
/* Ignore whitespace for compatibility with gnu make */
break;
cmd++;
@@ -292,37 +285,25 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
while (ch_isspace(*cmd))
cmd++;
-
- /*
- * If we did not end up with a command, just skip it.
- */
if (cmd[0] == '\0')
return true;
useShell = UseShell(cmd);
- /*
- * Print the command before echoing if we're not supposed to be quiet
- * for this one. We also print the command if -n given.
- */
+
if (!silent || !GNode_ShouldExecute(gn)) {
printf("%s\n", cmd);
fflush(stdout);
}
- /*
- * If we're not supposed to execute any commands, this is as far as
- * we go...
- */
if (!doIt && !GNode_ShouldExecute(gn))
return true;
DEBUG1(JOB, "Execute: '%s'\n", cmd);
+ if (useShell && shellPath == NULL)
+ Shell_Init(); /* we need shellPath */
+
if (useShell) {
- /*
- * We need to pass the command off to the shell, typically
- * because the command contains a "meta" character.
- */
static const char *shargv[5];
/* The following work for any of the builtin shell specs. */
@@ -337,11 +318,6 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
bp = NULL;
mav = NULL;
} else {
- /*
- * No meta-characters, so no need to exec a shell. Break the
- * command into words to form an argument vector we can
- * execute.
- */
Words words = Str_Words(cmd, false);
mav = words.words;
bp = words.freeIt;
@@ -380,24 +356,21 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
meta_compat_parent(cpid);
#endif
- /*
- * The child is off and running. Now all we can do is wait...
- */
+ /* The child is off and running. Now all we can do is wait... */
while ((retstat = wait(&reason)) != cpid) {
if (retstat > 0)
JobReapChild(retstat, reason, false); /* not ours? */
- if (retstat == -1 && errno != EINTR) {
+ if (retstat == -1 && errno != EINTR)
break;
- }
}
if (retstat < 0)
Fatal("error in wait: %d: %s", retstat, strerror(errno));
if (WIFSTOPPED(reason)) {
- status = WSTOPSIG(reason); /* stopped */
+ status = WSTOPSIG(reason);
} else if (WIFEXITED(reason)) {
- status = WEXITSTATUS(reason); /* exited */
+ status = WEXITSTATUS(reason);
#if defined(USE_META) && defined(USE_FILEMON_ONCE)
if (useMeta)
meta_cmd_finish(NULL);
@@ -408,7 +381,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
printf("*** Error code %d", status);
}
} else {
- status = WTERMSIG(reason); /* signaled */
+ status = WTERMSIG(reason);
printf("*** Signal %d", status);
}
@@ -439,6 +412,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
printf(" (ignored)\n");
status = 0;
}
+ fflush(stdout);
}
free(cmdStart);
@@ -594,10 +568,6 @@ MakeUnmade(GNode *gn, GNode *pgn)
gn->type |= OP_SILENT;
if (Job_CheckCommands(gn, Fatal)) {
- /*
- * Our commands are ok, but we still have to worry about
- * the -t flag.
- */
if (!opts.touch || (gn->type & OP_MAKE)) {
curTarg = gn;
#ifdef USE_META
@@ -781,7 +751,6 @@ Compat_MakeAll(GNodeList *targs)
errorNode = gn;
}
- /* If the user has defined a .END target, run its commands. */
if (errorNode == NULL) {
GNode *endNode = Targ_GetEndNode();
Compat_Make(endNode, endNode);
diff --git a/cond.c b/cond.c
index 4ed15ff26184..843abc9a92f8 100644
--- a/cond.c
+++ b/cond.c
@@ -1,4 +1,4 @@
-/* $NetBSD: cond.c,v 1.354 2023/08/11 04:56:31 rillig Exp $ */
+/* $NetBSD: cond.c,v 1.359 2023/12/29 12:59:43 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -81,8 +81,7 @@
* of one of the .if directives or the condition in a
* ':?then:else' variable modifier.
*
- * Cond_EndFile
- * At the end of reading a makefile, ensure that the
+ * Cond_EndFile At the end of reading a makefile, ensure that the
* conditional directives are well-balanced.
*/
@@ -92,7 +91,7 @@
#include "dir.h"
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: cond.c,v 1.354 2023/08/11 04:56:31 rillig Exp $");
+MAKE_RCSID("$NetBSD: cond.c,v 1.359 2023/12/29 12:59:43 rillig Exp $");
/*
* Conditional expressions conform to this grammar:
@@ -154,10 +153,10 @@ typedef struct CondParser {
* expanded before it is evaluated, due to ease of implementation.
* This means that at the point where the condition is evaluated,
* make cannot know anymore whether the left-hand side had originally
- * been a variable expression or a plain word.
+ * been an expression or a plain word.
*
* In conditional directives like '.if', the left-hand side must
- * either be a variable expression, a quoted string or a number.
+ * either be an expression, a quoted string or a number.
*/
bool leftUnquotedOK;
@@ -166,9 +165,7 @@ typedef struct CondParser {
/*
* Whether an error message has already been printed for this
- * condition. The first available error message is usually the most
- * specific one, therefore it makes sense to suppress the standard
- * "Malformed conditional" message.
+ * condition.
*/
bool printedError;
} CondParser;
@@ -212,16 +209,16 @@ ParseWord(const char **pp, bool doEval)
{
const char *p = *pp;
Buffer word;
- int paren_depth;
+ int depth;
- Buf_InitSize(&word, 16);
+ Buf_Init(&word);
- paren_depth = 0;
+ depth = 0;
for (;;) {
char ch = *p;
if (ch == '\0' || ch == ' ' || ch == '\t')
break;
- if ((ch == '&' || ch == '|') && paren_depth == 0)
+ if ((ch == '&' || ch == '|') && depth == 0)
break;
if (ch == '$') {
VarEvalMode emode = doEval
@@ -238,8 +235,8 @@ ParseWord(const char **pp, bool doEval)
continue;
}
if (ch == '(')
- paren_depth++;
- else if (ch == ')' && --paren_depth < 0)
+ depth++;
+ else if (ch == ')' && --depth < 0)
break;
Buf_AddByte(&word, ch);
p++;
@@ -258,7 +255,7 @@ ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func)
const char *p = *pp;
char *res;
- p++; /* Skip opening '(' - verified by caller */
+ p++; /* skip the '(' */
cpp_skip_hspace(&p);
res = ParseWord(&p, doEval);
cpp_skip_hspace(&p);
@@ -383,7 +380,7 @@ is_separator(char ch)
}
/*
- * In a quoted or unquoted string literal or a number, parse a variable
+ * In a quoted or unquoted string literal or a number, parse an
* expression and add its value to the buffer.
*
* Return whether to continue parsing the leaf.
@@ -429,7 +426,7 @@ CondParser_StringExpr(CondParser *par, const char *start,
}
/*
- * Parse a string from a variable expression or an optionally quoted string,
+ * Parse a string from an expression or an optionally quoted string,
* on the left-hand and right-hand sides of comparisons.
*
* Results:
@@ -487,10 +484,6 @@ CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
default:
if (!unquotedOK && !quoted && *start != '$' &&
!ch_isdigit(*start)) {
- /*
- * The left-hand side must be quoted,
- * a variable expression or a number.
- */
str = FStr_InitRefer(NULL);
goto return_str;
}
@@ -635,7 +628,6 @@ CondParser_Comparison(CondParser *par, bool doEval)
CondParser_SkipWhitespace(par);
if (!CondParser_ComparisonOp(par, &op)) {
- /* Unknown operator, compare against an empty string or 0. */
t = ToToken(doEval && EvalTruthy(par, lhs.str, lhsQuoted));
goto done_lhs;
}
@@ -667,19 +659,19 @@ done_lhs:
static bool
CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
{
- const char *cp = par->p;
+ const char *p = par->p;
Token tok;
FStr val;
- if (!skip_string(&cp, "empty"))
+ if (!skip_string(&p, "empty"))
return false;
- cpp_skip_whitespace(&cp);
- if (*cp != '(')
+ cpp_skip_whitespace(&p);
+ if (*p != '(')
return false;
- cp--; /* Make cp[1] point to the '('. */
- val = Var_Parse(&cp, SCOPE_CMDLINE,
+ p--; /* Make p[1] point to the '('. */
+ val = Var_Parse(&p, SCOPE_CMDLINE,
doEval ? VARE_WANTRES : VARE_PARSE_ONLY);
/* TODO: handle errors */
@@ -692,7 +684,7 @@ CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
FStr_Done(&val);
*out_token = tok;
- par->p = cp;
+ par->p = p;
return true;
}
@@ -734,7 +726,7 @@ CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
/*
* Parse a comparison that neither starts with '"' nor '$', such as the
* unusual 'bare == right' or '3 == ${VAR}', or a simple leaf without
- * operator, which is a number, a variable expression or a string literal.
+ * operator, which is a number, an expression or a string literal.
*
* TODO: Can this be merged into CondParser_Comparison?
*/
@@ -743,37 +735,36 @@ CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
{
Token t;
char *arg;
- const char *cp;
+ const char *p;
- /* Push anything numeric through the compare expression */
- cp = par->p;
- if (ch_isdigit(cp[0]) || cp[0] == '-' || cp[0] == '+')
+ p = par->p;
+ if (ch_isdigit(p[0]) || p[0] == '-' || p[0] == '+')
return CondParser_Comparison(par, doEval);
/*
- * Most likely we have a naked token to apply the default function to.
- * However ".if a == b" gets here when the "a" is unquoted and doesn't
- * start with a '$'. This surprises people.
+ * Most likely we have a bare word to apply the default function to.
+ * However, ".if a == b" gets here when the "a" is unquoted and
+ * doesn't start with a '$'. This surprises people.
* If what follows the function argument is a '=' or '!' then the
* syntax would be invalid if we did "defined(a)" - so instead treat
* as an expression.
*/
/*
- * XXX: In edge cases, a variable expression may be evaluated twice,
+ * XXX: In edge cases, an expression may be evaluated twice,
* see cond-token-plain.mk, keyword 'twice'.
*/
- arg = ParseWord(&cp, doEval);
+ arg = ParseWord(&p, doEval);
assert(arg[0] != '\0');
- if (*cp == '=' || *cp == '!' || *cp == '<' || *cp == '>')
+ if (*p == '=' || *p == '!' || *p == '<' || *p == '>')
return CondParser_Comparison(par, doEval);
- par->p = cp;
+ par->p = p;
/*
* Evaluate the argument using the default function.
* This path always treats .if as .ifdef. To get here, the character
* after .if must have been taken literally, so the argument cannot
- * be empty - even if it contained a variable expansion.
+ * be empty - even if it contained an expression.
*/
t = ToToken(doEval && par->evalBare(arg) != par->negateEvalBare);
free(arg);
@@ -961,7 +952,7 @@ CondParser_Eval(CondParser *par)
}
/*
- * Evaluate the condition, including any side effects from the variable
+ * Evaluate the condition, including any side effects from the
* expressions in the condition. The condition consists of &&, ||, !,
* function(arg), comparisons and parenthetical groupings thereof.
*/
@@ -1034,12 +1025,6 @@ DetermineKindOfConditional(const char **pp, bool *out_plain,
return true;
unknown_directive:
- /*
- * 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;
}
@@ -1068,7 +1053,7 @@ unknown_directive:
* conditional (when <cond> evaluates to true)
* CR_FALSE to skip the lines after the conditional
* (when <cond> evaluates to false, or when a previous
- * branch has already been taken)
+ * branch was already taken)
* CR_ERROR if the conditional was not valid, either because of
* a syntax error or because some variable was undefined
* or because the condition could not be evaluated
@@ -1115,7 +1100,7 @@ Cond_EvalLine(const char *line)
p++; /* skip the leading '.' */
cpp_skip_hspace(&p);
- if (IsEndif(p)) { /* It is an '.endif'. */
+ if (IsEndif(p)) {
if (p[5] != '\0') {
Parse_Error(PARSE_FATAL,
"The .endif directive does not take arguments");
@@ -1135,14 +1120,8 @@ Cond_EvalLine(const char *line)
/* Parse the name of the directive, such as 'if', 'elif', 'endif'. */
if (p[0] == 'e') {
- if (p[1] != 'l') {
- /*
- * Unknown directive. It might still be a
- * transformation rule like '.err.txt',
- * therefore no error message here.
- */
+ if (p[1] != 'l')
return CR_ERROR;
- }
/* Quite likely this is 'else' or 'elif' */
p += 2;
@@ -1176,13 +1155,8 @@ Cond_EvalLine(const char *line)
} else
isElif = false;
- if (p[0] != 'i' || p[1] != 'f') {
- /*
- * Unknown directive. It might still be a transformation rule
- * like '.elisp.scm', therefore no error message here.
- */
- return CR_ERROR; /* Not an ifxxx or elifxxx line */
- }
+ if (p[0] != 'i' || p[1] != 'f')
+ return CR_ERROR;
if (!DetermineKindOfConditional(&p, &plain, &evalBare, &negate))
return CR_ERROR;
@@ -1219,16 +1193,11 @@ Cond_EvalLine(const char *line)
state = cond_states[cond_depth];
cond_depth++;
if (!(state & IFS_ACTIVE)) {
- /*
- * If we aren't parsing the data,
- * treat as always false.
- */
cond_states[cond_depth] = IFS_WAS_ACTIVE;
return CR_FALSE;
}
}
- /* And evaluate the conditional expression */
res = CondEvalExpression(p, plain, evalBare, negate, true, false);
if (res == CR_ERROR) {
/* Syntax error, error message already output. */
diff --git a/configure b/configure
index 9d6130f66c84..2898a27bb98e 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.71 for bmake 20230723.
+# Generated by GNU Autoconf 2.71 for bmake 20240101.
#
# Report bugs to <sjg@NetBSD.org>.
#
@@ -610,8 +610,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='bmake'
PACKAGE_TARNAME='bmake'
-PACKAGE_VERSION='20230723'
-PACKAGE_STRING='bmake 20230723'
+PACKAGE_VERSION='20240101'
+PACKAGE_STRING='bmake 20240101'
PACKAGE_BUGREPORT='sjg@NetBSD.org'
PACKAGE_URL=''
@@ -724,6 +724,7 @@ with_defshell
with_makefile
with_meta
with_filemon
+with_bmake_strftime
with_machine
with_force_machine
with_force_machine_arch
@@ -1290,7 +1291,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 20230723 to adapt to many kinds of systems.
+\`configure' configures bmake 20240101 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1352,7 +1353,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of bmake 20230723:";;
+ short | recursive ) echo "Configuration of bmake 20240101:";;
esac
cat <<\_ACEOF
@@ -1374,6 +1375,7 @@ Optional Packages:
--without-makefile disable use of generated makefile
--without-meta disable use of meta-mode
--with-filemon={no,dev,ktrace,path/filemon.h} indicate filemon method for meta-mode. Path to filemon.h implies dev
+ --with-bmake-strftime force use of bmake strftime
--with-machine=MACHINE explicitly set MACHINE
--with-force-machine=MACHINE set FORCE_MACHINE
--with-force-machine-arch=MACHINE set FORCE_MACHINE_ARCH
@@ -1461,7 +1463,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-bmake configure 20230723
+bmake configure 20240101
generated by GNU Autoconf 2.71
Copyright (C) 2021 Free Software Foundation, Inc.
@@ -1968,7 +1970,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 20230723, which was
+It was created by bmake $as_me 20240101, which was
generated by GNU Autoconf 2.71. Invocation command line was
$ $0$ac_configure_args_raw
@@ -2783,7 +2785,7 @@ esac
fi
case "$OS" in
-CYGWIN*|MINGW*) use_makefile=no;;
+CYGWIN*|Darwin|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
@@ -2846,6 +2848,15 @@ esac
fi
+
+# Check whether --with-bmake_strftime was given.
+if test ${with_bmake_strftime+y}
+then :
+ withval=$with_bmake_strftime; case "${withval}" in
+yes|no) bmake_strftime=$withval;;
+esac
+fi
+
case "$use_meta" in
yes)
case "$use_filemon" in
@@ -4897,6 +4908,9 @@ if test $bmake_path_max -gt 1024; then
# this is all we expect
bmake_path_max=1024
fi
+if test ${bmake_strftime:-no} = yes; then
+ CPPFLAGS="${CPPFLAGS} -DFORCE_BMAKE_STRFTIME"
+fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
# if type does not work which(1) had better!
# note we cannot rely on type returning non-zero on failure
@@ -7574,7 +7588,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 20230723, which was
+This file was extended by bmake $as_me 20240101, which was
generated by GNU Autoconf 2.71. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -7638,7 +7652,7 @@ ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config='$ac_cs_config_escaped'
ac_cs_version="\\
-bmake config.status 20230723
+bmake config.status 20240101
configured by $0, generated by GNU Autoconf 2.71,
with options \\"\$ac_cs_config\\"
diff --git a/configure.in b/configure.in
index 5b97bc1bed52..ad652e53024b 100644
--- a/configure.in
+++ b/configure.in
@@ -1,11 +1,11 @@
dnl
dnl RCSid:
-dnl $Id: configure.in,v 1.97 2023/07/25 20:12:32 sjg Exp $
+dnl $Id: configure.in,v 1.101 2024/01/04 23:04:07 sjg Exp $
dnl
dnl Process this file with autoconf to produce a configure script
dnl
AC_PREREQ([2.71])
-AC_INIT([bmake],[20230723],[sjg@NetBSD.org])
+AC_INIT([bmake],[20240101],[sjg@NetBSD.org])
AC_CONFIG_HEADERS(config.h)
dnl make srcdir absolute
@@ -62,7 +62,7 @@ no) ;;
esac])
dnl
case "$OS" in
-CYGWIN*|MINGW*) use_makefile=no;;
+CYGWIN*|Darwin|MINGW*) use_makefile=no;;
*) use_makefile=yes;;
esac
AC_ARG_WITH(makefile,
@@ -114,6 +114,13 @@ dev) ;;
*) filemon_h=no;;
esac
])
+dnl some systems have broken/incomplete strftime
+AC_ARG_WITH(bmake_strftime,
+[ --with-bmake-strftime force use of bmake strftime],
+[case "${withval}" in
+yes|no) bmake_strftime=$withval;;
+esac])
+dnl
dnl echo "Note: use_meta=$use_meta use_filemon=$use_filemon filemon_h=$filemon_h" >&6
case "$use_meta" in
yes)
@@ -189,6 +196,9 @@ if test $bmake_path_max -gt 1024; then
# this is all we expect
bmake_path_max=1024
fi
+if test ${bmake_strftime:-no} = yes; then
+ CPPFLAGS="${CPPFLAGS} -DFORCE_BMAKE_STRFTIME"
+fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
AC_SUBST(bmake_path_max)dnl
dnl
diff --git a/dir.c b/dir.c
index 1da9e7d8fc13..5c11d6c794d7 100644
--- a/dir.c
+++ b/dir.c
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $ */
+/* $NetBSD: dir.c,v 1.286 2023/12/29 18:53:24 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -83,7 +83,7 @@
*
* Dir_End Clean up the module.
*
- * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath.
+ * Dir_SetPATH Set ${.PATH} to reflect the state of dirSearchPath.
*
* Dir_HasWildcards
* Returns true if the name given it needs to
@@ -94,13 +94,12 @@
* from the search path.
*
* Dir_FindFile Searches for a file on a given search path.
- * If it exists, the entire path is returned.
- * Otherwise NULL is returned.
+ * If it exists, returns the entire path, otherwise NULL.
*
* Dir_FindHereOrAbove
- * Search for a path in the current directory and
- * then all the directories above it in turn until
- * the path is found or we reach the root ("/").
+ * Search for a path in the current directory and then
+ * all the directories above it in turn, until the path
+ * is found or the root directory ("/") is reached.
*
* Dir_UpdateMTime
* Update the modification time and path of a node with
@@ -114,11 +113,6 @@
* preceded by the command flag and all of them
* separated by a space.
*
- * Dir_Destroy Destroy an element of a search path. Frees up all
- * things that can be freed for the element as long
- * as the element is no longer referenced by any other
- * search path.
- *
* SearchPath_Clear
* Resets a search path to the empty list.
*
@@ -138,7 +132,7 @@
#include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
+MAKE_RCSID("$NetBSD: dir.c,v 1.286 2023/12/29 18:53:24 rillig Exp $");
/*
* A search path is a list of CachedDir structures. A CachedDir has in it the
@@ -152,26 +146,21 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
* All previously-read directories are kept in openDirs, which is checked
* first before a directory is opened.
*
- * The need for the caching of whole directories is brought about by the
- * multi-level transformation code in suff.c, which tends to search for far
- * more files than regular make does. In the initial implementation, the
- * amount of time spent performing "stat" calls was truly astronomical.
- * The problem with caching at the start is, of course, that pmake doesn't
- * then detect changes to these directories during the course of the make.
- * Three possibilities suggest themselves:
+ * This cache is used by the multi-level transformation code in suff.c, which
+ * tends to search for far more files than in regular explicit targets. After
+ * a directory has been cached, any later changes to that directory are not
+ * reflected in the cache. To keep the cache up to date, there are several
+ * ideas:
*
* 1) just use stat to test for a file's existence. As mentioned above,
- * this is very inefficient due to the number of checks engendered by
+ * this is very inefficient due to the number of checks performed by
* the multi-level transformation code.
*
- * 2) use readdir() and company to search the directories, keeping them
- * open between checks. I have tried this and while it didn't slow down
- * the process too much, it could severely affect the amount of
- * parallelism available as each directory open would take another file
- * descriptor out of play for handling I/O for another job. Given that
- * it is only recently (as of 1993 or earlier) that UNIX OS's have taken
- * to allowing more than 20 or 32 file descriptors for a process, this
- * doesn't seem acceptable to me.
+ * 2) use readdir() to search the directories, keeping them open between
+ * checks. Around 1993 or earlier, this didn't slow down the process too
+ * much, but it consumed one file descriptor per open directory, which
+ * was critical on the then-current operating systems, as many limited
+ * the number of open file descriptors to 20 or 32.
*
* 3) record the mtime of the directory in the CachedDir structure and
* verify the directory hasn't changed since the contents were cached.
@@ -184,9 +173,9 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
* number of reloadings and if the number goes over a (small) limit,
* resort to using stat in its place.
*
- * An additional thing to consider is that pmake is used primarily to create
- * C programs and until recently (as of 1993 or earlier) pcc-based compilers
- * refused to allow you to specify where the resulting object file should be
+ * An additional thing to consider is that make is used primarily to create
+ * C programs and until recently (as of 1993 or earlier), pcc-based compilers
+ * didn't have an option to specify where the resulting object file should be
* placed. This forced all objects to be created in the current directory.
* This isn't meant as a full excuse, just an explanation of some of the
* reasons for the caching used here.
@@ -212,7 +201,7 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $");
/* A cache for the filenames in a directory. */
struct CachedDir {
/*
- * Name of directory, either absolute or relative to the current
+ * Name of the directory, either absolute or relative to the current
* directory. The name is not normalized in any way, that is, "."
* and "./." are different.
*
@@ -238,8 +227,6 @@ struct CachedDir {
typedef List CachedDirList;
typedef ListNode CachedDirListNode;
-typedef ListNode SearchPathNode;
-
/* A list of cached directories, with fast lookup by directory name. */
typedef struct OpenDirs {
CachedDirList list;
@@ -273,8 +260,7 @@ static CachedDir *dotLast = NULL;
*
* XXX: If this is done way early, there's a chance other rules will have
* already updated the file, in which case we'll update it again. Generally,
- * there won't be two rules to update a single file, so this should be ok,
- * but...
+ * there won't be two rules to update a single file, so this should be ok.
*/
static HashTable mtimes;
@@ -338,7 +324,7 @@ CachedDir_Unref(CachedDir *dir)
free(dir);
}
-/* Update the value of the CachedDir variable, updating the reference counts. */
+/* Update the value of 'var', updating the reference counts. */
static void
CachedDir_Assign(CachedDir **var, CachedDir *dir)
{
@@ -475,9 +461,7 @@ Dir_Init(void)
CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST"));
}
-/*
- * Called by Dir_InitDir and whenever .CURDIR is assigned to.
- */
+/* Called by Dir_InitDir and whenever .CURDIR is assigned to. */
void
Dir_InitCur(const char *newCurdir)
{
@@ -487,7 +471,7 @@ Dir_InitCur(const char *newCurdir)
return;
/*
- * Our build directory is not the same as our source directory.
+ * The build directory is not the same as the source directory.
* Keep this one around too.
*/
dir = SearchPath_Add(NULL, newCurdir);
@@ -498,7 +482,7 @@ Dir_InitCur(const char *newCurdir)
}
/*
- * (Re)initialize "dot" (current/object directory) path hash.
+ * (Re)initialize "dot" (the current/object directory).
* Some directories may be cached.
*/
void
@@ -599,8 +583,6 @@ Dir_SetSYSPATH(void)
* XXX: This code is not 100% correct ([^]] fails etc.). I really don't think
* that make(1) should be expanding patterns, because then you have to set a
* mechanism for escaping the expansion!
- *
- * Return true if the word should be expanded, false otherwise.
*/
bool
Dir_HasWildcards(const char *name)
@@ -637,20 +619,14 @@ Dir_HasWildcards(const char *name)
}
/*
- * See if any files match the pattern and add their names to the 'expansions'
- * list if they do.
+ * See if any files as seen from 'dir' match 'pattern', and add their names
+ * to 'expansions' if they do.
*
- * 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,
+ * Wildcards are only expanded in the final path component, but not in
+ * directories like src/lib*c/file*.c. To expand these wildcards,
* 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
- * dir Directory to search
- * expansion Place to store the results
*/
static void
DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
@@ -696,21 +672,18 @@ DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions)
}
}
-/*
- * Find the next closing brace in the string, taking nested braces into
- * account.
- */
+/* Find the next closing brace in 'p', taking nested braces into account. */
static const char *
closing_brace(const char *p)
{
- int nest = 0;
+ int depth = 0;
while (*p != '\0') {
- if (*p == '}' && nest == 0)
+ if (*p == '}' && depth == 0)
break;
if (*p == '{')
- nest++;
+ depth++;
if (*p == '}')
- nest--;
+ depth--;
p++;
}
return p;
@@ -723,14 +696,14 @@ closing_brace(const char *p)
static const char *
separator_comma(const char *p)
{
- int nest = 0;
+ int depth = 0;
while (*p != '\0') {
- if ((*p == '}' || *p == ',') && nest == 0)
+ if ((*p == '}' || *p == ',') && depth == 0)
break;
if (*p == '{')
- nest++;
+ depth++;
if (*p == '}')
- nest--;
+ depth--;
p++;
}
return p;
@@ -787,7 +760,7 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
const char *prefix, *middle, *piece, *middle_end, *suffix;
size_t prefix_len, suffix_len;
- /* Split the word into prefix '{' middle '}' suffix. */
+ /* Split the word into prefix, '{', middle, '}' and suffix. */
middle = brace + 1;
middle_end = closing_brace(middle);
@@ -824,11 +797,11 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path,
}
-/* Expand the pattern in each of the directories from the path. */
+/* Expand 'pattern' in each of the directories from 'path'. */
static void
DirExpandPath(const char *pattern, SearchPath *path, StringList *expansions)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
DirMatchFiles(pattern, dir, expansions);
@@ -861,11 +834,6 @@ SearchPath_ExpandMiddle(SearchPath *path, const char *pattern,
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.
@@ -880,7 +848,7 @@ SearchPath_ExpandMiddle(SearchPath *path, const char *pattern,
* 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.
+ * TODO: Check whether the above comment is still true.
*/
if (dirpath == NULL)
return;
@@ -921,15 +889,9 @@ SearchPath_Expand(SearchPath *path, const char *pattern, StringList *expansions)
goto done;
}
- /* At this point, the pattern does not contain '{'. */
-
slash = strchr(pattern, '/');
if (slash == NULL) {
- /* The pattern has no directory component. */
-
- /* First the files in dot. */
DirMatchFiles(pattern, dot, expansions);
- /* Then the files in every other directory on the path. */
DirExpandPath(pattern, path, expansions);
goto done;
}
@@ -972,13 +934,13 @@ done:
}
/*
- * Find if the file with the given name exists in the given path.
+ * Find if 'base' exists in 'dir'.
* Return the freshly allocated path to the file, or NULL.
*/
static char *
DirLookup(CachedDir *dir, const char *base)
{
- char *file; /* the current filename to check */
+ char *file;
DEBUG1(DIR, " %s ...\n", dir->name);
@@ -994,7 +956,7 @@ DirLookup(CachedDir *dir, const char *base)
/*
- * Find if the file with the given name exists in the given directory.
+ * Find if 'name' exists in 'dir'.
* Return the freshly allocated path to the file, or NULL.
*/
static char *
@@ -1016,12 +978,12 @@ DirLookupSubdir(CachedDir *dir, const char *name)
}
/*
- * Find if the file with the given name exists in the given path.
- * Return the freshly allocated path to the file, the empty string, or NULL.
- * Returning the empty string means that the search should be terminated.
+ * Find if 'name' (which has basename 'base') exists in 'dir'.
+ * Return the freshly allocated path to the file, an empty string, or NULL.
+ * Returning an empty string means that the search should be terminated.
*/
static char *
-DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
+DirLookupAbs(CachedDir *dir, const char *name, const char *base)
{
const char *dnp; /* pointer into dir->name */
const char *np; /* pointer into name */
@@ -1037,10 +999,10 @@ DirLookupAbs(CachedDir *dir, const char *name, const char *cp)
for (dnp = dir->name, np = name;
*dnp != '\0' && *dnp == *np; dnp++, np++)
continue;
- if (*dnp != '\0' || np != cp - 1)
+ if (*dnp != '\0' || np != base - 1)
return NULL;
- if (!HashSet_Contains(&dir->files, cp)) {
+ if (!HashSet_Contains(&dir->files, base)) {
DEBUG0(DIR, " must be here but isn't -- returning\n");
return bmake_strdup(""); /* to terminate the search */
}
@@ -1080,7 +1042,7 @@ static bool
FindFileRelative(SearchPath *path, bool seenDotLast,
const char *name, char **out_file)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
bool checkedDot = false;
char *file;
@@ -1090,11 +1052,11 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
if (dot != NULL) {
checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
- goto found;
+ goto done;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
- goto found;
+ goto done;
}
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
@@ -1107,18 +1069,18 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
checkedDot = true;
}
if ((file = DirLookupSubdir(dir, name)) != NULL)
- goto found;
+ goto done;
}
if (seenDotLast) {
if (dot != NULL && !checkedDot) {
checkedDot = true;
if ((file = DirLookupSubdir(dot, name)) != NULL)
- goto found;
+ goto done;
}
if (cur != NULL &&
(file = DirLookupSubdir(cur, name)) != NULL)
- goto found;
+ goto done;
}
if (checkedDot) {
@@ -1128,12 +1090,12 @@ FindFileRelative(SearchPath *path, bool seenDotLast,
*/
DEBUG0(DIR, " Checked . already, returning NULL\n");
file = NULL;
- goto found;
+ goto done;
}
return false;
-found:
+done:
*out_file = file;
return true;
}
@@ -1143,18 +1105,8 @@ FindFileAbsolute(SearchPath *path, bool seenDotLast,
const char *name, const char *base, char **out_file)
{
char *file;
- SearchPathNode *ln;
+ CachedDirListNode *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 &&
@@ -1187,13 +1139,6 @@ found:
/*
* Find the file with the given name along the given search path.
*
- * If the file is found in a directory that is not on the path
- * already (either 'name' is absolute or it is a relative path
- * [ dir1/.../dirn/file ] which exists below one of the directories
- * already on the search path), its directory is added to the end
- * of the path, on the assumption that there will be more files in
- * that directory later on. Sometimes this is true. Sometimes not.
- *
* Input:
* name the file to find
* path the directories to search, or NULL
@@ -1206,7 +1151,7 @@ Dir_FindFile(const char *name, SearchPath *path)
{
char *file; /* the current filename to check */
bool seenDotLast = false; /* true if we should search dot last */
- struct cached_stat cst; /* Buffer for stat, if necessary */
+ struct cached_stat cst;
const char *trailing_dot = ".";
const char *base = str_basename(name);
@@ -1233,21 +1178,19 @@ Dir_FindFile(const char *name, SearchPath *path)
* of each of the directories on the search path.
*/
if (base == name || (base - name == 2 && *name == '.')) {
- SearchPathNode *ln;
+ CachedDirListNode *ln;
/*
- * We look through all the directories on the path seeking one
+ * Look through all the directories on the path seeking one
* which contains the final component of the given name. If
- * 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.
+ * such a file is found, return its pathname.
+ * If there is no such file, go on to phase two.
*
- * No matter what, we always look for the file in the current
- * directory before anywhere else (unless we found the magic
- * DOTLAST path, in which case we search it last) and we *do
- * not* add the ./ to it if it exists.
+ * No matter what, always look for the file in the current
+ * directory before anywhere else (unless the path contains
+ * the magic '.DOTLAST', in which case search it last).
* This is so there are no conflicts between what the user
- * specifies (fish.c) and what pmake finds (./fish.c).
+ * specifies (fish.c) and what make finds (./fish.c).
*/
if (!seenDotLast && (file = DirFindDot(name, base)) != NULL)
return file;
@@ -1264,30 +1207,14 @@ Dir_FindFile(const char *name, SearchPath *path)
return file;
}
- /*
- * We didn't find the file on any directory in the search path.
- * If the name doesn't contain a slash, that means it doesn't exist.
- * If it *does* contain a slash, however, there is still hope: it
- * could be in a subdirectory of one of the members of the search
- * 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 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).]
- * This phase is only performed if the file is *not* absolute.
- */
if (base == name) {
DEBUG0(DIR, " failed.\n");
misses++;
return NULL;
}
- if (*base == '\0') {
- /* we were given a trailing "/" */
- base = trailing_dot;
- }
+ if (*base == '\0')
+ base = trailing_dot; /* we were given a trailing "/" */
if (name[0] != '/') {
if (FindFileRelative(path, seenDotLast, name, &file))
@@ -1298,16 +1225,7 @@ Dir_FindFile(const char *name, SearchPath *path)
}
/*
- * Didn't find it that way, either. Sigh. Phase 3. Add its directory
- * onto the search path in any case, just in case, then look for the
- * thing in the hash table. If we find it, grand. We return a new
- * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
- * Note that if the directory holding the file doesn't exist, this
- * will do an extra search of the final directory on the path. Unless
- * something weird happens, this search won't succeed and life will
- * be groovy.
- *
- * Sigh. We cannot add the directory onto the search path because
+ * We cannot add the directory onto the search path because
* of this amusing case:
* $(INSTALLDIR)/$(FILE): $(FILE)
*
@@ -1315,75 +1233,40 @@ Dir_FindFile(const char *name, SearchPath *path)
* When searching for $(FILE), we will find it in $(INSTALLDIR)
* b/c we added it here. This is not good...
*/
-#if 0
- {
- CachedDir *dir;
- char *prefix;
-
- if (base == trailing_dot) {
- base = strrchr(name, '/');
- base++;
- }
- prefix = bmake_strsedup(name, base - 1);
- (void)SearchPath_Add(path, prefix);
- free(prefix);
-
- bigmisses++;
- if (path->last == NULL)
- return NULL;
- dir = path->last->datum;
- if (HashSet_Contains(&dir->files, base))
- return bmake_strdup(name);
- return NULL;
- }
-#else
DEBUG1(DIR, " Looking for \"%s\" ...\n", name);
bigmisses++;
- if (cached_stat(name, &cst) == 0) {
+ if (cached_stat(name, &cst) == 0)
return bmake_strdup(name);
- }
DEBUG0(DIR, " failed. Returning NULL\n");
return NULL;
-#endif
}
/*
- * Search for a path starting at a given directory and then working our way
- * up towards the root.
- *
- * Input:
- * here starting directory
- * search_path the relative path we are looking for
- *
- * Results:
- * The found path, or NULL.
+ * Search for 'needle' starting at the directory 'here' and then working our
+ * way up towards the root directory. Return the allocated path, or NULL.
*/
char *
-Dir_FindHereOrAbove(const char *here, const char *search_path)
+Dir_FindHereOrAbove(const char *here, const char *needle)
{
struct cached_stat cst;
char *dirbase, *dirbase_end;
char *try, *try_end;
- /* copy out our starting point */
dirbase = bmake_strdup(here);
dirbase_end = dirbase + strlen(dirbase);
- /* loop until we determine a result */
for (;;) {
-
- /* try and stat(2) it ... */
- try = str_concat3(dirbase, "/", search_path);
+ try = str_concat3(dirbase, "/", needle);
if (cached_stat(try, &cst) != -1) {
- /*
- * success! if we found a file, chop off
- * the filename so we return a directory.
- */
if ((cst.cst_mode & S_IFMT) != S_IFDIR) {
+ /*
+ * Chop off the filename, to return a
+ * directory.
+ */
try_end = try + strlen(try);
while (try_end > try && *try_end != '/')
try_end--;
@@ -1396,16 +1279,10 @@ Dir_FindHereOrAbove(const char *here, const char *search_path)
}
free(try);
- /*
- * nope, we didn't find it. if we used up dirbase we've
- * reached the root and failed.
- */
if (dirbase_end == dirbase)
break; /* failed! */
- /*
- * truncate dirbase from the end to move up a dir
- */
+ /* Truncate dirbase from the end to move up a dir. */
while (dirbase_end > dirbase && *dirbase_end != '/')
dirbase_end--;
*dirbase_end = '\0'; /* chop! */
@@ -1476,10 +1353,10 @@ ResolveFullName(GNode *gn)
}
/*
- * Search gn along dirSearchPath and store its modification time in gn->mtime.
- * If no file is found, store 0 instead.
+ * Search 'gn' along 'dirSearchPath' and store its modification time in
+ * 'gn->mtime'. If no file is found, store 0 instead.
*
- * The found file is stored in gn->path, unless the node already had a path.
+ * The found file is stored in 'gn->path', unless the node already had a path.
*/
void
Dir_UpdateMTime(GNode *gn, bool forceRefresh)
@@ -1562,14 +1439,14 @@ CacheNewDir(const char *name, SearchPath *path)
}
/*
- * Read the list of filenames in the directory and store the result
- * in openDirs.
+ * Read the list of filenames in the directory 'name' and store the result
+ * in 'openDirs'.
*
- * If a path is given, append the directory to that path.
+ * If a search path is given, append the directory to that path.
*
* Input:
* path The path to which the directory should be
- * added, or NULL to only add the directory to openDirs
+ * added, or NULL to only add the directory to openDirs.
* name The name of the directory to add.
* The name is not normalized in any way.
* Output:
@@ -1584,7 +1461,7 @@ SearchPath_Add(SearchPath *path, const char *name)
{
if (path != NULL && strcmp(name, ".DOTLAST") == 0) {
- SearchPathNode *ln;
+ CachedDirListNode *ln;
/* XXX: Linear search gets slow with thousands of entries. */
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
@@ -1617,7 +1494,7 @@ SearchPath *
Dir_CopyDirSearchPath(void)
{
SearchPath *path = SearchPath_New();
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
Lst_Append(&path->dirs, CachedDir_Ref(dir));
@@ -1628,22 +1505,14 @@ Dir_CopyDirSearchPath(void)
/*
* Make a string by taking all the directories in the given search path and
* preceding them by the given flag. Used by the suffix module to create
- * variables for compilers based on suffix search paths.
- *
- * Input:
- * flag flag which should precede each directory
- * path list of directories
- *
- * Results:
- * The string mentioned above. Note that there is no space between the
- * given flag and each directory. The empty string is returned if things
- * don't go well.
+ * variables for compilers based on suffix search paths. Note that there is no
+ * space between the given flag and each directory.
*/
char *
SearchPath_ToFlags(SearchPath *path, const char *flag)
{
Buffer buf;
- SearchPathNode *ln;
+ CachedDirListNode *ln;
Buf_Init(&buf);
@@ -1663,7 +1532,7 @@ SearchPath_ToFlags(SearchPath *path, const char *flag)
void
SearchPath_Free(SearchPath *path)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
@@ -1694,7 +1563,7 @@ SearchPath_Clear(SearchPath *path)
void
SearchPath_AddAll(SearchPath *dst, SearchPath *src)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = src->dirs.first; ln != NULL; ln = ln->next) {
CachedDir *dir = ln->datum;
@@ -1709,7 +1578,6 @@ percentage(int num, int den)
return den != 0 ? num * 100 / den : 0;
}
-/********** DEBUG INFO **********/
void
Dir_PrintDirectories(void)
{
@@ -1732,7 +1600,7 @@ Dir_PrintDirectories(void)
void
SearchPath_Print(const SearchPath *path)
{
- SearchPathNode *ln;
+ CachedDirListNode *ln;
for (ln = path->dirs.first; ln != NULL; ln = ln->next) {
const CachedDir *dir = ln->datum;
diff --git a/for.c b/for.c
index 38fd1c447206..b8e8099816b0 100644
--- a/for.c
+++ b/for.c
@@ -1,4 +1,4 @@
-/* $NetBSD: for.c,v 1.176 2023/06/01 09:02:14 rillig Exp $ */
+/* $NetBSD: for.c,v 1.177 2023/11/19 22:50:11 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@@ -45,7 +45,7 @@
*
* After reaching the .endfor, the values from the .for line are grouped
* according to the number of variables. For each such group, the unexpanded
- * body is scanned for variable expressions, and those that match the
+ * body is scanned for expressions, and those that match the
* variable names are replaced with expressions of the form ${:U...}. After
* that, the body is treated like a file from an .include directive.
*
@@ -58,7 +58,7 @@
#include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: for.c,v 1.176 2023/06/01 09:02:14 rillig Exp $");
+MAKE_RCSID("$NetBSD: for.c,v 1.177 2023/11/19 22:50:11 rillig Exp $");
typedef struct ForLoop {
@@ -298,8 +298,8 @@ For_Accum(const char *line, int *forLevel)
/*
* When the body of a '.for i' loop is prepared for an iteration, each
* occurrence of $i in the body is replaced with ${:U...}, inserting the
- * value of the item. If this item contains a '$', it may be the start of a
- * variable expression. This expression is copied verbatim, its length is
+ * value of the item. If this item contains a '$', it may be the start of an
+ * expression. This expression is copied verbatim, its length is
* determined here, in a rather naive way, ignoring escape characters and
* funny delimiters in modifiers like ':S}from}to}'.
*/
@@ -428,7 +428,7 @@ ForLoop_SubstVarLong(ForLoop *f, unsigned int firstItem, Buffer *body,
/*
* While expanding the body of a .for loop, replace single-character
- * variable expressions like $i with their ${:U...} expansion.
+ * expressions like $i with their ${:U...} expansion.
*/
static void
ForLoop_SubstVarShort(ForLoop *f, unsigned int firstItem, Buffer *body,
@@ -464,7 +464,7 @@ found:
* Compute the body for the current iteration by copying the unexpanded body,
* replacing the expressions for the iteration variables on the way.
*
- * Using variable expressions ensures that the .for loop can't generate
+ * Using expressions ensures that the .for loop can't generate
* syntax, and that the later parsing will still see an expression.
* This code assumes that the variable with the empty name is never defined,
* see unit-tests/varname-empty.mk.
diff --git a/hash.c b/hash.c
index b1796c859390..88d41c2c0f6f 100644
--- a/hash.c
+++ b/hash.c
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.c,v 1.72 2022/02/09 21:09:24 rillig Exp $ */
+/* $NetBSD: hash.c,v 1.74 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -69,12 +69,12 @@
* SUCH DAMAGE.
*/
-/* Hash tables with string keys. */
+/* Hash tables with string keys and pointer values. */
#include "make.h"
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: hash.c,v 1.72 2022/02/09 21:09:24 rillig Exp $");
+MAKE_RCSID("$NetBSD: hash.c,v 1.74 2023/12/19 19:33:39 rillig Exp $");
/*
* The ratio of # entries to # buckets at which we rebuild the table to
@@ -113,7 +113,7 @@ Hash_Substring(Substring key)
static HashEntry *
HashTable_Find(HashTable *t, Substring key, unsigned int h)
{
- HashEntry *e;
+ HashEntry *he;
unsigned int chainlen = 0;
size_t keyLen = Substring_Length(key);
@@ -122,18 +122,18 @@ HashTable_Find(HashTable *t, Substring key, unsigned int h)
t, h, (int)keyLen, key.start);
#endif
- for (e = t->buckets[h & t->bucketsMask]; e != NULL; e = e->next) {
+ for (he = t->buckets[h & t->bucketsMask]; he != NULL; he = he->next) {
chainlen++;
- if (e->key_hash == h &&
- strncmp(e->key, key.start, keyLen) == 0 &&
- e->key[keyLen] == '\0')
+ if (he->hash == h &&
+ strncmp(he->key, key.start, keyLen) == 0 &&
+ he->key[keyLen] == '\0')
break;
}
if (chainlen > t->maxchain)
t->maxchain = chainlen;
- return e;
+ return he;
}
/* Set up the hash table. */
@@ -207,7 +207,7 @@ HashTable_FindValueBySubstringHash(HashTable *t, Substring key, unsigned int h)
/*
* Make the hash table larger. Any bucket numbers from the old table become
- * invalid; the hash codes stay valid though.
+ * invalid; the hash values stay valid though.
*/
static void
HashTable_Enlarge(HashTable *t)
@@ -226,8 +226,8 @@ HashTable_Enlarge(HashTable *t)
HashEntry *he = oldBuckets[i];
while (he != NULL) {
HashEntry *next = he->next;
- he->next = newBuckets[he->key_hash & newMask];
- newBuckets[he->key_hash & newMask] = he;
+ he->next = newBuckets[he->hash & newMask];
+ newBuckets[he->hash & newMask] = he;
he = next;
}
}
@@ -264,7 +264,7 @@ HashTable_CreateEntry(HashTable *t, const char *key, bool *out_isNew)
he = bmake_malloc(sizeof *he + (size_t)(keyEnd - key));
he->value = NULL;
- he->key_hash = h;
+ he->hash = h;
memcpy(he->key, key, (size_t)(keyEnd - key) + 1);
he->next = t->buckets[h & t->bucketsMask];
@@ -287,7 +287,7 @@ HashTable_Set(HashTable *t, const char *key, void *value)
void
HashTable_DeleteEntry(HashTable *t, HashEntry *he)
{
- HashEntry **ref = &t->buckets[he->key_hash & t->bucketsMask];
+ HashEntry **ref = &t->buckets[he->hash & t->bucketsMask];
HashEntry *p;
for (; (p = *ref) != NULL; ref = &p->next) {
diff --git a/hash.h b/hash.h
index 016108c39060..2bee685b7ebb 100644
--- a/hash.h
+++ b/hash.h
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.h,v 1.46 2022/01/31 22:58:26 rillig Exp $ */
+/* $NetBSD: hash.h,v 1.48 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -72,7 +72,7 @@
* from: @(#)hash.h 8.1 (Berkeley) 6/6/93
*/
-/* Hash tables with strings as keys and arbitrary pointers as values. */
+/* Hash tables with string keys and pointer values. */
#ifndef MAKE_HASH_H
#define MAKE_HASH_H
@@ -82,18 +82,17 @@ typedef struct HashEntry {
struct HashEntry *next; /* Used to link together all the entries
* associated with the same bucket. */
void *value;
- unsigned int key_hash; /* hash value of the key */
+ unsigned int hash; /* hash value of the key */
char key[1]; /* key string, variable length */
} HashEntry;
/* The hash table containing the entries. */
typedef struct HashTable {
- HashEntry **buckets; /* Pointers to HashEntry, one for each bucket
- * in the table. */
+ HashEntry **buckets;
unsigned int bucketsSize;
- unsigned int numEntries; /* Number of entries in the table. */
+ unsigned int numEntries;
unsigned int bucketsMask; /* Used to select the bucket for a hash. */
- unsigned int maxchain; /* max length of chain detected */
+ unsigned int maxchain; /* Maximum length of chain seen. */
} HashTable;
/* State of an iteration over all entries in a table. */
@@ -109,15 +108,15 @@ typedef struct HashSet {
} HashSet;
MAKE_INLINE void * MAKE_ATTR_USE
-HashEntry_Get(HashEntry *h)
+HashEntry_Get(HashEntry *he)
{
- return h->value;
+ return he->value;
}
MAKE_INLINE void
-HashEntry_Set(HashEntry *h, void *datum)
+HashEntry_Set(HashEntry *he, void *datum)
{
- h->value = datum;
+ he->value = datum;
}
/* Set things up for iterating over all entries in the hash table. */
diff --git a/job.c b/job.c
index a2e6eeb83090..21b119ed6865 100644
--- a/job.c
+++ b/job.c
@@ -1,4 +1,4 @@
-/* $NetBSD: job.c,v 1.459 2023/02/15 06:52:58 rillig Exp $ */
+/* $NetBSD: job.c,v 1.465 2024/01/07 11:39:04 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -70,12 +70,11 @@
*/
/*
- * job.c --
- * handle the creation etc. of our child processes.
+ * Create child processes and collect their output.
*
* Interface:
* Job_Init Called to initialize this module. In addition,
- * the .BEGIN target is made including all of its
+ * 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.
@@ -155,7 +154,7 @@
#include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: job.c,v 1.459 2023/02/15 06:52:58 rillig Exp $");
+MAKE_RCSID("$NetBSD: job.c,v 1.465 2024/01/07 11:39:04 rillig Exp $");
/*
* A shell defines how the commands are run. All commands for a target are
@@ -258,9 +257,7 @@ typedef struct ShellWriter {
} ShellWriter;
-/*
- * error handling variables
- */
+/* error handling variables */
static int job_errors = 0; /* number of errors reported */
static enum { /* Why is the make aborting? */
ABORT_NONE,
@@ -270,9 +267,7 @@ static enum { /* Why is the make aborting? */
} aborting = ABORT_NONE;
#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */
-/*
- * this tracks the number of tokens currently "out" to build jobs.
- */
+/* Tracks the number of tokens currently "out" to build jobs. */
int jobTokensRunning = 0;
typedef enum JobStartResult {
@@ -301,15 +296,15 @@ typedef enum JobStartResult {
#define DEFSHELL_INDEX_SH 1
#define DEFSHELL_INDEX_KSH 2
#define DEFSHELL_INDEX_CSH 3
-#else /* !DEFSHELL_CUSTOM */
+#else
#define DEFSHELL_INDEX_SH 0
#define DEFSHELL_INDEX_KSH 1
#define DEFSHELL_INDEX_CSH 2
-#endif /* !DEFSHELL_CUSTOM */
+#endif
#ifndef DEFSHELL_INDEX
#define DEFSHELL_INDEX 0 /* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */
-#endif /* !DEFSHELL_INDEX */
+#endif
static Shell shells[] = {
#ifdef DEFSHELL_CUSTOM
@@ -425,7 +420,7 @@ static char *shell_freeIt = NULL; /* Allocated memory for custom .SHELL */
static Job *job_table; /* The structures that describe them */
static Job *job_table_end; /* job_table + maxJobs */
-static unsigned int wantToken; /* we want a token */
+static unsigned int wantToken;
static bool lurking_children = false;
static bool make_suspended = false; /* Whether we've seen a SIGTSTP (etc) */
@@ -560,7 +555,7 @@ JobCreatePipe(Job *job, int minfd)
Punt("Cannot create pipe: %s", strerror(errno));
for (i = 0; i < 2; i++) {
- /* Avoid using low numbered fds */
+ /* Avoid using low-numbered fds */
fd = fcntl(pipe_fds[i], F_DUPFD, minfd);
if (fd != -1) {
close(pipe_fds[i]);
@@ -571,7 +566,6 @@ JobCreatePipe(Job *job, int minfd)
job->inPipe = pipe_fds[0];
job->outPipe = pipe_fds[1];
- /* Set close-on-exec flag for both */
if (fcntl(job->inPipe, F_SETFD, FD_CLOEXEC) == -1)
Punt("Cannot set close-on-exec: %s", strerror(errno));
if (fcntl(job->outPipe, F_SETFD, FD_CLOEXEC) == -1)
@@ -694,10 +688,10 @@ JobPassSig_suspend(int signo)
/*
* We've been continued.
*
- * A whole host of signals continue to happen!
+ * A whole host of signals is going to happen!
* SIGCHLD for any processes that actually suspended themselves.
- * SIGCHLD for any processes that exited while we were alseep.
- * The SIGCONT that actually caused us to wakeup.
+ * SIGCHLD for any processes that exited while we were asleep.
+ * The SIGCONT that actually caused us to wake up.
*
* Since we defer passing the SIGCONT on to our children until
* the main processing loop, we can be sure that all the SIGCHLD
@@ -746,7 +740,7 @@ ParseCommandFlags(char **pp, CommandFlags *out_cmdFlags)
else if (*p == '+')
out_cmdFlags->always = true;
else if (!ch_isspace(*p))
- /* Ignore whitespace for compatibility with gnu make */
+ /* Ignore whitespace for compatibility with GNU make */
break;
p++;
}
@@ -876,13 +870,9 @@ static void
JobWriteSpecials(Job *job, ShellWriter *wr, const char *escCmd, bool run,
CommandFlags *inout_cmdFlags, const char **inout_cmdTemplate)
{
- if (!run) {
- /*
- * If there is no command to run, there is no need to switch
- * error checking off and on again for nothing.
- */
+ if (!run)
inout_cmdFlags->ignerr = false;
- } else if (shell->hasErrCtl)
+ else if (shell->hasErrCtl)
ShellWriter_ErrOff(wr, job->echo && inout_cmdFlags->echo);
else if (shell->runIgnTmpl != NULL && shell->runIgnTmpl[0] != '\0') {
JobWriteSpecialsEchoCtl(job, wr, inout_cmdFlags, escCmd,
@@ -951,12 +941,10 @@ JobWriteCommand(Job *job, ShellWriter *wr, StringListNode *ln, const char *ucmd)
escCmd = shell->hasErrCtl ? NULL : EscapeShellDblQuot(xcmd);
if (!cmdFlags.echo) {
- if (job->echo && run && shell->hasEchoCtl) {
+ if (job->echo && run && shell->hasEchoCtl)
ShellWriter_EchoOff(wr);
- } else {
- if (shell->hasErrCtl)
- cmdFlags.echo = true;
- }
+ else if (shell->hasErrCtl)
+ cmdFlags.echo = true;
}
if (cmdFlags.ignerr) {
@@ -1404,7 +1392,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
return false;
}
- abortProc("%s: don't know how to make %s. Stop", progname, gn->name);
+ abortProc("don't know how to make %s. Stop", gn->name);
return false;
}
@@ -1431,7 +1419,7 @@ JobExec(Job *job, char **argv)
}
/*
- * Some jobs produce no output and it's disconcerting to have
+ * Some jobs produce no output, and it's disconcerting to have
* no feedback of their running (since they produce no output, the
* banner with their name in it never appears). This is an attempt to
* provide that feedback, even if nothing follows it.
@@ -1483,9 +1471,7 @@ JobExec(Job *job, char **argv)
execDie("lseek to 0", "stdin");
if (job->node->type & (OP_MAKE | OP_SUBMAKE)) {
- /*
- * Pass job token pipe to submakes.
- */
+ /* Pass job token pipe to submakes. */
if (fcntl(tokenWaitJob.inPipe, F_SETFD, 0) == -1)
execDie("clear close-on-exec",
"tokenWaitJob.inPipe");
@@ -1781,12 +1767,12 @@ JobStart(GNode *gn, bool special)
* itself.
*/
static char *
-PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
+PrintFilteredOutput(char *p, char *endp) /* XXX: should all be const */
{
- char *ecp; /* XXX: should be const */
+ char *ep; /* XXX: should be const */
if (shell->noPrint == NULL || shell->noPrint[0] == '\0')
- return cp;
+ return p;
/*
* XXX: What happens if shell->noPrint occurs on the boundary of
@@ -1794,9 +1780,9 @@ PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
* be a proper stream filter instead of doing string matching on
* selected chunks of the output.
*/
- while ((ecp = strstr(cp, shell->noPrint)) != NULL) {
- if (ecp != cp) {
- *ecp = '\0'; /* XXX: avoid writing to the buffer */
+ while ((ep = strstr(p, shell->noPrint)) != NULL) {
+ if (ep != p) {
+ *ep = '\0'; /* XXX: avoid writing to the buffer */
/*
* The only way there wouldn't be a newline after
* this line is if it were the last in the buffer.
@@ -1804,16 +1790,16 @@ PrintFilteredOutput(char *cp, char *endp) /* XXX: should all be const */
* there must be a newline, so we don't print one.
*/
/* XXX: What about null bytes in the output? */
- (void)fprintf(stdout, "%s", cp);
+ (void)fprintf(stdout, "%s", p);
(void)fflush(stdout);
}
- cp = ecp + shell->noPrintLen;
- if (cp == endp)
+ p = ep + shell->noPrintLen;
+ if (p == endp)
break;
- cp++; /* skip over the (XXX: assumed) newline */
- pp_skip_whitespace(&cp);
+ p++; /* skip over the (XXX: assumed) newline */
+ pp_skip_whitespace(&p);
}
- return cp;
+ return p;
}
/*
@@ -1908,7 +1894,7 @@ again:
*/
job->outBuf[i] = '\0';
if (i >= job->curPos) {
- char *cp;
+ char *p;
/*
* FIXME: SwitchOutputTo should be here, according to
@@ -1916,23 +1902,23 @@ again:
* do anything in the default shell, this bug has gone
* unnoticed until now.
*/
- cp = PrintFilteredOutput(job->outBuf, &job->outBuf[i]);
+ p = PrintFilteredOutput(job->outBuf, &job->outBuf[i]);
/*
* There's still more in the output buffer. This time,
* though, we know there's no newline at the end, so
* we add one of our own free will.
*/
- if (*cp != '\0') {
+ if (*p != '\0') {
if (!opts.silent)
SwitchOutputTo(job->node);
#ifdef USE_META
if (useMeta) {
- meta_job_output(job, cp,
+ meta_job_output(job, p,
gotNL ? "\n" : "");
}
#endif
- (void)fprintf(stdout, "%s%s", cp,
+ (void)fprintf(stdout, "%s%s", p,
gotNL ? "\n" : "");
(void)fflush(stdout);
}
@@ -2212,11 +2198,10 @@ Shell_GetNewline(void)
void
Job_SetPrefix(void)
{
- if (targPrefix != NULL) {
+ if (targPrefix != NULL)
free(targPrefix);
- } else if (!Var_Exists(SCOPE_GLOBAL, ".MAKE.JOB.PREFIX")) {
+ else if (!Var_Exists(SCOPE_GLOBAL, ".MAKE.JOB.PREFIX"))
Global_Set(".MAKE.JOB.PREFIX", "---");
- }
targPrefix = Var_Subst("${.MAKE.JOB.PREFIX}",
SCOPE_GLOBAL, VARE_WANTRES);
@@ -2283,9 +2268,7 @@ Job_Init(void)
watchfd(&childExitJob);
sigemptyset(&caught_signals);
- /*
- * Install a SIGCHLD handler.
- */
+ /* Install a SIGCHLD handler. */
(void)bmake_signal(SIGCHLD, JobChildSig);
sigaddset(&caught_signals, SIGCHLD);
@@ -2416,9 +2399,7 @@ Job_ParseShell(char *line)
memset(&newShell, 0, sizeof newShell);
- /*
- * Parse the specification by keyword
- */
+ /* Parse the specification by keyword. */
wordsList = Str_Words(line, true);
words = wordsList.words;
argc = wordsList.len;
@@ -2513,25 +2494,9 @@ Job_ParseShell(char *line)
}
}
} else {
- /*
- * The user provided a path. If s/he gave nothing else
- * (fullSpec is false), try and find a matching shell in the
- * ones we know of. Else we just take the specification at
- * its word and copy it to a new location. In either case,
- * we need to record the path the user gave for the shell.
- */
shellPath = path;
- path = strrchr(path, '/');
- if (path == NULL) {
- path = UNCONST(shellPath);
- } else {
- path++;
- }
- if (newShell.name != NULL) {
- shellName = newShell.name;
- } else {
- shellName = path;
- }
+ shellName = newShell.name != NULL ? newShell.name
+ : str_basename(path);
if (!fullSpec) {
if ((sh = FindShellByName(shellName)) == NULL) {
Parse_Error(PARSE_WARNING,
@@ -2628,11 +2593,10 @@ Job_Finish(void)
GNode *endNode = Targ_GetEndNode();
if (!Lst_IsEmpty(&endNode->commands) ||
!Lst_IsEmpty(&endNode->children)) {
- if (job_errors != 0) {
+ if (job_errors != 0)
Error("Errors reported so .END ignored");
- } else {
+ else
JobRun(endNode);
- }
}
return job_errors;
}
@@ -2772,9 +2736,7 @@ clearfd(Job *job)
fdsLen--;
}
#endif
- /*
- * Move last job in table into hole made by dead job.
- */
+ /* Move last job in table into hole made by dead job. */
if (fdsLen != i) {
fds[i] = fds[fdsLen];
jobByFdIndex[i] = jobByFdIndex[fdsLen];
@@ -2905,9 +2867,8 @@ Job_TokenWithdraw(void)
if (count == 0)
Fatal("eof on job pipe!");
if (count < 0 && jobTokensRunning != 0) {
- if (errno != EAGAIN) {
+ if (errno != EAGAIN)
Fatal("job pipe read: %s", strerror(errno));
- }
DEBUG1(JOB, "(%d) blocked for token\n", getpid());
wantToken = 1;
return false;
diff --git a/job.h b/job.h
index 574c12ed16a0..26185ba84a7d 100644
--- a/job.h
+++ b/job.h
@@ -1,4 +1,4 @@
-/* $NetBSD: job.h,v 1.77 2021/12/15 12:58:01 rillig Exp $ */
+/* $NetBSD: job.h,v 1.78 2023/12/19 19:33:39 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -73,9 +73,7 @@
* from: @(#)job.h 8.1 (Berkeley) 6/6/93
*/
-/*
- * Running of jobs in parallel mode.
- */
+/* Run jobs in parallel mode. */
#ifndef MAKE_JOB_H
#define MAKE_JOB_H
diff --git a/lst.c b/lst.c
index 09a6ef10c76e..7a7c08200947 100644
--- a/lst.c
+++ b/lst.c
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.c,v 1.106 2022/02/26 11:57:21 rillig Exp $ */
+/* $NetBSD: lst.c,v 1.107 2023/12/29 20:43:58 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -34,7 +34,7 @@
#include "make.h"
-MAKE_RCSID("$NetBSD: lst.c,v 1.106 2022/02/26 11:57:21 rillig Exp $");
+MAKE_RCSID("$NetBSD: lst.c,v 1.107 2023/12/29 20:43:58 rillig Exp $");
static ListNode *
LstNodeNew(ListNode *prev, ListNode *next, void *datum)
@@ -48,15 +48,6 @@ LstNodeNew(ListNode *prev, ListNode *next, void *datum)
return ln;
}
-/* Create and initialize a new, empty list. */
-List *
-Lst_New(void)
-{
- List *list = bmake_malloc(sizeof *list);
- Lst_Init(list);
- return list;
-}
-
void
Lst_Done(List *list)
{
@@ -80,15 +71,6 @@ Lst_DoneCall(List *list, LstFreeProc freeProc)
}
}
-/* Free a list and all its nodes. The node data are not freed though. */
-void
-Lst_Free(List *list)
-{
-
- Lst_Done(list);
- free(list);
-}
-
/* Insert a new node with the datum before the given node. */
void
Lst_InsertBefore(List *list, ListNode *ln, void *datum)
diff --git a/lst.h b/lst.h
index a5170b420b7a..59f8f201192f 100644
--- a/lst.h
+++ b/lst.h
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.h,v 1.103 2022/03/03 19:55:27 rillig Exp $ */
+/* $NetBSD: lst.h,v 1.104 2023/12/29 20:43:58 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -106,16 +106,10 @@ struct List {
/* Free the datum of a node, called before freeing the node itself. */
typedef void LstFreeProc(void *);
-/* Create or destroy a list */
-
-/* Create a new list. */
-List *Lst_New(void) MAKE_ATTR_USE;
/* Free the list nodes, but not the list itself. */
void Lst_Done(List *);
/* Free the list nodes, freeing the node data using the given function. */
void Lst_DoneCall(List *, LstFreeProc);
-/* Free the list, leaving the node data unmodified. */
-void Lst_Free(List *);
#define LST_INIT { NULL, NULL }
diff --git a/main.c b/main.c
index b0ce265d34a6..79e2e5e0b4e7 100644
--- a/main.c
+++ b/main.c
@@ -1,4 +1,4 @@
-/* $NetBSD: main.c,v 1.599 2023/09/10 21:52:36 rillig Exp $ */
+/* $NetBSD: main.c,v 1.609 2024/01/07 01:33:57 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -111,8 +111,8 @@
#include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: main.c,v 1.599 2023/09/10 21:52:36 rillig Exp $");
-#if defined(MAKE_NATIVE) && !defined(lint)
+MAKE_RCSID("$NetBSD: main.c,v 1.609 2024/01/07 01:33:57 sjg Exp $");
+#if defined(MAKE_NATIVE)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. "
"All rights reserved.");
@@ -125,7 +125,7 @@ __COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
CmdOpts opts;
time_t now; /* Time at start of make */
GNode *defaultNode; /* .DEFAULT node */
-bool allPrecious; /* .PRECIOUS given on line by itself */
+bool allPrecious; /* .PRECIOUS given on a line by itself */
bool deleteOnError; /* .DELETE_ON_ERROR: set */
static int maxJobTokens; /* -j argument */
@@ -152,7 +152,7 @@ static HashTable cached_realpaths;
/*
* For compatibility with the POSIX version of MAKEFLAGS that includes
- * all the options without '-', convert 'flags' to '-f -l -a -g -s'.
+ * all the options without '-', convert 'flags' to '-f -l -a -g -s '.
*/
static char *
explode(const char *flags)
@@ -226,8 +226,7 @@ MainParseArgDebugFile(const char *arg)
opts.debug_file = fopen(fname, mode);
if (opts.debug_file == NULL) {
- fprintf(stderr, "Cannot open debug file \"%s\"\n",
- fname);
+ fprintf(stderr, "Cannot open debug file \"%s\"\n", fname);
exit(2);
}
free(fname);
@@ -407,19 +406,19 @@ MainParseArgJobs(const char *arg)
forceJobs = true;
opts.maxJobs = (int)strtol(arg, &end, 0);
- p = arg + (end - arg);
+ p = end;
#ifdef _SC_NPROCESSORS_ONLN
if (*p != '\0') {
double d;
- if (*p == 'C') {
+ if (*p == 'C')
d = (opts.maxJobs > 0) ? opts.maxJobs : 1;
- } else if (*p == '.') {
+ else if (*p == '.') {
d = strtod(arg, &end);
- p = arg + (end - arg);
+ p = end;
} else
- d = 0;
- if (d > 0) {
+ d = 0.0;
+ if (d > 0.0) {
p = "";
opts.maxJobs = (int)sysconf(_SC_NPROCESSORS_ONLN);
opts.maxJobs = (int)(d * (double)opts.maxJobs);
@@ -443,8 +442,7 @@ MainParseArgJobs(const char *arg)
static void
MainParseArgSysInc(const char *argvalue)
{
- /* look for magic parent directory search string */
- if (strncmp(".../", argvalue, 4) == 0) {
+ if (strncmp(argvalue, ".../", 4) == 0) {
char *found_path = Dir_FindHereOrAbove(curdir, argvalue + 4);
if (found_path == NULL)
return;
@@ -480,7 +478,7 @@ MainParseOption(char c, const char *argvalue)
Global_Append(MAKEFLAGS, argvalue);
break;
case 'I':
- Parse_AddIncludeDir(argvalue);
+ SearchPath_Add(parseIncPath, argvalue);
Global_Append(MAKEFLAGS, "-I");
Global_Append(MAKEFLAGS, argvalue);
break;
@@ -691,13 +689,13 @@ Main_ParseArgLine(const char *line)
{
Words words;
char *buf;
+ const char *p;
if (line == NULL)
return;
- /* XXX: don't use line as an iterator variable */
- for (; *line == ' '; line++)
+ for (p = line; *p == ' '; p++)
continue;
- if (line[0] == '\0')
+ if (p[0] == '\0')
return;
#ifndef POSIX
@@ -716,7 +714,7 @@ Main_ParseArgLine(const char *line)
#endif
{
FStr argv0 = Var_Value(SCOPE_GLOBAL, ".MAKE");
- buf = str_concat3(argv0.str, " ", line);
+ buf = str_concat3(argv0.str, " ", p);
FStr_Done(&argv0);
}
@@ -791,22 +789,17 @@ SetVarObjdir(bool writable, const char *var, const char *suffix)
}
/*
- * Splits str into words, adding them to the list.
+ * Splits str into words (in-place, modifying it), adding them to the list.
* The string must be kept alive as long as the list.
*/
-int
-str2Lst_Append(StringList *lp, char *str)
+void
+AppendWords(StringList *lp, char *str)
{
- char *cp;
- int n;
-
+ char *p;
const char *sep = " \t";
- for (n = 0, cp = strtok(str, sep); cp != NULL; cp = strtok(NULL, sep)) {
- Lst_Append(lp, cp);
- n++;
- }
- return n;
+ for (p = strtok(str, sep); p != NULL; p = strtok(NULL, sep))
+ Lst_Append(lp, p);
}
#ifdef SIGINFO
@@ -1042,20 +1035,14 @@ InitVarMachineArch(void)
#ifndef NO_PWD_OVERRIDE
/*
- * 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)
- * or via subdir.mk in which case its likely a shell which does
+ * here. That is, sitting at a shell prompt (shell that provides $PWD)
+ * or via subdir.mk, in which case it's likely a shell which does
* not provide it.
*
* So, to stop it breaking this case only, we ignore PWD if
- * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a variable expression.
+ * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains an expression.
*/
static void
HandlePWD(const struct stat *curdir_st)
@@ -1189,7 +1176,7 @@ static void
InitDefSysIncPath(char *syspath)
{
static char defsyspath[] = _PATH_DEFSYSPATH;
- char *start, *cp;
+ char *start, *p;
/*
* If no user-supplied system path was given (through the -m option)
@@ -1201,11 +1188,11 @@ InitDefSysIncPath(char *syspath)
else
syspath = bmake_strdup(syspath);
- for (start = syspath; *start != '\0'; start = cp) {
- for (cp = start; *cp != '\0' && *cp != ':'; cp++)
+ for (start = syspath; *start != '\0'; start = p) {
+ for (p = start; *p != '\0' && *p != ':'; p++)
continue;
- if (*cp == ':')
- *cp++ = '\0';
+ if (*p == ':')
+ *p++ = '\0';
/* look for magic parent directory search string */
if (strncmp(start, ".../", 4) == 0) {
@@ -1296,17 +1283,17 @@ InitVpath(void)
/* TODO: handle errors */
path = vpath;
do {
- char *cp;
+ char *p;
/* skip to end of directory */
- for (cp = path; *cp != ':' && *cp != '\0'; cp++)
+ for (p = path; *p != ':' && *p != '\0'; p++)
continue;
/* Save terminator character so know when to stop */
- savec = *cp;
- *cp = '\0';
+ savec = *p;
+ *p = '\0';
/* Add directory to search path */
(void)SearchPath_Add(&dirSearchPath, path);
- *cp = savec;
- path = cp + 1;
+ *p = savec;
+ path = p + 1;
} while (savec == ':');
free(vpath);
}
@@ -1332,7 +1319,7 @@ ReadFirstDefaultMakefile(void)
SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
- (void)str2Lst_Append(&makefiles, prefs);
+ AppendWords(&makefiles, prefs);
for (ln = makefiles.first; ln != NULL; ln = ln->next)
if (ReadMakefile(ln->datum))
@@ -1378,22 +1365,12 @@ main_Init(int argc, char **argv)
exit(2);
}
- /*
- * Get the name of this type of MACHINE from utsname
- * so we can share an executable for similar machines.
- * (i.e. m68k: amiga hp300, mac68k, sun3, ...)
- *
- * Note that both MACHINE and MACHINE_ARCH are decided at
- * run-time.
- */
machine = InitVarMachine(&utsname);
machine_arch = InitVarMachineArch();
myPid = getpid(); /* remember this for vFork() */
- /*
- * Just in case MAKEOBJDIR wants us to do something tricky.
- */
+ /* Just in case MAKEOBJDIR wants us to do something tricky. */
Targ_Init();
Var_Init();
Global_Set_ReadOnly(".MAKE.OS", utsname.sysname);
@@ -1402,7 +1379,7 @@ main_Init(int argc, char **argv)
#ifdef MAKE_VERSION
Global_Set("MAKE_VERSION", MAKE_VERSION);
#endif
- Global_Set_ReadOnly(".newline", "\n"); /* handy for :@ loops */
+ Global_Set_ReadOnly(".newline", "\n");
#ifndef MAKEFILE_PREFERENCE_LIST
/* This is the traditional preference for makefiles. */
# define MAKEFILE_PREFERENCE_LIST "makefile Makefile"
@@ -1757,16 +1734,40 @@ Cmd_Exec(const char *cmd, char **error)
Buffer buf; /* buffer to store the result */
ssize_t bytes_read;
char *output;
- char *cp;
+ char *p;
int saved_errno;
+ char cmd_file[MAXPATHLEN];
+ size_t cmd_len;
+ int cmd_fd = -1;
- if (shellName == NULL)
+ if (shellPath == NULL)
Shell_Init();
+ cmd_len = strlen(cmd);
+ if (cmd_len > 1000) {
+ cmd_fd = mkTempFile(NULL, cmd_file, sizeof(cmd_file));
+ if (cmd_fd >= 0) {
+ ssize_t n;
+
+ n = write(cmd_fd, cmd, cmd_len);
+ close(cmd_fd);
+ if (n < (ssize_t)cmd_len) {
+ unlink(cmd_file);
+ cmd_fd = -1;
+ }
+ }
+ }
+
args[0] = shellName;
- args[1] = "-c";
- args[2] = cmd;
- args[3] = NULL;
+ if (cmd_fd >= 0) {
+ args[1] = cmd_file;
+ args[2] = NULL;
+ } else {
+ cmd_file[0] = '\0';
+ args[1] = "-c";
+ args[2] = cmd;
+ args[3] = NULL;
+ }
DEBUG1(VAR, "Capturing the output of command \"%s\"\n", cmd);
if (pipe(pipefds) == -1) {
@@ -1815,9 +1816,9 @@ Cmd_Exec(const char *cmd, char **error)
buf.data[buf.len - 1] = '\0';
output = Buf_DoneData(&buf);
- for (cp = output; *cp != '\0'; cp++)
- if (*cp == '\n')
- *cp = ' ';
+ for (p = output; *p != '\0'; p++)
+ if (*p == '\n')
+ *p = ' ';
if (WIFSIGNALED(status))
*error = str_concat3("\"", cmd, "\" exited on a signal");
@@ -1829,6 +1830,8 @@ Cmd_Exec(const char *cmd, char **error)
"Couldn't read shell's output for \"", cmd, "\"");
else
*error = NULL;
+ if (cmd_file[0] != '\0')
+ unlink(cmd_file);
return output;
}
@@ -1880,6 +1883,7 @@ Fatal(const char *fmt, ...)
Job_Wait();
(void)fflush(stdout);
+ fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
(void)vfprintf(stderr, fmt, ap);
va_end(ap);
@@ -2003,13 +2007,13 @@ execDie(const char *af, const char *av)
static void
purge_relative_cached_realpaths(void)
{
- HashEntry *he, *nhe;
+ HashEntry *he, *next;
HashIter hi;
HashIter_Init(&hi, &cached_realpaths);
he = HashIter_Next(&hi);
while (he != NULL) {
- nhe = HashIter_Next(&hi);
+ next = HashIter_Next(&hi);
if (he->key[0] != '/') {
DEBUG1(DIR, "cached_realpath: purging %s\n", he->key);
HashTable_DeleteEntry(&cached_realpaths, he);
@@ -2018,7 +2022,7 @@ purge_relative_cached_realpaths(void)
* free them or document why they cannot be freed.
*/
}
- he = nhe;
+ he = next;
}
}
@@ -2183,7 +2187,7 @@ getTmpdir(void)
/*
* Create and open a temp file using "pattern".
- * If out_fname is provided, set it to a copy of the filename created.
+ * If tfile is provided, set it to a copy of the filename created.
* Otherwise unlink the file once open.
*/
int
diff --git a/make.1 b/make.1
index e70252f1b73e..0be0f9c8d64f 100644
--- a/make.1
+++ b/make.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.371 2023/09/10 21:52:36 rillig Exp $
+.\" $NetBSD: make.1,v 1.372 2023/12/24 16:48:30 sjg Exp $
.\"
.\" Copyright (c) 1990, 1993
.\" The Regents of the University of California. All rights reserved.
@@ -29,7 +29,7 @@
.\"
.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94
.\"
-.Dd September 9, 2023
+.Dd December 24, 2023
.Dt MAKE 1
.Os
.Sh NAME
@@ -2523,7 +2523,7 @@ set the read-only attribute on the global variables specified as sources.
.It Ic .SHELL
Sets the shell that
.Nm
-uses to execute commands in jobs mode.
+uses to execute commands.
The sources are a set of
.Ar field\| Ns Cm \&= Ns Ar value
pairs.
diff --git a/make.c b/make.c
index b636800d33cb..53f7b35754ba 100644
--- a/make.c
+++ b/make.c
@@ -1,4 +1,4 @@
-/* $NetBSD: make.c,v 1.259 2023/02/14 21:38:31 rillig Exp $ */
+/* $NetBSD: make.c,v 1.262 2024/01/05 23:22:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -104,7 +104,7 @@
#include "job.h"
/* "@(#)make.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: make.c,v 1.259 2023/02/14 21:38:31 rillig Exp $");
+MAKE_RCSID("$NetBSD: make.c,v 1.262 2024/01/05 23:22:06 rillig Exp $");
/* Sequence # to detect recursion. */
static unsigned int checked_seqno = 1;
@@ -132,7 +132,7 @@ GNodeType_ToString(GNodeType type, void **freeIt)
{
Buffer buf;
- Buf_InitSize(&buf, 32);
+ Buf_Init(&buf);
#define ADD(flag) Buf_AddFlag(&buf, (type & (flag)) != OP_NONE, #flag)
ADD(OP_DEPENDS);
ADD(OP_FORCE);
@@ -174,7 +174,7 @@ GNodeFlags_ToString(GNodeFlags flags, void **freeIt)
{
Buffer buf;
- Buf_InitSize(&buf, 32);
+ Buf_Init(&buf);
Buf_AddFlag(&buf, flags.remake, "REMAKE");
Buf_AddFlag(&buf, flags.childMade, "CHILDMADE");
Buf_AddFlag(&buf, flags.force, "FORCE");
@@ -330,13 +330,12 @@ GNode_IsOODate(GNode *gn)
* out-of-date.
*/
if (DEBUG(MAKE)) {
- if (gn->type & OP_FORCE) {
+ if (gn->type & OP_FORCE)
debug_printf("! operator...");
- } else if (gn->type & OP_PHONY) {
+ else if (gn->type & OP_PHONY)
debug_printf(".PHONY node...");
- } else {
+ else
debug_printf(".EXEC node...");
- }
}
oodate = true;
} else if (IsOODateRegular(gn)) {
@@ -440,11 +439,10 @@ Make_HandleUse(GNode *cgn, GNode *pgn)
* We don't need to do this for commands.
* They get expanded properly when we execute.
*/
- if (gn->uname == NULL) {
+ if (gn->uname == NULL)
gn->uname = gn->name;
- } else {
+ else
free(gn->name);
- }
gn->name = Var_Subst(gn->uname, pgn, VARE_WANTRES);
/* TODO: handle errors */
if (gn->uname != NULL && strcmp(gn->name, gn->uname) != 0) {
@@ -546,9 +544,8 @@ Make_Recheck(GNode *gn)
* depend on FRC to be made, so we have to check for gn->children
* being empty as well.
*/
- if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) {
+ if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children))
gn->mtime = now;
- }
#else
/*
* This is what Make does and it's actually a good thing, as it
@@ -689,9 +686,8 @@ Make_Update(GNode *cgn)
* now -- some rules won't actually update the file. If the file
* still doesn't exist, make its mtime now.
*/
- if (cgn->made != UPTODATE) {
+ if (cgn->made != UPTODATE)
mtime = Make_Recheck(cgn);
- }
/*
* If this is a `::' node, we must consult its first instance
@@ -1224,7 +1220,7 @@ MakePrintStatusList(GNodeList *gnodes, int *errors)
static void
ExamineLater(GNodeList *examine, GNodeList *toBeExamined)
{
- ListNode *ln;
+ GNodeListNode *ln;
for (ln = toBeExamined->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
diff --git a/make.h b/make.h
index dab619c95b80..1b9147b383b0 100644
--- a/make.h
+++ b/make.h
@@ -1,4 +1,4 @@
-/* $NetBSD: make.h,v 1.325 2023/09/10 11:52:29 rillig Exp $ */
+/* $NetBSD: make.h,v 1.327 2023/12/17 09:02:26 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -882,7 +882,6 @@ void PrintLocation(FILE *, bool, const GNode *);
void PrintStackTrace(bool);
void Parse_Error(ParseErrorLevel, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
bool Parse_VarAssign(const char *, bool, GNode *) MAKE_ATTR_USE;
-void Parse_AddIncludeDir(const char *);
void Parse_File(const char *, int);
void Parse_PushInput(const char *, unsigned, unsigned, Buffer,
struct ForLoop *);
@@ -1064,7 +1063,7 @@ void PrintOnError(GNode *, const char *);
void Main_ExportMAKEFLAGS(bool);
bool Main_SetObjdir(bool, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3);
int mkTempFile(const char *, char *, size_t) MAKE_ATTR_USE;
-int str2Lst_Append(StringList *, char *);
+void AppendWords(StringList *, char *);
void GNode_FprintDetails(FILE *, const char *, const GNode *, const char *);
bool GNode_ShouldExecute(GNode *gn) MAKE_ATTR_USE;
diff --git a/meta.c b/meta.c
index 89f1a9fa1588..b9103a620e49 100644
--- a/meta.c
+++ b/meta.c
@@ -1,4 +1,4 @@
-/* $NetBSD: meta.c,v 1.206 2023/08/19 00:09:17 sjg Exp $ */
+/* $NetBSD: meta.c,v 1.207 2023/12/17 09:02:26 rillig Exp $ */
/*
* Implement 'meta' mode.
@@ -620,7 +620,7 @@ meta_mode_init(const char *make_mode)
metaBailiwickStr = Var_Subst("${.MAKE.META.BAILIWICK:O:u:tA}",
SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
- str2Lst_Append(&metaBailiwick, metaBailiwickStr);
+ AppendWords(&metaBailiwick, metaBailiwickStr);
/*
* We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS}
*/
@@ -629,7 +629,7 @@ meta_mode_init(const char *make_mode)
metaIgnorePathsStr = Var_Subst("${" MAKE_META_IGNORE_PATHS ":O:u:tA}",
SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
- str2Lst_Append(&metaIgnorePaths, metaIgnorePathsStr);
+ AppendWords(&metaIgnorePaths, metaIgnorePathsStr);
/*
* We ignore any paths that match ${.MAKE.META.IGNORE_PATTERNS}
diff --git a/mk/ChangeLog b/mk/ChangeLog
index 26be14aae809..447c8cf9c955 100644
--- a/mk/ChangeLog
+++ b/mk/ChangeLog
@@ -1,3 +1,45 @@
+2024-01-05 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20240105
+ * dirdeps.mk: for MAKE_VERSION 20240105 we do not have the same
+ limits on command line length, so skip export of lists to env.
+
+2023-12-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * man.mk: add logic for staging man pages
+
+2023-11-28 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * jobs.mk: avoid C suffix in JOB_MAX_C if factor is floating
+ point. This keeps JOB_MAX numeric incase another makefile does
+ comparisons.
+
+2023-11-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dpadd.mk: add support for DPLIBS_QUALIFIER_LIST
+
+ * gendirdeps.mk: if META_XTRAS is passed to us, add to META_FILES
+
+2023-10-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * compiler.mk (COMPILER_VERSION): clang at least is into
+ double digit major versions.
+
+2023-10-02 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20231001
+
+ * set _CCLINK in init.mk so lib.mk can use it for default SHLIB_LD
+
+ * lib.mk (cleanlib): use LD_solink so we remove all the right files.
+ Use -Wl for -soname since we now default to linking with CC
+ We should not need SHLIB_LDSTARTFILE or SHLIB_LDENDFILE when linking
+ with CC.
+
+2023-09-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * init.mk (QUALIFIED_VAR_LIST): Add SRCS
+
2023-09-09 Simon J Gerraty <sjg@beast.crufty.net>
* jobs.mk (JOB_MAX): use -jC if we can
diff --git a/mk/compiler.mk b/mk/compiler.mk
index ce394c3275bf..4f942d1f9f7e 100644
--- a/mk/compiler.mk
+++ b/mk/compiler.mk
@@ -1,4 +1,4 @@
-# $Id: compiler.mk,v 1.11 2022/09/09 17:44:29 sjg Exp $
+# $Id: compiler.mk,v 1.12 2023/10/03 18:47:48 sjg Exp $
#
# @(#) Copyright (c) 2019, Simon J. Gerraty
#
@@ -32,7 +32,7 @@ COMPILER_TYPE = gcc
.endif
.endif
.if empty(COMPILER_VERSION)
-COMPILER_VERSION != echo "${_v:M[1-9].[0-9]*}:[1]" | \
+COMPILER_VERSION != echo "${_v:M[1-9][0-9]*.[0-9]*}:[1]" | \
awk -F. '{print $$1 * 10000 + $$2 * 100 + $$3;}'
.endif
.undef _v
diff --git a/mk/dirdeps.mk b/mk/dirdeps.mk
index 6ed271db4df0..b3b34145e8e9 100644
--- a/mk/dirdeps.mk
+++ b/mk/dirdeps.mk
@@ -1,4 +1,4 @@
-# $Id: dirdeps.mk,v 1.165 2023/08/19 17:35:32 sjg Exp $
+# $Id: dirdeps.mk,v 1.166 2024/01/05 23:16:34 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause
#
@@ -754,7 +754,6 @@ _cache_script = echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}';
# guard against _new_dirdeps being too big for a single command line
_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@:S,^${SRCTOP}/,,}
_cache_xtra_deps := ${_build_xtra_dirs:S,^${SRCTOP}/,,}
-.export _cache_xtra_deps _new_dirdeps
.if !empty(DIRDEPS_EXPORT_VARS) || !empty(DEP_EXPORT_VARS)
# Discouraged, but there are always exceptions.
# Handle it here rather than explain how.
@@ -804,15 +803,27 @@ ${_this_dir}.$m: ${_build_dirs:M*.$q}
.if ${BUILD_DIRDEPS_CACHE} == "yes"
.if !empty(_build_dirs)
_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,}
+# anything in _{build,env}_xtra_dirs is hooked to dirdeps: only
+.if ${MAKE_VERSION} < 20240105
.if !empty(_cache_deps)
.export _cache_deps
_cache_script += for x in $$_cache_deps; do echo " _{SRCTOP}/$$x \\"; done;
.endif
-# anything in _{build,env}_xtra_dirs is hooked to dirdeps: only
+.export _cache_xtra_deps _new_dirdeps
x!= echo; { echo; ${_cache_script} echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
echo; echo 'dirdeps: ${_this_dir}.$m \'; \
for x in $$_cache_xtra_deps; do echo " _{SRCTOP}/$$x \\"; done; \
echo; for x in $$_new_dirdeps; do echo "_{SRCTOP}/$$x: _DIRDEP_USE"; done; } >&3
+.else
+# we do not have the same limits on command lines
+.if !empty(_cache_deps)
+_cache_script += for x in ${_cache_deps}; do echo " _{SRCTOP}/$$x \\"; done;
+.endif
+x!= echo; { echo; ${_cache_script} echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
+ echo; echo 'dirdeps: ${_this_dir}.$m \'; \
+ for x in ${_cache_xtra_deps}; do echo " _{SRCTOP}/$$x \\"; done; \
+ echo; for x in ${_new_dirdeps}; do echo "_{SRCTOP}/$$x: _DIRDEP_USE"; done; } >&3
+.endif
.endif
.else
${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m}
diff --git a/mk/dpadd.mk b/mk/dpadd.mk
index 07528f9e926c..a4c73c1854fc 100644
--- a/mk/dpadd.mk
+++ b/mk/dpadd.mk
@@ -1,6 +1,6 @@
-# $Id: dpadd.mk,v 1.30 2021/12/08 05:56:50 sjg Exp $
+# $Id: dpadd.mk,v 1.31 2023/11/25 01:07:49 sjg Exp $
#
-# @(#) Copyright (c) 2004, Simon J. Gerraty
+# @(#) Copyright (c) 2004-2023, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -75,6 +75,10 @@
# and -L${STAGE_OBJTOP}/usr/lib are sufficient, and we should
# have no need of anything else.
#
+# Sometimes things are more complicated so allow for
+# DPLIBS to be qualified with each of the variables in
+# DPLIBS_QUALIFIER_LIST (default is VAR_QUALIFIER_LIST same as
+# init.mk)
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@@ -110,7 +114,9 @@ CXXFLAGS_LAST += ${CXXFLAGS_DEBUG_XTRA}
.-include <local.dpadd.mk>
# DPLIBS helps us ensure we keep DPADD and LDADD in sync
-DPLIBS+= ${DPLIBS_LAST}
+DPLIBS_QUALIFIER_LIST ?= ${VAR_QUALIFIER_LIST}
+DPLIBS += ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS.$Q:U}@}
+DPLIBS+= ${DPLIBS_LAST} ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS_LAST.$Q:U}@}
DPADD+= ${DPLIBS:N-*}
.for __lib in ${DPLIBS}
.if "${__lib:M-*}" != ""
@@ -131,7 +137,7 @@ __dpadd_libs := ${DPADD:M*/lib*}
# dups will be dealt with later.
# Note: libfoo_pic uses DPLIBS_libfoo
__ldadd_all_xtras=
-.for __lib in ${__dpadd_libs:@d@${DPLIBS_${d:T:R:S,_pic,,}}@}
+.for __lib in ${__dpadd_libs:@d@${DPLIBS_${d:T:R:S,_pic,,}} ${DPLIBS_QUALIFIER_LIST:u:@Q@${DPLIBS_${d:T:R:S,_pic,,}.$Q:U}@}@}
__ldadd_all_xtras+= ${LDADD_${__lib}:U${__lib:T:R:S/lib/-l/:C/\.so.*//}}
.if "${DPADD:M${__lib}}" == ""
DPADD+= ${__lib}
diff --git a/mk/gendirdeps.mk b/mk/gendirdeps.mk
index ec4e188cdf78..5d54aa232152 100644
--- a/mk/gendirdeps.mk
+++ b/mk/gendirdeps.mk
@@ -1,4 +1,4 @@
-# $Id: gendirdeps.mk,v 1.49 2023/04/20 17:45:03 sjg Exp $
+# $Id: gendirdeps.mk,v 1.50 2023/11/04 16:47:34 sjg Exp $
# SPDX-License-Identifier: BSD-2-Clause
#
@@ -102,6 +102,10 @@ _DEPENDFILE := ${_CURDIR}/${.MAKE.DEPENDFILE:T}
# caller should have set this
META_FILES ?= ${.MAKE.META.FILES}
+# this sometimes needs to be passed separately
+.if !empty(META_XTRAS)
+META_FILES += ${META_XTRAS:N\*.meta}
+.endif
.if !empty(META_FILES)
diff --git a/mk/host-target.mk b/mk/host-target.mk
index f9f22027c00f..d8abea17fdac 100644
--- a/mk/host-target.mk
+++ b/mk/host-target.mk
@@ -1,5 +1,5 @@
# RCSid:
-# $Id: host-target.mk,v 1.18 2023/05/22 23:08:31 sjg Exp $
+# $Id: host-target.mk,v 1.19 2023/09/21 06:44:53 sjg Exp $
# Host platform information; may be overridden
.if !target(__${.PARSEFILE}__)
@@ -48,9 +48,14 @@ HOST_TARGET := ${host_os:S,/,,g}${HOST_OSMAJOR}-${_HOST_ARCH}
# sometimes we want HOST_TARGET32
MACHINE32.amd64 = i386
MACHINE32.x86_64 = i386
+.if !defined(_HOST_ARCH32)
_HOST_ARCH32 := ${MACHINE32.${_HOST_ARCH}:U${_HOST_ARCH:S,64$,,}}
+.export _HOST_ARCH32
+.endif
HOST_TARGET32 := ${host_os:S,/,,g}${HOST_OSMAJOR}-${_HOST_ARCH32}
+.export HOST_TARGET HOST_TARGET32
+
# tr is insanely non-portable, accommodate the lowest common denominator
TR ?= tr
toLower = ${TR} 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz'
diff --git a/mk/init.mk b/mk/init.mk
index b740378cd69a..b0d365754d86 100644
--- a/mk/init.mk
+++ b/mk/init.mk
@@ -1,4 +1,4 @@
-# $Id: init.mk,v 1.27 2022/01/01 17:32:18 sjg Exp $
+# $Id: init.mk,v 1.30 2023/10/03 16:25:01 sjg Exp $
#
# @(#) Copyright (c) 2002, Simon J. Gerraty
#
@@ -32,6 +32,11 @@ _this_mk_dir := ${.PARSEDIR}
# should have been set by sys.mk
CXX_SUFFIXES?= .cc .cpp .cxx .C
+.if defined(PROG_CXX) || ${SRCS:Uno:${CXX_SUFFIXES:S,^,N*,:ts:}} != ${SRCS:Uno:N/}
+_CCLINK ?= ${CXX}
+.endif
+_CCLINK ?= ${CC}
+
.if !empty(WARNINGS_SET) || !empty(WARNINGS_SET_${MACHINE_ARCH})
.include <warnings.mk>
.endif
@@ -51,6 +56,7 @@ QUALIFIED_VAR_LIST += \
CPPFLAGS \
CPUFLAGS \
LDFLAGS \
+ SRCS \
# a final :U avoids errors if someone uses :=
.for V in ${QUALIFIED_VAR_LIST:O:u:@q@$q $q_LAST@}
diff --git a/mk/install-mk b/mk/install-mk
index b042f99671a8..357afd171e8c 100644..100755
--- a/mk/install-mk
+++ b/mk/install-mk
@@ -59,7 +59,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: install-mk,v 1.242 2023/09/09 16:00:16 sjg Exp $
+# $Id: install-mk,v 1.244 2024/01/05 23:16:34 sjg Exp $
#
# @(#) Copyright (c) 1994-2023 Simon J. Gerraty
#
@@ -74,7 +74,7 @@
# sjg@crufty.net
#
-MK_VERSION=20230909
+MK_VERSION=20240105
OWNER=
GROUP=
MODE=444
diff --git a/mk/jobs.mk b/mk/jobs.mk
index 0643e6481082..404ce6c3dadd 100644
--- a/mk/jobs.mk
+++ b/mk/jobs.mk
@@ -1,4 +1,4 @@
-# $Id: jobs.mk,v 1.14 2023/09/11 16:52:44 sjg Exp $
+# $Id: jobs.mk,v 1.16 2023/11/29 15:59:50 sjg Exp $
#
# @(#) Copyright (c) 2012-2023, Simon J. Gerraty
#
@@ -78,7 +78,8 @@ JOB_MAX = ${.MAKE.JOBS}
# This should be derrived from number of cpu's
.if ${.MAKE.JOBS.C:Uno} == "yes"
# 1.2 - 1.5 times nCPU works well on most machines that support -jC
-JOB_MAX_C ?= 1.33C
+# if the factor is floating point, the C suffix isn't needed
+JOB_MAX_C ?= 1.33
JOB_MAX ?= ${JOB_MAX_C}
.endif
JOB_MAX ?= 8
diff --git a/mk/lib.mk b/mk/lib.mk
index 2f97fc93e5c6..9c1c2b446d41 100644
--- a/mk/lib.mk
+++ b/mk/lib.mk
@@ -1,4 +1,4 @@
-# $Id: lib.mk,v 1.75 2023/09/11 05:20:23 sjg Exp $
+# $Id: lib.mk,v 1.81 2023/10/03 18:18:57 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@@ -62,6 +62,10 @@ META_NOECHO?= echo
# (usually just ${CPPPICFLAGS} ${CPICFLAGS})
# APICFLAGS: flags for ${AS} to assemble .[sS] to ${PICO} objects.
+# we simplify life by letting the toolchain do most of the work
+# _CCLINK is set by init.mk based on whether we are doing C++ or not
+SHLIB_LD ?= ${_CCLINK}
+
.if ${TARGET_OSNAME} == "NetBSD"
.if ${MACHINE_ARCH} == "alpha"
# Alpha-specific shared library flags
@@ -111,7 +115,7 @@ APICFLAGS?= -k
# Platform-independent linker flags for ELF shared libraries
.if ${OBJECT_FMT} == "ELF"
SHLIB_SOVERSION= ${SHLIB_MAJOR}
-SHLIB_SHFLAGS= -soname lib${LIB}.so.${SHLIB_SOVERSION}
+SHLIB_SHFLAGS= -Wl,-soname,lib${LIB}.so.${SHLIB_SOVERSION}
SHLIB_LDSTARTFILE?= /usr/lib/crtbeginS.o
SHLIB_LDENDFILE?= /usr/lib/crtendS.o
.endif
@@ -125,7 +129,7 @@ LD_shared=${SHLIB_SHFLAGS}
.if ${TARGET_OSNAME} == "FreeBSD"
.if ${OBJECT_FMT} == "ELF"
SHLIB_SOVERSION= ${SHLIB_MAJOR}
-SHLIB_SHFLAGS= -soname lib${LIB}.so.${SHLIB_SOVERSION}
+SHLIB_SHFLAGS= -Wl,-soname,lib${LIB}.so.${SHLIB_SOVERSION}
.else
SHLIB_SHFLAGS= -assert pure-text
.endif
@@ -168,7 +172,6 @@ AR_cq= -cqs
.elif ${TARGET_OSNAME} == "FreeBSD"
LD_solib= lib${LIB}_pic.a
.elif ${TARGET_OSNAME} == "Linux"
-SHLIB_LD = ${CC}
# this is ambiguous of course
LD_shared=-shared -Wl,-soname,lib${LIB}.so.${SHLIB_MAJOR}
LD_solib= -Wl,--whole-archive lib${LIB}_pic.a -Wl,--no-whole-archive
@@ -180,7 +183,6 @@ LD_pobjs = ${POBJS}
LD_sobjs = ${SOBJS}
.endif
.elif ${TARGET_OSNAME} == "Darwin"
-SHLIB_LD = ${CC}
SHLIB_INSTALL_VERSION ?= ${SHLIB_MAJOR}
SHLIB_COMPATABILITY_VERSION ?= ${SHLIB_MAJOR}.${SHLIB_MINOR:U0}
SHLIB_COMPATABILITY ?= \
@@ -213,8 +215,6 @@ PICFLAG ?= -fPIC -fno-common
RANLIB = :
.endif
-SHLIB_LD ?= ${LD}
-
.if !empty(SHLIB_MAJOR)
.if ${NEED_SOLINKS} && empty(SHLIB_LINKS)
.if ${MK_LINKLIB} != "no"
@@ -441,8 +441,6 @@ lib${LIB}_pic.a: ${SOBJS}
@${AR} ${AR_cq} ${.TARGET} ${LD_sobjs}
${RANLIB} ${.TARGET}
-#SHLIB_LDADD?= ${LDADD}
-
# bound to be non-portable...
# this is known to work for NetBSD 1.6 and FreeBSD 4.2
lib${LIB}.${LD_so}: ${SOLIB} ${DPADD}
@@ -450,10 +448,9 @@ lib${LIB}.${LD_so}: ${SOLIB} ${DPADD}
@rm -f ${.TARGET}
.if ${TARGET_OSNAME:NFreeBSD:NNetBSD} == ""
.if ${OBJECT_FMT} == "ELF"
- ${SHLIB_LD} -x -shared ${SHLIB_SHFLAGS} ${LDFLAGS} -o ${.TARGET} \
- ${SHLIB_LDSTARTFILE} \
- --whole-archive ${SOLIB} --no-whole-archive \
- ${LDADD} ${SHLIB_LDADD} ${SHLIB_LDENDFILE}
+ ${SHLIB_LD} -shared -Wl,-x ${SHLIB_SHFLAGS} ${LDFLAGS} -o ${.TARGET} \
+ -Wl,--whole-archive ${SOLIB} -Wl,--no-whole-archive \
+ ${LDADD} ${SHLIB_LDADD}
.else
${SHLIB_LD} ${LD_x} ${LD_shared} ${LDFLAGS} \
-o ${.TARGET} ${SOLIB} ${LDADD} ${SHLIB_LDADD}
@@ -480,7 +477,7 @@ cleanlib: .PHONY
rm -f a.out [Ee]rrs mklog core *.core ${CLEANFILES}
rm -f lib${LIB}.a ${OBJS}
rm -f lib${LIB}_p.a ${POBJS}
- rm -f lib${LIB}_pic.a lib${LIB}.so.*.* ${SOBJS}
+ rm -f lib${LIB}_pic.a lib${LIB}*${LD_solink} lib${LIB}*${LD_solink}.* ${SOBJS}
rm -f llib-l${LIB}.ln ${LOBJS}
.if !empty(SHLIB_LINKS)
rm -f ${SHLIB_LINKS}
diff --git a/mk/man.mk b/mk/man.mk
index ce4380fedc55..be1b7d98cd04 100644
--- a/mk/man.mk
+++ b/mk/man.mk
@@ -1,4 +1,4 @@
-# $Id: man.mk,v 1.25 2021/10/31 03:03:14 sjg Exp $
+# $Id: man.mk,v 1.26 2023/12/30 02:10:38 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@@ -16,13 +16,13 @@ OPTIONS_DEFAULT_NO += CMT2DOC
# so we have to use sed(1).
# set MANTARGET=cat for formatted pages
-MANTARGET?= man
+MANTARGET ?= man
# set this to .0 for same behavior as bsd.man.mk
-MCATEXT?=
+MCATEXT ?=
-NROFF?= nroff
-MANDIR?= /usr/share/man
-MANDOC?= man
+NROFF ?= nroff
+MANDIR ?= /usr/share/man
+MANDOC ?= man
MAN_SUFFIXES?= .1 .2 .3 .4 .5 .6 .7 .8 .9
.SUFFIXES: ${MAN_SUFFIXES}
@@ -35,8 +35,24 @@ ${MAN_SUFFIXES:@s@$s${s:S,.,.cat,}@}:
@${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T}.new && \
mv ${.TARGET:T}.new ${.TARGET:T}
+
+.if !empty(MANOWN)
+MAN_INSTALL_OWN ?= -o ${MANOWN} -g ${MANGRP}
+MAN_CHOWN ?= chown
+.else
+MAN_CHOWN = :
+.endif
+
+MINSTALL = ${INSTALL} ${COPY} ${MAN_INSTALL_OWN} -m ${MANMODE}
+
.if defined(MAN) && !empty(MAN)
+.if ${MANTARGET} == "cat"
+MANALL ?= ${MAN:T:@p@${p:R}.cat${p:E}@}
+.else
+MANALL ?= ${MAN}
+.endif
+
.if ${MK_CMT2DOC} == "yes"
# use cmt2doc.py to extract manpages from source
CMT2DOC?= cmt2doc.py
@@ -52,36 +68,65 @@ ${CMT2DOC_SUFFIXES:@s@${MAN_SUFFIXES:@m@$s$m@}@}:
.endif
-_mandir=${DESTDIR}${MANDIR}/${MANTARGET}`echo $$page | sed -e 's/.*\.cat/./' -e 's/.*\.//'`
+# none of this is relevant unless doing maninstall
+.if make(*install)
+_mandir = ${DESTDIR}${MANDIR}/${MANTARGET}`echo $$page | sed -e 's/.*\.cat/./' -e 's/.*\.//'`
.if ${MANTARGET} == "cat"
-_mfromdir?=.
-MANALL= ${MAN:${MAN_SUFFIXES:S,.,,:@m@S/.$m/.cat$m/@:ts:}}
+_mfromdir ?= .
.if ${MCATEXT} == ""
-_minstpage=`echo $$page | sed 's/\.cat/./'`
+_minstpage = `echo $$page | sed 's/\.cat/./'`
.else
-_minstpage=`echo $$page | sed 's/\.cat.*//'`${MCATEXT}
+_minstpage = `echo $$page | sed 's/\.cat.*//'`${MCATEXT}
.endif
.endif
.if target(${MAN:[1]})
-_mfromdir?=.
+_mfromdir ?= .
.endif
-_mfromdir?=${.CURDIR}
-MANALL?= ${MAN}
-_minstpage?=$${page}
+_mfromdir ?= ${.CURDIR}
+_minstpage ?= $${page}
.endif
-.if !empty(MANOWN)
-MAN_INSTALL_OWN ?= -o ${MANOWN} -g ${MANGRP}
-MAN_CHOWN ?= chown
+.if defined(MANZ)
+# chown and chmod are done afterward automatically
+MCOMPRESS_CMD ?= gzip -cf
+MCOMPRESS_EXT ?= .gz
+
+_MANZ_USE: .USE
+ @${MCOMPRESS_CMD} ${.ALLSRC} > ${.TARGET}
+
+.for _page in ${MANALL}
+${_page:T}${MCOMPRESS_EXT}: ${_page} _MANZ_USE
+.endfor
+.endif
+
+.if ${MK_STAGING_MAN} == "yes"
+_mansets := ${MAN:E:O:u:M*[1-9]:@s@man$s@}
+.if ${MANTARGET} == "cat"
+STAGE_AS_SETS += ${_mansets}
+_stage_man = stage_as
.else
-MAN_CHOWN = :
+STAGE_SETS += ${_mansets}
+_stage_man = stage_files
+.endif
+STAGE_TARGETS += ${_stage_man}
+.for _page _as in ${MANALL:@x@$x ${x:T:S/.cat/./}@}
+${_stage_man}.man${_as:E}: ${_page}
+.if target(${_page:T}${MCOMPRESS_EXT})
+${_man_stage}.man${_as:E}: ${_page:T}${MCOMPRESS_EXT}
+.endif
+STAGE_DIR.man${_as:E} ?= ${STAGE_OBJTOP}${MANDIR}/${MANTARGET}${_as:E}${MANSUBDIR}
+.if ${MANTARGET} == "cat"
+STAGE_AS_${_page} = ${_as}
+.endif
+.endfor
+.if !defined(NO_MLINKS) && !empty(MLINKS)
+STAGE_SETS += mlinks
+STAGE_TARGETS += stage_links
+STAGE_LINKS.mlinks := ${MLINKS:M*.[1-9]:@f@${f:S,^,${MANDIR}/${MANTARGET}${f:E}${MANSUBDIR}/,}@}
+stage_links.mlinks: ${_mansets:@s@stage_files.$s@}
+.endif
.endif
-MINSTALL= ${INSTALL} ${COPY} ${MAN_INSTALL_OWN} -m ${MANMODE}
-.if defined(MANZ)
-# chown and chmod are done afterward automatically
-MCOMPRESS= gzip -cf
-MCOMPRESSSUFFIX= .gz
.endif
maninstall:
diff --git a/mk/meta.autodep.mk b/mk/meta.autodep.mk
index 6785d2ebf874..11b5f17f4edc 100644
--- a/mk/meta.autodep.mk
+++ b/mk/meta.autodep.mk
@@ -1,4 +1,4 @@
-# $Id: meta.autodep.mk,v 1.59 2023/08/19 17:35:32 sjg Exp $
+# $Id: meta.autodep.mk,v 1.60 2024/01/09 23:42:22 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@@ -305,7 +305,7 @@ ${_DEPENDFILE}: .PRECIOUS
CLEANFILES += *.meta filemon.* *.db
# these make it easy to gather some stats
-now_utc = ${%s:L:localtime}
+now_utc ?= ${%s:L:localtime}
start_utc := ${now_utc}
meta_stats= meta=${empty(.MAKE.META.FILES):?0:${.MAKE.META.FILES:[#]}} \
diff --git a/mk/own.mk b/mk/own.mk
index 7b0d74caba8a..5f0eb0d09f2a 100644
--- a/mk/own.mk
+++ b/mk/own.mk
@@ -1,4 +1,4 @@
-# $Id: own.mk,v 1.44 2021/12/08 05:56:50 sjg Exp $
+# $Id: own.mk,v 1.45 2023/12/30 02:10:38 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@@ -119,6 +119,7 @@ OPTIONS_DEFAULT_DEPENDENT+= \
PICINSTALL/LINKLIB \
PICLIB/PIC \
PROFILE/LINKLIB \
+ STAGING_MAN/STAGING \
STAGING_PROG/STAGING \
.include <options.mk>
diff --git a/mk/prog.mk b/mk/prog.mk
index e01a92ba9a0e..9afa908d507f 100644
--- a/mk/prog.mk
+++ b/mk/prog.mk
@@ -1,4 +1,4 @@
-# $Id: prog.mk,v 1.39 2023/04/20 23:45:56 sjg Exp $
+# $Id: prog.mk,v 1.40 2023/10/02 21:35:43 sjg Exp $
.if !target(__${.PARSEFILE}__)
__${.PARSEFILE}__: .NOTMAIN
@@ -75,12 +75,9 @@ ${CXX_SUFFIXES:%=%.o}:
.if defined(PROG_CXX)
PROG= ${PROG_CXX}
-_CCLINK= ${CXX}
_SUPCXX?= -lstdc++ -lm
.endif
-_CCLINK?= ${CC}
-
.if defined(PROG)
BINDIR ?= ${prefix}/bin
diff --git a/mk/rst2htm.mk b/mk/rst2htm.mk
index 0a26abb9126e..d7e98d087fd1 100644
--- a/mk/rst2htm.mk
+++ b/mk/rst2htm.mk
@@ -1,4 +1,4 @@
-# $Id: rst2htm.mk,v 1.13 2023/09/13 18:55:42 sjg Exp $
+# $Id: rst2htm.mk,v 1.14 2023/09/19 22:27:28 sjg Exp $
#
# @(#) Copyright (c) 2009, Simon J. Gerraty
#
@@ -44,6 +44,7 @@ RST_SUFFIXES ?= .rst .txt
CLEANFILES += ${HTMFILES} ${PDFFILES}
html: ${HTMFILES}
+pdf: ${PDFFILES}
.SUFFIXES: ${RST_SUFFIXES} .htm .pdf
diff --git a/os.sh b/os.sh
index 78b2de95a679..78b2de95a679 100644..100755
--- a/os.sh
+++ b/os.sh
diff --git a/parse.c b/parse.c
index 85af2ced7a85..824def432753 100644
--- a/parse.c
+++ b/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.706 2023/08/19 11:09:02 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.716 2024/01/07 11:39:04 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -121,7 +121,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.706 2023/08/19 11:09:02 rillig Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.716 2024/01/07 11:39:04 rillig Exp $");
/* Detects a multiple-inclusion guard in a makefile. */
typedef enum {
@@ -131,9 +131,7 @@ typedef enum {
GS_NO /* the file is not guarded */
} GuardState;
-/*
- * A file being read.
- */
+/* A file being parsed. */
typedef struct IncludedFile {
FStr name; /* absolute or relative to the cwd */
unsigned lineno; /* 1-based */
@@ -251,7 +249,7 @@ static StringList targCmds = LST_INIT;
*/
static GNode *order_pred;
-static int parseErrors = 0;
+static int parseErrors;
/*
* The include chain of makefiles. At index 0 is the top-level makefile from
@@ -328,6 +326,23 @@ enum PosixState posix_state = PS_NOT_YET;
static HashTable /* full file name -> Guard */ guards;
+
+static List *
+Lst_New(void)
+{
+ List *list = bmake_malloc(sizeof *list);
+ Lst_Init(list);
+ return list;
+}
+
+static void
+Lst_Free(List *list)
+{
+
+ Lst_Done(list);
+ free(list);
+}
+
static IncludedFile *
GetInclude(size_t i)
{
@@ -335,7 +350,7 @@ GetInclude(size_t i)
return Vector_Get(&includes, i);
}
-/* The makefile that is currently being read. */
+/* The makefile or the body of a .for loop that is currently being read. */
static IncludedFile *
CurFile(void)
{
@@ -382,7 +397,7 @@ LoadFile(const char *path, int fd)
}
assert(buf.len <= buf.cap);
- if (!Buf_EndsWith(&buf, '\n'))
+ if (buf.len > 0 && !Buf_EndsWith(&buf, '\n'))
Buf_AddByte(&buf, '\n');
return buf; /* may not be null-terminated */
@@ -442,8 +457,8 @@ IsEscaped(const char *line, const char *p)
}
/*
- * Add the filename and lineno to the GNode so that we remember where its
- * last command was added or where it was mentioned in a .depend file.
+ * Remember the location (filename and lineno) where the last command was
+ * added or where the node was mentioned in a .depend file.
*/
static void
RememberLocation(GNode *gn)
@@ -566,7 +581,7 @@ ParseErrorInternal(const GNode *gn,
}
/*
- * Print a parse error message, including location information.
+ * Print a message, including location information.
*
* If the level is PARSE_FATAL, continue parsing until the end of the
* current top-level makefile, then exit (see Parse_File).
@@ -621,8 +636,7 @@ HandleMessage(ParseErrorLevel level, const char *levelName, const char *umsg)
/*
* Add the child to the parent's children, and for non-special targets, vice
- * versa. Special targets such as .END do not need to be informed once the
- * child target has been made.
+ * versa.
*/
static void
LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
@@ -633,7 +647,10 @@ LinkSource(GNode *pgn, GNode *cgn, bool isSpecial)
Lst_Append(&pgn->children, cgn);
pgn->unmade++;
- /* Special targets like .END don't need any children. */
+ /*
+ * Special targets like .END do not need to be informed once the child
+ * target has been made.
+ */
if (!isSpecial)
Lst_Append(&cgn->parents, pgn);
@@ -672,10 +689,9 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
if (op == OP_DOUBLEDEP && (gn->type & OP_OPMASK) == OP_DOUBLEDEP) {
/*
* If the node was on the left-hand side of a '::' operator,
- * we need to create a new instance of it for the children
- * and commands on this dependency line since each of these
- * dependency groups has its own attributes and commands,
- * separate from the others.
+ * create a new node for the children and commands on this
+ * dependency line, since each of these dependency groups has
+ * its own attributes and commands, separate from the others.
*
* The new instance is placed on the 'cohorts' list of the
* initial one (note the initial one is not on its own
@@ -694,7 +710,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
if (doing_depend)
RememberLocation(cohort);
/*
- * Make the cohort invisible as well to avoid duplicating it
+ * Make the cohort invisible to avoid duplicating it
* into other variables. True, parents of this target won't
* tend to do anything with their local variables, but better
* safe than sorry.
@@ -709,11 +725,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d",
(unsigned int)gn->unmade_cohorts % 1000000);
} else {
- /*
- * We don't want to nuke any previous flags (whatever they
- * were) so we just OR the new operator into the old.
- */
- gn->type |= op;
+ gn->type |= op; /* preserve any previous flags */
}
return true;
@@ -730,12 +742,12 @@ ApplyDependencyOperator(GNodeType op)
}
/*
- * We add a .WAIT node in the dependency list. After any dynamic dependencies
+ * Add a .WAIT node in the dependency list. After any dynamic dependencies
* (and filename globbing) have happened, it is given a dependency on each
* previous child, back until the previous .WAIT node. The next child won't
* be scheduled until the .WAIT node is built.
*
- * We give each .WAIT node a unique name (mainly for diagnostics).
+ * Give each .WAIT node a unique name (mainly for diagnostics).
*/
static void
ApplyDependencySourceWait(bool isSpecial)
@@ -819,9 +831,7 @@ ApplyDependencySourceOrder(const char *src)
Targ_PrintNode(gn, 0);
}
}
- /*
- * The current source now becomes the predecessor for the next one.
- */
+ /* The current source now becomes the predecessor for the next one. */
order_pred = gn;
}
@@ -879,7 +889,8 @@ MaybeUpdateMainTarget(void)
for (ln = targets->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
if (GNode_IsMainCandidate(gn)) {
- DEBUG1(MAKE, "Setting main node to \"%s\"\n", gn->name);
+ DEBUG1(MAKE, "Setting main node to \"%s\"\n",
+ gn->name);
mainNode = gn;
return;
}
@@ -901,38 +912,31 @@ InvalidLineType(const char *line, const char *unexpanded_line)
} else if (strcmp(line, unexpanded_line) == 0)
Parse_Error(PARSE_FATAL, "Invalid line '%s'", line);
else
- Parse_Error(PARSE_FATAL, "Invalid line '%s', expanded to '%s'",
+ Parse_Error(PARSE_FATAL,
+ "Invalid line '%s', expanded to '%s'",
unexpanded_line, line);
}
static void
ParseDependencyTargetWord(char **pp, const char *lstart)
{
- const char *cp = *pp;
+ const char *p = *pp;
- while (*cp != '\0') {
- if ((ch_isspace(*cp) || *cp == '!' || *cp == ':' ||
- *cp == '(') &&
- !IsEscaped(lstart, cp))
+ while (*p != '\0') {
+ if ((ch_isspace(*p) || *p == '!' || *p == ':' || *p == '(')
+ && !IsEscaped(lstart, p))
break;
- if (*cp == '$') {
- /*
- * Must be a dynamic source (would have been expanded
- * otherwise).
- *
- * There should be no errors in this, as they would
- * have been discovered in the initial Var_Subst and
- * we wouldn't be here.
- */
- FStr val = Var_Parse(&cp, SCOPE_CMDLINE,
+ if (*p == '$') {
+ FStr val = Var_Parse(&p, SCOPE_CMDLINE,
VARE_PARSE_ONLY);
+ /* TODO: handle errors */
FStr_Done(&val);
} else
- cp++;
+ p++;
}
- *pp += cp - *pp;
+ *pp += p - *pp;
}
/*
@@ -1359,6 +1363,7 @@ ParseDependencySourceSpecial(ParseSpecial special, const char *word,
Suff_AddSuffix(word);
break;
case SP_PATH:
+ case SP_SYSPATH:
AddToPaths(word, paths);
break;
case SP_INCLUDES:
@@ -1379,9 +1384,6 @@ ParseDependencySourceSpecial(ParseSpecial special, const char *word,
case SP_READONLY:
Var_ReadOnly(word, true);
break;
- case SP_SYSPATH:
- AddToPaths(word, paths);
- break;
default:
break;
}
@@ -1392,7 +1394,7 @@ ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special,
GNodeType *inout_targetAttr,
SearchPathList **inout_paths)
{
- char savec = *nameEnd;
+ char savedNameEnd = *nameEnd;
*nameEnd = '\0';
if (!HandleDependencyTarget(name, inout_special,
@@ -1404,7 +1406,7 @@ ApplyDependencyTarget(char *name, char *nameEnd, ParseSpecial *inout_special,
else if (*inout_special == SP_PATH && *name != '.' && *name != '\0')
Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", name);
- *nameEnd = savec;
+ *nameEnd = savedNameEnd;
return true;
}
@@ -1466,17 +1468,17 @@ static void
ParseDependencySourcesSpecial(char *start,
ParseSpecial special, SearchPathList *paths)
{
- char savec;
while (*start != '\0') {
+ char savedEnd;
char *end = start;
while (*end != '\0' && !ch_isspace(*end))
end++;
- savec = *end;
+ savedEnd = *end;
*end = '\0';
ParseDependencySourceSpecial(special, start, paths);
- *end = savec;
- if (savec != '\0')
+ *end = savedEnd;
+ if (savedEnd != '\0')
end++;
pp_skip_whitespace(&end);
start = end;
@@ -1505,17 +1507,13 @@ ParseDependencySourcesMundane(char *start,
* rest of the line is the value.
*/
if (Parse_IsVar(start, &var)) {
- /*
- * Check if this makefile has disabled
- * setting local variables.
- */
- bool target_vars = GetBooleanExpr(
+ bool targetVarsEnabled = GetBooleanExpr(
"${.MAKE.TARGET_LOCAL_VARIABLES}", true);
- if (target_vars)
+ if (targetVarsEnabled)
LinkVarToTargets(&var);
free(var.varname);
- if (target_vars)
+ if (targetVarsEnabled)
return true;
}
@@ -1594,7 +1592,6 @@ ParseDependencySources(char *p, GNodeType targetAttr,
return;
}
- /* Now go for the sources. */
switch (special) {
case SP_INCLUDES:
case SP_LIBS:
@@ -1766,10 +1763,7 @@ Parse_IsVar(const char *p, VarAssign *out_var)
nameStart = p;
firstSpace = NULL;
- /*
- * Scan for one of the assignment operators outside a variable
- * expansion.
- */
+ /* Scan for one of the assignment operators outside an expression. */
while (*p != '\0') {
char ch = *p++;
if (ch == '(' || ch == '{') {
@@ -1821,14 +1815,14 @@ 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 *scope)
+VarCheckSyntax(VarAssignOp op, const char *uvalue, GNode *scope)
{
if (opts.strict) {
- if (type != VAR_SUBST && strchr(uvalue, '$') != NULL) {
- char *expandedValue = Var_Subst(uvalue,
+ if (op != VAR_SUBST && strchr(uvalue, '$') != NULL) {
+ char *parsedValue = Var_Subst(uvalue,
scope, VARE_PARSE_ONLY);
/* TODO: handle errors */
- free(expandedValue);
+ free(parsedValue);
}
}
}
@@ -1841,7 +1835,7 @@ VarAssign_EvalSubst(GNode *scope, const char *name, const char *uvalue,
char *evalue;
/*
- * make sure that we set the variable the first time to nothing
+ * Make sure that we set the variable the first time to nothing
* so that it gets substituted.
*
* TODO: Add a test that demonstrates why this code is needed,
@@ -1952,7 +1946,7 @@ Parse_Var(VarAssign *var, GNode *scope)
/*
- * See if the command possibly calls a sub-make by using the variable
+ * See if the command possibly calls a sub-make by using the
* expressions ${.MAKE}, ${MAKE} or the plain word "make".
*/
static bool
@@ -1991,16 +1985,10 @@ MaybeSubMake(const char *cmd)
return false;
}
-/*
- * Append the command to the target node.
- *
- * The node may be marked as a submake node if the command is determined to
- * be that.
- */
+/* Append the command to the target node. */
static void
GNode_AddCommand(GNode *gn, char *cmd)
{
- /* Add to last (ie current) cohort for :: targets */
if ((gn->type & OP_DOUBLEDEP) && gn->cohorts.last != NULL)
gn = gn->cohorts.last->datum;
@@ -2011,36 +1999,16 @@ GNode_AddCommand(GNode *gn, char *cmd)
gn->type |= OP_SUBMAKE;
RememberLocation(gn);
} else {
-#if 0
- /* XXX: We cannot do this until we fix the tree */
- Lst_Append(&gn->commands, cmd);
- Parse_Error(PARSE_WARNING,
- "overriding commands for target \"%s\"; "
- "previous commands defined at %s: %u ignored",
- gn->name, gn->fname, gn->lineno);
-#else
Parse_Error(PARSE_WARNING,
"duplicate script for target \"%s\" ignored",
gn->name);
ParseErrorInternal(gn, PARSE_WARNING,
"using previous script for \"%s\" defined here",
gn->name);
-#endif
}
}
/*
- * Add a directory to the path searched for included makefiles bracketed
- * by double-quotes.
- */
-void
-Parse_AddIncludeDir(const char *dir)
-{
- (void)SearchPath_Add(parseIncPath, dir);
-}
-
-
-/*
* Parse a directive like '.include' or '.-include'.
*
* .include "user-makefile.mk"
@@ -2063,13 +2031,9 @@ ParseInclude(char *directive)
return;
}
- if (*p++ == '<')
- endc = '>';
- else
- endc = '"';
+ endc = *p++ == '<' ? '>' : '"';
file = FStr_InitRefer(p);
- /* Skip to matching delimiter */
while (*p != '\0' && *p != endc)
p++;
@@ -2281,7 +2245,7 @@ IsSysVInclude(const char *line)
static void
ParseTraditionalInclude(char *line)
{
- char *cp; /* current position in file spec */
+ char *p; /* current position in file spec */
bool done = false;
bool silent = line[0] != 'i';
char *file = line + (silent ? 8 : 7);
@@ -2294,13 +2258,13 @@ ParseTraditionalInclude(char *line)
all_files = Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES);
/* TODO: handle errors */
- for (file = all_files; !done; file = cp + 1) {
+ for (file = all_files; !done; file = p + 1) {
/* Skip to end of line or next whitespace */
- for (cp = file; *cp != '\0' && !ch_isspace(*cp); cp++)
+ for (p = file; *p != '\0' && !ch_isspace(*p); p++)
continue;
- if (*cp != '\0')
- *cp = '\0';
+ if (*p != '\0')
+ *p = '\0';
else
done = true;
@@ -2345,9 +2309,8 @@ ParseGmakeExport(char *line)
#endif
/*
- * Called when EOF is reached in the current file. If we were reading an
- * include file or a .for loop, the includes stack is popped and things set
- * up to go back to reading the previous file at the previous location.
+ * When the end of the current file or .for loop is reached, continue reading
+ * the previous file at the previous location.
*
* Results:
* true to continue parsing, i.e. it had only reached the end of an
@@ -2610,23 +2573,9 @@ SkipIrrelevantBranches(void)
{
const char *line;
- while ((line = ReadLowLevelLine(LK_DOT)) != NULL) {
+ while ((line = ReadLowLevelLine(LK_DOT)) != NULL)
if (Cond_EvalLine(line) == CR_TRUE)
return true;
- /*
- * TODO: Check for typos in .elif directives such as .elsif
- * or .elseif.
- *
- * This check will probably duplicate some of the code in
- * ParseLine. Most of the code there cannot apply, only
- * ParseVarassign and ParseDependencyLine can, and to prevent
- * code duplication, these would need to be called with a
- * flag called onlyCheckSyntax.
- *
- * See directive-elif.mk for details.
- */
- }
-
return false;
}
@@ -2665,9 +2614,9 @@ ParseForLoop(const char *line)
/*
* Read an entire line from the input file.
*
- * Empty lines, .if and .for are completely handled by this function,
- * leaving only variable assignments, other directives, dependency lines
- * and shell commands to the caller.
+ * Empty lines, .if and .for are handled by this function, while variable
+ * assignments, other directives, dependency lines and shell commands are
+ * handled by the caller.
*
* Return a line without trailing whitespace, or NULL for EOF. The returned
* string will be freed at the end of including the file.
@@ -2800,26 +2749,26 @@ HandleBreak(const char *arg)
static bool
ParseDirective(char *line)
{
- char *cp = line + 1;
+ char *p = line + 1;
const char *arg;
Substring dir;
- pp_skip_whitespace(&cp);
- if (IsInclude(cp, false)) {
- ParseInclude(cp);
+ pp_skip_whitespace(&p);
+ if (IsInclude(p, false)) {
+ ParseInclude(p);
return true;
}
- dir.start = cp;
- while (ch_islower(*cp) || *cp == '-')
- cp++;
- dir.end = cp;
+ dir.start = p;
+ while (ch_islower(*p) || *p == '-')
+ p++;
+ dir.end = p;
- if (*cp != '\0' && !ch_isspace(*cp))
+ if (*p != '\0' && !ch_isspace(*p))
return false;
- pp_skip_whitespace(&cp);
- arg = cp;
+ pp_skip_whitespace(&p);
+ arg = p;
if (Substring_Equals(dir, "break"))
HandleBreak(arg);
@@ -2880,7 +2829,7 @@ Parse_GuardEndif(void)
static char *
FindSemicolon(char *p)
{
- int level = 0;
+ int depth = 0;
for (; *p != '\0'; p++) {
if (*p == '\\' && p[1] != '\0') {
@@ -2889,19 +2838,15 @@ FindSemicolon(char *p)
}
if (*p == '$' && (p[1] == '(' || p[1] == '{'))
- level++;
- else if (level > 0 && (*p == ')' || *p == '}'))
- level--;
- else if (level == 0 && *p == ';')
+ depth++;
+ else if (depth > 0 && (*p == ')' || *p == '}'))
+ depth--;
+ else if (depth == 0 && *p == ';')
break;
}
return p;
}
-/*
- * dependency -> [target...] op [source...] [';' command]
- * op -> ':' | '::' | '!'
- */
static void
ParseDependencyLine(char *line)
{
@@ -2909,11 +2854,6 @@ ParseDependencyLine(char *line)
char *expanded_line;
const char *shellcmd = NULL;
- /*
- * For some reason - probably to make the parser impossible -
- * a ';' can be used to separate commands from dependencies.
- * Attempt to skip over ';' inside substitution patterns.
- */
{
char *semicolon = FindSemicolon(line);
if (*semicolon != '\0') {
@@ -2924,7 +2864,7 @@ ParseDependencyLine(char *line)
}
/*
- * We now know it's a dependency line so it needs to have all
+ * We now know it's a dependency line, so it needs to have all
* variables expanded before being parsed.
*
* XXX: Ideally the dependency line would first be split into
@@ -2935,7 +2875,7 @@ ParseDependencyLine(char *line)
* as well.
*
* Parsing the line first would also prevent that targets
- * generated from variable expressions are interpreted as the
+ * generated from expressions are interpreted as the
* dependency operator, such as in "target${:U\:} middle: source",
* in which the middle is interpreted as a source, not a target.
*/
@@ -2975,13 +2915,6 @@ ParseDependencyLine(char *line)
static void
ParseLine(char *line)
{
- /*
- * Lines that begin with '.' can be pretty much anything:
- * - directives like '.include' or '.if',
- * - suffix rules like '.c.o:',
- * - dependencies for filenames that start with '.',
- * - variable assignments like '.tmp=value'.
- */
if (line[0] == '.' && ParseDirective(line))
return;
@@ -2992,9 +2925,6 @@ ParseLine(char *line)
#ifdef SYSVINCLUDE
if (IsSysVInclude(line)) {
- /*
- * It's an S3/S5-style "include".
- */
ParseTraditionalInclude(line);
return;
}
@@ -3003,9 +2933,6 @@ ParseLine(char *line)
#ifdef GMAKEEXPORT
if (strncmp(line, "export", 6) == 0 && ch_isspace(line[6]) &&
strchr(line, ':') == NULL) {
- /*
- * It's a Gmake "export".
- */
ParseGmakeExport(line);
return;
}
@@ -3019,10 +2946,7 @@ ParseLine(char *line)
ParseDependencyLine(line);
}
-/*
- * Parse a top-level makefile, incorporating its content into the global
- * dependency graph.
- */
+/* Interpret a top-level makefile. */
void
Parse_File(const char *name, int fd)
{
@@ -3043,7 +2967,6 @@ Parse_File(const char *name, int fd)
CurFile()->lineno, line);
ParseLine(line);
}
- /* Reached EOF, but it may be just EOF of an include file. */
} while (ParseEOF());
FinishDependencyGroup();
diff --git a/str.c b/str.c
index 2797e5567e92..1349831af2f1 100644
--- a/str.c
+++ b/str.c
@@ -1,4 +1,4 @@
-/* $NetBSD: str.c,v 1.99 2023/06/23 05:03:04 rillig Exp $ */
+/* $NetBSD: str.c,v 1.102 2024/01/05 23:22:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -71,7 +71,7 @@
#include "make.h"
/* "@(#)str.c 5.8 (Berkeley) 6/1/90" */
-MAKE_RCSID("$NetBSD: str.c,v 1.99 2023/06/23 05:03:04 rillig Exp $");
+MAKE_RCSID("$NetBSD: str.c,v 1.102 2024/01/05 23:22:06 rillig Exp $");
static HashTable interned_strings;
@@ -107,6 +107,10 @@ str_concat3(const char *s1, const char *s2, const char *s3)
* Fracture a string into an array of words (as delineated by tabs or spaces)
* taking quotation marks into account.
*
+ * A string that is empty or only contains whitespace nevertheless results in
+ * a single word. This is unexpected in many places, and the caller needs to
+ * correct for this edge case.
+ *
* If expand is true, quotes are removed and escape sequences such as \r, \t,
* etc... are expanded. In this case, return NULL on parse errors.
*
@@ -322,17 +326,13 @@ StrMatchResult
Str_Match(const char *str, const char *pat)
{
StrMatchResult res = { NULL, false };
- const char *fixed_str, *fixed_pat;
- bool asterisk, matched;
-
- asterisk = false;
- fixed_str = str;
- fixed_pat = pat;
+ bool asterisk = false;
+ const char *fixed_str = str;
+ const char *fixed_pat = pat;
match_fixed_length:
str = fixed_str;
pat = fixed_pat;
- matched = false;
for (; *pat != '\0' && *pat != '*'; str++, pat++) {
if (*str == '\0')
return res;
@@ -350,7 +350,7 @@ match_fixed_length:
if (*pat == ']' || *pat == '\0') {
if (neg)
goto end_of_char_list;
- goto match_done;
+ goto no_match;
}
if (*pat == *str)
goto end_of_char_list;
@@ -369,7 +369,7 @@ match_fixed_length:
end_of_char_list:
if (neg && *pat != ']' && *pat != '\0')
- goto match_done;
+ goto no_match;
while (*pat != ']' && *pat != '\0')
pat++;
if (*pat == '\0')
@@ -379,43 +379,40 @@ match_fixed_length:
if (*pat == '\\') /* match the next character exactly */
pat++;
- if (*pat != *str)
- goto match_done;
+ if (*pat != *str) {
+ if (asterisk && str == fixed_str) {
+ while (*str != '\0' && *str != *pat)
+ str++;
+ fixed_str = str;
+ goto match_fixed_length;
+ }
+ goto no_match;
+ }
}
- matched = true;
-match_done:
- if (!asterisk) {
- if (!matched)
- return res;
- if (*pat == '\0') {
- res.matched = *str == '\0';
- return res;
- }
+ if (*pat == '*') {
asterisk = true;
- } else {
- if (!matched) {
- fixed_str++;
- goto match_fixed_length;
- }
+ while (*pat == '*')
+ pat++;
if (*pat == '\0') {
- if (*str == '\0') {
- res.matched = true;
- return res;
- }
- fixed_str += strlen(str);
- goto match_fixed_length;
+ res.matched = true;
+ return res;
}
+ fixed_str = str;
+ fixed_pat = pat;
+ goto match_fixed_length;
+ }
+ if (asterisk && *str != '\0') {
+ fixed_str += strlen(str);
+ goto match_fixed_length;
}
+ res.matched = *str == '\0';
+ return res;
- while (*pat == '*')
- pat++;
- if (*pat == '\0') {
- res.matched = true;
+no_match:
+ if (!asterisk)
return res;
- }
- fixed_str = str;
- fixed_pat = pat;
+ fixed_str++;
goto match_fixed_length;
}
diff --git a/str.h b/str.h
index 71fd6bd15944..6bdfbf4d497f 100644
--- a/str.h
+++ b/str.h
@@ -1,4 +1,4 @@
-/* $NetBSD: str.h,v 1.17 2023/06/23 04:56:54 rillig Exp $ */
+/* $NetBSD: str.h,v 1.19 2024/01/05 21:56:55 rillig Exp $ */
/*
Copyright (c) 2021 Roland Illig <rillig@NetBSD.org>
@@ -76,27 +76,24 @@ typedef struct StrMatchResult {
} StrMatchResult;
+/* Return a string that is the sole owner of str. */
MAKE_INLINE FStr
-FStr_Init(const char *str, void *freeIt)
+FStr_InitOwn(char *str)
{
FStr fstr;
fstr.str = str;
- fstr.freeIt = freeIt;
+ fstr.freeIt = str;
return fstr;
}
-/* Return a string that is the sole owner of str. */
-MAKE_INLINE FStr
-FStr_InitOwn(char *str)
-{
- return FStr_Init(str, str);
-}
-
/* Return a string that refers to the shared str. */
MAKE_INLINE FStr
FStr_InitRefer(const char *str)
{
- return FStr_Init(str, NULL);
+ FStr fstr;
+ fstr.str = str;
+ fstr.freeIt = NULL;
+ return fstr;
}
MAKE_INLINE void
@@ -154,14 +151,6 @@ Substring_Eq(Substring sub, Substring str)
memcmp(sub.start, str.start, len) == 0;
}
-MAKE_STATIC Substring
-Substring_Sub(Substring sub, size_t start, size_t end)
-{
- assert(start <= Substring_Length(sub));
- assert(end <= Substring_Length(sub));
- return Substring_Init(sub.start + start, sub.start + end);
-}
-
MAKE_STATIC bool
Substring_HasPrefix(Substring sub, Substring prefix)
{
@@ -198,7 +187,7 @@ Substring_SkipFirst(Substring sub, char ch)
}
MAKE_STATIC const char *
-Substring_LastIndex(Substring sub, char ch)
+Substring_FindLast(Substring sub, char ch)
{
const char *p;
diff --git a/suff.c b/suff.c
index 01ae43cb21b4..e5f6ee83e86a 100644
--- a/suff.c
+++ b/suff.c
@@ -1,4 +1,4 @@
-/* $NetBSD: suff.c,v 1.368 2023/02/14 21:38:31 rillig Exp $ */
+/* $NetBSD: suff.c,v 1.377 2024/01/05 23:22:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -115,7 +115,7 @@
#include "dir.h"
/* "@(#)suff.c 8.4 (Berkeley) 3/21/94" */
-MAKE_RCSID("$NetBSD: suff.c,v 1.368 2023/02/14 21:38:31 rillig Exp $");
+MAKE_RCSID("$NetBSD: suff.c,v 1.377 2024/01/05 23:22:06 rillig Exp $");
typedef List SuffixList;
typedef ListNode SuffixListNode;
@@ -142,8 +142,6 @@ static GNodeList transforms = LST_INIT;
*/
static int sNum = 0;
-typedef List SuffixListList;
-
/*
* A suffix such as ".c" or ".o" that may be used in suffix transformation
* rules such as ".c.o:".
@@ -185,14 +183,6 @@ typedef struct Suffix {
SuffixList parents;
/* Suffixes we have a transformation from */
SuffixList children;
- /*
- * Lists in which this suffix is referenced.
- *
- * XXX: These lists are used nowhere, they are just appended to, for
- * no apparent reason. They do have the side effect of increasing
- * refCount though.
- */
- SuffixListList ref;
} Suffix;
/*
@@ -374,7 +364,6 @@ SuffixList_Unref(SuffixList *list, Suffix *suff)
}
}
-/* Free up all memory associated with the given suffix structure. */
static void
Suffix_Free(Suffix *suff)
{
@@ -392,7 +381,6 @@ Suffix_Free(Suffix *suff)
suff->name, suff->refCount);
#endif
- Lst_Done(&suff->ref);
Lst_Done(&suff->children);
Lst_Done(&suff->parents);
SearchPath_Free(suff->searchPath);
@@ -401,12 +389,6 @@ Suffix_Free(Suffix *suff)
free(suff);
}
-static void
-SuffFree(void *p)
-{
- Suffix_Free(p);
-}
-
/* Remove the suffix from the list, and free if it is otherwise unused. */
static void
SuffixList_Remove(SuffixList *list, Suffix *suff)
@@ -416,7 +398,7 @@ SuffixList_Remove(SuffixList *list, Suffix *suff)
/* XXX: can lead to suff->refCount == -1 */
SuffixList_Unref(&sufflist, suff);
DEBUG1(SUFF, "Removing suffix \"%s\"\n", suff->name);
- SuffFree(suff);
+ Suffix_Free(suff);
}
}
@@ -440,12 +422,10 @@ SuffixList_Insert(SuffixList *list, Suffix *suff)
DEBUG2(SUFF, "inserting \"%s\" (%d) at end of list\n",
suff->name, suff->sNum);
Lst_Append(list, Suffix_Ref(suff));
- Lst_Append(&suff->ref, list);
} else if (listSuff->sNum != suff->sNum) {
DEBUG4(SUFF, "inserting \"%s\" (%d) before \"%s\" (%d)\n",
suff->name, suff->sNum, listSuff->name, listSuff->sNum);
Lst_InsertBefore(list, ln, Suffix_Ref(suff));
- Lst_Append(&suff->ref, list);
} else {
DEBUG2(SUFF, "\"%s\" (%d) is already there\n",
suff->name, suff->sNum);
@@ -469,7 +449,6 @@ Suffix_New(const char *name)
suff->searchPath = SearchPath_New();
Lst_Init(&suff->children);
Lst_Init(&suff->parents);
- Lst_Init(&suff->ref);
suff->sNum = sNum++;
suff->include = false;
suff->library = false;
@@ -496,7 +475,7 @@ Suff_ClearSuffixes(void)
Lst_Init(&sufflist);
sNum = 0;
if (nullSuff != NULL)
- SuffFree(nullSuff);
+ Suffix_Free(nullSuff);
emptySuff = nullSuff = Suffix_New("");
SearchPath_AddAll(nullSuff->searchPath, &dirSearchPath);
@@ -821,19 +800,7 @@ UpdateTargets(Suffix *suff)
}
}
-/*
- * Add the suffix to the end of the list of known suffixes.
- * Should we restructure the suffix graph? Make doesn't.
- *
- * A GNode is created for the suffix (XXX: this sounds completely wrong) and
- * a Suffix structure is created and added to the suffixes list unless the
- * suffix was already known.
- * The mainNode passed can be modified if a target mutated into a
- * transform and that target happened to be the main target.
- *
- * Input:
- * name the name of the suffix to add
- */
+/* Add the suffix to the end of the list of known suffixes. */
void
Suff_AddSuffix(const char *name)
{
@@ -1247,9 +1214,7 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
if (!Dir_HasWildcards(cgn->name))
return;
- /*
- * Expand the word along the chosen path
- */
+ /* Expand the word along the chosen path. */
Lst_Init(&expansions);
SearchPath_Expand(Suff_FindPath(cgn), cgn->name, &expansions);
@@ -1258,10 +1223,10 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
/*
* Fetch next expansion off the list and find its GNode
*/
- char *cp = Lst_Dequeue(&expansions);
+ char *name = Lst_Dequeue(&expansions);
- DEBUG1(SUFF, "%s...", cp);
- gn = Targ_GetNode(cp);
+ DEBUG1(SUFF, "%s...", name);
+ gn = Targ_GetNode(name);
/* Insert gn before the original child. */
Lst_InsertBefore(&pgn->children, cln, gn);
@@ -1274,8 +1239,8 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
DEBUG0(SUFF, "\n");
/*
- * Now the source is expanded, remove it from the list of children to
- * keep it from being processed.
+ * Now that the source is expanded, remove it from the list of
+ * children, to keep it from being processed.
*/
pgn->unmade--;
Lst_Remove(&pgn->children, cln);
@@ -1287,57 +1252,56 @@ ExpandWildcards(GNodeListNode *cln, GNode *pgn)
* add those nodes to the members list.
*
* Unfortunately, we can't use Str_Words because it doesn't understand about
- * variable expressions with spaces in them.
+ * expressions with spaces in them.
*/
static void
-ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
+ExpandChildrenRegular(char *p, GNode *pgn, GNodeList *members)
{
char *start;
- pp_skip_hspace(&cp);
- start = cp;
- while (*cp != '\0') {
- if (*cp == ' ' || *cp == '\t') {
+ pp_skip_hspace(&p);
+ start = p;
+ while (*p != '\0') {
+ if (*p == ' ' || *p == '\t') {
GNode *gn;
/*
* White-space -- terminate element, find the node,
* add it, skip any further spaces.
*/
- *cp++ = '\0';
+ *p++ = '\0';
gn = Targ_GetNode(start);
Lst_Append(members, gn);
- pp_skip_hspace(&cp);
+ pp_skip_hspace(&p);
/* Continue at the next non-space. */
- start = cp;
- } else if (*cp == '$') {
- /* Skip over the variable expression. */
- const char *nested_p = cp;
+ start = p;
+ } else if (*p == '$') {
+ /* Skip over the expression. */
+ const char *nested_p = p;
FStr junk = Var_Parse(&nested_p, pgn, VARE_PARSE_ONLY);
/* TODO: handle errors */
if (junk.str == var_Error) {
Parse_Error(PARSE_FATAL,
- "Malformed variable expression at \"%s\"",
- cp);
- cp++;
+ "Malformed expression at \"%s\"", p);
+ p++;
} else {
- cp += nested_p - cp;
+ p += nested_p - p;
}
FStr_Done(&junk);
- } else if (cp[0] == '\\' && cp[1] != '\0') {
+ } else if (p[0] == '\\' && p[1] != '\0') {
/* Escaped something -- skip over it. */
/*
* XXX: In other places, escaping at this syntactical
* position is done by a '$', not a '\'. The '\' is
* only used in variable modifiers.
*/
- cp += 2;
+ p += 2;
} else {
- cp++;
+ p++;
}
}
- if (cp != start) {
+ if (p != start) {
/*
* Stuff left over -- add it to the list too
*/
@@ -1347,7 +1311,7 @@ ExpandChildrenRegular(char *cp, GNode *pgn, GNodeList *members)
}
/*
- * Expand the names of any children of a given node that contain variable
+ * Expand the names of any children of a given node that contain
* expressions or file wildcards into actual targets.
*
* The expanded node is removed from the parent's list of children, and the
@@ -1361,7 +1325,7 @@ static void
ExpandChildren(GNodeListNode *cln, GNode *pgn)
{
GNode *cgn = cln->datum;
- char *cp; /* Expanded value */
+ char *expanded;
if (!Lst_IsEmpty(&cgn->order_pred) || !Lst_IsEmpty(&cgn->order_succ))
/* It is all too hard to process the result of .ORDER */
@@ -1383,7 +1347,7 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
}
DEBUG1(SUFF, "Expanding \"%s\"...", cgn->name);
- cp = Var_Subst(cgn->name, pgn, VARE_UNDEFERR);
+ expanded = Var_Subst(cgn->name, pgn, VARE_UNDEFERR);
/* TODO: handle errors */
{
@@ -1395,39 +1359,32 @@ ExpandChildren(GNodeListNode *cln, GNode *pgn)
* call on the Arch module to find the nodes for us,
* expanding variables in the parent's scope.
*/
- char *p = cp;
- (void)Arch_ParseArchive(&p, &members, pgn);
+ char *ap = expanded;
+ (void)Arch_ParseArchive(&ap, &members, pgn);
} else {
- ExpandChildrenRegular(cp, pgn, &members);
+ ExpandChildrenRegular(expanded, pgn, &members);
}
- /*
- * Add all elements of the members list to the parent node.
- */
+ /* Add all members to the parent node. */
while (!Lst_IsEmpty(&members)) {
GNode *gn = Lst_Dequeue(&members);
DEBUG1(SUFF, "%s...", gn->name);
- /*
- * Add gn to the parents child list before the
- * original child.
- */
Lst_InsertBefore(&pgn->children, cln, gn);
Lst_Append(&gn->parents, pgn);
pgn->unmade++;
- /* Expand wildcards on new node */
ExpandWildcards(cln->prev, pgn);
}
Lst_Done(&members);
- free(cp);
+ free(expanded);
}
DEBUG0(SUFF, "\n");
/*
- * Now the source is expanded, remove it from the list of children to
- * keep it from being processed.
+ * The source is expanded now, so remove it from the list of children,
+ * to keep it from being processed.
*/
pgn->unmade--;
Lst_Remove(&pgn->children, cln);
@@ -1446,16 +1403,10 @@ ExpandAllChildren(GNode *gn)
}
/*
- * Find a path along which to expand the node.
- *
- * If the node has a known suffix, use that path.
- * If it has no known suffix, use the default system search path.
+ * Find a path along which to search or expand the node.
*
- * Input:
- * gn Node being examined
- *
- * Results:
- * The appropriate path to search for the GNode.
+ * If the node has a known suffix, use that path,
+ * otherwise use the default system search path.
*/
SearchPath *
Suff_FindPath(GNode *gn)
@@ -1529,7 +1480,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
/* Apply the rule. */
Make_HandleUse(gn, tgn);
- /* Deal with wildcards and variables in any acquired sources. */
+ /* Deal with wildcards and expressions in any acquired sources. */
ln = ln != NULL ? ln->next : NULL;
while (ln != NULL) {
GNodeListNode *nln = ln->next;
@@ -1556,7 +1507,7 @@ ApplyTransform(GNode *tgn, GNode *sgn, Suffix *tsuff, Suffix *ssuff)
static void
ExpandMember(GNode *gn, const char *eoarch, GNode *mem, Suffix *memSuff)
{
- GNodeListNode *ln;
+ SuffixListNode *ln;
size_t nameLen = (size_t)(eoarch - gn->name);
/* Use first matching suffix... */
@@ -1565,7 +1516,6 @@ ExpandMember(GNode *gn, const char *eoarch, GNode *mem, Suffix *memSuff)
break;
if (ln != NULL) {
- /* Got one -- apply it */
Suffix *suff = ln->datum;
if (!ApplyTransform(gn, mem, suff, memSuff)) {
DEBUG2(SUFF, "\tNo transformation from %s -> %s\n",
@@ -1579,9 +1529,6 @@ static void FindDeps(GNode *, CandidateSearcher *);
/*
* Locate dependencies for an OP_ARCHV node.
*
- * Input:
- * gn Node for which to locate dependencies
- *
* Side Effects:
* Same as Suff_FindDeps
*/
@@ -1595,7 +1542,7 @@ FindDepsArchive(GNode *gn, CandidateSearcher *cs)
const char *name; /* Start of member's name */
/*
- * The node is an archive(member) pair. so we must find a
+ * The node is an 'archive(member)' pair, so we must find a
* suffix for both of them.
*/
eoarch = strchr(gn->name, '(');
@@ -1969,8 +1916,7 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs)
if (targ->node == NULL)
targ->node = Targ_GetNode(targ->file);
- ApplyTransform(targ->node, src->node,
- targ->suff, src->suff);
+ ApplyTransform(targ->node, src->node, targ->suff, src->suff);
if (targ->node != gn) {
/*
@@ -2068,9 +2014,6 @@ FindDeps(GNode *gn, CandidateSearcher *cs)
*
* Need to handle the changing of the null suffix gracefully so the old
* transformation rules don't just go away.
- *
- * Input:
- * name Name of null suffix
*/
void
Suff_SetNull(const char *name)
@@ -2102,16 +2045,21 @@ Suff_Init(void)
Suff_ClearSuffixes();
}
-
/* Clean up the suffixes module. */
void
Suff_End(void)
{
#ifdef CLEANUP
- Lst_DoneCall(&sufflist, SuffFree);
- Lst_DoneCall(&suffClean, SuffFree);
+ SuffixListNode *ln;
+
+ for (ln = sufflist.first; ln != NULL; ln = ln->next)
+ Suffix_Free(ln->datum);
+ Lst_Done(&sufflist);
+ for (ln = suffClean.first; ln != NULL; ln = ln->next)
+ Suffix_Free(ln->datum);
+ Lst_Done(&suffClean);
if (nullSuff != NULL)
- SuffFree(nullSuff);
+ Suffix_Free(nullSuff);
Lst_Done(&transforms);
#endif
}
@@ -2135,7 +2083,7 @@ Suffix_Print(const Suffix *suff)
{
Buffer buf;
- Buf_InitSize(&buf, 16);
+ Buf_Init(&buf);
Buf_AddFlag(&buf, suff->include, "SUFF_INCLUDE");
Buf_AddFlag(&buf, suff->library, "SUFF_LIBRARY");
Buf_AddFlag(&buf, suff->isNull, "SUFF_NULL");
@@ -2191,7 +2139,7 @@ Suff_NamesStr(void)
SuffixListNode *ln;
Suffix *suff;
- Buf_InitSize(&buf, 16);
+ Buf_Init(&buf);
for (ln = sufflist.first; ln != NULL; ln = ln->next) {
suff = ln->datum;
if (ln != sufflist.first)
diff --git a/unit-tests/Makefile b/unit-tests/Makefile
index 910bed094afd..111e6325a667 100644
--- a/unit-tests/Makefile
+++ b/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.207 2023/09/09 16:44:03 sjg Exp $
+# $Id: Makefile,v 1.210 2024/01/08 18:28:08 sjg Exp $
#
-# $NetBSD: Makefile,v 1.341 2023/09/09 16:41:04 sjg Exp $
+# $NetBSD: Makefile,v 1.342 2024/01/07 02:07:44 sjg Exp $
#
# Unit tests for make(1)
#
@@ -217,7 +217,7 @@ TESTS+= hanoi-include
TESTS+= impsrc
TESTS+= include-main
TESTS+= job-flags
-#TESTS+= job-output-long-lines
+TESTS+= job-output-long-lines
TESTS+= job-output-null
TESTS+= jobs-empty-commands
TESTS+= jobs-empty-commands-error
@@ -249,7 +249,7 @@ TESTS+= opt-debug-graph1
TESTS+= opt-debug-graph2
TESTS+= opt-debug-graph3
TESTS+= opt-debug-hash
-#TESTS+= opt-debug-jobs
+TESTS+= opt-debug-jobs
TESTS+= opt-debug-lint
TESTS+= opt-debug-loud
TESTS+= opt-debug-meta
@@ -821,6 +821,7 @@ _SED_CMDS+= -e 's,${.OBJDIR},<curdir>,g' -e 's,${.OBJDIR:tA},<curdir>,g'
_SED_CMDS+= -e 's,^${TEST_MAKE:T:S,.,\\.,g}[][0-9]*:,make:,'
_SED_CMDS+= -e 's,${TEST_MAKE:S,.,\\.,g},make,'
_SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
+_SED_CMDS+= -e 's,${TEST_MAKE:T:S,.,\\.,g}\(\[[1-9][0-9]*\]:\),make\1,'
_SED_CMDS+= -e 's,<curdir>/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
_SED_CMDS+= -e '/MAKE_VERSION/d'
diff --git a/unit-tests/cmd-errors-jobs.exp b/unit-tests/cmd-errors-jobs.exp
index c6baacfe0ed7..d0a6e51eb84d 100644
--- a/unit-tests/cmd-errors-jobs.exp
+++ b/unit-tests/cmd-errors-jobs.exp
@@ -1,7 +1,7 @@
: undefined--eol
make: Unclosed variable "UNCLOSED"
: unclosed-variable-
-make: Unclosed variable expression (expecting '}') for "UNCLOSED"
+make: Unclosed expression, expecting '}' for "UNCLOSED"
: unclosed-modifier-
make: Unknown modifier "Z"
: unknown-modifier--eol
diff --git a/unit-tests/cmd-errors-lint.exp b/unit-tests/cmd-errors-lint.exp
index 90b63bbcb08e..bdf4ae1a17e8 100644
--- a/unit-tests/cmd-errors-lint.exp
+++ b/unit-tests/cmd-errors-lint.exp
@@ -1,7 +1,7 @@
: undefined
make: Unclosed variable "UNCLOSED"
: unclosed-variable
-make: Unclosed variable expression (expecting '}') for "UNCLOSED"
+make: Unclosed expression, expecting '}' for "UNCLOSED"
: unclosed-modifier
make: Unknown modifier "Z"
: unknown-modifier
diff --git a/unit-tests/cmd-errors.exp b/unit-tests/cmd-errors.exp
index c6baacfe0ed7..d0a6e51eb84d 100644
--- a/unit-tests/cmd-errors.exp
+++ b/unit-tests/cmd-errors.exp
@@ -1,7 +1,7 @@
: undefined--eol
make: Unclosed variable "UNCLOSED"
: unclosed-variable-
-make: Unclosed variable expression (expecting '}') for "UNCLOSED"
+make: Unclosed expression, expecting '}' for "UNCLOSED"
: unclosed-modifier-
make: Unknown modifier "Z"
: unknown-modifier--eol
diff --git a/unit-tests/cmdline-undefined.mk b/unit-tests/cmdline-undefined.mk
index 062449f80df2..81b44518dd41 100644
--- a/unit-tests/cmdline-undefined.mk
+++ b/unit-tests/cmdline-undefined.mk
@@ -1,6 +1,6 @@
-# $NetBSD: cmdline-undefined.mk,v 1.3 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cmdline-undefined.mk,v 1.4 2023/11/19 21:47:52 rillig Exp $
#
-# Tests for undefined variable expressions in the command line.
+# Tests for undefined expressions in the command line.
all:
# When the command line is parsed, variable assignments using the
diff --git a/unit-tests/comment.mk b/unit-tests/comment.mk
index 792350f6223a..a3bf781b9a67 100644
--- a/unit-tests/comment.mk
+++ b/unit-tests/comment.mk
@@ -1,4 +1,4 @@
-# $NetBSD: comment.mk,v 1.5 2022/05/08 06:51:27 rillig Exp $
+# $NetBSD: comment.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Demonstrate how comments are written in makefiles.
@@ -55,7 +55,7 @@ VAR= \# # Both in the assignment.
# Since 2012-03-24 the variable modifier :[#] does not need to be escaped.
# To keep the parsing code simple, any "[#" does not start a comment, even
-# outside of a variable expression.
+# outside of an expression.
WORDS= ${VAR:[#]} [#
.if ${WORDS} != "1 [#"
. error
diff --git a/unit-tests/cond-cmp-string.mk b/unit-tests/cond-cmp-string.mk
index 44d8beceacdd..7b13ebf2212b 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.17 2023/03/28 14:38:29 rillig Exp $
+# $NetBSD: cond-cmp-string.mk,v 1.18 2023/11/19 21:47:52 rillig Exp $
#
# Tests for string comparisons in .if conditions.
@@ -20,11 +20,11 @@
. error
.endif
-# The left-hand side of the comparison requires that any variable expression
+# The left-hand side of the comparison requires that any expression
# is defined.
#
# The variable named "" is never defined, nevertheless it can be used as a
-# starting point for variable expressions. Applying the :U modifier to such
+# starting point for expressions. Applying the :U modifier to such
# an undefined expression turns it into a defined expression.
#
# See ApplyModifier_Defined and DEF_DEFINED.
@@ -63,13 +63,13 @@
. error
.endif
-# A variable expression can be enclosed in double quotes.
+# An expression can be enclosed in double quotes.
.if ${:Uword} != "${:Uword}"
. error
.endif
# Between 2003-01-01 (maybe even earlier) and 2020-10-30, adding one of the
-# characters " \t!=><" directly after a variable expression resulted in a
+# characters " \t!=><" directly after an expression resulted in a
# "Malformed conditional", even though the string was well-formed.
.if ${:Uword } != "${:Uword} "
. error
@@ -89,12 +89,12 @@
. error
.endif
-# Adding another variable expression to the string literal works though.
+# Adding another expression to the string literal works though.
.if ${:Uword} != "${:Uwo}${:Urd}"
. error
.endif
-# Adding a space at the beginning of the quoted variable expression works
+# Adding a space at the beginning of the quoted expression works
# though.
.if ${:U word } != " ${:Uword} "
. error
diff --git a/unit-tests/cond-cmp-unary.mk b/unit-tests/cond-cmp-unary.mk
index 58927e3b4944..80626a279358 100755
--- a/unit-tests/cond-cmp-unary.mk
+++ b/unit-tests/cond-cmp-unary.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-unary.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cond-cmp-unary.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Tests for unary comparisons in .if conditions, that is, comparisons with
# a single operand. If the operand is a number, it is compared to zero,
@@ -24,7 +24,7 @@
. error
.endif
-# The empty string may come from a variable expression.
+# The empty string may come from an expression.
#
# XXX: As of 2023-06-01, this empty string is interpreted "as a number" in
# EvalTruthy, which is plain wrong. The bug is in TryParseNumber.
@@ -32,13 +32,13 @@
. error
.endif
-# A variable expression that is not surrounded by quotes is interpreted
+# An expression that is not surrounded by quotes is interpreted
# as a number if possible, otherwise as a string.
.if ${:U0}
. error
.endif
-# A non-zero number from a variable expression evaluates to true.
+# A non-zero number from an expression evaluates to true.
.if !${:U12345}
. error
.endif
diff --git a/unit-tests/cond-eof.mk b/unit-tests/cond-eof.mk
index 25c6ec5de708..a5f7ce95f89e 100644
--- a/unit-tests/cond-eof.mk
+++ b/unit-tests/cond-eof.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-eof.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cond-eof.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Tests for parsing the end of '.if' conditions, which are represented as the
# token TOK_EOF.
@@ -9,7 +9,7 @@ SIDE_EFFECT2= ${:!echo 'side effect 2' 1>&2!}
# In the following conditions, ${SIDE_EFFECT} is the position of the first
# parse error. Before cond.c 1.286 from 2021-12-10, it was always fully
-# evaluated, even if it was not necessary to expand the variable expression.
+# evaluated, even if it was not necessary to expand the expression.
# These syntax errors are an edge case that does not occur during normal
# operation. Still, it is easy to avoid evaluating these expressions, just in
# case they have side effects.
diff --git a/unit-tests/cond-func-defined.exp b/unit-tests/cond-func-defined.exp
index c2fc78c1a35a..d556f3b982b3 100644
--- a/unit-tests/cond-func-defined.exp
+++ b/unit-tests/cond-func-defined.exp
@@ -1,6 +1,6 @@
make: "cond-func-defined.mk" line 24: Missing closing parenthesis for defined()
make: "cond-func-defined.mk" line 34: Missing closing parenthesis for defined()
-make: "cond-func-defined.mk" line 47: In .for loops, variable expressions for the loop variables are
+make: "cond-func-defined.mk" line 47: In .for loops, expressions for the loop variables are
make: "cond-func-defined.mk" line 49: substituted at evaluation time. There is no actual variable
make: "cond-func-defined.mk" line 51: involved, even if it feels like it.
make: Fatal errors encountered -- cannot continue
diff --git a/unit-tests/cond-func-defined.mk b/unit-tests/cond-func-defined.mk
index 10adfde292ed..14597398bc62 100644
--- a/unit-tests/cond-func-defined.mk
+++ b/unit-tests/cond-func-defined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-defined.mk,v 1.10 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cond-func-defined.mk,v 1.11 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the defined() function in .if conditions.
@@ -25,7 +25,7 @@ ${:UA B}= variable name with spaces
. error
.endif
-# If necessary, the whitespace can be generated by a variable expression.
+# If necessary, the whitespace can be generated by an expression.
.if !defined(${:UA B})
. error
.endif
@@ -43,8 +43,8 @@ ${:UA B}= variable name with spaces
. if defined(var)
. error
. else
-# expect+1: In .for loops, variable expressions for the loop variables are
-. info In .for loops, variable expressions for the loop variables are
+# expect+1: In .for loops, expressions for the loop variables are
+. info In .for loops, expressions for the loop variables are
# expect+1: substituted at evaluation time. There is no actual variable
. info substituted at evaluation time. There is no actual variable
# expect+1: involved, even if it feels like it.
diff --git a/unit-tests/cond-func-empty.mk b/unit-tests/cond-func-empty.mk
index 2ad1c16edbcc..057b175a7693 100644
--- a/unit-tests/cond-func-empty.mk
+++ b/unit-tests/cond-func-empty.mk
@@ -1,9 +1,9 @@
-# $NetBSD: cond-func-empty.mk,v 1.22 2023/08/11 05:01:12 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.24 2023/12/19 19:33:40 rillig Exp $
#
-# Tests for the empty() function in .if conditions, which tests a variable
+# Tests for the empty() function in .if conditions, which tests an
# expression for emptiness.
#
-# Note that the argument in the parentheses is a variable name, not a variable
+# Note that the argument in the parentheses is a variable name, not an
# expression. That name may be followed by ':...' modifiers.
#
@@ -120,7 +120,7 @@ ${:U }= space
. error
.endif
-# The :L modifier creates a variable expression that has the same value as
+# The :L modifier creates an expression that has the same value as
# its name, which both are "VAR" in this case. The value is therefore not
# empty.
.if empty(VAR:L)
@@ -138,7 +138,7 @@ ${:U }= space
. error
.endif
-# Ensure that variable expressions that appear as part of the function call
+# Ensure that expressions that appear as part of the function call
# argument are properly parsed. Typical use cases for this are .for loops,
# which are expanded to exactly these ${:U} expressions.
#
@@ -188,20 +188,20 @@ ${:U WORD }= variable name with spaces
# side containing the '!empty' was evaluated though, as it had always been.
#
# When evaluating the !empty condition, the variable name was parsed as
-# "VARNAME${:U2}", but without expanding any nested variable expression, in
+# "VARNAME${:U2}", but without expanding any nested expression, in
# this case the ${:U2}. The expression '${:U2}' was replaced with an empty
# string, the resulting variable name was thus "VARNAME". This conceptually
# wrong variable name should have been discarded quickly after parsing it, to
# prevent it from doing any harm.
#
-# The variable expression was expanded though, and this was wrong. The
+# The expression was expanded, and this was wrong. The
# expansion was done without VARE_WANTRES (called VARF_WANTRES back then)
# though. This had the effect that the ${:U1} from the value of VARNAME
# expanded to an empty string. This in turn created the seemingly recursive
# definition VARNAME=${VARNAME}, and that definition was never meant to be
# expanded.
#
-# This was fixed by expanding nested variable expressions in the variable name
+# This was fixed by expanding nested expressions in the variable name
# only if the flag VARE_WANTRES is given.
VARNAME= ${VARNAME${:U1}}
.if defined(VARNAME${:U2}) && !empty(VARNAME${:U2})
diff --git a/unit-tests/cond-func-exists.mk b/unit-tests/cond-func-exists.mk
index 48d7e727dc3f..40228cd44902 100644
--- a/unit-tests/cond-func-exists.mk
+++ b/unit-tests/cond-func-exists.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-exists.mk,v 1.6 2020/11/30 20:12:29 rillig Exp $
+# $NetBSD: cond-func-exists.mk,v 1.7 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the exists() function in .if conditions.
@@ -17,7 +17,7 @@
.endif
# The only way to escape characters that would otherwise influence the parser
-# is to enclose them in a variable expression. For function arguments,
+# is to enclose them in an expression. For function arguments,
# neither the backslash nor the dollar sign act as escape character.
.if exists(\.)
. error
@@ -27,7 +27,7 @@
. error
.endif
-# The argument to the function can have several variable expressions.
+# The argument to the function can have several expressions.
# See cond-func.mk for the characters that cannot be used directly.
.if !exists(${.PARSEDIR}/${.PARSEFILE})
. error
diff --git a/unit-tests/cond-func.mk b/unit-tests/cond-func.mk
index 2e8a6785b0e8..aabd31b4db46 100644
--- a/unit-tests/cond-func.mk
+++ b/unit-tests/cond-func.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func.mk,v 1.13 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cond-func.mk,v 1.14 2023/11/19 21:47:52 rillig Exp $
#
# Tests for those parts of the functions in .if conditions that are common
# among several functions.
@@ -38,7 +38,7 @@ ${VARNAME_UNBALANCED_BRACES}= variable name with unbalanced braces
. error
.endif
-# If necessary, the whitespace can be generated by a variable expression.
+# If necessary, the whitespace can be generated by an expression.
.if !defined(${:UA B})
. error
.endif
diff --git a/unit-tests/cond-late.exp b/unit-tests/cond-late.exp
index e179e8c74cc4..703677da634c 100644
--- a/unit-tests/cond-late.exp
+++ b/unit-tests/cond-late.exp
@@ -1,4 +1,4 @@
-make: Bad conditional expression ' != "no"' in ' != "no"?:'
+make: Bad conditional expression ' != "no"' before '?:'
yes
no
exit status 0
diff --git a/unit-tests/cond-late.mk b/unit-tests/cond-late.mk
index 1cfaaa2ee4e9..8e3d41f60001 100644
--- a/unit-tests/cond-late.mk
+++ b/unit-tests/cond-late.mk
@@ -1,6 +1,6 @@
-# $NetBSD: cond-late.mk,v 1.4 2023/05/10 15:53:32 rillig Exp $
+# $NetBSD: cond-late.mk,v 1.6 2023/12/10 20:12:28 rillig Exp $
#
-# Using the :? modifier, variable expressions can contain conditional
+# Using the :? modifier, expressions can contain conditional
# expressions that are evaluated late, at expansion time.
#
# Any expressions appearing in these conditions are expanded before parsing
@@ -31,6 +31,6 @@ cond-literal:
VAR= ${${UNDEF} != "no":?:}
# expect-reset
-# expect: make: Bad conditional expression ' != "no"' in ' != "no"?:'
+# expect: make: Bad conditional expression ' != "no"' before '?:'
.if empty(VAR:Mpattern)
.endif
diff --git a/unit-tests/cond-op-and.mk b/unit-tests/cond-op-and.mk
index 6fbcbdc4b2db..19a0672ba44e 100644
--- a/unit-tests/cond-op-and.mk
+++ b/unit-tests/cond-op-and.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op-and.mk,v 1.8 2023/08/15 21:27:09 rillig Exp $
+# $NetBSD: cond-op-and.mk,v 1.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the && operator in .if conditions.
@@ -33,15 +33,15 @@
# Test combinations of outer '||' with inner '&&', to ensure that the operands
# of the inner '&&' are only evaluated if necessary.
DEF= defined
-# expect+1: Malformed conditional (0 || (${DEF} && ${UNDEF})
+# expect+1: Malformed conditional (0 || (${DEF} && ${UNDEF}))
.if 0 || (${DEF} && ${UNDEF})
.endif
.if 0 || (!${DEF} && ${UNDEF})
.endif
-# expect+1: Malformed conditional (0 || (${UNDEF} && ${UNDEF})
+# expect+1: Malformed conditional (0 || (${UNDEF} && ${UNDEF}))
.if 0 || (${UNDEF} && ${UNDEF})
.endif
-# expect+1: Malformed conditional (0 || (!${UNDEF} && ${UNDEF})
+# expect+1: Malformed conditional (0 || (!${UNDEF} && ${UNDEF}))
.if 0 || (!${UNDEF} && ${UNDEF})
.endif
.if 1 || (${DEF} && ${UNDEF})
diff --git a/unit-tests/cond-op-or.mk b/unit-tests/cond-op-or.mk
index d66fa5af640f..165408f3c130 100644
--- a/unit-tests/cond-op-or.mk
+++ b/unit-tests/cond-op-or.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op-or.mk,v 1.10 2023/08/15 21:27:09 rillig Exp $
+# $NetBSD: cond-op-or.mk,v 1.11 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the || operator in .if conditions.
@@ -43,13 +43,13 @@ DEF= defined
.endif
.if 1 && (${DEF} || ${UNDEF})
.endif
-# expect+1: Malformed conditional (1 && (!${DEF} || ${UNDEF})
+# expect+1: Malformed conditional (1 && (!${DEF} || ${UNDEF}))
.if 1 && (!${DEF} || ${UNDEF})
.endif
-# expect+1: Malformed conditional (1 && (${UNDEF} || ${UNDEF})
+# expect+1: Malformed conditional (1 && (${UNDEF} || ${UNDEF}))
.if 1 && (${UNDEF} || ${UNDEF})
.endif
-# expect+1: Malformed conditional (1 && (!${UNDEF} || ${UNDEF})
+# expect+1: Malformed conditional (1 && (!${UNDEF} || ${UNDEF}))
.if 1 && (!${UNDEF} || ${UNDEF})
.endif
diff --git a/unit-tests/cond-short.exp b/unit-tests/cond-short.exp
index 745d7e912c2b..44681b57ebc1 100644
--- a/unit-tests/cond-short.exp
+++ b/unit-tests/cond-short.exp
@@ -7,7 +7,7 @@ expected M pattern
expected or
expected or exists
expected or empty
-make: "cond-short.mk" line 214: Comparison with '<' requires both operands '' and '42' to be numeric
+make: "cond-short.mk" line 231: Comparison with '<' requires both operands '' and '42' to be numeric
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/cond-short.mk b/unit-tests/cond-short.mk
index 525ff7d0f2ab..bcdf372ca6e6 100644
--- a/unit-tests/cond-short.mk
+++ b/unit-tests/cond-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-short.mk,v 1.20 2023/03/04 13:42:36 rillig Exp $
+# $NetBSD: cond-short.mk,v 1.23 2023/11/19 22:32:44 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@@ -9,9 +9,9 @@
# Before 2020-06-28, the right-hand side of an && or || operator was always
# evaluated, which was wrong. In cond.c 1.69 and var.c 1.197 on 2015-10-11,
# Var_Parse got a new parameter named 'wantit'. Since then it would have been
-# possible to skip evaluation of irrelevant variable expressions and only
+# possible to skip evaluation of irrelevant expressions and only
# parse them. They were still evaluated though, the only difference to
-# relevant variable expressions was that in the irrelevant variable
+# relevant expressions was that in the irrelevant
# expressions, undefined variables were allowed. This allowed for conditions
# like 'defined(VAR) && ${VAR:S,from,to,} != ""', which no longer produced an
# error message 'Malformed conditional', but the irrelevant expression was
@@ -178,6 +178,23 @@ INDIR_UNDEF= ${UNDEF}
. error
.endif
+
+# Since cond.c 1.76 from 2020.06.28 and before var.c 1.225 from 2020.07.01,
+# the following snippet resulted in the error message 'Variable VAR is
+# recursive'. The condition '0' evaluated to false, which made the right-hand
+# side of the '&&' irrelevant. Back then, irrelevant condition parts were
+# still evaluated, but in "irrelevant mode", which allowed undefined variables
+# to occur in expressions. In this mode, the variable name 'VAR' was
+# unnecessarily evaluated, resulting in the expression '${VAR${:U1}}'. In
+# this expression, the variable name was 'VAR${:U1}', and of this variable
+# name, only the fixed part 'VAR' was evaluated, without the part '${:U1}'.
+# This partial evaluation led to the wrong error message about 'VAR' being
+# recursive.
+VAR= ${VAR${:U1}}
+.if 0 && !empty(VAR)
+.endif
+
+
# Enclosing the expression in double quotes changes how that expression is
# evaluated. In irrelevant expressions that are enclosed in double quotes,
# expressions based on undefined variables are allowed and evaluate to an
diff --git a/unit-tests/cond-token-number.mk b/unit-tests/cond-token-number.mk
index 6cec21cf6c44..7e73f8b76f94 100644
--- a/unit-tests/cond-token-number.mk
+++ b/unit-tests/cond-token-number.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-number.mk,v 1.9 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cond-token-number.mk,v 1.10 2023/11/19 21:47:52 rillig Exp $
#
# Tests for number tokens in .if conditions.
#
@@ -52,13 +52,13 @@
. error
.endif
-# When the number comes from a variable expression though, it may be signed.
+# When the number comes from an expression though, it may be signed.
# XXX: This is inconsistent.
.if ${:U+0}
. error
.endif
-# When the number comes from a variable expression though, it may be signed.
+# When the number comes from an expression though, it may be signed.
# XXX: This is inconsistent.
.if !${:U+1}
. error
diff --git a/unit-tests/cond-token-plain.exp b/unit-tests/cond-token-plain.exp
index a14f69581658..8044f3bac826 100644
--- a/unit-tests/cond-token-plain.exp
+++ b/unit-tests/cond-token-plain.exp
@@ -35,9 +35,9 @@ make: "cond-token-plain.mk" line 121: ok
CondParser_Eval: V${UNDEF}AR
make: "cond-token-plain.mk" line 130: Undefined variables in bare words expand to an empty string.
CondParser_Eval: 0${:Ux00}
-make: "cond-token-plain.mk" line 139: Numbers can be composed from literals and variable expressions.
+make: "cond-token-plain.mk" line 139: Numbers can be composed from literals and expressions.
CondParser_Eval: 0${:Ux01}
-make: "cond-token-plain.mk" line 144: Numbers can be composed from literals and variable expressions.
+make: "cond-token-plain.mk" line 144: Numbers can be composed from literals and expressions.
CondParser_Eval: "" ==
make: "cond-token-plain.mk" line 151: Missing right-hand side of operator '=='
CondParser_Eval: == ""
diff --git a/unit-tests/cond-token-plain.mk b/unit-tests/cond-token-plain.mk
index b6444e8ac1d3..79fcc771a855 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.18 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cond-token-plain.mk,v 1.19 2023/11/19 21:47:52 rillig Exp $
#
# Tests for plain tokens (that is, string literals without quotes)
# in .if conditions. These are also called bare words.
@@ -115,7 +115,7 @@ VAR= defined
. error
.endif
-# Bare words may be intermixed with variable expressions.
+# Bare words may be intermixed with expressions.
.if V${:UA}R
# expect+1: ok
. info ok
@@ -135,13 +135,13 @@ VAR= defined
.if 0${:Ux00}
. error
.else
-# expect+1: Numbers can be composed from literals and variable expressions.
-. info Numbers can be composed from literals and variable expressions.
+# expect+1: Numbers can be composed from literals and expressions.
+. info Numbers can be composed from literals and expressions.
.endif
.if 0${:Ux01}
-# expect+1: Numbers can be composed from literals and variable expressions.
-. info Numbers can be composed from literals and variable expressions.
+# expect+1: Numbers can be composed from literals and expressions.
+. info Numbers can be composed from literals and expressions.
.else
. error
.endif
@@ -205,7 +205,7 @@ ${:U\\\\}= backslash
# expect+1: Malformed conditional (left == right)
.if left == right
.endif
-# Before cond.c 1.276 from 2021-09-21, a variable expression containing the
+# Before cond.c 1.276 from 2021-09-21, an expression containing the
# modifier ':?:' allowed unquoted string literals for the rest of the
# condition. This was an unintended implementation mistake.
# expect+1: Malformed conditional (${0:?:} || left == right)
@@ -245,7 +245,7 @@ ${:U\\\\}= backslash
# A different situation is when CondParser.leftUnquotedOK is true. This
# situation arises in expressions of the form ${cond:?yes:no}. As of
# 2021-12-30, the condition in such an expression is evaluated before parsing
-# the condition, see varmod-ifelse.mk. To pass a variable expression to the
+# the condition, see varmod-ifelse.mk. To pass an expression to the
# condition parser, it needs to be escaped. This rarely happens in practice,
# in most cases the conditions are simple enough that it doesn't matter
# whether the condition is first evaluated and then parsed, or vice versa.
diff --git a/unit-tests/cond-token-string.exp b/unit-tests/cond-token-string.exp
index 959effba0908..db07adcb2d09 100644
--- a/unit-tests/cond-token-string.exp
+++ b/unit-tests/cond-token-string.exp
@@ -10,7 +10,7 @@ make: "cond-token-string.mk" line 61: The string literal " " is not empty, even
CondParser_Eval: "${UNDEF}"
make: "cond-token-string.mk" line 71: An undefined variable in quotes expands to an empty string, which then evaluates to false.
CondParser_Eval: "${:Uvalue}"
-make: "cond-token-string.mk" line 77: A nonempty variable expression evaluates to true.
+make: "cond-token-string.mk" line 77: A nonempty expression evaluates to true.
CondParser_Eval: "${:U}"
make: "cond-token-string.mk" line 86: An empty variable evaluates to false.
CondParser_Eval: ("${VAR}")
diff --git a/unit-tests/cond-token-string.mk b/unit-tests/cond-token-string.mk
index 7ecf3059e300..d13c68da134a 100644
--- a/unit-tests/cond-token-string.mk
+++ b/unit-tests/cond-token-string.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-token-string.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cond-token-string.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
# Tests for quoted string literals in .if conditions.
#
@@ -26,7 +26,7 @@
.endif
# The 'x' produces a "Malformed conditional" since the left-hand side of a
-# comparison in an .if directive must be either a variable expression, a
+# comparison in an .if directive must be either an expression, a
# quoted string literal or a number that starts with a digit.
# expect+1: Malformed conditional (x${:Uvalue} == "")
.if x${:Uvalue} == ""
@@ -73,8 +73,8 @@
.endif
.if "${:Uvalue}"
-# expect+1: A nonempty variable expression evaluates to true.
-. info A nonempty variable expression evaluates to true.
+# expect+1: A nonempty expression evaluates to true.
+. info A nonempty expression evaluates to true.
.else
. error
.endif
@@ -87,7 +87,7 @@
.endif
# A non-empty string evaluates to true, no matter if it's a literal string or
-# if it contains variable expressions. The parentheses are not necessary for
+# if it contains expressions. The parentheses are not necessary for
# the parser, in this case their only purpose is to make the code harder to
# read for humans.
VAR= value
diff --git a/unit-tests/cond-token-var.mk b/unit-tests/cond-token-var.mk
index b50c0439c127..c6471756a3dd 100644
--- a/unit-tests/cond-token-var.mk
+++ b/unit-tests/cond-token-var.mk
@@ -1,11 +1,11 @@
-# $NetBSD: cond-token-var.mk,v 1.7 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: cond-token-var.mk,v 1.8 2023/11/19 21:47:52 rillig Exp $
#
-# Tests for variable expressions in .if conditions.
+# Tests for expressions in .if conditions.
#
-# Note the fine distinction between a variable and a variable expression.
-# A variable has a name and a value. To access the value, one writes a
-# variable expression of the form ${VAR}. This is a simple variable
-# expression. Variable expressions can get more complicated by adding
+# Note the fine distinction between a variable and an expression.
+# A variable has a name and a value. To access the value, one writes an
+# expression of the form ${VAR}. This is a simple
+# expression. Expressions can get more complicated by adding
# variable modifiers such as in ${VAR:Mpattern}.
#
# XXX: Strictly speaking, variable modifiers should be called expression
@@ -49,7 +49,7 @@ DEF= defined
.if ${UNDEF:U}
.endif
-# If the value of the variable expression is a number, it is compared against
+# If the value of the expression is a number, it is compared against
# zero.
.if ${:U0}
. error
@@ -58,7 +58,7 @@ DEF= defined
. error
.endif
-# If the value of the variable expression is not a number, any non-empty
+# If the value of the expression is not a number, any non-empty
# value evaluates to true, even if there is only whitespace.
.if ${:U}
. error
diff --git a/unit-tests/dep-var.exp b/unit-tests/dep-var.exp
index 4e38057bf6b9..cc229d32e6d4 100755
--- a/unit-tests/dep-var.exp
+++ b/unit-tests/dep-var.exp
@@ -22,7 +22,7 @@ Var_Parse: $): (parse-only)
Global: .ALLTARGETS = all ${DEF2} a-${DEF2}-b ${UNDEF3} 1-${INDIRECT_1}-1 $$) undef1 def2 a-def2-b 1-2-$INDIRECT_2-2-1 $)
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
-make: Malformed variable expression at "$)"
+make: Malformed expression at "$)"
def2
a-def2-b
1-2-NDIRECT_2-2-1
diff --git a/unit-tests/dep-var.mk b/unit-tests/dep-var.mk
index 16f7498fd5cc..8c1636bbdec2 100755
--- a/unit-tests/dep-var.mk
+++ b/unit-tests/dep-var.mk
@@ -1,4 +1,4 @@
-# $NetBSD: dep-var.mk,v 1.8 2023/05/10 15:53:32 rillig Exp $
+# $NetBSD: dep-var.mk,v 1.11 2023/12/19 19:33:40 rillig Exp $
#
# Tests for variable references in dependency declarations.
#
@@ -9,11 +9,11 @@
# expect: Var_Parse: ${UNDEF1} (eval-defined)
# Even though undefined expressions should lead to errors, no error message is
-# generated for this line. The variable expression ${UNDEF1} simply expands
+# generated for this line. The expression ${UNDEF1} simply expands
# to an empty string.
all: ${UNDEF1}
-# Using a double dollar in order to circumvent immediate variable expansion
+# Using a double dollar in order to circumvent immediate expression expansion
# feels like unintended behavior. At least the manual page says nothing at
# all about defined or undefined variables in dependency lines.
#
@@ -25,7 +25,7 @@ all: $${DEF2} a-$${DEF2}-b
# XXX: The -dv log says later when expanding the sources of 'all':
# Var_Parse: ${UNDEF3} (eval-defined)
# but no error message is generated for this line, just like for UNDEF1.
-# The variable expression ${UNDEF3} simply expands to an empty string.
+# The expression ${UNDEF3} simply expands to an empty string.
all: $${UNDEF3}
# Try out how many levels of indirection are really expanded in dependency
@@ -63,7 +63,7 @@ INDIRECT_3= indirect
UNDEF1= undef1
DEF2= def2
-# Cover the code in SuffExpandChildren that deals with malformed variable
+# Cover the code in SuffExpandChildren that deals with malformed
# expressions.
#
# This seems to be an edge case that never happens in practice, and it would
diff --git a/unit-tests/depsrc-ignore.exp b/unit-tests/depsrc-ignore.exp
index 162f10ddc17b..608671f58ed8 100644
--- a/unit-tests/depsrc-ignore.exp
+++ b/unit-tests/depsrc-ignore.exp
@@ -1,8 +1,8 @@
ignore-errors begin
false ignore-errors
+*** Error code 1 (ignored)
ignore-errors end
all begin
-*** Error code 1 (ignored)
false all
*** Error code 1 (continuing)
diff --git a/unit-tests/deptgt-delete_on_error.exp b/unit-tests/deptgt-delete_on_error.exp
index 75fbbe12472f..e60aa01351e2 100644
--- a/unit-tests/deptgt-delete_on_error.exp
+++ b/unit-tests/deptgt-delete_on_error.exp
@@ -16,6 +16,7 @@ make: *** deptgt-delete_on_error-regular-delete removed
Stop.
make: stopped in unit-tests
+*** Error code 1 (ignored)
Parallel mode
> deptgt-delete_on_error-regular; false
@@ -45,5 +46,4 @@ make: stopped in unit-tests
make: stopped in unit-tests
*** Error code 1 (ignored)
-*** Error code 1 (ignored)
exit status 0
diff --git a/unit-tests/deptgt-makeflags.exp b/unit-tests/deptgt-makeflags.exp
index ac8ffc83470d..ea29f76ad464 100644
--- a/unit-tests/deptgt-makeflags.exp
+++ b/unit-tests/deptgt-makeflags.exp
@@ -1,4 +1,4 @@
-Global: delete DOLLAR (not found)
+Global: ignoring delete 'DOLLAR' as it is not found
Command: DOLLAR = $$$$
Global: .MAKEOVERRIDES = VAR DOLLAR
CondParser_Eval: ${DOLLAR} != "\$\$"
diff --git a/unit-tests/deptgt-makeflags.mk b/unit-tests/deptgt-makeflags.mk
index 26f3f5794354..2f8b00743e3f 100644
--- a/unit-tests/deptgt-makeflags.mk
+++ b/unit-tests/deptgt-makeflags.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt-makeflags.mk,v 1.7 2021/11/29 00:17:10 rillig Exp $
+# $NetBSD: deptgt-makeflags.mk,v 1.9 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the special target .MAKEFLAGS in dependency declarations,
# which adds command line options later, at parse time.
@@ -65,9 +65,9 @@
.endif
# Next try at defining another newline variable. Since whitespace around the
-# variable value is trimmed, two empty variable expressions ${:U} surround the
+# variable value is trimmed, two empty expressions ${:U} surround the
# literal newline now. This prevents the newline from being skipped during
-# parsing. The ':=' assignment operator expands the empty variable
+# parsing. The ':=' assignment operator expands the empty
# expressions, leaving only the newline as the variable value.
#
# This is one of the very few ways (maybe even the only one) to inject literal
diff --git a/unit-tests/deptgt.mk b/unit-tests/deptgt.mk
index 67c47e4909e6..30b8399191bd 100644
--- a/unit-tests/deptgt.mk
+++ b/unit-tests/deptgt.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt.mk,v 1.14 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: deptgt.mk,v 1.16 2023/12/17 09:44:00 rillig Exp $
#
# Tests for special targets like .BEGIN or .SUFFIXES in dependency
# declarations.
@@ -30,7 +30,7 @@ VAR=value # targets := NULL
: command3 # parse error, since targets == NULL
# In a dependency declaration, the list of targets can be empty.
-# It doesn't matter whether the empty string is generated by a variable
+# It doesn't matter whether the empty string is generated by an
# expression or whether it is just omitted.
.MAKEFLAGS: -dp
${:U}: empty-source
@@ -54,7 +54,7 @@ $$$$$$$${:U:Z}:
# expect+1: warning: Extra target (ordinary) ignored
.PATH ordinary:
-# expect+1: Special and mundane targets don't mix. Mundane ones ignored
+# expect+1: warning: Special and mundane targets don't mix. Mundane ones ignored
ordinary .PATH:
all:
diff --git a/unit-tests/dir.mk b/unit-tests/dir.mk
index d3c75c8cb11d..956285393489 100644
--- a/unit-tests/dir.mk
+++ b/unit-tests/dir.mk
@@ -1,4 +1,4 @@
-# $NetBSD: dir.mk,v 1.10 2023/01/24 00:24:02 sjg Exp $
+# $NetBSD: dir.mk,v 1.11 2023/12/19 19:33:40 rillig Exp $
#
# Tests for dir.c.
@@ -67,7 +67,7 @@ fetch fetch-post extract extract-post:
# The expansions may have duplicates.
# When the source of the dependency line is expanded later, each of the
-# expanded words will be the same.
+# expanded words resolves to the same node.
all: dup-{1,1,1,1,1,1,1}
dup-1:
diff --git a/unit-tests/directive-else.mk b/unit-tests/directive-else.mk
index 41b38a14a6cf..cda671907217 100644
--- a/unit-tests/directive-else.mk
+++ b/unit-tests/directive-else.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-else.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: directive-else.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the .else directive.
#
@@ -45,7 +45,7 @@
.else # comment
.endif
-# A variable expression does count as an argument, even if it is empty.
+# An expression does count as an argument, even if it is empty.
.if 0
# expect+1: The .else directive does not take arguments
.else ${:U}
diff --git a/unit-tests/directive-export-gmake.exp b/unit-tests/directive-export-gmake.exp
index cf08a0443acc..c37d3b2d8591 100644
--- a/unit-tests/directive-export-gmake.exp
+++ b/unit-tests/directive-export-gmake.exp
@@ -1,5 +1,6 @@
make: "directive-export-gmake.mk" line 71: Invalid line 'export VAR=${:U1}', expanded to 'export VAR=1'
make: "directive-export-gmake.mk" line 85: 16:00:00
+make: "directive-export-gmake.mk" line 92: Variable/Value missing from "export"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/directive-export-gmake.mk b/unit-tests/directive-export-gmake.mk
index f7a617ab7553..de79470bf305 100644
--- a/unit-tests/directive-export-gmake.mk
+++ b/unit-tests/directive-export-gmake.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export-gmake.mk,v 1.7 2023/08/20 20:48:32 rillig Exp $
+# $NetBSD: directive-export-gmake.mk,v 1.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the export directive (without leading dot), as in GNU make.
@@ -67,7 +67,7 @@ export VAR=an ${UNDEF} variable
.for value in 1
# XXX: The ':' in this line is inside an expression and should thus not be
# interpreted as a dependency operator.
-# expect+1: Invalid line 'export VAR=${:U1}'
+# expect+1: Invalid line 'export VAR=${:U1}', expanded to 'export VAR=1'
export VAR=${value}
.endfor
@@ -83,3 +83,24 @@ INDIRECT_TZ= ${:UAmerica/Los_Angeles}
export TZ=${INDIRECT_TZ}
# expect+1: 16:00:00
.info ${%T:L:localtime=86400}
+
+
+# The '=' must be present in the unexpanded line, it cannot be generated by
+# an expression.
+EQ= =
+# expect+1: Variable/Value missing from "export"
+export EQ_VAR${EQ}eq-value
+.if ${:!env!:MEQ_VAR=*}
+. error
+.endif
+
+
+# The variable name must be given directly, it is not expanded. The name of
+# the exported variable thus starts with a '$', and that name may be filtered
+# out by the platform.
+INDIRECT_NAME= I_NAME
+INDIRECT_VALUE= indirect value
+export ${INDIRECT_NAME}=${INDIRECT_VALUE}
+.if ${:!env!:MI_NAME=*}
+. error
+.endif
diff --git a/unit-tests/directive-export.exp b/unit-tests/directive-export.exp
index a5d706e58f32..774a814570e3 100644
--- a/unit-tests/directive-export.exp
+++ b/unit-tests/directive-export.exp
@@ -1,4 +1,4 @@
-make: "directive-export.mk" line 50: 00:00:00
-make: "directive-export.mk" line 55: 00:00:00
-make: "directive-export.mk" line 58: 16:00:00
+make: "directive-export.mk" line 56: 00:00:00
+make: "directive-export.mk" line 61: 00:00:00
+make: "directive-export.mk" line 64: 16:00:00
exit status 0
diff --git a/unit-tests/directive-export.mk b/unit-tests/directive-export.mk
index d46b1dc01f27..08109814bcfd 100644
--- a/unit-tests/directive-export.mk
+++ b/unit-tests/directive-export.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-export.mk,v 1.9 2023/08/20 20:48:32 rillig Exp $
+# $NetBSD: directive-export.mk,v 1.10 2023/11/19 09:45:19 rillig Exp $
#
# Tests for the .export directive.
#
@@ -35,7 +35,13 @@ VAR= value $$ ${INDIRECT}
.export ${:U}
-# Trigger the "This isn't going to end well" in ExportVarEnv.
+# Before a child process is started, whether for the '!=' assignment operator
+# or for the ':sh' modifier, all variables that were marked for being exported
+# are expanded and then exported. If expanding such a variable requires
+# running a child command, the marked-as-exported variables would need to be
+# exported first, ending in an endless loop. To avoid this endless loop,
+# don't export the variables while preparing a child process, see
+# ExportVarEnv.
EMPTY_SHELL= ${:sh}
.export EMPTY_SHELL # only marked for export at this point
_!= :;: # Force the variable to be actually exported.
diff --git a/unit-tests/directive-for-empty.mk b/unit-tests/directive-for-empty.mk
index 52a65b39beeb..1c4cb0f1ad27 100644
--- a/unit-tests/directive-for-empty.mk
+++ b/unit-tests/directive-for-empty.mk
@@ -1,12 +1,12 @@
-# $NetBSD: directive-for-empty.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: directive-for-empty.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
#
# Tests for .for loops containing conditions of the form 'empty(var:...)'.
#
-# When a .for loop is expanded, variable expressions in the body of the loop
+# When a .for loop is expanded, expressions in the body of the loop
# are replaced with expressions containing the variable values. This
# replacement is a bit naive but covers most of the practical cases. The one
# popular exception is the condition 'empty(var:Modifiers)', which does not
-# look like a variable expression and is thus not replaced.
+# look like an expression and is thus not replaced.
#
# See also:
# https://gnats.netbsd.org/43821
@@ -24,7 +24,7 @@
.endfor
-# In conditions, the function call to 'empty' does not look like a variable
+# In conditions, the function call to 'empty' does not look like an
# expression, therefore it is not replaced. Since there is no global variable
# named 'i', this expression makes for a leaky abstraction. If the .for
# variables were real variables, calling 'empty' would work on them as well.
@@ -51,8 +51,8 @@
# loop would be naive and require many special cases, as there are many cases
# that need to be considered when deciding whether the token 'empty' is a
# function call or not, as demonstrated by the following examples. For
-# variable expressions like '${i:Modifiers}', this is simpler as a single
-# dollar almost always starts a variable expression. For counterexamples and
+# expressions like '${i:Modifiers}', this is simpler as a single
+# dollar almost always starts an expression. For counterexamples and
# edge cases, see directive-for-escape.mk. Adding another such tricky detail
# is out of the question.
.MAKEFLAGS: -df
diff --git a/unit-tests/directive-for-errors.mk b/unit-tests/directive-for-errors.mk
index 0af65350e643..94362847cdfd 100644
--- a/unit-tests/directive-for-errors.mk
+++ b/unit-tests/directive-for-errors.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-errors.mk,v 1.6 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: directive-for-errors.mk,v 1.9 2023/12/19 19:33:40 rillig Exp $
#
# Tests for error handling in .for loops.
@@ -9,7 +9,7 @@
.fori in 1 2 3
. warning <${i}>
.endfor
-# expect-2: <>
+# expect-2: warning: <>
# expect-2: for-less endfor
@@ -35,7 +35,7 @@
#
# The '$$' was not replaced with the values '1' or '3' from the .for loop,
# instead it was kept as-is, and when the .info directive expanded its
-# argument, each '$$' got replaced with a single '$'. The "long variable
+# argument, each '$$' got replaced with a single '$'. The "long
# expression" ${$} got replaced though, even though this would be a parse
# error everywhere outside a .for loop.
${:U\$}= dollar # see whether the "variable" '$' is local
@@ -67,7 +67,7 @@ ${:U\\}= backslash # see whether the "variable" '\' is local
# The list of values after the 'in' may be empty, no matter if this emptiness
-# comes from an empty expansion or even from a syntactically empty line.
+# comes from an expanded expression or from a syntactically empty line.
.for i in
. info Would be reached if there were items to loop over.
.endfor
@@ -89,6 +89,6 @@ ${:U\\}= backslash # see whether the "variable" '\' is local
.for i in 1 2 ${:U3:Z} 4
. warning Should not be reached.
.endfor
-# expect-2: Should not be reached.
-# expect-3: Should not be reached.
-# expect-4: Should not be reached.
+# expect-2: warning: Should not be reached.
+# expect-3: warning: Should not be reached.
+# expect-4: warning: Should not be reached.
diff --git a/unit-tests/directive-for-escape.exp b/unit-tests/directive-for-escape.exp
index dc63776c3764..6c84b7780e84 100644
--- a/unit-tests/directive-for-escape.exp
+++ b/unit-tests/directive-for-escape.exp
@@ -1,12 +1,12 @@
For: end for 1
For: loop body with chars = !"#$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~:
. info ${:U!"#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
-make: Unclosed variable expression, expecting '}' for modifier "U!"" of variable "" with value "!""
+make: Unclosed expression, expecting '}' for modifier "U!"" of variable "" with value "!""
make: "directive-for-escape.mk" line 19: !"
For: end for 1
For: loop body with chars = !"\\#$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~:
. info ${:U!"\\\\#$%&'()*+,-./0-9\:;<=>?@A-Z[\\]_^a-z{|\}~}
-make: Unclosed variable expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
+make: Unclosed expression, expecting '}' for modifier "U!"\\\\" of variable "" with value "!"\\"
make: "directive-for-escape.mk" line 30: !"\\
For: end for 1
For: loop body with i = $:
diff --git a/unit-tests/directive-for-escape.mk b/unit-tests/directive-for-escape.mk
index 05eac84d4d6b..16df5b1db4e3 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.21 2023/06/23 06:11:06 rillig Exp $
+# $NetBSD: directive-for-escape.mk,v 1.23 2023/11/19 22:32:44 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
@@ -69,7 +69,7 @@ VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
# Try to cover the code for nested '{}' in ExprLen, without success.
#
-# The value of the variable VALUES is not meant to be a variable expression.
+# The value of the variable VALUES is not meant to be an expression.
# Instead, it is meant to represent literal text, the only escaping mechanism
# being that each '$' is written as '$$'.
VALUES= $${UNDEF:U\$$\$$ {{}} end}
@@ -128,7 +128,7 @@ ${:U\\}= backslash
# XXX: It is not the job of ExprLen to parse an expression, it is naive to
# expect ExprLen to get all the details right in just a few lines of code.
# Each variable modifier has its own inconsistent way of parsing nested
-# variable expressions, braces and parentheses. (Compare ':M', ':S', and
+# expressions, braces and parentheses. (Compare ':M', ':S', and
# ':D' for details.) The only sensible thing to do is therefore to let
# Var_Parse do all the parsing work.
VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
@@ -147,7 +147,7 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}}}>end
# expect-2: $
# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
-# could contain colons, which affected variable expressions having this exact
+# could contain colons, which affected expressions having this exact
# modifier. This possibility was neither intended nor documented.
NUMBERS= one two three
# expect+1: invalid character ':' in .for loop variable name
@@ -156,7 +156,7 @@ NUMBERS= one two three
.endfor
# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
-# could contain braces, which allowed to replace sequences of variable
+# could contain braces, which allowed to replace sequences of
# expressions. This possibility was neither intended nor documented.
BASENAME= one
EXT= .c
@@ -203,7 +203,7 @@ i,= comma
. info eight ${$}${$}${$}${$} and no cents.
.endfor
# Outside a .for loop, '${$}' is interpreted differently. The outer '$' starts
-# a variable expression. The inner '$' is followed by a '}' and is thus a
+# an expression. The inner '$' is followed by a '}' and is thus a
# silent syntax error, the '$' is skipped. The variable name is thus '', and
# since since there is never a variable named '', the whole expression '${$}'
# evaluates to an empty string.
diff --git a/unit-tests/directive-for-if.mk b/unit-tests/directive-for-if.mk
index f2d0b550c693..f5a20279cc97 100644
--- a/unit-tests/directive-for-if.mk
+++ b/unit-tests/directive-for-if.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-if.mk,v 1.2 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: directive-for-if.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
#
# Test for a .for directive that contains an .if directive.
#
@@ -71,7 +71,7 @@ _!= echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet.
.endfor
# Before for.c 1.39 from 2008-12-21, a common workaround was to surround the
-# variable expression from the .for loop with '"'. Such a string literal
+# expression from the .for loop with '"'. Such a string literal
# has been allowed since cond.c 1.23 from 2004-04-13. Between that commit and
# the one from 2008, the parser would still get confused if the value from the
# .for loop contained '"', which was effectively a code injection.
diff --git a/unit-tests/directive-for.exp b/unit-tests/directive-for.exp
index 7f37da4ef364..0d0313c4e7b0 100755
--- a/unit-tests/directive-for.exp
+++ b/unit-tests/directive-for.exp
@@ -17,14 +17,15 @@ make: "directive-for.mk" line 146: }{ }{ }{
make: "directive-for.mk" line 166: invalid character ':' in .for loop variable name
make: "directive-for.mk" line 173: invalid character '$' in .for loop variable name
make: "directive-for.mk" line 185: invalid character '$' in .for loop variable name
-make: "directive-for.mk" line 196: Unknown modifier "Z"
-make: "directive-for.mk" line 197: XXX: Not reached word1
-make: "directive-for.mk" line 197: XXX: Not reached word3
-make: "directive-for.mk" line 204: no iteration variables in for
-make: "directive-for.mk" line 230: 1 open conditional
-make: "directive-for.mk" line 246: for-less endfor
-make: "directive-for.mk" line 247: if-less endif
-make: "directive-for.mk" line 255: if-less endif
+make: "directive-for.mk" line 210: Unknown modifier "Z"
+make: "directive-for.mk" line 211: XXX: Should not reach word1
+make: "directive-for.mk" line 211: XXX: Should not reach before--after
+make: "directive-for.mk" line 211: XXX: Should not reach word3
+make: "directive-for.mk" line 219: no iteration variables in for
+make: "directive-for.mk" line 245: 1 open conditional
+make: "directive-for.mk" line 261: for-less endfor
+make: "directive-for.mk" line 262: if-less endif
+make: "directive-for.mk" line 270: if-less endif
For: new loop 2
For: end for 2
For: end for 1
@@ -35,7 +36,7 @@ For: loop body with outer = o:
endfor
For: end for 1
For: loop body with inner = i:
-make: "directive-for.mk" line 303: newline-item=(a)
+make: "directive-for.mk" line 318: newline-item=(a)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/directive-for.mk b/unit-tests/directive-for.mk
index 22b9f78e5fe1..becc314226dc 100755
--- a/unit-tests/directive-for.mk
+++ b/unit-tests/directive-for.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for.mk,v 1.22 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: directive-for.mk,v 1.24 2023/12/06 22:28:20 rillig Exp $
#
# Tests for the .for directive.
#
@@ -25,7 +25,7 @@ NUMBERS+= ${num}
# The .for loop also works for multiple iteration variables.
-# This is something that the modifier :@ cannot do.
+# This is something that the modifier :@ cannot do as easily.
.for name value in VARNAME value NAME2 value2
${name}= ${value}
.endfor
@@ -189,15 +189,30 @@ INDIRECT= direct
.endfor
+# Regular global variables and the "variables" from the .for loop don't
+# interfere with each other. In the following snippet, the variable 'DIRECT'
+# is used both as a global variable, as well as an iteration variable in the
+# .for loop. The expression '${INDIRECT}' refers to the global variable, not
+# to the one from the .for loop.
+DIRECT= global
+INDIRECT= ${DIRECT}
+.for DIRECT in iteration
+. if "${DIRECT} ${INDIRECT}" != "iteration global"
+. error
+. endif
+.endfor
+
+
# XXX: A parse error or evaluation error in the items of the .for loop
# should skip the whole loop. As of 2023-05-09, the loop is expanded as
# usual.
# expect+1: Unknown modifier "Z"
-.for var in word1 ${:Uword2:Z} word3
-. info XXX: Not reached ${var}
+.for var in word1 before-${:Uword2:Z}-after word3
+. info XXX: Should not reach ${var}
.endfor
-# expect-2: XXX: Not reached word1
-# expect-3: XXX: Not reached word3
+# expect-2: XXX: Should not reach word1
+# expect-3: XXX: Should not reach before--after
+# expect-4: XXX: Should not reach word3
# An empty list of variables to the left of the 'in' is a parse error.
diff --git a/unit-tests/directive-ifmake.mk b/unit-tests/directive-ifmake.mk
index 2a0cedda463e..fa0a56c60030 100644
--- a/unit-tests/directive-ifmake.mk
+++ b/unit-tests/directive-ifmake.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-ifmake.mk,v 1.11 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: directive-ifmake.mk,v 1.12 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the .ifmake directive, which provides a shortcut for asking
# whether a certain target is requested to be made from the command line.
@@ -75,7 +75,7 @@
. error
.endif
-# A condition that consists of a variable expression only (without any
+# A condition that consists of an expression only (without any
# comparison operator) can be used with .if and the other .ifxxx directives.
.ifmake ${:Ufirst}
# expect+1: ok
diff --git a/unit-tests/directive-ifndef.mk b/unit-tests/directive-ifndef.mk
index 95b8df6bcbb8..44eec55b4a87 100644
--- a/unit-tests/directive-ifndef.mk
+++ b/unit-tests/directive-ifndef.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-ifndef.mk,v 1.8 2023/06/19 20:44:06 rillig Exp $
+# $NetBSD: directive-ifndef.mk,v 1.9 2023/10/19 18:24:33 rillig Exp $
#
# Tests for the .ifndef directive, which can be used for multiple-inclusion
# guards. In contrast to C, where #ifndef and #define nicely line up the
@@ -50,4 +50,38 @@ DEFINED=
. error
.endif
+
+# The negation from the 'if-not-defined' directive only applies to bare words,
+# but not to numbers, quoted strings or expressions. Those are evaluated
+# without extra negation, just like in a plain '.if' directive.
+.ifndef 0
+. error
+.endif
+.ifndef 1
+.else
+. error
+.endif
+.ifndef ""
+. error
+.endif
+.ifndef "word"
+.else
+. error
+.endif
+.ifndef ${:UUNDEFINED}
+.else
+. error
+.endif
+.ifndef ${:UDEFINED}
+. error
+.endif
+.ifndef ${:U0}
+. error
+.endif
+.ifndef ${:U1}
+.else
+. error
+.endif
+
+
all:
diff --git a/unit-tests/directive-include-guard.exp b/unit-tests/directive-include-guard.exp
index ba1ea1a8b388..70d23a19fe7c 100644
--- a/unit-tests/directive-include-guard.exp
+++ b/unit-tests/directive-include-guard.exp
@@ -2,6 +2,10 @@ Parse_PushInput: file variable-ifndef.tmp, line 1
Skipping 'variable-ifndef.tmp' because 'VARIABLE_IFNDEF' is defined
Parse_PushInput: file variable-ifndef-reuse.tmp, line 1
Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined
+Parse_PushInput: file variable-ifndef-zero.tmp, line 1
+Parse_PushInput: file variable-ifndef-zero.tmp, line 1
+Parse_PushInput: file variable-ifndef-one.tmp, line 1
+Parse_PushInput: file variable-ifndef-one.tmp, line 1
Parse_PushInput: file comments.tmp, line 1
Skipping 'comments.tmp' because 'COMMENTS' is defined
Parse_PushInput: file variable-if.tmp, line 1
@@ -10,10 +14,16 @@ Parse_PushInput: file variable-if-reuse.tmp, line 1
Skipping 'variable-if-reuse.tmp' because 'VARIABLE_IF' is defined
Parse_PushInput: file variable-if-triple-negation.tmp, line 1
Parse_PushInput: file variable-if-triple-negation.tmp, line 1
+Parse_PushInput: file variable-if-spaced.tmp, line 1
+Parse_PushInput: file variable-if-spaced.tmp, line 1
+Parse_PushInput: file variable-if-parenthesized.tmp, line 1
+Parse_PushInput: file variable-if-parenthesized.tmp, line 1
Parse_PushInput: file variable-ifdef-negated.tmp, line 1
Parse_PushInput: file variable-ifdef-negated.tmp, line 1
Parse_PushInput: file variable-name-mismatch.tmp, line 1
Parse_PushInput: file variable-name-mismatch.tmp, line 1
+Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
+Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
Parse_PushInput: file variable-name-exclamation.tmp, line 1
Parse_PushInput: file variable-name-exclamation.tmp, line 1
Parse_PushInput: file variable-name-exclamation-middle.tmp, line 1
@@ -85,4 +95,10 @@ Parse_PushInput: file target-already-defined.tmp, line 1
Skipping 'target-already-defined.tmp' because 'target-already-defined' is defined
Parse_PushInput: file target-name-exclamation.tmp, line 1
Parse_PushInput: file target-name-exclamation.tmp, line 1
+Parse_PushInput: file target-name-parenthesized.tmp, line 1
+Parse_PushInput: file target-name-parenthesized.tmp, line 1
+Parse_PushInput: file target-call-parenthesized.tmp, line 1
+Parse_PushInput: file target-call-parenthesized.tmp, line 1
+Parse_PushInput: file multiline.tmp, line 1
+Skipping 'multiline.tmp' because 'MULTILINE' is defined
exit status 0
diff --git a/unit-tests/directive-include-guard.mk b/unit-tests/directive-include-guard.mk
index fcd80ca8a1b3..85c0242c2009 100644
--- a/unit-tests/directive-include-guard.mk
+++ b/unit-tests/directive-include-guard.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-include-guard.mk,v 1.12 2023/08/11 04:56:31 rillig Exp $
+# $NetBSD: directive-include-guard.mk,v 1.16 2023/12/17 14:07:22 rillig Exp $
#
# Tests for multiple-inclusion guards in makefiles.
#
@@ -15,8 +15,9 @@
# .endif
#
# When such a file is included for the second or later time, and the guard
-# variable or the guard target is defined, including the file has no effect,
-# as all its content is skipped.
+# variable or the guard target is defined, the file is skipped completely, as
+# including it would not have any effect, not even on the special variable
+# '.MAKE.MAKEFILES', as that variable skips duplicate pathnames.
#
# See also:
# https://gcc.gnu.org/onlinedocs/cppinternals/Guard-Macros.html
@@ -27,7 +28,7 @@
# This is the canonical form of a variable-based multiple-inclusion guard.
-INCS+= variable-ifndef
+CASES+= variable-ifndef
LINES.variable-ifndef= \
'.ifndef VARIABLE_IFNDEF' \
'VARIABLE_IFNDEF=' \
@@ -38,7 +39,7 @@ LINES.variable-ifndef= \
# A file that reuses a guard from a previous file (or whose guard is defined
# for any other reason) is only processed once, to see whether it is guarded.
# Its content is skipped, therefore the syntax error is not detected.
-INCS+= variable-ifndef-reuse
+CASES+= variable-ifndef-reuse
LINES.variable-ifndef-reuse= \
'.ifndef VARIABLE_IFNDEF' \
'syntax error' \
@@ -46,8 +47,27 @@ LINES.variable-ifndef-reuse= \
# expect: Parse_PushInput: file variable-ifndef-reuse.tmp, line 1
# expect: Skipping 'variable-ifndef-reuse.tmp' because 'VARIABLE_IFNDEF' is defined
+# The guard variable cannot be a number, as numbers are interpreted
+# differently from bare words.
+CASES+= variable-ifndef-zero
+LINES.variable-ifndef-zero= \
+ '.ifndef 0e0' \
+ 'syntax error' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
+# expect: Parse_PushInput: file variable-ifndef-zero.tmp, line 1
+
+# The guard variable cannot be a number, as numbers are interpreted
+# differently from bare words.
+CASES+= variable-ifndef-one
+LINES.variable-ifndef-one= \
+ '.ifndef 1' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
+# expect: Parse_PushInput: file variable-ifndef-one.tmp, line 1
+
# Comments and empty lines do not affect the multiple-inclusion guard.
-INCS+= comments
+CASES+= comments
LINES.comments= \
'\# comment' \
'' \
@@ -62,7 +82,7 @@ LINES.comments= \
# An alternative form uses the 'defined' function. It is more verbose than
# the canonical form but avoids the '.ifndef' directive, as that directive is
# not commonly used.
-INCS+= variable-if
+CASES+= variable-if
LINES.variable-if= \
'.if !defined(VARIABLE_IF)' \
'VARIABLE_IF=' \
@@ -73,7 +93,7 @@ LINES.variable-if= \
# A file that reuses a guard from a previous file (or whose guard is defined
# for any other reason) is only processed once, to see whether it is guarded.
# Its content is skipped, therefore the syntax error is not detected.
-INCS+= variable-if-reuse
+CASES+= variable-if-reuse
LINES.variable-if-reuse= \
'.if !defined(VARIABLE_IF)' \
'syntax error' \
@@ -83,7 +103,7 @@ LINES.variable-if-reuse= \
# Triple negation is so uncommon that it's not recognized, even though it has
# the same effect as a single negation.
-INCS+= variable-if-triple-negation
+CASES+= variable-if-triple-negation
LINES.variable-if-triple-negation= \
'.if !!!defined(VARIABLE_IF_TRIPLE_NEGATION)' \
'VARIABLE_IF_TRIPLE_NEGATION=' \
@@ -91,9 +111,29 @@ LINES.variable-if-triple-negation= \
# expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
# expect: Parse_PushInput: file variable-if-triple-negation.tmp, line 1
+# If the guard variable is enclosed in spaces, it does not have an effect, as
+# that form is not common in practice.
+CASES+= variable-if-spaced
+LINES.variable-if-spaced= \
+ '.if !defined( VARIABLE_IF_SPACED )' \
+ 'VARIABLE_IF_SPACED=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if-spaced.tmp, line 1
+# expect: Parse_PushInput: file variable-if-spaced.tmp, line 1
+
+# If the guard variable condition is enclosed in parentheses, it does not have
+# an effect, as that form is not common in practice.
+CASES+= variable-if-parenthesized
+LINES.variable-if-parenthesized= \
+ '.if (!defined(VARIABLE_IF_PARENTHESIZED))' \
+ 'VARIABLE_IF_PARENTHESIZED=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-if-parenthesized.tmp, line 1
+# expect: Parse_PushInput: file variable-if-parenthesized.tmp, line 1
+
# A conditional other than '.if' or '.ifndef' does not guard the file, even if
# it is otherwise equivalent to the above accepted forms.
-INCS+= variable-ifdef-negated
+CASES+= variable-ifdef-negated
LINES.variable-ifdef-negated= \
'.ifdef !VARIABLE_IFDEF_NEGATED' \
'VARIABLE_IFDEF_NEGATED=' \
@@ -102,7 +142,7 @@ LINES.variable-ifdef-negated= \
# expect: Parse_PushInput: file variable-ifdef-negated.tmp, line 1
# The variable names in the '.if' and the assignment must be the same.
-INCS+= variable-name-mismatch
+CASES+= variable-name-mismatch
LINES.variable-name-mismatch= \
'.ifndef VARIABLE_NAME_MISMATCH' \
'VARIABLE_NAME_DIFFERENT=' \
@@ -110,13 +150,23 @@ LINES.variable-name-mismatch= \
# expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
# expect: Parse_PushInput: file variable-name-mismatch.tmp, line 1
+# If the guard variable condition is enclosed in parentheses, it does not have
+# an effect, as that form is not common in practice.
+CASES+= variable-ifndef-parenthesized
+LINES.variable-ifndef-parenthesized= \
+ '.ifndef (VARIABLE_IFNDEF_PARENTHESIZED)' \
+ 'VARIABLE_IFNDEF_PARENTHESIZED=' \
+ '.endif'
+# expect: Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
+# expect: Parse_PushInput: file variable-ifndef-parenthesized.tmp, line 1
+
# The variable name '!VARNAME' cannot be used in an '.ifndef' directive, as
# the '!' would be a negation. It is syntactically valid in a '.if !defined'
# condition, but this case is so uncommon that the guard mechanism doesn't
# accept '!' in the guard variable name. Furthermore, when defining the
# variable, the character '!' has to be escaped, to prevent it from being
# interpreted as the '!' dependency operator.
-INCS+= variable-name-exclamation
+CASES+= variable-name-exclamation
LINES.variable-name-exclamation= \
'.if !defined(!VARIABLE_NAME_EXCLAMATION)' \
'${:U!}VARIABLE_NAME_EXCLAMATION=' \
@@ -124,11 +174,11 @@ LINES.variable-name-exclamation= \
# expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
# expect: Parse_PushInput: file variable-name-exclamation.tmp, line 1
-# A variable name can contain a '!' in the middle, as that character is
-# interpreted as an ordinary character in conditions as well as on the left
-# side of a variable assignment. For guard variable names, the '!' is not
-# supported in any place, though.
-INCS+= variable-name-exclamation-middle
+# In general, a variable name can contain a '!' in the middle, as that
+# character is interpreted as an ordinary character in conditions as well as
+# on the left side of a variable assignment. For guard variable names, the
+# '!' is not supported in any place, though.
+CASES+= variable-name-exclamation-middle
LINES.variable-name-exclamation-middle= \
'.ifndef VARIABLE_NAME!MIDDLE' \
'VARIABLE_NAME!MIDDLE=' \
@@ -141,7 +191,7 @@ LINES.variable-name-exclamation-middle= \
# where parentheses or braces are handled inconsistently to make this naming
# choice a bad idea, therefore these characters are not allowed in guard
# variable names.
-INCS+= variable-name-parentheses
+CASES+= variable-name-parentheses
LINES.variable-name-parentheses= \
'.ifndef VARIABLE_NAME(&)PARENTHESES' \
'VARIABLE_NAME(&)PARENTHESES=' \
@@ -150,7 +200,7 @@ LINES.variable-name-parentheses= \
# expect: Parse_PushInput: file variable-name-parentheses.tmp, line 1
# The guard condition must consist of only the guard variable, nothing else.
-INCS+= variable-ifndef-plus
+CASES+= variable-ifndef-plus
LINES.variable-ifndef-plus= \
'.ifndef VARIABLE_IFNDEF_PLUS && VARIABLE_IFNDEF_SECOND' \
'VARIABLE_IFNDEF_PLUS=' \
@@ -160,7 +210,7 @@ LINES.variable-ifndef-plus= \
# expect: Parse_PushInput: file variable-ifndef-plus.tmp, line 1
# The guard condition must consist of only the guard variable, nothing else.
-INCS+= variable-if-plus
+CASES+= variable-if-plus
LINES.variable-if-plus= \
'.if !defined(VARIABLE_IF_PLUS) && !defined(VARIABLE_IF_SECOND)' \
'VARIABLE_IF_PLUS=' \
@@ -171,7 +221,7 @@ LINES.variable-if-plus= \
# The variable name in an '.ifndef' guard must be given directly, it must not
# contain any '$' expression.
-INCS+= variable-ifndef-indirect
+CASES+= variable-ifndef-indirect
LINES.variable-ifndef-indirect= \
'.ifndef $${VARIABLE_IFNDEF_INDIRECT:L}' \
'VARIABLE_IFNDEF_INDIRECT=' \
@@ -181,7 +231,7 @@ LINES.variable-ifndef-indirect= \
# The variable name in an '.if' guard must be given directly, it must not
# contain any '$' expression.
-INCS+= variable-if-indirect
+CASES+= variable-if-indirect
LINES.variable-if-indirect= \
'.if !defined($${VARIABLE_IF_INDIRECT:L})' \
'VARIABLE_IF_INDIRECT=' \
@@ -193,7 +243,7 @@ LINES.variable-if-indirect= \
# characters and underscores. The place where the guard variable is defined
# is more flexible, as long as the variable is defined at the point where the
# file is included the next time.
-INCS+= variable-assign-indirect
+CASES+= variable-assign-indirect
LINES.variable-assign-indirect= \
'.ifndef VARIABLE_ASSIGN_INDIRECT' \
'$${VARIABLE_ASSIGN_INDIRECT:L}=' \
@@ -203,7 +253,7 @@ LINES.variable-assign-indirect= \
# The time at which the guard variable is defined doesn't matter, as long as
# it is defined at the point where the file is included the next time.
-INCS+= variable-assign-late
+CASES+= variable-assign-late
LINES.variable-assign-late= \
'.ifndef VARIABLE_ASSIGN_LATE' \
'VARIABLE_ASSIGN_LATE_OTHER=' \
@@ -214,7 +264,7 @@ LINES.variable-assign-late= \
# The time at which the guard variable is defined doesn't matter, as long as
# it is defined at the point where the file is included the next time.
-INCS+= variable-assign-nested
+CASES+= variable-assign-nested
LINES.variable-assign-nested= \
'.ifndef VARIABLE_ASSIGN_NESTED' \
'. if 1' \
@@ -231,7 +281,7 @@ LINES.variable-assign-nested= \
# skips almost all lines, as they are irrelevant, but the structure of the
# top-level '.if/.endif' conditional can be determined reliably enough to
# decide whether the file is guarded.
-INCS+= variable-already-defined
+CASES+= variable-already-defined
LINES.variable-already-defined= \
'.ifndef VARIABLE_ALREADY_DEFINED' \
'VARIABLE_ALREADY_DEFINED=' \
@@ -244,7 +294,7 @@ VARIABLE_ALREADY_DEFINED=
# the file is processed but its content is skipped. If that same guard
# variable is undefined when the file is included the second time, the file is
# processed as usual.
-INCS+= variable-defined-then-undefined
+CASES+= variable-defined-then-undefined
LINES.variable-defined-then-undefined= \
'.ifndef VARIABLE_DEFINED_THEN_UNDEFINED' \
'.endif'
@@ -258,7 +308,7 @@ UNDEF_BETWEEN.variable-defined-then-undefined= \
# several, as each of these conditionals would require its separate guard.
# This case is not expected to occur in practice, as the two parts would
# rather be split into separate files.
-INCS+= variable-two-times
+CASES+= variable-two-times
LINES.variable-two-times= \
'.ifndef VARIABLE_TWO_TIMES_1' \
'VARIABLE_TWO_TIMES_1=' \
@@ -275,7 +325,7 @@ LINES.variable-two-times= \
# Choosing unique guard names is the responsibility of the makefile authors.
# A typical pattern of guard variable names is '${PROJECT}_${DIR}_${FILE}_MK'.
# System-provided files typically start the guard names with '_'.
-INCS+= variable-clash
+CASES+= variable-clash
LINES.variable-clash= \
${LINES.variable-if}
# expect: Parse_PushInput: file variable-clash.tmp, line 1
@@ -283,7 +333,7 @@ LINES.variable-clash= \
# The conditional must come before the assignment, otherwise the conditional
# is useless, as it always evaluates to false.
-INCS+= variable-swapped
+CASES+= variable-swapped
LINES.variable-swapped= \
'SWAPPED=' \
'.ifndef SWAPPED' \
@@ -294,7 +344,7 @@ LINES.variable-swapped= \
# If the guard variable is undefined between the first and the second time the
# file is included, the guarded file is included again.
-INCS+= variable-undef-between
+CASES+= variable-undef-between
LINES.variable-undef-between= \
'.ifndef VARIABLE_UNDEF_BETWEEN' \
'VARIABLE_UNDEF_BETWEEN=' \
@@ -306,7 +356,7 @@ UNDEF_BETWEEN.variable-undef-between= \
# If the guard variable is undefined while the file is included the first
# time, the guard does not have an effect, and the file is included again.
-INCS+= variable-undef-inside
+CASES+= variable-undef-inside
LINES.variable-undef-inside= \
'.ifndef VARIABLE_UNDEF_INSIDE' \
'VARIABLE_UNDEF_INSIDE=' \
@@ -317,7 +367,7 @@ LINES.variable-undef-inside= \
# If the file does not define the guard variable, the guard does not have an
# effect, and the file is included again.
-INCS+= variable-not-defined
+CASES+= variable-not-defined
LINES.variable-not-defined= \
'.ifndef VARIABLE_NOT_DEFINED' \
'.endif'
@@ -325,7 +375,7 @@ LINES.variable-not-defined= \
# expect: Parse_PushInput: file variable-not-defined.tmp, line 1
# The outermost '.if' must not have an '.elif' branch.
-INCS+= elif
+CASES+= elif
LINES.elif= \
'.ifndef ELIF' \
'ELIF=' \
@@ -336,7 +386,7 @@ LINES.elif= \
# When a file with an '.if/.elif/.endif' conditional at the top level is
# included, it is never optimized, as one of its branches is taken.
-INCS+= elif-reuse
+CASES+= elif-reuse
LINES.elif-reuse= \
'.ifndef ELIF' \
'syntax error' \
@@ -346,7 +396,7 @@ LINES.elif-reuse= \
# expect: Parse_PushInput: file elif-reuse.tmp, line 1
# The outermost '.if' must not have an '.else' branch.
-INCS+= else
+CASES+= else
LINES.else= \
'.ifndef ELSE' \
'ELSE=' \
@@ -357,7 +407,7 @@ LINES.else= \
# When a file with an '.if/.else/.endif' conditional at the top level is
# included, it is never optimized, as one of its branches is taken.
-INCS+= else-reuse
+CASES+= else-reuse
LINES.else-reuse= \
'.ifndef ELSE' \
'syntax error' \
@@ -368,7 +418,7 @@ LINES.else-reuse= \
# The inner '.if' directives may have an '.elif' or '.else', and it doesn't
# matter which of their branches are taken.
-INCS+= inner-if-elif-else
+CASES+= inner-if-elif-else
LINES.inner-if-elif-else= \
'.ifndef INNER_IF_ELIF_ELSE' \
'INNER_IF_ELIF_ELSE=' \
@@ -394,7 +444,7 @@ LINES.inner-if-elif-else= \
# usually chosen according to a pattern that doesn't interfere with real
# target names, they don't need to be declared '.PHONY' as they don't generate
# filesystem operations.
-INCS+= target
+CASES+= target
LINES.target= \
'.if !target(__target.tmp__)' \
'__target.tmp__: .NOTMAIN' \
@@ -405,7 +455,7 @@ LINES.target= \
# When used for system files, the target name may include '<' and '>', for
# symmetry with the '.include <sys.mk>' directive. The characters '<' and '>'
# are ordinary characters.
-INCS+= target-sys
+CASES+= target-sys
LINES.target-sys= \
'.if !target(__<target-sys.tmp>__)' \
'__<target-sys.tmp>__: .NOTMAIN' \
@@ -419,7 +469,7 @@ LINES.target-sys= \
# and once for determining the guard name. This double evaluation should not
# matter in practice, as guard expressions are expected to be simple,
# deterministic and without side effects.
-INCS+= target-indirect
+CASES+= target-indirect
LINES.target-indirect= \
'.if !target($${target-indirect.tmp:L})' \
'target-indirect.tmp: .NOTMAIN' \
@@ -432,7 +482,7 @@ LINES.target-indirect= \
# pattern based on the same idea, use __${.PARSEDIR}/${.PARSEFILE}__ instead.
# This form does not work when the basename contains whitespace characters, as
# it is not possible to define a target with whitespace, not even by cheating.
-INCS+= target-indirect-PARSEFILE
+CASES+= target-indirect-PARSEFILE
LINES.target-indirect-PARSEFILE= \
'.if !target(__$${.PARSEFILE}__)' \
'__$${.PARSEFILE}__: .NOTMAIN' \
@@ -442,7 +492,7 @@ LINES.target-indirect-PARSEFILE= \
# Two files with different basenames can both use the same syntactic pattern
# for the target guard name, as the expressions expand to different strings.
-INCS+= target-indirect-PARSEFILE2
+CASES+= target-indirect-PARSEFILE2
LINES.target-indirect-PARSEFILE2= \
'.if !target(__$${.PARSEFILE}__)' \
'__$${.PARSEFILE}__: .NOTMAIN' \
@@ -453,8 +503,8 @@ LINES.target-indirect-PARSEFILE2= \
# Using plain .PARSEFILE without .PARSEDIR leads to name clashes. The include
# guard is the same as in the test case 'target-indirect-PARSEFILE', as the
# guard name only contains the basename but not the directory name. So even
-# without defining the guard variable, the file is considered guarded.
-INCS+= subdir/target-indirect-PARSEFILE
+# without defining the guard target, the file is considered guarded.
+CASES+= subdir/target-indirect-PARSEFILE
LINES.subdir/target-indirect-PARSEFILE= \
'.if !target(__$${.PARSEFILE}__)' \
'.endif'
@@ -463,7 +513,7 @@ LINES.subdir/target-indirect-PARSEFILE= \
# Another common form of guard target is __${.PARSEDIR}/${.PARSEFILE}__
# or __${.PARSEDIR:tA}/${.PARSEFILE}__ to be truly unique.
-INCS+= target-indirect-PARSEDIR-PARSEFILE
+CASES+= target-indirect-PARSEDIR-PARSEFILE
LINES.target-indirect-PARSEDIR-PARSEFILE= \
'.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
'__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
@@ -475,7 +525,7 @@ LINES.target-indirect-PARSEDIR-PARSEFILE= \
# Using the combination of '.PARSEDIR' and '.PARSEFILE', a file in a
# subdirectory gets a different guard target name than the previous one.
-INCS+= subdir/target-indirect-PARSEDIR-PARSEFILE
+CASES+= subdir/target-indirect-PARSEDIR-PARSEFILE
LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
'.if !target(__$${.PARSEDIR}/$${.PARSEFILE}__)' \
'__$${.PARSEDIR}/$${.PARSEFILE}__: .NOTMAIN' \
@@ -487,7 +537,7 @@ LINES.subdir/target-indirect-PARSEDIR-PARSEFILE= \
# If the guard target is not defined when including the file the next time,
# the file is processed again.
-INCS+= target-unguarded
+CASES+= target-unguarded
LINES.target-unguarded= \
'.if !target(target-unguarded)' \
'.endif'
@@ -495,7 +545,7 @@ LINES.target-unguarded= \
# expect: Parse_PushInput: file target-unguarded.tmp, line 1
# The guard condition must consist of only the guard target, nothing else.
-INCS+= target-plus
+CASES+= target-plus
LINES.target-plus= \
'.if !target(target-plus) && 1' \
'target-plus: .NOTMAIN' \
@@ -505,7 +555,7 @@ LINES.target-plus= \
# If the guard target is defined before the file is included the first time,
# the file is read once and then considered guarded.
-INCS+= target-already-defined
+CASES+= target-already-defined
LINES.target-already-defined= \
'.if !target(target-already-defined)' \
'target-already-defined: .NOTMAIN' \
@@ -523,7 +573,7 @@ target-already-defined: .NOTMAIN
# the '\' escapes the '!' from being a dependency operator, but when reading
# the target name, the '\' is kept, resulting in the target name
# '\!target-name-exclamation' instead of '!target-name-exclamation'.
-INCS+= target-name-exclamation
+CASES+= target-name-exclamation
LINES.target-name-exclamation= \
'.if !target(!target-name-exclamation)' \
'\!target-name-exclamation: .NOTMAIN' \
@@ -531,13 +581,47 @@ LINES.target-name-exclamation= \
# expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
# expect: Parse_PushInput: file target-name-exclamation.tmp, line 1
+# If the guard target name is enclosed in spaces, it does not have an effect,
+# as that form is not common in practice.
+CASES+= target-name-parenthesized
+LINES.target-name-parenthesized= \
+ '.if !target( target-name-parenthesized )' \
+ 'target-name-parenthesized: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-name-parenthesized.tmp, line 1
+# expect: Parse_PushInput: file target-name-parenthesized.tmp, line 1
+
+# If the guard target condition is enclosed in parentheses, it does not have
+# an effect, as that form is not common in practice.
+CASES+= target-call-parenthesized
+LINES.target-call-parenthesized= \
+ '.if (!target(target-call-parenthesized))' \
+ 'target-call-parenthesized: .NOTMAIN' \
+ '.endif'
+# expect: Parse_PushInput: file target-call-parenthesized.tmp, line 1
+# expect: Parse_PushInput: file target-call-parenthesized.tmp, line 1
+
+# If the '.if' or '.ifndef' directive spans more than a single line, it is
+# still recognized as a guard condition. This case is entirely uncommon, but
+# at the point where the guard condition is checked, line continuations have
+# already been converted to spaces.
+CASES+= multiline
+LINES.multiline= \
+ '.\' \
+ ' ifndef \' \
+ ' MULTILINE' \
+ 'MULTILINE=' \
+ '.endif'
+# expect: Parse_PushInput: file multiline.tmp, line 1
+# expect: Skipping 'multiline.tmp' because 'MULTILINE' is defined
+
# Now run all test cases by including each of the files twice and looking at
# the debug output. The files that properly guard against multiple inclusion
# generate a 'Skipping' line, the others repeat the 'Parse_PushInput' line.
#
# Some debug output lines are suppressed in the .exp file, see ./Makefile.
-.for i in ${INCS}
+.for i in ${CASES}
. for fname in $i.tmp
_:= ${fname:H:N.:@dir@${:!mkdir -p ${dir}!}@}
_!= printf '%s\n' ${LINES.$i} > ${fname}
diff --git a/unit-tests/directive-warning.mk b/unit-tests/directive-warning.mk
index b4c8f4730b78..bf0683f8911f 100644
--- a/unit-tests/directive-warning.mk
+++ b/unit-tests/directive-warning.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-warning.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: directive-warning.mk,v 1.9 2023/12/17 09:44:00 rillig Exp $
#
# Tests for the .warning directive.
#
@@ -16,7 +16,7 @@
.warnin message # misspelled
# expect+1: Missing argument for ".warning"
.warning # "Missing argument"
-.warning message # expect+0: message
+.warning message # expect+0: warning: message
# expect+1: Unknown directive "warnings"
.warnings # misspelled
# expect+1: Unknown directive "warnings"
diff --git a/unit-tests/directive.mk b/unit-tests/directive.mk
index d51f0c2c8747..61938360dfc7 100644
--- a/unit-tests/directive.mk
+++ b/unit-tests/directive.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive.mk,v 1.8 2023/08/19 11:09:02 rillig Exp $
+# $NetBSD: directive.mk,v 1.9 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the preprocessing directives, such as .if or .info.
@@ -13,7 +13,7 @@
# expect+1: Unknown directive "indented"
. indented tab
-# Directives must be written directly, not indirectly via variable
+# Directives must be written directly, not indirectly via
# expressions.
# expect+1: Unknown directive ""
.${:Uinfo} directives cannot be indirect
diff --git a/unit-tests/escape.exp b/unit-tests/escape.exp
index 6238e27b0191..ff9c8b7cf811 100644
--- a/unit-tests/escape.exp
+++ b/unit-tests/escape.exp
@@ -1,5 +1,4 @@
var-1bs
-printf "%s=:%s:\n" VAR1BS 111\\111; printf "%s=:%s:\n" VAR1BSa 111\\aaa; printf "%s=:%s:\n" VAR1BSA 111\\aaa; printf "%s=:%s:\n" VAR1BSda 111\\\$\{a\}; printf "%s=:%s:\n" VAR1BSdA 111\\\$\{A\}; printf "%s=:%s:\n" VAR1BSc 111\#\ backslash\ escapes\ comment\ char,\ so\ this\ is\ part\ of\ the\ value; printf "%s=:%s:\n" VAR1BSsc 111\\\ ;
VAR1BS=:111\111:
VAR1BSa=:111\aaa:
VAR1BSA=:111\aaa:
@@ -8,25 +7,22 @@ VAR1BSdA=:111\${A}:
VAR1BSc=:111# backslash escapes comment char, so this is part of the value:
VAR1BSsc=:111\ :
var-2bs
-printf "%s=:%s:\n" VAR2BS 222\\\\222; printf "%s=:%s:\n" VAR2BSa 222\\\\aaa; printf "%s=:%s:\n" VAR2BSA 222\\\\aaa; printf "%s=:%s:\n" VAR2BSda 222\\\\\$\{a\}; printf "%s=:%s:\n" VAR2BSdA 222\\\\\$\{A\}; printf "%s=:%s:\n" VAR2BSc 222\\\\; printf "%s=:%s:\n" VAR2BSsc 222\\\\;
-VAR2BS=:222\\222:
-VAR2BSa=:222\\aaa:
-VAR2BSA=:222\\aaa:
-VAR2BSda=:222\\${a}:
-VAR2BSdA=:222\\${A}:
-VAR2BSc=:222\\:
-VAR2BSsc=:222\\:
-var-1bsnl
-printf "%s=:%s:\n" VAR1BSNL 111\ 111; printf "%s=:%s:\n" VAR1BSNLa 111\ aaa; printf "%s=:%s:\n" VAR1BSNLA 111\ aaa; printf "%s=:%s:\n" VAR1BSNLda 111\ \$\{a\}; printf "%s=:%s:\n" VAR1BSNLdA 111\ \$\{A\}; printf "%s=:%s:\n" VAR1BSNLc 111; printf "%s=:%s:\n" VAR1BSNLsc 111;
-VAR1BSNL=:111 111:
-VAR1BSNLa=:111 aaa:
-VAR1BSNLA=:111 aaa:
-VAR1BSNLda=:111 ${a}:
-VAR1BSNLdA=:111 ${A}:
-VAR1BSNLc=:111:
-VAR1BSNLsc=:111:
+VAR2.BS=:222\\222:
+VAR2.BS.a=:222\\aaa:
+VAR2.BS.A=:222\\aaa:
+VAR2.BS.d.a=:222\\${a}:
+VAR2.BS.d.A=:222\\${A}:
+VAR2.BS.c=:222\\:
+VAR2.BS.s.c=:222\\:
+var-1bs-nl
+VAR1.BS-NL=:111 111:
+VAR1.BS-NL.a=:111 aaa:
+VAR1.BS-NL.A=:111 aaa:
+VAR1.BS-NL.d-a=:111 ${a}:
+VAR1.BS-NL.d-A=:111 ${A}:
+VAR1.BS-NL.c=:111:
+VAR1.BS-NL.s-c=:111:
var-2bsnl
-printf "%s=:%s:\n" VAR2BSNL 222\\\\; printf "%s=:%s:\n" VAR2BSNLa 222\\\\; printf "%s=:%s:\n" VAR2BSNLA 222\\\\; printf "%s=:%s:\n" VAR2BSNLda 222\\\\; printf "%s=:%s:\n" VAR2BSNLdA 222\\\\; printf "%s=:%s:\n" VAR2BSNLc 222\\\\; printf "%s=:%s:\n" VAR2BSNLsc 222\\\\;
VAR2BSNL=:222\\:
VAR2BSNLa=:222\\:
VAR2BSNLA=:222\\:
@@ -35,7 +31,6 @@ VAR2BSNLdA=:222\\:
VAR2BSNLc=:222\\:
VAR2BSNLsc=:222\\:
var-3bsnl
-printf "%s=:%s:\n" VAR3BSNL 333\\\\\ 333=; printf "%s=:%s:\n" VAR3BSNLa 333\\\\\ aaa=; printf "%s=:%s:\n" VAR3BSNLA 333\\\\\ aaa=; printf "%s=:%s:\n" VAR3BSNLda 333\\\\\ \$\{a\}=; printf "%s=:%s:\n" VAR3BSNLdA 333\\\\\ \$\{A\}=; printf "%s=:%s:\n" VAR3BSNLc 333\\\\; printf "%s=:%s:\n" VAR3BSNLsc 333\\\\;
VAR3BSNL=:333\\ 333=:
VAR3BSNLa=:333\\ aaa=:
VAR3BSNLA=:333\\ aaa=:
@@ -44,7 +39,6 @@ VAR3BSNLdA=:333\\ ${A}=:
VAR3BSNLc=:333\\:
VAR3BSNLsc=:333\\:
var-1bsnl-space
-printf "%s=:%s:\n" VAR1BSNL00 first\ line; printf "%s=:%s:\n" VAR1BSNL0 first\ line\ no\ space\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLs first\ line\ one\ space\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLss first\ line\ two\ spaces\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLt first\ line\ one\ tab\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLtt first\ line\ two\ tabs\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLxx first\ line\ many\ spaces\ and\ tabs\ \[\ \ \ \ \]\ on\ second\ line;
VAR1BSNL00=:first line:
VAR1BSNL0=:first line no space on second line:
VAR1BSNLs=:first line one space on second line:
diff --git a/unit-tests/escape.mk b/unit-tests/escape.mk
index 8bdd3ad2ab49..a363a19f1946 100644
--- a/unit-tests/escape.mk
+++ b/unit-tests/escape.mk
@@ -1,4 +1,4 @@
-# $NetBSD: escape.mk,v 1.14 2020/11/03 17:38:45 rillig Exp $
+# $NetBSD: escape.mk,v 1.15 2023/10/19 18:24:33 rillig Exp $
#
# Test backslash escaping.
@@ -53,7 +53,7 @@ should continue the comment. \
__printvars: .USE .MADE
@echo ${.TARGET}
- ${.ALLSRC:@v@ printf "%s=:%s:\n" ${v:Q} ${${v}:Q}; @}
+ @${.ALLSRC:@v@ printf "%s=:%s:\n" ${v:Q} ${${v}:Q}; @}
# Embedded backslash in variable should be taken literally.
#
@@ -83,7 +83,8 @@ all: var-2bs
var-2bs: .PHONY __printvars VAR2BS VAR2BSa VAR2BSA VAR2BSda VAR2BSdA \
VAR2BSc VAR2BSsc
-# Backslash-newline in a variable setting is replaced by a single space.
+# In a variable assignment, when the sequence <backslash><newline> occurs at
+# the end of a physical line, it is replaced with a single space.
#
VAR1BSNL= 111\
111
diff --git a/unit-tests/hanoi-include.mk b/unit-tests/hanoi-include.mk
index 5e7d5c476dfc..f243af83d1df 100644
--- a/unit-tests/hanoi-include.mk
+++ b/unit-tests/hanoi-include.mk
@@ -1,4 +1,4 @@
-# $NetBSD: hanoi-include.mk,v 1.4 2023/01/19 22:48:42 rillig Exp $
+# $NetBSD: hanoi-include.mk,v 1.5 2023/10/19 18:24:33 rillig Exp $
#
# Implements the Towers of Hanoi puzzle, demonstrating a bunch of more or less
# useful programming techniques:
@@ -6,14 +6,15 @@
# * default assignment using the ?= assignment operator
# * including the same file recursively (rather unusual)
# * extracting the current value of a variable using the .for loop
-# * using shell commands for calculations since make is a text processor
# * using the :: dependency operator for adding commands to a target
# * on-the-fly variable assignment expressions using the ::= modifier
#
# usage:
# env N=3 make -r -f hanoi-include.mk
#
-# endless loop, since command line variables cannot be overridden:
+# Specifying N in the command line instead of in the environment would produce
+# an endless loop, since variables from the command line cannot be overridden
+# by global variables:
# make -r -f hanoi-include.mk N=3
N?= 5 # Move this number of disks ...
diff --git a/unit-tests/jobs-error-indirect.exp b/unit-tests/jobs-error-indirect.exp
index 5c5a3801f4f6..79843a235666 100644
--- a/unit-tests/jobs-error-indirect.exp
+++ b/unit-tests/jobs-error-indirect.exp
@@ -2,7 +2,7 @@ false
*** [indirect] Error code 1
make: stopped in unit-tests
-1 error
+make: 1 error
make: stopped in unit-tests
exit status 2
diff --git a/unit-tests/jobs-error-nested-make.exp b/unit-tests/jobs-error-nested-make.exp
index 88c32ab8d1f6..2baf893c6623 100644
--- a/unit-tests/jobs-error-nested-make.exp
+++ b/unit-tests/jobs-error-nested-make.exp
@@ -3,7 +3,7 @@ false
*** [nested] Error code 1
make: stopped in unit-tests
-1 error
+make: 1 error
make: stopped in unit-tests
diff --git a/unit-tests/jobs-error-nested.exp b/unit-tests/jobs-error-nested.exp
index f96b5d016777..873613d40d48 100644
--- a/unit-tests/jobs-error-nested.exp
+++ b/unit-tests/jobs-error-nested.exp
@@ -3,13 +3,13 @@ false
*** [nested] Error code 1
make: stopped in unit-tests
-1 error
+make: 1 error
make: stopped in unit-tests
*** [all] Error code 2
make: stopped in unit-tests
-1 error
+make: 1 error
make: stopped in unit-tests
exit status 2
diff --git a/unit-tests/lint.mk b/unit-tests/lint.mk
index 5db417639d0b..431143c644ee 100755
--- a/unit-tests/lint.mk
+++ b/unit-tests/lint.mk
@@ -1,4 +1,4 @@
-# $NetBSD: lint.mk,v 1.4 2021/01/30 13:50:18 rillig Exp $
+# $NetBSD: lint.mk,v 1.5 2023/11/19 21:47:52 rillig Exp $
#
# Demonstrates stricter checks that are only enabled in lint mode, using the
# option -dL.
@@ -6,7 +6,7 @@
# 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
+# makefile, in Parse_File. After that, when expanding expressions
# in shell commands, the parse errors were not checked again.
# Ouch: as of 2020-08-03, the variable is malformed and parsing stops
diff --git a/unit-tests/moderrs.exp b/unit-tests/moderrs.exp
index 9d8bd308c36c..6b41241b6800 100644
--- a/unit-tests/moderrs.exp
+++ b/unit-tests/moderrs.exp
@@ -9,13 +9,13 @@ make: Unknown modifier "Z"
VAR:Z=before-inner}-after
unclosed-direct:
-want: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
-make: Unclosed variable expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
+want: Unclosed expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
+make: Unclosed expression, expecting '}' for modifier "S,V,v," of variable "VAR" with value "Thevariable"
VAR:S,V,v,=Thevariable
unclosed-indirect:
-want: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
-make: Unclosed variable expression after indirect modifier, expecting '}' for variable "VAR"
+want: Unclosed expression after indirect modifier, expecting '}' for variable "VAR"
+make: Unclosed expression after indirect modifier, expecting '}' for variable "VAR"
VAR:S,V,v,=Thevariable
unfinished-indirect:
@@ -33,7 +33,7 @@ make: Unfinished modifier for "UNDEF" ('@' missing)
1 2 3
loop-close:
-make: Unclosed variable expression, expecting '}' for modifier "@var@${var}}...@" of variable "UNDEF" with value "1}... 2}... 3}..."
+make: Unclosed expression, expecting '}' for modifier "@var@${var}}...@" of variable "UNDEF" with value "1}... 2}... 3}..."
1}... 2}... 3}...
1}... 2}... 3}...
@@ -67,7 +67,7 @@ make: Unfinished modifier for "VAR" (',' missing)
4:
make: Unfinished modifier for "VAR" (',' missing)
5:
-make: Unclosed variable expression, expecting '}' for modifier "S,from,to," of variable "VAR" with value "TheVariable"
+make: Unclosed expression, expecting '}' for modifier "S,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
@@ -82,7 +82,7 @@ make: Unfinished modifier for "VAR" (',' missing)
4:
make: Unfinished modifier for "VAR" (',' missing)
5:
-make: Unclosed variable expression, expecting '}' for modifier "C,from,to," of variable "VAR" with value "TheVariable"
+make: Unclosed expression, expecting '}' for modifier "C,from,to," of variable "VAR" with value "TheVariable"
6: TheVariable
7: TheVariable
@@ -124,13 +124,13 @@ make: Unknown modifier "__"
mod-sysv-parse:
make: Unknown modifier "3"
-make: Unclosed variable expression, expecting '}' for modifier "3" of variable "FIB" with value ""
+make: Unclosed expression, expecting '}' for modifier "3" of variable "FIB" with value ""
make: Unknown modifier "3="
-make: Unclosed variable expression, expecting '}' for modifier "3=" of variable "FIB" with value ""
+make: Unclosed expression, expecting '}' for modifier "3=" of variable "FIB" with value ""
make: Unknown modifier "3=x3"
-make: Unclosed variable expression, expecting '}' for modifier "3=x3" of variable "FIB" with value ""
+make: Unclosed expression, expecting '}' for modifier "3=x3" of variable "FIB" with value ""
1 1 2 x3 5 8 1x3 21 34
diff --git a/unit-tests/moderrs.mk b/unit-tests/moderrs.mk
index ffd920314c5d..bde263af4079 100644
--- a/unit-tests/moderrs.mk
+++ b/unit-tests/moderrs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: moderrs.mk,v 1.30 2021/06/21 08:28:37 rillig Exp $
+# $NetBSD: moderrs.mk,v 1.31 2023/11/19 22:32:44 rillig Exp $
#
# various modifier error tests
@@ -34,11 +34,11 @@ mod-unknown-indirect: print-header print-footer
@echo 'VAR:${MOD_UNKN}=before-${VAR:${MOD_UNKN}:inner}-after'
unclosed-direct: print-header print-footer
- @echo 'want: Unclosed variable expression, expecting $'}$' for modifier "S,V,v," of variable "VAR" with value "Thevariable"'
+ @echo 'want: Unclosed expression, expecting $'}$' for modifier "S,V,v," of variable "VAR" with value "Thevariable"'
@echo VAR:S,V,v,=${VAR:S,V,v,
unclosed-indirect: print-header print-footer
- @echo 'want: Unclosed variable expression after indirect modifier, expecting $'}$' for variable "VAR"'
+ @echo 'want: Unclosed expression after indirect modifier, expecting $'}$' for variable "VAR"'
@echo VAR:${MOD_TERM},=${VAR:${MOD_S}
unfinished-indirect: print-header print-footer
diff --git a/unit-tests/opt-debug-file.mk b/unit-tests/opt-debug-file.mk
index a8190b2f1b50..e6c23c4faa1a 100644
--- a/unit-tests/opt-debug-file.mk
+++ b/unit-tests/opt-debug-file.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-file.mk,v 1.9 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: opt-debug-file.mk,v 1.10 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the -dF command line option, which redirects the debug log
# to a file instead of writing it to stderr.
@@ -18,7 +18,7 @@ VAR= value ${:Uexpanded}
# Make sure that the debug logging file contains some logging.
DEBUG_OUTPUT:= ${:!cat opt-debug-file.debuglog!}
# Grmbl. Because of the := operator in the above line, the variable
-# value contains ${:Uexpanded}. This variable expression is expanded
+# value contains ${:Uexpanded}. This expression is expanded
# when it is used in the condition below. Therefore, be careful when storing
# untrusted input in variables.
#.MAKEFLAGS: -dc -dFstderr
diff --git a/unit-tests/opt-debug-jobs.mk b/unit-tests/opt-debug-jobs.mk
index f3732df7e25d..ac63bb9c5e86 100644
--- a/unit-tests/opt-debug-jobs.mk
+++ b/unit-tests/opt-debug-jobs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-jobs.mk,v 1.5 2020/11/12 21:54:52 rillig Exp $
+# $NetBSD: opt-debug-jobs.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the -dj command line option, which adds debug logging about
# running jobs in multiple shells.
@@ -11,7 +11,7 @@
all:
# Only the actual command is logged.
- # To see the evaluation of the variable expressions, use -dv.
+ # To see the evaluation of the expressions, use -dv.
: ${:Uexpanded} expression
# Undefined variables expand to empty strings.
diff --git a/unit-tests/opt-debug-lint.mk b/unit-tests/opt-debug-lint.mk
index 0fcaf8b102ad..042bfd51d35b 100644
--- a/unit-tests/opt-debug-lint.mk
+++ b/unit-tests/opt-debug-lint.mk
@@ -1,7 +1,7 @@
-# $NetBSD: opt-debug-lint.mk,v 1.15 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: opt-debug-lint.mk,v 1.16 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the -dL command line option, which runs additional checks
-# to catch common mistakes, such as unclosed variable expressions.
+# to catch common mistakes, such as unclosed expressions.
.MAKEFLAGS: -dL
@@ -91,7 +91,7 @@ ${UNDEF}: ${UNDEF}
#
# Before var.c 1.856 from 2021-03-14, this regular expression was then
# compiled even though that was not necessary for checking the syntax at the
-# level of variable expressions. The unexpanded '$' then resulted in a wrong
+# level of expressions. The unexpanded '$' then resulted in a wrong
# error message.
#
# This only happened in lint mode since in default mode the early check for
diff --git a/unit-tests/opt-debug-loud.mk b/unit-tests/opt-debug-loud.mk
index 38a3c7d7a8e1..5ea1f90ff7be 100644
--- a/unit-tests/opt-debug-loud.mk
+++ b/unit-tests/opt-debug-loud.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-loud.mk,v 1.4 2020/10/05 19:27:48 rillig Exp $
+# $NetBSD: opt-debug-loud.mk,v 1.5 2023/12/19 19:33:40 rillig Exp $
#
# Tests for the -dl command line option, which prints the commands before
# running them, ignoring the command line option for silent mode (-s) as
@@ -8,8 +8,8 @@
.MAKEFLAGS: -dl -s
.SILENT:
-# The -dl command line option does not affect commands that are run during
-# variable expansion, such as :!cmd! or :sh.
+# The -dl command line option does not affect commands that are run when
+# evaluating expressions and their modifiers, such as :!cmd! or :sh.
.if ${:!echo word!} != "word"
. error
.endif
diff --git a/unit-tests/opt-debug-var.mk b/unit-tests/opt-debug-var.mk
index 5b0c5648ab55..9017f18e81d3 100644
--- a/unit-tests/opt-debug-var.mk
+++ b/unit-tests/opt-debug-var.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-var.mk,v 1.2 2022/01/23 16:09:38 rillig Exp $
+# $NetBSD: opt-debug-var.mk,v 1.3 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the -dv command line option, which adds debug logging about
# variable assignment and evaluation.
@@ -15,7 +15,7 @@ SUBST:= value
.if defined(ASSIGNED)
.endif
-# The usual form of variable expressions is ${VAR}. The form $(VAR) is used
+# The usual form of expressions is ${VAR}. The form $(VAR) is used
# less often as it can be visually confused with the shell construct for
# capturing the output of a subshell, which looks the same.
#
diff --git a/unit-tests/parse-var.mk b/unit-tests/parse-var.mk
index 6205664c558e..b35726e76efc 100644
--- a/unit-tests/parse-var.mk
+++ b/unit-tests/parse-var.mk
@@ -1,6 +1,6 @@
-# $NetBSD: parse-var.mk,v 1.8 2023/02/18 11:16:09 rillig Exp $
+# $NetBSD: parse-var.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
-# Tests for parsing variable expressions.
+# Tests for parsing expressions.
#
# TODO: Add systematic tests for all of the below combinations.
#
@@ -77,7 +77,7 @@
.MAKEFLAGS: -dL
# In variable assignments, there may be spaces in the middle of the left-hand
-# side of the assignment, but only if they occur inside variable expressions.
+# side of the assignment, but only if they occur inside expressions.
# Leading spaces (but not tabs) are possible but unusual.
# Trailing spaces are common in some coding styles, others omit them.
VAR.${:U param }= value
diff --git a/unit-tests/recursive.exp b/unit-tests/recursive.exp
index dc50fe034969..f63b7a54049e 100644
--- a/unit-tests/recursive.exp
+++ b/unit-tests/recursive.exp
@@ -1,5 +1,5 @@
-make: "recursive.mk" line 37: Unclosed variable "MISSING_PAREN"
-make: "recursive.mk" line 39: Unclosed variable "MISSING_BRACE"
+make: "recursive.mk" line 38: Unclosed variable "MISSING_PAREN"
+make: "recursive.mk" line 40: Unclosed variable "MISSING_BRACE"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/recursive.mk b/unit-tests/recursive.mk
index 307515d70a08..b97c4b37eabb 100644
--- a/unit-tests/recursive.mk
+++ b/unit-tests/recursive.mk
@@ -1,4 +1,4 @@
-# $NetBSD: recursive.mk,v 1.6 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: recursive.mk,v 1.7 2023/10/19 18:24:33 rillig Exp $
#
# In -dL mode, a variable may get expanded before it makes sense.
# This would stop make from doing anything since the "recursive" error
@@ -8,22 +8,23 @@
# whether there are unclosed variables. The variable value is therefore
# parsed with VARE_PARSE_ONLY for that purpose.
#
-# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
-# GNU Automake.
.MAKEFLAGS: -dL
+
AM_V_lt= ${am__v_lt_${V}}
am__v_lt_= ${am__v_lt_${AM_DEFAULT_VERBOSITY}}
am__v_lt_0= --silent
am__v_lt_1=
-# On 2020-08-06, make reported: "Variable am__v_lt_ is recursive."
+# Since parse.c 1.243 from 2020-07-31 and before parse.c 1.249 from
+# 2020-08-06, when make ran in -dL mode, it reported: "Variable am__v_lt_ is
+# recursive."
+#
+# Seen in pkgsrc/x11/libXfixes, and probably many more package that use
+# GNU Automake.
libXfixes_la_LINK= ... ${AM_V_lt} ...
-# somewhere later ...
-AM_DEFAULT_VERBOSITY= 1
-
# The purpose of the -dL flag is to detect unclosed variables. This
# can be achieved by just parsing the variable and not evaluating it.
diff --git a/unit-tests/sh-dots.mk b/unit-tests/sh-dots.mk
index f85af9025e55..5294a4175b63 100755
--- a/unit-tests/sh-dots.mk
+++ b/unit-tests/sh-dots.mk
@@ -1,4 +1,4 @@
-# $NetBSD: sh-dots.mk,v 1.3 2020/10/25 22:04:24 rillig Exp $
+# $NetBSD: sh-dots.mk,v 1.4 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the special shell command line "...", which does not run the
# commands below it but appends them to the list of commands that are run
@@ -29,8 +29,8 @@ commented: .IGNORE
... # Run the below commands later
@echo commented delayed ${.TARGET}
-# The dots don't have to be written literally, they can also come from a
-# variable expression.
+# The dots don't have to be written literally, they can also come from an
+# expression.
indirect:
@echo indirect regular
${:U...}
diff --git a/unit-tests/sh-leading-hyphen.exp b/unit-tests/sh-leading-hyphen.exp
index d049757777cd..50bcbbf9bb71 100644
--- a/unit-tests/sh-leading-hyphen.exp
+++ b/unit-tests/sh-leading-hyphen.exp
@@ -6,6 +6,6 @@ unknown-command: not found
*** Error code 127 (ignored)
unknown-long-option 'needed for needshell in compat.c'
unknown-long-option: not found
-whitespace in leading part
*** Error code 127 (ignored)
+whitespace in leading part
exit status 0
diff --git a/unit-tests/shell-sh.mk b/unit-tests/shell-sh.mk
index b3d4f18bbac9..5f7b04716ee1 100644
--- a/unit-tests/shell-sh.mk
+++ b/unit-tests/shell-sh.mk
@@ -1,9 +1,9 @@
-# $NetBSD: shell-sh.mk,v 1.1 2020/10/03 14:39:36 rillig Exp $
+# $NetBSD: shell-sh.mk,v 1.2 2023/12/24 16:48:30 sjg Exp $
#
# Tests for using a bourne shell for running the commands.
# This is the default shell, so there's nothing surprising.
-.SHELL: name="sh" path="sh"
+.SHELL: name="sh"
all:
: normal
diff --git a/unit-tests/unexport.mk b/unit-tests/unexport.mk
index 4363aaac3eee..4bcc5b21ca02 100644
--- a/unit-tests/unexport.mk
+++ b/unit-tests/unexport.mk
@@ -1,4 +1,4 @@
-# $NetBSD: unexport.mk,v 1.5 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: unexport.mk,v 1.6 2023/10/19 18:24:33 rillig Exp $
# pick up a bunch of exported vars
FILTER_CMD= grep ^UT_
@@ -10,7 +10,7 @@ UT_TEST= unexport
# Until 2020-08-08, Var_UnExport had special handling for '\n', that code
# was not reachable though. At that point, backslash-newline has already
-# been replaced with a simple space, and variables are not yet expanded.
+# been replaced with a simple space, and expressions are not yet expanded.
UT_BEFORE_NL= before
UT_AFTER_NL= after
.export UT_BEFORE_NL UT_AFTER_NL
diff --git a/unit-tests/var-eval-short.mk b/unit-tests/var-eval-short.mk
index aa8bf79decec..2b25d82e96b8 100644
--- a/unit-tests/var-eval-short.mk
+++ b/unit-tests/var-eval-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-eval-short.mk,v 1.10 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: var-eval-short.mk,v 1.11 2023/10/19 18:24:33 rillig Exp $
#
# Tests for each variable modifier to ensure that they only do the minimum
# necessary computations. If the result of the expression is irrelevant,
@@ -18,7 +18,7 @@ FAIL= ${:!echo unexpected 1>&2!}
# is ignored as well. To do that, it is necessary to step through the code of
# each modifier.
-# TODO: Test the modifiers in the same order as they appear in ApplyModifier.
+# TODO: Test the modifiers in the same order as they occur in ApplyModifier.
.if 0 && ${FAIL}
.endif
diff --git a/unit-tests/var-op-append.mk b/unit-tests/var-op-append.mk
index 5f06fd887332..e16b89139cc1 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.10 2023/06/21 07:30:50 rillig Exp $
+# $NetBSD: var-op-append.mk,v 1.12 2023/11/02 05:46:26 rillig Exp $
#
# Tests for the '+=' variable assignment operator, which appends to a
# variable, creating it if necessary.
@@ -39,7 +39,7 @@ VAR+= # empty
# '+=' assignment operator. As far as possible, the '+' is interpreted as
# part of the assignment operator.
#
-# See Parse_Var
+# See Parse_Var, AdjustVarassignOp.
C++= value
.if ${C+} != "value" || defined(C++)
. error
@@ -56,4 +56,33 @@ VAR.${:U\$\$\$\$\$\$\$\$}+= dollars
. error
.endif
+
+# Appending to an environment variable in the global scope creates a global
+# variable of the same name, taking its initial value from the environment
+# variable. After the assignment, the environment variable is left as-is,
+# the value of the global variable is not synced back to the environment
+# variable.
+export ENV_PLUS_GLOBAL=from-env-value
+ENV_PLUS_GLOBAL+= appended-value
+.if ${ENV_PLUS_GLOBAL} != "from-env-value appended-value"
+. error
+.endif
+EXPORTED!= echo "$$ENV_PLUS_GLOBAL"
+.if ${EXPORTED} != "from-env-value"
+. error
+.endif
+
+# Appending to an environment variable in the command line scope ignores the
+# environment variable.
+export ENV_PLUS_COMMAND=from-env-value
+.MAKEFLAGS: ENV_PLUS_COMMAND+=appended-command
+.if ${ENV_PLUS_COMMAND} != "appended-command"
+. error ${ENV_PLUS_COMMAND}
+.endif
+EXPORTED!= echo "$$ENV_PLUS_GLOBAL"
+.if ${EXPORTED} != "from-env-value"
+. error
+.endif
+
+
all:
diff --git a/unit-tests/var-op-assign.mk b/unit-tests/var-op-assign.mk
index 4753975ec138..a900c28a918d 100644
--- a/unit-tests/var-op-assign.mk
+++ b/unit-tests/var-op-assign.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-assign.mk,v 1.10 2023/08/19 10:52:14 rillig Exp $
+# $NetBSD: var-op-assign.mk,v 1.11 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the = variable assignment operator, which overwrites an existing
# variable or creates it.
@@ -66,7 +66,7 @@ VARIABLE NAME= variable value
# neither contain parentheses nor braces. This is only a side-effect from
# the implementation of the parser, which cheats when parsing a variable
# name. It only counts parentheses and braces instead of properly parsing
-# nested variable expressions such as VAR.${param}.
+# nested expressions such as VAR.${param}.
#
VAR(spaces in parentheses)= ()
VAR{spaces in braces}= {}
diff --git a/unit-tests/var-op-default.mk b/unit-tests/var-op-default.mk
index ca4fbcc27c88..9d07ddf39e41 100644
--- a/unit-tests/var-op-default.mk
+++ b/unit-tests/var-op-default.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-default.mk,v 1.3 2020/12/07 21:35:43 rillig Exp $
+# $NetBSD: var-op-default.mk,v 1.5 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the ?= variable assignment operator, which only assigns
# if the variable is still undefined.
@@ -45,7 +45,7 @@ i?= default
# and 'VAR.${param}' expand to 'VAR.param', and the second '?=' assignment
# has no effect.
#
-# Since 2000.05.11.07.43.42 it has been possible to use nested variable
+# Since 2000.05.11.07.43.42 it has been possible to use nested
# expressions in variable names, which made make much more versatile.
# On 2008.03.31.00.12.21, this particular case of the '?=' operator has been
# fixed. Before, the '?=' operator had not expanded the variable name
@@ -61,8 +61,8 @@ VAR.${:Uparam}?= not used
# Now demonstrate that the variable name is indeed expanded exactly once.
# This is tricky to measure correctly since there are many inconsistencies
-# in and around the code that expands variable expressions in the various
-# places where variable expressions can occur. If in doubt, enable the
+# in and around the code that expands expressions in the various
+# places where expressions can occur. If in doubt, enable the
# following debug flags to see what happens:
#.MAKEFLAGS: -dcpv
EXPAND_NAME= EXPAND.$$$$ # The full variable name is EXPAND.$$
diff --git a/unit-tests/var-op-expand.mk b/unit-tests/var-op-expand.mk
index a0b9cc475404..76b90bf72b56 100644
--- a/unit-tests/var-op-expand.mk
+++ b/unit-tests/var-op-expand.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-op-expand.mk,v 1.18 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: var-op-expand.mk,v 1.19 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the := variable assignment operator, which expands its
# right-hand side.
@@ -20,9 +20,9 @@ VAR:= value
# When a ':=' assignment is performed, its right-hand side is evaluated and
# expanded as far as possible. Contrary to other situations, '$$' and
-# variable expressions based on undefined variables are preserved though.
+# expressions based on undefined variables are preserved though.
#
-# Whether a variable expression is undefined or not is determined at the end
+# Whether an expression is undefined or not is determined at the end
# of evaluating the expression. The consequence is that ${:Ufallback} expands
# to "fallback"; initially this expression is undefined since it is based on
# the variable named "", which is guaranteed to be never defined, but at the
diff --git a/unit-tests/var-op-shell.mk b/unit-tests/var-op-shell.mk
index de26bff94905..4441efaf4a90 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.7 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: var-op-shell.mk,v 1.8 2024/01/05 23:36:45 rillig Exp $
#
# Tests for the != variable assignment operator, which runs its right-hand
# side through the shell.
@@ -91,4 +91,22 @@ OUTPUT!= echo '$$$$$$$$'
OUTPUT!= echo '$$$$$$$$'
.MAKEFLAGS: -d0
+
+# Since main.c 1.607 from 2024-01-05, long shell commands are not run directly
+# via '$shell -c $command', they are first written to a temporary file that is
+# then fed to the shell via '$shell $tmpfile'.
+OUTPUT_SHORT!= echo "$$0"
+OUTPUT_LONG!= echo "$$0" || : ${:U:range=1000}
+# When running '$shell -c $command', '$0' in the shell evaluates to the name
+# of the shell.
+.if ${OUTPUT_SHORT} != ${.SHELL:T}
+. error
+.endif
+# When running '$shell $tmpfile', '$0' in the shell evaluates to the name of
+# the temporary file.
+.if !${OUTPUT_LONG:M*/make*}
+. error
+.endif
+
+
all:
diff --git a/unit-tests/var-readonly.exp b/unit-tests/var-readonly.exp
index 39a9383953dd..ae266753ee71 100644
--- a/unit-tests/var-readonly.exp
+++ b/unit-tests/var-readonly.exp
@@ -1 +1,4 @@
+Global: ignoring delete 'N' as it is read-only
+Global: .MAKEFLAGS = -r -k -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0
exit status 0
diff --git a/unit-tests/var-readonly.mk b/unit-tests/var-readonly.mk
index 050b44d1b4e8..e9ff6f38819f 100644
--- a/unit-tests/var-readonly.mk
+++ b/unit-tests/var-readonly.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-readonly.mk,v 1.3 2023/06/19 15:37:48 sjg Exp $
+# $NetBSD: var-readonly.mk,v 1.4 2023/12/20 08:42:10 rillig Exp $
# the answer
N = 42
@@ -10,10 +10,12 @@ N = 666
.endif
# undef should fail
+.MAKEFLAGS: -dv
.undef N
.ifndef N
.error N should not be undef'd
.endif
+.MAKEFLAGS: -d0
.NOREADONLY: N
# now we can change it
diff --git a/unit-tests/var-recursive.exp b/unit-tests/var-recursive.exp
index 2158286749f3..5415dc93a53d 100644
--- a/unit-tests/var-recursive.exp
+++ b/unit-tests/var-recursive.exp
@@ -1,19 +1,19 @@
make: "var-recursive.mk" line 21: still there
-Variable DIRECT is recursive.
+make: Variable DIRECT is recursive.
in var-recursive.mk:22
make: stopped in unit-tests
-Variable INDIRECT1 is recursive.
+make: Variable INDIRECT1 is recursive.
in var-recursive.mk:29
make: stopped in unit-tests
make: "var-recursive.mk" line 37: ok
-Variable V is recursive.
+make: Variable V is recursive.
in var-recursive.mk:45
make: stopped in unit-tests
: OK
-In a command near "var-recursive.mk" line 57: Variable VAR is recursive.
+In a command near "var-recursive.mk" line 57: make[1]: Variable VAR is recursive.
make: stopped in unit-tests
exit status 0
diff --git a/unit-tests/var-recursive.mk b/unit-tests/var-recursive.mk
index 998d0d8c312a..72231656673c 100644
--- a/unit-tests/var-recursive.mk
+++ b/unit-tests/var-recursive.mk
@@ -1,6 +1,6 @@
-# $NetBSD: var-recursive.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: var-recursive.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
-# Tests for variable expressions that refer to themselves and thus
+# Tests for expressions that refer to themselves and thus
# cannot be evaluated.
TESTS= direct indirect conditional short target
diff --git a/unit-tests/var-scope-cmdline.mk b/unit-tests/var-scope-cmdline.mk
index eaa2c58347ae..5c0f246a0a22 100644
--- a/unit-tests/var-scope-cmdline.mk
+++ b/unit-tests/var-scope-cmdline.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-scope-cmdline.mk,v 1.3 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: var-scope-cmdline.mk,v 1.4 2023/11/19 21:47:52 rillig Exp $
#
# Tests for variables specified on the command line.
#
@@ -55,7 +55,7 @@
# temporary loop variable after finishing the loop. It was probably not
# intended back then that a side effect of this seemingly simple change was
# that both global and cmdline variables could now be undefined at will as a
-# side effect of evaluating a variable expression. As of 2021-02-23, this is
+# side effect of evaluating an expression. As of 2021-02-23, this is
# still possible.
#
# Most cmdline variables are set at the very beginning, when parsing the
diff --git a/unit-tests/var-scope-local-legacy.exp b/unit-tests/var-scope-local-legacy.exp
index fb7e0863b2e6..33ce145fb8fd 100644
--- a/unit-tests/var-scope-local-legacy.exp
+++ b/unit-tests/var-scope-local-legacy.exp
@@ -2,5 +2,5 @@
: XY=undef_
: AF=undef_
: %D=undef_ %F=undef_
-: @D=._ @F=all_
+: @D=global-value_ @F=all_
exit status 0
diff --git a/unit-tests/var-scope-local-legacy.mk b/unit-tests/var-scope-local-legacy.mk
index 9b70e3f8f143..70bc20fd9848 100644
--- a/unit-tests/var-scope-local-legacy.mk
+++ b/unit-tests/var-scope-local-legacy.mk
@@ -1,7 +1,24 @@
-# $NetBSD: var-scope-local-legacy.mk,v 1.2 2022/09/27 19:18:45 rillig Exp $
+# $NetBSD: var-scope-local-legacy.mk,v 1.3 2023/12/17 14:07:22 rillig Exp $
#
# Tests for legacy target-local variables, such as ${<F} or ${@D}.
+
+# In the global or command line scopes, the legacy forms are not recognized,
+# as the target-specific variables are not available either. The expressions
+# are retained so that they can be resolved later, in the target scope.
+.if "${@D}" != "\${@D}"
+. error
+.endif
+
+# It's possible to define variables of the legacy name in the global or
+# command line scope, and they override the target-local variables, leading to
+# unnecessary confusion.
+@D= global-value
+.if "${@D}" != "global-value"
+. error
+.endif
+
+
all: .PHONY
# Only variables of length 2 can be legacy, this one cannot.
: LEN4=${LEN4:Uundef}_
@@ -13,5 +30,6 @@ all: .PHONY
# The variable '.MEMBER' is undefined, therefore '%D' and '%F' are
# undefined as well.
: %D=${%D:Uundef}_ %F=${%F:Uundef}_
- # The directory name of the target is '.', its basename is 'all'.
+ # The directory name of the target is shadowed by the global variable,
+ # it would be '.' otherwise. The basename is 'all'.
: @D=${@D:Uundef}_ @F=${@F:Uundef}_
diff --git a/unit-tests/var-scope-local.exp b/unit-tests/var-scope-local.exp
index a9a947a620c7..c2e345d8fed8 100644
--- a/unit-tests/var-scope-local.exp
+++ b/unit-tests/var-scope-local.exp
@@ -1,8 +1,8 @@
Global: .ALLTARGETS = all target-rule.ext dir/subdir/target-rule.ext target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from inference-rule.ir-to dir/subdir/inference-rule.ir-to inference-rule.ir-from dir/subdir/inference-rule.ir-from inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to inference-rule-chain.ir-gen-from dir/subdir/inference-rule-chain.ir-gen-from one
Global: .ALLTARGETS = all target-rule.ext dir/subdir/target-rule.ext target-rule.ir-gen-from dir/subdir/target-rule-dir.ir-gen-from inference-rule.ir-to dir/subdir/inference-rule.ir-to inference-rule.ir-from dir/subdir/inference-rule.ir-from inference-rule-chain.ir-to dir/subdir/inference-rule-chain.ir-to inference-rule-chain.ir-gen-from dir/subdir/inference-rule-chain.ir-gen-from one two
Var_Parse: ${.MAKE.TARGET_LOCAL_VARIABLES} (eval)
-Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
+one: ignoring ' = three' as the variable name '' expands to empty
+two: ignoring ' = three' as the variable name '' expands to empty
Global: one two = # (empty)
Global: one two = three
Global: .MAKEFLAGS = -r -k -d v -d
diff --git a/unit-tests/var-scope-local.mk b/unit-tests/var-scope-local.mk
index 2cfeda044c40..5c4f5634b230 100644
--- a/unit-tests/var-scope-local.mk
+++ b/unit-tests/var-scope-local.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-scope-local.mk,v 1.7 2023/04/29 10:16:24 rillig Exp $
+# $NetBSD: var-scope-local.mk,v 1.9 2023/12/20 09:03:09 rillig Exp $
#
# Tests for target-local variables, such as ${.TARGET} or $@. These variables
# are relatively short-lived as they are created just before making the
@@ -107,7 +107,7 @@ dir/subdir/inference-rule-chain.ir-gen-from: .PHONY
.if $(@) != "\$\(@)"
. error
.endif
-# If the variable expression contains modifiers, the behavior depends on the
+# If the expression contains modifiers, the behavior depends on the
# actual modifiers. The modifier ':M' keeps the expression in the state
# 'undefined'. Since the expression is still undefined after evaluating all
# the modifiers, the value of the expression is discarded and the expression
@@ -146,10 +146,10 @@ dir/subdir/inference-rule-chain.ir-gen-from: .PHONY
#
# The empty variable name is expanded twice, once for 'one' and once for
# 'two'.
-# expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
-# expect: Var_SetExpand: variable name "" expands to empty string, with value "three" - ignored
+# expect: one: ignoring ' = three' as the variable name '' expands to empty
+# expect: two: ignoring ' = three' as the variable name '' expands to empty
one two:=three
-# If the two targets to the left are generated by a variable expression, the
+# If the two targets to the left are generated by an expression, the
# line is parsed as a variable assignment since its left-hand side is a single
# word.
# expect: Global: one two = three
@@ -225,7 +225,7 @@ var-scope-local-append.o: VAR+= local
# target is actually made.
# expect: : Making var-scope-local-append.o with VAR="local to var-scope-local-append.o".
var-scope-local-append.o: VAR += to ${.TARGET}
-# To access the value of a global variable, use a variable expression. This
+# To access the value of a global variable, use an expression. This
# expression is expanded before parsing the whole dependency line. Since the
# expansion happens to the right of the dependency operator ':', the expanded
# text does not influence parsing of the dependency line. Since the expansion
diff --git a/unit-tests/vardebug.exp b/unit-tests/vardebug.exp
index a668807b94e3..a0aa6a301800 100644
--- a/unit-tests/vardebug.exp
+++ b/unit-tests/vardebug.exp
@@ -1,13 +1,13 @@
-Global: delete FROM_CMDLINE (not found)
+Global: ignoring delete 'FROM_CMDLINE' as it is not found
Command: FROM_CMDLINE = # (empty)
Global: .MAKEOVERRIDES = FROM_CMDLINE
Global: VAR = added
Global: VAR = overwritten
Global: delete VAR
-Global: delete VAR (not found)
-Var_SetExpand: variable name "${:U}" expands to empty string, with value "empty name" - ignored
-Var_AppendExpand: variable name "${:U}" expands to empty string, with value "empty name" - ignored
-Global: FROM_CMDLINE = overwritten ignored!
+Global: ignoring delete 'VAR' as it is not found
+Global: ignoring ' = empty name' as the variable name '${:U}' expands to empty
+Global: ignoring ' += empty name' as the variable name '${:U}' expands to empty
+Global: ignoring 'FROM_CMDLINE = overwritten' due to a command line variable of the same name
Global: VAR = 1
Global: VAR = 1 2
Global: VAR = 1 2 3
@@ -54,14 +54,14 @@ Var_Parse: ${:Uvariable:unknown} (eval-defined)
Evaluating modifier ${:U...} on value "" (eval-defined, undefined)
Result of ${:Uvariable} is "variable" (eval-defined, defined)
Evaluating modifier ${:u...} on value "variable" (eval-defined, defined)
-make: "vardebug.mk" line 46: Unknown modifier "unknown"
+make: "vardebug.mk" line 63: Unknown modifier "unknown"
Result of ${:unknown} is error (eval-defined, defined)
-make: "vardebug.mk" line 46: Malformed conditional (${:Uvariable:unknown})
+make: "vardebug.mk" line 63: Malformed conditional (${:Uvariable:unknown})
Var_Parse: ${UNDEFINED} (eval-defined)
-make: "vardebug.mk" line 56: Malformed conditional (${UNDEFINED})
-Global: delete .SHELL (not found)
+make: "vardebug.mk" line 73: Malformed conditional (${UNDEFINED})
+Global: ignoring delete '.SHELL' as it is not found
Command: .SHELL = </path/to/shell>
-Command: .SHELL = overwritten ignored (read-only)
+Command: ignoring '.SHELL = overwritten' as it is read-only
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
make: Fatal errors encountered -- cannot continue
diff --git a/unit-tests/vardebug.mk b/unit-tests/vardebug.mk
index 34015aa71db9..b9b094772b54 100644
--- a/unit-tests/vardebug.mk
+++ b/unit-tests/vardebug.mk
@@ -1,32 +1,46 @@
-# $NetBSD: vardebug.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: vardebug.mk,v 1.9 2023/12/20 09:46:00 rillig Exp $
#
# Demonstrates the debugging output for var.c.
.MAKEFLAGS: -dv FROM_CMDLINE=
+# expect: Global: VAR = added
VAR= added # VarAdd
+# expect: Global: VAR = overwritten
VAR= overwritten # Var_Set
-.undef VAR # Var_Delete (found)
-.undef VAR # Var_Delete (not found)
+# expect: Global: delete VAR
+.undef VAR
+# expect: Global: ignoring delete 'VAR' as it is not found
+.undef VAR
# The variable with the empty name cannot be set at all.
+# expect: Global: ignoring ' = empty name' as the variable name '${:U}' expands to empty
${:U}= empty name # Var_Set
+# expect: Global: ignoring ' += empty name' as the variable name '${:U}' expands to empty
${:U}+= empty name # Var_Append
FROM_CMDLINE= overwritten # Var_Set (ignored)
+# expect: Global: VAR = 1
VAR= 1
+# expect: Global: VAR = 1 2
VAR+= 2
+# expect: Global: VAR = 1 2 3
VAR+= 3
+# expect: Pattern for ':M' is "[2]"
+# expect: Result of ${VAR:M[2]} is "2"
.if ${VAR:M[2]} # ModifyWord_Match
.endif
-.if ${VAR:N[2]} # ModifyWord_NoMatch (no debug output)
+# expect: Pattern for ':N' is "[2]"
+# expect: Result of ${VAR:N[2]} is "1 3"
+.if ${VAR:N[2]} # ModifyWord_NoMatch
.endif
.if ${VAR:S,2,two,} # ParseModifierPart
.endif
+# expect: Result of ${VAR:Q} is "1\ 2\ 3"
.if ${VAR:Q} # VarQuote
.endif
@@ -34,13 +48,16 @@ VAR+= 3
.endif
# ApplyModifiers, "Got ..."
+# expect: Result of ${:Mvalu[e]} is "value" (eval-defined, defined)
.if ${:Uvalue:${:UM*e}:Mvalu[e]}
.endif
+# expect: Global: delete VAR
.undef ${:UVAR} # Var_Delete
# When ApplyModifiers results in an error, this appears in the debug log
# as "is error", without surrounding quotes.
+# expect: Result of ${:unknown} is error (eval-defined, defined)
# expect+2: Malformed conditional (${:Uvariable:unknown})
# expect+1: Unknown modifier "unknown"
.if ${:Uvariable:unknown}
@@ -59,9 +76,7 @@ VAR+= 3
# By default, .SHELL is not defined and thus can be set. As soon as it is
# accessed, it is initialized in the command line scope (during VarFind),
# where it is set to read-only. Assigning to it is ignored.
+# expect: Command: ignoring '.SHELL = overwritten' as it is read-only
.MAKEFLAGS: .SHELL=overwritten
.MAKEFLAGS: -d0
-
-all:
- @:
diff --git a/unit-tests/varmisc.exp b/unit-tests/varmisc.exp
index f56f72d0ab9c..61e6a49963a0 100644
--- a/unit-tests/varmisc.exp
+++ b/unit-tests/varmisc.exp
@@ -17,12 +17,9 @@ false
FALSE
do not evaluate or expand :? if discarding
is set
-year=2016 month=04 day=01
-date=20160401
Version=123.456.789 == 123456789
Literal=3.4.5 == 3004005
We have target specific vars
-MAN= make.1
save-dollars: 0 = $
save-dollars: 1 = $$
save-dollars: 2 = $$
@@ -54,7 +51,7 @@ make: Unclosed variable "UNCLOSED"
make: Unclosed variable "UNCLOSED"
make: Unclosed variable "PATTERN"
-make: Unclosed variable expression, expecting '}' for modifier "M${PATTERN" of variable "UNCLOSED" with value ""
+make: Unclosed expression, expecting '}' for modifier "M${PATTERN" of variable "UNCLOSED" with value ""
make: Unclosed variable "param"
make: Unclosed variable "UNCLOSED."
diff --git a/unit-tests/varmisc.mk b/unit-tests/varmisc.mk
index 81818f3fb8bb..f6a0e4da2d88 100644
--- a/unit-tests/varmisc.mk
+++ b/unit-tests/varmisc.mk
@@ -1,10 +1,10 @@
-# $Id: varmisc.mk,v 1.25 2021/12/07 00:03:11 sjg Exp $
-# $NetBSD: varmisc.mk,v 1.32 2021/12/05 10:02:51 rillig Exp $
+# $Id: varmisc.mk,v 1.26 2023/11/25 01:39:31 sjg Exp $
+# $NetBSD: varmisc.mk,v 1.33 2023/10/19 18:24:33 rillig Exp $
#
# Miscellaneous variable tests.
all: unmatched_var_paren D_true U_true D_false U_false Q_lhs Q_rhs NQ_none \
- strftime cmpv manok
+ cmpv
all: save-dollars
all: export-appended
all: parse-dynamic
@@ -47,13 +47,6 @@ NQ_none:
@echo do not evaluate or expand :? if discarding
@echo ${VSET:U${1:L:?${True}:${False}}}
-April1= 1459494000
-
-# slightly contorted syntax to use utc via variable
-strftime:
- @echo ${year=%Y month=%m day=%d:L:gmtime=1459494000}
- @echo date=${%Y%m%d:L:${gmtime=${April1}:L}}
-
# big jumps to handle 3 digits per step
M_cmpv.units= 1 1000 1000000
M_cmpv= S,., ,g:_:range:@i@+ $${_:[-$$i]} \* $${M_cmpv.units:[$$i]}@:S,^,expr 0 ,1:sh
@@ -66,17 +59,6 @@ cmpv:
@echo Literal=3.4.5 == ${3.4.5:L:${M_cmpv}}
@echo We have ${${.TARGET:T}.only}
-# catch mishandling of nested variables in .for loop
-MAN=
-MAN1= make.1
-.for s in 1 2
-. if defined(MAN$s) && !empty(MAN$s)
-MAN+= ${MAN$s}
-. endif
-.endfor
-
-manok:
- @echo MAN=${MAN}
# Test parsing of boolean values.
# begin .MAKE.SAVE_DOLLARS; see Var_SetWithFlags and ParseBoolean.
@@ -131,10 +113,10 @@ VAR.${PARAM}+= 2
.if ${VAR.+} != "1 2"
. error "${VAR.+}"
.endif
-.for param in + ! ?
+.for param in : + ! ?
VAR.${param}= ${param}
.endfor
-.if ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?"
+.if ${VAR.${:U\:}} != ":" || ${VAR.+} != "+" || ${VAR.!} != "!" || ${VAR.?} != "?"
. error "${VAR.+}" "${VAR.!}" "${VAR.?}"
.endif
diff --git a/unit-tests/varmod-assign.exp b/unit-tests/varmod-assign.exp
index 1ad388418ab5..f258f92ea05b 100644
--- a/unit-tests/varmod-assign.exp
+++ b/unit-tests/varmod-assign.exp
@@ -12,6 +12,31 @@ Var_Parse: ${${VARNAME}} != "assigned-value" (eval-defined)
Var_Parse: ${VARNAME}} != "assigned-value" (eval-defined)
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
+Var_Parse: ${CMD_CMD_VAR::=new-value} || ${CMD_GLOBAL_VAR::=new-value} || ${CMD_ENV_VAR::=new-value} || "${CMD_NEW_VAR::=new-value}" (eval-defined)
+Evaluating modifier ${CMD_CMD_VAR::...} on value "cmd-value"
+Modifier part: "new-value"
+Command: CMD_CMD_VAR = new-value
+Global: .MAKEOVERRIDES = FIRST LAST LAST LAST APPENDED RAN RAN RAN IT1 THEN1 IE2 ELSE2 CMD_CMD_VAR CMD_CMD_VAR
+Result of ${CMD_CMD_VAR::=new-value} is ""
+Var_Parse: ${CMD_GLOBAL_VAR::=new-value} || ${CMD_ENV_VAR::=new-value} || "${CMD_NEW_VAR::=new-value}" (eval-defined)
+Evaluating modifier ${CMD_GLOBAL_VAR::...} on value "global-value"
+Modifier part: "new-value"
+Global: CMD_GLOBAL_VAR = new-value
+Result of ${CMD_GLOBAL_VAR::=new-value} is ""
+Var_Parse: ${CMD_ENV_VAR::=new-value} || "${CMD_NEW_VAR::=new-value}" (eval-defined)
+Evaluating modifier ${CMD_ENV_VAR::...} on value "env-value"
+Modifier part: "new-value"
+Global: CMD_ENV_VAR = new-value
+Result of ${CMD_ENV_VAR::=new-value} is ""
+Var_Parse: ${CMD_NEW_VAR::=new-value}" (eval)
+Evaluating modifier ${CMD_NEW_VAR::...} on value "" (eval, undefined)
+Modifier part: "new-value"
+Global: ignoring delete 'CMD_NEW_VAR' as it is not found
+Command: CMD_NEW_VAR = new-value
+Global: .MAKEOVERRIDES = FIRST LAST LAST LAST APPENDED RAN RAN RAN IT1 THEN1 IE2 ELSE2 CMD_CMD_VAR CMD_CMD_VAR CMD_NEW_VAR
+Result of ${CMD_NEW_VAR::=new-value} is "" (eval, undefined)
+Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d
+Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0
make: Bad modifier ":" for variable ""
mod-assign-empty: value}
make: Bad modifier ":" for variable ""
@@ -25,4 +50,11 @@ make: Unfinished modifier for "ASSIGN" ('}' missing)
ok=word
make: " echo word; false " returned non-zero status
err=previous
+Command: TARGET_CMD_VAR = cmd-value
+Global: TARGET_GLOBAL_VAR = global-value
+target: TARGET_TARGET_VAR = target-value
+target: TARGET_TARGET_VAR = new-value
+Global: TARGET_GLOBAL_VAR = new-value
+Global: TARGET_ENV_VAR = new-value
+target: TARGET_NEW_VAR = new-value
exit status 0
diff --git a/unit-tests/varmod-assign.mk b/unit-tests/varmod-assign.mk
index a6236253068d..f7112c47c935 100644
--- a/unit-tests/varmod-assign.mk
+++ b/unit-tests/varmod-assign.mk
@@ -1,40 +1,46 @@
-# $NetBSD: varmod-assign.mk,v 1.15 2022/02/09 21:09:24 rillig Exp $
+# $NetBSD: varmod-assign.mk,v 1.19 2024/01/07 11:42:22 rillig Exp $
#
# Tests for the obscure ::= variable modifiers, which perform variable
# assignments during evaluation, just like the = operator in C.
+.if !make(target)
+
all: mod-assign-empty
all: mod-assign-parse
all: mod-assign-shell-error
-# The modifier '::?=' applies the assignment operator '?=' 3 times. The
+# In the following loop expression,
+# the '::?=' modifier applies the assignment operator '?=' 3 times. The
# operator '?=' only has an effect for the first time, therefore the variable
# FIRST ends up with the value 1.
.if "${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}" != " first=1"
. error
.endif
-# The modifier '::=' applies the assignment operator '=' 3 times. The
+# In the following loop expression,
+# the modifier '::=' applies the assignment operator '=' 3 times. The
# operator '=' overwrites the previous value, therefore the variable LAST ends
# up with the value 3.
.if "${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}" != " last=3"
. error
.endif
-# The modifier '::+=' applies the assignment operator '+=' 3 times. The
+# In the following loop expression,
+# the modifier '::+=' applies the assignment operator '+=' 3 times. The
# operator '+=' appends 3 times to the variable, therefore the variable
# APPENDED ends up with the value "1 2 3".
.if "${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}" != " appended=1 2 3"
. error
.endif
-# The modifier '::!=' applies the assignment operator '!=' 3 times. Just as
+# In the following loop expression,
+# the modifier '::!=' applies the assignment operator '!=' 3 times. Just as
# with the modifier '::=', the last value is stored in the RAN variable.
.if "${1 2 3:L:@i@${RAN::!=${i:%=echo '<%>';}}@} ran=${RAN}" != " ran=<3>"
. error
.endif
-# The assignments were performed as part of .if conditions and thus happened
+# When a '::=' modifier is evaluated as part of an .if condition, it happens
# in the command line scope.
.if "${FIRST}, ${LAST}, ${APPENDED}, ${RAN}" != "1, 3, 1 2 3, <3>"
. error
@@ -116,7 +122,7 @@ APPEND.dollar= $${APPEND.indirect}
.endif
-# The assignment modifier can be used in a variable expression that is
+# The assignment modifier can be used in an expression that is
# enclosed in parentheses. In such a case, parsing stops at the first ')',
# not at the first '}'.
VAR= previous
@@ -149,3 +155,54 @@ ${VARNAME}= initial-value # Sets 'VAR.${param}' to 'expanded'.
. error
.endif
.MAKEFLAGS: -d0
+
+
+# Conditional directives are evaluated in command line scope. An assignment
+# modifier that creates a new variable creates it in the command line scope.
+# Existing variables are updated in their previous scope, and environment
+# variables are created in the global scope, as in other situations.
+.MAKEFLAGS: CMD_CMD_VAR=cmd-value
+CMD_GLOBAL_VAR=global-value
+export CMD_ENV_VAR=env-value
+.MAKEFLAGS: -dv
+# expect-reset
+# expect: Command: CMD_CMD_VAR = new-value
+# expect: Global: CMD_GLOBAL_VAR = new-value
+# expect: Global: CMD_ENV_VAR = new-value
+# expect: Global: ignoring delete 'CMD_NEW_VAR' as it is not found
+# expect: Command: CMD_NEW_VAR = new-value
+.if ${CMD_CMD_VAR::=new-value} \
+ || ${CMD_GLOBAL_VAR::=new-value} \
+ || ${CMD_ENV_VAR::=new-value} \
+ || "${CMD_NEW_VAR::=new-value}"
+. error
+.endif
+.MAKEFLAGS: -d0
+
+# Run the 'target' test in a separate sub-make, with reduced debug logging.
+all: run-target
+run-target: .PHONY
+ @${MAKE} -r -f ${MAKEFILE} -dv target 2>&1 | grep ': TARGET_'
+
+.else # make(target)
+
+# The commands of a target are evaluated in target scope. An assignment
+# modifier that creates a new variable creates it in the target scope.
+# Existing variables are updated in their previous scope, and environment
+# variables are created in the global scope, as in other situations.
+#
+# expect: target: TARGET_TARGET_VAR = new-value
+# expect: Global: TARGET_GLOBAL_VAR = new-value
+# expect: Global: TARGET_ENV_VAR = new-value
+# expect: target: TARGET_NEW_VAR = new-value
+.MAKEFLAGS: TARGET_CMD_VAR=cmd-value
+TARGET_GLOBAL_VAR=global-value
+export TARGET_ENV_VAR=env-value
+target: .PHONY TARGET_TARGET_VAR=target-value
+ : ${TARGET_TARGET_VAR::=new-value}
+ : ${TARGET_CMD_VAR::=new-value}
+ : ${TARGET_GLOBAL_VAR::=new-value}
+ : ${TARGET_ENV_VAR::=new-value}
+ : ${TARGET_NEW_VAR::=new-value}
+
+.endif
diff --git a/unit-tests/varmod-defined.exp b/unit-tests/varmod-defined.exp
index b44d58c657aa..d82a292292a4 100644
--- a/unit-tests/varmod-defined.exp
+++ b/unit-tests/varmod-defined.exp
@@ -14,7 +14,7 @@ Modifier part: "${8_DOLLARS}"
ModifyWords: split "$$$$$$$$" into 1 word
Global: var = $$$$$$$$
Var_Parse: ${8_DOLLARS} (eval-keep-undefined)
-ModifyWord_Loop: in "$$$$$$$$", replace "var" with "${8_DOLLARS}" to "$$$$"
+ModifyWord_Loop: expand "${8_DOLLARS}" to "$$$$"
Global: delete var
Result of ${VAR:@var@${8_DOLLARS}@} is "$$$$" (eval-keep-dollar-and-undefined, regular)
Global: VAR = $$$$
diff --git a/unit-tests/varmod-defined.mk b/unit-tests/varmod-defined.mk
index 8effec82620d..2ee9def9e164 100644
--- a/unit-tests/varmod-defined.mk
+++ b/unit-tests/varmod-defined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-defined.mk,v 1.13 2022/08/24 20:22:10 rillig Exp $
+# $NetBSD: varmod-defined.mk,v 1.16 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :D variable modifier, which returns the given string
# if the variable is defined. It is closely related to the :U modifier.
@@ -46,10 +46,10 @@ DEF= defined
. error
.endif
-# Like in several other places in variable expressions, when
+# Like in several other places in expressions, when
# ApplyModifier_Defined calls Var_Parse, double dollars lead to a parse
# error that is silently ignored. This makes all dollar signs disappear,
-# except for the last, which is a well-formed variable expression.
+# except for the last, which is a well-formed expression.
#
.if ${DEF:D$$$$$${DEF}} != "defined"
. error
@@ -58,7 +58,7 @@ DEF= defined
# Any other text is written without any further escaping. In contrast
# to the :M modifier, parentheses and braces do not need to be nested.
# Instead, the :D modifier is implemented sanely by parsing nested
-# expressions as such, without trying any shortcuts. See ApplyModifier_Match
+# expressions as such, without trying any shortcuts. See ParseModifier_Match
# for an inferior variant.
#
.if ${DEF:D!&((((} != "!&(((("
@@ -106,7 +106,7 @@ VAR:= ${VAR:@var@${8_DOLLARS}@}
# Before var.c 1.1030 from 2022-08-24, the following expression caused an
-# out-of-bounds read when parsing the indirect ':D' modifier.
+# out-of-bounds read when parsing the indirect ':U' modifier.
M_U_backslash:= ${:UU\\}
.if ${:${M_U_backslash}} != "\\"
. error
diff --git a/unit-tests/varmod-edge.exp b/unit-tests/varmod-edge.exp
index 92204d8e1840..c2477f16e950 100644
--- a/unit-tests/varmod-edge.exp
+++ b/unit-tests/varmod-edge.exp
@@ -1,7 +1,7 @@
make: "varmod-edge.mk" line 184: ok M-paren
make: "varmod-edge.mk" line 184: ok M-mixed
make: "varmod-edge.mk" line 184: ok M-unescape
-make: Unclosed variable expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
+make: Unclosed expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
make: "varmod-edge.mk" line 184: ok M-nest-mix
make: "varmod-edge.mk" line 184: ok M-nest-brk
make: "varmod-edge.mk" line 184: ok M-pat-err
diff --git a/unit-tests/varmod-edge.mk b/unit-tests/varmod-edge.mk
index 2d63b372e85e..91220d99e47d 100644
--- a/unit-tests/varmod-edge.mk
+++ b/unit-tests/varmod-edge.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-edge.mk,v 1.17 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varmod-edge.mk,v 1.19 2023/11/19 22:06:15 rillig Exp $
#
# Tests for edge cases in variable modifiers.
#
@@ -16,7 +16,7 @@ MOD.M-paren= ${INP.M-paren:M(*)}
EXP.M-paren= (parentheses) ()
# The first closing brace matches the opening parenthesis.
-# The second closing brace actually ends the variable expression.
+# The second closing brace actually ends the expression.
#
# XXX: This is unexpected but rarely occurs in practice.
TESTS+= M-mixed
@@ -40,7 +40,7 @@ EXP.M-unescape= \(\{}\):
# as open_parens + open_braces == closing_parens + closing_braces. This
# means that ( and } form a matching pair.
#
-# Nested variable expressions are not parsed as such. Instead, only the
+# Nested expressions are not parsed as such. Instead, only the
# parentheses and braces are counted. This leads to a parse error since
# the nested expression is not "${:U*)}" but only "${:U*)", which is
# missing the closing brace. The expression is evaluated anyway.
@@ -51,7 +51,7 @@ TESTS+= M-nest-mix
INP.M-nest-mix= (parentheses)
MOD.M-nest-mix= ${INP.M-nest-mix:M${:U*)}}
EXP.M-nest-mix= (parentheses)}
-# make: Unclosed variable expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
+# make: Unclosed expression, expecting '}' for modifier "U*)" of variable "" with value "*)"
# In contrast to parentheses and braces, the brackets are not counted
# when the :M modifier is parsed since Makefile variables only take the
diff --git a/unit-tests/varmod-gmtime.mk b/unit-tests/varmod-gmtime.mk
index 3dbc5e32e7e4..c30f5edbecc2 100644
--- a/unit-tests/varmod-gmtime.mk
+++ b/unit-tests/varmod-gmtime.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-gmtime.mk,v 1.19 2023/08/19 11:53:10 rillig Exp $
+# $NetBSD: varmod-gmtime.mk,v 1.21 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :gmtime variable modifier, which formats a timestamp
# using strftime(3) in UTC.
@@ -45,7 +45,7 @@
# Before var.c 1.1050 from 2023-05-09, it was not possible to pass the
-# seconds via a variable expression.
+# seconds via an expression.
.if ${%Y:L:gmtime=${:U1593536400}} != "2020"
. error
.endif
@@ -174,4 +174,15 @@ TIMESTAMPS+= $t
. endif
.endfor
+
+.if ${year=%Y month=%m day=%d:L:gmtime=1459494000} != "year=2016 month=04 day=01"
+. error
+.endif
+# Slightly contorted syntax to convert a UTC timestamp from an expression to a
+# formatted timestamp.
+.if ${%Y%m%d:L:${gmtime=${:U1459494000}:L}} != "20160401"
+. error
+.endif
+
+
all:
diff --git a/unit-tests/varmod-ifelse.exp b/unit-tests/varmod-ifelse.exp
index 413f0b9ba4de..653fe104ddaa 100644
--- a/unit-tests/varmod-ifelse.exp
+++ b/unit-tests/varmod-ifelse.exp
@@ -1,14 +1,14 @@
-make: Bad conditional expression 'variable expression == "literal"' in 'variable expression == "literal"?bad:bad'
-make: "varmod-ifelse.mk" line 28: Malformed conditional (${${:Uvariable expression} == "literal":?bad:bad})
-make: Bad conditional expression ' == ""' in ' == ""?bad-assign:bad-assign'
-make: Bad conditional expression ' == ""' in ' == ""?bad-cond:bad-cond'
+make: Bad conditional expression 'bare words == "literal"' before '?bad:bad'
+make: "varmod-ifelse.mk" line 28: Malformed conditional (${${:Ubare words} == "literal":?bad:bad})
+make: Bad conditional expression ' == ""' before '?bad-assign:bad-assign'
+make: Bad conditional expression ' == ""' before '?bad-cond:bad-cond'
make: "varmod-ifelse.mk" line 46: Malformed conditional (${${UNDEF} == "":?bad-cond:bad-cond})
-make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
+make: Bad conditional expression '1 == == 2' before '?yes:no'
make: "varmod-ifelse.mk" line 69: Malformed conditional (${1 == == 2:?yes:no} != "")
CondParser_Eval: "${1 == == 2:?yes:no}" != ""
CondParser_Eval: 1 == == 2
Comparing 1.000000 == 0.000000
-make: Bad conditional expression '1 == == 2' in '1 == == 2?yes:no'
+make: Bad conditional expression '1 == == 2' before '?yes:no'
Comparing "" != ""
make: "varmod-ifelse.mk" line 96: warning: Oops, the parse error should have been propagated.
CondParser_Eval: ${ ${:U\$}{VAR} == value:?ok:bad} != "ok"
@@ -17,15 +17,15 @@ Comparing "value" == "value"
Comparing "ok" != "ok"
make: "varmod-ifelse.mk" line 158: no.
make: "varmod-ifelse.mk" line 162: Comparison with '>=' requires both operands 'no' and '10' to be numeric
-make: Bad conditional expression 'string == "literal" || no >= 10' in 'string == "literal" || no >= 10?yes:no'
+make: Bad conditional expression 'string == "literal" || no >= 10' before '?yes:no'
make: "varmod-ifelse.mk" line 162: .
-make: Bad conditional expression 'string == "literal" && >= 10' in 'string == "literal" && >= 10?yes:no'
+make: Bad conditional expression 'string == "literal" && >= 10' before '?yes:no'
make: "varmod-ifelse.mk" line 169: .
-make: Bad conditional expression 'string == "literal" || >= 10' in 'string == "literal" || >= 10?yes:no'
+make: Bad conditional expression 'string == "literal" || >= 10' before '?yes:no'
make: "varmod-ifelse.mk" line 172: .
make: "varmod-ifelse.mk" line 180: <true>
make: "varmod-ifelse.mk" line 183: <false>
-make: Bad conditional expression ' ' in ' ?true:false'
+make: Bad conditional expression ' ' before '?true:false'
make: "varmod-ifelse.mk" line 186: <>
CondParser_Eval: 0 && ${1:?${:Uthen0:S,}},,}:${:Uelse0:S,}},,}} != "not evaluated"
CondParser_Eval: 1 && ${0:?${:Uthen1:S,}},,}:${:Uelse1:S,}},,}} != "else1"
diff --git a/unit-tests/varmod-ifelse.mk b/unit-tests/varmod-ifelse.mk
index 62d3333b71a4..5c8b55d92717 100644
--- a/unit-tests/varmod-ifelse.mk
+++ b/unit-tests/varmod-ifelse.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-ifelse.mk,v 1.23 2023/07/01 09:06:34 rillig Exp $
+# $NetBSD: varmod-ifelse.mk,v 1.26 2023/12/10 20:12:28 rillig Exp $
#
# Tests for the ${cond:?then:else} variable modifier, which evaluates either
# the then-expression or the else-expression, depending on the condition.
@@ -13,19 +13,19 @@
# The variable name of the expression is expanded and then taken as the
# condition. In the below example it becomes:
#
-# variable expression == "literal"
+# bare words == "literal"
#
# This confuses the parser, which expects an operator instead of the bare
# word "expression". If the name were expanded lazily, everything would be
# fine since the condition would be:
#
-# ${:Uvariable expression} == "literal"
+# ${:Ubare words} == "literal"
#
# Evaluating the variable name lazily would require additional code in
# Var_Parse and ParseVarname, it would be more useful and predictable
# though.
-# expect+1: Malformed conditional (${${:Uvariable expression} == "literal":?bad:bad})
-.if ${${:Uvariable expression} == "literal":?bad:bad}
+# expect+1: Malformed conditional (${${:Ubare words} == "literal":?bad:bad})
+.if ${${:Ubare words} == "literal":?bad:bad}
. error
.else
. error
@@ -61,7 +61,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# This line generates 2 error messages. The first comes from evaluating the
# malformed conditional "1 == == 2", which is reported as "Bad conditional
-# expression" by ApplyModifier_IfElse. The variable expression containing that
+# expression" by ApplyModifier_IfElse. The expression containing that
# conditional therefore returns a parse error from Var_Parse, and this parse
# error propagates to CondEvalExpression, where the "Malformed conditional"
# comes from.
@@ -79,7 +79,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
# XXX: The left-hand side is enclosed in quotes. This results in Var_Parse
# being called without VARE_UNDEFERR. When ApplyModifier_IfElse
# returns AMR_CLEANUP as result, Var_Parse returns varUndefined since the
-# value of the variable expression is still undefined. CondParser_String is
+# value of the expression is still undefined. CondParser_String is
# then supposed to do proper error handling, but since varUndefined is local
# to var.c, it cannot distinguish this return value from an ordinary empty
# string. The left-hand side of the comparison is therefore just an empty
@@ -98,7 +98,7 @@ COND:= ${${UNDEF} == "":?bad-assign:bad-assign}
.MAKEFLAGS: -d0
# As of 2020-12-10, the variable "VAR" is first expanded, and the result of
-# this expansion is then taken as the condition. To force the variable
+# this expansion is then taken as the condition. To force the
# expression in the condition to be evaluated at exactly the right point,
# the '$' of the intended '${VAR}' escapes from the parser in form of the
# expression ${:U\$}. Because of this escaping, the variable "VAR" and thus
@@ -157,17 +157,17 @@ NUMBER= no # not really a number
# expect+1: no.
.info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
# expect+3: Comparison with '>=' requires both operands 'no' and '10' to be numeric
-# expect: make: Bad conditional expression 'string == "literal" || no >= 10' in 'string == "literal" || no >= 10?yes:no'
+# expect: make: Bad conditional expression 'string == "literal" || no >= 10' before '?yes:no'
# expect+1: .
.info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}.
# The following situation occasionally occurs with MKINET6 or similar
# variables.
NUMBER= # empty, not really a number either
-# expect: make: Bad conditional expression 'string == "literal" && >= 10' in 'string == "literal" && >= 10?yes:no'
+# expect: make: Bad conditional expression 'string == "literal" && >= 10' before '?yes:no'
# expect+1: .
.info ${${STRING} == "literal" && ${NUMBER} >= 10:?yes:no}.
-# expect: make: Bad conditional expression 'string == "literal" || >= 10' in 'string == "literal" || >= 10?yes:no'
+# expect: make: Bad conditional expression 'string == "literal" || >= 10' before '?yes:no'
# expect+1: .
.info ${${STRING} == "literal" || ${NUMBER} >= 10:?yes:no}.
@@ -291,3 +291,17 @@ INDIRECT_COND2= $${DELAYED} == "two"
.MAKEFLAGS: -d0
+
+
+# In the modifier parts for the 'then' and 'else' branches, subexpressions are
+# parsed in by inspecting the actual modifiers. In 2008, 2015, 2020, 2022 and
+# 2023, the exact parsing algorithm switched a few times, counting balanced
+# braces instead of proper subexpressions, which meant that unbalanced braces
+# were parsed differently, depending on whether the branch was active or not.
+BRACES= }}}
+NO= ${0:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}}
+YES= ${1:?${BRACES:S,}}},yes,}:${BRACES:S,}}},no,}}
+BOTH= <${YES}> <${NO}>
+.if ${BOTH} != "<yes> <no>"
+. error
+.endif
diff --git a/unit-tests/varmod-indirect.mk b/unit-tests/varmod-indirect.mk
index f79f041cd1ea..66714c75dca3 100644
--- a/unit-tests/varmod-indirect.mk
+++ b/unit-tests/varmod-indirect.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-indirect.mk,v 1.12 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varmod-indirect.mk,v 1.14 2023/11/19 22:32:44 rillig Exp $
#
# Tests for indirect variable modifiers, such as in ${VAR:${M_modifiers}}.
# These can be used for very basic purposes like converting a string to either
@@ -11,9 +11,9 @@
# To apply a modifier indirectly via another variable, the whole
-# modifier must be put into a single variable expression.
+# modifier must be put into a single expression.
# The following expression generates a parse error since its indirect
-# modifier contains more than a sole variable expression.
+# modifier contains more than a sole expression.
#
# expect+1: Unknown modifier "${"
.if ${value:L:${:US}${:U,value,replacement,}} != "S,value,replacement,}"
@@ -71,20 +71,20 @@
.endif
-# The nested variable expression expands to "tu", and this is interpreted as
+# The nested expression expands to "tu", and this is interpreted as
# a variable modifier for the value "Upper", resulting in "UPPER".
.if ${Upper:L:${:Utu}} != "UPPER"
. error
.endif
-# The nested variable expression expands to "tl", and this is interpreted as
+# The nested expression expands to "tl", and this is interpreted as
# a variable modifier for the value "Lower", resulting in "lower".
.if ${Lower:L:${:Utl}} != "lower"
. error
.endif
-# The nested variable expression is ${1 != 1:?Z:tl}, consisting of the
+# The nested expression is ${1 != 1:?Z:tl}, consisting of the
# condition "1 != 1", the then-branch "Z" and the else-branch "tl". Since
# the condition evaluates to false, the then-branch is ignored (it would
# have been an unknown modifier anyway) and the ":tl" modifier is applied.
@@ -133,7 +133,7 @@ M_NoPrimes= ${PRIMES:${M_ListToSkip}}
.MAKEFLAGS: -d0
-# In contrast to the .if conditions, the .for loop allows undefined variable
+# In contrast to the .if conditions, the .for loop allows undefined
# expressions. These expressions expand to empty strings.
# An undefined expression without any modifiers expands to an empty string.
@@ -172,10 +172,10 @@ M_NoPrimes= ${PRIMES:${M_ListToSkip}}
# a variable assignment using ':='.
.MAKEFLAGS: -dpv
-# The undefined variable expression is kept as-is.
+# The undefined expression is kept as-is.
_:= before ${UNDEF} after
-# The undefined variable expression is kept as-is.
+# The undefined expression is kept as-is.
_:= before ${UNDEF:${:US,a,a,}} after
# XXX: The subexpression ${:U} is fully defined, therefore it is expanded.
@@ -189,7 +189,7 @@ _:= before ${UNDEF:${:US,a,a,}} after
_:= before ${UNDEF:${:U}} after
# XXX: This expands to ${UNDEF:Z}, which will behave differently if the
-# variable '_' is used in a context where the variable expression ${_} is
+# variable '_' is used in a context where the expression ${_} is
# parsed but not evaluated.
# expect+1: Unknown modifier "Z"
_:= before ${UNDEF:${:UZ}} after
@@ -201,7 +201,7 @@ _:= before ${UNDEF:${:UZ}} after
# When evaluating indirect modifiers, these modifiers may expand to ':tW',
# which modifies the interpretation of the expression value. This modified
# interpretation only lasts until the end of the indirect modifier, it does
-# not influence the outer variable expression.
+# not influence the outer expression.
.if ${1 2 3:L:tW:[#]} != 1 # direct :tW applies to the :[#]
. error
.endif
@@ -213,7 +213,7 @@ _:= before ${UNDEF:${:UZ}} after
# When evaluating indirect modifiers, these modifiers may expand to ':ts*',
# which modifies the interpretation of the expression value. This modified
# interpretation only lasts until the end of the indirect modifier, it does
-# not influence the outer variable expression.
+# not influence the outer expression.
#
# In this first expression, the direct ':ts*' has no effect since ':U' does not
# treat the expression value as a list of words but as a single word. It has
diff --git a/unit-tests/varmod-l-name-to-value.mk b/unit-tests/varmod-l-name-to-value.mk
index 354622cf098b..e87e68967544 100644
--- a/unit-tests/varmod-l-name-to-value.mk
+++ b/unit-tests/varmod-l-name-to-value.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-l-name-to-value.mk,v 1.7 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-l-name-to-value.mk,v 1.8 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :L modifier, which returns the variable name as the new value.
@@ -28,7 +28,7 @@
.endif
# Between 2020-09-22 (var.c 1.527) and 2020-09-30 (var.c 1.553), there was
-# a bug in the evaluation of variable expressions. Indirect modifiers like
+# a bug in the evaluation of expressions. Indirect modifiers like
# the below :L did not update the definedness of the enclosing expression.
# This resulted in a wrong "Malformed conditional".
.if ${value:${:UL}} == ""
diff --git a/unit-tests/varmod-localtime.mk b/unit-tests/varmod-localtime.mk
index 86f90064ded3..f6fcc61a7fa6 100644
--- a/unit-tests/varmod-localtime.mk
+++ b/unit-tests/varmod-localtime.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-localtime.mk,v 1.13 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varmod-localtime.mk,v 1.14 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :localtime variable modifier, which formats a timestamp
# using strftime(3) in local time.
@@ -45,7 +45,7 @@
# Before var.c 1.1050 from 2023-05-09, it was not possible to pass the
-# seconds via a variable expression.
+# seconds via an expression.
.if ${%Y:L:localtime=${:U1593536400}} != "2020"
. error
.endif
diff --git a/unit-tests/varmod-loop-varname.mk b/unit-tests/varmod-loop-varname.mk
index 43c72cee3ee5..6f7436f277da 100644
--- a/unit-tests/varmod-loop-varname.mk
+++ b/unit-tests/varmod-loop-varname.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-loop-varname.mk,v 1.5 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varmod-loop-varname.mk,v 1.6 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the first part of the variable modifier ':@var@...@', which
# contains the variable name to use during the loop.
@@ -112,7 +112,7 @@ RES3= 3
#
# As of 2020-10-18, the :@ modifier is implemented by actually setting a
# 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
+# loop. This is different from the .for loops, which substitute the
# expression with ${:Uvalue}, leading to different unwanted side effects.
#
# To make the behavior more predictable, the :@ modifier should restore the
diff --git a/unit-tests/varmod-loop.mk b/unit-tests/varmod-loop.mk
index d09e49a6d842..64cc6ca85043 100644
--- a/unit-tests/varmod-loop.mk
+++ b/unit-tests/varmod-loop.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-loop.mk,v 1.23 2023/02/18 11:55:20 rillig Exp $
+# $NetBSD: varmod-loop.mk,v 1.24 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the expression modifier ':@var@body@', which replaces each word of
# the expression with the expanded body, which may contain references to the
@@ -52,7 +52,7 @@ mod-loop-dollar:
#
# As of 2020-10-18, the :@ modifier is implemented by actually setting a
# 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
+# loop. This is different from the .for loops, which substitute the
# expression with ${:Uvalue}, leading to different unwanted side effects.
#
# To make the behavior more predictable, the :@ modifier should restore the
@@ -111,7 +111,7 @@ SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}
# The variable SUBST_CONTAINING_LOOP therefore gets assigned the raw value
# "$$$$ $$$$$$$$ $$$$$$$$".
#
-# The variable expression in the condition then expands this raw stored value
+# The expression in the condition then expands this raw stored value
# once, resulting in "$$ $$$$ $$$$". The effects from VARE_KEEP_DOLLAR no
# longer take place since they had only been active during the evaluation of
# the variable assignment.
diff --git a/unit-tests/varmod-match-escape.mk b/unit-tests/varmod-match-escape.mk
index 098a24dd9f72..1da3918fe1a5 100755
--- a/unit-tests/varmod-match-escape.mk
+++ b/unit-tests/varmod-match-escape.mk
@@ -1,8 +1,8 @@
-# $NetBSD: varmod-match-escape.mk,v 1.10 2023/06/23 04:56:54 rillig Exp $
+# $NetBSD: varmod-match-escape.mk,v 1.12 2023/11/19 21:47:52 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
-# first backslash or not. See ApplyModifier_Match, "copy = true".
+# depending on whether there was an expression somewhere before the
+# first backslash or not. See ParseModifier_Match, "copy = true".
#
# Apart from the different and possibly confusing debug output, there is no
# difference in behavior. When parsing the modifier text, only \{, \} and \:
@@ -18,23 +18,23 @@ SPECIALS= \: : \\ * \*
.endif
# And now both cases combined: A single modifier with both an escaped ':'
-# as well as a variable expression that expands to a ':'.
+# as well as an expression that expands to a ':'.
#
-# XXX: As of 2020-11-01, when an escaped ':' occurs before the variable
+# XXX: As of 2020-11-01, when an escaped ':' occurs before the
# expression, the whole modifier text is subject to unescaping '\:' to ':',
-# before the variable expression is expanded. This means that the '\:' in
-# the variable expression is expanded as well, turning ${:U\:} into a simple
+# before the expression is expanded. This means that the '\:' in
+# the expression is expanded as well, turning ${:U\:} into a simple
# ${:U:}, which silently expands to an empty string, instead of generating
# an error message.
#
# XXX: As of 2020-11-01, the modifier on the right-hand side of the
-# comparison is parsed differently though. First, the variable expression
+# comparison is parsed differently though. First, the expression
# is parsed, resulting in ':' and needSubst=true. After that, the escaped
# ':' is seen, and this time, copy=true is not executed but stays copy=false.
# Therefore the escaped ':' is kept as-is, and the final pattern becomes
# ':\:'.
#
-# If ApplyModifier_Match had used the same parsing algorithm as Var_Subst,
+# If ParseModifier_Match had used the same parsing algorithm as Var_Subst,
# both patterns would end up as '::'.
#
VALUES= : :: :\:
@@ -53,7 +53,7 @@ VALUES= : :: :\:
.endif
# XXX: As of 2020-11-01, unlike all other variable modifiers, '\$' is not
-# parsed as an escaped '$'. Instead, ApplyModifier_Match first scans for
+# parsed as an escaped '$'. Instead, ParseModifier_Match first scans for
# the ':' at the end of the modifier, which results in the pattern '\$'.
# No unescaping takes place since the pattern neither contained '\:' nor
# '\{' nor '\}'. But the text is expanded, and a lonely '$' at the end
diff --git a/unit-tests/varmod-match.exp b/unit-tests/varmod-match.exp
index 4ac89bda8e0b..e4e0783a7b15 100644
--- a/unit-tests/varmod-match.exp
+++ b/unit-tests/varmod-match.exp
@@ -1,25 +1,14 @@
-CondParser_Eval: ${NUMBERS:M[A-Z]*} != "One Two Three Four"
-Comparing "One Two Three Four" != "One Two Three Four"
-CondParser_Eval: ${NUMBERS:M[^A-Z]*} != "five six seven"
-Comparing "five six seven" != "five six seven"
-CondParser_Eval: ${NUMBERS:M[^s]*[ex]} != "One Three five"
-Comparing "One Three five" != "One Three five"
-CondParser_Eval: ${:U****************:M****************b}
-CondParser_Eval: ${:U..................................................b:M*?*?*?*?*?a}
-CondParser_Eval: ${:Ua \$ sign:M*$$*} != "\$"
-Comparing "$" != "$"
-CondParser_Eval: ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk"
-Comparing "any-asterisk" != "any-asterisk"
-make: "varmod-match.mk" line 162: warning: Unfinished character list in pattern '[' of modifier ':M'
-make: "varmod-match.mk" line 162: Unknown modifier "]"
-make: "varmod-match.mk" line 162: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
-make: "varmod-match.mk" line 205: warning: Unfinished character list in pattern 'a[' of modifier ':M'
-make: "varmod-match.mk" line 213: warning: Unfinished character list in pattern 'a[^' of modifier ':M'
-make: "varmod-match.mk" line 221: warning: Unfinished character list in pattern '[-x1-3' of modifier ':M'
-make: "varmod-match.mk" line 229: warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M'
-make: "varmod-match.mk" line 238: warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M'
-make: "varmod-match.mk" line 258: warning: Unfinished character range in pattern '[x-' of modifier ':M'
-make: "varmod-match.mk" line 270: warning: Unfinished character range in pattern '[^x-' of modifier ':M'
+make: "varmod-match.mk" line 289: warning: Unfinished character list in pattern 'a[' of modifier ':M'
+make: "varmod-match.mk" line 297: warning: Unfinished character list in pattern 'a[^' of modifier ':M'
+make: "varmod-match.mk" line 305: warning: Unfinished character list in pattern '[-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 313: warning: Unfinished character list in pattern '*[-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 322: warning: Unfinished character list in pattern '[^-x1-3' of modifier ':M'
+make: "varmod-match.mk" line 336: warning: Unfinished character list in pattern '?[\' of modifier ':M'
+make: "varmod-match.mk" line 344: warning: Unfinished character range in pattern '[x-' of modifier ':M'
+make: "varmod-match.mk" line 356: warning: Unfinished character range in pattern '[^x-' of modifier ':M'
+make: "varmod-match.mk" line 364: warning: Unfinished character list in pattern '[' of modifier ':M'
+make: "varmod-match.mk" line 364: Unknown modifier "]"
+make: "varmod-match.mk" line 364: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod-match.mk b/unit-tests/varmod-match.mk
index 2804c2f2da4a..d93d1839192d 100644
--- a/unit-tests/varmod-match.mk
+++ b/unit-tests/varmod-match.mk
@@ -1,96 +1,96 @@
-# $NetBSD: varmod-match.mk,v 1.15 2023/06/23 04:56:54 rillig Exp $
+# $NetBSD: varmod-match.mk,v 1.20 2023/12/17 23:19:02 rillig Exp $
#
-# Tests for the :M variable modifier, which filters words that match the
+# Tests for the ':M' modifier, which keeps only those words that match the
# given pattern.
#
-# See ApplyModifier_Match and ModifyWord_Match for the implementation.
+# Table of contents
+#
+# 1. Pattern characters '*', '?' and '\'
+# 2. Character lists and character ranges
+# 3. Parsing and escaping
+# 4. Interaction with other modifiers
+# 5. Performance
+# 6. Error handling
+# 7. Historical bugs
+#
+# See ApplyModifier_Match, ParseModifier_Match, ModifyWord_Match and
+# Str_Match.
-.MAKEFLAGS: -dc
-NUMBERS= One Two Three Four five six seven
+# 1. Pattern characters '*', '?' and '\'
+#
+# * matches 0 or more characters
+# ? matches 1 character
+# \x matches the character 'x'
-# Only keep words that start with an uppercase letter.
-.if ${NUMBERS:M[A-Z]*} != "One Two Three Four"
+# The pattern is anchored both at the beginning and at the end of the word.
+# Since the pattern 'e' does not contain any pattern matching characters, it
+# matches exactly the word 'e', twice.
+.if ${a c e aa cc ee e f g:L:Me} != "e e"
. error
.endif
-# Only keep words that start with a character other than an uppercase letter.
-.if ${NUMBERS:M[^A-Z]*} != "five six seven"
+# The pattern character '?' matches exactly 1 character, the pattern character
+# '*' matches 0 or more characters. The whole pattern matches all words that
+# start with 's' and have 3 or more characters.
+.if ${One Two Three Four five six seven:L:Ms??*} != "six seven"
. error
.endif
-# Only keep words that don't start with s and at the same time end with
-# either of [ex].
-#
-# This test case ensures that the negation from the first character class
-# does not propagate to the second character class.
-.if ${NUMBERS:M[^s]*[ex]} != "One Three five"
+# Ensure that a pattern without placeholders only matches itself.
+.if ${a aa aaa b ba baa bab:L:Ma} != "a"
. error
.endif
-# Before 2020-06-13, this expression called Str_Match 601,080,390 times.
-# Since 2020-06-13, this expression calls Str_Match 1 time.
-.if ${:U****************:M****************b}
+# Ensure that a pattern that ends with '*' is properly anchored at the
+# beginning.
+.if ${a aa aaa b ba baa bab:L:Ma*} != "a aa aaa"
+. error
.endif
-# Before 2023-06-22, this expression called Str_Match 2,621,112 times.
-# Adding another '*?' to the pattern called Str_Match 20,630,572 times.
-# Adding another '*?' to the pattern called Str_Match 136,405,672 times.
-# Adding another '*?' to the pattern called Str_Match 773,168,722 times.
-# Adding another '*?' to the pattern called Str_Match 3,815,481,072 times.
-# Since 2023-06-22, Str_Match no longer backtracks.
-.if ${:U..................................................b:M*?*?*?*?*?a}
+# Ensure that a pattern that starts with '*' is properly anchored at the end.
+.if ${a aa aaa b ba baa bab:L:M*a} != "a aa aaa ba baa"
+. error
.endif
-# To match a dollar sign in a word, double it.
-#
-# This is different from the :S and :C variable modifiers, where a '$'
-# has to be escaped as '\$'.
-.if ${:Ua \$ sign:M*$$*} != "\$"
+# Test the fast code path for '*' followed by a regular character.
+.if ${:U file.c file.*c file.h file\.c :M*.c} != "file.c file\\.c"
. error
.endif
-
-# In the :M modifier, '\$' does not escape a dollar. Instead it is
-# interpreted as a backslash followed by whatever expression the
-# '$' starts.
-#
-# This differs from the :S, :C and several other variable modifiers.
-${:U*}= asterisk
-.if ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk"
+# Ensure that the fast code path correctly handles the backslash.
+.if ${:U file.c file.*c file.h file\.c :M*\.c} != "file.c file\\.c"
+. error
+.endif
+# Ensure that the fast code path correctly handles '\*'.
+.if ${:U file.c file.*c file.h file\.c :M*\*c} != "file.*c"
+. error
+.endif
+# Ensure that the partial match '.c' doesn't confuse the fast code path.
+.if ${:U file.c.cc file.cc.cc file.cc.c :M*.cc} != "file.c.cc file.cc.cc"
+. error
+.endif
+# Ensure that the substring '.cc' doesn't confuse the fast code path for '.c'.
+.if ${:U file.c.cc file.cc.cc file.cc.c :M*.c} != "file.cc.c"
. error
.endif
-# TODO: ${VAR:M(((}}}}
-# TODO: ${VAR:M{{{)))}
-# TODO: ${VAR:M${UNBALANCED}}
-# TODO: ${VAR:M${:U(((\}\}\}}}
-.MAKEFLAGS: -d0
-
-# Special characters:
-# * matches 0 or more arbitrary characters
-# ? matches a single arbitrary character
-# \ starts an escape sequence, only outside ranges
-# [ starts a set for matching a single character
-# ] ends a set for matching a single character
-# - in a set, forms a range of characters
-# ^ as the first character in a set, negates the set
-# ( during parsing of the pattern, starts a nesting level
-# ) during parsing of the pattern, ends a nesting level
-# { during parsing of the pattern, starts a nesting level
-# } during parsing of the pattern, ends a nesting level
-# : during parsing of the pattern, finishes the pattern
-# $ during parsing of the pattern, starts a nested expression
-# # in a line except a shell command, starts a comment
-#
-# Pattern parts:
-# * matches 0 or more arbitrary characters
-# ? matches exactly 1 arbitrary character
-# \x matches exactly the character 'x'
-# [...] matches exactly 1 character from the set
-# [^...] matches exactly 1 character outside the set
-# [a-z] matches exactly 1 character from the range 'a' to 'z'
+# 2. Character lists and character ranges
#
+# [...] matches 1 character from the listed characters
+# [^...] matches 1 character from the unlisted characters
+# [a-z] matches 1 character from the range 'a' to 'z'
+# [z-a] matches 1 character from the range 'a' to 'z'
+
+# Only keep words that start with an uppercase letter.
+.if ${One Two Three Four five six seven:L:M[A-Z]*} != "One Two Three Four"
+. error
+.endif
+
+# Only keep words that start with a character other than an uppercase letter.
+.if ${One Two Three Four five six seven:L:M[^A-Z]*} != "five six seven"
+. error
+.endif
# [] matches never
.if ${ ab a[]b a[b a b :L:M[]} != ""
@@ -134,12 +134,82 @@ ${:U*}= asterisk
. error
.endif
-# [\] matches a single backslash
-WORDS= a\b a[\]b ab
+# [\] matches a single backslash; no escaping takes place in
+# character ranges
+# Without the 'b' in the below words, the backslash would end a word and thus
+# influence how the string is split into words.
+WORDS= a\b a[\]b ab a\\b
.if ${WORDS:Ma[\]b} != "a\\b"
. error
.endif
+# [[-]] May look like it would match a single '[', '\' or ']', but
+# the inner ']' has two roles: it is the upper bound of the
+# character range as well as the closing character of the
+# character list. The outer ']' is just a regular character.
+WORDS= [ ] [] \] ]]
+.if ${WORDS:M[[-]]} != "[] \\] ]]"
+. error
+.endif
+
+# [b[-]a]
+# Same as for '[[-]]': the character list stops at the first
+# ']', and the 'a]' is treated as a literal string.
+WORDS= [a \a ]a []a \]a ]]a [a] \a] ]a] ba]
+.if ${WORDS:M[b[-]a]} != "[a] \\a] ]a] ba]"
+. error
+.endif
+
+# [-] Matches a single '-' since the '-' only becomes part of a
+# character range if it is preceded and followed by another
+# character.
+WORDS= - -]
+.if ${WORDS:M[-]} != "-"
+. error
+.endif
+
+# Only keep words that don't start with s and at the same time end with
+# either of [ex].
+#
+# This test case ensures that the negation from the first character list
+# '[^s]' does not propagate to the second character list '[ex]'.
+.if ${One Two Three Four five six seven:L:M[^s]*[ex]} != "One Three five"
+. error
+.endif
+
+
+# 3. Parsing and escaping
+#
+# * matches 0 or more characters
+# ? matches 1 character
+# \ outside a character list, escapes the following character
+# [ starts a character list for matching 1 character
+# ] ends a character list for matching 1 character
+# - in a character list, forms a character range
+# ^ at the beginning of a character list, negates the list
+# ( while parsing the pattern, starts a nesting level
+# ) while parsing the pattern, ends a nesting level
+# { while parsing the pattern, starts a nesting level
+# } while parsing the pattern, ends a nesting level
+# : while parsing the pattern, terminates the pattern
+# $ while parsing the pattern, starts a nested expression
+# # in a line except a shell command, starts a comment
+
+# The pattern can come from an expression. For single-letter
+# variables, either the short form or the long form can be used, just as
+# everywhere else.
+PRIMES= 2 3 5 7 11
+n= 2
+.if ${PRIMES:M$n} != "2"
+. error
+.endif
+.if ${PRIMES:M${n}} != "2"
+. error
+.endif
+.if ${PRIMES:M${:U2}} != "2"
+. error
+.endif
+
# : terminates the pattern
.if ${ A * :L:M:} != ""
. error
@@ -155,50 +225,64 @@ WORDS= a\b a[\]b ab
. error
.endif
-# [:] matches never since the ':' starts the next modifier
-# expect+3: warning: Unfinished character list in pattern '[' of modifier ':M'
-# expect+2: Unknown modifier "]"
-# expect+1: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
-.if ${ ${:U\:} ${:U\:\:} :L:M[:]} != ":"
-. error
-.else
+# To match a dollar sign in a word, double it.
+#
+# This is different from the :S and :C modifiers, where a '$' has to be
+# escaped as '\$'.
+.if ${:Ua \$ sign:M*$$*} != "\$"
. error
.endif
-# [\] matches exactly a backslash; no escaping takes place in
-# character ranges
-# Without the 'a' in the below words, the backslash would end a word and thus
-# influence how the string is split into words.
-WORDS= 1\a 2\\a
-.if ${WORDS:M?[\]a} != "1\\a"
+# In the :M modifier, '\$' does not escape a dollar. Instead it is
+# interpreted as a backslash followed by whatever expression the
+# '$' starts.
+#
+# This differs from the :S, :C and several other modifiers.
+${:U*}= asterisk
+.if ${:Ua \$ sign any-asterisk:M*\$*} != "any-asterisk"
. error
.endif
-# [[-]] May look like it would match a single '[', '\' or ']', but
-# the inner ']' has two roles: it is the upper bound of the
-# character range as well as the closing character of the
-# character list. The outer ']' is just a regular character.
-WORDS= [ ] [] \] ]]
-.if ${WORDS:M[[-]]} != "[] \\] ]]"
+# TODO: ${VAR:M(((}}}}
+# TODO: ${VAR:M{{{)))}
+# TODO: ${VAR:M${UNBALANCED}}
+# TODO: ${VAR:M${:U(((\}\}\}}}
+
+
+# 4. Interaction with other modifiers
+
+# The modifier ':tW' prevents splitting at whitespace. Even leading and
+# trailing whitespace is preserved.
+.if ${ plain string :L:tW:M*} != " plain string "
. error
.endif
-# [b[-]a]
-# Same as for '[[-]]': the character list stops at the first
-# ']', and the 'a]' is treated as a literal string.
-WORDS= [a \a ]a []a \]a ]]a [a] \a] ]a] ba]
-.if ${WORDS:M[b[-]a]} != "[a] \\a] ]a] ba]"
+# Without the modifier ':tW', the string is split into words. All whitespace
+# around and between the words is normalized to a single space.
+.if ${ plain string :L:M*} != "plain string"
. error
.endif
-# [-] Matches a single '-' since the '-' only becomes part of a
-# character range if it is preceded and followed by another
-# character.
-WORDS= - -]
-.if ${WORDS:M[-]} != "-"
-. error
+
+# 5. Performance
+
+# Before 2020-06-13, this expression called Str_Match 601,080,390 times.
+# Since 2020-06-13, this expression calls Str_Match 1 time.
+.if ${:U****************:M****************b}
+.endif
+
+# Before 2023-06-22, this expression called Str_Match 2,621,112 times.
+# Adding another '*?' to the pattern called Str_Match 20,630,572 times.
+# Adding another '*?' to the pattern called Str_Match 136,405,672 times.
+# Adding another '*?' to the pattern called Str_Match 773,168,722 times.
+# Adding another '*?' to the pattern called Str_Match 3,815,481,072 times.
+# Since 2023-06-22, Str_Match no longer backtracks.
+.if ${:U..................................................b:M*?*?*?*?*?a}
.endif
+
+# 6. Error handling
+
# [ Incomplete empty character list, never matches.
WORDS= a a[
# expect+1: warning: Unfinished character list in pattern 'a[' of modifier ':M'
@@ -247,7 +331,9 @@ WORDS= - + x xx 0 1 2 3 4 [x1-3
# part of the word. Only the very last word of a string can be
# '\', as there is no following space that could be escaped.
WORDS= \\ \a ${:Ux\\}
-.if ${WORDS:M?[\]} != "\\\\ x\\"
+PATTERN= ${:U?[\\}
+# expect+1: warning: Unfinished character list in pattern '?[\' of modifier ':M'
+.if ${WORDS:M${PATTERN}} != "\\\\ x\\"
. error
.endif
@@ -271,35 +357,18 @@ WORDS= [x- x x- y yyyyy
. error
.endif
-
-# The modifier ':tW' prevents splitting at whitespace. Even leading and
-# trailing whitespace is preserved.
-.if ${ plain string :L:tW:M*} != " plain string "
+# [:] matches never since the ':' starts the next modifier
+# expect+3: warning: Unfinished character list in pattern '[' of modifier ':M'
+# expect+2: Unknown modifier "]"
+# expect+1: Malformed conditional (${ ${:U\:} ${:U\:\:} :L:M[:]} != ":")
+.if ${ ${:U\:} ${:U\:\:} :L:M[:]} != ":"
. error
-.endif
-
-# Without the modifier ':tW', the string is split into words. All whitespace
-# around and between the words is normalized to a single space.
-.if ${ plain string :L:M*} != "plain string"
+.else
. error
.endif
-# The pattern can come from a variable expression. For single-letter
-# variables, either the short form or the long form can be used, just as
-# everywhere else.
-PRIMES= 2 3 5 7 11
-n= 2
-.if ${PRIMES:M$n} != "2"
-. error
-.endif
-.if ${PRIMES:M${n}} != "2"
-. error
-.endif
-.if ${PRIMES:M${:U2}} != "2"
-. error
-.endif
-
+# 7. Historical bugs
# Before var.c 1.1031 from 2022-08-24, the following expressions caused an
# out-of-bounds read beyond the indirect ':M' modifiers.
diff --git a/unit-tests/varmod-mtime.exp b/unit-tests/varmod-mtime.exp
index a97c9805b5f1..039173feddc3 100644
--- a/unit-tests/varmod-mtime.exp
+++ b/unit-tests/varmod-mtime.exp
@@ -1,8 +1,14 @@
-make: "varmod-mtime.mk" line 60: Cannot determine mtime for 'no/such/file1': <ENOENT>
-make: "varmod-mtime.mk" line 60: Cannot determine mtime for 'no/such/file2': <ENOENT>
-make: "varmod-mtime.mk" line 60: Malformed conditional (${no/such/file1 no/such/file2:L:mtime=error})
-make: "varmod-mtime.mk" line 71: Invalid argument 'errorhandler-no' for modifier ':mtime'
-make: "varmod-mtime.mk" line 71: Malformed conditional (${MAKEFILE:mtime=errorhandler-no} > 0)
+make: "varmod-mtime.mk" line 47: Invalid argument '123x' for modifier ':mtime'
+make: "varmod-mtime.mk" line 47: Malformed conditional (${no/such/file:L:mtime=123x})
+make: "varmod-mtime.mk" line 70: Cannot determine mtime for 'no/such/file1': <ENOENT>
+make: "varmod-mtime.mk" line 70: Cannot determine mtime for 'no/such/file2': <ENOENT>
+make: "varmod-mtime.mk" line 70: Malformed conditional (${no/such/file1 no/such/file2:L:mtime=error})
+make: "varmod-mtime.mk" line 81: Invalid argument 'errorhandler-no' for modifier ':mtime'
+make: "varmod-mtime.mk" line 81: Malformed conditional (${MAKEFILE:mtime=errorhandler-no} > 0)
+make: "varmod-mtime.mk" line 90: Invalid argument 'warn' for modifier ':mtime'
+make: "varmod-mtime.mk" line 90: Malformed conditional (${MAKEFILE:mtime=warn} > 0)
+make: "varmod-mtime.mk" line 115: Unknown modifier "mtim"
+make: "varmod-mtime.mk" line 115: Malformed conditional (${anything:L:mtim})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod-mtime.mk b/unit-tests/varmod-mtime.mk
index 84cc7c996952..298756e152c1 100644
--- a/unit-tests/varmod-mtime.mk
+++ b/unit-tests/varmod-mtime.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-mtime.mk,v 1.5 2023/08/19 08:19:25 rillig Exp $
+# $NetBSD: varmod-mtime.mk,v 1.9 2023/12/17 14:07:22 rillig Exp $
#
# Tests for the ':mtime' variable modifier, which maps each word of the
# expression to that file's modification time.
@@ -41,6 +41,16 @@ not_found_mtime:= ${no/such/file:L:mtime}
.endif
+# The fallback timestamp must only be an integer, without trailing characters.
+# expect+2: Invalid argument '123x' for modifier ':mtime'
+# expect+1: Malformed conditional (${no/such/file:L:mtime=123x})
+.if ${no/such/file:L:mtime=123x}
+. error
+.else
+. error
+.endif
+
+
# The timestamp of a newly created file must be at least as great as the
# timestamp when parsing of this makefile started.
COOKIE= ${TMPDIR:U/tmp}/varmod-mtime.cookie
@@ -74,9 +84,42 @@ _!= rm -f ${COOKIE}
.endif
+# Only the word 'error' can be used as a fallback argument to the modifier.
+# expect+2: Invalid argument 'warn' for modifier ':mtime'
+# expect+1: Malformed conditional (${MAKEFILE:mtime=warn} > 0)
+.if ${MAKEFILE:mtime=warn} > 0
+. error
+.else
+. error
+.endif
+
+
# Ensure that the fallback for a missing modification time is indeed the
# current time, and not any later time.
end:= ${%s:L:gmtime}
.if ${not_found_mtime} > ${end}
. error
.endif
+
+
+# If the expression is irrelevant, the ':mtime' modifier is only parsed, it
+# does not perform any filesystem operations.
+.if 0 && ${no/such/file:L:mtime=error}
+. error
+.endif
+
+
+# If there is a typo in the modifier name, it does not match.
+# expect+2: Unknown modifier "mtim"
+# expect+1: Malformed conditional (${anything:L:mtim})
+.if ${anything:L:mtim}
+. error
+.else
+. error
+.endif
+
+
+# An empty word list results in an empty mtime list.
+.if ${:U:mtime} != ""
+. error
+.endif
diff --git a/unit-tests/varmod-order.exp b/unit-tests/varmod-order.exp
index 487243a1678d..12d0bff75157 100644
--- a/unit-tests/varmod-order.exp
+++ b/unit-tests/varmod-order.exp
@@ -2,9 +2,9 @@ make: Bad modifier ":OX" for variable "WORDS"
make: "varmod-order.mk" line 16: Undefined variable "${WORDS:OX"
make: Bad modifier ":OxXX" for variable "WORDS"
make: "varmod-order.mk" line 21: Undefined variable "${WORDS:Ox"
-make: Unclosed variable expression, expecting '}' for modifier "O" of variable "WORDS" with value "eight five four nine one seven six ten three two"
-make: Unclosed variable expression, expecting '}' for modifier "On" of variable "NUMBERS" with value "1 2 3 4 5 6 7 8 9 10"
-make: Unclosed variable expression, expecting '}' for modifier "Onr" of variable "NUMBERS" with value "10 9 8 7 6 5 4 3 2 1"
+make: Unclosed expression, expecting '}' for modifier "O" of variable "WORDS" with value "eight five four nine one seven six ten three two"
+make: Unclosed expression, expecting '}' for modifier "On" of variable "NUMBERS" with value "1 2 3 4 5 6 7 8 9 10"
+make: Unclosed expression, expecting '}' for modifier "Onr" of variable "NUMBERS" with value "10 9 8 7 6 5 4 3 2 1"
make: Bad modifier ":Oxn" for variable "NUMBERS"
make: "varmod-order.mk" line 33: Malformed conditional (${NUMBERS:Oxn})
make: Bad modifier ":On_typo" for variable "NUMBERS"
diff --git a/unit-tests/varmod-range.exp b/unit-tests/varmod-range.exp
index b6226ec82c5b..9b0dad40a78e 100644
--- a/unit-tests/varmod-range.exp
+++ b/unit-tests/varmod-range.exp
@@ -1,13 +1,14 @@
-make: "varmod-range.mk" line 55: Invalid number "x}Rest" != "Rest"" for ':range' modifier
-make: "varmod-range.mk" line 55: Malformed conditional ("${:U:range=x}Rest" != "Rest")
-make: "varmod-range.mk" line 66: Unknown modifier "x0"
-make: "varmod-range.mk" line 66: Malformed conditional ("${:U:range=0x0}Rest" != "Rest")
-make: "varmod-range.mk" line 84: Unknown modifier "rang"
-make: "varmod-range.mk" line 84: Malformed conditional ("${a b c:L:rang}Rest" != "Rest")
-make: "varmod-range.mk" line 93: Unknown modifier "rango"
-make: "varmod-range.mk" line 93: Malformed conditional ("${a b c:L:rango}Rest" != "Rest")
-make: "varmod-range.mk" line 102: Unknown modifier "ranger"
-make: "varmod-range.mk" line 102: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest")
+make: "varmod-range.mk" line 43: Malformed conditional (${:range=5} != "")
+make: "varmod-range.mk" line 67: Invalid number "x}Rest" != "Rest"" for ':range' modifier
+make: "varmod-range.mk" line 67: Malformed conditional ("${:U:range=x}Rest" != "Rest")
+make: "varmod-range.mk" line 78: Unknown modifier "x0"
+make: "varmod-range.mk" line 78: Malformed conditional ("${:U:range=0x0}Rest" != "Rest")
+make: "varmod-range.mk" line 96: Unknown modifier "rang"
+make: "varmod-range.mk" line 96: Malformed conditional ("${a b c:L:rang}Rest" != "Rest")
+make: "varmod-range.mk" line 105: Unknown modifier "rango"
+make: "varmod-range.mk" line 105: Malformed conditional ("${a b c:L:rango}Rest" != "Rest")
+make: "varmod-range.mk" line 114: Unknown modifier "ranger"
+make: "varmod-range.mk" line 114: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varmod-range.mk b/unit-tests/varmod-range.mk
index 86ef11a59eaf..920001096054 100644
--- a/unit-tests/varmod-range.mk
+++ b/unit-tests/varmod-range.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-range.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varmod-range.mk,v 1.10 2023/12/17 14:07:22 rillig Exp $
#
# Tests for the :range variable modifier, which generates sequences
# of integers from the given range.
@@ -7,7 +7,7 @@
# modword.mk
# The :range modifier generates a sequence of integers, one number per
-# word of the variable expression's value.
+# word of the expression's value.
.if ${a b c:L:range} != "1 2 3"
. error
.endif
@@ -19,20 +19,32 @@
.endif
# The :range modifier takes the number of words from the value of the
-# variable expression. If that expression is undefined, the range is
+# expression. If that expression is undefined, the range is
# undefined as well. This should not come as a surprise.
.if "${:range}" != ""
. error
.endif
+# An empty expression results in a sequence of a single number, even though
+# the expression contains 0 words.
+.if ${:U:range} != "1"
+. error
+.endif
+
# The :range modifier can be given a parameter, which makes the generated
-# range independent from the value or the name of the variable expression.
-#
-# XXX: As of 2020-09-27, the :range=... modifier does not turn the undefined
-# expression into a defined one. This looks like an oversight.
+# range independent from the value or the name of the expression.
.if "${:range=5}" != ""
. error
.endif
+# XXX: As of 2023-12-17, the ':range=n' modifier does not turn the undefined
+# expression into a defined one, even though it does not depend on the value
+# of the expression. This looks like an oversight.
+# expect+1: Malformed conditional (${:range=5} != "")
+.if ${:range=5} != ""
+. error
+.else
+. error
+.endif
# Negative ranges don't make sense.
# As of 2020-11-01, they are accepted though, using up all available memory.
diff --git a/unit-tests/varmod-subst-regex.mk b/unit-tests/varmod-subst-regex.mk
index 197691d73aad..c1ffc4580d3a 100644
--- a/unit-tests/varmod-subst-regex.mk
+++ b/unit-tests/varmod-subst-regex.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-subst-regex.mk,v 1.7 2021/06/21 08:17:39 rillig Exp $
+# $NetBSD: varmod-subst-regex.mk,v 1.11 2023/12/18 11:13:51 rillig Exp $
#
# Tests for the :C,from,to, variable modifier.
@@ -10,7 +10,7 @@ all: mod-regex-limits
all: mod-regex-errors
all: unmatched-subexpression
-# The variable expression expands to 4 words. Of these words, none matches
+# The expression expands to 4 words. Of these words, none matches
# the regular expression "a b" since these words don't contain any
# whitespace.
.if ${:Ua b b c:C,a b,,} != "a b b c"
@@ -18,7 +18,7 @@ all: unmatched-subexpression
.endif
# Using the '1' modifier does not change anything. The '1' modifier just
-# means to apply at most 1 replacement in the whole variable expression.
+# means to apply at most 1 replacement in the whole expression.
.if ${:Ua b b c:C,a b,,1} != "a b b c"
. error
.endif
@@ -84,9 +84,54 @@ all: unmatched-subexpression
. error
.endif
+
+# Like the ':S' modifier, the ':C' modifier matches on an expression
+# that contains no words at all, but only if the regular expression matches an
+# empty string, for example, when the regular expression is anchored at the
+# beginning or the end of the word. An unanchored regular expression that
+# matches the empty string is uncommon in practice, as it would match before
+# each character of the word.
+.if "<${:U:S,,unanchored,}> <${:U:C,.?,unanchored,}>" != "<> <unanchored>"
+. error
+.endif
+.if "<${:U:S,^,prefix,}> <${:U:C,^,prefix,}>" != "<prefix> <prefix>"
+. error
+.endif
+.if "<${:U:S,$,suffix,}> <${:U:C,$,suffix,}>" != "<suffix> <suffix>"
+. error
+.endif
+.if "<${:U:S,^$,whole,}> <${:U:C,^$,whole,}>" != "<whole> <whole>"
+. error
+.endif
+.if "<${:U:S,,unanchored,g}> <${:U:C,.?,unanchored,g}>" != "<> <unanchored>"
+. error
+.endif
+.if "<${:U:S,^,prefix,g}> <${:U:C,^,prefix,g}>" != "<prefix> <prefix>"
+. error
+.endif
+.if "<${:U:S,$,suffix,g}> <${:U:C,$,suffix,g}>" != "<suffix> <suffix>"
+. error
+.endif
+.if "<${:U:S,^$,whole,g}> <${:U:C,^$,whole,g}>" != "<whole> <whole>"
+. error
+.endif
+.if "<${:U:S,,unanchored,W}> <${:U:C,.?,unanchored,W}>" != "<> <unanchored>"
+. error
+.endif
+.if "<${:U:S,^,prefix,W}> <${:U:C,^,prefix,W}>" != "<prefix> <prefix>"
+. error
+.endif
+.if "<${:U:S,$,suffix,W}> <${:U:C,$,suffix,W}>" != "<suffix> <suffix>"
+. error
+.endif
+.if "<${:U:S,^$,whole,W}> <${:U:C,^$,whole,W}>" != "<whole> <whole>"
+. error
+.endif
+
+
# Multiple asterisks form an invalid regular expression. This produces an
# error message and (as of 2020-08-28) stops parsing in the middle of the
-# variable expression. The unparsed part of the expression is then copied
+# expression. The unparsed part of the expression is then copied
# verbatim to the output, which is unexpected and can lead to strange shell
# commands being run.
mod-regex-compile-error:
@@ -101,7 +146,7 @@ mod-regex-limits:
@echo $@:22-missing:${:U1 23 456:C,(.).,\2\2,:Q}
@echo $@:22-ok:${:U1 23 456:C,(.)(.),\2\2,:Q}
# The :C modifier only handles single-digit capturing groups,
- # which is more than enough for daily use.
+ # which is enough for all practical use cases.
@echo $@:capture:${:UabcdefghijABCDEFGHIJrest:C,(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.)(.),\9\8\7\6\5\4\3\2\1\0\10\11\12,}
mod-regex-errors:
@@ -109,7 +154,7 @@ mod-regex-errors:
# If the replacement pattern produces a parse error because of an
# unknown modifier, the parse error is ignored in ParseModifierPart
- # and the faulty variable expression expands to "".
+ # and the faulty expression expands to "".
@echo $@: ${word:L:C,.*,x${:U:Z}y,W}
# In regular expressions with alternatives, not all capturing groups are
diff --git a/unit-tests/varmod-subst.mk b/unit-tests/varmod-subst.mk
index 0ddf2cfb2019..2903d36445f8 100644
--- a/unit-tests/varmod-subst.mk
+++ b/unit-tests/varmod-subst.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-subst.mk,v 1.12 2023/06/16 07:20:45 rillig Exp $
+# $NetBSD: varmod-subst.mk,v 1.14 2023/12/18 11:13:51 rillig Exp $
#
# Tests for the :S,from,to, variable modifier.
@@ -9,7 +9,8 @@ all: mod-subst-dollar
WORDS= sequences of letters
-# The empty pattern never matches anything.
+# The empty pattern never matches anything, except if it is anchored at the
+# beginning or the end of the word.
.if ${WORDS:S,,,} != ${WORDS}
. error
.endif
@@ -141,6 +142,47 @@ WORDS= sequences of letters
.endif
+# In an empty expression, the ':S' modifier matches a single time, but only if
+# the search string is empty and anchored at either the beginning or the end
+# of the word.
+.if ${:U:S,,out-of-nothing,} != ""
+. error
+.endif
+.if ${:U:S,^,out-of-nothing,} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,$,out-of-nothing,} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,^$,out-of-nothing,} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,,out-of-nothing,g} != ""
+. error
+.endif
+.if ${:U:S,^,out-of-nothing,g} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,$,out-of-nothing,g} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,^$,out-of-nothing,g} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,,out-of-nothing,W} != ""
+. error
+.endif
+.if ${:U:S,^,out-of-nothing,W} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,$,out-of-nothing,W} != "out-of-nothing"
+. error
+.endif
+.if ${:U:S,^$,out-of-nothing,W} != "out-of-nothing"
+. error
+.endif
+
+
mod-subst:
@echo $@:
@echo :${:Ua b b c:S,a b,,:Q}:
diff --git a/unit-tests/varmod-sysv.mk b/unit-tests/varmod-sysv.mk
index 78651ea869dc..0f92e1df7032 100644
--- a/unit-tests/varmod-sysv.mk
+++ b/unit-tests/varmod-sysv.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-sysv.mk,v 1.15 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varmod-sysv.mk,v 1.16 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the variable modifier ':from=to', which replaces the suffix
# "from" with "to". It can also use '%' as a wildcard.
@@ -49,7 +49,7 @@
. error
.endif
-# In the modifier ':from=to', both parts can contain variable expressions.
+# In the modifier ':from=to', both parts can contain expressions.
.if ${one two:L:${:Uone}=${:U1}} != "1 two"
. error
.endif
@@ -69,7 +69,7 @@
.endif
# The replacement string can contain spaces, thereby changing the number
-# of words in the variable expression.
+# of words in the expression.
.if ${In:L:%=% ${:Uthe Sun}} != "In the Sun"
. error
.endif
@@ -206,7 +206,7 @@
. error
.endif
-# This is not a SysV modifier since the nested variable expression expands
+# This is not a SysV modifier since the nested expression expands
# to an empty string. The '=' in it should be irrelevant during parsing.
# XXX: As of 2020-12-05, this expression generates an "Unfinished modifier"
# error, while the correct error message would be "Unknown modifier" since
@@ -221,7 +221,7 @@
# "fromto}...". The next modifier is a SysV modifier. ApplyModifier_SysV
# parses the modifier as "from${:D=}to", ending at the '}'. Next, the two
# parts of the modifier are parsed using ParseModifierPart, which scans
-# differently, properly handling nested variable expressions. The two parts
+# differently, properly handling nested expressions. The two parts
# are now "fromto}..." and "replaced".
.if "${:Ufromto\}...:from${:D=}to}...=replaced}" != "replaced"
. error
diff --git a/unit-tests/varmod-to-separator.mk b/unit-tests/varmod-to-separator.mk
index f17dece447a9..57a7bd84ec44 100644
--- a/unit-tests/varmod-to-separator.mk
+++ b/unit-tests/varmod-to-separator.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-to-separator.mk,v 1.12 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varmod-to-separator.mk,v 1.13 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :ts variable modifier, which joins the words of the variable
# using an arbitrary character as word separator.
@@ -39,7 +39,7 @@ WORDS= one two three four five six
# quote though, or other special characters like dollar or backslash.
#
# This example also demonstrates that the closing brace is not interpreted
-# as a separator, but as the closing delimiter of the whole variable
+# as a separator, but as the closing delimiter of the whole
# expression.
.if ${WORDS:tu:ts} != "ONETWOTHREEFOURFIVESIX"
. warning Colon as separator does not work.
diff --git a/unit-tests/varmod-undefined.mk b/unit-tests/varmod-undefined.mk
index 9fd41c7fdb60..fd56ffd35e30 100644
--- a/unit-tests/varmod-undefined.mk
+++ b/unit-tests/varmod-undefined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-undefined.mk,v 1.8 2022/08/06 21:26:05 rillig Exp $
+# $NetBSD: varmod-undefined.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the :U variable modifier, which returns the given string
# if the variable is undefined.
@@ -19,14 +19,14 @@
.endif
# .endfor
-# The variable expressions in the text of the :U modifier may be arbitrarily
+# The expressions in the text of the :U modifier may be arbitrarily
# nested.
.if ${:U${:Unested}${${${:Udeeply}}}} != nested
. error
.endif
-# The nested variable expressions may contain braces, and these braces don't
+# The nested expressions may contain braces, and these braces don't
# need to match pairwise. In the following example, the :S modifier uses '{'
# as delimiter, which confuses both editors and humans because the opening
# and closing braces don't match anymore. It's syntactically valid though.
diff --git a/unit-tests/varmod.mk b/unit-tests/varmod.mk
index 2ed2f6ee7cea..8e4c43bdfec7 100644
--- a/unit-tests/varmod.mk
+++ b/unit-tests/varmod.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod.mk,v 1.8 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varmod.mk,v 1.9 2023/11/19 21:47:52 rillig Exp $
#
# Tests for variable modifiers, such as :Q, :S,from,to or :Ufallback.
#
@@ -59,7 +59,7 @@
DOLLAR1= $$
DOLLAR2= ${:U\$}
-# To get a single '$' sign in the value of a variable expression, it has to
+# To get a single '$' sign in the value of an expression, it has to
# be written as '$$' in a literal variable value.
#
# See Var_Parse, where it calls Var_Subst.
@@ -107,7 +107,7 @@ DOLLAR2= ${:U\$}
# The variable modifier :P does not fall back to the SysV modifier.
# Therefore the modifier :P=RE generates a parse error.
-# XXX: The .error should not be reached since the variable expression is
+# XXX: The .error should not be reached since the expression is
# malformed, and this error should be propagated up to Cond_EvalLine.
VAR= STOP
# expect+1: Missing delimiter ':' after modifier "P"
diff --git a/unit-tests/varname-dot-shell.exp b/unit-tests/varname-dot-shell.exp
index 800e8a375761..22c2afe218a9 100755
--- a/unit-tests/varname-dot-shell.exp
+++ b/unit-tests/varname-dot-shell.exp
@@ -1,26 +1,26 @@
Parsing line 10: ORIG_SHELL:= ${.SHELL}
Global: ORIG_SHELL = # (empty)
Var_Parse: ${.SHELL} (eval-keep-dollar-and-undefined)
-Global: delete .SHELL (not found)
+Global: ignoring delete '.SHELL' as it is not found
Command: .SHELL = (details omitted)
Global: ORIG_SHELL = (details omitted)
Parsing line 12: .SHELL= overwritten
-Global: .SHELL = overwritten
+Global: ignoring '.SHELL = overwritten' due to a command line variable of the same name
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
Var_Parse: ${ORIG_SHELL} (eval-defined)
Comparing "(details omitted)" != "(details omitted)"
Parsing line 19: .MAKEFLAGS: .SHELL+=appended
ParseDependency(.MAKEFLAGS: .SHELL+=appended)
-Ignoring append to .SHELL since it is read-only
+Command: ignoring '.SHELL += appended' as it is read-only
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
Var_Parse: ${ORIG_SHELL} (eval-defined)
Comparing "(details omitted)" != "(details omitted)"
Parsing line 27: .undef .SHELL
-Global: delete .SHELL
+Global: ignoring delete '.SHELL' as it is not found
Parsing line 28: .SHELL= newly overwritten
-Global: .SHELL = newly overwritten
+Global: ignoring '.SHELL = newly overwritten' due to a command line variable of the same name
CondParser_Eval: ${.SHELL} != ${ORIG_SHELL}
Var_Parse: ${.SHELL} != ${ORIG_SHELL} (eval-defined)
Var_Parse: ${ORIG_SHELL} (eval-defined)
diff --git a/unit-tests/varname-dot-suffixes.exp b/unit-tests/varname-dot-suffixes.exp
index 186b5f06c227..230ba36d56ed 100644
--- a/unit-tests/varname-dot-suffixes.exp
+++ b/unit-tests/varname-dot-suffixes.exp
@@ -1,20 +1,20 @@
-Global: delete .SUFFIXES (not found)
+Global: ignoring delete '.SUFFIXES' as it is not found
Global: .MAKEFLAGS = -r -k -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0
-Global: .SUFFIXES = set ignored (read-only)
-Global: .SUFFIXES = append ignored (read-only)
+Global: ignoring '.SUFFIXES = set' as it is read-only
+Global: ignoring '.SUFFIXES = append' as it is read-only
Global: _ = # (empty)
Var_Parse: ${.SUFFIXES::=assign} (eval-keep-dollar-and-undefined)
Evaluating modifier ${.SUFFIXES::...} on value ".c .o .1 .err .tar.gz" (eval-keep-dollar-and-undefined, regular)
Modifier part: "assign"
-Global: .SUFFIXES = assign ignored (read-only)
+Global: ignoring '.SUFFIXES = assign' as it is read-only
Result of ${.SUFFIXES::=assign} is "" (eval-keep-dollar-and-undefined, regular)
Global: _ = # (empty)
Var_Parse: ${preserve:L:_=.SUFFIXES} (eval-keep-dollar-and-undefined)
Evaluating modifier ${preserve:L} on value "" (eval-keep-dollar-and-undefined, undefined)
Result of ${preserve:L} is "preserve" (eval-keep-dollar-and-undefined, defined)
Evaluating modifier ${preserve:_...} on value "preserve" (eval-keep-dollar-and-undefined, defined)
-Global: .SUFFIXES = preserve ignored (read-only)
+Global: ignoring '.SUFFIXES = preserve' as it is read-only
Result of ${preserve:_=.SUFFIXES} is "preserve" (eval-keep-dollar-and-undefined, defined)
Global: _ = preserve
Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d
@@ -26,13 +26,13 @@ Evaluating modifier ${1 2:@...} on value "1 2" (eval-defined, defined)
Modifier part: ".SUFFIXES"
Modifier part: "${.SUFFIXES}"
ModifyWords: split "1 2" into 2 words
-Command: .SUFFIXES = 1 ignored (read-only)
+Command: ignoring '.SUFFIXES = 1' as it is read-only
Var_Parse: ${.SUFFIXES} (eval-defined)
-ModifyWord_Loop: in "1", replace ".SUFFIXES" with "${.SUFFIXES}" to ".c .o .1 .err .tar.gz"
-Command: .SUFFIXES = 2 ignored (read-only)
+ModifyWord_Loop: expand "${.SUFFIXES}" to ".c .o .1 .err .tar.gz"
+Command: ignoring '.SUFFIXES = 2' as it is read-only
Var_Parse: ${.SUFFIXES} (eval-defined)
-ModifyWord_Loop: in "2", replace ".SUFFIXES" with "${.SUFFIXES}" to ".c .o .1 .err .tar.gz"
-Command: delete .SUFFIXES (not found)
+ModifyWord_Loop: expand "${.SUFFIXES}" to ".c .o .1 .err .tar.gz"
+Command: ignoring delete '.SUFFIXES' as it is not found
Result of ${1 2:@.SUFFIXES@${.SUFFIXES}@} is ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" (eval-defined, defined)
Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 -d v -d
Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0 -d v -d 0
diff --git a/unit-tests/varname-dot-suffixes.mk b/unit-tests/varname-dot-suffixes.mk
index f9f995fcd845..27521f621cb0 100644
--- a/unit-tests/varname-dot-suffixes.mk
+++ b/unit-tests/varname-dot-suffixes.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname-dot-suffixes.mk,v 1.3 2022/04/15 09:33:20 rillig Exp $
+# $NetBSD: varname-dot-suffixes.mk,v 1.5 2023/12/20 09:03:09 rillig Exp $
#
# Tests for the special "variable" .SUFFIXES, which lists the suffixes that
# have been registered for use in suffix transformation rules. Suffixes are
@@ -51,7 +51,7 @@
# Deleting .SUFFIXES has no effect since there is no actual variable of that
# name.
.MAKEFLAGS: -dv
-# expect: Global: delete .SUFFIXES (not found)
+# expect: Global: ignoring delete '.SUFFIXES' as it is not found
.undef .SUFFIXES
.MAKEFLAGS: -d0
.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz"
@@ -61,13 +61,13 @@
# The list of suffixes can only be modified using dependency declarations, any
# attempt at setting the variable named '.SUFFIXES' is rejected.
.MAKEFLAGS: -dv
-# expect: Global: .SUFFIXES = set ignored (read-only)
+# expect: Global: ignoring '.SUFFIXES = set' as it is read-only
.SUFFIXES= set
-# expect: Global: .SUFFIXES = append ignored (read-only)
+# expect: Global: ignoring '.SUFFIXES = append' as it is read-only
.SUFFIXES+= append
-# expect: Global: .SUFFIXES = assign ignored (read-only)
+# expect: Global: ignoring '.SUFFIXES = assign' as it is read-only
_:= ${.SUFFIXES::=assign}
-# expect: Global: .SUFFIXES = preserve ignored (read-only)
+# expect: Global: ignoring '.SUFFIXES = preserve' as it is read-only
_:= ${preserve:L:_=.SUFFIXES}
.MAKEFLAGS: -d0
@@ -94,10 +94,9 @@ _:= ${preserve:L:_=.SUFFIXES}
# the name would be '.SUFFIXES.', furthermore the name of the iteration
# variable is typically in singular form.
.MAKEFLAGS: -dv
-# expect: Command: .SUFFIXES = 1 ignored (read-only)
-# expect: Command: .SUFFIXES = 2 ignored (read-only)
-# XXX: Missing space after ':'
-# expect: Command: delete .SUFFIXES (not found)
+# expect: Command: ignoring '.SUFFIXES = 1' as it is read-only
+# expect: Command: ignoring '.SUFFIXES = 2' as it is read-only
+# expect: Command: ignoring delete '.SUFFIXES' as it is not found
.if ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz"
. error
.endif
diff --git a/unit-tests/varname-empty.exp b/unit-tests/varname-empty.exp
index a861ba378cef..2165784933e4 100644
--- a/unit-tests/varname-empty.exp
+++ b/unit-tests/varname-empty.exp
@@ -1,22 +1,22 @@
-Var_SetExpand: variable name "${:U}" expands to empty string, with value "cmdline-u" - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "cmdline-plain" - ignored
+Command: ignoring ' = cmdline-u' as the variable name '${:U}' expands to empty
+Command: ignoring ' = cmdline-plain' as the variable name '' expands to empty
Global: .CURDIR = <curdir>
Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE} (eval)
Global: .TARGETS = # (empty)
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_SetExpand: variable name "" expands to empty string, with value "default" - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "assigned" - ignored
-SetVar: variable name is empty - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "" - ignored
-Var_SetExpand: variable name "" expands to empty string, with value "subst" - ignored
+Global: ignoring delete '.INCLUDEDFROMDIR' as it is not found
+Global: ignoring delete '.INCLUDEDFROMFILE' as it is not found
+Global: ignoring ' = default' as the variable name '' expands to empty
+Global: ignoring ' = assigned' as the variable name '' expands to empty
+Global: ignoring ' = appended' as the variable name is empty
+Global: ignoring ' = ' as the variable name '' expands to empty
+Global: ignoring ' = subst' as the variable name '' expands to empty
Capturing the output of command "echo 'shell-output'"
-Var_SetExpand: variable name "" expands to empty string, with value "shell-output" - ignored
-Var_SetExpand: variable name "${:U}" expands to empty string, with value "assigned indirectly" - ignored
-Var_AppendExpand: variable name "${:U}" expands to empty string, with value "appended indirectly" - ignored
+Global: ignoring ' = shell-output' as the variable name '' expands to empty
+Global: ignoring ' = assigned indirectly' as the variable name '${:U}' expands to empty
+Global: ignoring ' += appended indirectly' as the variable name '${:U}' expands to empty
Global: .MAKEFLAGS = -r -d v -d
Global: .MAKEFLAGS = -r -d v -d 0
out: fallback
diff --git a/unit-tests/varname-empty.mk b/unit-tests/varname-empty.mk
index f077d2ec07b4..e018a5d44894 100755
--- a/unit-tests/varname-empty.mk
+++ b/unit-tests/varname-empty.mk
@@ -1,9 +1,9 @@
-# $NetBSD: varname-empty.mk,v 1.9 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: varname-empty.mk,v 1.10 2023/11/19 21:47:52 rillig Exp $
#
# Tests for the special variable with the empty name.
#
# There is no variable named "" at all, and this fact is used a lot in
-# variable expressions of the form ${:Ufallback}. These expressions are
+# expressions of the form ${:Ufallback}. These expressions are
# based on the variable named "" and use the :U modifier to assign a
# fallback value to the expression (but not to the variable).
#
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 10a9647fbd1e..b422f25ff12e 100644
--- a/unit-tests/varname-make_print_var_on_error-jobs.mk
+++ b/unit-tests/varname-make_print_var_on_error-jobs.mk
@@ -1,9 +1,9 @@
-# $NetBSD: varname-make_print_var_on_error-jobs.mk,v 1.3 2021/02/04 21:33:14 rillig Exp $
+# $NetBSD: varname-make_print_var_on_error-jobs.mk,v 1.4 2023/11/19 22:32:44 rillig Exp $
#
# Tests for the special MAKE_PRINT_VAR_ON_ERROR variable, which prints the
# values of selected variables on error.
#
-# The variable .ERROR_CMD contains all commands of the target, with variable
+# The variable .ERROR_CMD contains all commands of the target, with
# expressions expanded, just as they were printed to the shell command file.
#
# The commands in .ERROR_CMD are space-separated. Since each command usually
diff --git a/unit-tests/varname.mk b/unit-tests/varname.mk
index 72db79078741..cad0a10fe563 100644
--- a/unit-tests/varname.mk
+++ b/unit-tests/varname.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varname.mk,v 1.13 2023/08/19 11:09:02 rillig Exp $
+# $NetBSD: varname.mk,v 1.14 2023/11/19 21:47:52 rillig Exp $
#
# Tests for special variables, such as .MAKE or .PARSEDIR.
# And for variable names in general.
@@ -12,7 +12,7 @@ VAR{{{}}}= 3 braces
. error
.endif
-# In variable expressions, the parser works differently. It doesn't treat
+# In expressions, the parser works differently. It doesn't treat
# braces and parentheses equally, therefore the first closing brace already
# marks the end of the variable name.
VARNAME= VAR(((
diff --git a/unit-tests/varparse-dynamic.mk b/unit-tests/varparse-dynamic.mk
index 88e4e6a4917e..40f43b049b13 100644
--- a/unit-tests/varparse-dynamic.mk
+++ b/unit-tests/varparse-dynamic.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varparse-dynamic.mk,v 1.7 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varparse-dynamic.mk,v 1.8 2023/11/19 22:32:44 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.
@@ -24,7 +24,7 @@
.endif
# 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
+# based on this variable is not expanded. But there may be nested
# expressions in the modifiers, and these are kept unexpanded as well.
.if ${.TARGET:M${:Ufallback}} != "\${.TARGET:M\${:Ufallback}}"
. error
diff --git a/unit-tests/varparse-errors.exp b/unit-tests/varparse-errors.exp
index 41db6d8c6f43..4193bea181c9 100644
--- a/unit-tests/varparse-errors.exp
+++ b/unit-tests/varparse-errors.exp
@@ -6,20 +6,20 @@ make: Bad modifier ":OX" for variable ""
make: Bad modifier ":OX" for variable ""
make: "varparse-errors.mk" line 71: Undefined variable "${:U:OX"
make: Bad modifier ":OX" for variable ""
-make: Unclosed variable expression, expecting '}' for modifier "Q" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "sh" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "tA" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "tsX" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "ts" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "ts\040" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "u" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "H" of variable "" with value "."
-make: Unclosed variable expression, expecting '}' for modifier "[1]" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "hash" of variable "" with value "b2af338b"
-make: Unclosed variable expression, expecting '}' for modifier "range" of variable "" with value "1"
-make: Unclosed variable expression, expecting '}' for modifier "_" of variable "" with value ""
-make: Unclosed variable expression, expecting '}' for modifier "gmtime" of variable "" with value "<timestamp>"
-make: Unclosed variable expression, expecting '}' for modifier "localtime" of variable "" with value "<timestamp>"
+make: Unclosed expression, expecting '}' for modifier "Q" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "sh" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "tA" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "tsX" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "ts" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "ts\040" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "u" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "H" of variable "" with value "."
+make: Unclosed expression, expecting '}' for modifier "[1]" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "hash" of variable "" with value "b2af338b"
+make: Unclosed expression, expecting '}' for modifier "range" of variable "" with value "1"
+make: Unclosed expression, expecting '}' for modifier "_" of variable "" with value ""
+make: Unclosed expression, expecting '}' for modifier "gmtime" of variable "" with value "<timestamp>"
+make: Unclosed expression, expecting '}' for modifier "localtime" of variable "" with value "<timestamp>"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/unit-tests/varparse-errors.mk b/unit-tests/varparse-errors.mk
index 3ae6d2db1de9..fc94d9dd3d18 100644
--- a/unit-tests/varparse-errors.mk
+++ b/unit-tests/varparse-errors.mk
@@ -1,6 +1,6 @@
-# $NetBSD: varparse-errors.mk,v 1.9 2023/06/01 20:56:35 rillig Exp $
+# $NetBSD: varparse-errors.mk,v 1.11 2023/11/19 22:32:44 rillig Exp $
-# Tests for parsing and evaluating all kinds of variable expressions.
+# Tests for parsing and evaluating all kinds of expressions.
#
# This is the basis for redesigning the error handling in Var_Parse and
# Var_Subst, collecting typical and not so typical use cases.
@@ -17,13 +17,13 @@ INDIRECT= An ${:Uindirect} value.
REF_UNDEF= A reference to an ${UNDEF}undefined variable.
-ERR_UNCLOSED= An ${UNCLOSED variable expression.
+ERR_UNCLOSED= An ${UNCLOSED expression.
ERR_BAD_MOD= An ${:Uindirect:Z} expression with an unknown modifier.
ERR_EVAL= An evaluation error ${:Uvalue:C,.,\3,}.
-# In a conditional, a variable expression that is not enclosed in quotes is
+# In a conditional, an expression that is not enclosed in quotes is
# expanded using the mode VARE_UNDEFERR.
# The variable itself must be defined.
# It may refer to undefined variables though.
diff --git a/unit-tests/varparse-mod.mk b/unit-tests/varparse-mod.mk
index 0b4cbf6ca40a..c5fa6f5ece71 100644
--- a/unit-tests/varparse-mod.mk
+++ b/unit-tests/varparse-mod.mk
@@ -1,6 +1,6 @@
-# $NetBSD: varparse-mod.mk,v 1.1 2020/10/02 20:34:59 rillig Exp $
+# $NetBSD: varparse-mod.mk,v 1.2 2023/11/19 21:47:52 rillig Exp $
-# Tests for parsing variable expressions with modifiers.
+# Tests for parsing expressions with modifiers.
# As of 2020-10-02, the below condition does not result in a parse error.
# The condition contains two separate mistakes. The first mistake is that
@@ -8,7 +8,7 @@
# there is a stray '}' at the end of the whole condition.
#
# As of 2020-10-02, the actual parse result of this condition is a single
-# variable expression with 2 modifiers. The first modifier is
+# expression with 2 modifiers. The first modifier is
# ":!echo "\$VAR"} !". Afterwards, the parser optionally skips a ':' (at the
# bottom of ApplyModifiers) and continues with the next modifier, in this case
# "= "value"", which is interpreted as a SysV substitution modifier with an
diff --git a/unit-tests/varparse-undef-partial.mk b/unit-tests/varparse-undef-partial.mk
index 27f44d79b31a..9a5704265086 100644
--- a/unit-tests/varparse-undef-partial.mk
+++ b/unit-tests/varparse-undef-partial.mk
@@ -1,7 +1,7 @@
-# $NetBSD: varparse-undef-partial.mk,v 1.3 2020/11/04 05:10:01 rillig Exp $
+# $NetBSD: varparse-undef-partial.mk,v 1.5 2024/01/07 11:39:04 rillig Exp $
# When an undefined variable is expanded in a ':=' assignment, only the
-# initial '$' of the variable expression is skipped by the parser, while
+# initial '$' of the expression is skipped by the parser, while
# the remaining expression is evaluated. In edge cases this can lead to
# a completely different interpretation of the partially expanded text.
@@ -11,11 +11,10 @@ PARAM= :Q
# The expression ${VAR.${PARAM}} refers to the variable named "VAR.:Q",
# with the ":Q" being part of the name. This variable is not defined,
-# therefore the initial '$' of that whole expression is skipped by the
-# parser (see Var_Subst, the Buf_AddByte in the else branch) and the rest
-# of the expression is expanded as usual.
+# therefore the initial '$' of that whole expression is skipped by the parser
+# (see VarSubstExpr) and the rest of the expression is expanded as usual.
#
-# The resulting variable expression is ${VAR.:Q}, which means that the
+# The resulting expression is ${VAR.:Q}, which means that the
# interpretation of the ":Q" has changed from being part of the variable
# name to being a variable modifier. This is a classical code injection.
EVAL:= ${LIST}
@@ -37,7 +36,7 @@ ${:UVAR.\:Q}= var-dot with parameter :Q
# In contrast to the previous line, evaluating the original LIST again now
# produces a different result since the variable named "VAR.:Q" is now
# defined. It is expanded as usual, interpreting the ":Q" as part of the
-# variable name, as would be expected from reading the variable expression.
+# variable name, as would be expected from reading the expression.
EVAL:= ${LIST}
.if ${EVAL} != "defined var-dot with parameter :Q end"
. error ${EVAL}
diff --git a/util.c b/util.c
index eeda3d8f8a0c..f660c21a228a 100644
--- a/util.c
+++ b/util.c
@@ -3,7 +3,7 @@
/*
* Missing stuff from OS's
*
- * $Id: util.c,v 1.50 2021/12/21 18:47:24 sjg Exp $
+ * $Id: util.c,v 1.52 2024/01/04 00:27:30 sjg Exp $
*/
#include <sys/param.h>
@@ -431,18 +431,28 @@ snprintf(char *s, size_t n, const char *fmt, ...)
}
#endif
-#if !defined(HAVE_STRFTIME)
+#if !defined(HAVE_STRFTIME) || defined(FORCE_BMAKE_STRFTIME)
+/* we only implement enough to pass our unit-tests */
size_t
strftime(char *buf, size_t len, const char *fmt, const struct tm *tm)
{
- static char months[][4] = {
- "Jan", "Feb", "Mar", "Apr", "May", "Jun",
- "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ static const char *months[] = {
+ "January", "February", "March",
+ "April", "May", "June",
+ "July", "August", "September",
+ "October", "November", "December"
};
-
+ static const char *days[] = {
+ "Sunday", "Monday", "Tuesday", "Wednesday",
+ "Thursday", "Friday", "Saturday"
+ };
+ int i;
size_t s;
char *b = buf;
+ char *cp;
+ if (fmt == NULL || *fmt == '\0')
+ fmt = "%c";
while (*fmt) {
if (len == 0)
return buf - b;
@@ -451,6 +461,7 @@ strftime(char *buf, size_t len, const char *fmt, const struct tm *tm)
len--;
continue;
}
+ fmt++;
switch (*fmt++) {
case '%':
*buf++ = '%';
@@ -461,26 +472,87 @@ strftime(char *buf, size_t len, const char *fmt, const struct tm *tm)
*buf = '%';
s = 1;
break;
+ case 'A':
+ s = snprintf(buf, len, "%s", days[tm->tm_wday]);
+ break;
+ case 'a':
+ s = snprintf(buf, len, "%.3s", days[tm->tm_wday]);
+ break;
+ case 'B':
+ if (tm->tm_mon >= 12)
+ return buf - b;
+ s = snprintf(buf, len, "%s", months[tm->tm_mon]);
+ break;
+ case 'b':
+ if (tm->tm_mon >= 12)
+ return buf - b;
+ s = snprintf(buf, len, "%.3s", months[tm->tm_mon]);
+ break;
+ case 'c':
+ s = strftime(buf, len, "%a %b %e %H:%M:%S %Y", tm);
+ break;
+ case 'd':
+ s = snprintf(buf, len, "%02d", tm->tm_mday);
+ break;
+ case 'e':
+ s = snprintf(buf, len, "%2d", tm->tm_mday);
+ break;
+ case 'F':
+ s = strftime(buf, len, "%y-%m-%d", tm);
+ break;
+ case 'H':
+ s = snprintf(buf, len, "%02d", tm->tm_hour);
+ break;
+ case 'I':
+ if ((i = tm->tm_hour) == 0)
+ i = 24;
+ s = snprintf(buf, len, "%02d", (i > 12) ? (i - 12) : i);
+ break;
+ case 'j':
+ s = snprintf(buf, len, "%03d", tm->tm_yday + 1);
+ break;
case 'k':
s = snprintf(buf, len, "%d", tm->tm_hour);
break;
case 'M':
s = snprintf(buf, len, "%02d", tm->tm_min);
break;
+ case 'm':
+ s = snprintf(buf, len, "%02d", 1 + tm->tm_mon);
+ break;
case 'S':
s = snprintf(buf, len, "%02d", tm->tm_sec);
break;
- case 'b':
- if (tm->tm_mon >= 12)
- return buf - b;
- s = snprintf(buf, len, "%s", months[tm->tm_mon]);
+ case 's':
+ s = snprintf(buf, len, "%ld", (long)time(NULL));
break;
- case 'd':
- s = snprintf(buf, len, "%02d", tm->tm_mday);
+ case 'T':
+ s = strftime(buf, len, "%H:%M:%S", tm);
+ break;
+ case 'w':
+ s = snprintf(buf, len, "%02d", tm->tm_wday);
break;
case 'Y':
s = snprintf(buf, len, "%d", 1900 + tm->tm_year);
break;
+ case 'y':
+ s = snprintf(buf, len, "%02d", tm->tm_year % 100);
+ break;
+ case 'Z':
+ if ((cp = getenv("TZ")) != NULL) {
+ char tz[20];
+
+ i = snprintf(tz, sizeof(tz), "%s", cp);
+ if (i > 5) {
+ cp = &tz[i - 3];
+ tz[3] = '\0';
+ } else
+ cp = tz;
+ s = snprintf(buf, len, "%s",
+ tm->tm_isdst ? cp : tz);
+ } else
+ s = 0;
+ break;
default:
s = snprintf(buf, len, "Unsupported format %c",
fmt[-1]);
diff --git a/var.c b/var.c
index 54bea0179b72..38f0d9b6913d 100644
--- a/var.c
+++ b/var.c
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.1064 2023/08/19 19:59:17 rillig Exp $ */
+/* $NetBSD: var.c,v 1.1094 2024/01/07 11:39:04 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -98,9 +98,9 @@
* Var_Value Return the unexpanded value of a variable, or NULL if
* the variable is undefined.
*
- * Var_Subst Substitute all variable expressions in a string.
+ * Var_Subst Substitute all expressions in a string.
*
- * Var_Parse Parse a variable expression such as ${VAR:Mpattern}.
+ * Var_Parse Parse an expression such as ${VAR:Mpattern}.
*
* Var_Delete
* Delete a variable.
@@ -147,7 +147,7 @@
#include "metachar.h"
/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.1064 2023/08/19 19:59:17 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.1094 2024/01/07 11:39:04 rillig Exp $");
/*
* Variables are defined using one of the VAR=value assignments. Their
@@ -166,7 +166,7 @@ MAKE_RCSID("$NetBSD: var.c,v 1.1064 2023/08/19 19:59:17 rillig Exp $");
* Environment variables are short-lived. They are returned by VarFind, and
* after using them, they must be freed using VarFreeShortLived.
*
- * Undefined variables occur during evaluation of variable expressions such
+ * Undefined variables occur during evaluation of expressions such
* as ${UNDEF:Ufallback} in Var_Parse and ApplyModifiers.
*/
typedef struct Var {
@@ -221,8 +221,8 @@ typedef struct Var {
/*
* At the point where this variable was exported, it contained an
* unresolved reference to another variable. Before any child
- * process is started, it needs to be exported again, in the hope
- * that the referenced variable can then be resolved.
+ * process is started, it needs to be actually exported, resolving
+ * the referenced variable just in time.
*/
bool reexport:1;
} Var;
@@ -230,9 +230,6 @@ typedef struct Var {
/*
* Exporting variables is expensive and may leak memory, so skip it if we
* can.
- *
- * To avoid this, it might be worth encapsulating the environment variables
- * in a separate data structure called EnvVars.
*/
typedef enum VarExportedMode {
VAR_EXPORTED_NONE,
@@ -272,10 +269,7 @@ typedef struct SepBuf {
} SepBuf;
-/*
- * This lets us tell if we have replaced the original environ
- * (which we cannot free).
- */
+/* Whether we have replaced the original environ (which we cannot free). */
char **savedEnv = NULL;
/*
@@ -319,9 +313,7 @@ static bool save_dollars = false;
* 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.
+ * whenever they are referenced.
*
* Each target has its own scope, containing the 7 target-local variables
* .TARGET, .ALLSRC, etc. Variables set on dependency lines also go in
@@ -386,11 +378,11 @@ CanonicalVarname(Substring name)
if (Substring_Equals(name, ".TARGET"))
return Substring_InitStr(TARGET);
+ /* GNU make has an additional alias $^ == ${.ALLSRC}. */
+
if (Substring_Equals(name, ".SHELL") && shellPath == NULL)
Shell_Init();
- /* GNU make has an additional alias $^ == ${.ALLSRC}. */
-
return name;
}
@@ -514,15 +506,14 @@ Var_Delete(GNode *scope, const char *varname)
Var *v;
if (he == NULL) {
- DEBUG2(VAR, "%s: delete %s (not found)\n",
+ DEBUG2(VAR, "%s: ignoring delete '%s' as it is not found\n",
scope->name, varname);
return;
}
- DEBUG2(VAR, "%s: delete %s\n", scope->name, varname);
v = he->value;
if (v->readOnly) {
- DEBUG2(VAR, "%s: delete %s (readOnly)\n",
+ DEBUG2(VAR, "%s: ignoring delete '%s' as it is read-only\n",
scope->name, varname);
return;
}
@@ -533,6 +524,7 @@ Var_Delete(GNode *scope, const char *varname)
return;
}
+ DEBUG2(VAR, "%s: delete %s\n", scope->name, varname);
if (v->exported)
unsetenv(v->name.str);
if (strcmp(v->name.str, ".MAKE.EXPORTED") == 0)
@@ -623,13 +615,8 @@ ExportVarEnv(Var *v)
return true;
}
- if (v->inUse) {
- /*
- * We recursed while exporting in a child.
- * This isn't going to end well, just skip it.
- */
- return false;
- }
+ if (v->inUse)
+ return false; /* see EMPTY_SHELL in directive-export.mk */
/* XXX: name is injected without escaping it */
expr = str_concat3("${", name, "}");
@@ -677,7 +664,7 @@ ExportVarLiteral(Var *v)
/*
* Mark a single variable to be exported later for subprocesses.
*
- * Internal variables (those starting with '.') are not exported.
+ * Internal variables are not exported.
*/
static bool
ExportVar(const char *name, VarExportMode mode)
@@ -714,9 +701,9 @@ Var_ReexportVars(void)
* We allow the makefiles to update MAKELEVEL and ensure
* children see a correctly incremented value.
*/
- char tmp[21];
- snprintf(tmp, sizeof tmp, "%d", makelevel + 1);
- setenv(MAKE_LEVEL_ENV, tmp, 1);
+ char level_buf[21];
+ snprintf(level_buf, sizeof level_buf, "%d", makelevel + 1);
+ setenv(MAKE_LEVEL_ENV, level_buf, 1);
if (var_exportedVars == VAR_EXPORTED_NONE)
return;
@@ -802,10 +789,10 @@ Var_ExportVars(const char *varnames)
static void
ClearEnv(void)
{
- const char *cp;
+ const char *level;
char **newenv;
- cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
+ level = getenv(MAKE_LEVEL_ENV); /* we should preserve this */
if (environ == savedEnv) {
/* we have been here before! */
newenv = bmake_realloc(environ, 2 * sizeof(char *));
@@ -821,8 +808,8 @@ ClearEnv(void)
environ = savedEnv = newenv;
newenv[0] = NULL;
newenv[1] = NULL;
- if (cp != NULL && *cp != '\0')
- setenv(MAKE_LEVEL_ENV, cp, 1);
+ if (level != NULL && *level != '\0')
+ setenv(MAKE_LEVEL_ENV, level, 1);
}
static void
@@ -878,12 +865,12 @@ UnexportVar(Substring varname, UnexportWhat what)
if (what == UNEXPORT_NAMED) {
/* Remove the variable names from .MAKE.EXPORTED. */
/* XXX: v->name is injected without escaping it */
- char *expr = str_concat3("${.MAKE.EXPORTED:N",
- v->name.str, "}");
- char *cp = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES);
+ char *expr = str_concat3(
+ "${.MAKE.EXPORTED:N", v->name.str, "}");
+ char *filtered = Var_Subst(expr, SCOPE_GLOBAL, VARE_WANTRES);
/* TODO: handle errors */
- Global_Set(".MAKE.EXPORTED", cp);
- free(cp);
+ Global_Set(".MAKE.EXPORTED", filtered);
+ free(filtered);
free(expr);
}
}
@@ -906,11 +893,7 @@ UnexportVars(FStr *varnames, UnexportWhat what)
Global_Delete(".MAKE.EXPORTED");
}
-/*
- * This is called when .unexport[-env] is seen.
- *
- * str must have the form "unexport[-env] varname...".
- */
+/* Handle the .unexport and .unexport-env directives. */
void
Var_UnExport(bool isEnv, const char *arg)
{
@@ -922,32 +905,6 @@ Var_UnExport(bool isEnv, const char *arg)
FStr_Done(&varnames);
}
-/*
- * When there is a variable of the same name in the command line scope, the
- * global variable would not be visible anywhere. Therefore there is no
- * point in setting it at all.
- *
- * See 'scope == SCOPE_CMDLINE' in Var_SetWithFlags.
- */
-static bool
-ExistsInCmdline(const char *name, const char *val)
-{
- Var *v;
-
- v = VarFind(name, SCOPE_CMDLINE, false);
- if (v == NULL)
- return false;
-
- if (v->fromCmd) {
- DEBUG3(VAR, "%s: %s = %s ignored!\n",
- SCOPE_GLOBAL->name, name, val);
- return true;
- }
-
- VarFreeShortLived(v);
- return false;
-}
-
/* Set the variable to the value; the name is not expanded. */
void
Var_SetWithFlags(GNode *scope, const char *name, const char *val,
@@ -957,12 +914,24 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
assert(val != NULL);
if (name[0] == '\0') {
- DEBUG0(VAR, "SetVar: variable name is empty - ignored\n");
+ DEBUG3(VAR,
+ "%s: ignoring '%s = %s' as the variable name is empty\n",
+ scope->name, name, val);
return;
}
- if (scope == SCOPE_GLOBAL && ExistsInCmdline(name, val))
+ if (scope == SCOPE_GLOBAL
+ && VarFind(name, SCOPE_CMDLINE, false) != NULL) {
+ /*
+ * The global variable would not be visible anywhere.
+ * Therefore, there is no point in setting it at all.
+ */
+ DEBUG3(VAR,
+ "%s: ignoring '%s = %s' "
+ "due to a command line variable of the same name\n",
+ scope->name, name, val);
return;
+ }
/*
* Only look for a variable in the given scope since anything set
@@ -982,15 +951,17 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
Var_Delete(SCOPE_GLOBAL, name);
}
if (strcmp(name, ".SUFFIXES") == 0) {
- /* special: treat as readOnly */
- DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n",
+ /* special: treat as read-only */
+ DEBUG3(VAR,
+ "%s: ignoring '%s = %s' as it is read-only\n",
scope->name, name, val);
return;
}
v = VarAdd(name, val, scope, flags);
} else {
if (v->readOnly && !(flags & VAR_SET_READONLY)) {
- DEBUG3(VAR, "%s: %s = %s ignored (read-only)\n",
+ DEBUG3(VAR,
+ "%s: ignoring '%s = %s' as it is read-only\n",
scope->name, name, val);
return;
}
@@ -1003,29 +974,32 @@ Var_SetWithFlags(GNode *scope, const char *name, const char *val,
ExportVar(name, VEM_PLAIN);
}
- /*
- * Any variables given on the command line are automatically exported
- * to the environment (as per POSIX standard), except for internals.
- */
- if (scope == SCOPE_CMDLINE && !(flags & VAR_SET_NO_EXPORT) &&
- name[0] != '.') {
+ if (scope == SCOPE_CMDLINE) {
v->fromCmd = true;
/*
- * If requested, don't export these in the environment
- * individually. We still put them in .MAKEOVERRIDES so
- * that the command-line settings continue to override
- * Makefile settings.
- */
- if (!opts.varNoExportEnv)
- setenv(name, val, 1);
- /* XXX: What about .MAKE.EXPORTED? */
- /*
- * XXX: Why not just mark the variable for needing export, as
- * in ExportVarPlain?
+ * Any variables given on the command line are automatically
+ * exported to the environment (as per POSIX standard), except
+ * for internals.
*/
+ if (!(flags & VAR_SET_NO_EXPORT) && name[0] != '.') {
- Global_Append(".MAKEOVERRIDES", name);
+ /*
+ * If requested, don't export these in the
+ * environment individually. We still put
+ * them in .MAKEOVERRIDES so that the
+ * command-line settings continue to override
+ * Makefile settings.
+ */
+ if (!opts.varNoExportEnv)
+ setenv(name, val, 1);
+ /* XXX: What about .MAKE.EXPORTED? */
+ /*
+ * XXX: Why not just mark the variable for
+ * needing export, as in ExportVarPlain?
+ */
+ Global_Append(".MAKEOVERRIDES", name);
+ }
}
if (name[0] == '.' && strcmp(name, MAKE_SAVE_DOLLARS) == 0)
@@ -1042,20 +1016,12 @@ Var_Set(GNode *scope, const char *name, const char *val)
}
/*
- * 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.
- *
- * Input:
- * scope scope in which to set it
- * name name of the variable to set, is expanded once
- * val value to give to the variable
+ * In the scope, expand the variable name once, then create the variable or
+ * replace its value.
*/
void
Var_SetExpand(GNode *scope, const char *name, const char *val)
{
- const char *unexpanded_name = name;
FStr varname = FStr_InitRefer(name);
assert(val != NULL);
@@ -1063,10 +1029,10 @@ Var_SetExpand(GNode *scope, const char *name, const char *val)
Var_Expand(&varname, scope, VARE_WANTRES);
if (varname.str[0] == '\0') {
- DEBUG2(VAR,
- "Var_SetExpand: variable name \"%s\" expands "
- "to empty string, with value \"%s\" - ignored\n",
- unexpanded_name, val);
+ DEBUG4(VAR,
+ "%s: ignoring '%s = %s' "
+ "as the variable name '%s' expands to empty\n",
+ scope->name, varname.str, val, name);
} else
Var_SetWithFlags(scope, varname.str, val, VAR_SET_NONE);
@@ -1107,8 +1073,8 @@ Var_Append(GNode *scope, const char *name, const char *val)
if (v == NULL) {
Var_SetWithFlags(scope, name, val, VAR_SET_NONE);
} else if (v->readOnly) {
- DEBUG1(VAR, "Ignoring append to %s since it is read-only\n",
- name);
+ DEBUG3(VAR, "%s: ignoring '%s += %s' as it is read-only\n",
+ scope->name, name, val);
} else if (scope == SCOPE_CMDLINE || !v->fromCmd) {
Buf_AddByte(&v->val, ' ');
Buf_AddStr(&v->val, val);
@@ -1129,24 +1095,12 @@ Var_Append(GNode *scope, const char *name, const char *val)
}
/*
- * The variable of the given name has the given value appended to it in the
- * given scope.
+ * In the scope, expand the variable name once. If the variable exists in the
+ * scope, add a space and the value, otherwise set the variable to the value.
*
- * If the variable doesn't exist, it is created. Otherwise the strings are
- * concatenated, with a space in between.
- *
- * Input:
- * scope scope in which this should occur
- * name name of the variable to modify, is expanded once
- * val string to append to it
- *
- * Notes:
- * 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 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.
+ * Appending to an environment variable only works in the global scope, that
+ * is, for variable assignments in makefiles, but not inside conditions or the
+ * commands of a target.
*/
void
Var_AppendExpand(GNode *scope, const char *name, const char *val)
@@ -1157,10 +1111,10 @@ Var_AppendExpand(GNode *scope, const char *name, const char *val)
Var_Expand(&xname, scope, VARE_WANTRES);
if (xname.str != name && xname.str[0] == '\0')
- DEBUG2(VAR,
- "Var_AppendExpand: variable name \"%s\" expands "
- "to empty string, with value \"%s\" - ignored\n",
- name, val);
+ DEBUG4(VAR,
+ "%s: ignoring '%s += %s' "
+ "as the variable name '%s' expands to empty\n",
+ scope->name, xname.str, val, name);
else
Var_Append(scope, xname.str, val);
@@ -1206,11 +1160,11 @@ Var_ExistsExpand(GNode *scope, const char *name)
/*
* Return the unexpanded value of the given variable in the given scope,
- * or the usual scopes.
+ * falling back to the command, global and environment scopes, in this order,
+ * but see the -e option.
*
* Input:
- * scope scope in which to search for it
- * name name to find, is not expanded any further
+ * name the name to find, is not expanded any further
*
* Results:
* The value if the variable exists, NULL if it doesn't.
@@ -1235,9 +1189,7 @@ Var_Value(GNode *scope, const char *name)
return FStr_InitOwn(value);
}
-/*
- * set readOnly attribute of specified var if it exists
- */
+/* Set or clear the read-only attribute of the variable if it exists. */
void
Var_ReadOnly(const char *name, bool bf)
{
@@ -1352,7 +1304,7 @@ SepBuf_DoneData(SepBuf *buf)
/*
- * This callback for ModifyWords gets a single word from a variable expression
+ * This callback for ModifyWords gets a single word from an expression
* and typically adds a modification of this word to the buffer. It may also
* do nothing or add several words.
*
@@ -1366,10 +1318,6 @@ SepBuf_DoneData(SepBuf *buf)
typedef void (*ModifyWordProc)(Substring word, SepBuf *buf, void *data);
-/*
- * Callback for ModifyWords to implement the :H modifier.
- * Add the dirname of the given word to the buffer.
- */
/*ARGSUSED*/
static void
ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
@@ -1377,10 +1325,6 @@ ModifyWord_Head(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
SepBuf_AddSubstring(buf, Substring_Dirname(word));
}
-/*
- * Callback for ModifyWords to implement the :T modifier.
- * Add the basename of the given word to the buffer.
- */
/*ARGSUSED*/
static void
ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
@@ -1388,30 +1332,22 @@ ModifyWord_Tail(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
SepBuf_AddSubstring(buf, Substring_Basename(word));
}
-/*
- * Callback for ModifyWords to implement the :E modifier.
- * Add the filename suffix of the given word to the buffer, if it exists.
- */
/*ARGSUSED*/
static void
ModifyWord_Suffix(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
- const char *lastDot = Substring_LastIndex(word, '.');
+ const char *lastDot = Substring_FindLast(word, '.');
if (lastDot != NULL)
SepBuf_AddRange(buf, lastDot + 1, word.end);
}
-/*
- * Callback for ModifyWords to implement the :R modifier.
- * Add the filename without extension of the given word to the buffer.
- */
/*ARGSUSED*/
static void
ModifyWord_Root(Substring word, SepBuf *buf, void *dummy MAKE_ATTR_UNUSED)
{
const char *lastDot, *end;
- lastDot = Substring_LastIndex(word, '.');
+ lastDot = Substring_FindLast(word, '.');
end = lastDot != NULL ? lastDot : word.end;
SepBuf_AddRange(buf, word.start, end);
}
@@ -1425,7 +1361,6 @@ struct ModifyWord_SysVSubstArgs {
const char *rhs;
};
-/* Callback for ModifyWords to implement the :%.from=%.to modifier. */
static void
ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data)
{
@@ -1459,14 +1394,6 @@ ModifyWord_SysVSubst(Substring word, SepBuf *buf, void *data)
}
#endif
-
-struct ModifyWord_SubstArgs {
- Substring lhs;
- Substring rhs;
- PatternFlags pflags;
- bool matched;
-};
-
static const char *
Substring_Find(Substring haystack, Substring needle)
{
@@ -1480,19 +1407,21 @@ Substring_Find(Substring haystack, Substring needle)
return NULL;
}
-/*
- * Callback for ModifyWords to implement the :S,from,to, modifier.
- * Perform a string substitution on the given word.
- */
+struct ModifyWord_SubstArgs {
+ Substring lhs;
+ Substring rhs;
+ PatternFlags pflags;
+ bool matched;
+};
+
static void
ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
{
struct ModifyWord_SubstArgs *args = data;
size_t wordLen, lhsLen;
- const char *wordEnd, *match;
+ const char *match;
wordLen = Substring_Length(word);
- wordEnd = word.end;
if (args->pflags.subOnce && args->matched)
goto nosub;
@@ -1507,7 +1436,7 @@ ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
/* :S,^prefix,replacement, or :S,^whole$,replacement, */
SepBuf_AddSubstring(buf, args->rhs);
- SepBuf_AddRange(buf, word.start + lhsLen, wordEnd);
+ SepBuf_AddRange(buf, word.start + lhsLen, word.end);
args->matched = true;
return;
}
@@ -1515,11 +1444,11 @@ ModifyWord_Subst(Substring word, SepBuf *buf, void *data)
if (args->pflags.anchorEnd) {
if (wordLen < lhsLen)
goto nosub;
- if (memcmp(wordEnd - lhsLen, args->lhs.start, lhsLen) != 0)
+ if (memcmp(word.end - lhsLen, args->lhs.start, lhsLen) != 0)
goto nosub;
/* :S,suffix$,replacement, */
- SepBuf_AddRange(buf, word.start, wordEnd - lhsLen);
+ SepBuf_AddRange(buf, word.start, word.end - lhsLen);
SepBuf_AddSubstring(buf, args->rhs);
args->matched = true;
return;
@@ -1606,10 +1535,6 @@ struct ModifyWord_SubstRegexArgs {
bool matched;
};
-/*
- * Callback for ModifyWords to implement the :C/from/to/ modifier.
- * Perform a regex substitution on the given word.
- */
static void
ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data)
{
@@ -1643,7 +1568,7 @@ ok:
wp += (size_t)m[0].rm_eo;
if (args->pflags.subGlobal) {
flags |= REG_NOTBOL;
- if (m[0].rm_so == 0 && m[0].rm_eo == 0) {
+ if (m[0].rm_so == 0 && m[0].rm_eo == 0 && *wp != '\0') {
SepBuf_AddBytes(buf, wp, 1);
wp++;
}
@@ -1663,7 +1588,6 @@ struct ModifyWord_LoopArgs {
VarEvalMode emode;
};
-/* Callback for ModifyWords to implement the :@var@...@ modifier of ODE make. */
static void
ModifyWord_Loop(Substring word, SepBuf *buf, void *data)
{
@@ -1680,10 +1604,8 @@ ModifyWord_Loop(Substring word, SepBuf *buf, void *data)
s = Var_Subst(args->body, args->scope, args->emode);
/* TODO: handle errors */
- assert(word.end[0] == '\0'); /* assume null-terminated word */
- DEBUG4(VAR, "ModifyWord_Loop: "
- "in \"%s\", replace \"%s\" with \"%s\" to \"%s\"\n",
- word.start, args->var, args->body, s);
+ DEBUG2(VAR, "ModifyWord_Loop: expand \"%s\" to \"%s\"\n",
+ args->body, s);
if (s[0] == '\n' || Buf_EndsWith(&buf->buf, '\n'))
buf->needSep = false;
@@ -1717,18 +1639,13 @@ VarSelectWords(const char *str, int first, int last,
words = Substring_Words(str, false);
}
- /*
- * Now sanitize the given range. If first or last are negative,
- * convert them to the positive equivalents (-1 gets converted to len,
- * -2 gets converted to (len - 1), etc.).
- */
+ /* Convert -1 to len, -2 to (len - 1), etc. */
len = (int)words.len;
if (first < 0)
first += len + 1;
if (last < 0)
last += len + 1;
- /* We avoid scanning more of the list than we need to. */
if (first > last) {
start = (first > len ? len : first) - 1;
end = last < 1 ? 0 : last - 1;
@@ -1750,10 +1667,6 @@ VarSelectWords(const char *str, int first, int last,
}
-/*
- * Callback for ModifyWords to implement the :tA modifier.
- * Replace each word with the result of realpath() if successful.
- */
/*ARGSUSED*/
static void
ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
@@ -1929,59 +1842,52 @@ FormatTime(const char *fmt, time_t t, bool gmt)
* XXX: As of 2020-11-15, some modifiers such as :S, :C, :P, :L do not
* need to be followed by a ':' or endc; this was an unintended mistake.
*
- * If parsing fails because of a missing delimiter (as in the :S, :C or :@
- * modifiers), return AMR_CLEANUP.
+ * If parsing fails because of a missing delimiter after a modifier part (as
+ * in the :S, :C or :@ modifiers), return AMR_CLEANUP.
*
* If parsing fails because the modifier is unknown, return AMR_UNKNOWN to
- * try the SysV modifier ${VAR:from=to} as fallback. This should only be
+ * try the SysV modifier ':from=to' as fallback. This should only be
* done as long as there have been no side effects from evaluating nested
* variables, to avoid evaluating them more than once. In this case, the
* parsing position may or may not be updated. (XXX: Why not? The original
* parsing position is well-known in ApplyModifiers.)
*
* If parsing fails and the SysV modifier ${VAR:from=to} should not be used
- * as a fallback, either issue an error message using Error or Parse_Error
- * and then return AMR_CLEANUP, or return AMR_BAD for the default error
- * message. Both of these return values will stop processing the variable
- * expression. (XXX: As of 2020-08-23, evaluation of the whole string
- * continues nevertheless after skipping a few bytes, which essentially is
- * undefined behavior. Not in the sense of C, but still the resulting string
- * is garbage.)
+ * as a fallback, issue an error message using Parse_Error (preferred over
+ * Error) and then return AMR_CLEANUP, which stops processing the expression.
+ * (XXX: As of 2020-08-23, evaluation of the string continues nevertheless
+ * after skipping a few bytes, which results in garbage.)
*
* Evaluating the modifier
*
* After parsing, the modifier is evaluated. The side effects from evaluating
- * nested variable expressions in the modifier text often already happen
+ * nested expressions in the modifier text often already happen
* during parsing though. For most modifiers this doesn't matter since their
* only noticeable effect is that they update the value of the expression.
* Some modifiers such as ':sh' or '::=' have noticeable side effects though.
*
- * Evaluating the modifier usually takes the current value of the variable
- * expression from ch->expr->value, or the variable name from ch->var->name
- * and stores the result back in expr->value via Expr_SetValueOwn or
+ * Evaluating the modifier usually takes the current value of the
+ * expression from ch->expr->value, or the variable name from ch->var->name,
+ * and stores the result back in ch->expr->value via Expr_SetValueOwn or
* Expr_SetValueRefer.
*
- * If evaluating fails (as of 2020-08-23), an error message is printed using
- * Error. This function has no side-effects, it really just prints the error
- * message. Processing the expression continues as if everything were ok.
- * XXX: This should be fixed by adding proper error handling to Var_Subst,
- * Var_Parse, ApplyModifiers and ModifyWords.
- *
- * Housekeeping
+ * If evaluating fails, the fallback error message "Bad modifier" is printed
+ * using Error. This function has no side effects, it really just prints the
+ * error message, continuing as if nothing had happened. TODO: This should be
+ * fixed by adding proper error handling to Var_Subst, Var_Parse,
+ * ApplyModifiers and ModifyWords.
*
* Some modifiers such as :D and :U turn undefined expressions into defined
- * expressions (see Expr_Define).
- *
- * Some modifiers need to free some memory.
+ * expressions using Expr_Define.
*/
typedef enum ExprDefined {
- /* The variable expression is based on a regular, defined variable. */
+ /* The expression is based on a regular, defined variable. */
DEF_REGULAR,
- /* The variable expression is based on an undefined variable. */
+ /* The expression is based on an undefined variable. */
DEF_UNDEF,
/*
- * The variable expression started as an undefined expression, but one
+ * The expression started as an undefined expression, but one
* of the modifiers (such as ':D' or ':U') has turned the expression
* from undefined to defined.
*/
@@ -2030,9 +1936,6 @@ typedef struct Expr {
*
* After such a chain ends, its properties no longer have any effect.
*
- * It may or may not have been intended that 'defined' has scope Expr while
- * 'sep' and 'oneBigWord' have smaller scope.
- *
* See varmod-indirect.mk.
*/
typedef struct ModChain {
@@ -2041,10 +1944,10 @@ typedef struct ModChain {
char const_member startc;
/* '\0' or '}' or ')' */
char const_member endc;
- /* Word separator in expansions (see the :ts modifier). */
+ /* Separator when joining words (see the :ts modifier). */
char sep;
/*
- * True if some modifiers that otherwise split the variable value
+ * Whether some modifiers that otherwise split the variable value
* into words, like :S and :C, treat the variable value as a single
* big word, possibly containing spaces.
*/
@@ -2105,7 +2008,7 @@ ModChain_ShouldEval(const ModChain *ch)
typedef enum ApplyModifierResult {
/* Continue parsing */
AMR_OK,
- /* Not a match, try other modifiers as well. */
+ /* Not a match, try the ':from=to' modifier as well. */
AMR_UNKNOWN,
/* Error out with "Bad modifier" message. */
AMR_BAD,
@@ -2151,14 +2054,13 @@ ParseModifierPartExpr(const char **pp, LazyBuf *part, const ModChain *ch,
* In a part of a modifier, parse some text that looks like a subexpression.
* If the text starts with '$(', any '(' and ')' must be balanced.
* If the text starts with '${', any '{' and '}' must be balanced.
- * If the text starts with '$', that '$' is copied, it is not parsed as a
- * short-name variable expression.
+ * If the text starts with '$', that '$' is copied verbatim, it is not parsed
+ * as a short-name expression.
*/
static void
ParseModifierPartBalanced(const char **pp, LazyBuf *part)
{
const char *p = *pp;
- const char *start = *pp;
if (p[1] == '(' || p[1] == '{') {
char startc = p[1];
@@ -2173,10 +2075,10 @@ ParseModifierPartBalanced(const char **pp, LazyBuf *part)
depth--;
}
}
- LazyBuf_AddSubstring(part, Substring_Init(start, p));
+ LazyBuf_AddSubstring(part, Substring_Init(*pp, p));
*pp = p;
} else {
- LazyBuf_Add(part, *start);
+ LazyBuf_Add(part, *p);
*pp = p + 1;
}
}
@@ -2192,13 +2094,13 @@ ParseModifierPartSubst(
ModChain *ch,
LazyBuf *part,
/*
- * For the first part of the modifier ':S', set anchorEnd if the last
+ * For the first part of the ':S' modifier, set anchorEnd if the last
* character of the pattern is a $.
*/
PatternFlags *out_pflags,
/*
- * For the second part of the :S modifier, allow ampersands to be escaped
- * and replace unescaped ampersands with subst->lhs.
+ * For the second part of the ':S' modifier, allow ampersands to be
+ * escaped and replace unescaped ampersands with subst->lhs.
*/
struct ModifyWord_SubstArgs *subst
)
@@ -2268,7 +2170,7 @@ ParseModifierPart(
const char **pp,
/* Parsing stops at this delimiter */
char delim,
- /* Mode for evaluating nested variables. */
+ /* Mode for evaluating nested expressions. */
VarEvalMode emode,
ModChain *ch,
LazyBuf *part
@@ -2465,12 +2367,11 @@ ParseModifier_Defined(const char **pp, ModChain *ch, bool shouldEval,
/*
* XXX: This code is similar to the one in Var_Parse. See if
- * the code can be merged. See also ApplyModifier_Match and
+ * the code can be merged. See also ParseModifier_Match and
* ParseModifierPart.
*/
- /* Escaped delimiter or other special character */
- /* See Buf_AddEscaped in for.c. */
+ /* See Buf_AddEscaped in for.c for the counterpart. */
if (*p == '\\') {
char c = p[1];
if ((IsDelimiter(c, ch) && c != '\0') ||
@@ -2482,7 +2383,6 @@ ParseModifier_Defined(const char **pp, ModChain *ch, bool shouldEval,
}
}
- /* Nested variable expression */
if (*p == '$') {
FStr val = Var_Parse(&p, ch->expr->scope,
shouldEval ? ch->expr->emode : VARE_PARSE_ONLY);
@@ -2493,7 +2393,6 @@ ParseModifier_Defined(const char **pp, ModChain *ch, bool shouldEval,
continue;
}
- /* Ordinary text */
if (shouldEval)
LazyBuf_Add(buf, *p);
p++;
@@ -2631,11 +2530,11 @@ ApplyModifier_Path(const char **pp, ModChain *ch)
Expr_Define(expr);
gn = Targ_FindNode(expr->name);
- if (gn == NULL || gn->type & OP_NOPATH) {
+ if (gn == NULL || gn->type & OP_NOPATH)
path = NULL;
- } else if (gn->path != NULL) {
+ else if (gn->path != NULL)
path = bmake_strdup(gn->path);
- } else {
+ else {
SearchPath *searchPath = Suff_FindPath(gn);
path = Dir_FindFile(expr->name, searchPath);
}
@@ -2752,9 +2651,9 @@ ParseModifier_Match(const char **pp, const ModChain *ch)
* See if the code can be merged.
* See also ApplyModifier_Defined.
*/
- int nest = 0;
+ int depth = 0;
const char *p;
- for (p = mod + 1; *p != '\0' && !(*p == ':' && nest == 0); p++) {
+ for (p = mod + 1; *p != '\0' && !(*p == ':' && depth == 0); p++) {
if (*p == '\\' && p[1] != '\0' &&
(IsDelimiter(p[1], ch) || p[1] == ch->startc)) {
if (!needSubst)
@@ -2765,10 +2664,10 @@ ParseModifier_Match(const char **pp, const ModChain *ch)
if (*p == '$')
needSubst = true;
if (*p == '(' || *p == '{')
- nest++;
+ depth++;
if (*p == ')' || *p == '}') {
- nest--;
- if (nest < 0)
+ depth--;
+ if (depth < 0)
break;
}
}
@@ -2906,22 +2805,24 @@ ApplyModifier_Mtime(const char **pp, ModChain *ch)
if (args.use_fallback) {
p++;
if (TryParseTime(&p, &args.fallback)) {
- } else if (strncmp(p, "error", 5) == 0
- && IsDelimiter(p[5], ch)) {
+ } else if (strncmp(p, "error", 5) == 0) {
p += 5;
args.error = true;
- } else {
- Parse_Error(PARSE_FATAL,
- "Invalid argument '%.*s' for modifier ':mtime'",
- (int)strcspn(p, ":{}()"), p);
- return AMR_CLEANUP;
- }
+ } else
+ goto invalid_argument;
+ if (!IsDelimiter(*p, ch))
+ goto invalid_argument;
*pp = p;
}
- if (!ModChain_ShouldEval(ch))
- return AMR_OK;
- ModifyWords(ch, ModifyWord_Mtime, &args, ch->oneBigWord);
+ if (ModChain_ShouldEval(ch))
+ ModifyWords(ch, ModifyWord_Mtime, &args, ch->oneBigWord);
return args.rc;
+
+invalid_argument:
+ Parse_Error(PARSE_FATAL,
+ "Invalid argument '%.*s' for modifier ':mtime'",
+ (int)strcspn(*pp + 1, ":{}()"), *pp + 1);
+ return AMR_CLEANUP;
}
static void
@@ -3093,8 +2994,8 @@ ApplyModifier_ToSep(const char **pp, ModChain *ch)
const char *sep = *pp + 2;
/*
- * Even in parse-only mode, proceed as normal since there is
- * neither any observable side effect nor a performance penalty.
+ * Even in parse-only mode, apply the side effects, since the side
+ * effects are neither observable nor is there a performance penalty.
* Checking for wantRes for every single piece of code in here
* would make the code in this function too hard to read.
*/
@@ -3113,7 +3014,7 @@ ApplyModifier_ToSep(const char **pp, ModChain *ch)
goto ok;
}
- /* ":ts<unrecognised><unrecognised>". */
+ /* ":ts<unrecognized><unrecognized>". */
if (sep[0] != '\\') {
(*pp)++; /* just for backwards compatibility */
return AMR_BAD;
@@ -3143,7 +3044,7 @@ ApplyModifier_ToSep(const char **pp, ModChain *ch)
p++;
} else if (!ch_isdigit(sep[1])) {
(*pp)++; /* just for backwards compatibility */
- return AMR_BAD; /* ":ts<backslash><unrecognised>". */
+ return AMR_BAD; /* ":ts<backslash><unrecognized>". */
}
if (!TryParseChar(&p, base, &ch->sep)) {
@@ -3239,7 +3140,7 @@ ApplyModifier_To(const char **pp, ModChain *ch)
return AMR_OK;
}
- /* Found ":t<unrecognised>:" or ":t<unrecognised><endc>". */
+ /* Found ":t<unrecognized>:" or ":t<unrecognized><endc>". */
*pp = mod + 1; /* XXX: unnecessary but observable */
return AMR_BAD;
}
@@ -3249,17 +3150,16 @@ static ApplyModifierResult
ApplyModifier_Words(const char **pp, ModChain *ch)
{
Expr *expr = ch->expr;
- const char *estr;
int first, last;
const char *p;
- LazyBuf estrBuf;
- FStr festr;
+ LazyBuf argBuf;
+ FStr arg;
(*pp)++; /* skip the '[' */
- if (!ParseModifierPart(pp, ']', expr->emode, ch, &estrBuf))
+ if (!ParseModifierPart(pp, ']', expr->emode, ch, &argBuf))
return AMR_CLEANUP;
- festr = LazyBuf_DoneGet(&estrBuf);
- estr = festr.str;
+ arg = LazyBuf_DoneGet(&argBuf);
+ p = arg.str;
if (!IsDelimiter(**pp, ch))
goto bad_modifier; /* Found junk after ']' */
@@ -3267,80 +3167,67 @@ ApplyModifier_Words(const char **pp, ModChain *ch)
if (!ModChain_ShouldEval(ch))
goto ok;
- if (estr[0] == '\0')
- goto bad_modifier; /* Found ":[]". */
+ if (p[0] == '\0')
+ goto bad_modifier; /* Found ":[]". */
- if (estr[0] == '#' && estr[1] == '\0') { /* Found ":[#]" */
- if (ch->oneBigWord) {
+ if (strcmp(p, "#") == 0) { /* Found ":[#]" */
+ if (ch->oneBigWord)
Expr_SetValueRefer(expr, "1");
- } else {
+ else {
Buffer buf;
SubstringWords words = Expr_Words(expr);
size_t ac = words.len;
SubstringWords_Free(words);
- /* 3 digits + '\0' is usually enough */
- Buf_InitSize(&buf, 4);
+ Buf_Init(&buf);
Buf_AddInt(&buf, (int)ac);
Expr_SetValueOwn(expr, Buf_DoneData(&buf));
}
goto ok;
}
- if (estr[0] == '*' && estr[1] == '\0') { /* Found ":[*]" */
+ if (strcmp(p, "*") == 0) { /* ":[*]" */
ch->oneBigWord = true;
goto ok;
}
- if (estr[0] == '@' && estr[1] == '\0') { /* Found ":[@]" */
+ if (strcmp(p, "@") == 0) { /* ":[@]" */
ch->oneBigWord = false;
goto ok;
}
- /*
- * We expect estr to contain a single integer for :[N], or two
- * integers separated by ".." for :[start..end].
- */
- p = estr;
+ /* Expect ":[N]" or ":[start..end]" */
if (!TryParseIntBase0(&p, &first))
- goto bad_modifier; /* Found junk instead of a number */
+ goto bad_modifier;
- if (p[0] == '\0') { /* Found only one integer in :[N] */
+ if (p[0] == '\0') /* ":[N]" */
last = first;
- } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') {
- /* Expecting another integer after ".." */
+ else if (strncmp(p, "..", 2) == 0) {
p += 2;
if (!TryParseIntBase0(&p, &last) || *p != '\0')
- goto bad_modifier; /* Found junk after ".." */
+ goto bad_modifier;
} else
- goto bad_modifier; /* Found junk instead of ".." */
+ goto bad_modifier;
- /*
- * Now first and last are properly filled in, but we still have to
- * check for 0 as a special case.
- */
- if (first == 0 && last == 0) {
- /* ":[0]" or perhaps ":[0..0]" */
+ if (first == 0 && last == 0) { /* ":[0]" or ":[0..0]" */
ch->oneBigWord = true;
goto ok;
}
- /* ":[0..N]" or ":[N..0]" */
- if (first == 0 || last == 0)
+ if (first == 0 || last == 0) /* ":[0..N]" or ":[N..0]" */
goto bad_modifier;
- /* Normal case: select the words described by first and last. */
Expr_SetValueOwn(expr,
VarSelectWords(Expr_Str(expr), first, last,
ch->sep, ch->oneBigWord));
ok:
- FStr_Done(&festr);
+ FStr_Done(&arg);
return AMR_OK;
bad_modifier:
- FStr_Done(&festr);
+ FStr_Done(&arg);
return AMR_BAD;
}
@@ -3385,7 +3272,7 @@ SubNumAsc(const void *sa, const void *sb)
a = num_val(*((const Substring *)sa));
b = num_val(*((const Substring *)sb));
- return (a > b) ? 1 : (b > a) ? -1 : 0;
+ return a > b ? 1 : b > a ? -1 : 0;
}
static int
@@ -3395,10 +3282,19 @@ SubNumDesc(const void *sa, const void *sb)
}
static int
+Substring_Cmp(Substring a, Substring b)
+{
+ for (; a.start < a.end && b.start < b.end; a.start++, b.start++)
+ if (a.start[0] != b.start[0])
+ return (unsigned char)a.start[0]
+ - (unsigned char)b.start[0];
+ return (int)((a.end - a.start) - (b.end - b.start));
+}
+
+static int
SubStrAsc(const void *sa, const void *sb)
{
- return strcmp(
- ((const Substring *)sa)->start, ((const Substring *)sb)->start);
+ return Substring_Cmp(*(const Substring *)sa, *(const Substring *)sb);
}
static int
@@ -3510,8 +3406,8 @@ ApplyModifier_IfElse(const char **pp, ModChain *ch)
if (cond_rc == CR_ERROR) {
Substring thenExpr = LazyBuf_Get(&thenBuf);
Substring elseExpr = LazyBuf_Get(&elseBuf);
- Error("Bad conditional expression '%s' in '%s?%.*s:%.*s'",
- expr->name, expr->name,
+ Error("Bad conditional expression '%s' before '?%.*s:%.*s'",
+ expr->name,
(int)Substring_Length(thenExpr), thenExpr.start,
(int)Substring_Length(elseExpr), elseExpr.start);
LazyBuf_Done(&thenBuf);
@@ -3570,7 +3466,7 @@ ApplyModifier_Assign(const char **pp, ModChain *ch)
goto found_op;
if ((op[0] == '+' || op[0] == '?' || op[0] == '!') && op[1] == '=')
goto found_op;
- return AMR_UNKNOWN; /* "::<unrecognised>" */
+ return AMR_UNKNOWN; /* "::<unrecognized>" */
found_op:
if (expr->name[0] == '\0') {
@@ -3578,7 +3474,7 @@ found_op:
return AMR_BAD;
}
- *pp = mod + (op[0] == '+' || op[0] == '?' || op[0] == '!' ? 3 : 2);
+ *pp = mod + (op[0] != '=' ? 3 : 2);
if (!ParseModifierPart(pp, ch->endc, expr->emode, ch, &buf))
return AMR_CLEANUP;
@@ -3590,13 +3486,9 @@ found_op:
goto done;
scope = expr->scope; /* scope where v belongs */
- if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL) {
- Var *v = VarFind(expr->name, expr->scope, false);
- if (v == NULL)
- scope = SCOPE_GLOBAL;
- else
- VarFreeShortLived(v);
- }
+ if (expr->defined == DEF_REGULAR && expr->scope != SCOPE_GLOBAL
+ && VarFind(expr->name, expr->scope, false) == NULL)
+ scope = SCOPE_GLOBAL;
if (op[0] == '+')
Var_Append(scope, expr->name, val.str);
@@ -3640,7 +3532,7 @@ ApplyModifier_Remember(const char **pp, ModChain *ch)
/*
* XXX: This ad-hoc call to strcspn deviates from the usual
* behavior defined in ParseModifierPart. This creates an
- * unnecessary, undocumented inconsistency in make.
+ * unnecessary and undocumented inconsistency in make.
*/
const char *arg = mod + 2;
size_t argLen = strcspn(arg, ":)}");
@@ -3690,7 +3582,7 @@ ApplyModifier_Unique(const char **pp, ModChain *ch)
words = Expr_Words(ch->expr);
if (words.len > 1) {
- size_t si, di;
+ size_t di, si;
di = 0;
for (si = 1; si < words.len; si++) {
@@ -3709,6 +3601,26 @@ ApplyModifier_Unique(const char **pp, ModChain *ch)
}
#ifdef SYSVVARSUB
+/* Test whether the modifier has the form '<lhs>=<rhs>'. */
+static bool
+IsSysVModifier(const char *p, char startc, char endc)
+{
+ bool eqFound = false;
+
+ int depth = 1;
+ while (*p != '\0' && depth > 0) {
+ if (*p == '=') /* XXX: should also test depth == 1 */
+ eqFound = true;
+ else if (*p == endc)
+ depth--;
+ else if (*p == startc)
+ depth++;
+ if (depth > 0)
+ p++;
+ }
+ return *p == endc && eqFound;
+}
+
/* :from=to */
static ApplyModifierResult
ApplyModifier_SysV(const char **pp, ModChain *ch)
@@ -3721,33 +3633,15 @@ ApplyModifier_SysV(const char **pp, ModChain *ch)
const char *lhsSuffix;
const char *mod = *pp;
- bool eqFound = false;
- /*
- * First we make a pass through the string trying to verify it is a
- * SysV-make-style translation. It must be: <lhs>=<rhs>
- */
- int depth = 1;
- const char *p = mod;
- while (*p != '\0' && depth > 0) {
- if (*p == '=') { /* XXX: should also test depth == 1 */
- eqFound = true;
- /* continue looking for ch->endc */
- } else if (*p == ch->endc)
- depth--;
- else if (*p == ch->startc)
- depth++;
- if (depth > 0)
- p++;
- }
- if (*p != ch->endc || !eqFound)
+ if (!IsSysVModifier(mod, ch->startc, ch->endc))
return AMR_UNKNOWN;
if (!ParseModifierPart(pp, '=', expr->emode, ch, &lhsBuf))
return AMR_CLEANUP;
/*
- * The SysV modifier lasts until the end of the variable expression.
+ * The SysV modifier lasts until the end of the expression.
*/
if (!ParseModifierPart(pp, ch->endc, expr->emode, ch, &rhsBuf)) {
LazyBuf_Done(&lhsBuf);
@@ -3948,16 +3842,16 @@ typedef enum ApplyModifiersIndirectResult {
} ApplyModifiersIndirectResult;
/*
- * While expanding a variable expression, expand and apply indirect modifiers,
+ * While expanding an expression, expand and apply indirect modifiers,
* such as in ${VAR:${M_indirect}}.
*
- * All indirect modifiers of a group must come from a single variable
+ * All indirect modifiers of a group must come from a single
* expression. ${VAR:${M1}} is valid but ${VAR:${M1}${M2}} is not.
*
* Multiple groups of indirect modifiers can be chained by separating them
* with colons. ${VAR:${M1}:${M2}} contains 2 indirect modifiers.
*
- * If the variable expression is not followed by ch->endc or ':', fall
+ * If the expression is not followed by ch->endc or ':', fall
* back to trying the SysV modifier, such as in ${VAR:${FROM}=${TO}}.
*/
static ApplyModifiersIndirectResult
@@ -3990,8 +3884,8 @@ ApplyModifiersIndirect(ModChain *ch, const char **pp)
if (*p == ':')
p++;
else if (*p == '\0' && ch->endc != '\0') {
- Error("Unclosed variable expression after indirect "
- "modifier, expecting '%c' for variable \"%s\"",
+ Error("Unclosed expression after indirect modifier, "
+ "expecting '%c' for variable \"%s\"",
ch->endc, expr->name);
*pp = p;
return AMIR_OUT;
@@ -4043,7 +3937,7 @@ ApplySingleModifier(const char **pp, ModChain *ch)
if (*p == '\0' && ch->endc != '\0') {
Error(
- "Unclosed variable expression, expecting '%c' for "
+ "Unclosed expression, expecting '%c' for "
"modifier \"%.*s\" of variable \"%s\" with value \"%s\"",
ch->endc,
(int)(p - mod), mod,
@@ -4064,11 +3958,11 @@ ApplySingleModifier(const char **pp, ModChain *ch)
}
#if __STDC_VERSION__ >= 199901L
-#define ModChain_Literal(expr, startc, endc, sep, oneBigWord) \
+#define ModChain_Init(expr, startc, endc, sep, oneBigWord) \
(ModChain) { expr, startc, endc, sep, oneBigWord }
#else
MAKE_INLINE ModChain
-ModChain_Literal(Expr *expr, char startc, char endc, char sep, bool oneBigWord)
+ModChain_Init(Expr *expr, char startc, char endc, char sep, bool oneBigWord)
{
ModChain ch;
ch.expr = expr;
@@ -4089,7 +3983,7 @@ ApplyModifiers(
char endc /* ')' or '}'; or '\0' for indirect modifiers */
)
{
- ModChain ch = ModChain_Literal(expr, startc, endc, ' ', false);
+ ModChain ch = ModChain_Init(expr, startc, endc, ' ', false);
const char *p;
const char *mod;
@@ -4101,7 +3995,7 @@ ApplyModifiers(
if (*p == '\0' && endc != '\0') {
Error(
- "Unclosed variable expression (expecting '%c') for \"%s\"",
+ "Unclosed expression, expecting '%c' for \"%s\"",
ch.endc, expr->name);
goto cleanup;
}
@@ -4110,17 +4004,17 @@ ApplyModifiers(
ApplyModifierResult res;
if (*p == '$') {
+ /*
+ * TODO: Only evaluate the expression once, no matter
+ * whether it's an indirect modifier or the initial
+ * part of a SysV modifier.
+ */
ApplyModifiersIndirectResult amir =
ApplyModifiersIndirect(&ch, &p);
if (amir == AMIR_CONTINUE)
continue;
if (amir == AMIR_OUT)
break;
- /*
- * It's neither '${VAR}:' nor '${VAR}}'. Try to parse
- * it as a SysV modifier, as that is the only modifier
- * that can start with '$'.
- */
}
mod = p;
@@ -4137,7 +4031,7 @@ ApplyModifiers(
return;
bad_modifier:
- /* XXX: The modifier end is only guessed. */
+ /* Take a guess at where the modifier ends. */
Error("Bad modifier \":%.*s\" for variable \"%s\"",
(int)strcspn(mod, ":)}"), mod, expr->name);
@@ -4226,7 +4120,7 @@ ParseVarname(const char **pp, char startc, char endc,
LazyBuf *buf)
{
const char *p = *pp;
- int depth = 0; /* Track depth so we can spot parse errors. */
+ int depth = 0;
LazyBuf_Init(buf, p);
@@ -4238,7 +4132,6 @@ ParseVarname(const char **pp, char startc, char endc,
if (*p == endc)
depth--;
- /* A variable inside a variable, expand. */
if (*p == '$') {
FStr nested_val = Var_Parse(&p, scope, emode);
/* TODO: handle errors */
@@ -4336,7 +4229,8 @@ FindLocalLegacyVar(Substring varname, GNode *scope,
if (strchr("@%?*!<>", varname.start[0]) == NULL)
return NULL;
- v = VarFindSubstring(Substring_Sub(varname, 0, 1), scope, false);
+ v = VarFindSubstring(Substring_Init(varname.start, varname.start + 1),
+ scope, false);
if (v == NULL)
return NULL;
@@ -4400,11 +4294,11 @@ ParseVarnameLong(
ParseVarname(&p, startc, endc, scope, emode, &varname);
name = LazyBuf_Get(&varname);
- if (*p == ':') {
+ if (*p == ':')
haveModifier = true;
- } else if (*p == endc) {
+ else if (*p == endc)
haveModifier = false;
- } else {
+ else {
Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"",
(int)Substring_Length(name), name.start);
LazyBuf_Done(&varname);
@@ -4446,14 +4340,14 @@ ParseVarnameLong(
}
/*
- * The variable expression is based on an undefined variable.
+ * The expression is based on an undefined variable.
* Nevertheless it needs a Var, for modifiers that access the
* variable name, such as :L or :?.
*
* Most modifiers leave this expression in the "undefined"
- * state (VES_UNDEF), only a few modifiers like :D, :U, :L,
+ * state (DEF_UNDEF), only a few modifiers like :D, :U, :L,
* :P turn this undefined expression into a defined
- * expression (VES_DEF).
+ * expression (DEF_DEFINED).
*
* In the end, after applying all modifiers, if the expression
* is still undefined, Var_Parse will return an empty string
@@ -4474,12 +4368,12 @@ ParseVarnameLong(
}
#if __STDC_VERSION__ >= 199901L
-#define Expr_Literal(name, value, emode, scope, defined) \
- { name, value, emode, scope, defined }
+#define Expr_Init(name, value, emode, scope, defined) \
+ (Expr) { name, value, emode, scope, defined }
#else
MAKE_INLINE Expr
-Expr_Literal(const char *name, FStr value,
- VarEvalMode emode, GNode *scope, ExprDefined defined)
+Expr_Init(const char *name, FStr value,
+ VarEvalMode emode, GNode *scope, ExprDefined defined)
{
Expr expr;
@@ -4494,8 +4388,7 @@ Expr_Literal(const char *name, FStr value,
/*
* Expressions of the form ${:U...} with a trivial value are often generated
- * by .for loops and are boring, therefore parse and evaluate them in a fast
- * lane without debug logging.
+ * by .for loops and are boring, so evaluate them without debug logging.
*/
static bool
Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
@@ -4513,27 +4406,24 @@ Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
if (*p != '}')
return false;
- if (emode == VARE_PARSE_ONLY)
- *out_value = FStr_InitRefer("");
- else
- *out_value = FStr_InitOwn(bmake_strsedup(*pp + 4, p));
+ *out_value = emode == VARE_PARSE_ONLY
+ ? FStr_InitRefer("")
+ : FStr_InitOwn(bmake_strsedup(*pp + 4, p));
*pp = p + 1;
return true;
}
/*
- * Given the start of a variable expression (such as $v, $(VAR),
- * ${VAR:Mpattern}), extract the variable name and value, and the modifiers,
- * if any. While doing that, apply the modifiers to the value of the
- * expression, forming its final value. A few of the modifiers such as :!cmd!
- * or ::= have side effects.
+ * Given the start of an expression (such as $v, $(VAR), ${VAR:Mpattern}),
+ * extract the variable name and the modifiers, if any. While parsing, apply
+ * the modifiers to the value of the expression.
*
* Input:
* *pp The string to parse.
* When called from CondParser_FuncCallEmpty, it can
* also point to the "y" of "empty(VARNAME:Modifiers)".
- * scope The scope for finding variables
- * emode Controls the exact details of parsing and evaluation
+ * scope The scope for finding variables.
+ * emode Controls the exact details of parsing and evaluation.
*
* Output:
* *pp The position where to continue parsing.
@@ -4542,7 +4432,7 @@ Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
* point to some random character in the string, to the
* location of the parse error, or at the end of the
* string.
- * return The value of the variable expression, never NULL.
+ * return The value of the expression, never NULL.
* return var_Error if there was a parse error.
* return var_Error if the base variable of the expression was
* undefined, emode is VARE_UNDEFERR, and none of
@@ -4574,15 +4464,13 @@ Var_Parse(const char **pp, GNode *scope, VarEvalMode emode)
bool dynamic;
const char *extramodifiers;
Var *v;
- Expr expr = Expr_Literal(NULL, FStr_InitRefer(NULL), emode,
+ Expr expr = Expr_Init(NULL, FStr_InitRefer(NULL), emode,
scope, DEF_REGULAR);
FStr val;
if (Var_Parse_FastLane(pp, emode, &val))
return val;
- /* TODO: Reduce computations in parse-only mode. */
-
DEBUG2(VAR, "Var_Parse: %s (%s)\n", start, VarEvalMode_Name[emode]);
val = FStr_InitRefer(NULL);
@@ -4615,11 +4503,18 @@ Var_Parse(const char **pp, GNode *scope, VarEvalMode emode)
}
/*
- * XXX: This assignment creates an alias to the current value of the
+ * FIXME: This assignment creates an alias to the current value of the
* variable. This means that as long as the value of the expression
- * stays the same, the value of the variable must not change.
- * Using the '::=' modifier, it could be possible to trigger exactly
- * this situation.
+ * stays the same, the value of the variable must not change, and the
+ * variable must not be deleted. Using the ':@' modifier, it is
+ * possible (since var.c 1.212 from 2017-02-01) to delete the variable
+ * while its value is still being used:
+ *
+ * VAR= value
+ * _:= ${VAR:${:U@VAR@loop@}:S,^,prefix,}
+ *
+ * The same effect might be achievable using the '::=' or the ':_'
+ * modifiers.
*
* At the bottom of this function, the resulting value is compared to
* the then-current value of the variable. This might also invoke
@@ -4711,10 +4606,10 @@ VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
} else if (val.str == var_Error) {
/*
- * XXX: This condition is wrong. If val == var_Error,
- * this doesn't necessarily mean there was an undefined
- * variable. It could equally well be a parse error;
- * see unit-tests/varmod-order.exp.
+ * FIXME: The condition 'val.str == var_Error' doesn't
+ * mean there was an undefined variable. It could
+ * equally well be a parse error; see
+ * unit-tests/varmod-order.mk.
*/
/*
@@ -4725,10 +4620,10 @@ VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
if (!*inout_errorReported) {
Parse_Error(PARSE_FATAL,
"Undefined variable \"%.*s\"",
- (int)(size_t)(nested_p - p), p);
+ (int)(nested_p - p), p);
+ *inout_errorReported = true;
}
p = nested_p;
- *inout_errorReported = true;
} else {
/*
* Copy the initial '$' of the undefined expression,
@@ -4750,8 +4645,8 @@ VarSubstExpr(const char **pp, Buffer *buf, GNode *scope,
}
/*
- * Skip as many characters as possible -- either to the end of the string
- * or to the next dollar sign (variable expression).
+ * Skip as many characters as possible -- either to the end of the string,
+ * or to the next dollar sign, which may start an expression.
*/
static void
VarSubstPlain(const char **pp, Buffer *res)
@@ -4766,11 +4661,11 @@ VarSubstPlain(const char **pp, Buffer *res)
}
/*
- * Expand all variable expressions like $V, ${VAR}, $(VAR:Modifiers) in the
+ * Expand all expressions like $V, ${VAR}, $(VAR:Modifiers) in the
* given string.
*
* Input:
- * str The string in which the variable expressions are
+ * str The string in which the expressions are
* expanded.
* scope The scope in which to start searching for
* variables. The other scopes are searched as well.
@@ -4786,7 +4681,6 @@ Var_Subst(const char *str, GNode *scope, VarEvalMode emode)
* Set true if an error has already been reported, to prevent a
* plethora of messages when recursing
*/
- /* See varparse-errors.mk for why the 'static' is necessary here. */
static bool errorReported;
Buf_Init(&res);