aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon J. Gerraty <sjg@FreeBSD.org>2021-12-18 18:09:14 +0000
committerSimon J. Gerraty <sjg@FreeBSD.org>2021-12-18 18:09:14 +0000
commit129043849f62f9cfa72f6fae68417d9995860f3f (patch)
tree176e86d2eced9a37d41d4661fd8f128a56d7befc
parent7ed5694dd9b9d91e6afd7442b04b602041650354 (diff)
parent2935fe8237c83c1dcb113dd5335733263e68e6fd (diff)
downloadsrc-129043849f62f9cfa72f6fae68417d9995860f3f.tar.gz
src-129043849f62f9cfa72f6fae68417d9995860f3f.zip
Merge bmake-20211212
commit '2935fe8237c83c1dcb113dd5335733263e68e6fd'
-rw-r--r--contrib/bmake/ChangeLog231
-rw-r--r--contrib/bmake/FILES15
-rw-r--r--contrib/bmake/Makefile17
-rw-r--r--contrib/bmake/Makefile.config.in2
-rw-r--r--contrib/bmake/VERSION2
-rw-r--r--contrib/bmake/_strtol.h213
-rw-r--r--contrib/bmake/arch.c124
-rw-r--r--contrib/bmake/bmake.119
-rw-r--r--contrib/bmake/bmake.cat112
-rwxr-xr-xcontrib/bmake/boot-strap17
-rw-r--r--contrib/bmake/bsd.after-import.mk3
-rw-r--r--contrib/bmake/buf.c23
-rw-r--r--contrib/bmake/buf.h3
-rw-r--r--contrib/bmake/compat.c28
-rw-r--r--contrib/bmake/cond.c342
-rw-r--r--contrib/bmake/config.h.in46
-rwxr-xr-xcontrib/bmake/configure481
-rw-r--r--contrib/bmake/configure.in200
-rw-r--r--contrib/bmake/dir.c33
-rwxr-xr-xcontrib/bmake/enum.c80
-rwxr-xr-xcontrib/bmake/enum.h179
-rw-r--r--contrib/bmake/filemon/filemon_ktrace.c3
-rw-r--r--contrib/bmake/for.c130
-rw-r--r--contrib/bmake/hash.c15
-rw-r--r--contrib/bmake/hash.h11
-rwxr-xr-xcontrib/bmake/import.sh13
-rw-r--r--contrib/bmake/job.c43
-rw-r--r--contrib/bmake/lst.h6
-rw-r--r--contrib/bmake/main.c8
-rwxr-xr-xcontrib/bmake/make-bootstrap.sh.in2
-rw-r--r--contrib/bmake/make.119
-rw-r--r--contrib/bmake/make.c140
-rw-r--r--contrib/bmake/make.h87
-rw-r--r--contrib/bmake/meta.c50
-rw-r--r--contrib/bmake/metachar.c6
-rw-r--r--contrib/bmake/metachar.h10
-rw-r--r--contrib/bmake/mk/ChangeLog104
-rw-r--r--contrib/bmake/mk/FILES1
-rw-r--r--contrib/bmake/mk/auto.dep.mk52
-rw-r--r--contrib/bmake/mk/autoconf.mk23
-rw-r--r--contrib/bmake/mk/autodep.mk7
-rw-r--r--contrib/bmake/mk/compiler.mk10
-rw-r--r--contrib/bmake/mk/dep.mk4
-rw-r--r--contrib/bmake/mk/dirdeps.mk57
-rw-r--r--contrib/bmake/mk/doc.mk4
-rw-r--r--contrib/bmake/mk/dpadd.mk4
-rw-r--r--contrib/bmake/mk/final.mk4
-rw-r--r--contrib/bmake/mk/init.mk4
-rwxr-xr-xcontrib/bmake/mk/install-mk4
-rw-r--r--contrib/bmake/mk/java.mk4
-rw-r--r--contrib/bmake/mk/ldorder.mk4
-rw-r--r--contrib/bmake/mk/lib.mk6
-rw-r--r--contrib/bmake/mk/man.mk54
-rw-r--r--contrib/bmake/mk/meta.autodep.mk12
-rw-r--r--contrib/bmake/mk/meta.stage.mk11
-rw-r--r--contrib/bmake/mk/meta.sys.mk26
-rwxr-xr-xcontrib/bmake/mk/meta2deps.py29
-rw-r--r--contrib/bmake/mk/obj.mk4
-rw-r--r--contrib/bmake/mk/options.mk38
-rw-r--r--contrib/bmake/mk/own.mk6
-rw-r--r--contrib/bmake/mk/prlist.mk4
-rw-r--r--contrib/bmake/mk/prog.mk4
-rwxr-xr-xcontrib/bmake/mk/stage-install.sh10
-rw-r--r--contrib/bmake/mk/sys.mk9
-rw-r--r--contrib/bmake/mk/sys.vars.mk4
-rw-r--r--contrib/bmake/mk/sys/SCO_SV.mk13
-rw-r--r--contrib/bmake/mk/sys/UnixWare.mk24
-rw-r--r--contrib/bmake/nonints.h9
-rw-r--r--contrib/bmake/os.sh4
-rw-r--r--contrib/bmake/parse.c171
-rw-r--r--contrib/bmake/sigact.h104
-rw-r--r--contrib/bmake/sigaction.c397
-rw-r--r--contrib/bmake/str.c9
-rw-r--r--contrib/bmake/str.h27
-rw-r--r--contrib/bmake/suff.c119
-rw-r--r--contrib/bmake/targ.c83
-rw-r--r--contrib/bmake/trace.c6
-rw-r--r--contrib/bmake/unit-tests/Makefile75
-rw-r--r--contrib/bmake/unit-tests/Makefile.config.in4
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric.exp4
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-numeric.mk18
-rw-r--r--contrib/bmake/unit-tests/cond-cmp-string.mk4
-rw-r--r--contrib/bmake/unit-tests/cond-eof.exp3
-rw-r--r--contrib/bmake/unit-tests/cond-eof.mk12
-rw-r--r--contrib/bmake/unit-tests/cond-func-defined.mk4
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.exp4
-rw-r--r--contrib/bmake/unit-tests/cond-func-empty.mk73
-rw-r--r--contrib/bmake/unit-tests/cond-func.exp2
-rw-r--r--contrib/bmake/unit-tests/cond-op-and.exp5
-rw-r--r--contrib/bmake/unit-tests/cond-op-and.mk30
-rw-r--r--contrib/bmake/unit-tests/cond-op-or.exp5
-rw-r--r--contrib/bmake/unit-tests/cond-op-or.mk30
-rw-r--r--contrib/bmake/unit-tests/cond-op.exp34
-rw-r--r--contrib/bmake/unit-tests/cond-op.mk39
-rw-r--r--contrib/bmake/unit-tests/cond-short.mk69
-rw-r--r--contrib/bmake/unit-tests/cond-token-plain.exp27
-rw-r--r--contrib/bmake/unit-tests/cond-token-plain.mk35
-rw-r--r--contrib/bmake/unit-tests/deptgt-default.exp1
-rw-r--r--contrib/bmake/unit-tests/deptgt-default.mk17
-rw-r--r--contrib/bmake/unit-tests/deptgt-makeflags.mk29
-rw-r--r--contrib/bmake/unit-tests/directive-else.exp6
-rw-r--r--contrib/bmake/unit-tests/directive-endif.exp8
-rw-r--r--contrib/bmake/unit-tests/directive-export-impl.exp4
-rw-r--r--contrib/bmake/unit-tests/directive-for-escape.exp63
-rw-r--r--contrib/bmake/unit-tests/directive-for-escape.mk49
-rw-r--r--contrib/bmake/unit-tests/directive-for-if.exp8
-rw-r--r--contrib/bmake/unit-tests/directive-for-if.mk86
-rw-r--r--contrib/bmake/unit-tests/directive-for-null.exp2
-rwxr-xr-xcontrib/bmake/unit-tests/directive-include.exp3
-rwxr-xr-xcontrib/bmake/unit-tests/directive-include.mk24
-rw-r--r--contrib/bmake/unit-tests/export.mk4
-rw-r--r--contrib/bmake/unit-tests/job-output-null.exp6
-rw-r--r--contrib/bmake/unit-tests/job-output-null.mk21
-rwxr-xr-xcontrib/bmake/unit-tests/lint.exp2
-rw-r--r--contrib/bmake/unit-tests/objdir-writable.exp2
-rw-r--r--contrib/bmake/unit-tests/objdir-writable.mk11
-rw-r--r--contrib/bmake/unit-tests/opt-debug-errors-jobs.exp10
-rw-r--r--contrib/bmake/unit-tests/opt-debug-errors-jobs.mk14
-rw-r--r--contrib/bmake/unit-tests/opt-debug-graph1.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-debug-graph2.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-debug-graph3.exp1
-rw-r--r--contrib/bmake/unit-tests/opt-file.mk12
-rw-r--r--contrib/bmake/unit-tests/opt-tracefile.exp11
-rw-r--r--contrib/bmake/unit-tests/opt-tracefile.mk18
-rw-r--r--contrib/bmake/unit-tests/suff-main-several.exp1
-rw-r--r--contrib/bmake/unit-tests/suff-transform-debug.exp1
-rw-r--r--contrib/bmake/unit-tests/var-eval-short.exp14
-rw-r--r--contrib/bmake/unit-tests/var-eval-short.mk18
-rw-r--r--contrib/bmake/unit-tests/var-op-expand.exp8
-rw-r--r--contrib/bmake/unit-tests/var-op-expand.mk105
-rw-r--r--contrib/bmake/unit-tests/vardebug.exp4
-rw-r--r--contrib/bmake/unit-tests/varmisc.mk12
-rw-r--r--contrib/bmake/unit-tests/varmod-assign.exp12
-rw-r--r--contrib/bmake/unit-tests/varmod-assign.mk107
-rw-r--r--contrib/bmake/unit-tests/varmod-defined.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-defined.mk5
-rw-r--r--contrib/bmake/unit-tests/varmod-gmtime.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-indirect.exp2
-rw-r--r--contrib/bmake/unit-tests/varmod-localtime.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-localtime.mk2
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-delete.exp4
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-delete.mk33
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-varname.exp16
-rw-r--r--contrib/bmake/unit-tests/varmod-loop-varname.mk7
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.exp6
-rw-r--r--contrib/bmake/unit-tests/varmod-loop.mk10
-rw-r--r--contrib/bmake/unit-tests/varmod-order-numeric.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-order-numeric.mk54
-rw-r--r--contrib/bmake/unit-tests/varmod-order-reverse.mk9
-rw-r--r--contrib/bmake/unit-tests/varmod-order-shuffle.mk19
-rw-r--r--contrib/bmake/unit-tests/varmod-order-string.exp1
-rw-r--r--contrib/bmake/unit-tests/varmod-order-string.mk28
-rw-r--r--contrib/bmake/unit-tests/varmod-order.exp25
-rw-r--r--contrib/bmake/unit-tests/varmod-order.mk91
-rw-r--r--contrib/bmake/unit-tests/varmod-root.exp10
-rw-r--r--contrib/bmake/unit-tests/varmod-root.mk37
-rw-r--r--contrib/bmake/unit-tests/varmod-select-words.mk5
-rw-r--r--contrib/bmake/unit-tests/varmod-subst.mk15
-rw-r--r--contrib/bmake/unit-tests/varmod-to-separator.exp4
-rw-r--r--contrib/bmake/unit-tests/varmod-unique.mk35
-rw-r--r--contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk130
-rw-r--r--contrib/bmake/unit-tests/varname-dot-suffixes.exp39
-rw-r--r--contrib/bmake/unit-tests/varname-dot-suffixes.mk104
-rw-r--r--contrib/bmake/unit-tests/varname-empty.exp4
-rw-r--r--contrib/bmake/util.c82
-rw-r--r--contrib/bmake/var.c631
166 files changed, 4798 insertions, 1888 deletions
diff --git a/contrib/bmake/ChangeLog b/contrib/bmake/ChangeLog
index 35235e1f8205..5eb2d7c5bb4a 100644
--- a/contrib/bmake/ChangeLog
+++ b/contrib/bmake/ChangeLog
@@ -1,3 +1,234 @@
+2021-12-15 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * cond.c: fix mem leak in CondParser_Leaf
+
+2021-12-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20211212
+ Merge with NetBSD make, pick up
+ o rename Parse_SetInput to Parse_PushInput
+ o remove remove period from end of error messages and warnings
+ to be more consistent
+ o arch.c: use simpler memory management for parsing archive members
+ o cond.c: rework and reduce recursion
+ o for.c: rename some functions to better reflect purpose
+ o suff.c: add Suff_NamesStr to provide .SUFFIXES as a string.
+ o var.c: in parse errors, mark whitespace more clearly
+ inline ParseEmptyArg into CondParser_FuncCallEmpty
+ minimize calls to LazyBuf_Get in ParseVarnameLong
+ treat .SUFFIXES as a read-only variable
+
+2021-12-07 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20211207
+ Merge with NetBSD make, pick up
+ o inline HashIter_Init
+ o parse.c: inline common subexpression in ParseRawLine
+ o var.c: merge branches for modifiers ':D' and ':U'
+ extract common code into Expr_Words
+ extract common code into Expr_Str
+ move low-level implementation details out of Var_Parse
+
+2021-12-06 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20211206
+ Merge with NetBSD make, pick up
+ o add unit-tests/varmod-loop-delete
+ o for.c: inline Str_Words - reduce memory allocation
+ o parse.c: do not try to expand fixed variable names
+ only allocate the name of an included file if necessary
+ clean up ParseInclude
+ o var.c: fix use-after-free in modifier ':@'
+ save a memory allocation in each modifier ':O' and ':u'
+ save a memory allocation in the modifier ':[...]'
+ in UnexportVars, replace Str_Words with Substring_Words to
+ reduce allocations and copying.
+
+2021-12-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20211204
+ Merge with NetBSD make, pick up
+ o flesh out a number of tests
+ o replace enums with bitfields, this simplifies a lot of code.
+ o var.c: refactor ParseModifierPartSubst
+
+2021-10-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20211024
+ Merge with NetBSD make, pick up
+ o Punt on write errors - ENOSPC etc.
+
+2021-10-22 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * configure.in: use_defshell, set both DEFSHELL_INDEX
+ and defshell_path if appropriate.
+ This makes it easier to use say the KSH specification with
+ and alternate path for the shell.
+
+ * configure.in compat.c: for SCO we need to force UseShell
+
+ * configure.in: SCO /bin/sh is not usable, provide a list of
+ alternatives for use as .SHELL.
+ We still have to mark some tests as broken, plus more if we end up
+ with ksh as .SHELL.
+ Issue a warning about skipped tests.
+
+ * boot-strap: leave TOOL_DIFF to configure
+
+ * configure.in: on SCO native cc is not usable,
+ gcc is to be found in /usr/gnu/bin
+ and while ancient is at least able to compile bmake.
+ Thus we add /usr/gnu/bin to PATH if it exists, and later
+ check if $CC would have been found via $PATH.
+ If not we set CC to the full path of $CC.
+ Also gnu diff is known to support -u, so if it exists use it.
+
+ * configure.in: move getopt to AC_REPLACE_FUNCS
+ also add AC_C_INLINE - in an attempt to compile using
+ native cc on SCO.
+
+ * configure.in: check for stresep as well as strsep, since we
+ define the later to the former if necessary, and if we have to
+ provide stresep we also need to provide a prototype.
+
+ * configure.in: we no longer need to worry about
+ sys/cdefs.h providing __RCSID which simplifies things quite a bit.
+
+ * make.h: make sure we have __RCSID
+
+ * unit-tests/Makefile.config.in: add TOOL_DIFF so configure
+ can control it.
+
+2021-10-20 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION: 20211020
+ Merge with NetBSD make, pick up
+ o confirm sync of unit-tests
+
+2021-10-18 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * configure.in: check if timezone Europe/Berlin is supported
+ if not try UTC-1
+ * configure.in: if .OBJDIR is $srcdir/obj we need to create a
+ symlink unit-tests -> ../unit-tests/obj so that
+ unit-tests/Makefile.config is put in the right place.
+ * refine filtering of .OBJDIR in unit-tests
+
+2021-10-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * Fix unit-tests on Minix 3.2.0
+ o job.c: do not punt if read of token pipe fails for EAGAIN.
+ On Minix at least, we are not ready to read the childExitJob pipe
+ when poll says we are.
+ There should actually be no reason for this pipe to be
+ non-blocking, but while that works fine on {Net,Free}BSD it
+ breaks another test case on Minix.
+ o unit-tests/Makefile: deal with variants of error messages
+ and use of obj as .OBJDIR
+
+2021-10-14 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * configure.in: add sigaction to AC_REPLACE_FUNCS
+ we also need to check for sigaddset etc just for the benefit of
+ sigact.c
+
+ * Add sigact.c as sigaction.c so this "just works".
+ This should have been done back when bmake_signal started using
+ sigaction (I only just noticed that sigact.c wasn't here ;-)
+ Note: I no longer have access to any system where this would matter.
+
+2021-10-13 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20211011
+
+ * Makefile: cleanup a little
+
+ * configure.in: check for sigsetmask
+
+2021-10-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20211001
+ Merge with NetBSD make, pick up
+ o reduce locations reducing text size
+ o remove unnecessary const
+ o cond.c: fix lint warning on i386
+ do not allow unquoted 'left == right' after modifier ':?'
+ o hash.c: fix build for DEBUG_HASH_LOOKUP
+ o var.c: fix memory leak in error case of the ':?' modifier
+
+2021-09-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210911
+ Merge with NetBSD make, pick up
+ o var.c: replace remaining ModChain_ShouldEval with Expr_ShouldEval
+
+2021-09-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210906
+ Merge with NetBSD make, pick up
+ o more unit tests
+ o lint cleanup
+ o rename some functions to better fit purpose
+ o for.c: cleanup - remove unnecessary optimization
+ fix embedded newlines
+ o parse.c: correct case for CVS/RCS
+
+2021-08-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210808
+ Merge with NetBSD make, pick up
+ o var.c: remove redundant initialization in ApplyModifier_Order
+
+ * mk/options.mk: issue warning for incorrect usage
+
+2021-08-03 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * var.c: use long for :On if we don't have a 64bit int type
+
+ * VERSION (_MAKE_VERSION): 20210803
+ Merge with NetBSD make, pick up
+ o rework varmod-order tests to avoid qsort instability
+ o make.1: clarify :On entry
+
+2021-07-31 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210731
+ Merge with NetBSD make, pick up
+ o fix some lint issues
+ o more unit tests
+ o var.c: rework of ApplyModifier_Order
+
+2021-07-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * util.c: add strto*l if HAVE_STRTO*L not defined
+
+ * VERSION (_MAKE_VERSION): 20210730
+ Merge with NetBSD make, pick up
+ o var.c: add :On and :Orn for numeric sort
+ disabled if no 64bit type available.
+ o _strtol.h: to implement strto*l functions
+
+2021-07-04 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210704
+ Merge with NetBSD make, pick up
+ o unit-tests: fix some tests to be more portable
+ - job-output-null not all shells do the same number of write calls
+ - objdir-writable if TMPDIR is set; /tmp may not be usable
+
+2021-07-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * VERSION (_MAKE_VERSION): 20210701
+ Merge with NetBSD make, pick up
+ o unit-tests: allow for BROKEN_TESTS to list TESTS to be skipped;
+ some tests just cannot work in some environments.
+ o buf.c: simpler upper bound for length in Buf_AddInt
+ o cond.c: fix grammar in error message for malformed conditional
+ o for.c: prevent newline injection (from ${.newline}) in .for loops
+ o var.c: use more practical data type in RegexReplace
+ (avoid need for %zu)
+ extract RegexReplace from ModifyWord_SubstRegex
+
2021-06-21 Simon J Gerraty <sjg@beast.crufty.net>
* VERSION (_MAKE_VERSION): 20210621
diff --git a/contrib/bmake/FILES b/contrib/bmake/FILES
index dc0f6f33c763..53b961468db6 100644
--- a/contrib/bmake/FILES
+++ b/contrib/bmake/FILES
@@ -7,6 +7,7 @@ PSD.doc/Makefile
PSD.doc/tutorial.ms
README
VERSION
+_strtol.h
aclocal.m4
arch.c
bmake.1
@@ -23,8 +24,6 @@ configure.in
dir.c
dir.h
dirname.c
-enum.c
-enum.h
filemon/filemon.h
filemon/filemon_dev.c
filemon/filemon_ktrace.c
@@ -61,6 +60,8 @@ pathnames.h
ranlib.h
realpath.c
setenv.c
+sigact.h
+sigaction.c
sigcompat.c
str.c
str.h
@@ -322,6 +323,8 @@ unit-tests/directive-for-escape.exp
unit-tests/directive-for-escape.mk
unit-tests/directive-for-generating-endif.exp
unit-tests/directive-for-generating-endif.mk
+unit-tests/directive-for-if.exp
+unit-tests/directive-for-if.mk
unit-tests/directive-for-lines.exp
unit-tests/directive-for-lines.mk
unit-tests/directive-for-null.exp
@@ -684,6 +687,8 @@ unit-tests/varmod-l-name-to-value.exp
unit-tests/varmod-l-name-to-value.mk
unit-tests/varmod-localtime.exp
unit-tests/varmod-localtime.mk
+unit-tests/varmod-loop-delete.exp
+unit-tests/varmod-loop-delete.mk
unit-tests/varmod-loop-varname.exp
unit-tests/varmod-loop-varname.mk
unit-tests/varmod-loop.exp
@@ -694,10 +699,14 @@ unit-tests/varmod-match.exp
unit-tests/varmod-match.mk
unit-tests/varmod-no-match.exp
unit-tests/varmod-no-match.mk
+unit-tests/varmod-order-numeric.exp
+unit-tests/varmod-order-numeric.mk
unit-tests/varmod-order-reverse.exp
unit-tests/varmod-order-reverse.mk
unit-tests/varmod-order-shuffle.exp
unit-tests/varmod-order-shuffle.mk
+unit-tests/varmod-order-string.exp
+unit-tests/varmod-order-string.mk
unit-tests/varmod-order.exp
unit-tests/varmod-order.mk
unit-tests/varmod-path.exp
@@ -814,6 +823,8 @@ unit-tests/varname-dot-path.exp
unit-tests/varname-dot-path.mk
unit-tests/varname-dot-shell.exp
unit-tests/varname-dot-shell.mk
+unit-tests/varname-dot-suffixes.exp
+unit-tests/varname-dot-suffixes.mk
unit-tests/varname-dot-targets.exp
unit-tests/varname-dot-targets.mk
unit-tests/varname-empty.exp
diff --git a/contrib/bmake/Makefile b/contrib/bmake/Makefile
index 38ccb8a6a636..82d9db52af76 100644
--- a/contrib/bmake/Makefile
+++ b/contrib/bmake/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.114 2020/11/13 21:47:25 sjg Exp $
+# $Id: Makefile,v 1.117 2021/12/04 18:51:30 sjg Exp $
PROG= bmake
@@ -8,7 +8,6 @@ SRCS= \
compat.c \
cond.c \
dir.c \
- enum.c \
for.c \
hash.c \
job.c \
@@ -92,10 +91,11 @@ isBSD44:=${BSD44_LIST:M${OS}}
.if ${isBSD44} == ""
MANTARGET= cat
INSTALL?=${srcdir}/install-sh
-.if (${MACHINE} == "sun386")
+.if ${MACHINE} == "sun386"
# even I don't have one of these anymore :-)
CFLAGS+= -DPORTAR
-.elif (${MACHINE} != "sunos")
+.elif ${OS} != "SunOS"
+# assume the worst
SRCS+= sigcompat.c
CFLAGS+= -DSIGNAL_FLAGS=SA_RESTART
.endif
@@ -131,7 +131,7 @@ EXTRACT_MAN=no
MAN= ${PROG}.1
MAN1= ${MAN}
-.if (${PROG} != "make")
+.if ${PROG} != "make"
CLEANFILES+= my.history
.if make(${MAN}) || !exists(${srcdir}/${MAN})
my.history:
@@ -189,11 +189,12 @@ main.o: ${srcdir}/VERSION
CONFIGURE_DEPS += ${.CURDIR}/VERSION
# we do not need or want the generated makefile
CONFIGURE_ARGS += --without-makefile
+AUTOCONF_GENERATED_MAKEFILE = Makefile.config
.include <autoconf.mk>
.endif
-SHARE_MK?=${SHAREDIR}/mk
-MKSRC=${srcdir}/mk
-INSTALL?=${srcdir}/install-sh
+SHARE_MK ?= ${SHAREDIR}/mk
+MKSRC = ${srcdir}/mk
+INSTALL ?= ${srcdir}/install-sh
.if ${MK_INSTALL_MK} == "yes"
install: install-mk
diff --git a/contrib/bmake/Makefile.config.in b/contrib/bmake/Makefile.config.in
index 55cd60ca80ba..042b2570ff88 100644
--- a/contrib/bmake/Makefile.config.in
+++ b/contrib/bmake/Makefile.config.in
@@ -4,7 +4,7 @@ _MAKE_VERSION?=@_MAKE_VERSION@
prefix?= @prefix@
srcdir= @srcdir@
-CC?= @CC@
+CC= @CC@
@force_machine@MACHINE?= @machine@
@force_machine_arch@MACHINE_ARCH?= @machine_arch@
DEFAULT_SYS_PATH?= @default_sys_path@
diff --git a/contrib/bmake/VERSION b/contrib/bmake/VERSION
index 7c28f11013b7..8485c4810bf0 100644
--- a/contrib/bmake/VERSION
+++ b/contrib/bmake/VERSION
@@ -1,2 +1,2 @@
# keep this compatible with sh and make
-_MAKE_VERSION=20210621
+_MAKE_VERSION=20211212
diff --git a/contrib/bmake/_strtol.h b/contrib/bmake/_strtol.h
new file mode 100644
index 000000000000..51c71490ae57
--- /dev/null
+++ b/contrib/bmake/_strtol.h
@@ -0,0 +1,213 @@
+/* $NetBSD: _strtol.h,v 1.11 2017/07/06 21:08:44 joerg Exp $ */
+
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Original version ID:
+ * NetBSD: src/lib/libc/locale/_wcstol.h,v 1.2 2003/08/07 16:43:03 agc Exp
+ */
+
+/*
+ * function template for strtol, strtoll and strtoimax.
+ *
+ * parameters:
+ * _FUNCNAME : function name
+ * __INT : return type
+ * __INT_MIN : lower limit of the return type
+ * __INT_MAX : upper limit of the return type
+ */
+#if defined(_KERNEL) || defined(_STANDALONE) || defined(HAVE_NBTOOL_CONFIG_H) || defined(BCS_ONLY)
+__INT
+_FUNCNAME(const char *nptr, char **endptr, int base)
+#else
+#include <locale.h>
+#include "setlocale_local.h"
+#define INT_FUNCNAME_(pre, name, post) pre ## name ## post
+#define INT_FUNCNAME(pre, name, post) INT_FUNCNAME_(pre, name, post)
+
+static __INT
+INT_FUNCNAME(_int_, _FUNCNAME, _l)(const char *nptr, char **endptr,
+ int base, locale_t loc)
+#endif
+{
+ const char *s;
+ __INT acc, cutoff;
+ unsigned char c;
+ int i, neg, any, cutlim;
+
+ _DIAGASSERT(nptr != NULL);
+ /* endptr may be NULL */
+
+ /* check base value */
+ if (base && (base < 2 || base > 36)) {
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+ errno = EINVAL;
+ if (endptr != NULL)
+ /* LINTED interface specification */
+ *endptr = __UNCONST(nptr);
+ return 0;
+#else
+ panic("%s: invalid base %d", __func__, base);
+#endif
+ }
+
+ /*
+ * Skip white space and pick up leading +/- sign if any.
+ * If base is 0, allow 0x for hex and 0 for octal, else
+ * assume decimal; if base is already 16, allow 0x.
+ */
+ s = nptr;
+#if defined(_KERNEL) || defined(_STANDALONE) || \
+ defined(HAVE_NBTOOL_CONFIG_H) || defined(BCS_ONLY)
+ do {
+ c = *s++;
+ } while (isspace(c));
+#else
+ do {
+ c = *s++;
+ } while (isspace_l(c, loc));
+#endif
+ if (c == '-') {
+ neg = 1;
+ c = *s++;
+ } else {
+ neg = 0;
+ if (c == '+')
+ c = *s++;
+ }
+ if ((base == 0 || base == 16) &&
+ c == '0' && (*s == 'x' || *s == 'X') &&
+ ((s[1] >= '0' && s[1] <= '9') ||
+ (s[1] >= 'a' && s[1] <= 'f') ||
+ (s[1] >= 'A' && s[1] <= 'F'))) {
+ c = s[1];
+ s += 2;
+ base = 16;
+#if 0
+ } else if ((base == 0 || base == 2) &&
+ c == '0' && (*s == 'b' || *s == 'B') &&
+ (s[1] >= '0' && s[1] <= '1')) {
+ c = s[1];
+ s += 2;
+ base = 2;
+#endif
+ } else if (base == 0)
+ base = (c == '0' ? 8 : 10);
+
+ /*
+ * Compute the cutoff value between legal numbers and illegal
+ * numbers. That is the largest legal value, divided by the
+ * base. An input number that is greater than this value, if
+ * followed by a legal input character, is too big. One that
+ * is equal to this value may be valid or not; the limit
+ * between valid and invalid numbers is then based on the last
+ * digit. For instance, if the range for longs is
+ * [-2147483648..2147483647] and the input base is 10,
+ * cutoff will be set to 214748364 and cutlim to either
+ * 7 (neg==0) or 8 (neg==1), meaning that if we have accumulated
+ * a value > 214748364, or equal but the next digit is > 7 (or 8),
+ * the number is too big, and we will return a range error.
+ *
+ * Set any if any `digits' consumed; make it negative to indicate
+ * overflow.
+ */
+ cutoff = (__INT)(neg ? __INT_MIN : __INT_MAX);
+ cutlim = (int)(cutoff % base);
+ cutoff /= base;
+ if (neg) {
+ if (cutlim > 0) {
+ cutlim -= base;
+ cutoff += 1;
+ }
+ cutlim = -cutlim;
+ }
+ for (acc = 0, any = 0;; c = *s++) {
+ if (c >= '0' && c <= '9')
+ i = c - '0';
+ else if (c >= 'a' && c <= 'z')
+ i = (c - 'a') + 10;
+ else if (c >= 'A' && c <= 'Z')
+ i = (c - 'A') + 10;
+ else
+ break;
+ if (i >= base)
+ break;
+ if (any < 0)
+ continue;
+ if (neg) {
+ if (acc < cutoff || (acc == cutoff && i > cutlim)) {
+ acc = __INT_MIN;
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+ any = -1;
+ errno = ERANGE;
+#else
+ any = 0;
+ break;
+#endif
+ } else {
+ any = 1;
+ acc *= base;
+ acc -= i;
+ }
+ } else {
+ if (acc > cutoff || (acc == cutoff && i > cutlim)) {
+ acc = __INT_MAX;
+#if !defined(_KERNEL) && !defined(_STANDALONE)
+ any = -1;
+ errno = ERANGE;
+#else
+ any = 0;
+ break;
+#endif
+ } else {
+ any = 1;
+ acc *= base;
+ acc += i;
+ }
+ }
+ }
+ if (endptr != NULL)
+ /* LINTED interface specification */
+ *endptr = __UNCONST(any ? s - 1 : nptr);
+ return(acc);
+}
+
+#if !defined(_KERNEL) && !defined(_STANDALONE) && \
+ !defined(HAVE_NBTOOL_CONFIG_H) && !defined(BCS_ONLY)
+__INT
+_FUNCNAME(const char *nptr, char **endptr, int base)
+{
+ return INT_FUNCNAME(_int_, _FUNCNAME, _l)(nptr, endptr, base, _current_locale());
+}
+
+__INT
+INT_FUNCNAME(, _FUNCNAME, _l)(const char *nptr, char **endptr, int base, locale_t loc)
+{
+ return INT_FUNCNAME(_int_, _FUNCNAME, _l)(nptr, endptr, base, loc);
+}
+#endif
diff --git a/contrib/bmake/arch.c b/contrib/bmake/arch.c
index 6d9dd60dfbe9..515713af7af7 100644
--- a/contrib/bmake/arch.c
+++ b/contrib/bmake/arch.c
@@ -1,4 +1,4 @@
-/* $NetBSD: arch.c,v 1.200 2021/05/30 21:16:54 rillig Exp $ */
+/* $NetBSD: arch.c,v 1.205 2021/12/12 22:41:47 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.200 2021/05/30 21:16:54 rillig Exp $");
+MAKE_RCSID("$NetBSD: arch.c,v 1.205 2021/12/12 22:41:47 rillig Exp $");
typedef struct List ArchList;
typedef struct ListNode ArchListNode;
@@ -250,7 +250,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
char *cp; /* Pointer into line */
GNode *gn; /* New node */
MFStr libName; /* Library-part of specification */
- char *memName; /* Member-part of specification */
+ FStr mem; /* Member-part of specification */
char saveChar; /* Ending delimiter of member-name */
bool expandLibName; /* Whether the parsed libName contains
* variable expressions that need to be
@@ -301,7 +301,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
pp_skip_whitespace(&cp);
- memName = cp;
+ mem = FStr_InitRefer(cp);
while (*cp != '\0' && *cp != ')' && !ch_isspace(*cp)) {
if (*cp == '$') {
/* Expand nested variable expressions. */
@@ -342,7 +342,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
/*
* If we didn't move anywhere, we must be done
*/
- if (cp == memName)
+ if (cp == mem.str)
break;
saveChar = *cp;
@@ -363,22 +363,22 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
*/
if (doSubst) {
char *fullName;
- char *p;
- char *unexpandedMemName = memName;
+ char *p, *expandedMem;
+ const char *unexpandedMem = mem.str;
- (void)Var_Subst(memName, scope, VARE_UNDEFERR,
- &memName);
+ (void)Var_Subst(mem.str, scope, VARE_UNDEFERR,
+ &expandedMem);
/* TODO: handle errors */
+ mem = FStr_InitOwn(expandedMem);
/*
* Now form an archive spec and recurse to deal with
* nested variables and multi-word variable values.
*/
- fullName = FullName(libName.str, memName);
+ fullName = FullName(libName.str, mem.str);
p = fullName;
- if (strchr(memName, '$') != NULL &&
- strcmp(memName, unexpandedMemName) == 0) {
+ if (strcmp(mem.str, unexpandedMem) == 0) {
/*
* Must contain dynamic sources, so we can't
* deal with it now. Just create an ARCHV node
@@ -398,9 +398,9 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
free(fullName);
/* XXX: does unexpandedMemName leak? */
- } else if (Dir_HasWildcards(memName)) {
+ } else if (Dir_HasWildcards(mem.str)) {
StringList members = LST_INIT;
- SearchPath_Expand(&dirSearchPath, memName, &members);
+ SearchPath_Expand(&dirSearchPath, mem.str, &members);
while (!Lst_IsEmpty(&members)) {
char *member = Lst_Dequeue(&members);
@@ -416,7 +416,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
Lst_Done(&members);
} else {
- char *fullname = FullName(libName.str, memName);
+ char *fullname = FullName(libName.str, mem.str);
gn = Targ_GetNode(fullname);
free(fullname);
@@ -430,8 +430,7 @@ Arch_ParseArchive(char **pp, GNodeList *gns, GNode *scope)
gn->type |= OP_ARCHV;
Lst_Append(gns, gn);
}
- if (doSubst)
- free(memName);
+ FStr_Done(&mem);
*cp = saveChar;
}
@@ -600,14 +599,14 @@ ArchStatMember(const char *archive, const char *member, bool addToCache)
if (strncmp(memName, AR_EFMT1, sizeof AR_EFMT1 - 1) == 0 &&
ch_isdigit(memName[sizeof AR_EFMT1 - 1])) {
- int elen = atoi(memName + sizeof AR_EFMT1 - 1);
+ size_t elen = atoi(memName + sizeof AR_EFMT1 - 1);
- if ((unsigned int)elen > MAXPATHLEN)
+ if (elen > MAXPATHLEN)
goto badarch;
- if (fread(memName, (size_t)elen, 1, arch) != 1)
+ if (fread(memName, elen, 1, arch) != 1)
goto badarch;
memName[elen] = '\0';
- if (fseek(arch, -elen, SEEK_CUR) != 0)
+ if (fseek(arch, -(long)elen, SEEK_CUR) != 0)
goto badarch;
if (DEBUG(ARCH) || DEBUG(MAKE))
debug_printf(
@@ -839,14 +838,15 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
if (strncmp(out_arh->AR_NAME, AR_EFMT1, sizeof AR_EFMT1 - 1) ==
0 &&
(ch_isdigit(out_arh->AR_NAME[sizeof AR_EFMT1 - 1]))) {
- int elen = atoi(&out_arh->AR_NAME[sizeof AR_EFMT1 - 1]);
+ size_t elen = atoi(
+ &out_arh->AR_NAME[sizeof AR_EFMT1 - 1]);
char ename[MAXPATHLEN + 1];
- if ((unsigned int)elen > MAXPATHLEN) {
+ if (elen > MAXPATHLEN) {
fclose(arch);
return NULL;
}
- if (fread(ename, (size_t)elen, 1, arch) != 1) {
+ if (fread(ename, elen, 1, arch) != 1) {
fclose(arch);
return NULL;
}
@@ -859,14 +859,14 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
if (strncmp(ename, member, len) == 0) {
/* Found as extended name */
if (fseek(arch,
- -(long)sizeof(struct ar_hdr) - elen,
- SEEK_CUR) != 0) {
+ -(long)(sizeof(struct ar_hdr) - elen),
+ SEEK_CUR) != 0) {
fclose(arch);
return NULL;
}
return arch;
}
- if (fseek(arch, -elen, SEEK_CUR) != 0) {
+ if (fseek(arch, -(long)elen, SEEK_CUR) != 0) {
fclose(arch);
return NULL;
}
@@ -882,7 +882,7 @@ ArchFindMember(const char *archive, const char *member, struct ar_hdr *out_arh,
*/
out_arh->AR_SIZE[sizeof out_arh->AR_SIZE - 1] = '\0';
size = (int)strtol(out_arh->AR_SIZE, NULL, 10);
- if (fseek(arch, (size + 1) & ~1, SEEK_CUR) != 0) {
+ if (fseek(arch, (size + 1) & ~1L, SEEK_CUR) != 0) {
fclose(arch);
return NULL;
}
@@ -991,12 +991,12 @@ Arch_UpdateMemberMTime(GNode *gn)
const char *nameEnd = strchr(nameStart, ')');
size_t nameLen = (size_t)(nameEnd - nameStart);
- if ((pgn->flags & REMAKE) &&
+ if (pgn->flags.remake &&
strncmp(nameStart, gn->name, nameLen) == 0) {
Arch_UpdateMTime(pgn);
gn->mtime = pgn->mtime;
}
- } else if (pgn->flags & REMAKE) {
+ } else if (pgn->flags.remake) {
/*
* Something which isn't a library depends on the
* existence of this target, so it needs to exist.
@@ -1036,6 +1036,35 @@ Arch_FindLib(GNode *gn, SearchPath *path)
#endif
}
+/* ARGSUSED */
+static bool
+RanlibOODate(const GNode *gn MAKE_ATTR_UNUSED)
+{
+#ifdef RANLIBMAG
+ struct ar_hdr *arh; /* Header for __.SYMDEF */
+ int tocModTime; /* The table-of-contents' mod time */
+
+ arh = ArchStatMember(gn->path, RANLIBMAG, false);
+
+ if (arh == NULL) {
+ /* A library without a table of contents is out-of-date. */
+ if (DEBUG(ARCH) || DEBUG(MAKE))
+ debug_printf("no toc...");
+ return true;
+ }
+
+ tocModTime = (int)strtol(arh->ar_date, NULL, 10);
+
+ if (DEBUG(ARCH) || DEBUG(MAKE))
+ debug_printf("%s modified %s...",
+ RANLIBMAG, Targ_FmtTime(tocModTime));
+ return gn->youngestChild == NULL ||
+ gn->youngestChild->mtime > tocModTime;
+#else
+ return false;
+#endif
+}
+
/*
* Decide if a node with the OP_LIB attribute is out-of-date. Called from
* GNode_IsOODate to make its life easier.
@@ -1069,46 +1098,19 @@ Arch_FindLib(GNode *gn, SearchPath *path)
bool
Arch_LibOODate(GNode *gn)
{
- bool oodate;
if (gn->type & OP_PHONY) {
- oodate = true;
+ return true;
} else if (!GNode_IsTarget(gn) && Lst_IsEmpty(&gn->children)) {
- oodate = false;
+ return false;
} else if ((!Lst_IsEmpty(&gn->children) && gn->youngestChild == NULL) ||
(gn->mtime > now) ||
(gn->youngestChild != NULL &&
gn->mtime < gn->youngestChild->mtime)) {
- oodate = true;
+ return true;
} else {
-#ifdef RANLIBMAG
- struct ar_hdr *arh; /* Header for __.SYMDEF */
- int modTimeTOC; /* The table-of-contents' mod time */
-
- arh = ArchStatMember(gn->path, RANLIBMAG, false);
-
- if (arh != NULL) {
- modTimeTOC = (int)strtol(arh->ar_date, NULL, 10);
-
- if (DEBUG(ARCH) || DEBUG(MAKE))
- debug_printf("%s modified %s...",
- RANLIBMAG,
- Targ_FmtTime(modTimeTOC));
- oodate = gn->youngestChild == NULL ||
- gn->youngestChild->mtime > modTimeTOC;
- } else {
- /*
- * A library without a table of contents is out-of-date.
- */
- if (DEBUG(ARCH) || DEBUG(MAKE))
- debug_printf("no toc...");
- oodate = true;
- }
-#else
- oodate = false;
-#endif
+ return RanlibOODate(gn);
}
- return oodate;
}
/* Initialize the archives module. */
diff --git a/contrib/bmake/bmake.1 b/contrib/bmake/bmake.1
index e0d8267994c1..daea73cf538d 100644
--- a/contrib/bmake/bmake.1
+++ b/contrib/bmake/bmake.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $
+.\" $NetBSD: make.1,v 1.300 2021/12/12 20:45:48 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 December 22, 2020
+.Dd December 12, 2021
.Dt BMAKE 1
.Os
.Sh NAME
@@ -1163,6 +1163,9 @@ executes.
.It Ev .SHELL
The pathname of the shell used to run target scripts.
It is read-only.
+.It Ev .SUFFIXES
+The list of known suffixes.
+It is read-only.
.It Ev .TARGETS
The list of targets explicitly specified on the command line, if any.
.It Ev VPATH
@@ -1233,8 +1236,20 @@ but selects all words which do not match
.Ar pattern .
.It Cm \&:O
Orders every word in variable alphabetically.
+.It Cm \&:On
+Orders every word in variable numerically.
+A number followed by one of
+.Ql k ,
+.Ql M
+or
+.Ql G
+is multiplied by the appropriate factor (1024 (k), 1048576 (M), or
+1073741824 (G)).
+Both upper- and lower-case letters are accepted.
.It Cm \&:Or
Orders every word in variable in reverse alphabetical order.
+.It Cm \&:Orn
+Orders every word in variable in reverse numerical order.
.It Cm \&:Ox
Shuffles the words in variable.
The results will be different each time you are referring to the
diff --git a/contrib/bmake/bmake.cat1 b/contrib/bmake/bmake.cat1
index d9723b35cf7f..73191dea7344 100644
--- a/contrib/bmake/bmake.cat1
+++ b/contrib/bmake/bmake.cat1
@@ -757,6 +757,8 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
.SHELL The pathname of the shell used to run target scripts. It
is read-only.
+ .SUFFIXES The list of known suffixes. It is read-only.
+
.TARGETS The list of targets explicitly specified on the command
line, if any.
@@ -809,8 +811,16 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
:O Orders every word in variable alphabetically.
+ :On Orders every word in variable numerically. A number followed by one
+ of `k', `M' or `G' is multiplied by the appropriate factor (1024
+ (k), 1048576 (M), or 1073741824 (G)). Both upper- and lower-case
+ letters are accepted.
+
:Or Orders every word in variable in reverse alphabetical order.
+ :Orn
+ Orders every word in variable in reverse numerical order.
+
:Ox Shuffles the words in variable. The results will be different each
time you are referring to the modified variable; use the assignment
with expansion (`:=') to prevent such behavior. For example,
@@ -1581,4 +1591,4 @@ BMAKE(1) FreeBSD General Commands Manual BMAKE(1)
There is no way of escaping a space character in a filename.
-FreeBSD 13.0 December 22, 2020 FreeBSD 13.0
+FreeBSD 13.0 December 12, 2021 FreeBSD 13.0
diff --git a/contrib/bmake/boot-strap b/contrib/bmake/boot-strap
index d251649db670..40984edcbe62 100755
--- a/contrib/bmake/boot-strap
+++ b/contrib/bmake/boot-strap
@@ -119,7 +119,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: boot-strap,v 1.54 2020/11/13 21:47:25 sjg Exp $
+# $Id: boot-strap,v 1.57 2021/10/22 20:32:21 sjg Exp $
#
# @(#) Copyright (c) 2001 Simon J. Gerraty
#
@@ -405,17 +405,6 @@ Bmake() {
)
}
-# there is actually a shell where type is not a builtin
-# if type is missing, which(1) had better exists!
-if (type cat) > /dev/null 2>&1; then
-which() {
- type "$@" | sed 's,[()],,g;s,^[^/][^/]*,,;q'
-}
-fi
-# make sure test below uses the same diff that configure did
-TOOL_DIFF=`which diff`
-export TOOL_DIFF
-
op_configure() {
$srcdir/configure $CONFIGURE_ARGS || exit 1
}
@@ -431,7 +420,7 @@ op_build() {
op_test() {
[ -x bmake ] || op_build
- Bmake test || exit 1
+ Bmake test "$@" || exit 1
}
op_clean() {
@@ -483,5 +472,5 @@ You may need the -r or -R option to more/less to view it correctly.
EOM
}
-op_$op
+op_$op "$@"
exit 0
diff --git a/contrib/bmake/bsd.after-import.mk b/contrib/bmake/bsd.after-import.mk
index 0d48f3c26648..fec42c9deeed 100644
--- a/contrib/bmake/bsd.after-import.mk
+++ b/contrib/bmake/bsd.after-import.mk
@@ -1,4 +1,4 @@
-# $Id: bsd.after-import.mk,v 1.16 2020/07/12 03:39:01 sjg Exp $
+# $Id: bsd.after-import.mk,v 1.17 2021/10/22 06:31:32 sjg Exp $
# This makefile is for use when integrating bmake into a BSD build
# system. Use this makefile after importing bmake.
@@ -59,6 +59,7 @@ bootstrap: ${BMAKE_SRC}/boot-strap ${MAKEFILE}
# Makefiles need a little more tweaking than say config.h
MAKEFILE_SED = sed -e '/^MACHINE/d' \
-e '/include.*VERSION/d' \
+ -e '/^CC=/s,=,?=,' \
-e '/^PROG/ { s,=,?=,;s,bmake,$${.CURDIR:T},; }' \
-e 's,^.-include,.sinclude,' \
-e '/^\..*include *</ { s,<,<bsd.,;/autoconf/d; }' \
diff --git a/contrib/bmake/buf.c b/contrib/bmake/buf.c
index cef082247278..8c26cfa24955 100644
--- a/contrib/bmake/buf.c
+++ b/contrib/bmake/buf.c
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.c,v 1.51 2021/01/30 21:18:14 rillig Exp $ */
+/* $NetBSD: buf.c,v 1.53 2021/11/28 22:48:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -75,7 +75,7 @@
#include "make.h"
/* "@(#)buf.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: buf.c,v 1.51 2021/01/30 21:18:14 rillig Exp $");
+MAKE_RCSID("$NetBSD: buf.c,v 1.53 2021/11/28 22:48:06 rillig Exp $");
/* Make space in the buffer for adding at least 16 more bytes. */
void
@@ -122,19 +122,22 @@ Buf_AddStr(Buffer *buf, const char *str)
void
Buf_AddInt(Buffer *buf, int n)
{
- enum {
- bits = sizeof(int) * CHAR_BIT,
- max_octal_digits = (bits + 2) / 3,
- max_decimal_digits = /* at most */ max_octal_digits,
- max_sign_chars = 1,
- str_size = max_sign_chars + max_decimal_digits + 1
- };
- char str[str_size];
+ char str[sizeof(int) * CHAR_BIT + 1];
size_t len = (size_t)snprintf(str, sizeof str, "%d", n);
Buf_AddBytes(buf, str, len);
}
+void
+Buf_AddFlag(Buffer *buf, bool flag, const char *name)
+{
+ if (flag) {
+ if (buf->len > 0)
+ Buf_AddByte(buf, '|');
+ Buf_AddBytes(buf, name, strlen(name));
+ }
+}
+
/* Mark the buffer as empty, so it can be filled with data again. */
void
Buf_Empty(Buffer *buf)
diff --git a/contrib/bmake/buf.h b/contrib/bmake/buf.h
index 938820e4745f..af36773405b0 100644
--- a/contrib/bmake/buf.h
+++ b/contrib/bmake/buf.h
@@ -1,4 +1,4 @@
-/* $NetBSD: buf.h,v 1.43 2021/04/03 11:08:40 rillig Exp $ */
+/* $NetBSD: buf.h,v 1.44 2021/11/28 22:48:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -111,6 +111,7 @@ void Buf_AddBytes(Buffer *, const char *, size_t);
void Buf_AddBytesBetween(Buffer *, const char *, const char *);
void Buf_AddStr(Buffer *, const char *);
void Buf_AddInt(Buffer *, int);
+void Buf_AddFlag(Buffer *, bool, const char *);
void Buf_Empty(Buffer *);
void Buf_Init(Buffer *);
void Buf_InitSize(Buffer *, size_t);
diff --git a/contrib/bmake/compat.c b/contrib/bmake/compat.c
index f8c47397f3df..df487dbdf254 100644
--- a/contrib/bmake/compat.c
+++ b/contrib/bmake/compat.c
@@ -1,4 +1,4 @@
-/* $NetBSD: compat.c,v 1.227 2021/04/27 15:19:25 christos Exp $ */
+/* $NetBSD: compat.c,v 1.229 2021/11/28 23:12:51 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -99,7 +99,7 @@
#include "pathnames.h"
/* "@(#)compat.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: compat.c,v 1.227 2021/04/27 15:19:25 christos Exp $");
+MAKE_RCSID("$NetBSD: compat.c,v 1.229 2021/11/28 23:12:51 rillig Exp $");
static GNode *curTarg = NULL;
static pid_t compatChild;
@@ -187,7 +187,7 @@ DebugFailedTarget(const char *cmd, const GNode *gn)
static bool
UseShell(const char *cmd MAKE_ATTR_UNUSED)
{
-#if !defined(MAKE_NATIVE)
+#if defined(FORCE_USE_SHELL) || !defined(MAKE_NATIVE)
/*
* In a non-native build, the host environment might be weird enough
* that it's necessary to go through a shell to get the correct
@@ -240,7 +240,7 @@ Compat_RunCommand(const char *cmdp, GNode *gn, StringListNode *ln)
* using a shell */
const char *volatile cmd = cmdp;
- silent = (gn->type & OP_SILENT) != 0;
+ silent = (gn->type & OP_SILENT) != OP_NONE;
errCheck = !(gn->type & OP_IGNORE);
doIt = false;
@@ -497,7 +497,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
* again. This is our signal to not attempt to do anything but abort
* our parent as well.
*/
- gn->flags |= REMAKE;
+ gn->flags.remake = true;
gn->made = BEINGMADE;
if (!(gn->type & OP_MADE))
@@ -505,9 +505,9 @@ MakeUnmade(GNode *gn, GNode *pgn)
MakeNodes(&gn->children, gn);
- if (!(gn->flags & REMAKE)) {
+ if (!gn->flags.remake) {
gn->made = ABORTED;
- pgn->flags &= ~(unsigned)REMAKE;
+ pgn->flags.remake = false;
return false;
}
@@ -565,7 +565,7 @@ MakeUnmade(GNode *gn, GNode *pgn)
RunCommands(gn);
curTarg = NULL;
} else {
- Job_Touch(gn, (gn->type & OP_SILENT) != 0);
+ Job_Touch(gn, (gn->type & OP_SILENT) != OP_NONE);
}
} else {
gn->made = ERROR;
@@ -585,13 +585,13 @@ MakeUnmade(GNode *gn, GNode *pgn)
*/
gn->made = MADE;
if (Make_Recheck(gn) == 0)
- pgn->flags |= FORCE;
+ pgn->flags.force = true;
if (!(gn->type & OP_EXEC)) {
- pgn->flags |= CHILDMADE;
+ pgn->flags.childMade = true;
GNode_UpdateYoungestChild(pgn, gn);
}
} else if (opts.keepgoing) {
- pgn->flags &= ~(unsigned)REMAKE;
+ pgn->flags.remake = false;
} else {
PrintOnError(gn, "\nStop.");
exit(1);
@@ -612,11 +612,11 @@ MakeOther(GNode *gn, GNode *pgn)
case BEINGMADE:
Error("Graph cycles through %s", gn->name);
gn->made = ERROR;
- pgn->flags &= ~(unsigned)REMAKE;
+ pgn->flags.remake = false;
break;
case MADE:
if (!(gn->type & OP_EXEC)) {
- pgn->flags |= CHILDMADE;
+ pgn->flags.childMade = true;
GNode_UpdateYoungestChild(pgn, gn);
}
break;
@@ -663,7 +663,7 @@ Compat_Make(GNode *gn, GNode *pgn)
* Already had an error when making this.
* Tell the parent to abort.
*/
- pgn->flags &= ~(unsigned)REMAKE;
+ pgn->flags.remake = false;
} else {
MakeOther(gn, pgn);
}
diff --git a/contrib/bmake/cond.c b/contrib/bmake/cond.c
index a8d88d1d6816..96609682866b 100644
--- a/contrib/bmake/cond.c
+++ b/contrib/bmake/cond.c
@@ -1,4 +1,4 @@
-/* $NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $ */
+/* $NetBSD: cond.c,v 1.302 2021/12/12 09:36:00 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -95,14 +95,12 @@
#include "dir.h"
/* "@(#)cond.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: cond.c,v 1.267 2021/06/11 14:52:03 rillig Exp $");
+MAKE_RCSID("$NetBSD: cond.c,v 1.302 2021/12/12 09:36:00 rillig Exp $");
/*
* The parsing of conditional expressions is based on this grammar:
- * Or -> And '||' Or
- * Or -> And
- * And -> Term '&&' And
- * And -> Term
+ * Or -> And ('||' And)*
+ * And -> Term ('&&' Term)*
* Term -> Function '(' Argument ')'
* Term -> Leaf Operator Leaf
* Term -> Leaf
@@ -151,9 +149,23 @@ typedef struct CondParser {
bool plain;
/* The function to apply on unquoted bare words. */
- bool (*evalBare)(size_t, const char *);
+ bool (*evalBare)(const char *);
bool negateEvalBare;
+ /*
+ * Whether the left-hand side of a comparison may be an unquoted
+ * string. This is allowed for expressions of the form
+ * ${condition:?:}, see ApplyModifier_IfElse. Such a condition is
+ * 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.
+ *
+ * In all other contexts, the left-hand side must either be a
+ * variable expression, a quoted string or a number.
+ */
+ bool leftUnquotedOK;
+
const char *p; /* The remaining condition to parse */
Token curr; /* Single push-back token used in parsing */
@@ -171,24 +183,13 @@ static CondResult CondParser_Or(CondParser *par, bool);
static unsigned int cond_depth = 0; /* current .if nesting level */
static unsigned int cond_min_depth = 0; /* depth at makefile open */
-static const char *opname[] = { "<", "<=", ">", ">=", "==", "!=" };
-
-/*
- * Indicate when we should be strict about lhs of comparisons.
- * In strict mode, the lhs must be a variable expression or a string literal
- * in quotes. In non-strict mode it may also be an unquoted string literal.
- *
- * True when CondEvalExpression is called from Cond_EvalLine (.if etc).
- * False when CondEvalExpression is called from ApplyModifier_IfElse
- * since lhs is already expanded, and at that point we cannot tell if
- * it was a variable reference or not.
- */
-static bool lhsStrict;
+/* Names for ComparisonOp. */
+static const char opname[][3] = { "<", "<=", ">", ">=", "==", "!=" };
static bool
-is_token(const char *str, const char *tok, size_t len)
+is_token(const char *str, const char *tok, unsigned char len)
{
- return strncmp(str, tok, len) == 0 && !ch_isalpha(str[len]);
+ return strncmp(str, tok, (size_t)len) == 0 && !ch_isalpha(str[len]);
}
static Token
@@ -197,16 +198,6 @@ ToToken(bool cond)
return cond ? TOK_TRUE : TOK_FALSE;
}
-/* Push back the most recent token read. We only need one level of this. */
-static void
-CondParser_PushBack(CondParser *par, Token t)
-{
- assert(par->curr == TOK_NONE);
- assert(t != TOK_NONE);
-
- par->curr = t;
-}
-
static void
CondParser_SkipWhitespace(CondParser *par)
{
@@ -214,7 +205,9 @@ CondParser_SkipWhitespace(CondParser *par)
}
/*
- * Parse the argument of a built-in function.
+ * Parse a single word, taking into account balanced parentheses as well as
+ * embedded expressions. Used for the argument of a built-in function as
+ * well as for bare words, which are then passed to the default function.
*
* Arguments:
* *pp initially points at the '(',
@@ -223,12 +216,12 @@ CondParser_SkipWhitespace(CondParser *par)
* *out_arg receives the argument as string.
*
* func says whether the argument belongs to an actual function, or
- * whether the parsed argument is passed to the default function.
+ * NULL when parsing a bare word.
*
- * Return the length of the argument, or 0 on error.
+ * Return the length of the argument, or an ambiguous 0 on error.
*/
static size_t
-ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func,
+ParseWord(CondParser *par, const char **pp, bool doEval, const char *func,
char **out_arg)
{
const char *p = *pp;
@@ -239,11 +232,6 @@ ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func,
if (func != NULL)
p++; /* Skip opening '(' - verified by caller */
- if (*p == '\0') {
- *out_arg = NULL; /* Missing closing parenthesis: */
- return 0; /* .if defined( */
- }
-
cpp_skip_hspace(&p);
Buf_InitSize(&argBuf, 16);
@@ -299,9 +287,8 @@ ParseFuncArg(CondParser *par, const char **pp, bool doEval, const char *func,
}
/* Test whether the given variable is defined. */
-/*ARGSUSED*/
static bool
-FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncDefined(const char *arg)
{
FStr value = Var_Value(SCOPE_CMDLINE, arg);
bool result = value.str != NULL;
@@ -309,10 +296,9 @@ FuncDefined(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
return result;
}
-/* See if the given target is being made. */
-/*ARGSUSED*/
+/* See if the given target is requested to be made. */
static bool
-FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncMake(const char *arg)
{
StringListNode *ln;
@@ -323,9 +309,8 @@ FuncMake(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
}
/* See if the given file exists. */
-/*ARGSUSED*/
static bool
-FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncExists(const char *arg)
{
bool result;
char *path;
@@ -339,9 +324,8 @@ FuncExists(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
}
/* See if the given node exists and is an actual target. */
-/*ARGSUSED*/
static bool
-FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncTarget(const char *arg)
{
GNode *gn = Targ_FindNode(arg);
return gn != NULL && GNode_IsTarget(gn);
@@ -351,9 +335,8 @@ FuncTarget(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
* See if the given node exists and is an actual target with commands
* associated with it.
*/
-/*ARGSUSED*/
static bool
-FuncCommands(size_t argLen MAKE_ATTR_UNUSED, const char *arg)
+FuncCommands(const char *arg)
{
GNode *gn = Targ_FindNode(arg);
return gn != NULL && GNode_IsTarget(gn) && !Lst_IsEmpty(&gn->commands);
@@ -375,12 +358,12 @@ TryParseNumber(const char *str, double *out_value)
unsigned long ul_val;
double dbl_val;
- errno = 0;
if (str[0] == '\0') { /* XXX: why is an empty string a number? */
*out_value = 0.0;
return true;
}
+ errno = 0;
ul_val = strtoul(str, &end, str[1] == 'x' ? 16 : 10);
if (*end == '\0' && errno != ERANGE) {
*out_value = str[0] == '-' ? -(double)-ul_val : (double)ul_val;
@@ -412,17 +395,16 @@ is_separator(char ch)
*/
static bool
CondParser_StringExpr(CondParser *par, const char *start,
- bool const doEval, bool const quoted,
- Buffer *buf, FStr *const inout_str)
+ bool doEval, bool quoted,
+ Buffer *buf, FStr *inout_str)
{
VarEvalMode emode;
const char *nested_p;
bool atStart;
VarParseResult parseResult;
- /* if we are in quotes, an undefined variable is ok */
- emode = doEval && !quoted ? VARE_UNDEFERR
- : doEval ? VARE_WANTRES
+ emode = doEval && quoted ? VARE_WANTRES
+ : doEval ? VARE_UNDEFERR
: VARE_PARSE_ONLY;
nested_p = par->p;
@@ -477,7 +459,7 @@ CondParser_StringExpr(CondParser *par, const char *start,
* Sets out_quoted if the leaf was a quoted string literal.
*/
static void
-CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS,
+CondParser_Leaf(CondParser *par, bool doEval, bool unquotedOK,
FStr *out_str, bool *out_quoted)
{
Buffer buf;
@@ -525,11 +507,11 @@ CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS,
goto cleanup;
continue;
default:
- if (strictLHS && !quoted && *start != '$' &&
+ if (!unquotedOK && !quoted && *start != '$' &&
!ch_isdigit(*start)) {
/*
* The left-hand side must be quoted,
- * a variable reference or a number.
+ * a variable expression or a number.
*/
str = FStr_InitRefer(NULL);
goto cleanup;
@@ -541,15 +523,16 @@ CondParser_Leaf(CondParser *par, bool doEval, bool strictLHS,
}
got_str:
str = FStr_InitOwn(buf.data);
+ buf.data = NULL;
cleanup:
- Buf_DoneData(&buf); /* XXX: memory leak on failure? */
+ Buf_Done(&buf);
*out_str = str;
}
static bool
-EvalBare(const CondParser *par, const char *arg, size_t arglen)
+EvalBare(const CondParser *par, const char *arg)
{
- bool res = par->evalBare(arglen, arg);
+ bool res = par->evalBare(arg);
return par->negateEvalBare ? !res : res;
}
@@ -573,11 +556,14 @@ EvalNotEmpty(CondParser *par, const char *value, bool quoted)
/* For .if ${...}, check for non-empty string. This is different from
* the evaluation function from that .if variant, which would test
* whether a variable of the given name were defined. */
- /* XXX: Whitespace should count as empty, just as in ParseEmptyArg. */
+ /*
+ * XXX: Whitespace should count as empty, just as in
+ * CondParser_FuncCallEmpty.
+ */
if (par->plain)
return value[0] != '\0';
- return EvalBare(par, value, strlen(value));
+ return EvalBare(par, value);
}
/* Evaluate a numerical comparison, such as in ".if ${VAR} >= 9". */
@@ -682,11 +668,7 @@ CondParser_Comparison(CondParser *par, bool doEval)
ComparisonOp op;
bool lhsQuoted, rhsQuoted;
- /*
- * Parse the variable spec and skip over it, saving its
- * value in lhs.
- */
- CondParser_Leaf(par, doEval, lhsStrict, &lhs, &lhsQuoted);
+ CondParser_Leaf(par, doEval, par->leftUnquotedOK, &lhs, &lhsQuoted);
if (lhs.str == NULL)
goto done_lhs;
@@ -702,12 +684,12 @@ CondParser_Comparison(CondParser *par, bool doEval)
if (par->p[0] == '\0') {
Parse_Error(PARSE_FATAL,
- "Missing right-hand-side of operator '%s'", opname[op]);
+ "Missing right-hand side of operator '%s'", opname[op]);
par->printedError = true;
goto done_lhs;
}
- CondParser_Leaf(par, doEval, false, &rhs, &rhsQuoted);
+ CondParser_Leaf(par, doEval, true, &rhs, &rhsQuoted);
if (rhs.str == NULL)
goto done_rhs;
@@ -729,50 +711,37 @@ done_lhs:
* The argument to empty() is a variable name, optionally followed by
* variable modifiers.
*/
-/*ARGSUSED*/
-static size_t
-ParseEmptyArg(CondParser *par MAKE_ATTR_UNUSED, const char **pp,
- bool doEval, const char *func MAKE_ATTR_UNUSED,
- char **out_arg)
+static bool
+CondParser_FuncCallEmpty(CondParser *par, bool doEval, Token *out_token)
{
+ const char *cp = par->p;
+ Token tok;
FStr val;
- size_t magic_res;
- /* We do all the work here and return the result as the length */
- *out_arg = NULL;
+ if (!is_token(cp, "empty", 5))
+ return false;
+ cp += 5;
+
+ cpp_skip_whitespace(&cp);
+ if (*cp != '(')
+ return false;
- (*pp)--; /* Make (*pp)[1] point to the '('. */
- (void)Var_Parse(pp, SCOPE_CMDLINE,
+ cp--; /* Make cp[1] point to the '('. */
+ (void)Var_Parse(&cp, SCOPE_CMDLINE,
doEval ? VARE_WANTRES : VARE_PARSE_ONLY, &val);
/* TODO: handle errors */
- /* If successful, *pp points beyond the closing ')' now. */
- if (val.str == var_Error) {
- FStr_Done(&val);
- return (size_t)-1;
+ if (val.str == var_Error)
+ tok = TOK_ERROR;
+ else {
+ cpp_skip_whitespace(&val.str);
+ tok = val.str[0] != '\0' && doEval ? TOK_FALSE : TOK_TRUE;
}
- /*
- * A variable is empty when it just contains spaces...
- * 4/15/92, christos
- */
- cpp_skip_whitespace(&val.str);
-
- /*
- * For consistency with the other functions we can't generate the
- * true/false here.
- */
- magic_res = val.str[0] != '\0' ? 2 : 1;
FStr_Done(&val);
- return magic_res;
-}
-
-/*ARGSUSED*/
-static bool
-FuncEmpty(size_t arglen, const char *arg MAKE_ATTR_UNUSED)
-{
- /* Magic values ahead, see ParseEmptyArg. */
- return arglen == 1;
+ *out_token = tok;
+ par->p = cp;
+ return true;
}
/* Parse a function call expression, such as 'defined(${file})'. */
@@ -780,61 +749,51 @@ static bool
CondParser_FuncCall(CondParser *par, bool doEval, Token *out_token)
{
static const struct fn_def {
- const char *fn_name;
- size_t fn_name_len;
- size_t (*fn_parse)(CondParser *, const char **, bool,
- const char *, char **);
- bool (*fn_eval)(size_t, const char *);
+ const char fn_name[9];
+ unsigned char fn_name_len;
+ bool (*fn_eval)(const char *);
} fns[] = {
- { "defined", 7, ParseFuncArg, FuncDefined },
- { "make", 4, ParseFuncArg, FuncMake },
- { "exists", 6, ParseFuncArg, FuncExists },
- { "empty", 5, ParseEmptyArg, FuncEmpty },
- { "target", 6, ParseFuncArg, FuncTarget },
- { "commands", 8, ParseFuncArg, FuncCommands }
+ { "defined", 7, FuncDefined },
+ { "make", 4, FuncMake },
+ { "exists", 6, FuncExists },
+ { "target", 6, FuncTarget },
+ { "commands", 8, FuncCommands }
};
const struct fn_def *fn;
char *arg = NULL;
size_t arglen;
const char *cp = par->p;
- const struct fn_def *fns_end = fns + sizeof fns / sizeof fns[0];
-
- for (fn = fns; fn != fns_end; fn++) {
- if (!is_token(cp, fn->fn_name, fn->fn_name_len))
- continue;
+ const struct fn_def *last_fn = fns + sizeof fns / sizeof fns[0] - 1;
- cp += fn->fn_name_len;
- cpp_skip_whitespace(&cp);
- if (*cp != '(')
- break;
+ for (fn = fns; !is_token(cp, fn->fn_name, fn->fn_name_len); fn++)
+ if (fn == last_fn)
+ return false;
- arglen = fn->fn_parse(par, &cp, doEval, fn->fn_name, &arg);
- if (arglen == 0 || arglen == (size_t)-1) {
- par->p = cp;
- *out_token = arglen == 0 ? TOK_FALSE : TOK_ERROR;
- return true;
- }
+ cp += fn->fn_name_len;
+ cpp_skip_whitespace(&cp);
+ if (*cp != '(')
+ return false;
- /* Evaluate the argument using the required function. */
- *out_token = ToToken(!doEval || fn->fn_eval(arglen, arg));
- free(arg);
- par->p = cp;
- return true;
- }
+ arglen = ParseWord(par, &cp, doEval, fn->fn_name, &arg);
+ *out_token = ToToken(arglen != 0 && (!doEval || fn->fn_eval(arg)));
- return false;
+ free(arg);
+ par->p = cp;
+ return true;
}
/*
- * Parse a comparison such as '${VAR} == "value"', or a simple leaf without
+ * 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.
+ *
+ * TODO: Can this be merged into CondParser_Comparison?
*/
static Token
CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
{
Token t;
char *arg = NULL;
- size_t arglen;
const char *cp;
const char *cp1;
@@ -855,7 +814,7 @@ CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
* XXX: Is it possible to have a variable expression evaluated twice
* at this point?
*/
- arglen = ParseFuncArg(par, &cp, doEval, NULL, &arg);
+ (void)ParseWord(par, &cp, doEval, NULL, &arg);
cp1 = cp;
cpp_skip_whitespace(&cp1);
if (*cp1 == '=' || *cp1 == '!' || *cp1 == '<' || *cp1 == '>')
@@ -868,7 +827,7 @@ CondParser_ComparisonOrLeaf(CondParser *par, bool doEval)
* after .if must have been taken literally, so the argument cannot
* be empty - even if it contained a variable expansion.
*/
- t = ToToken(!doEval || EvalBare(par, arg, arglen));
+ t = ToToken(!doEval || EvalBare(par, arg));
free(arg);
return t;
}
@@ -934,12 +893,30 @@ CondParser_Token(CondParser *par, bool doEval)
return CondParser_Comparison(par, doEval);
default:
+ if (CondParser_FuncCallEmpty(par, doEval, &t))
+ return t;
if (CondParser_FuncCall(par, doEval, &t))
return t;
return CondParser_ComparisonOrLeaf(par, doEval);
}
}
+/* Skip the next token if it equals t. */
+static bool
+CondParser_Skip(CondParser *par, Token t)
+{
+ Token actual;
+
+ actual = CondParser_Token(par, false);
+ if (actual == t)
+ return true;
+
+ assert(par->curr == TOK_NONE);
+ assert(actual != TOK_NONE);
+ par->curr = actual;
+ return false;
+}
+
/*
* Term -> '(' Or ')'
* Term -> '!' Term
@@ -980,56 +957,44 @@ CondParser_Term(CondParser *par, bool doEval)
}
/*
- * And -> Term '&&' And
- * And -> Term
+ * And -> Term ('&&' Term)*
*/
static CondResult
CondParser_And(CondParser *par, bool doEval)
{
- CondResult res;
- Token op;
-
- res = CondParser_Term(par, doEval);
- if (res == CR_ERROR)
- return CR_ERROR;
+ CondResult res, rhs;
- op = CondParser_Token(par, doEval);
- if (op == TOK_AND) {
- if (res == CR_TRUE)
- return CondParser_And(par, doEval);
- if (CondParser_And(par, false) == CR_ERROR)
+ res = CR_TRUE;
+ do {
+ if ((rhs = CondParser_Term(par, doEval)) == CR_ERROR)
return CR_ERROR;
- return res;
- }
+ if (rhs == CR_FALSE) {
+ res = CR_FALSE;
+ doEval = false;
+ }
+ } while (CondParser_Skip(par, TOK_AND));
- CondParser_PushBack(par, op);
return res;
}
/*
- * Or -> And '||' Or
- * Or -> And
+ * Or -> And ('||' And)*
*/
static CondResult
CondParser_Or(CondParser *par, bool doEval)
{
- CondResult res;
- Token op;
+ CondResult res, rhs;
- res = CondParser_And(par, doEval);
- if (res == CR_ERROR)
- return CR_ERROR;
-
- op = CondParser_Token(par, doEval);
- if (op == TOK_OR) {
- if (res == CR_FALSE)
- return CondParser_Or(par, doEval);
- if (CondParser_Or(par, false) == CR_ERROR)
+ res = CR_FALSE;
+ do {
+ if ((rhs = CondParser_And(par, doEval)) == CR_ERROR)
return CR_ERROR;
- return res;
- }
+ if (rhs == CR_TRUE) {
+ res = CR_TRUE;
+ doEval = false;
+ }
+ } while (CondParser_Skip(par, TOK_OR));
- CondParser_PushBack(par, op);
return res;
}
@@ -1060,23 +1025,22 @@ CondParser_Eval(CondParser *par, bool *out_value)
* COND_PARSE if the condition was valid grammatically
* COND_INVALID if not a valid conditional.
*
- * (*value) is set to the boolean value of the condition
+ * *out_value is set to the boolean value of the condition
*/
static CondEvalResult
CondEvalExpression(const char *cond, bool *out_value, bool plain,
- bool (*evalBare)(size_t, const char *), bool negate,
- bool eprint, bool strictLHS)
+ bool (*evalBare)(const char *), bool negate,
+ bool eprint, bool leftUnquotedOK)
{
CondParser par;
CondEvalResult rval;
- lhsStrict = strictLHS;
-
cpp_skip_hspace(&cond);
par.plain = plain;
par.evalBare = evalBare;
par.negateEvalBare = negate;
+ par.leftUnquotedOK = leftUnquotedOK;
par.p = cond;
par.curr = TOK_NONE;
par.printedError = false;
@@ -1097,7 +1061,7 @@ CondEvalResult
Cond_EvalCondition(const char *cond, bool *out_value)
{
return CondEvalExpression(cond, out_value, true,
- FuncDefined, false, false, false);
+ FuncDefined, false, false, true);
}
static bool
@@ -1109,7 +1073,7 @@ IsEndif(const char *p)
static bool
DetermineKindOfConditional(const char **pp, bool *out_plain,
- bool (**out_evalBare)(size_t, const char *),
+ bool (**out_evalBare)(const char *),
bool *out_negate)
{
const char *p = *pp;
@@ -1198,7 +1162,7 @@ Cond_EvalLine(const char *line)
static unsigned int cond_states_cap = 128;
bool plain;
- bool (*evalBare)(size_t, const char *);
+ bool (*evalBare)(const char *);
bool negate;
bool isElif;
bool value;
@@ -1217,7 +1181,7 @@ Cond_EvalLine(const char *line)
if (IsEndif(p)) { /* It is an '.endif'. */
if (p[5] != '\0') {
Parse_Error(PARSE_FATAL,
- "The .endif directive does not take arguments.");
+ "The .endif directive does not take arguments");
}
if (cond_depth == cond_min_depth) {
@@ -1236,7 +1200,7 @@ Cond_EvalLine(const char *line)
if (p[1] != 'l') {
/*
* Unknown directive. It might still be a
- * transformation rule like '.elisp.scm',
+ * transformation rule like '.err.txt',
* therefore no error message here.
*/
return COND_INVALID;
@@ -1248,8 +1212,8 @@ Cond_EvalLine(const char *line)
if (p[2] != '\0')
Parse_Error(PARSE_FATAL,
- "The .else directive "
- "does not take arguments.");
+ "The .else directive "
+ "does not take arguments");
if (cond_depth == cond_min_depth) {
Parse_Error(PARSE_FATAL, "if-less else");
@@ -1328,7 +1292,7 @@ Cond_EvalLine(const char *line)
/* And evaluate the conditional expression */
if (CondEvalExpression(p, &value, plain, evalBare, negate,
- true, true) == COND_INVALID) {
+ true, false) == COND_INVALID) {
/* Syntax error in conditional, error message already output. */
/* Skip everything to matching .endif */
/* XXX: An extra '.else' is not detected in this case. */
diff --git a/contrib/bmake/config.h.in b/contrib/bmake/config.h.in
index 18f07aeda37f..aebebe783a44 100644
--- a/contrib/bmake/config.h.in
+++ b/contrib/bmake/config.h.in
@@ -9,6 +9,9 @@
/* Shell spec to use by default */
#undef DEFSHELL_INDEX
+/* Path of default shell */
+#undef DEFSHELL_PATH
+
/* Define to 1 if you have the <ar.h> header file. */
#undef HAVE_AR_H
@@ -65,6 +68,9 @@
/* Define to 1 if you have the <limits.h> header file. */
#undef HAVE_LIMITS_H
+/* Define to 1 if the system has the type `long long int'. */
+#undef HAVE_LONG_LONG_INT
+
/* Define to 1 if you have the <memory.h> header file. */
#undef HAVE_MEMORY_H
@@ -98,15 +104,36 @@
/* Define to 1 if you have the `setpgid' function. */
#undef HAVE_SETPGID
+/* Define to 1 if you have the `setrlimit' function. */
+#undef HAVE_SETRLIMIT
+
/* Define to 1 if you have the `setsid' function. */
#undef HAVE_SETSID
/* Define to 1 if you have the `sigaction' function. */
#undef HAVE_SIGACTION
+/* Define to 1 if you have the `sigaddset' function. */
+#undef HAVE_SIGADDSET
+
+/* Define to 1 if you have the `sigpending' function. */
+#undef HAVE_SIGPENDING
+
+/* Define to 1 if you have the `sigprocmask' function. */
+#undef HAVE_SIGPROCMASK
+
+/* Define to 1 if you have the `sigsetmask' function. */
+#undef HAVE_SIGSETMASK
+
+/* Define to 1 if you have the `sigsuspend' function. */
+#undef HAVE_SIGSUSPEND
+
/* Define to 1 if you have the `sigvec' function. */
#undef HAVE_SIGVEC
+/* Define to 1 if the system has the type `sig_atomic_t'. */
+#undef HAVE_SIG_ATOMIC_T
+
/* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF
@@ -143,6 +170,12 @@
/* Define to 1 if you have the `strtol' function. */
#undef HAVE_STRTOL
+/* Define to 1 if you have the `strtoll' function. */
+#undef HAVE_STRTOLL
+
+/* Define to 1 if you have the `strtoul' function. */
+#undef HAVE_STRTOUL
+
/* Define to 1 if `st_rdev' is a member of `struct stat'. */
#undef HAVE_STRUCT_STAT_ST_RDEV
@@ -197,6 +230,9 @@
/* Define to 1 if you have the `unsetenv' function. */
#undef HAVE_UNSETENV
+/* Define to 1 if the system has the type `unsigned long long int'. */
+#undef HAVE_UNSIGNED_LONG_LONG_INT
+
/* Define to 1 if you have the <utime.h> header file. */
#undef HAVE_UTIME_H
@@ -324,6 +360,16 @@
/* Define to empty if `const' does not conform to ANSI C. */
#undef const
+/* Define to `__inline__' or `__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+#undef inline
+#endif
+
+/* Define to the type of a signed integer type of width exactly 64 bits if
+ such a type exists and the standard includes do not define it. */
+#undef int64_t
+
/* Define to `int' if <sys/types.h> does not define. */
#undef mode_t
diff --git a/contrib/bmake/configure b/contrib/bmake/configure
index 6dbf5ad31a00..42295d1db3e3 100755
--- a/contrib/bmake/configure
+++ b/contrib/bmake/configure
@@ -1,6 +1,6 @@
#! /bin/sh
# Guess values for system-dependent variables and create Makefiles.
-# Generated by GNU Autoconf 2.69 for bmake 20210201.
+# Generated by GNU Autoconf 2.69 for bmake 20211020.
#
# Report bugs to <sjg@NetBSD.org>.
#
@@ -580,8 +580,8 @@ MAKEFLAGS=
# Identity of this package.
PACKAGE_NAME='bmake'
PACKAGE_TARNAME='bmake'
-PACKAGE_VERSION='20210201'
-PACKAGE_STRING='bmake 20210201'
+PACKAGE_VERSION='20211020'
+PACKAGE_STRING='bmake 20211020'
PACKAGE_BUGREPORT='sjg@NetBSD.org'
PACKAGE_URL=''
@@ -622,11 +622,13 @@ ac_includes_default="\
#endif"
ac_subst_vars='LTLIBOBJS
+UTC_1
_MAKE_VERSION
filemon_h
use_filemon
use_meta
diff_u
+diff
GCC
INSTALL
default_sys_path
@@ -1255,7 +1257,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 20210201 to adapt to many kinds of systems.
+\`configure' configures bmake 20211020 to adapt to many kinds of systems.
Usage: $0 [OPTION]... [VAR=VALUE]...
@@ -1316,7 +1318,7 @@ fi
if test -n "$ac_init_help"; then
case $ac_init_help in
- short | recursive ) echo "Configuration of bmake 20210201:";;
+ short | recursive ) echo "Configuration of bmake 20211020:";;
esac
cat <<\_ACEOF
@@ -1422,7 +1424,7 @@ fi
test -n "$ac_init_help" && exit $ac_status
if $ac_init_version; then
cat <<\_ACEOF
-bmake configure 20210201
+bmake configure 20211020
generated by GNU Autoconf 2.69
Copyright (C) 2012 Free Software Foundation, Inc.
@@ -1721,6 +1723,82 @@ fi
} # ac_fn_c_try_link
+# ac_fn_c_find_intX_t LINENO BITS VAR
+# -----------------------------------
+# Finds a signed integer type with width BITS, setting cache variable VAR
+# accordingly.
+ac_fn_c_find_intX_t ()
+{
+ as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5
+$as_echo_n "checking for int$2_t... " >&6; }
+if eval \${$3+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ eval "$3=no"
+ # Order is important - never check a type that is potentially smaller
+ # than half of the expected target width.
+ for ac_type in int$2_t 'int' 'long int' \
+ 'long long int' 'short int' 'signed char'; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+ enum { N = $2 / 2 - 1 };
+int
+main ()
+{
+static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+$ac_includes_default
+ enum { N = $2 / 2 - 1 };
+int
+main ()
+{
+static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1)
+ < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))];
+test_array [0] = 0;
+return test_array [0];
+
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+
+else
+ case $ac_type in #(
+ int$2_t) :
+ eval "$3=yes" ;; #(
+ *) :
+ eval "$3=\$ac_type" ;;
+esac
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ if eval test \"x\$"$3"\" = x"no"; then :
+
+else
+ break
+fi
+ done
+fi
+eval ac_res=\$$3
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5
+$as_echo "$ac_res" >&6; }
+ eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno
+
+} # ac_fn_c_find_intX_t
+
# ac_fn_c_check_type LINENO TYPE VAR INCLUDES
# -------------------------------------------
# Tests whether TYPE exists after having included INCLUDES, setting cache
@@ -2002,7 +2080,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 20210201, which was
+It was created by bmake $as_me 20211020, which was
generated by GNU Autoconf 2.69. Invocation command line was
$ $0 $@
@@ -2355,26 +2433,41 @@ ac_config_headers="$ac_config_headers config.h"
case "$srcdir" in
/*) ;;
-*) srcdir=`cd $srcdir && pwd`;;
+*) srcdir=`cd $srcdir && 'pwd'`;;
esac
. $srcdir/VERSION
OS=`uname -s`
+use_defshell() {
+ case "$defshell_path$DEFSHELL_INDEX" in
+ "") ;;
+ *) return 0;;
+ esac
+ case "$1" in
+ *csh) # we must be desperate
+ DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;;
+ *ksh)
+ DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;;
+ sh|/bin/sh)
+ DEFSHELL_INDEX=DEFSHELL_INDEX_SH;;
+ *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM
+ defshell_path=$1
+ ;;
+ esac
+ case "$1" in
+ /bin/*) ;;
+ */*) defshell_path=$1;;
+ esac
+}
# Check whether --with-defshell was given.
if test "${with_defshell+set}" = set; then :
withval=$with_defshell; case "${withval}" in
yes) as_fn_error $? "bad value ${withval} given for bmake DEFSHELL" "$LINENO" 5 ;;
no) ;;
-*) case "$with_defshell" in
- sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; # it's the default anyway
- ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;;
- csh) DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; # kidding right?
- *) defshell_path=$with_defshell;; # better be sh compatible!
- esac
- ;;
- esac
+*) use_defshell $with_defshell;;
+esac
fi
case "$OS" in
@@ -2446,6 +2539,37 @@ yes)
esac
;;
esac
+case "$OS" in
+Minix) CPPFLAGS="${CPPFLAGS} -D_NETBSD_SOURCE"
+ test -x /usr/pkg/bin/clang && CC=${CC:-clang}
+ ;;
+SCO_SV) # /bin/sh is not usable
+ ALT_DEF_SHELLS="/bin/lsh /usr/bin/bash /bin/ksh"
+ CPPFLAGS="${CPPFLAGS} -DFORCE_USE_SHELL"
+ ;;
+esac
+echo $ECHO_N "checking whether system has timezone Europe/Berlin... $ECHO_C" >&6
+if test -f /usr/share/zoneinfo/Europe/Berlin; then
+ echo yes >&6
+ # seems a safe bet
+ UTC_1=Europe/Berlin
+else
+ utcH=`TZ=UTC date +%H 2> /dev/null`
+ utc_1H=`TZ=UTC-1 date +%H 2> /dev/null`
+ if test ${utcH-0} -lt ${utc_1H-0}; then
+ UTC_1=UTC-1
+ echo no, using UTC-1 >&6
+ else
+ echo no >&6
+ fi
+fi
+oldPATH=$PATH
+for d in /usr/gnu/bin
+do
+ test -d $d || continue
+ PATH=$PATH:$d
+done
+export PATH
ac_ext=c
ac_cpp='$CPP $CPPFLAGS'
ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'
@@ -4543,7 +4667,29 @@ if test $bmake_path_max -gt 1024; then
bmake_path_max=1024
fi
echo "Using: BMAKE_PATH_MAX=$bmake_path_max" >&6
-
+if (type cat) > /dev/null 2>&1; then
+: which
+which() {
+ type "$@" | sed 's,[()],,g;s,^[^/][^/]*,,;q'
+}
+fi
+case "$CC" in
+/*) ;;
+*)
+ for x in $CC
+ do
+ _cc=`which $x`
+ break
+ done
+ if test -x ${_cc:-/dev/null}; then
+ _cc_dir=`dirname $_cc`
+ case ":$oldPATH:" in
+ *:$_cc_dir:*) ;;
+ *) CC=$_cc_dir/$CC;;
+ esac
+ fi
+ ;;
+esac
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sys/wait.h that is POSIX.1 compatible" >&5
$as_echo_n "checking for sys/wait.h that is POSIX.1 compatible... " >&6; }
if ${ac_cv_header_sys_wait_h+:} false; then :
@@ -4803,22 +4949,6 @@ done
ac_fn_c_check_header_mongrel "$LINENO" "sys/cdefs.h" "ac_cv_header_sys_cdefs_h" "$ac_includes_default"
if test "x$ac_cv_header_sys_cdefs_h" = xyes; then :
- echo $ECHO_N "checking whether sys/cdefs.h is compatible... $ECHO_C" >&6
-cat confdefs.h - <<_ACEOF >conftest.$ac_ext
-/* end confdefs.h. */
-#include <sys/cdefs.h>
-#ifdef __RCSID
-yes
-#endif
-
-_ACEOF
-if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
- $EGREP "yes" >/dev/null 2>&1; then :
- echo yes >&6
-else
- echo no >&6; CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd` -DNEED_HOST_CDEFS_H"
-fi
-rm -f conftest*
else
CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`"
@@ -5175,6 +5305,176 @@ $as_echo "#define const /**/" >>confdefs.h
fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5
+$as_echo_n "checking for inline... " >&6; }
+if ${ac_cv_c_inline+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_c_inline=no
+for ac_kw in inline __inline__ __inline; do
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#ifndef __cplusplus
+typedef int foo_t;
+static $ac_kw foo_t static_foo () {return 0; }
+$ac_kw foo_t foo () {return 0; }
+#endif
+
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+ ac_cv_c_inline=$ac_kw
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+ test "$ac_cv_c_inline" != no && break
+done
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5
+$as_echo "$ac_cv_c_inline" >&6; }
+
+case $ac_cv_c_inline in
+ inline | yes) ;;
+ *)
+ case $ac_cv_c_inline in
+ no) ac_val=;;
+ *) ac_val=$ac_cv_c_inline;;
+ esac
+ cat >>confdefs.h <<_ACEOF
+#ifndef __cplusplus
+#define inline $ac_val
+#endif
+_ACEOF
+ ;;
+esac
+
+ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t"
+case $ac_cv_c_int64_t in #(
+ no|yes) ;; #(
+ *)
+
+cat >>confdefs.h <<_ACEOF
+#define int64_t $ac_cv_c_int64_t
+_ACEOF
+;;
+esac
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for unsigned long long int" >&5
+$as_echo_n "checking for unsigned long long int... " >&6; }
+if ${ac_cv_type_unsigned_long_long_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_type_unsigned_long_long_int=yes
+ if test "x${ac_cv_prog_cc_c99-no}" = xno; then
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+
+ /* For now, do not test the preprocessor; as of 2007 there are too many
+ implementations with broken preprocessors. Perhaps this can
+ be revisited in 2012. In the meantime, code should not expect
+ #if to work with literals wider than 32 bits. */
+ /* Test literals. */
+ long long int ll = 9223372036854775807ll;
+ long long int nll = -9223372036854775807LL;
+ unsigned long long int ull = 18446744073709551615ULL;
+ /* Test constant expressions. */
+ typedef int a[((-9223372036854775807LL < 0 && 0 < 9223372036854775807ll)
+ ? 1 : -1)];
+ typedef int b[(18446744073709551615ULL <= (unsigned long long int) -1
+ ? 1 : -1)];
+ int i = 63;
+int
+main ()
+{
+/* Test availability of runtime routines for shift and division. */
+ long long int llmax = 9223372036854775807ll;
+ unsigned long long int ullmax = 18446744073709551615ull;
+ return ((ll << 63) | (ll >> 63) | (ll < i) | (ll > i)
+ | (llmax / ll) | (llmax % ll)
+ | (ull << 63) | (ull >> 63) | (ull << i) | (ull >> i)
+ | (ullmax / ull) | (ullmax % ull));
+ ;
+ return 0;
+}
+
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+
+else
+ ac_cv_type_unsigned_long_long_int=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+ conftest$ac_exeext conftest.$ac_ext
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_unsigned_long_long_int" >&5
+$as_echo "$ac_cv_type_unsigned_long_long_int" >&6; }
+ if test $ac_cv_type_unsigned_long_long_int = yes; then
+
+$as_echo "#define HAVE_UNSIGNED_LONG_LONG_INT 1" >>confdefs.h
+
+ fi
+
+
+
+ { $as_echo "$as_me:${as_lineno-$LINENO}: checking for long long int" >&5
+$as_echo_n "checking for long long int... " >&6; }
+if ${ac_cv_type_long_long_int+:} false; then :
+ $as_echo_n "(cached) " >&6
+else
+ ac_cv_type_long_long_int=yes
+ if test "x${ac_cv_prog_cc_c99-no}" = xno; then
+ ac_cv_type_long_long_int=$ac_cv_type_unsigned_long_long_int
+ if test $ac_cv_type_long_long_int = yes; then
+ if test "$cross_compiling" = yes; then :
+ :
+else
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h. */
+#include <limits.h>
+ #ifndef LLONG_MAX
+ # define HALF \
+ (1LL << (sizeof (long long int) * CHAR_BIT - 2))
+ # define LLONG_MAX (HALF - 1 + HALF)
+ #endif
+int
+main ()
+{
+long long int n = 1;
+ int i;
+ for (i = 0; ; i++)
+ {
+ long long int m = n << i;
+ if (m >> i != n)
+ return 1;
+ if (LLONG_MAX / 2 < m)
+ break;
+ }
+ return 0;
+ ;
+ return 0;
+}
+_ACEOF
+if ac_fn_c_try_run "$LINENO"; then :
+
+else
+ ac_cv_type_long_long_int=no
+fi
+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \
+ conftest.$ac_objext conftest.beam conftest.$ac_ext
+fi
+
+ fi
+ fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_type_long_long_int" >&5
+$as_echo "$ac_cv_type_long_long_int" >&6; }
+ if test $ac_cv_type_long_long_int = yes; then
+
+$as_echo "#define HAVE_LONG_LONG_INT 1" >>confdefs.h
+
+ fi
+
ac_fn_c_check_type "$LINENO" "mode_t" "ac_cv_type_mode_t" "$ac_includes_default"
if test "x$ac_cv_type_mode_t" = xyes; then :
@@ -5332,9 +5632,9 @@ ac_fn_c_check_type "$LINENO" "sig_atomic_t" "ac_cv_type_sig_atomic_t" "
"
if test "x$ac_cv_type_sig_atomic_t" = xyes; then :
-else
-
-$as_echo "#define sig_atomic_t int" >>confdefs.h
+cat >>confdefs.h <<_ACEOF
+#define HAVE_SIG_ATOMIC_T 1
+_ACEOF
fi
@@ -5674,7 +5974,6 @@ for ac_func in \
errx \
getcwd \
getenv \
- getopt \
getwd \
killpg \
mmap \
@@ -5682,15 +5981,23 @@ for ac_func in \
select \
setenv \
setpgid \
+ setrlimit \
setsid \
- sigaction \
+ sigaddset \
+ sigpending \
+ sigprocmask \
+ sigsetmask \
+ sigsuspend \
sigvec \
snprintf \
strerror \
+ stresep \
strftime \
strsep \
strtod \
strtol \
+ strtoll \
+ strtoul \
sysctl \
unsetenv \
vsnprintf \
@@ -5712,6 +6019,19 @@ fi
done
+ac_fn_c_check_func "$LINENO" "getopt" "ac_cv_func_getopt"
+if test "x$ac_cv_func_getopt" = xyes; then :
+ $as_echo "#define HAVE_GETOPT 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" getopt.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS getopt.$ac_objext"
+ ;;
+esac
+
+fi
+
ac_fn_c_check_func "$LINENO" "realpath" "ac_cv_func_realpath"
if test "x$ac_cv_func_realpath" = xyes; then :
$as_echo "#define HAVE_REALPATH 1" >>confdefs.h
@@ -5738,6 +6058,19 @@ esac
fi
+ac_fn_c_check_func "$LINENO" "sigaction" "ac_cv_func_sigaction"
+if test "x$ac_cv_func_sigaction" = xyes; then :
+ $as_echo "#define HAVE_SIGACTION 1" >>confdefs.h
+
+else
+ case " $LIBOBJS " in
+ *" sigaction.$ac_objext "* ) ;;
+ *) LIBOBJS="$LIBOBJS sigaction.$ac_objext"
+ ;;
+esac
+
+fi
+
ac_fn_c_check_func "$LINENO" "stresep" "ac_cv_func_stresep"
if test "x$ac_cv_func_stresep" = xyes; then :
$as_echo "#define HAVE_STRESEP 1" >>confdefs.h
@@ -6006,13 +6339,19 @@ $as_echo "#define __func__ __FUNCTION__" >>confdefs.h
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
-echo $ECHO_N "checking if diff -u works... $ECHO_C" >&6
-if diff -u /dev/null /dev/null > /dev/null 2>&1; then
- diff_u=-u
- echo yes >&6
-else
- diff_u=
- echo no >&6
+if test -x /usr/gnu/bin/diff; then
+ diff=/usr/gnu/bin/diff
+ diff_u=-u
+else
+ diff=${diff:-diff}
+ echo $ECHO_N "checking if $diff -u works... $ECHO_C" >&6
+ if $diff -u /dev/null /dev/null > /dev/null 2>&1; then
+ diff_u=-u
+ echo yes >&6
+ else
+ diff_u=
+ echo no >&6
+ fi
fi
echo "checking for MACHINE & MACHINE_ARCH..." >&6
cat > conftest.$ac_ext <<EOF
@@ -6140,24 +6479,46 @@ do
done
mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"`
echo "Using: MKSRC=$mksrc" 1>&6
-if test -x /usr/xpg4/bin/sh; then
- defshell_path=${defshell_path:-/usr/xpg4/bin/sh}
-fi
-if test -n "$defshell_path"; then
+for sh in /usr/xpg4/bin/sh $ALT_DEF_SHELLS
+do
+ test -x $sh || continue
+ use_defshell $sh
+ break
+done
+case "$defshell_path$DEFSHELL_INDEX" in
+"") ;;
+*DEFSHELL_INDEX_CUSTOM)
echo "Using: SHELL=$defshell_path" >&6
cat >>confdefs.h <<_ACEOF
#define DEFSHELL_CUSTOM "$defshell_path"
_ACEOF
-fi
-if test -n "$DEFSHELL_INDEX"; then
+ ;;
+/*INDEX*)
+ echo "Using: SHELL=$DEFSHELL_INDEX ($defshell_path)" | sed 's,DEFSHELL_INDEX_,,' >&6
cat >>confdefs.h <<_ACEOF
#define DEFSHELL_INDEX $DEFSHELL_INDEX
_ACEOF
-fi
+
+cat >>confdefs.h <<_ACEOF
+#define DEFSHELL_PATH "$defshell_path"
+_ACEOF
+
+ ;;
+*)
+ echo "Using: SHELL=$DEFSHELL_INDEX" | sed 's,DEFSHELL_INDEX_,,' >&6
+
+cat >>confdefs.h <<_ACEOF
+#define DEFSHELL_INDEX $DEFSHELL_INDEX
+_ACEOF
+
+ ;;
+esac
+
+
@@ -6175,6 +6536,18 @@ bm_outfiles="Makefile.config unit-tests/Makefile.config make-bootstrap.sh"
if test $use_makefile = yes; then
bm_outfiles="makefile $bm_outfiles"
fi
+
+here=`'pwd'`
+: srcdir=$srcdir
+: here= $here
+case "$here" in
+$srcdir/obj*) # make sure we put unit-tests/Makefile.config in the right place
+ obj=`basename $here`
+ mkdir -p $srcdir/unit-tests/$obj
+ test -d unit-tests || ln -s ../unit-tests/$obj unit-tests
+ ;;
+esac
+
ac_config_files="$ac_config_files $bm_outfiles"
cat >confcache <<\_ACEOF
@@ -6684,7 +7057,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 20210201, which was
+This file was extended by bmake $as_me 20211020, which was
generated by GNU Autoconf 2.69. Invocation command line was
CONFIG_FILES = $CONFIG_FILES
@@ -6746,7 +7119,7 @@ _ACEOF
cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1
ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`"
ac_cs_version="\\
-bmake config.status 20210201
+bmake config.status 20211020
configured by $0, generated by GNU Autoconf 2.69,
with options \\"\$ac_cs_config\\"
diff --git a/contrib/bmake/configure.in b/contrib/bmake/configure.in
index 877493594a20..3eefa1f25c39 100644
--- a/contrib/bmake/configure.in
+++ b/contrib/bmake/configure.in
@@ -1,37 +1,53 @@
dnl
dnl RCSid:
-dnl $Id: configure.in,v 1.70 2021/02/01 18:29:26 sjg Exp $
+dnl $Id: configure.in,v 1.85 2021/10/23 20:57:08 sjg Exp $
dnl
dnl Process this file with autoconf to produce a configure script
dnl
AC_PREREQ(2.50)
-AC_INIT([bmake], [20210201], [sjg@NetBSD.org])
+AC_INIT([bmake], [20211020], [sjg@NetBSD.org])
AC_CONFIG_HEADERS(config.h)
dnl make srcdir absolute
case "$srcdir" in
/*) ;;
-*) srcdir=`cd $srcdir && pwd`;;
+*) srcdir=`cd $srcdir && 'pwd'`;;
esac
dnl get _MAKE_VERSION
. $srcdir/VERSION
OS=`uname -s`
+dnl function to set DEFSHELL_INDEX
+use_defshell() {
+ case "$defshell_path$DEFSHELL_INDEX" in
+ "") ;;
+ *) return 0;;
+ esac
+ case "$1" in
+ *csh) # we must be desperate
+ DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;;
+ *ksh)
+ DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;;
+ sh|/bin/sh)
+ DEFSHELL_INDEX=DEFSHELL_INDEX_SH;;
+ *) DEFSHELL_INDEX=DEFSHELL_INDEX_CUSTOM
+ defshell_path=$1
+ ;;
+ esac
+ case "$1" in
+ /bin/*) ;;
+ */*) defshell_path=$1;;
+ esac
+}
dnl
AC_ARG_WITH(defshell,
[ --with-defshell=SHELL use SHELL by default - must be sh compatible, use sh or ksh to pick the internal definitions],
[case "${withval}" in
yes) AC_MSG_ERROR(bad value ${withval} given for bmake DEFSHELL) ;;
no) ;;
-*) case "$with_defshell" in
- sh) DEFSHELL_INDEX=DEFSHELL_INDEX_SH;; # it's the default anyway
- ksh) DEFSHELL_INDEX=DEFSHELL_INDEX_KSH;;
- csh) DEFSHELL_INDEX=DEFSHELL_INDEX_CSH;; # kidding right?
- *) defshell_path=$with_defshell;; # better be sh compatible!
- esac
- ;;
- esac])
+*) use_defshell $with_defshell;;
+esac])
dnl
case "$OS" in
CYGWIN*|MINGW*) use_makefile=no;;
@@ -97,6 +113,45 @@ yes)
esac
dnl
dnl Check for OS problems
+dnl
+dnl Minix 3 at least has bugs in headers where _NETBSD_SOURCE
+dnl is needed for compilation
+case "$OS" in
+Minix) CPPFLAGS="${CPPFLAGS} -D_NETBSD_SOURCE"
+ test -x /usr/pkg/bin/clang && CC=${CC:-clang}
+ ;;
+SCO_SV) # /bin/sh is not usable
+ ALT_DEF_SHELLS="/bin/lsh /usr/bin/bash /bin/ksh"
+ CPPFLAGS="${CPPFLAGS} -DFORCE_USE_SHELL"
+ ;;
+esac
+dnl
+dnl Not everyone groks TZ=Europe/Berlin
+dnl which is used by the localtime tests
+echo $ECHO_N "checking whether system has timezone Europe/Berlin... $ECHO_C" >&6
+if test -f /usr/share/zoneinfo/Europe/Berlin; then
+ echo yes >&6
+ # seems a safe bet
+ UTC_1=Europe/Berlin
+else
+ utcH=`TZ=UTC date +%H 2> /dev/null`
+ utc_1H=`TZ=UTC-1 date +%H 2> /dev/null`
+ if test ${utcH-0} -lt ${utc_1H-0}; then
+ UTC_1=UTC-1
+ echo no, using UTC-1 >&6
+ else
+ echo no >&6
+ fi
+fi
+dnl
+dnl Add some places to look for compilers
+oldPATH=$PATH
+for d in /usr/gnu/bin
+do
+ test -d $d || continue
+ PATH=$PATH:$d
+done
+export PATH
dnl Solaris's signal.h only privides sigset_t etc if one of
dnl _EXTENSIONS_ _POSIX_C_SOURCE or _XOPEN_SOURCE are defined.
dnl The later two seem to cause more problems than they solve so if we
@@ -126,7 +181,33 @@ AC_SUBST(bmake_path_max)dnl
dnl
dnl AC_C_CROSS
dnl
-
+dnl if type does not work which(1) had better!
+dnl note we cannot rely on type returning non-zero on failure
+if (type cat) > /dev/null 2>&1; then
+: which
+which() {
+ type "$@" | sed 's,[[()]],,g;s,^[[^/]][[^/]]*,,;q'
+}
+fi
+dnl if CC is somewhere that was not in PATH we need its full path
+dnl watch out for included flags!
+case "$CC" in
+/*) ;;
+*)
+ for x in $CC
+ do
+ _cc=`which $x`
+ break
+ done
+ if test -x ${_cc:-/dev/null}; then
+ _cc_dir=`dirname $_cc`
+ case ":$oldPATH:" in
+ *:$_cc_dir:*) ;;
+ *) CC=$_cc_dir/$CC;;
+ esac
+ fi
+ ;;
+esac
dnl Checks for header files.
AC_HEADER_SYS_WAIT
AC_HEADER_DIRENT
@@ -158,24 +239,16 @@ AC_CHECK_HEADERS( \
dnl Both *BSD and Linux have sys/cdefs.h, most do not.
dnl If it is missing, we add -I${srcdir}/missing to CFLAGS
-dnl also if sys/cdefs.h does not have __RCSID we need to use ours
-dnl but we need to include the host's one too *sigh*
-AC_CHECK_HEADER(sys/cdefs.h,
-echo $ECHO_N "checking whether sys/cdefs.h is compatible... $ECHO_C" >&6
-AC_EGREP_CPP(yes,
-[#include <sys/cdefs.h>
-#ifdef __RCSID
-yes
-#endif
-],
-echo yes >&6,
-echo no >&6; CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd` -DNEED_HOST_CDEFS_H"),
+AC_CHECK_HEADER(sys/cdefs.h,,
CPPFLAGS="${CPPFLAGS} -I`cd ${srcdir}/missing && pwd`")
dnl Checks for typedefs, structures, and compiler characteristics.
AC_C___ATTRIBUTE__
AC_C_BIGENDIAN
AC_C_CONST
+AC_C_INLINE
+AC_TYPE_INT64_T
+AC_TYPE_LONG_LONG_INT
AC_TYPE_MODE_T
AC_TYPE_OFF_T
AC_TYPE_PID_T
@@ -187,9 +260,8 @@ AC_STRUCT_TM
dnl we need sig_atomic_t
AH_TEMPLATE([sig_atomic_t],[type that signal handlers can safely frob])
-AC_CHECK_TYPE(sig_atomic_t,,[
-AC_DEFINE([sig_atomic_t],[int],)
-],[
+AC_CHECK_TYPES([sig_atomic_t],[],[],
+[
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
@@ -207,7 +279,6 @@ AC_CHECK_FUNCS( \
errx \
getcwd \
getenv \
- getopt \
getwd \
killpg \
mmap \
@@ -215,15 +286,23 @@ AC_CHECK_FUNCS( \
select \
setenv \
setpgid \
+ setrlimit \
setsid \
- sigaction \
+ sigaddset \
+ sigpending \
+ sigprocmask \
+ sigsetmask \
+ sigsuspend \
sigvec \
snprintf \
strerror \
+ stresep \
strftime \
strsep \
strtod \
strtol \
+ strtoll \
+ strtoul \
sysctl \
unsetenv \
vsnprintf \
@@ -236,8 +315,10 @@ AC_CHECK_FUNCS( \
dnl functions which we may need to provide
AC_REPLACE_FUNCS( \
+ getopt \
realpath \
dirname \
+ sigaction \
stresep \
strlcpy \
)
@@ -261,13 +342,20 @@ AC_COMPILE_IFELSE([AC_LANG_PROGRAM([],[[const char *func = __func__;]])],,
AC_DEFINE(__func__, __FUNCTION__, C99 function name))
dnl
dnl we want this for unit-tests/Makefile
-echo $ECHO_N "checking if diff -u works... $ECHO_C" >&6
-if diff -u /dev/null /dev/null > /dev/null 2>&1; then
- diff_u=-u
- echo yes >&6
+dnl GNU diff is known to support -u
+if test -x /usr/gnu/bin/diff; then
+ diff=/usr/gnu/bin/diff
+ diff_u=-u
else
- diff_u=
- echo no >&6
+ diff=${diff:-diff}
+ echo $ECHO_N "checking if $diff -u works... $ECHO_C" >&6
+ if $diff -u /dev/null /dev/null > /dev/null 2>&1; then
+ diff_u=-u
+ echo yes >&6
+ else
+ diff_u=
+ echo no >&6
+ fi
fi
dnl
dnl AC_* don't quite cut it.
@@ -404,16 +492,28 @@ done
mksrc=`echo $mksrc | sed "s,$srcdir,\\\${srcdir},"`
echo "Using: MKSRC=$mksrc" 1>&6
dnl On some systems we want a different default shell by default
-if test -x /usr/xpg4/bin/sh; then
- defshell_path=${defshell_path:-/usr/xpg4/bin/sh}
-fi
-if test -n "$defshell_path"; then
+for sh in /usr/xpg4/bin/sh $ALT_DEF_SHELLS
+do
+ test -x $sh || continue
+ use_defshell $sh
+ break
+done
+case "$defshell_path$DEFSHELL_INDEX" in
+"") ;;
+*DEFSHELL_INDEX_CUSTOM)
echo "Using: SHELL=$defshell_path" >&6
AC_DEFINE_UNQUOTED(DEFSHELL_CUSTOM, "$defshell_path", Path of default shell)
-fi
-if test -n "$DEFSHELL_INDEX"; then
- AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default)
-fi
+ ;;
+/*INDEX*)
+ echo "Using: SHELL=$DEFSHELL_INDEX ($defshell_path)" | sed 's,DEFSHELL_INDEX_,,' >&6
+ AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default)
+AC_DEFINE_UNQUOTED(DEFSHELL_PATH, "$defshell_path", Path of default shell)
+ ;;
+*)
+ echo "Using: SHELL=$DEFSHELL_INDEX" | sed 's,DEFSHELL_INDEX_,,' >&6
+ AC_DEFINE_UNQUOTED(DEFSHELL_INDEX, $DEFSHELL_INDEX, Shell spec to use by default)
+ ;;
+esac
dnl
AC_SUBST(machine)
AC_SUBST(force_machine)
@@ -423,15 +523,29 @@ AC_SUBST(mksrc)
AC_SUBST(default_sys_path)
AC_SUBST(INSTALL)
AC_SUBST(GCC)
+AC_SUBST(diff)
AC_SUBST(diff_u)
AC_SUBST(use_meta)
AC_SUBST(use_filemon)
AC_SUBST(filemon_h)
AC_SUBST(_MAKE_VERSION)
+AC_SUBST(UTC_1)
bm_outfiles="Makefile.config unit-tests/Makefile.config make-bootstrap.sh"
if test $use_makefile = yes; then
bm_outfiles="makefile $bm_outfiles"
fi
+
+here=`'pwd'`
+: srcdir=$srcdir
+: here= $here
+case "$here" in
+$srcdir/obj*) # make sure we put unit-tests/Makefile.config in the right place
+ obj=`basename $here`
+ mkdir -p $srcdir/unit-tests/$obj
+ test -d unit-tests || ln -s ../unit-tests/$obj unit-tests
+ ;;
+esac
+
AC_OUTPUT($bm_outfiles)
cat <<EOF
diff --git a/contrib/bmake/dir.c b/contrib/bmake/dir.c
index 627e654387f8..e10d7b7421db 100644
--- a/contrib/bmake/dir.c
+++ b/contrib/bmake/dir.c
@@ -1,4 +1,4 @@
-/* $NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $ */
+/* $NetBSD: dir.c,v 1.275 2021/11/28 21:46:17 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -138,7 +138,7 @@
#include "job.h"
/* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */
-MAKE_RCSID("$NetBSD: dir.c,v 1.272 2021/04/04 10:13:09 rillig Exp $");
+MAKE_RCSID("$NetBSD: dir.c,v 1.275 2021/11/28 21:46:17 rillig Exp $");
/*
* A search path is a list of CachedDir structures. A CachedDir has in it the
@@ -246,12 +246,6 @@ typedef struct OpenDirs {
HashTable /* of CachedDirListNode */ table;
} OpenDirs;
-typedef enum CachedStatsFlags {
- CST_NONE = 0,
- CST_LSTAT = 1 << 0, /* call lstat(2) instead of stat(2) */
- CST_UPDATE = 1 << 1 /* ignore existing cached entry */
-} CachedStatsFlags;
-
SearchPath dirSearchPath = { LST_INIT }; /* main search path */
@@ -419,9 +413,9 @@ OpenDirs_Remove(OpenDirs *odirs, const char *name)
*/
static int
cached_stats(const char *pathname, struct cached_stat *out_cst,
- CachedStatsFlags flags)
+ bool useLstat, bool forceRefresh)
{
- HashTable *tbl = flags & CST_LSTAT ? &lmtimes : &mtimes;
+ HashTable *tbl = useLstat ? &lmtimes : &mtimes;
struct stat sys_st;
struct cached_stat *cst;
int rc;
@@ -430,14 +424,14 @@ cached_stats(const char *pathname, struct cached_stat *out_cst,
return -1; /* This can happen in meta mode. */
cst = HashTable_FindValue(tbl, pathname);
- if (cst != NULL && !(flags & CST_UPDATE)) {
+ if (cst != NULL && !forceRefresh) {
*out_cst = *cst;
DEBUG2(DIR, "Using cached time %s for %s\n",
Targ_FmtTime(cst->cst_mtime), pathname);
return 0;
}
- rc = (flags & CST_LSTAT ? lstat : stat)(pathname, &sys_st);
+ rc = (useLstat ? lstat : stat)(pathname, &sys_st);
if (rc == -1)
return -1; /* don't cache negative lookups */
@@ -462,13 +456,13 @@ cached_stats(const char *pathname, struct cached_stat *out_cst,
int
cached_stat(const char *pathname, struct cached_stat *cst)
{
- return cached_stats(pathname, cst, CST_NONE);
+ return cached_stats(pathname, cst, false, false);
}
int
cached_lstat(const char *pathname, struct cached_stat *cst)
{
- return cached_stats(pathname, cst, CST_LSTAT);
+ return cached_stats(pathname, cst, true, false);
}
/* Initialize the directories module. */
@@ -1127,9 +1121,8 @@ found:
}
static bool
-FindFileAbsolute(SearchPath *path, bool const seenDotLast,
- const char *const name, const char *const base,
- char **out_file)
+FindFileAbsolute(SearchPath *path, bool seenDotLast,
+ const char *name, const char *base, char **out_file)
{
char *file;
SearchPathNode *ln;
@@ -1448,7 +1441,7 @@ ResolveFullName(GNode *gn)
fullName = Dir_FindFile(gn->name, Suff_FindPath(gn));
- if (fullName == NULL && gn->flags & FROM_DEPEND &&
+ if (fullName == NULL && gn->flags.fromDepend &&
!Lst_IsEmpty(&gn->implicitParents))
fullName = ResolveMovedDepends(gn);
@@ -1471,7 +1464,7 @@ ResolveFullName(GNode *gn)
* The found file is stored in gn->path, unless the node already had a path.
*/
void
-Dir_UpdateMTime(GNode *gn, bool recheck)
+Dir_UpdateMTime(GNode *gn, bool forceRefresh)
{
char *fullName;
struct cached_stat cst;
@@ -1488,7 +1481,7 @@ Dir_UpdateMTime(GNode *gn, bool recheck)
fullName = ResolveFullName(gn);
- if (cached_stats(fullName, &cst, recheck ? CST_UPDATE : CST_NONE) < 0) {
+ if (cached_stats(fullName, &cst, false, forceRefresh) < 0) {
if (gn->type & OP_MEMBER) {
if (fullName != gn->path)
free(fullName);
diff --git a/contrib/bmake/enum.c b/contrib/bmake/enum.c
deleted file mode 100755
index a0ae8569b322..000000000000
--- a/contrib/bmake/enum.c
+++ /dev/null
@@ -1,80 +0,0 @@
-/* $NetBSD: enum.c,v 1.15 2021/02/02 17:56:31 rillig Exp $ */
-
-/*
- Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "make.h"
-
-MAKE_RCSID("$NetBSD: enum.c,v 1.15 2021/02/02 17:56:31 rillig Exp $");
-
-/*
- * Convert a bitset into a string representation, showing the names of the
- * individual bits.
- *
- * Optionally, shortcuts for groups of bits can be added. To have an effect,
- * they need to be listed before their individual bits.
- */
-const char *
-Enum_FlagsToString(char *buf, size_t buf_size,
- int value, const EnumToStringSpec *spec)
-{
- const char *buf_start = buf;
- const char *sep = "";
- size_t sep_len = 0;
-
- for (; spec->es_value != 0; spec++) {
- size_t name_len;
-
- if ((value & spec->es_value) != spec->es_value)
- continue;
- value &= ~spec->es_value;
-
- assert(buf_size >= sep_len + 1);
- memcpy(buf, sep, sep_len);
- buf += sep_len;
- buf_size -= sep_len;
-
- name_len = strlen(spec->es_name);
- assert(buf_size >= name_len + 1);
- memcpy(buf, spec->es_name, name_len);
- buf += name_len;
- buf_size -= name_len;
-
- sep = ENUM__SEP;
- sep_len = sizeof ENUM__SEP - 1;
- }
-
- /* If this assertion fails, the listed enum values are incomplete. */
- assert(value == 0);
-
- if (buf == buf_start)
- return "none";
-
- assert(buf_size >= 1);
- buf[0] = '\0';
- return buf_start;
-}
diff --git a/contrib/bmake/enum.h b/contrib/bmake/enum.h
deleted file mode 100755
index e10fcae045f6..000000000000
--- a/contrib/bmake/enum.h
+++ /dev/null
@@ -1,179 +0,0 @@
-/* $NetBSD: enum.h,v 1.19 2021/03/15 16:00:05 rillig Exp $ */
-
-/*
- Copyright (c) 2020 Roland Illig <rillig@NetBSD.org>
- All rights reserved.
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- 1. Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
- 2. Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS
- BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- POSSIBILITY OF SUCH DAMAGE.
- */
-
-#ifndef MAKE_ENUM_H
-#define MAKE_ENUM_H
-
-/* Generate string representations for bitmasks and simple enums. */
-
-#include <stddef.h>
-
-typedef struct EnumToStringSpec {
- int es_value;
- const char *es_name;
-} EnumToStringSpec;
-
-
-const char *Enum_FlagsToString(char *, size_t, int, const EnumToStringSpec *);
-
-
-/* For Enum_FlagsToString, the separator between flags. */
-#define ENUM__SEP "|"
-
-/*
- * Generate the string that joins all possible flags, to see how large the
- * buffer must be.
- */
-#define ENUM__JOIN_STR_1(v1) \
- #v1
-#define ENUM__JOIN_STR_2(v1, v2) \
- ENUM__JOIN_STR_1(v1) ENUM__SEP \
- ENUM__JOIN_STR_1(v2)
-#define ENUM__JOIN_STR_4(v1, v2, v3, v4) \
- ENUM__JOIN_STR_2(v1, v2) ENUM__SEP \
- ENUM__JOIN_STR_2(v3, v4)
-#define ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8) \
- ENUM__JOIN_STR_4(v1, v2, v3, v4) ENUM__SEP \
- ENUM__JOIN_STR_4(v5, v6, v7, v8)
-#define ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
- v09, v10, v11, v12, v13, v14, v15, v16) \
- ENUM__JOIN_STR_8(v01, v02, v03, v04, v05, v06, v07, v08) ENUM__SEP \
- ENUM__JOIN_STR_8(v09, v10, v11, v12, v13, v14, v15, v16)
-
-#define ENUM__JOIN_2(part1, part2) \
- part1 ENUM__SEP part2
-#define ENUM__JOIN_3(part1, part2, part3) \
- part1 ENUM__SEP part2 ENUM__SEP part3
-#define ENUM__JOIN_4(part1, part2, part3, part4) \
- part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4
-#define ENUM__JOIN_5(part1, part2, part3, part4, part5) \
- part1 ENUM__SEP part2 ENUM__SEP part3 ENUM__SEP part4 ENUM__SEP part5
-
-/* List the pairs of enum value and corresponding name. */
-#define ENUM__SPEC_1(v1) \
- { v1, #v1 }
-#define ENUM__SPEC_2(v1, v2) \
- ENUM__SPEC_1(v1), \
- ENUM__SPEC_1(v2)
-#define ENUM__SPEC_4(v1, v2, v3, v4) \
- ENUM__SPEC_2(v1, v2), \
- ENUM__SPEC_2(v3, v4)
-#define ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8) \
- ENUM__SPEC_4(v1, v2, v3, v4), \
- ENUM__SPEC_4(v5, v6, v7, v8)
-#define ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
- v09, v10, v11, v12, v13, v14, v15, v16) \
- ENUM__SPEC_8(v01, v02, v03, v04, v05, v06, v07, v08), \
- ENUM__SPEC_8(v09, v10, v11, v12, v13, v14, v15, v16)
-
-#define ENUM__SPECS_2(part1, part2) \
- { part1, part2, { 0, "" } }
-#define ENUM__SPECS_3(part1, part2, part3) \
- { part1, part2, part3, { 0, "" } }
-#define ENUM__SPECS_4(part1, part2, part3, part4) \
- { part1, part2, part3, part4, { 0, "" } }
-#define ENUM__SPECS_5(part1, part2, part3, part4, part5) \
- { part1, part2, part3, part4, part5, { 0, "" } }
-
-
-/* Declare the necessary data structures for calling Enum_FlagsToString. */
-#define ENUM__FLAGS_RTTI(typnam, specs, joined) \
- static const EnumToStringSpec typnam ## _ ## ToStringSpecs[] = specs; \
- enum { typnam ## _ ## ToStringSize = sizeof (joined) }; \
- MAKE_INLINE const char *typnam ## _ToString(char *buf, typnam value) \
- { return Enum_FlagsToString(buf, typnam ## _ ## ToStringSize, \
- value, typnam ## _ ## ToStringSpecs); \
- } \
- extern void enum_flags_rtti_dummy(void)
-
-/*
- * Declare the necessary data structures for calling Enum_FlagsToString
- * for an enum with 3 flags.
- */
-#define ENUM_FLAGS_RTTI_3(typnam, v1, v2, v3) \
- ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_2( \
- ENUM__SPEC_2(v1, v2), \
- ENUM__SPEC_1(v3)), \
- ENUM__JOIN_2( \
- ENUM__JOIN_STR_2(v1, v2), \
- ENUM__JOIN_STR_1(v3)))
-
-/*
- * Declare the necessary data structures for calling Enum_FlagsToString
- * for an enum with 6 flags.
- */
-#define ENUM_FLAGS_RTTI_6(typnam, v1, v2, v3, v4, v5, v6) \
- ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_2( \
- ENUM__SPEC_4(v1, v2, v3, v4), \
- ENUM__SPEC_2(v5, v6)), \
- ENUM__JOIN_2( \
- ENUM__JOIN_STR_4(v1, v2, v3, v4), \
- ENUM__JOIN_STR_2(v5, v6)))
-
-/*
- * Declare the necessary data structures for calling Enum_FlagsToString
- * for an enum with 9 flags.
- */
-#define ENUM_FLAGS_RTTI_9(typnam, v1, v2, v3, v4, v5, v6, v7, v8, v9) \
- ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_2( \
- ENUM__SPEC_8(v1, v2, v3, v4, v5, v6, v7, v8), \
- ENUM__SPEC_1(v9)), \
- ENUM__JOIN_2( \
- ENUM__JOIN_STR_8(v1, v2, v3, v4, v5, v6, v7, v8), \
- ENUM__JOIN_STR_1(v9)))
-
-/*
- * Declare the necessary data structures for calling Enum_FlagsToString
- * for an enum with 31 flags.
- */
-#define ENUM_FLAGS_RTTI_31(typnam, \
- v01, v02, v03, v04, v05, v06, v07, v08, \
- v09, v10, v11, v12, v13, v14, v15, v16, \
- v17, v18, v19, v20, v21, v22, v23, v24, \
- v25, v26, v27, v28, v29, v30, v31) \
- ENUM__FLAGS_RTTI(typnam, \
- ENUM__SPECS_5( \
- ENUM__SPEC_16(v01, v02, v03, v04, v05, v06, v07, v08, \
- v09, v10, v11, v12, v13, v14, v15, v16), \
- ENUM__SPEC_8(v17, v18, v19, v20, v21, v22, v23, v24), \
- ENUM__SPEC_4(v25, v26, v27, v28), \
- ENUM__SPEC_2(v29, v30), \
- ENUM__SPEC_1(v31)), \
- ENUM__JOIN_5( \
- ENUM__JOIN_STR_16(v01, v02, v03, v04, v05, v06, v07, v08, \
- v09, v10, v11, v12, v13, v14, v15, v16), \
- ENUM__JOIN_STR_8(v17, v18, v19, v20, v21, v22, v23, v24), \
- ENUM__JOIN_STR_4(v25, v26, v27, v28), \
- ENUM__JOIN_STR_2(v29, v30), \
- ENUM__JOIN_STR_1(v31)))
-
-#endif
diff --git a/contrib/bmake/filemon/filemon_ktrace.c b/contrib/bmake/filemon/filemon_ktrace.c
index 1abef7e78af1..53a85cbe74f7 100644
--- a/contrib/bmake/filemon/filemon_ktrace.c
+++ b/contrib/bmake/filemon/filemon_ktrace.c
@@ -1,4 +1,4 @@
-/* $NetBSD: filemon_ktrace.c,v 1.14 2021/02/01 21:34:41 rillig Exp $ */
+/* $NetBSD: filemon_ktrace.c,v 1.15 2021/07/31 09:30:17 rillig Exp $ */
/*
* Copyright (c) 2019 The NetBSD Foundation, Inc.
@@ -227,7 +227,6 @@ filemon_open(void)
/* Success! */
return F;
- (void)fclose(F->in);
fail1: (void)close(ktrpipe[0]);
(void)close(ktrpipe[1]);
fail0: free(F);
diff --git a/contrib/bmake/for.c b/contrib/bmake/for.c
index 615efb7634c9..013b0f21d841 100644
--- a/contrib/bmake/for.c
+++ b/contrib/bmake/for.c
@@ -1,4 +1,4 @@
-/* $NetBSD: for.c,v 1.142 2021/04/03 11:08:40 rillig Exp $ */
+/* $NetBSD: for.c,v 1.150 2021/12/12 15:44:41 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@@ -45,9 +45,9 @@
*
* 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 variable
- * names are replaced with expressions of the form ${:U...} or $(:U...).
- * After that, the body is treated like a file from an .include directive.
+ * body is scanned for variable 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.
*
* Interface:
* For_Eval Evaluate the loop in the passed line.
@@ -58,7 +58,7 @@
#include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: for.c,v 1.142 2021/04/03 11:08:40 rillig Exp $");
+MAKE_RCSID("$NetBSD: for.c,v 1.150 2021/12/12 15:44:41 rillig Exp $");
/* One of the variables to the left of the "in" in a .for loop. */
@@ -70,17 +70,13 @@ typedef struct ForVar {
typedef struct ForLoop {
Buffer body; /* Unexpanded body of the loop */
Vector /* of ForVar */ vars; /* Iteration variables */
- Words items; /* Substitution items */
+ SubstringWords items; /* Substitution items */
Buffer curBody; /* Expanded body of the current iteration */
- /* Is any of the names 1 character long? If so, when the variable values
- * are substituted, the parser must handle $V expressions as well, not
- * only ${V} and $(V). */
- bool short_var;
- unsigned int sub_next; /* Where to continue iterating */
+ unsigned int nextItem; /* Where to continue iterating */
} ForLoop;
-static ForLoop *accumFor; /* Loop being accumulated */
+static ForLoop *accumFor; /* Loop being accumulated */
static int forLevel = 0; /* Nesting level */
@@ -91,11 +87,9 @@ ForLoop_New(void)
Buf_Init(&f->body);
Vector_Init(&f->vars, sizeof(ForVar));
- f->items.words = NULL;
- f->items.freeIt = NULL;
+ SubstringWords_Init(&f->items);
Buf_Init(&f->curBody);
- f->short_var = false;
- f->sub_next = 0;
+ f->nextItem = 0;
return f;
}
@@ -111,7 +105,7 @@ ForLoop_Free(ForLoop *f)
}
Vector_Done(&f->vars);
- Words_Free(f->items);
+ SubstringWords_Free(f->items);
Buf_Done(&f->curBody);
free(f);
@@ -150,8 +144,6 @@ ForLoop_ParseVarnames(ForLoop *f, const char **pp)
p += 2;
break;
}
- if (len == 1)
- f->short_var = true;
ForLoop_AddVar(f, p, len);
p += len;
@@ -178,10 +170,10 @@ ForLoop_ParseItems(ForLoop *f, const char *p)
return false;
}
- f->items = Str_Words(items, false);
+ f->items = Substring_Words(items, false);
free(items);
- if (f->items.len == 1 && f->items.words[0][0] == '\0')
+ if (f->items.len == 1 && Substring_IsEmpty(f->items.words[0]))
f->items.len = 0; /* .for var in ${:U} */
if (f->items.len != 0 && f->items.len % f->vars.len != 0) {
@@ -285,33 +277,32 @@ For_Accum(const char *line)
static size_t
-for_var_len(const char *var)
+ExprLen(const char *s, const char *e)
{
- char ch, var_start, var_end;
+ char expr_open, expr_close;
int depth;
- size_t len;
+ const char *p;
- var_start = *var;
- if (var_start == '\0')
- /* just escape the $ */
- return 0;
+ if (s == e)
+ return 0; /* just escape the '$' */
- if (var_start == '(')
- var_end = ')';
- else if (var_start == '{')
- var_end = '}';
+ expr_open = s[0];
+ if (expr_open == '(')
+ expr_close = ')';
+ else if (expr_open == '{')
+ expr_close = '}';
else
return 1; /* Single char variable */
depth = 1;
- for (len = 1; (ch = var[len++]) != '\0';) {
- if (ch == var_start)
+ for (p = s + 1; p != e; p++) {
+ if (*p == expr_open)
depth++;
- else if (ch == var_end && --depth == 0)
- return len;
+ else if (*p == expr_close && --depth == 0)
+ return (size_t)(p + 1 - s);
}
- /* Variable end not found, escape the $ */
+ /* Expression end not found, escape the $ */
return 0;
}
@@ -320,12 +311,13 @@ for_var_len(const char *var)
* that characters that break this syntax must be backslash-escaped.
*/
static bool
-NeedsEscapes(const char *value, char endc)
+NeedsEscapes(Substring value, char endc)
{
const char *p;
- for (p = value; *p != '\0'; p++) {
- if (*p == ':' || *p == '$' || *p == '\\' || *p == endc)
+ for (p = value.start; p != value.end; p++) {
+ if (*p == ':' || *p == '$' || *p == '\\' || *p == endc ||
+ *p == '\n')
return true;
}
return false;
@@ -338,34 +330,44 @@ NeedsEscapes(const char *value, char endc)
* The result is later unescaped by ApplyModifier_Defined.
*/
static void
-Buf_AddEscaped(Buffer *cmds, const char *item, char endc)
+Buf_AddEscaped(Buffer *cmds, Substring item, char endc)
{
+ const char *p;
char ch;
if (!NeedsEscapes(item, endc)) {
- Buf_AddStr(cmds, item);
+ Buf_AddBytesBetween(cmds, item.start, item.end);
return;
}
/* Escape ':', '$', '\\' and 'endc' - these will be removed later by
* :U processing, see ApplyModifier_Defined. */
- while ((ch = *item++) != '\0') {
+ for (p = item.start; p != item.end; p++) {
+ ch = *p;
if (ch == '$') {
- size_t len = for_var_len(item);
+ size_t len = ExprLen(p + 1, item.end);
if (len != 0) {
- Buf_AddBytes(cmds, item - 1, len + 1);
- item += len;
+ /*
+ * XXX: Should a '\' be added here?
+ * See directive-for-escape.mk, ExprLen.
+ */
+ Buf_AddBytes(cmds, p, 1 + len);
+ p += len;
continue;
}
Buf_AddByte(cmds, '\\');
} else if (ch == ':' || ch == '\\' || ch == endc)
Buf_AddByte(cmds, '\\');
+ else if (ch == '\n') {
+ Parse_Error(PARSE_FATAL, "newline in .for value");
+ ch = ' '; /* prevent newline injection */
+ }
Buf_AddByte(cmds, ch);
}
}
/*
- * While expanding the body of a .for loop, replace the variable name of an
+ * When expanding the body of a .for loop, replace the variable name of an
* expression like ${i} or ${i:...} or $(i) or $(i:...) with ":Uvalue".
*/
static void
@@ -376,8 +378,8 @@ ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
const char *p = *pp;
for (i = 0; i < f->vars.len; i++) {
- ForVar *forVar = Vector_Get(&f->vars, i);
- char *varname = forVar->name;
+ const ForVar *forVar = Vector_Get(&f->vars, i);
+ const char *varname = forVar->name;
size_t varnameLen = forVar->nameLen;
if (varnameLen >= (size_t)(bodyEnd - p))
@@ -396,7 +398,7 @@ ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
Buf_AddStr(&f->curBody, ":U");
Buf_AddEscaped(&f->curBody,
- f->items.words[f->sub_next + i], endc);
+ f->items.words[f->nextItem + i], endc);
p += varnameLen;
*inout_mark = p;
@@ -406,18 +408,18 @@ ForLoop_SubstVarLong(ForLoop *f, const char **pp, const char *bodyEnd,
}
/*
- * While expanding the body of a .for loop, replace single-character
+ * When expanding the body of a .for loop, replace single-character
* variable expressions like $i with their ${:U...} expansion.
*/
static void
ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark)
{
const char ch = *p;
- ForVar *vars;
+ const ForVar *vars;
size_t i;
/* Skip $$ and stupid ones. */
- if (!f->short_var || strchr("}):$", ch) != NULL)
+ if (ch == '}' || ch == ')' || ch == ':' || ch == '$')
return;
vars = Vector_Get(&f->vars, 0);
@@ -429,10 +431,12 @@ ForLoop_SubstVarShort(ForLoop *f, const char *p, const char **inout_mark)
return;
found:
+ Buf_AddBytesBetween(&f->curBody, *inout_mark, p);
+ *inout_mark = p + 1;
+
/* Replace $<ch> with ${:U<value>} */
- Buf_AddBytesBetween(&f->curBody, *inout_mark, p), *inout_mark = p + 1;
Buf_AddStr(&f->curBody, "{:U");
- Buf_AddEscaped(&f->curBody, f->items.words[f->sub_next + i], '}');
+ Buf_AddEscaped(&f->curBody, f->items.words[f->nextItem + i], '}');
Buf_AddByte(&f->curBody, '}');
}
@@ -446,14 +450,14 @@ found:
* defined, see unit-tests/varname-empty.mk for more details.
*
* The detection of substitutions of the loop control variables is naive.
- * Many of the modifiers use '\' to escape '$' (not '$'), so it is possible
- * to contrive a makefile where an unwanted substitution happens.
+ * Many of the modifiers use '\$' instead of '$$' to escape '$', so it is
+ * possible to contrive a makefile where an unwanted substitution happens.
*/
static void
ForLoop_SubstBody(ForLoop *f)
{
const char *p, *bodyEnd;
- const char *mark; /* where the last replacement left off */
+ const char *mark; /* where the last substitution left off */
Buf_Empty(&f->curBody);
@@ -461,9 +465,9 @@ ForLoop_SubstBody(ForLoop *f)
bodyEnd = f->body.data + f->body.len;
for (p = mark; (p = strchr(p, '$')) != NULL;) {
if (p[1] == '{' || p[1] == '(') {
+ char endc = p[1] == '{' ? '}' : ')';
p += 2;
- ForLoop_SubstVarLong(f, &p, bodyEnd,
- p[-1] == '{' ? '}' : ')', &mark);
+ ForLoop_SubstVarLong(f, &p, bodyEnd, endc, &mark);
} else if (p[1] != '\0') {
ForLoop_SubstVarShort(f, p + 1, &mark);
p += 2;
@@ -483,7 +487,7 @@ ForReadMore(void *v_arg, size_t *out_len)
{
ForLoop *f = v_arg;
- if (f->sub_next == f->items.len) {
+ if (f->nextItem == f->items.len) {
/* No more iterations */
ForLoop_Free(f);
return NULL;
@@ -491,7 +495,7 @@ ForReadMore(void *v_arg, size_t *out_len)
ForLoop_SubstBody(f);
DEBUG1(FOR, "For: loop body:\n%s", f->curBody.data);
- f->sub_next += (unsigned int)f->vars.len;
+ f->nextItem += (unsigned int)f->vars.len;
*out_len = f->curBody.len;
return f->curBody.data;
@@ -513,5 +517,5 @@ For_Run(int lineno)
return;
}
- Parse_SetInput(NULL, lineno, -1, ForReadMore, f);
+ Parse_PushInput(NULL, lineno, -1, ForReadMore, f);
}
diff --git a/contrib/bmake/hash.c b/contrib/bmake/hash.c
index 8b503ac31fb5..ec0079fa4ba4 100644
--- a/contrib/bmake/hash.c
+++ b/contrib/bmake/hash.c
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $ */
+/* $NetBSD: hash.c,v 1.66 2021/12/07 21:58:01 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -74,7 +74,7 @@
#include "make.h"
/* "@(#)hash.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: hash.c,v 1.64 2021/04/11 12:46:54 rillig Exp $");
+MAKE_RCSID("$NetBSD: hash.c,v 1.66 2021/12/07 21:58:01 rillig Exp $");
/*
* The ratio of # entries to # buckets at which we rebuild the table to
@@ -152,7 +152,7 @@ HashTable_FindEntryBySubstring(HashTable *t, Substring key, unsigned int h)
unsigned int chainlen = 0;
#ifdef DEBUG_HASH_LOOKUP
- DEBUG4(HASH, "%s: %p h=%08x key=%.*s\n", __func__, t, h,
+ DEBUG5(HASH, "%s: %p h=%08x key=%.*s\n", __func__, t, h,
(int)Substring_Length(key), key.start);
#endif
@@ -333,15 +333,6 @@ HashTable_DeleteEntry(HashTable *t, HashEntry *he)
abort();
}
-/* Set things up for iterating over all entries in the hash table. */
-void
-HashIter_Init(HashIter *hi, HashTable *t)
-{
- hi->table = t;
- hi->nextBucket = 0;
- hi->entry = NULL;
-}
-
/*
* Return the next entry in the hash table, or NULL if the end of the table
* is reached.
diff --git a/contrib/bmake/hash.h b/contrib/bmake/hash.h
index 8e7a567b6dba..8ff5490bdd95 100644
--- a/contrib/bmake/hash.h
+++ b/contrib/bmake/hash.h
@@ -1,4 +1,4 @@
-/* $NetBSD: hash.h,v 1.40 2021/04/11 12:46:54 rillig Exp $ */
+/* $NetBSD: hash.h,v 1.41 2021/12/07 21:58:01 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -120,6 +120,15 @@ HashEntry_Set(HashEntry *h, void *datum)
h->value = datum;
}
+/* Set things up for iterating over all entries in the hash table. */
+MAKE_INLINE void
+HashIter_Init(HashIter *hi, HashTable *t)
+{
+ hi->table = t;
+ hi->nextBucket = 0;
+ hi->entry = NULL;
+}
+
void HashTable_Init(HashTable *);
void HashTable_Done(HashTable *);
HashEntry *HashTable_FindEntry(HashTable *, const char *);
diff --git a/contrib/bmake/import.sh b/contrib/bmake/import.sh
index b80e120daab1..08f352609fe8 100755
--- a/contrib/bmake/import.sh
+++ b/contrib/bmake/import.sh
@@ -26,6 +26,7 @@ option_parsing() {
*=*) eval "$1"; shift;;
--) shift; break;;
-a) TARBALL=$2; shift 2;;
+ -d) RM=echo; shift;;
-n) ECHO=echo; shift;;
-P) PR=$2; shift 2;;
-r) REVIEWER=$2; shift 2;;
@@ -55,6 +56,7 @@ TF=/tmp/.$USER.$$
Cd `dirname $0`
test -s ${TARBALL:-/dev/null} || Error need TARBALL
here=`pwd`
+SB=${SB:-`dirname $here`}
# thing should match what the TARBALL contains
thing=`basename $here`
@@ -66,7 +68,7 @@ esac
VERSION=`grep '^_MAKE_VERSION' VERSION | sed 's,.*=[[:space:]]*,,'`
rm -f *~
-mkdir -p ../tmp
+mkdir -p $SB/tmp
# new files are handled automatically
# but we need to rm if needed
@@ -81,12 +83,15 @@ comm -13 $TF.adds $TF.rms > $TF.rm
if [ -z "$ECHO" ]; then
test -s $TF.rm && xargs rm -f < $TF.rm
$GIT add -A
- $GIT diff --staged | tee ../tmp/bmake-import.diff
- echo "$GIT tag -a vendor/NetBSD/bmake/$VERSION" > ../tmp/bmake-post.sh
- echo "After you commit, run $here/../tmp/bmake-post.sh"
+ $GIT diff --staged | tee $SB/tmp/bmake-import.diff
+ { echo "$GIT tag -a vendor/NetBSD/bmake/$VERSION"
+ echo "echo \"When ready do: $GIT push --follow-tags\""
+ } > $SB/tmp/bmake-post.sh
+ echo "After you commit, run $SB/tmp/bmake-post.sh"
else
comm -23 $TF.adds $TF.rms > $TF.add
test -s $TF.rm && { echo Removing:; cat $TF.rm; }
test -s $TF.add && { echo Adding:; cat $TF.add; }
$GIT diff
fi
+${RM:-rm} -f $TF.*
diff --git a/contrib/bmake/job.c b/contrib/bmake/job.c
index e4b77477e816..f90c35bb6d49 100644
--- a/contrib/bmake/job.c
+++ b/contrib/bmake/job.c
@@ -1,4 +1,4 @@
-/* $NetBSD: job.c,v 1.435 2021/06/16 09:47:51 rillig Exp $ */
+/* $NetBSD: job.c,v 1.440 2021/11/28 19:51:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -155,7 +155,7 @@
#include "trace.h"
/* "@(#)job.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: job.c,v 1.435 2021/06/16 09:47:51 rillig Exp $");
+MAKE_RCSID("$NetBSD: job.c,v 1.440 2021/11/28 19:51:06 rillig Exp $");
/*
* A shell defines how the commands are run. All commands for a target are
@@ -794,8 +794,8 @@ ShellWriter_WriteFmt(ShellWriter *wr, const char *fmt, const char *arg)
DEBUG1(JOB, fmt, arg);
(void)fprintf(wr->f, fmt, arg);
- /* XXX: Is flushing needed in any case, or only if f == stdout? */
- (void)fflush(wr->f);
+ if (wr->f == stdout)
+ (void)fflush(wr->f);
}
static void
@@ -1097,10 +1097,20 @@ DebugFailedJob(const Job *job)
if (!DEBUG(ERROR))
return;
- debug_printf("\n*** Failed target: %s\n*** Failed commands:\n",
- job->node->name);
- for (ln = job->node->commands.first; ln != NULL; ln = ln->next)
- debug_printf("\t%s\n", (const char *)ln->datum);
+ debug_printf("\n");
+ debug_printf("*** Failed target: %s\n", job->node->name);
+ debug_printf("*** Failed commands:\n");
+ for (ln = job->node->commands.first; ln != NULL; ln = ln->next) {
+ const char *cmd = ln->datum;
+ debug_printf("\t%s\n", cmd);
+
+ if (strchr(cmd, '$') != NULL) {
+ char *xcmd;
+ (void)Var_Subst(cmd, job->node, VARE_WANTRES, &xcmd);
+ debug_printf("\t=> %s\n", xcmd);
+ free(xcmd);
+ }
+ }
}
static void
@@ -1192,7 +1202,9 @@ JobFinish (Job *job, WAIT_T status)
JobClosePipes(job);
if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
- (void)fclose(job->cmdFILE);
+ if (fclose(job->cmdFILE) != 0)
+ Punt("Cannot write shell script for '%s': %s",
+ job->node->name, strerror(errno));
job->cmdFILE = NULL;
}
done = true;
@@ -1384,7 +1396,7 @@ Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...))
* this node's parents so they never get examined.
*/
- if (gn->flags & FROM_DEPEND) {
+ if (gn->flags.fromDepend) {
if (!Job_RunTarget(".STALE", gn->fname))
fprintf(stdout,
"%s: %s, %d: ignoring stale %s for %s\n",
@@ -1558,7 +1570,9 @@ JobExec(Job *job, char **argv)
watchfd(job);
if (job->cmdFILE != NULL && job->cmdFILE != stdout) {
- (void)fclose(job->cmdFILE);
+ if (fclose(job->cmdFILE) != 0)
+ Punt("Cannot write shell script for '%s': %s",
+ job->node->name, strerror(errno));
job->cmdFILE = NULL;
}
@@ -2128,7 +2142,7 @@ Job_CatchOutput(void)
JobRestartJobs();
} else if (count == 0)
Punt("unexpected eof on token pipe");
- else
+ else if (errno != EAGAIN)
Punt("token pipe read: %s", strerror(errno));
nready--;
}
@@ -2182,8 +2196,11 @@ InitShellNameAndPath(void)
return;
}
#endif
-
+#ifdef DEFSHELL_PATH
+ shellPath = DEFSHELL_PATH;
+#else
shellPath = str_concat3(_PATH_DEFSHELLDIR, "/", shellName);
+#endif
}
void
diff --git a/contrib/bmake/lst.h b/contrib/bmake/lst.h
index cddd6439f611..02f1ae9ec38c 100644
--- a/contrib/bmake/lst.h
+++ b/contrib/bmake/lst.h
@@ -1,4 +1,4 @@
-/* $NetBSD: lst.h,v 1.98 2021/04/03 11:08:40 rillig Exp $ */
+/* $NetBSD: lst.h,v 1.99 2021/12/05 10:11:31 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
@@ -142,9 +142,9 @@ ListNode *Lst_FindDatum(List *, const void *);
/* Insert a datum before the given node. */
void Lst_InsertBefore(List *, ListNode *, void *);
-/* Place a datum at the front of the list. */
+/* Add a datum at the front of the list. */
void Lst_Prepend(List *, void *);
-/* Place a datum at the end of the list. */
+/* Add a datum at the end of the list. */
void Lst_Append(List *, void *);
/* Remove the node from the list. */
void Lst_Remove(List *, ListNode *);
diff --git a/contrib/bmake/main.c b/contrib/bmake/main.c
index 85a8a1cce7a1..60be60c7ca90 100644
--- a/contrib/bmake/main.c
+++ b/contrib/bmake/main.c
@@ -1,4 +1,4 @@
-/* $NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $ */
+/* $NetBSD: main.c,v 1.541 2021/08/14 13:32:12 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -111,7 +111,7 @@
#include "trace.h"
/* "@(#)main.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: main.c,v 1.540 2021/06/18 12:54:17 rillig Exp $");
+MAKE_RCSID("$NetBSD: main.c,v 1.541 2021/08/14 13:32:12 rillig Exp $");
#if defined(MAKE_NATIVE) && !defined(lint)
__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993 "
"The Regents of the University of California. "
@@ -1127,7 +1127,7 @@ InitObjdir(const char *machine, const char *machine_arch)
static void
UnlimitFiles(void)
{
-#if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE))
+#if defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)
struct rlimit rl;
if (getrlimit(RLIMIT_NOFILE, &rl) != -1 &&
rl.rlim_cur != rl.rlim_max) {
@@ -1685,7 +1685,7 @@ main_CleanUp(void)
static int
main_Exit(bool outOfDate)
{
- if (opts.strict && (main_errors > 0 || Parse_GetFatals() > 0))
+ if (opts.strict && (main_errors > 0 || Parse_NumErrors() > 0))
return 2; /* Not 1 so -q can distinguish error */
return outOfDate ? 1 : 0;
}
diff --git a/contrib/bmake/make-bootstrap.sh.in b/contrib/bmake/make-bootstrap.sh.in
index 0ecce455da74..829ae7e280e2 100755
--- a/contrib/bmake/make-bootstrap.sh.in
+++ b/contrib/bmake/make-bootstrap.sh.in
@@ -59,7 +59,7 @@ do_link() {
${CC} ${LDSTATIC} ${LDFLAGS} -o "$output" "$@" ${LIBS}
}
-BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o enum.o for.o getopt hash.o \
+BASE_OBJECTS="arch.o buf.o compat.o cond.o dir.o for.o hash.o \
lst.o make.o make_malloc.o metachar.o parse.o sigcompat.o str.o \
suff.o targ.o trace.o var.o util.o"
diff --git a/contrib/bmake/make.1 b/contrib/bmake/make.1
index b50a3b4a74b9..dbbff753673a 100644
--- a/contrib/bmake/make.1
+++ b/contrib/bmake/make.1
@@ -1,4 +1,4 @@
-.\" $NetBSD: make.1,v 1.296 2021/02/04 21:42:46 rillig Exp $
+.\" $NetBSD: make.1,v 1.300 2021/12/12 20:45:48 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 December 22, 2020
+.Dd December 12, 2021
.Dt MAKE 1
.Os
.Sh NAME
@@ -1174,6 +1174,9 @@ executes.
.It Ev .SHELL
The pathname of the shell used to run target scripts.
It is read-only.
+.It Ev .SUFFIXES
+The list of known suffixes.
+It is read-only.
.It Ev .TARGETS
The list of targets explicitly specified on the command line, if any.
.It Ev VPATH
@@ -1244,8 +1247,20 @@ but selects all words which do not match
.Ar pattern .
.It Cm \&:O
Orders every word in variable alphabetically.
+.It Cm \&:On
+Orders every word in variable numerically.
+A number followed by one of
+.Ql k ,
+.Ql M
+or
+.Ql G
+is multiplied by the appropriate factor (1024 (k), 1048576 (M), or
+1073741824 (G)).
+Both upper- and lower-case letters are accepted.
.It Cm \&:Or
Orders every word in variable in reverse alphabetical order.
+.It Cm \&:Orn
+Orders every word in variable in reverse numerical order.
.It Cm \&:Ox
Shuffles the words in variable.
The results will be different each time you are referring to the
diff --git a/contrib/bmake/make.c b/contrib/bmake/make.c
index a85a497be32d..2c1a243965f1 100644
--- a/contrib/bmake/make.c
+++ b/contrib/bmake/make.c
@@ -1,4 +1,4 @@
-/* $NetBSD: make.c,v 1.244 2021/04/04 10:05:08 rillig Exp $ */
+/* $NetBSD: make.c,v 1.248 2021/11/28 23:12:51 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.244 2021/04/04 10:05:08 rillig Exp $");
+MAKE_RCSID("$NetBSD: make.c,v 1.248 2021/11/28 23:12:51 rillig Exp $");
/* Sequence # to detect recursion. */
static unsigned int checked_seqno = 1;
@@ -138,35 +138,83 @@ make_abort(GNode *gn, int lineno)
abort();
}
-ENUM_FLAGS_RTTI_31(GNodeType,
- OP_DEPENDS, OP_FORCE, OP_DOUBLEDEP,
-/* OP_OPMASK is omitted since it combines other flags */
- OP_OPTIONAL, OP_USE, OP_EXEC, OP_IGNORE,
- OP_PRECIOUS, OP_SILENT, OP_MAKE, OP_JOIN,
- OP_MADE, OP_SPECIAL, OP_USEBEFORE, OP_INVISIBLE,
- OP_NOTMAIN, OP_PHONY, OP_NOPATH, OP_WAIT,
- OP_NOMETA, OP_META, OP_NOMETA_CMP, OP_SUBMAKE,
- OP_TRANSFORM, OP_MEMBER, OP_LIB, OP_ARCHV,
- OP_HAS_COMMANDS, OP_SAVE_CMDS, OP_DEPS_FOUND, OP_MARK);
-
-ENUM_FLAGS_RTTI_9(GNodeFlags,
- REMAKE, CHILDMADE, FORCE, DONE_WAIT,
- DONE_ORDER, FROM_DEPEND, DONE_ALLSRC, CYCLE,
- DONECYCLE);
+static const char *
+GNodeType_ToString(GNodeType type, void **freeIt)
+{
+ Buffer buf;
+
+ Buf_InitSize(&buf, 32);
+#define ADD(flag) Buf_AddFlag(&buf, (type & (flag)) != OP_NONE, #flag)
+ ADD(OP_DEPENDS);
+ ADD(OP_FORCE);
+ ADD(OP_DOUBLEDEP);
+ ADD(OP_OPTIONAL);
+ ADD(OP_USE);
+ ADD(OP_EXEC);
+ ADD(OP_IGNORE);
+ ADD(OP_PRECIOUS);
+ ADD(OP_SILENT);
+ ADD(OP_MAKE);
+ ADD(OP_JOIN);
+ ADD(OP_MADE);
+ ADD(OP_SPECIAL);
+ ADD(OP_USEBEFORE);
+ ADD(OP_INVISIBLE);
+ ADD(OP_NOTMAIN);
+ ADD(OP_PHONY);
+ ADD(OP_NOPATH);
+ ADD(OP_WAIT);
+ ADD(OP_NOMETA);
+ ADD(OP_META);
+ ADD(OP_NOMETA_CMP);
+ ADD(OP_SUBMAKE);
+ ADD(OP_TRANSFORM);
+ ADD(OP_MEMBER);
+ ADD(OP_LIB);
+ ADD(OP_ARCHV);
+ ADD(OP_HAS_COMMANDS);
+ ADD(OP_SAVE_CMDS);
+ ADD(OP_DEPS_FOUND);
+ ADD(OP_MARK);
+#undef ADD
+ return buf.len == 0 ? "none" : (*freeIt = Buf_DoneData(&buf));
+}
+
+static const char *
+GNodeFlags_ToString(GNodeFlags flags, void **freeIt)
+{
+ Buffer buf;
+
+ Buf_InitSize(&buf, 32);
+#define ADD(flag, name) Buf_AddFlag(&buf, flags.flag, name)
+ ADD(remake, "REMAKE");
+ ADD(childMade, "CHILDMADE");
+ ADD(force, "FORCE");
+ ADD(doneWait, "DONE_WAIT");
+ ADD(doneOrder, "DONE_ORDER");
+ ADD(fromDepend, "FROM_DEPEND");
+ ADD(doneAllsrc, "DONE_ALLSRC");
+ ADD(cycle, "CYCLE");
+ ADD(doneCycle, "DONECYCLE");
+#undef ADD
+ return buf.len == 0 ? "none" : (*freeIt = Buf_DoneData(&buf));
+}
void
GNode_FprintDetails(FILE *f, const char *prefix, const GNode *gn,
const char *suffix)
{
- char type_buf[GNodeType_ToStringSize];
- char flags_buf[GNodeFlags_ToStringSize];
+ void *type_freeIt = NULL;
+ void *flags_freeIt = NULL;
fprintf(f, "%s%s, type %s, flags %s%s",
prefix,
GNodeMade_Name(gn->made),
- GNodeType_ToString(type_buf, gn->type),
- GNodeFlags_ToString(flags_buf, gn->flags),
+ GNodeType_ToString(gn->type, &type_freeIt),
+ GNodeFlags_ToString(gn->flags, &flags_freeIt),
suffix);
+ free(type_freeIt);
+ free(flags_freeIt);
}
bool
@@ -286,8 +334,8 @@ GNode_IsOODate(GNode *gn)
*/
DEBUG0(MAKE, ".JOIN node...");
DEBUG1(MAKE, "source %smade...",
- gn->flags & CHILDMADE ? "" : "not ");
- oodate = (gn->flags & CHILDMADE) != 0;
+ gn->flags.childMade ? "" : "not ");
+ oodate = gn->flags.childMade;
} else if (gn->type & (OP_FORCE | OP_EXEC | OP_PHONY)) {
/*
* A node which is the object of the force (!) operator or
@@ -315,10 +363,10 @@ GNode_IsOODate(GNode *gn)
* child after it was considered made.
*/
if (DEBUG(MAKE)) {
- if (gn->flags & FORCE)
+ if (gn->flags.force)
debug_printf("non existing child...");
}
- oodate = (gn->flags & FORCE) != 0;
+ oodate = gn->flags.force;
}
#ifdef USE_META
@@ -568,7 +616,7 @@ UpdateImplicitParentsVars(GNode *cgn, const char *cname)
for (ln = cgn->implicitParents.first; ln != NULL; ln = ln->next) {
GNode *pgn = ln->datum;
- if (pgn->flags & REMAKE) {
+ if (pgn->flags.remake) {
Var_Set(pgn, IMPSRC, cname);
if (cpref != NULL)
Var_Set(pgn, PREFIX, cpref);
@@ -585,7 +633,7 @@ IsWaitingForOrder(GNode *gn)
for (ln = gn->order_pred.first; ln != NULL; ln = ln->next) {
GNode *ogn = ln->datum;
- if (GNode_IsDone(ogn) || !(ogn->flags & REMAKE))
+ if (GNode_IsDone(ogn) || !ogn->flags.remake)
continue;
DEBUG2(MAKE,
@@ -684,13 +732,13 @@ Make_Update(GNode *cgn)
debug_printf(", unmade %d ", pgn->unmade - 1);
}
- if (!(pgn->flags & REMAKE)) {
+ if (!pgn->flags.remake) {
/* This parent isn't needed */
DEBUG0(MAKE, "- not needed\n");
continue;
}
if (mtime == 0 && !(cgn->type & OP_WAIT))
- pgn->flags |= FORCE;
+ pgn->flags.force = true;
/*
* If the parent has the .MADE attribute, its timestamp got
@@ -707,7 +755,7 @@ Make_Update(GNode *cgn)
if (!(cgn->type & (OP_EXEC | OP_USE | OP_USEBEFORE))) {
if (cgn->made == MADE)
- pgn->flags |= CHILDMADE;
+ pgn->flags.childMade = true;
GNode_UpdateYoungestChild(pgn, cgn);
}
@@ -740,7 +788,7 @@ Make_Update(GNode *cgn)
* nodes.
*/
if (pgn->unmade != 0 && !(centurion->type & OP_WAIT)
- && !(centurion->flags & DONE_ORDER)) {
+ && !centurion->flags.doneOrder) {
DEBUG0(MAKE, "- unmade children\n");
continue;
}
@@ -873,7 +921,7 @@ GNode_SetLocalVars(GNode *gn)
{
GNodeListNode *ln;
- if (gn->flags & DONE_ALLSRC)
+ if (gn->flags.doneAllsrc)
return;
UnmarkChildren(gn);
@@ -887,7 +935,7 @@ GNode_SetLocalVars(GNode *gn)
if (gn->type & OP_JOIN)
Var_Set(gn, TARGET, GNode_VarAllsrc(gn));
- gn->flags |= DONE_ALLSRC;
+ gn->flags.doneAllsrc = true;
}
static bool
@@ -942,7 +990,7 @@ MakeBuildParent(GNode *pn, GNodeListNode *toBeMadeNext)
if (!MakeBuildChild(pn, toBeMadeNext)) {
/* When this node is built, reschedule its parents. */
- pn->flags |= DONE_ORDER;
+ pn->flags.doneOrder = true;
}
}
@@ -1085,7 +1133,7 @@ static void MakePrintStatusList(GNodeList *, int *);
static bool
MakePrintStatus(GNode *gn, int *errors)
{
- if (gn->flags & DONECYCLE) {
+ if (gn->flags.doneCycle) {
/*
* We've completely processed this node before, don't do
* it again.
@@ -1094,7 +1142,7 @@ MakePrintStatus(GNode *gn, int *errors)
}
if (gn->unmade == 0) {
- gn->flags |= DONECYCLE;
+ gn->flags.doneCycle = true;
switch (gn->made) {
case UPTODATE:
printf("`%s%s' is up to date.\n", gn->name,
@@ -1138,17 +1186,17 @@ MakePrintStatus(GNode *gn, int *errors)
* If printing cycles and came to one that has unmade children,
* print out the cycle by recursing on its children.
*/
- if (!(gn->flags & CYCLE)) {
+ if (!gn->flags.cycle) {
/* First time we've seen this node, check all children */
- gn->flags |= CYCLE;
+ gn->flags.cycle = true;
MakePrintStatusList(&gn->children, errors);
/* Mark that this node needn't be processed again */
- gn->flags |= DONECYCLE;
+ gn->flags.doneCycle = true;
return false;
}
/* Only output the error once per node */
- gn->flags |= DONECYCLE;
+ gn->flags.doneCycle = true;
Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num);
if ((*errors)++ > 100)
/* Abandon the whole error report */
@@ -1177,7 +1225,7 @@ ExamineLater(GNodeList *examine, GNodeList *toBeExamined)
for (ln = toBeExamined->first; ln != NULL; ln = ln->next) {
GNode *gn = ln->datum;
- if (gn->flags & REMAKE)
+ if (gn->flags.remake)
continue;
if (gn->type & (OP_USE | OP_USEBEFORE))
continue;
@@ -1213,10 +1261,10 @@ Make_ExpandUse(GNodeList *targs)
while (!Lst_IsEmpty(&examine)) {
GNode *gn = Lst_Dequeue(&examine);
- if (gn->flags & REMAKE)
+ if (gn->flags.remake)
/* We've looked at this one already */
continue;
- gn->flags |= REMAKE;
+ gn->flags.remake = true;
DEBUG2(MAKE, "Make_ExpandUse: examine %s%s\n",
gn->name, gn->cohort_num);
@@ -1301,7 +1349,7 @@ Make_ProcessWait(GNodeList *targs)
*/
pgn = GNode_New(".MAIN");
- pgn->flags = REMAKE;
+ pgn->flags.remake = true;
pgn->type = OP_PHONY | OP_DEPENDS;
/* Get it displayed in the diag dumps */
Lst_Prepend(Targ_List(), pgn);
@@ -1329,9 +1377,9 @@ Make_ProcessWait(GNodeList *targs)
pgn = Lst_Dequeue(&examine);
/* We only want to process each child-list once */
- if (pgn->flags & DONE_WAIT)
+ if (pgn->flags.doneWait)
continue;
- pgn->flags |= DONE_WAIT;
+ pgn->flags.doneWait = true;
DEBUG1(MAKE, "Make_ProcessWait: examine %s\n", pgn->name);
if (pgn->type & OP_DOUBLEDEP)
diff --git a/contrib/bmake/make.h b/contrib/bmake/make.h
index a074923c643e..6f14c44eb067 100644
--- a/contrib/bmake/make.h
+++ b/contrib/bmake/make.h
@@ -1,4 +1,4 @@
-/* $NetBSD: make.h,v 1.263 2021/06/21 10:33:11 rillig Exp $ */
+/* $NetBSD: make.h,v 1.270 2021/11/28 23:12:51 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -136,25 +136,38 @@
#endif
#define MAKE_INLINE static inline MAKE_ATTR_UNUSED
+
+/* MAKE_STATIC marks a function that may or may not be inlined. */
+#if defined(lint)
+/* As of 2021-07-31, NetBSD lint ignores __attribute__((unused)). */
+#define MAKE_STATIC MAKE_INLINE
+#else
#define MAKE_STATIC static MAKE_ATTR_UNUSED
+#endif
#if __STDC_VERSION__ >= 199901L || defined(lint) || defined(USE_C99_BOOLEAN)
#include <stdbool.h>
+#elif defined(__bool_true_false_are_defined)
+/*
+ * All files of make must be compiled with the same definition of bool.
+ * Since one of the files includes <stdbool.h>, that means the header is
+ * available on this platform. Recompile everything with -DUSE_C99_BOOLEAN.
+ */
+#error "<stdbool.h> is included in pre-C99 mode"
+#elif defined(bool) || defined(true) || defined(false)
+/*
+ * In pre-C99 mode, make does not expect that bool is already defined.
+ * You need to ensure that all translation units use the same definition for
+ * bool.
+ */
+#error "bool/true/false is defined in pre-C99 mode"
#else
-#ifndef bool
-typedef unsigned int Boolean;
-#define bool Boolean
-#endif
-#ifndef true
+typedef unsigned char bool;
#define true 1
-#endif
-#ifndef false
#define false 0
#endif
-#endif
#include "lst.h"
-#include "enum.h"
#include "make_malloc.h"
#include "str.h"
#include "hash.h"
@@ -324,26 +337,25 @@ typedef enum GNodeType {
OP_NOTARGET = OP_NOTMAIN | OP_USE | OP_EXEC | OP_TRANSFORM
} GNodeType;
-typedef enum GNodeFlags {
- GNF_NONE = 0,
+typedef struct GNodeFlags {
/* this target needs to be (re)made */
- REMAKE = 1 << 0,
+ bool remake:1;
/* children of this target were made */
- CHILDMADE = 1 << 1,
+ bool childMade:1;
/* children don't exist, and we pretend made */
- FORCE = 1 << 2,
+ bool force:1;
/* Set by Make_ProcessWait() */
- DONE_WAIT = 1 << 3,
+ bool doneWait:1;
/* Build requested by .ORDER processing */
- DONE_ORDER = 1 << 4,
+ bool doneOrder:1;
/* Node created from .depend */
- FROM_DEPEND = 1 << 5,
+ bool fromDepend:1;
/* We do it once only */
- DONE_ALLSRC = 1 << 6,
+ bool doneAllsrc:1;
/* Used by MakePrintStatus */
- CYCLE = 1 << 12,
+ bool cycle:1;
/* Used by MakePrintStatus */
- DONECYCLE = 1 << 13
+ bool doneCycle:1;
} GNodeFlags;
typedef struct List StringList;
@@ -583,7 +595,7 @@ void debug_printf(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2);
do { \
if (DEBUG(module)) \
debug_printf args; \
- } while (/*CONSTCOND*/false)
+ } while (false)
#define DEBUG0(module, text) \
DEBUG_IMPL(module, ("%s", text))
@@ -710,7 +722,7 @@ bool GNode_ShouldExecute(GNode *gn);
MAKE_INLINE bool
GNode_IsTarget(const GNode *gn)
{
- return (gn->type & OP_OPMASK) != 0;
+ return (gn->type & OP_OPMASK) != OP_NONE;
}
MAKE_INLINE const char *
@@ -722,7 +734,7 @@ GNode_Path(const GNode *gn)
MAKE_INLINE bool
GNode_IsWaitingFor(const GNode *gn)
{
- return (gn->flags & REMAKE) && gn->made <= REQUESTED;
+ return gn->flags.remake && gn->made <= REQUESTED;
}
MAKE_INLINE bool
@@ -758,16 +770,13 @@ GNode_VarArchive(GNode *gn) { return GNode_ValueDirect(gn, ARCHIVE); }
MAKE_INLINE const char *
GNode_VarMember(GNode *gn) { return GNode_ValueDirect(gn, MEMBER); }
-#if defined(__GNUC__) && __STDC_VERSION__ >= 199901L
-#define UNCONST(ptr) ({ \
- union __unconst { \
- const void *__cp; \
- void *__p; \
- } __d; \
- __d.__cp = ptr, __d.__p; })
-#else
-#define UNCONST(ptr) (void *)(ptr)
-#endif
+MAKE_INLINE void *
+UNCONST(const void *ptr)
+{
+ void *ret;
+ memcpy(&ret, &ptr, sizeof(ret));
+ return ret;
+}
/* At least GNU/Hurd systems lack hardcoded MAXPATHLEN/PATH_MAX */
#ifdef HAVE_LIMITS_H
@@ -833,6 +842,16 @@ pp_skip_hspace(char **pp)
# define MAKE_RCSID(id) extern void do_not_define_rcsid(void)
#elif defined(MAKE_NATIVE)
# include <sys/cdefs.h>
+# ifndef __IDSTRING
+# define __IDSTRING(name,string) \
+ static const char name[] MAKE_ATTR_UNUSED = string
+# endif
+# ifndef __RCSID
+# define __RCSID(s) __IDSTRING(rcsid,s)
+# endif
+# ifndef __COPYRIGHT
+# define __COPYRIGHT(s) __IDSTRING(copyright,s)
+# endif
# define MAKE_RCSID(id) __RCSID(id)
#elif defined(MAKE_ALL_IN_ONE) && defined(__COUNTER__)
# define MAKE_RCSID_CONCAT(x, y) CONCAT(x, y)
diff --git a/contrib/bmake/meta.c b/contrib/bmake/meta.c
index c1f78136fb7c..67c57647f2af 100644
--- a/contrib/bmake/meta.c
+++ b/contrib/bmake/meta.c
@@ -1,4 +1,4 @@
-/* $NetBSD: meta.c,v 1.181 2021/04/04 10:05:08 rillig Exp $ */
+/* $NetBSD: meta.c,v 1.185 2021/11/27 22:04:02 rillig Exp $ */
/*
* Implement 'meta' mode.
@@ -98,6 +98,9 @@ extern char **environ;
#if !defined(HAVE_STRSEP)
# define strsep(s, d) stresep((s), (d), '\0')
#endif
+#if !defined(HAVE_STRESEP)
+char * stresep(char **, const char *, int);
+#endif
/*
* Filemon is a kernel module which snoops certain syscalls.
@@ -149,11 +152,11 @@ meta_open_filemon(BuildMon *pbm)
else
pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL, 0);
if ((dupfd = dup(pbm->mon_fd)) == -1) {
- err(1, "Could not dup filemon output!");
+ Punt("Could not dup filemon output: %s", strerror(errno));
}
(void)fcntl(dupfd, F_SETFD, FD_CLOEXEC);
if (filemon_setfd(pbm->filemon, dupfd) == -1) {
- err(1, "Could not set filemon file descriptor!");
+ Punt("Could not set filemon file descriptor: %s", strerror(errno));
}
/* we don't need these once we exec */
(void)fcntl(pbm->mon_fd, F_SETFD, FD_CLOEXEC);
@@ -191,7 +194,9 @@ filemon_read(FILE *mfp, int fd)
error = EIO;
}
}
- fflush(mfp);
+ if (fflush(mfp) != 0)
+ Punt("Cannot write filemon data to meta file: %s",
+ strerror(errno));
if (close(fd) < 0)
error = errno;
return error;
@@ -248,8 +253,8 @@ meta_name(char *mname, size_t mnamelen,
const char *cwd)
{
char buf[MAXPATHLEN];
- char *rp;
- char *cp;
+ char *rp, *cp;
+ const char *tname_base;
char *tp;
char *dtp;
size_t ldname;
@@ -261,13 +266,13 @@ meta_name(char *mname, size_t mnamelen,
* So we use realpath() just to get the dirname, and leave the
* basename as given to us.
*/
- if ((cp = strrchr(tname, '/')) != NULL) {
+ if ((tname_base = strrchr(tname, '/')) != NULL) {
if (cached_realpath(tname, buf) != NULL) {
if ((rp = strrchr(buf, '/')) != NULL) {
rp++;
- cp++;
- if (strcmp(cp, rp) != 0)
- strlcpy(rp, cp, sizeof buf - (size_t)(rp - buf));
+ tname_base++;
+ if (strcmp(tname_base, rp) != 0)
+ strlcpy(rp, tname_base, sizeof buf - (size_t)(rp - buf));
}
tname = buf;
} else {
@@ -325,8 +330,7 @@ is_submake(const char *cmd, GNode *gn)
static const char *p_make = NULL;
static size_t p_len;
char *mp = NULL;
- char *cp;
- char *cp2;
+ const char *cp, *cp2;
bool rc = false;
if (p_make == NULL) {
@@ -412,7 +416,7 @@ printCMDs(GNode *gn, FILE *fp)
} \
return false; \
} \
-} while (/*CONSTCOND*/false)
+} while (false)
/*
@@ -523,7 +527,7 @@ meta_create(BuildMon *pbm, GNode *gn)
#endif
if ((fp = fopen(fname, "w")) == NULL)
- err(1, "Could not open meta file '%s'", fname);
+ Punt("Could not open meta file '%s': %s", fname, strerror(errno));
fprintf(fp, "# Meta data file %s\n", fname);
@@ -541,7 +545,9 @@ meta_create(BuildMon *pbm, GNode *gn)
}
fprintf(fp, "-- command output --\n");
- fflush(fp);
+ if (fflush(fp) != 0)
+ Punt("Cannot write expanded command to meta file: %s",
+ strerror(errno));
Global_Append(".MAKE.META.FILES", fname);
Global_Append(".MAKE.META.CREATED", fname);
@@ -557,9 +563,9 @@ meta_create(BuildMon *pbm, GNode *gn)
}
static bool
-boolValue(char *s)
+boolValue(const char *s)
{
- switch(*s) {
+ switch (*s) {
case '0':
case 'N':
case 'n':
@@ -594,7 +600,7 @@ void
meta_mode_init(const char *make_mode)
{
static bool once = false;
- char *cp;
+ const char *cp;
FStr value;
useMeta = true;
@@ -715,7 +721,7 @@ meta_job_child(Job *job)
pid = getpid();
if (filemon_setpid_child(pbm->filemon, pid) == -1) {
- err(1, "Could not set filemon pid!");
+ Punt("Could not set filemon pid: %s", strerror(errno));
}
}
}
@@ -855,8 +861,10 @@ meta_cmd_finish(void *pbmp)
if (pbm->filemon != NULL) {
while (filemon_process(pbm->filemon) > 0)
continue;
- if (filemon_close(pbm->filemon) == -1)
+ if (filemon_close(pbm->filemon) == -1) {
error = errno;
+ Punt("filemon failed: %s", strerror(errno));
+ }
x = filemon_read(pbm->mfp, pbm->mon_fd);
if (error == 0 && x != 0)
error = x;
@@ -1710,7 +1718,7 @@ meta_compat_parent(pid_t child)
fflush(stdout);
buf[nread] = '\0';
meta_job_output(NULL, buf, "");
- } while (/*CONSTCOND*/false);
+ } while (false);
if (metafd != -1 && FD_ISSET(metafd, &readfds) != 0) {
if (meta_job_event(NULL) <= 0)
metafd = -1;
diff --git a/contrib/bmake/metachar.c b/contrib/bmake/metachar.c
index dcb049dff44d..e99630602e3d 100644
--- a/contrib/bmake/metachar.c
+++ b/contrib/bmake/metachar.c
@@ -1,4 +1,4 @@
-/* $NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $ */
+/* $NetBSD: metachar.c,v 1.10 2021/06/21 18:54:41 rillig Exp $ */
/*
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -39,7 +39,7 @@
#include "metachar.h"
-MAKE_RCSID("$NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $");
+MAKE_RCSID("$NetBSD: metachar.c,v 1.10 2021/06/21 18:54:41 rillig Exp $");
/*
* The following array is used to make a fast determination of which
@@ -48,7 +48,7 @@ MAKE_RCSID("$NetBSD: metachar.c,v 1.9 2021/01/19 20:51:46 rillig Exp $");
* directly by us.
*/
-unsigned char _metachar[128] = {
+const unsigned char _metachar[128] = {
/* nul soh stx etx eot enq ack bel */
1, 0, 0, 0, 0, 0, 0, 0,
/* bs ht nl vt np cr so si */
diff --git a/contrib/bmake/metachar.h b/contrib/bmake/metachar.h
index 1fd1397cfe63..d6fd2299d43e 100644
--- a/contrib/bmake/metachar.h
+++ b/contrib/bmake/metachar.h
@@ -1,4 +1,4 @@
-/* $NetBSD: metachar.h,v 1.16 2021/04/03 11:08:40 rillig Exp $ */
+/* $NetBSD: metachar.h,v 1.17 2021/06/21 18:54:41 rillig Exp $ */
/*
* Copyright (c) 2015 The NetBSD Foundation, Inc.
@@ -33,9 +33,13 @@
#include "make.h"
-extern unsigned char _metachar[];
+extern const unsigned char _metachar[];
-#define is_shell_metachar(c) (_metachar[(c) & 0x7f] != 0)
+MAKE_INLINE bool
+is_shell_metachar(char c)
+{
+ return _metachar[c & 0x7f] != 0;
+}
MAKE_INLINE bool
needshell(const char *cmd)
diff --git a/contrib/bmake/mk/ChangeLog b/contrib/bmake/mk/ChangeLog
index f73c4fb68c6b..93bde20d754d 100644
--- a/contrib/bmake/mk/ChangeLog
+++ b/contrib/bmake/mk/ChangeLog
@@ -1,3 +1,107 @@
+2021-12-12 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * sys.mk: simplify; include meta.sys.mk if MK_META_MODE is yes.
+
+ * meta.sys.mk: do not check for /dev/filemon if .MAKE.PATH_FILEMON
+ is something else.
+
+ * meta.autodep.mk: we can now reference ${.SUFFIXES}
+
+ * meta2deps.py: derive a list of dirdep extensions from
+ TARGET_SPEC to trim from dirdeps.
+
+ * dirdeps.mk: flip the computation of qualified vs unqualified
+ dirdeps - it is much simpler to check for unqualified first.
+
+2021-12-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20211212
+
+ * auto.dep.mk: rearrange so that the trivial implementation
+ for recent bmake is more obvious.
+
+2021-12-07 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20211207
+
+ * Ensure guard targets are .NOTMAIN
+
+ * meta.sys.mk: check for nofilemon support when we skip level 0
+
+ * auto.dep.mk: make this usable in meta mode
+ for platforms that cannot use meta.autodep.mk
+
+ * meta2deps.py: avoid confusion if MACHINE and another
+ TARGET_SPEC_VAR have same value.
+
+2021-11-27 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * dirdeps.mk: when building dirdeps.cache, minimize the amount of
+ data put into env, by stripping ${SRCTOP}/ from each entry.
+ A long sandbox name can double the amount of memory consumed and
+ in extreme cases cause failure.
+ While we are at it, strip ${SRCTOP}/ from a lot of the debug output.
+
+2021-11-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20211111
+
+ * meta.stage.mk (LN_CP_SCRIPT): if staging to NFS cp -p can fail
+ so fallback to cp if necessary.
+
+2021-10-30 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * man.mk (CMT2DOC): use cmt2doc.py rather than the 30 year
+ old cmt2doc.pl
+
+2021-10-24 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * meta.stage.mk: stage_as_and_symlink use ${STAGE_LINK_AS_$f:U$f}
+ as the symlink (rare)
+
+2021-10-16 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * autoconf.mk: if AUTOCONF_GENERATED_MAKEFILE is set and has not
+ been read, throw an error after running configure telling user to
+ restart.
+
+2021-10-13 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20211011
+
+ * Add support for SCO_SV
+
+2021-10-01 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20211001
+
+ * man.mk: use MAN_SUFFIXES and CMT2DOC_SUFFIXES for more
+ flexibility
+
+2021-09-13 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * options.mk (describe-options): print options and their values
+ and optional description
+
+2021-09-11 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20210911
+
+ * options.mk (show-options): print options and their values
+
+2021-09-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20210909
+
+ * lib.mk: apply patch from <daniel@octaforge.org>
+ to fix shared libs on Linux
+
+2021-08-08 Simon J Gerraty <sjg@beast.crufty.net>
+
+ * install-mk (MK_VERSION): 20210808
+
+ * options.mk: issue warning for WITH_*=no
+
2021-06-16 Simon J Gerraty <sjg@beast.crufty.net>
* install-mk (MK_VERSION): 20210616
diff --git a/contrib/bmake/mk/FILES b/contrib/bmake/mk/FILES
index 51b1acd716b2..91c4387e8ccb 100644
--- a/contrib/bmake/mk/FILES
+++ b/contrib/bmake/mk/FILES
@@ -54,6 +54,7 @@ sys/Linux.mk
sys/NetBSD.mk
sys/OSF1.mk
sys/OpenBSD.mk
+sys/SCO_SV.mk
sys/SunOS.mk
sys/UnixWare.mk
target-flags.mk
diff --git a/contrib/bmake/mk/auto.dep.mk b/contrib/bmake/mk/auto.dep.mk
index d905649ab206..c30b37f44053 100644
--- a/contrib/bmake/mk/auto.dep.mk
+++ b/contrib/bmake/mk/auto.dep.mk
@@ -1,8 +1,8 @@
#
# RCSid:
-# $Id: auto.dep.mk,v 1.6 2020/08/19 17:51:53 sjg Exp $
+# $Id: auto.dep.mk,v 1.10 2021/12/11 18:57:41 sjg Exp $
#
-# @(#) Copyright (c) 2010, Simon J. Gerraty
+# @(#) Copyright (c) 2010-2021, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -30,45 +30,59 @@
# dep.mk will handle that itself.
#
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
-
-# this what bmake > 20100401 will look for
-.MAKE.DEPENDFILE ?= .depend
+__${.PARSEFILE}__: .NOTMAIN
# set this to -MMD to ignore /usr/include
# actually it ignores <> so may not be a great idea
CFLAGS_MD ?= -MD
# -MF etc not available on all gcc versions.
+.if ${COMPILER_TYPE:Ugcc} == "gcc" && ${COMPILER_VERSION:U0} < 30000
+CFLAGS_MF=
+.endif
CFLAGS_MF ?= -MF ${.TARGET:T}.d -MT ${.TARGET:T}
CFLAGS += ${CFLAGS_MD} ${CFLAGS_MF}
CXXFLAGS += ${CFLAGS_MD} ${CFLAGS_MF}
-CLEANFILES += .depend ${.MAKE.DEPENDFILE} *.d
+CLEANFILES += .depend *.d
+
+.if ${MAKE_VERSION} >= 20160218
+
+# we have .dinclude and this is all that is required
+.if empty(_SKIP_BUILD)
+_all_objs = ${OBJS} ${POBJS} ${SOBJS}
+.for d in ${_all_objs:M*o:T:O:u:%=%.d}
+.dinclude <$d>
+.endfor
+.endif
+
+.else # we lack .dinclude
+
+.if ${.MAKE.MODE:Unormal:Mmeta} != ""
+# ignore .MAKE.DEPENDFILE
+DEPENDFILE = .depend
+.else
+# this what bmake > 20100401 will look for
+.MAKE.DEPENDFILE ?= .depend
+DEPENDFILE ?= ${.MAKE.DEPENDFILE}
+.endif
+
+CLEANFILES += ${DEPENDFILE}
-.if ${MAKE_VERSION} < 20160218
# skip generating dependfile for misc targets
.if ${.TARGETS:Uall:M*all} != ""
-.END: ${.MAKE.DEPENDFILE}
+.END: ${DEPENDFILE}
.endif
# doing 'make depend' isn't a big win with this model
.if !target(depend)
-depend: ${.MAKE.DEPENDFILE}
+depend: ${DEPENDFILE}
.endif
# this is trivial
-${.MAKE.DEPENDFILE}: ${OBJS} ${POBJS} ${SOBJS}
+${DEPENDFILE}: ${OBJS} ${POBJS} ${SOBJS}
-@for f in ${.ALLSRC:M*o:T:O:u:%=%.d}; do \
echo ".-include \"$$f\""; \
done > $@
-.else
-# we have .dinclude
-.if empty(_SKIP_BUILD)
-_all_objs = ${OBJS} ${POBJS} ${SOBJS}
-.for d in ${_all_objs:M*o:T:O:u:%=%.d}
-.dinclude <$d>
-.endfor
-.endif
.endif
.endif
diff --git a/contrib/bmake/mk/autoconf.mk b/contrib/bmake/mk/autoconf.mk
index 61e6978043a8..6011a8af4df5 100644
--- a/contrib/bmake/mk/autoconf.mk
+++ b/contrib/bmake/mk/autoconf.mk
@@ -1,4 +1,4 @@
-# $Id: autoconf.mk,v 1.10 2020/08/19 17:51:53 sjg Exp $
+# $Id: autoconf.mk,v 1.16 2021/10/19 17:36:06 sjg Exp $
#
# @(#) Copyright (c) 1996-2009, Simon J. Gerraty
#
@@ -13,32 +13,37 @@
# sjg@crufty.net
#
-.NOPATH: config.h config.status
+.NOPATH: config.h config.gen config.recheck config.status
CONFIGURE_DEPS += ${.CURDIR}/config.h.in ${.CURDIR}/configure
.if !target(config.h)
-config.h: ${CONFIGURE_DEPS} config.status
+config.h: .NOTMAIN ${CONFIGURE_DEPS} config.status
./config.status
+.if !empty(AUTOCONF_GENERATED_MAKEFILE) && ${AUTOCONF_GENERATED_MAKEFILE:T:@m@${"${.MAKE.MAKEFILES:T:M$m}":?yes:no}@:Mno} != ""
+ @echo Generated ${AUTOCONF_GENERATED_MAKEFILE}, you need to restart; exit 1
+.endif
.endif
.if !target(config.status)
# avoid the targets behaving differently
+config.status: .NOTMAIN
.if exists(${.OBJDIR}/config.status)
config.status: config.recheck
.else
config.status: config.gen
.endif
-config.recheck: ${CONFIGURE_DEPS}
+config.recheck: .NOTMAIN ${CONFIGURE_DEPS} config.gen
./config.status --recheck
@touch $@
-config.gen: ${CONFIGURE_DEPS}
+config.gen: .NOTMAIN ${CONFIGURE_DEPS}
CC="${CC} ${CCMODE}" ${.CURDIR}/configure --no-create ${CONFIGURE_ARGS}
@touch $@ config.recheck
-CLEANFILES+= config.recheck config.gen config.status *.meta
+CLEANFILES+= config.recheck config.gen config.status *.meta \
+ ${AUTOCONF_GENERATED_MAKEFILE:U}
.endif
# avoid things blowing up if these are not here...
@@ -67,14 +72,14 @@ ACLOCAL += aclocal.m4
ACCONFIG += acconfig.h
.endif
-config.h.in: ${.CURDIR}/configure.in ${ACCONFIG}
+config.h.in: .NOTMAIN ${.CURDIR}/configure.in ${ACCONFIG}
(cd ${.CURDIR} && ${AUTOHEADER})
-configure: ${.CURDIR}/configure.in ${ACLOCAL}
+configure: .NOTMAIN ${.CURDIR}/configure.in ${ACLOCAL}
(cd ${.CURDIR} && ${AUTOCONF})
AUTOCONF_INPUTS += configure
-autoconf-input: ${AUTOCONF_INPUTS}
+autoconf-input: .NOTMAIN ${AUTOCONF_INPUTS}
.endif
.endif
diff --git a/contrib/bmake/mk/autodep.mk b/contrib/bmake/mk/autodep.mk
index a7bb942278c9..9104da2e2567 100644
--- a/contrib/bmake/mk/autodep.mk
+++ b/contrib/bmake/mk/autodep.mk
@@ -1,6 +1,6 @@
#
# RCSid:
-# $Id: autodep.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $
+# $Id: autodep.mk,v 1.40 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 1999-2010, Simon J. Gerraty
#
@@ -20,7 +20,7 @@
# dependencies are normally updated as part of compilation.
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
DEPENDFILE?= .depend
.for d in ${DEPENDFILE:N.depend}
@@ -67,6 +67,9 @@ __dependsrcs= ${__dependsrcsx:O:u}
CFLAGS_MD?=-MD
# -MF etc not available on all gcc versions.
# we "fix" the .o later
+.if ${COMPILER_TYPE:Ugcc} == "gcc" && ${COMPILER_VERSION:U0} < 30000
+CFLAGS_MF=
+.endif
CFLAGS_MF?=-MF ${.TARGET:T:R}.d -MT ${.TARGET:T:R}.o
CFLAGS+= ${CFLAGS_MD} ${CFLAGS_MF}
RM?= rm
diff --git a/contrib/bmake/mk/compiler.mk b/contrib/bmake/mk/compiler.mk
index b20ecaa047e3..83c0cead8f25 100644
--- a/contrib/bmake/mk/compiler.mk
+++ b/contrib/bmake/mk/compiler.mk
@@ -1,4 +1,4 @@
-# $Id: compiler.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
+# $Id: compiler.mk,v 1.10 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 2019, Simon J. Gerraty
#
@@ -14,7 +14,7 @@
#
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.if ${MACHINE} == "common"
COMPILER_TYPE = none
@@ -22,12 +22,12 @@ COMPILER_VERSION = 0
.endif
.if empty(COMPILER_TYPE) || empty(COMPILER_VERSION)
# gcc does not always say gcc
-_v != ${CC} --version 2> /dev/null | \
- egrep -i 'clang|cc|[1-9]\.[0-9]|Free Software Foundation'
+_v != (${CC} --version) 2> /dev/null | \
+ egrep -i 'clang|cc|[1-9]\.[0-9]|Free Software Foundation'; echo
.if empty(COMPILER_TYPE)
.if ${_v:Mclang} != ""
COMPILER_TYPE = clang
-.elif ${_v:M[Gg][Cc][Cc]} != "" || ${_v:MFoundation*} != ""
+.elif ${_v:M[Gg][Cc][Cc]} != "" || ${_v:MFoundation*} != "" || ${CC:T:M*gcc*} != ""
COMPILER_TYPE = gcc
.endif
.endif
diff --git a/contrib/bmake/mk/dep.mk b/contrib/bmake/mk/dep.mk
index b07191a09cbf..ea73846f5fd8 100644
--- a/contrib/bmake/mk/dep.mk
+++ b/contrib/bmake/mk/dep.mk
@@ -1,7 +1,7 @@
-# $Id: dep.mk,v 1.17 2014/08/04 05:12:27 sjg Exp $
+# $Id: dep.mk,v 1.18 2021/12/08 05:56:50 sjg Exp $
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
# handle Proc*C as well...
.if defined(SRCS)
diff --git a/contrib/bmake/mk/dirdeps.mk b/contrib/bmake/mk/dirdeps.mk
index 38ead3de37cd..5e735f236b9f 100644
--- a/contrib/bmake/mk/dirdeps.mk
+++ b/contrib/bmake/mk/dirdeps.mk
@@ -1,4 +1,4 @@
-# $Id: dirdeps.mk,v 1.140 2021/06/20 23:42:38 sjg Exp $
+# $Id: dirdeps.mk,v 1.147 2021/12/14 02:09:53 sjg Exp $
# Copyright (c) 2010-2021, Simon J. Gerraty
# Copyright (c) 2010-2018, Juniper Networks, Inc.
@@ -66,6 +66,10 @@
# processing is recursive and results in .MAKE.LEVEL 0 learning the
# dependencies of the tree wrt the initial directory (_DEP_RELDIR).
#
+# NOTE: given the extent of processing that DIRDEPS undergoes it
+# is important that any variables in entries use :U to guard
+# against surprises when undefined.
+#
# TARGET_SPEC_VARS
# The default value is just MACHINE, and for most environments
# this is sufficient. The _DIRDEP_USE target actually sets
@@ -399,7 +403,7 @@ DIRDEP_LOADAVG_REPORT = \
_DIRDEP_USE: .USE .MAKE
@for m in ${.MAKE.MAKEFILE_PREFERENCE}; do \
test -s ${.TARGET:R}/$$m || continue; \
- echo "${TRACER}Checking ${.TARGET:S,${SRCTOP}/,,} for ${.TARGET:E} ..."; \
+ echo "${TRACER}Checking ${.TARGET:S,^${SRCTOP}/,,} for ${.TARGET:E} ..."; \
${DIRDEP_USE_PRELUDE} \
MACHINE_ARCH= NO_SUBDIR=1 ${DIRDEP_USE_ENV} \
TARGET_SPEC=${.TARGET:E} \
@@ -562,7 +566,7 @@ ${DIRDEPS_CACHE}: .META .NOMETA_CMP
${"${DEBUG_DIRDEPS:Nno}":?DEBUG_DIRDEPS='${DEBUG_DIRDEPS}':} \
${.MAKEFLAGS:tW:S,-D ,-D,g:tw:M*WITH*} \
${.MAKEFLAGS:tW:S,-d ,-d,g:tw:M-d*} \
- 3>&1 1>&2 | sed 's,${SRCTOP},$${SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \
+ 3>&1 1>&2 | sed 's,${SRCTOP},_{SRCTOP},g;s,_{,$${,g' >> ${.TARGET}.new && \
mv ${.TARGET}.new ${.TARGET}
.endif
@@ -638,6 +642,7 @@ _build_dirs += ${_machines:@m@${_CURDIR}.$m@}
.endif
.if ${_debug_reldir}
+.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: nDIRDEPS=${DIRDEPS:[#]}
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: DIRDEPS='${DIRDEPS}'
.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: _machines='${_machines}'
.endif
@@ -656,11 +661,10 @@ DEP_DIRDEPS_FILTER = U
# this is what we start with
__depdirs := ${DIRDEPS:${NSkipDir}:${DEP_DIRDEPS_FILTER:ts:}:C,//+,/,g:O:u:@d@${SRCTOP}/$d@}
-# some entries may be qualified with .<machine>
-# the :M*/*/*.* just tries to limit the dirs we check to likely ones.
-# the ${d:E:M*/*} ensures we don't consider junos/usr.sbin/mgd
-__qual_depdirs := ${__depdirs:M*/*/*.*:@d@${exists($d):?:${"${d:E:M*/*}":?:${exists(${d:R}):?$d:}}}@}
-__unqual_depdirs := ${__depdirs:${__qual_depdirs:Uno:${M_ListToSkip}}}
+# some entries may be qualified with .<machine> or .<target_spec>
+# we can tell the unqualified ones easily - because they exist
+__unqual_depdirs := ${__depdirs:@d@${exists($d):?$d:}@}
+__qual_depdirs := ${__depdirs:${__unqual_depdirs:Uno:${M_ListToSkip}}}
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
# if it was called out - we likely need it.
@@ -671,9 +675,9 @@ __qual_depdirs += ${__hostdpadd}
.if ${_debug_reldir}
.info DEP_DIRDEPS_FILTER=${DEP_DIRDEPS_FILTER:ts:}
-.info depdirs=${__depdirs}
-.info qualified=${__qual_depdirs}
-.info unqualified=${__unqual_depdirs}
+.info depdirs=${__depdirs:S,^${SRCTOP}/,,}
+.info qualified=${__qual_depdirs:S,^${SRCTOP}/,,}
+.info unqualified=${__unqual_depdirs:S,^${SRCTOP}/,,}
.endif
# _build_dirs is what we will feed to _DIRDEP_USE
@@ -694,13 +698,14 @@ _build_all_dirs := ${_build_all_dirs:O:u}
# Normally if doing make -V something,
# we do not want to waste time chasing DIRDEPS
# but if we want to count the number of Makefile.depend* read, we do.
-.if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS}} == ""
+.if ${.MAKEFLAGS:M-V${_V_READ_DIRDEPS:U}} == ""
.if !empty(_build_all_dirs)
.if ${BUILD_DIRDEPS_CACHE} == "yes"
x!= echo; { echo; echo '\# ${DEP_RELDIR}.${DEP_TARGET_SPEC}'; } >&3
# guard against _new_dirdeps being too big for a single command line
-_new_dirdeps := ${_build_all_dirs:@x@${target($x):?:$x}@}
-.export _build_xtra_dirs _new_dirdeps
+_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(DEP_EXPORT_VARS)
# Discouraged, but there are always exceptions.
# Handle it here rather than explain how.
@@ -713,7 +718,7 @@ dirdeps: ${_build_all_dirs}
${_build_all_dirs}: _DIRDEP_USE
.if ${_debug_reldir}
-.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: needs: ${_build_dirs}
+.info ${DEP_RELDIR}.${DEP_TARGET_SPEC}: needs: ${_build_dirs:S,^${SRCTOP}/,,}
.endif
.if !empty(DEP_EXPORT_VARS)
@@ -724,37 +729,37 @@ DEP_EXPORT_VARS=
# this builds the dependency graph
.for m in ${_machines}
.if ${BUILD_DIRDEPS_CACHE} == "yes" && !empty(_build_dirs)
-x!= echo; { echo; echo 'DIRDEPS.${_this_dir}.$m = \'; } >&3
_cache_deps =
+x!= echo; { echo; echo 'DIRDEPS.${_this_dir}.$m = \'; } >&3
.endif
# it would be nice to do :N${.TARGET}
.if !empty(__qual_depdirs)
.for q in ${__qual_depdirs:${M_dep_qual_fixes:ts:}:E:O:u:N$m}
.if ${_debug_reldir} || ${DEBUG_DIRDEPS:@x@${${DEP_RELDIR}.$m:L:M$x}${${DEP_RELDIR}.$q:L:M$x}@} != ""
-.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q}
+.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$q:S,^${SRCTOP}/,,}
.endif
.if ${BUILD_DIRDEPS_CACHE} == "yes"
-_cache_deps += ${_build_dirs:M*.$q}
+_cache_deps += ${_build_dirs:M*.$q:S,^${SRCTOP}/,,}
.else
${_this_dir}.$m: ${_build_dirs:M*.$q}
.endif
.endfor
.endif
.if ${_debug_reldir}
-.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m}
+.info ${DEP_RELDIR}.$m: graph: ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,}
.endif
.if ${BUILD_DIRDEPS_CACHE} == "yes"
.if !empty(_build_dirs)
-_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m}
+_cache_deps += ${_build_dirs:M*.$m:N${_this_dir}.$m:S,^${SRCTOP}/,,}
.if !empty(_cache_deps)
.export _cache_deps
-x!= echo; for x in $$_cache_deps; do echo " $$x \\"; done >&3
+x!= echo; for x in $$_cache_deps; do echo " _{SRCTOP}/$$x \\"; done >&3
.endif
-# anything in _build_xtra_dirs is hooked to dirdeps: only
+# anything in _{build,env}_xtra_dirs is hooked to dirdeps: only
x!= echo; { echo; echo '${_this_dir}.$m: $${DIRDEPS.${_this_dir}.$m}'; \
echo; echo 'dirdeps: ${_this_dir}.$m \'; \
- for x in $$_build_xtra_dirs; do echo " $$x \\"; done; \
- echo; for x in $$_new_dirdeps; do echo "$$x: _DIRDEP_USE"; done; } >&3
+ 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
.else
${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m}
@@ -769,7 +774,7 @@ ${_this_dir}.$m: ${_build_dirs:M*.$m:N${_this_dir}.$m}
# once only
_dirdeps_checked.$d:
.if ${_debug_search}
-.info checking $d
+.info checking ${d:S,^${SRCTOP}/,,}
.endif
# Note: _build_all_dirs is fully qualifed so d:R is always the directory
.if exists(${d:R})
@@ -798,7 +803,7 @@ _qm := ${_m:C;(\.depend)$;\1.${d:E};:${M_dep_qual_fixes:ts:}}
.endif
# set this "just in case"
# we can skip :tA since we computed the path above
-DEP_RELDIR := ${_m:H:S,${SRCTOP}/,,}
+DEP_RELDIR := ${_m:H:S,^${SRCTOP}/,,}
# and reset this
DIRDEPS =
.if ${_debug_reldir} && ${_qm} != ${_m}
diff --git a/contrib/bmake/mk/doc.mk b/contrib/bmake/mk/doc.mk
index b6ebd8ad3d3e..06dd517473ca 100644
--- a/contrib/bmake/mk/doc.mk
+++ b/contrib/bmake/mk/doc.mk
@@ -1,7 +1,7 @@
-# $Id: doc.mk,v 1.7 2019/06/09 16:22:08 sjg Exp $
+# $Id: doc.mk,v 1.8 2021/12/08 05:56:50 sjg Exp $
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.include <init.mk>
diff --git a/contrib/bmake/mk/dpadd.mk b/contrib/bmake/mk/dpadd.mk
index aef12528f163..07528f9e926c 100644
--- a/contrib/bmake/mk/dpadd.mk
+++ b/contrib/bmake/mk/dpadd.mk
@@ -1,4 +1,4 @@
-# $Id: dpadd.mk,v 1.29 2021/04/20 02:30:44 sjg Exp $
+# $Id: dpadd.mk,v 1.30 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 2004, Simon J. Gerraty
#
@@ -77,7 +77,7 @@
#
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
# sometimes we play games with .CURDIR etc
# _* hold the original values of .*
diff --git a/contrib/bmake/mk/final.mk b/contrib/bmake/mk/final.mk
index e7285ee9a5c1..3670dfc67a55 100644
--- a/contrib/bmake/mk/final.mk
+++ b/contrib/bmake/mk/final.mk
@@ -1,7 +1,7 @@
-# $Id: final.mk,v 1.9 2018/01/24 22:57:11 sjg Exp $
+# $Id: final.mk,v 1.10 2021/12/08 05:56:50 sjg Exp $
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
# provide a hook for folk who want to do scary stuff
.-include <${.CURDIR:H}/Makefile-final.inc>
diff --git a/contrib/bmake/mk/init.mk b/contrib/bmake/mk/init.mk
index 8f6f8f2fbaf4..356c8501367e 100644
--- a/contrib/bmake/mk/init.mk
+++ b/contrib/bmake/mk/init.mk
@@ -1,4 +1,4 @@
-# $Id: init.mk,v 1.25 2020/11/27 17:59:46 sjg Exp $
+# $Id: init.mk,v 1.26 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 2002, Simon J. Gerraty
#
@@ -14,7 +14,7 @@
#
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.if ${MAKE_VERSION:U0} > 20100408
_this_mk_dir := ${.PARSEDIR:tA}
diff --git a/contrib/bmake/mk/install-mk b/contrib/bmake/mk/install-mk
index 96c35b1052ec..21d00ae919a5 100755
--- a/contrib/bmake/mk/install-mk
+++ b/contrib/bmake/mk/install-mk
@@ -55,7 +55,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: install-mk,v 1.196 2021/06/19 15:30:41 sjg Exp $
+# $Id: install-mk,v 1.206 2021/12/11 18:57:41 sjg Exp $
#
# @(#) Copyright (c) 1994 Simon J. Gerraty
#
@@ -70,7 +70,7 @@
# sjg@crufty.net
#
-MK_VERSION=20210616
+MK_VERSION=20211212
OWNER=
GROUP=
MODE=444
diff --git a/contrib/bmake/mk/java.mk b/contrib/bmake/mk/java.mk
index e2149e7089a7..3a4e7911e536 100644
--- a/contrib/bmake/mk/java.mk
+++ b/contrib/bmake/mk/java.mk
@@ -1,6 +1,6 @@
#
# RCSid:
-# $Id: java.mk,v 1.15 2020/08/19 17:51:53 sjg Exp $
+# $Id: java.mk,v 1.16 2021/12/08 05:56:50 sjg Exp $
# @(#) Copyright (c) 1998-2001, Simon J. Gerraty
#
@@ -16,7 +16,7 @@
#
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.include <init.mk>
diff --git a/contrib/bmake/mk/ldorder.mk b/contrib/bmake/mk/ldorder.mk
index 3d44df5874fe..5ae54385993e 100644
--- a/contrib/bmake/mk/ldorder.mk
+++ b/contrib/bmake/mk/ldorder.mk
@@ -1,4 +1,4 @@
-# $Id: ldorder.mk,v 1.25 2018/04/24 23:50:26 sjg Exp $
+# $Id: ldorder.mk,v 1.26 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 2015, Simon J. Gerraty
#
@@ -130,7 +130,7 @@ ${_ldorder}: ${_ldorders}
# it can also add to CFLAGS etc.
.for __inc in ${LDORDER_LIBS:S,$,.${LDORDER_INC},}
.if !target(__${__inc}__)
-__${__inc}__:
+__${__inc}__: .NOTMAIN
# make sure this is reset
LDORDER_LIBS =
_ldorders =
diff --git a/contrib/bmake/mk/lib.mk b/contrib/bmake/mk/lib.mk
index c3979414ec49..259107458ae9 100644
--- a/contrib/bmake/mk/lib.mk
+++ b/contrib/bmake/mk/lib.mk
@@ -1,7 +1,7 @@
-# $Id: lib.mk,v 1.71 2020/08/19 17:51:53 sjg Exp $
+# $Id: lib.mk,v 1.73 2021/12/08 05:56:50 sjg Exp $
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.include <init.mk>
@@ -170,7 +170,7 @@ 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_shared=-shared -Wl,-soname,lib${LIB}.so.${SHLIB_MAJOR}
LD_solib= -Wl,--whole-archive lib${LIB}_pic.a -Wl,--no-whole-archive
.if ${COMPILER_TYPE} == "gcc"
# Linux uses GNU ld, which is a multi-pass linker
diff --git a/contrib/bmake/mk/man.mk b/contrib/bmake/mk/man.mk
index 391b08afe491..ce4380fedc55 100644
--- a/contrib/bmake/mk/man.mk
+++ b/contrib/bmake/mk/man.mk
@@ -1,9 +1,12 @@
-# $Id: man.mk,v 1.20 2012/12/13 01:51:01 sjg Exp $
+# $Id: man.mk,v 1.25 2021/10/31 03:03:14 sjg Exp $
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
+
+OPTIONS_DEFAULT_NO += CMT2DOC
.include <init.mk>
+.include <options.mk>
# unlike bsd.man.mk we handle 3 approaches
# 1. install unformated nroff (default)
@@ -21,45 +24,38 @@ NROFF?= nroff
MANDIR?= /usr/share/man
MANDOC?= man
-.SUFFIXES: .1 .2 .3 .4 .5 .6 .7 .8 .9 .cat1 .cat2 .cat3 .cat4 .cat5 .cat6 \
- .cat7 .cat8 .cat9
+MAN_SUFFIXES?= .1 .2 .3 .4 .5 .6 .7 .8 .9
+.SUFFIXES: ${MAN_SUFFIXES}
+.if ${MANTARGET} == "cat"
+.SUFFIXES: ${MAN_SUFFIXES:S,.,.cat,}
+.endif
-.9.cat9 .8.cat8 .7.cat7 .6.cat6 .5.cat5 .4.cat4 .3.cat3 .2.cat2 .1.cat1:
+${MAN_SUFFIXES:@s@$s${s:S,.,.cat,}@}:
@echo "${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T}"
- @${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T} || ( rm -f ${.TARGET:T} ; false )
+ @${NROFF} -${MANDOC} ${.IMPSRC} > ${.TARGET:T}.new && \
+ mv ${.TARGET:T}.new ${.TARGET:T}
.if defined(MAN) && !empty(MAN)
-# we use cmt2doc.pl to extract manpages from source
-# this is triggered by the setting of EXTRACT_MAN or MAN being set but
-# not existsing.
-
-.if !exists(${MAN:[1]}) && !target(${MAN:[1]})
-.if defined(EXTRACT_MAN) && ${EXTRACT_MAN} == "no"
-MAN=
-.else
-.if exists(/usr/local/share/bin/cmt2doc.pl)
-CMT2DOC?= cmt2doc.pl
+.if ${MK_CMT2DOC} == "yes"
+# use cmt2doc.py to extract manpages from source
+CMT2DOC?= cmt2doc.py
CMT2DOC_OPTS?= ${CMT2DOC_ORGOPT} -pmS${.TARGET:E}
-.endif
-.ifdef CMT2DOC
-.c.8 .c.5 .c.3 .c.4 .c.1 \
- .cc.8 .cc.5 .cc.3 .cc.4 .cc.1 \
- .h.8 .h.5 .h.3 .h.4 .h.1 \
- .sh.8 .sh.5 .sh.3 .sh.4 .sh.1 \
- .pl.8 .pl.5 .pl.3 .pl.4 .pl.1:
+CMT2DOC_SUFFIXES+= .c .h .sh .pl .py
+
+.SUFFIXES: ${CMT2DOC_SUFFIXES}
+
+${CMT2DOC_SUFFIXES:@s@${MAN_SUFFIXES:@m@$s$m@}@}:
@echo "${CMT2DOC} ${.IMPSRC} > ${.TARGET:T}"
- @${CMT2DOC} ${CMT2DOC_OPTS} ${.IMPSRC} > ${.TARGET:T} || ( rm -f ${.TARGET:T} ; false )
-.else
-MAN=
-.endif
-.endif
+ @${CMT2DOC} ${CMT2DOC_OPTS} ${.IMPSRC} > ${.TARGET:T}.new && \
+ mv ${.TARGET:T}.new ${.TARGET:T}
+
.endif
_mandir=${DESTDIR}${MANDIR}/${MANTARGET}`echo $$page | sed -e 's/.*\.cat/./' -e 's/.*\.//'`
.if ${MANTARGET} == "cat"
_mfromdir?=.
-MANALL= ${MAN:S/.1$/.cat1/g:S/.2$/.cat2/g:S/.3$/.cat3/g:S/.4$/.cat4/g:S/.5$/.cat5/g:S/.6$/.cat6/g:S/.7$/.cat7/g:S/.8$/.cat8/g:S/.9$/.cat9/g}
+MANALL= ${MAN:${MAN_SUFFIXES:S,.,,:@m@S/.$m/.cat$m/@:ts:}}
.if ${MCATEXT} == ""
_minstpage=`echo $$page | sed 's/\.cat/./'`
.else
diff --git a/contrib/bmake/mk/meta.autodep.mk b/contrib/bmake/mk/meta.autodep.mk
index 5d09dbd88e81..f9ed6d305b99 100644
--- a/contrib/bmake/mk/meta.autodep.mk
+++ b/contrib/bmake/mk/meta.autodep.mk
@@ -1,4 +1,4 @@
-# $Id: meta.autodep.mk,v 1.54 2021/03/06 17:03:18 sjg Exp $
+# $Id: meta.autodep.mk,v 1.55 2021/12/13 08:12:01 sjg Exp $
#
# @(#) Copyright (c) 2010, Simon J. Gerraty
@@ -23,8 +23,12 @@ __${_this}__: .NOTMAIN
PICO?= .pico
.if defined(SRCS)
+.if ${MAKE_VERSION:U0} >= 20211212
+OBJ_EXTENSIONS += ${.SUFFIXES:M*o}
+.else
# it would be nice to be able to query .SUFFIXES
-OBJ_EXTENSIONS+= .o .po .lo ${PICO}
+OBJ_EXTENSIONS += .o .po .lo ${PICO}
+.endif
# explicit dependencies help short-circuit .SUFFIX searches
SRCS_DEP_FILTER+= N*.[hly]
@@ -163,7 +167,11 @@ _depend = .depend
# it would be nice to be able to get .SUFFIXES as ${.SUFFIXES}
# we actually only care about the .SUFFIXES of files that might be
# generated by tools like yacc.
+.if ${MAKE_VERSION:U0} >= 20211212
+DEPEND_SUFFIXES += ${.SUFFIXES:N.sh:N*[0-9aFfglopmnrSsty]}
+.else
DEPEND_SUFFIXES += .c .h .cpp .hpp .cxx .hxx .cc .hh
+.endif
.depend: .NOMETA $${.MAKE.META.CREATED} ${_this}
@echo "Updating $@: ${.OODATE:T:[1..8]}"
@egrep -i '^R .*\.(${DEPEND_SUFFIXES:tl:O:u:S,^.,,:ts|})$$' /dev/null ${.MAKE.META.FILES:T:O:u:${META_FILE_FILTER:ts:}:M*o.meta} | \
diff --git a/contrib/bmake/mk/meta.stage.mk b/contrib/bmake/mk/meta.stage.mk
index 463521b79595..1da45f4f2167 100644
--- a/contrib/bmake/mk/meta.stage.mk
+++ b/contrib/bmake/mk/meta.stage.mk
@@ -1,4 +1,4 @@
-# $Id: meta.stage.mk,v 1.61 2021/01/31 04:43:12 sjg Exp $
+# $Id: meta.stage.mk,v 1.64 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 2011-2017, Simon J. Gerraty
#
@@ -66,7 +66,7 @@ GENDIRDEPS_FILTER += Nnot-empty-is-important \
LN_CP_SCRIPT = LnCp() { \
rm -f $$2 2> /dev/null; \
{ [ -z "$$mode" ] && ${LN:Uln} $$1 $$2 2> /dev/null; } || \
- cp -p $$1 $$2; }
+ cp -p $$1 $$2 2> /dev/null || cp $$1 $$2; }
# a staging conflict should cause an error
# a warning is handy when bootstapping different options.
@@ -264,7 +264,8 @@ CLEANFILES += ${STAGE_AS_SETS:@s@stage*$s@}
# sometimes things need to be renamed as they are staged
# each ${file} will be staged as ${STAGE_AS_${file:T}}
# one could achieve the same with SYMLINKS
-# stage_as_and_symlink makes the original name a symlink to the new name
+# stage_as_and_symlink makes the original name (or ${STAGE_LINK_AS_${name}})
+# a symlink to the new name
# it is the same as using stage_as and stage_symlinks but ensures
# both operations happen together
.for s in ${STAGE_AS_SETS:O:u}
@@ -294,7 +295,7 @@ STAGE_AS_AND_SYMLINK.$s ?= ${.ALLSRC:N.dirdep:Nstage_*}
stage_as_and_symlink: stage_as_and_symlink.$s
stage_as_and_symlink.$s: .dirdep
@${STAGE_AS_SCRIPT}; StageAs ${FLAGS.$@} ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@$f ${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}}@}
- @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} $f@}
+ @${STAGE_LINKS_SCRIPT}; StageLinks -s ${STAGE_FILES_DIR.$s:U${STAGE_DIR.$s}:${STAGE_DIR_FILTER}} ${STAGE_AS_AND_SYMLINK.$s:O:@f@${STAGE_AS_${f:tA}:U${STAGE_AS_${f:T}:U${f:T}}} ${STAGE_LINK_AS_${f}:U$f}@}
@touch $@
.endif
.endif
@@ -306,7 +307,7 @@ CLEANFILES += ${STAGE_TARGETS} stage_incs stage_includes
# this lot also only makes sense the first time...
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
# stage_*links usually needs to follow any others.
# for non-jobs mode the order here matters
diff --git a/contrib/bmake/mk/meta.sys.mk b/contrib/bmake/mk/meta.sys.mk
index 77b4893a8785..7cc802e2f2fe 100644
--- a/contrib/bmake/mk/meta.sys.mk
+++ b/contrib/bmake/mk/meta.sys.mk
@@ -1,7 +1,7 @@
-# $Id: meta.sys.mk,v 1.38 2020/08/19 17:51:53 sjg Exp $
+# $Id: meta.sys.mk,v 1.42 2021/12/13 05:50:55 sjg Exp $
#
-# @(#) Copyright (c) 2010-2020, Simon J. Gerraty
+# @(#) Copyright (c) 2010-2021, Simon J. Gerraty
#
# This file is provided in the hope that it will
# be of use. There is absolutely NO WARRANTY.
@@ -30,21 +30,31 @@ SYS_MK_DIR := ${_PARSEDIR}
.endif
META_MODE += meta verbose
+.if ${MAKE_VERSION:U0} > 20130323 && empty(.MAKE.PATH_FILEMON)
+# we do not support filemon
+META_MODE += nofilemon
+MKDEP_MK ?= auto.dep.mk
+.endif
+
.MAKE.MODE ?= ${META_MODE}
-.if ${.MAKE.LEVEL} == 0
+_filemon := ${.MAKE.PATH_FILEMON:U/dev/filemon}
+
+.if empty(UPDATE_DEPENDFILE)
_make_mode := ${.MAKE.MODE} ${META_MODE}
.if ${_make_mode:M*read*} != "" || ${_make_mode:M*nofilemon*} != ""
# tell everyone we are not updating Makefile.depend*
UPDATE_DEPENDFILE = NO
.export UPDATE_DEPENDFILE
.endif
-.if ${UPDATE_DEPENDFILE:Uyes:tl} == "no" && !exists(/dev/filemon)
+.if ${_filemon:T:Mfilemon} == "filemon"
+.if ${UPDATE_DEPENDFILE:Uyes:tl} == "no" && !exists(${_filemon})
# we should not get upset
META_MODE += nofilemon
.export META_MODE
.endif
.endif
+.endif
.if !defined(NO_SILENT)
.if ${MAKE_VERSION} > 20110818
@@ -106,7 +116,7 @@ _metaError: .NOMETA .NOTMAIN
# Are we, after all, in meta mode?
.if ${.MAKE.MODE:Uno:Mmeta*} != ""
-MKDEP_MK = meta.autodep.mk
+MKDEP_MK ?= meta.autodep.mk
.if ${.MAKE.MAKEFILES:M*sys.dependfile.mk} == ""
# this does all the smarts of setting .MAKE.DEPENDFILE
@@ -140,12 +150,13 @@ META_NOECHO= :
.warning Setting UPDATE_DEPENDFILE=NO due to -k
UPDATE_DEPENDFILE= NO
.export UPDATE_DEPENDFILE
-.elif !exists(/dev/filemon)
-.error ${.newline}ERROR: The filemon module (/dev/filemon) is not loaded.
+.elif ${_filemon:T} == "filemon" && !exists(${_filemon})
+.error ${.newline}ERROR: The filemon module (${_filemon}) is not loaded.
.endif
.endif
.if ${.MAKE.LEVEL} == 0
+.if ${MK_DIRDEPS_BUILD:Uyes} == "yes"
# make sure dirdeps target exists and do it first
all: dirdeps .WAIT
dirdeps:
@@ -156,6 +167,7 @@ dirdeps:
# by default dirdeps is all we want at level0
.MAIN: dirdeps
.endif
+.endif
.endif
.else
diff --git a/contrib/bmake/mk/meta2deps.py b/contrib/bmake/mk/meta2deps.py
index 193e303de3da..76ac59465b9b 100755
--- a/contrib/bmake/mk/meta2deps.py
+++ b/contrib/bmake/mk/meta2deps.py
@@ -37,7 +37,7 @@ We only pay attention to a subset of the information in the
"""
RCSid:
- $Id: meta2deps.py,v 1.38 2021/06/17 05:20:08 sjg Exp $
+ $Id: meta2deps.py,v 1.40 2021/12/13 19:32:46 sjg Exp $
Copyright (c) 2011-2020, Simon J. Gerraty
Copyright (c) 2011-2017, Juniper Networks, Inc.
@@ -165,6 +165,19 @@ def add_trims(x):
x + '/',
x]
+def target_spec_exts(target_spec):
+ """return a list of dirdep extensions that could match target_spec"""
+
+ if target_spec.find(',') < 0:
+ return ['.'+target_spec]
+ w = target_spec.split(',')
+ n = len(w)
+ e = []
+ while n > 0:
+ e.append('.'+','.join(w[0:n]))
+ n -= 1
+ return e
+
class MetaFile:
"""class to parse meta files generated by bmake."""
@@ -226,7 +239,8 @@ class MetaFile:
self.machine = conf.get('MACHINE', '')
self.machine_arch = conf.get('MACHINE_ARCH', '')
- self.target_spec = conf.get('TARGET_SPEC', '')
+ self.target_spec = conf.get('TARGET_SPEC', self.machine)
+ self.exts = target_spec_exts(self.target_spec)
self.curdir = conf.get('CURDIR')
self.reldir = conf.get('RELDIR')
self.dpdeps = conf.get('DPDEPS')
@@ -250,7 +264,7 @@ class MetaFile:
trim_list = add_trims(self.machine)
if self.machine == 'host':
trim_list += add_trims(self.host_target)
- if self.target_spec:
+ if self.target_spec != self.machine:
trim_list += add_trims(self.target_spec)
for objroot in conf.get('OBJROOTS', []):
@@ -280,6 +294,7 @@ class MetaFile:
print("srctops=", self.srctops, file=self.debug_out)
print("objroots=", self.objroots, file=self.debug_out)
print("excludes=", self.excludes, file=self.debug_out)
+ print("ext_list=", self.exts, file=self.debug_out)
self.dirdep_re = re.compile(r'([^/]+)/(.+)')
@@ -355,10 +370,10 @@ class MetaFile:
ddep = open(ddepf, 'r').readline().strip('# \n')
if self.debug > 1:
print("found %s: %s\n" % (ddepf, ddep), file=self.debug_out)
- if ddep.endswith(self.machine):
- ddep = ddep[0:-(1+len(self.machine))]
- elif self.target_spec and ddep.endswith(self.target_spec):
- ddep = ddep[0:-(1+len(self.target_spec))]
+ for e in self.exts:
+ if ddep.endswith(e):
+ ddep = ddep[0:-len(e)]
+ break
if not ddep:
# no .dirdeps, so remember that we've seen the raw input
diff --git a/contrib/bmake/mk/obj.mk b/contrib/bmake/mk/obj.mk
index 487e25a55b6c..0df5a087f972 100644
--- a/contrib/bmake/mk/obj.mk
+++ b/contrib/bmake/mk/obj.mk
@@ -1,4 +1,4 @@
-# $Id: obj.mk,v 1.16 2020/08/19 17:51:53 sjg Exp $
+# $Id: obj.mk,v 1.17 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 1999-2010, Simon J. Gerraty
#
@@ -14,7 +14,7 @@
#
.if !target(__${.PARSEFILE:S,bsd.,,}__)
-__${.PARSEFILE:S,bsd.,,}__:
+__${.PARSEFILE:S,bsd.,,}__: .NOTMAIN
.include <init.mk>
diff --git a/contrib/bmake/mk/options.mk b/contrib/bmake/mk/options.mk
index eb5253a6b7e8..bf78ba24f1ca 100644
--- a/contrib/bmake/mk/options.mk
+++ b/contrib/bmake/mk/options.mk
@@ -1,4 +1,4 @@
-# $Id: options.mk,v 1.13 2020/08/19 17:51:53 sjg Exp $
+# $Id: options.mk,v 1.19 2021/10/03 16:29:51 sjg Exp $
#
# @(#) Copyright (c) 2012, Simon J. Gerraty
#
@@ -26,8 +26,8 @@
# User sets WITH_* and WITHOUT_* to indicate what they want.
# We set ${OPTION_PREFIX:UMK_}* which is then all we need care about.
OPTIONS_DEFAULT_VALUES += \
- ${OPTIONS_DEFAULT_NO:O:u:S,$,/no,} \
- ${OPTIONS_DEFAULT_YES:O:u:S,$,/yes,}
+ ${OPTIONS_DEFAULT_NO:U:O:u:S,$,/no,} \
+ ${OPTIONS_DEFAULT_YES:U:O:u:S,$,/yes,}
OPTION_PREFIX ?= MK_
@@ -36,6 +36,10 @@ OPTION_PREFIX ?= MK_
# DOMINANT_* is set to "yes"
# Otherwise WITH_* and WITHOUT_* override the default.
.for o in ${OPTIONS_DEFAULT_VALUES:M*/*}
+.if defined(WITH_${o:H}) && ${WITH_${o:H}} == "no"
+# a common miss-use - point out correct usage
+.warning use WITHOUT_${o:H}=1 not WITH_${o:H}=no
+.endif
.if defined(NO_${o:H}) || defined(NO${o:H})
# we cannot do it
${OPTION_PREFIX}${o:H} ?= no
@@ -77,6 +81,32 @@ ${OPTION_PREFIX}${o:H} ?= no
${OPTION_PREFIX}${o:H} ?= ${${OPTION_PREFIX}${o:T}}
.endif
.endfor
-.undef OPTIONS_DEFAULT_VALUES
+
+# allow displaying/describing set options
+.set_options := ${.set_options} \
+ ${OPTIONS_DEFAULT_VALUES:H:N.} \
+ ${OPTIONS_DEFAULT_DEPENDENT:U:H:N.} \
+
+# this can be used in .info as well as target below
+OPTIONS_SHOW ?= ${.set_options:O:u:@o@${OPTION_PREFIX}$o=${${OPTION_PREFIX}$o}@}
+# prefix for variables describing options
+OPTION_DESCRIPTION_PREFIX ?= DESCRIPTION_
+OPTION_DESCRIPTION_SEPARATOR ?= ==
+
+OPTIONS_DESCRIBE ?= ${.set_options:O:u:@o@${OPTION_PREFIX}$o=${${OPTION_PREFIX}$o}${${OPTION_DESCRIPTION_PREFIX}$o:S,^, ${OPTION_DESCRIPTION_SEPARATOR} ,1}${.newline}@}
+
+.if !commands(show-options)
+show-options: .NOTMAIN .PHONY
+ @echo; echo "${OPTIONS_SHOW:ts\n}"; echo
+.endif
+
+.if !commands(describe-options)
+describe-options: .NOTMAIN .PHONY
+ @echo; echo "${OPTIONS_DESCRIBE}"; echo
+.endif
+
+# we expect to be included more than once
+.undef OPTIONS_DEFAULT_DEPENDENT
.undef OPTIONS_DEFAULT_NO
+.undef OPTIONS_DEFAULT_VALUES
.undef OPTIONS_DEFAULT_YES
diff --git a/contrib/bmake/mk/own.mk b/contrib/bmake/mk/own.mk
index 63322297420b..7b0d74caba8a 100644
--- a/contrib/bmake/mk/own.mk
+++ b/contrib/bmake/mk/own.mk
@@ -1,7 +1,7 @@
-# $Id: own.mk,v 1.42 2020/11/27 18:00:08 sjg Exp $
+# $Id: own.mk,v 1.44 2021/12/08 05:56:50 sjg Exp $
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.if !target(__init.mk__)
.include "init.mk"
@@ -257,11 +257,13 @@ MK_NLS= no
.if ${MK_META_MODE:Uno} == "yes"
# should all be set by sys.mk if not default
TARGET_SPEC_VARS ?= MACHINE
+.if ${MAKE_VERSION} >= 20120325
.if ${TARGET_SPEC_VARS:[#]} > 1
TARGET_SPEC_VARS_REV := ${TARGET_SPEC_VARS:[-1..1]}
.else
TARGET_SPEC_VARS_REV = ${TARGET_SPEC_VARS}
.endif
+.endif
.if ${MK_STAGING} == "yes"
STAGE_ROOT?= ${OBJROOT}/stage
STAGE_OBJTOP?= ${STAGE_ROOT}/${TARGET_SPEC_VARS_REV:ts/}
diff --git a/contrib/bmake/mk/prlist.mk b/contrib/bmake/mk/prlist.mk
index aca1fde25555..3308a9522c89 100644
--- a/contrib/bmake/mk/prlist.mk
+++ b/contrib/bmake/mk/prlist.mk
@@ -1,4 +1,4 @@
-# $Id: prlist.mk,v 1.4 2020/08/19 17:51:53 sjg Exp $
+# $Id: prlist.mk,v 1.5 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 2006, Simon J. Gerraty
#
@@ -14,7 +14,7 @@
#
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
# this needs to be included after all the lists it will process
# are defined - which is why it is a separate file.
diff --git a/contrib/bmake/mk/prog.mk b/contrib/bmake/mk/prog.mk
index ea48837d5544..902b221f7fe5 100644
--- a/contrib/bmake/mk/prog.mk
+++ b/contrib/bmake/mk/prog.mk
@@ -1,7 +1,7 @@
-# $Id: prog.mk,v 1.36 2020/08/19 17:51:53 sjg Exp $
+# $Id: prog.mk,v 1.37 2021/12/08 05:56:50 sjg Exp $
.if !target(__${.PARSEFILE}__)
-__${.PARSEFILE}__:
+__${.PARSEFILE}__: .NOTMAIN
.include <init.mk>
diff --git a/contrib/bmake/mk/stage-install.sh b/contrib/bmake/mk/stage-install.sh
index 674652d1d482..97d50871f95c 100755
--- a/contrib/bmake/mk/stage-install.sh
+++ b/contrib/bmake/mk/stage-install.sh
@@ -37,7 +37,7 @@
#
# RCSid:
-# $Id: stage-install.sh,v 1.9 2020/08/28 01:04:13 sjg Exp $
+# $Id: stage-install.sh,v 1.10 2021/11/17 07:06:31 sjg Exp $
#
# @(#) Copyright (c) 2013-2020, Simon J. Gerraty
#
@@ -117,8 +117,12 @@ StageDirdep() {
t=$1
if [ -s $t.dirdep ]; then
cmp -s $_DIRDEP $t.dirdep && return
- echo "ERROR: $t installed by `cat $t.dirdep` not `cat $_DIRDEP`" >&2
- exit 1
+ case "${STAGE_CONFLICT:-error}" in
+ [Ee]*) STAGE_CONFLICT=ERROR action=exit;;
+ *) STAGE_CONFLICT=WARNING action=: ;;
+ esac
+ echo "$STAGE_CONFLICT: $t installed by `cat $t.dirdep` not `cat $_DIRDEP`" >&2
+ $action 1
fi
LnCp $_DIRDEP $t.dirdep || exit 1
}
diff --git a/contrib/bmake/mk/sys.mk b/contrib/bmake/mk/sys.mk
index 7ef8f724ef10..e39d5ac74548 100644
--- a/contrib/bmake/mk/sys.mk
+++ b/contrib/bmake/mk/sys.mk
@@ -1,4 +1,4 @@
-# $Id: sys.mk,v 1.52 2020/12/22 20:44:24 sjg Exp $
+# $Id: sys.mk,v 1.53 2021/12/13 05:50:13 sjg Exp $
#
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
#
@@ -81,11 +81,10 @@ OPTIONS_DEFAULT_DEPENDENT += \
.-include <options.mk>
-.if ${MK_DIRDEPS_BUILD:Uno} == "yes"
-MK_META_MODE = yes
+# :Uno incase options.mk not installed
+.if ${MK_META_MODE:Uno} == "yes"
.-include <meta.sys.mk>
-.elif ${MK_META_MODE:Uno} == "yes"
-.MAKE.MODE = meta verbose ${META_MODE}
+.MAKE.MODE ?= meta verbose {META_MODE}
.endif
# make sure we have a harmless value
.MAKE.MODE ?= normal
diff --git a/contrib/bmake/mk/sys.vars.mk b/contrib/bmake/mk/sys.vars.mk
index 592cbdc644dc..391967b18a2b 100644
--- a/contrib/bmake/mk/sys.vars.mk
+++ b/contrib/bmake/mk/sys.vars.mk
@@ -1,4 +1,4 @@
-# $Id: sys.vars.mk,v 1.6 2020/10/28 20:50:04 sjg Exp $
+# $Id: sys.vars.mk,v 1.7 2021/12/08 05:56:50 sjg Exp $
#
# @(#) Copyright (c) 2003-2009, Simon J. Gerraty
#
@@ -19,7 +19,7 @@
#
# _this ?= ${.PARSEFILE}
# .if !target(__${_this}__)
-# __${_this}__:
+# __${_this}__: .NOTMAIN
#
.if ${MAKE_VERSION:U0} > 20100408
_this = ${.PARSEDIR:tA}/${.PARSEFILE}
diff --git a/contrib/bmake/mk/sys/SCO_SV.mk b/contrib/bmake/mk/sys/SCO_SV.mk
new file mode 100644
index 000000000000..9bf4b7c587c5
--- /dev/null
+++ b/contrib/bmake/mk/sys/SCO_SV.mk
@@ -0,0 +1,13 @@
+# $Id: SCO_SV.mk,v 1.1 2021/10/13 16:45:52 sjg Exp $
+
+OS = SCO_SV
+OS_DEF_FLAG := -D${OS}
+
+CC ?= gcc
+CXX ?= g++
+DEV_TOOLS_PREFIX ?= /usr/xdev
+FC ?= gfortran
+INSTALL ?= /usr/gnu/bin/install
+LD ?= gcc
+
+.include "UnixWare.mk"
diff --git a/contrib/bmake/mk/sys/UnixWare.mk b/contrib/bmake/mk/sys/UnixWare.mk
index 272d3e65c2d8..9e0216399ade 100644
--- a/contrib/bmake/mk/sys/UnixWare.mk
+++ b/contrib/bmake/mk/sys/UnixWare.mk
@@ -1,16 +1,18 @@
-# $Id: UnixWare.mk,v 1.7 2020/08/19 17:51:53 sjg Exp $
+# $Id: UnixWare.mk,v 1.8 2021/10/13 16:45:52 sjg Exp $
# based on "Id: SunOS.5.sys.mk,v 1.6 2003/09/30 16:42:23 sjg Exp "
# $NetBSD: sys.mk,v 1.19.2.1 1994/07/26 19:58:31 cgd Exp $
# @(#)sys.mk 5.11 (Berkeley) 3/13/91
OS ?= UnixWare
+OS_DEF_FLAG ?= -DUNIXWARE
unix ?= We run ${OS}.
ROOT_GROUP ?= root
+DEV_TOOLS_PREFIX ?= /usr/local
-# can't fine one anywhere, so just stop the dependency
+# can't find one anywhere, so just stop the dependency
LIBCRT0 ?= /dev/null
-PATH ?=/usr/sbin:/usr/bin:/usr/ccs/bin:/usr/ccs/lib:/usr/ucb:/usr/local/bin
+PATH ?= /usr/sbin:/usr/bin:/usr/ccs/bin:/usr/ccs/lib:/usr/ucb:${DEV_TOOLS_PREFIX}/bin
.SUFFIXES: .out .a .ln .o .c ${CXX_SUFFIXES} .F .f .r .y .l .s .S .cl .p .h .sh .m4
@@ -32,8 +34,8 @@ COMPILE.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} -c
LINK.S ?= ${CC} ${AFLAGS} ${CPPFLAGS} ${LDFLAGS}
# at least gcc 2.95 on UnixWare has no internal macro to identify the system
-.if exists(/usr/local/bin/gcc)
-CC ?= gcc -pipe -DUNIXWARE
+.if exists(${DEV_TOOLS_PREFIX}/bin/gcc)
+CC ?= gcc -pipe ${OS_DEF_FLAG}
DBG ?= -O -g
STATIC ?= -static
.else
@@ -45,8 +47,8 @@ CFLAGS ?= ${DBG}
COMPILE.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} -c
LINK.c ?= ${CC} ${CFLAGS} ${CPPFLAGS} ${LDFLAGS}
-.if exists(/usr/local/bin/g++)
-CXX ?= g++ -DUNIXWARE
+.if exists(${DEV_TOOLS_PREFIX}/bin/g++)
+CXX ?= g++ ${OS_DEF_FLAG}
.else
CXX ?= c++ # XXX: don't know about UDK compilers
.endif
@@ -54,13 +56,17 @@ CXXFLAGS ?= ${CFLAGS}
COMPILE.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} -c
LINK.cc ?= ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${LDFLAGS}
+.if exists(${DEV_TOOLS_PREFIX}/bin/cpp)
+CPP ?= cpp
+.else
CPP ?= /usr/ccs/lib/cpp
+.endif
.if defined(DESTDIR)
CPPFLAGS+= -nostdinc -idirafter ${DESTDIR}/usr/include
.endif
MK_DEP ?= mkdeps.sh -N
-.if exists(/usr/local/bin/g77)
+.if exists(${DEV_TOOLS_PREFIX}/bin/g77)
FC ?= g77
.else
FC ?= f77 # XXX: don't know about UDK compilers
@@ -125,7 +131,7 @@ SIZE ?= size
TSORT ?= tsort
-.if exists(/usr/local/bin/bison)
+.if exists(${DEV_TOOLS_PREFIX}/bin/bison)
YACC ?= bison -y
.else
YACC ?= yacc
diff --git a/contrib/bmake/nonints.h b/contrib/bmake/nonints.h
index 7119d798432b..8583e50270fd 100644
--- a/contrib/bmake/nonints.h
+++ b/contrib/bmake/nonints.h
@@ -1,4 +1,4 @@
-/* $NetBSD: nonints.h,v 1.213 2021/04/11 13:35:56 rillig Exp $ */
+/* $NetBSD: nonints.h,v 1.217 2021/12/12 20:45:48 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -165,9 +165,9 @@ bool Parse_IsVar(const char *, VarAssign *out_var);
void Parse_Var(VarAssign *, GNode *);
void Parse_AddIncludeDir(const char *);
void Parse_File(const char *, int);
-void Parse_SetInput(const char *, int, int, ReadMoreProc, void *);
+void Parse_PushInput(const char *, int, int, ReadMoreProc, void *);
void Parse_MainName(GNodeList *);
-int Parse_GetFatals(void);
+int Parse_NumErrors(void);
#ifndef HAVE_STRLCPY
@@ -192,6 +192,7 @@ void Suff_FindDeps(GNode *);
SearchPath *Suff_FindPath(GNode *);
void Suff_SetNull(const char *);
void Suff_PrintAll(void);
+const char *Suff_NamesStr(void);
/* targ.c */
void Targ_Init(void);
@@ -211,7 +212,7 @@ void Targ_PrintCmds(GNode *);
void Targ_PrintNode(GNode *, int);
void Targ_PrintNodes(GNodeList *, int);
const char *Targ_FmtTime(time_t);
-void Targ_PrintType(int);
+void Targ_PrintType(GNodeType);
void Targ_PrintGraph(int);
void Targ_Propagate(void);
const char *GNodeMade_Name(GNodeMade);
diff --git a/contrib/bmake/os.sh b/contrib/bmake/os.sh
index 7e6823b240c3..1fc66ecc6a63 100644
--- a/contrib/bmake/os.sh
+++ b/contrib/bmake/os.sh
@@ -17,7 +17,7 @@
# Simon J. Gerraty <sjg@crufty.net>
# RCSid:
-# $Id: os.sh,v 1.56 2020/08/05 23:25:22 sjg Exp $
+# $Id: os.sh,v 1.59 2021/11/14 04:18:00 sjg Exp $
#
# @(#) Copyright (c) 1994 Simon J. Gerraty
#
@@ -172,7 +172,7 @@ Interix)
MACHINE=i386
MACHINE_ARCH=i386
;;
-UnixWare)
+UnixWare|SCO_SV)
OSREL=`uname -v`
OSMAJOR=`IFS=.; set $OSREL; echo $1`
MACHINE_ARCH=`uname -m`
diff --git a/contrib/bmake/parse.c b/contrib/bmake/parse.c
index 3c2d75acdf9a..6a310c74ba5c 100644
--- a/contrib/bmake/parse.c
+++ b/contrib/bmake/parse.c
@@ -1,4 +1,4 @@
-/* $NetBSD: parse.c,v 1.560 2021/06/21 10:42:06 rillig Exp $ */
+/* $NetBSD: parse.c,v 1.574 2021/12/12 15:44:41 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -124,7 +124,7 @@
#include "pathnames.h"
/* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: parse.c,v 1.560 2021/06/21 10:42:06 rillig Exp $");
+MAKE_RCSID("$NetBSD: parse.c,v 1.574 2021/12/12 15:44:41 rillig Exp $");
/* types and constants */
@@ -139,10 +139,14 @@ typedef struct IFile {
unsigned int cond_depth; /* 'if' nesting when file opened */
bool depending; /* state of doing_depend on EOF */
- /* The buffer from which the file's content is read. */
+ /*
+ * The buffer from which the file's content is read. The buffer
+ * always ends with '\n', the buffer is not null-terminated, that is,
+ * buf_end[0] is already out of bounds.
+ */
char *buf_freeIt;
char *buf_ptr; /* next char to be read */
- char *buf_end;
+ char *buf_end; /* buf_end[-1] == '\n' */
/* Function to read more data, with a single opaque argument. */
ReadMoreProc readMore;
@@ -233,7 +237,7 @@ static GNode *order_pred;
/* parser state */
/* number of fatal errors */
-static int fatals = 0;
+static int parseErrors = 0;
/*
* Variables for doing includes
@@ -276,7 +280,7 @@ SearchPath *defSysIncPath; /* default for sysIncPath */
* keyword is used as a source ("0" if the keyword isn't special as a source)
*/
static const struct {
- const char *name; /* Name of keyword */
+ const char name[17]; /* Name of keyword */
ParseSpecial spec; /* Type when used as a target */
GNodeType op; /* Operator when used as a source */
} parseKeywords[] = {
@@ -329,8 +333,6 @@ static const struct {
/* file loader */
struct loadedfile {
- /* XXX: What is the lifetime of this path? Who manages the memory? */
- const char *path; /* name, for error reports */
char *buf; /* contents buffer */
size_t len; /* length of contents */
bool used; /* XXX: have we used the data yet */
@@ -338,12 +340,11 @@ struct loadedfile {
/* XXX: What is the lifetime of the path? Who manages the memory? */
static struct loadedfile *
-loadedfile_create(const char *path, char *buf, size_t buflen)
+loadedfile_create(char *buf, size_t buflen)
{
struct loadedfile *lf;
lf = bmake_malloc(sizeof *lf);
- lf->path = path == NULL ? "(stdin)" : path;
lf->buf = buf;
lf->len = buflen;
lf->used = false;
@@ -468,8 +469,7 @@ loadfile(const char *path, int fd)
close(fd);
{
- struct loadedfile *lf = loadedfile_create(path,
- buf.data, buf.len);
+ struct loadedfile *lf = loadedfile_create(buf.data, buf.len);
Buf_DoneData(&buf);
return lf;
}
@@ -545,7 +545,7 @@ ParseIsEscaped(const char *line, const char *c)
* was first defined.
*/
static void
-ParseMark(GNode *gn)
+RememberLocation(GNode *gn)
{
IFile *curFile = CurFile();
gn->fname = curFile->fname;
@@ -628,7 +628,7 @@ ParseVErrorInternal(FILE *f, const char *fname, size_t lineno,
goto print_stack_trace;
if (type == PARSE_WARNING && !opts.parseWarnFatal)
goto print_stack_trace;
- fatals++;
+ parseErrors++;
if (type == PARSE_WARNING && !fatal_warning_error_printed) {
Error("parsing warnings being treated as errors");
fatal_warning_error_printed = true;
@@ -797,7 +797,7 @@ TryApplyDependencyOperator(GNode *gn, GNodeType op)
cohort = Targ_NewInternalNode(gn->name);
if (doing_depend)
- ParseMark(cohort);
+ RememberLocation(cohort);
/*
* Make the cohort invisible as well to avoid duplicating it
* into other variables. True, parents of this target won't
@@ -852,7 +852,7 @@ ParseDependencySourceWait(bool isSpecial)
snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number);
gn = Targ_NewInternalNode(wait_src);
if (doing_depend)
- ParseMark(gn);
+ RememberLocation(gn);
gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN;
LinkToTargets(gn, isSpecial);
@@ -912,7 +912,7 @@ ParseDependencySourceOrder(const char *src)
*/
gn = Targ_GetNode(src);
if (doing_depend)
- ParseMark(gn);
+ RememberLocation(gn);
if (order_pred != NULL) {
Lst_Append(&order_pred->order_succ, gn);
Lst_Append(&gn->order_pred, order_pred);
@@ -949,7 +949,7 @@ ParseDependencySourceOther(const char *src, GNodeType tOp,
/* Find/create the 'src' node and attach to all targets */
gn = Targ_GetNode(src);
if (doing_depend)
- ParseMark(gn);
+ RememberLocation(gn);
if (tOp != OP_NONE)
gn->type |= tOp;
else
@@ -1019,7 +1019,7 @@ ParseErrorNoDependency(const char *lstart)
(strncmp(lstart, "======", 6) == 0) ||
(strncmp(lstart, ">>>>>>", 6) == 0))
Parse_Error(PARSE_FATAL,
- "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts");
+ "Makefile appears to contain unresolved CVS/RCS/??? merge conflicts");
else if (lstart[0] == '.') {
const char *dirstart = lstart + 1;
const char *dirend;
@@ -1100,7 +1100,7 @@ ParseDependencyTargetSpecial(ParseSpecial *inout_specType,
case SP_INTERRUPT: {
GNode *gn = Targ_GetNode(targetName);
if (doing_depend)
- ParseMark(gn);
+ RememberLocation(gn);
gn->type |= OP_NOTMAIN | OP_SPECIAL;
Lst_Append(targets, gn);
break;
@@ -1230,7 +1230,7 @@ ParseDependencyTargetMundane(char *targetName, StringList *curTargs)
? Suff_AddTransform(targName)
: Targ_GetNode(targName);
if (doing_depend)
- ParseMark(gn);
+ RememberLocation(gn);
Lst_Append(targets, gn);
}
@@ -1612,10 +1612,8 @@ ParseDependencySourcesMundane(char *start, char *end,
* See the tests depsrc-*.mk.
*/
static void
-ParseDependencySources(char *const line, char *const cp,
- GNodeType const tOp,
- ParseSpecial const specType,
- SearchPathList ** inout_paths)
+ParseDependencySources(char *line, char *cp, GNodeType tOp,
+ ParseSpecial specType, SearchPathList **inout_paths)
{
if (line[0] == '\0') {
ParseDependencySourcesEmpty(specType, *inout_paths);
@@ -2099,7 +2097,7 @@ ParseAddCmd(GNode *gn, char *cmd)
Lst_Append(&gn->commands, cmd);
if (MaybeSubMake(cmd))
gn->type |= OP_SUBMAKE;
- ParseMark(gn);
+ RememberLocation(gn);
} else {
#if 0
/* XXX: We cannot do this until we fix the tree */
@@ -2132,7 +2130,7 @@ Parse_AddIncludeDir(const char *dir)
/*
* Handle one of the .[-ds]include directives by remembering the current file
* and pushing the included file on the stack. After the included file has
- * finished, parsing continues with the including file; see Parse_SetInput
+ * finished, parsing continues with the including file; see Parse_PushInput
* and ParseEOF.
*
* System includes are looked up in sysIncPath, any other includes are looked
@@ -2140,7 +2138,7 @@ Parse_AddIncludeDir(const char *dir)
* line options.
*/
static void
-IncludeFile(char *file, bool isSystem, bool depinc, bool silent)
+IncludeFile(const char *file, bool isSystem, bool depinc, bool silent)
{
struct loadedfile *lf;
char *fullname; /* full pathname of file */
@@ -2239,60 +2237,63 @@ IncludeFile(char *file, bool isSystem, bool depinc, bool silent)
lf = loadfile(fullname, fd);
/* Start reading from this file next */
- Parse_SetInput(fullname, 0, -1, loadedfile_readMore, lf);
+ Parse_PushInput(fullname, 0, -1, loadedfile_readMore, lf);
CurFile()->lf = lf;
if (depinc)
doing_depend = depinc; /* only turn it on */
+ /* TODO: consider free(fullname); */
}
+/*
+ * Parse a directive like '.include' or '.-include'.
+ *
+ * .include "user-makefile.mk"
+ * .include <system-makefile.mk>
+ */
static void
ParseInclude(char *directive)
{
- char endc; /* the character which ends the file spec */
- char *cp; /* current position in file spec */
+ char endc; /* '>' or '"' */
+ char *p;
bool silent = directive[0] != 'i';
- char *file = directive + (silent ? 8 : 7);
+ FStr file;
- /* Skip to delimiter character so we know where to look */
- pp_skip_hspace(&file);
+ p = directive + (silent ? 8 : 7);
+ pp_skip_hspace(&p);
- if (*file != '"' && *file != '<') {
+ if (*p != '"' && *p != '<') {
Parse_Error(PARSE_FATAL,
".include filename must be delimited by '\"' or '<'");
return;
}
- /*
- * Set the search path on which to find the include file based on the
- * characters which bracket its name. Angle-brackets imply it's
- * a system Makefile while double-quotes imply it's a user makefile
- */
- if (*file == '<')
+ if (*p++ == '<')
endc = '>';
else
endc = '"';
+ file = FStr_InitRefer(p);
/* Skip to matching delimiter */
- for (cp = ++file; *cp != '\0' && *cp != endc; cp++)
- continue;
+ while (*p != '\0' && *p != endc)
+ p++;
- if (*cp != endc) {
+ if (*p != endc) {
Parse_Error(PARSE_FATAL,
"Unclosed .include filename. '%c' expected", endc);
return;
}
- *cp = '\0';
+ *p = '\0';
- /*
- * Substitute for any variables in the filename before trying to
- * find the file.
- */
- (void)Var_Subst(file, SCOPE_CMDLINE, VARE_WANTRES, &file);
- /* TODO: handle errors */
+ if (strchr(file.str, '$') != NULL) {
+ char *xfile;
+ Var_Subst(file.str, SCOPE_CMDLINE, VARE_WANTRES, &xfile);
+ /* TODO: handle errors */
+ file = FStr_InitOwn(xfile);
+ }
- IncludeFile(file, endc == '>', directive[0] == 'd', silent);
- free(file);
+ IncludeFile(file.str, endc == '>', directive[0] == 'd', silent);
+ FStr_Done(&file);
}
/*
@@ -2314,8 +2315,8 @@ SetFilenameVars(const char *filename, const char *dirvar, const char *filevar)
basename = slash + 1;
}
- Global_SetExpand(dirvar, dirname.str);
- Global_SetExpand(filevar, basename);
+ Global_Set(dirvar, dirname.str);
+ Global_Set(filevar, basename);
DEBUG5(PARSE, "%s: ${%s} = `%s' ${%s} = `%s'\n",
__func__, dirvar, dirname.str, filevar, basename);
@@ -2418,7 +2419,7 @@ ParseTrackInput(const char *name)
* The given file is added to the includes stack.
*/
void
-Parse_SetInput(const char *name, int lineno, int fd,
+Parse_PushInput(const char *name, int lineno, int fd,
ReadMoreProc readMore, void *readMoreArg)
{
IFile *curFile;
@@ -2431,7 +2432,7 @@ Parse_SetInput(const char *name, int lineno, int fd,
else
ParseTrackInput(name);
- DEBUG3(PARSE, "Parse_SetInput: %s %s, line %d\n",
+ DEBUG3(PARSE, "Parse_PushInput: %s %s, line %d\n",
readMore == loadedfile_readMore ? "file" : ".for loop in",
name, lineno);
@@ -2649,13 +2650,15 @@ typedef enum ParseRawLineResult {
/*
* Parse until the end of a line, taking into account lines that end with
- * backslash-newline.
+ * backslash-newline. The resulting line goes from out_line to out_line_end;
+ * the line is not null-terminated.
*/
static ParseRawLineResult
ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
char **out_firstBackslash, char **out_firstComment)
{
char *line = curFile->buf_ptr;
+ char *buf_end = curFile->buf_end;
char *p = line;
char *line_end = line;
char *firstBackslash = NULL;
@@ -2667,14 +2670,14 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
for (;;) {
char ch;
- if (p == curFile->buf_end) {
+ if (p == buf_end) {
res = PRLR_EOF;
break;
}
ch = *p;
if (ch == '\0' ||
- (ch == '\\' && p + 1 < curFile->buf_end && p[1] == '\0')) {
+ (ch == '\\' && p + 1 < buf_end && p[1] == '\0')) {
Parse_Error(PARSE_FATAL, "Zero byte read from file");
return PRLR_ERROR;
}
@@ -2685,7 +2688,7 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
firstBackslash = p;
if (p[1] == '\n') {
curFile->lineno++;
- if (p + 2 == curFile->buf_end) {
+ if (p + 2 == buf_end) {
line_end = p;
*line_end = '\n';
p += 2;
@@ -2694,7 +2697,7 @@ ParseRawLine(IFile *curFile, char **out_line, char **out_line_end,
}
p += 2;
line_end = p;
- assert(p <= curFile->buf_end);
+ assert(p <= buf_end);
continue;
}
@@ -2904,7 +2907,7 @@ ParseForLoop(const char *line)
line = ParseGetLine(GLM_FOR_BODY);
if (line == NULL) {
Parse_Error(PARSE_FATAL,
- "Unexpected end of file in for loop.");
+ "Unexpected end of file in .for loop");
break;
}
} while (For_Accum(line));
@@ -3012,12 +3015,6 @@ ParseLine_ShellCommand(const char *p)
}
}
-MAKE_INLINE bool
-IsDirective(const char *dir, size_t dirlen, const char *name)
-{
- return dirlen == strlen(name) && memcmp(dir, name, dirlen) == 0;
-}
-
/*
* See if the line starts with one of the known directives, and if so, handle
* the directive.
@@ -3026,8 +3023,8 @@ static bool
ParseDirective(char *line)
{
char *cp = line + 1;
- const char *dir, *arg;
- size_t dirlen;
+ const char *arg;
+ Substring dir;
pp_skip_whitespace(&cp);
if (IsInclude(cp, false)) {
@@ -3035,10 +3032,10 @@ ParseDirective(char *line)
return true;
}
- dir = cp;
+ dir.start = cp;
while (ch_isalpha(*cp) || *cp == '-')
cp++;
- dirlen = (size_t)(cp - dir);
+ dir.end = cp;
if (*cp != '\0' && !ch_isspace(*cp))
return false;
@@ -3046,31 +3043,31 @@ ParseDirective(char *line)
pp_skip_whitespace(&cp);
arg = cp;
- if (IsDirective(dir, dirlen, "undef")) {
- Var_Undef(cp);
+ if (Substring_Equals(dir, "undef")) {
+ Var_Undef(arg);
return true;
- } else if (IsDirective(dir, dirlen, "export")) {
+ } else if (Substring_Equals(dir, "export")) {
Var_Export(VEM_PLAIN, arg);
return true;
- } else if (IsDirective(dir, dirlen, "export-env")) {
+ } else if (Substring_Equals(dir, "export-env")) {
Var_Export(VEM_ENV, arg);
return true;
- } else if (IsDirective(dir, dirlen, "export-literal")) {
+ } else if (Substring_Equals(dir, "export-literal")) {
Var_Export(VEM_LITERAL, arg);
return true;
- } else if (IsDirective(dir, dirlen, "unexport")) {
+ } else if (Substring_Equals(dir, "unexport")) {
Var_UnExport(false, arg);
return true;
- } else if (IsDirective(dir, dirlen, "unexport-env")) {
+ } else if (Substring_Equals(dir, "unexport-env")) {
Var_UnExport(true, arg);
return true;
- } else if (IsDirective(dir, dirlen, "info")) {
+ } else if (Substring_Equals(dir, "info")) {
ParseMessage(PARSE_INFO, "info", arg);
return true;
- } else if (IsDirective(dir, dirlen, "warning")) {
+ } else if (Substring_Equals(dir, "warning")) {
ParseMessage(PARSE_WARNING, "warning", arg);
return true;
- } else if (IsDirective(dir, dirlen, "error")) {
+ } else if (Substring_Equals(dir, "error")) {
ParseMessage(PARSE_FATAL, "error", arg);
return true;
}
@@ -3251,7 +3248,7 @@ Parse_File(const char *name, int fd)
if (name == NULL)
name = "(stdin)";
- Parse_SetInput(name, 0, -1, loadedfile_readMore, lf);
+ Parse_PushInput(name, 0, -1, loadedfile_readMore, lf);
CurFile()->lf = lf;
do {
@@ -3265,7 +3262,7 @@ Parse_File(const char *name, int fd)
FinishDependencyGroup();
- if (fatals != 0) {
+ if (parseErrors != 0) {
(void)fflush(stdout);
(void)fprintf(stderr,
"%s: Fatal errors encountered -- cannot continue",
@@ -3320,7 +3317,7 @@ Parse_MainName(GNodeList *mainList)
}
int
-Parse_GetFatals(void)
+Parse_NumErrors(void)
{
- return fatals;
+ return parseErrors;
}
diff --git a/contrib/bmake/sigact.h b/contrib/bmake/sigact.h
new file mode 100644
index 000000000000..1dc04ba2dbdd
--- /dev/null
+++ b/contrib/bmake/sigact.h
@@ -0,0 +1,104 @@
+/* NAME:
+ * sigact.h - sigaction et al
+ *
+ * SYNOPSIS:
+ * #include "sigact.h"
+ *
+ * DESCRIPTION:
+ * This header is the interface to a fake sigaction(2)
+ * implementation. It provides a POSIX compliant interface
+ * to whatever signal handling mechanisms are available.
+ * It also provides a Signal() function that is implemented
+ * in terms of sigaction().
+ * If not using signal(2) as part of the underlying
+ * implementation (USE_SIGNAL or USE_SIGMASK), and
+ * NO_SIGNAL is not defined, it also provides a signal()
+ * function that calls Signal().
+ *
+ * SEE ALSO:
+ * sigact.c
+ */
+/*
+ * RCSid:
+ * $Id: sigact.h,v 1.4 2021/10/14 19:39:17 sjg Exp $
+ */
+#ifndef _SIGACT_H
+#define _SIGACT_H
+
+#include <sys/cdefs.h>
+
+/*
+ * most modern systems use void for signal handlers but
+ * not all.
+ */
+#ifndef SIG_HDLR
+# define SIG_HDLR void
+#endif
+
+/*
+ * if you want to install this header as signal.h,
+ * modify this to pick up the original signal.h
+ */
+#ifndef SIGKILL
+# include <signal.h>
+#endif
+#ifndef SIGKILL
+# include <sys/signal.h>
+#endif
+
+#ifndef SIG_ERR
+# define SIG_ERR (SIG_HDLR (*)())-1
+#endif
+#ifndef BADSIG
+# define BADSIG SIG_ERR
+#endif
+
+#ifndef SA_NOCLDSTOP
+/* we assume we need the fake sigaction */
+/* sa_flags */
+#define SA_NOCLDSTOP 1 /* don't send SIGCHLD on child stop */
+#define SA_RESTART 2 /* re-start I/O */
+
+/* sigprocmask flags */
+#define SIG_BLOCK 1
+#define SIG_UNBLOCK 2
+#define SIG_SETMASK 4
+
+/*
+ * this is a bit untidy
+ */
+#ifdef _SIGSET_T_
+typedef _SIGSET_T_ sigset_t;
+#endif
+
+/*
+ * POSIX sa_handler should return void, but since we are
+ * implementing in terms of something else, it may
+ * be appropriate to use the normal SIG_HDLR return type
+ */
+struct sigaction
+{
+ SIG_HDLR (*sa_handler)();
+ sigset_t sa_mask;
+ int sa_flags;
+};
+
+
+int sigaction ( int /*sig*/, const struct sigaction */*act*/, struct sigaction */*oact*/ );
+int sigaddset ( sigset_t */*mask*/, int /*sig*/ );
+int sigdelset ( sigset_t */*mask*/, int /*sig*/ );
+int sigemptyset ( sigset_t */*mask*/ );
+int sigfillset ( sigset_t */*mask*/ );
+int sigismember ( const sigset_t */*mask*/, int /*sig*/ );
+int sigpending ( sigset_t */*set*/ );
+int sigprocmask ( int how, const sigset_t */*set*/, sigset_t */*oset*/ );
+int sigsuspend ( sigset_t */*mask*/ );
+
+#ifndef sigmask
+# define sigmask(s) (1<<((s)-1) & (32 - 1)) /* convert SIGnum to mask */
+#endif
+#if !defined(NSIG) && defined(_NSIG)
+# define NSIG _NSIG
+#endif
+#endif /* ! SA_NOCLDSTOP */
+#endif /* _SIGACT_H */
diff --git a/contrib/bmake/sigaction.c b/contrib/bmake/sigaction.c
new file mode 100644
index 000000000000..dc647e7b058f
--- /dev/null
+++ b/contrib/bmake/sigaction.c
@@ -0,0 +1,397 @@
+/* NAME:
+ * sigact.c - fake sigaction(2)
+ *
+ * SYNOPSIS:
+ * #include "sigact.h"
+ *
+ * int sigaction(int sig, struct sigaction *act,
+ * struct sigaction *oact);
+ * int sigaddset(sigset_t *mask, int sig);
+ * int sigdelset(sigset_t *mask, int sig);
+ * int sigemptyset(sigset_t *mask);
+ * int sigfillset(sigset_t *mask);
+ * int sigismember(sigset_t *mask, int sig);
+ * int sigpending(sigset_t *set);
+ * int sigprocmask(int how, sigset_t *set, sigset_t *oset);
+ * int sigsuspend(sigset_t *mask);
+ *
+ * SIG_HDLR (*Signal(int sig, SIG_HDLR (*disp)(int)))(int);
+ *
+ * DESCRIPTION:
+ * This is a fake sigaction implementation. It uses
+ * sigsetmask(2) et al or sigset(2) and friends if
+ * available, otherwise it just uses signal(2). If it
+ * thinks sigaction(2) really exists it compiles to "almost"
+ * nothing.
+ *
+ * In any case it provides a Signal() function that is
+ * implemented in terms of sigaction().
+ * If not using signal(2) as part of the underlying
+ * implementation (USE_SIGNAL or USE_SIGMASK), and
+ * NO_SIGNAL is not defined, it also provides a signal()
+ * function that calls Signal().
+ *
+ * The need for all this mucking about is the problems
+ * caused by mixing various signal handling mechanisms in
+ * the one process. This module allows for a consistent
+ * POSIX compliant interface to whatever is actually
+ * available.
+ *
+ * sigaction() allows the caller to examine and/or set the
+ * action to be associated with a given signal. "act" and
+ * "oact" are pointers to 'sigaction structs':
+ *.nf
+ *
+ * struct sigaction
+ * {
+ * SIG_HDLR (*sa_handler)();
+ * sigset_t sa_mask;
+ * int sa_flags;
+ * };
+ *.fi
+ *
+ * SIG_HDLR is normally 'void' in the POSIX implementation
+ * and for most current systems. On some older UNIX
+ * systems, signal handlers do not return 'void', so
+ * this implementation keeps 'sa_handler' inline with the
+ * hosts normal signal handling conventions.
+ * 'sa_mask' controls which signals will be blocked while
+ * the selected signal handler is active. It is not used
+ * in this implementation.
+ * 'sa_flags' controls various semantics such as whether
+ * system calls should be automagically restarted
+ * (SA_RESTART) etc. It is not used in this
+ * implementation.
+ * Either "act" or "oact" may be NULL in which case the
+ * appropriate operation is skipped.
+ *
+ * sigaddset() adds "sig" to the sigset_t pointed to by "mask".
+ *
+ * sigdelset() removes "sig" from the sigset_t pointed to
+ * by "mask".
+ *
+ * sigemptyset() makes the sigset_t pointed to by "mask" empty.
+ *
+ * sigfillset() makes the sigset_t pointed to by "mask"
+ * full ie. match all signals.
+ *
+ * sigismember() returns true if "sig" is found in "*mask".
+ *
+ * sigpending() is supposed to return "set" loaded with the
+ * set of signals that are blocked and pending for the
+ * calling process. It does nothing in this impementation.
+ *
+ * sigprocmask() is used to examine and/or change the
+ * signal mask for the calling process. Either "set" or
+ * "oset" may be NULL in which case the appropriate
+ * operation is skipped. "how" may be one of SIG_BLOCK,
+ * SIG_UNBLOCK or SIG_SETMASK. If this package is built
+ * with USE_SIGNAL, then this routine achieves nothing.
+ *
+ * sigsuspend() sets the signal mask to "*mask" and waits
+ * for a signal to be delivered after which the previous
+ * mask is restored.
+ *
+ *
+ * RETURN VALUE:
+ * 0==success, -1==failure
+ *
+ * BUGS:
+ * Since we fake most of this, don't expect fancy usage to
+ * work.
+ *
+ * AUTHOR:
+ * Simon J. Gerraty <sjg@crufty.net>
+ */
+/* COPYRIGHT:
+ * @(#)Copyright (c) 1992-2021, Simon J. Gerraty
+ *
+ * This is free software. It comes with NO WARRANTY.
+ * Permission to use, modify and distribute this source code
+ * is granted subject to the following conditions.
+ * 1/ that that the above copyright notice and this notice
+ * are preserved in all copies and that due credit be given
+ * to the author.
+ * 2/ that any changes to this code are clearly commented
+ * as such so that the author does get blamed for bugs
+ * other than his own.
+ *
+ * Please send copies of changes and bug-fixes to:
+ * sjg@crufty.net
+ *
+ */
+#ifndef lint
+static char *RCSid = "$Id: sigact.c,v 1.8 2021/10/14 19:39:17 sjg Exp $";
+#endif
+
+#undef _ANSI_SOURCE /* causes problems */
+
+#include <signal.h>
+#include <sys/cdefs.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+# ifdef NO_SIGSET
+# undef HAVE_SIGSET
+# endif
+# ifndef HAVE_SIGACTION
+# ifdef HAVE_SIGSETMASK
+# define USE_SIGMASK
+# else
+# ifdef HAVE_SIGSET
+# define USE_SIGSET
+# else
+# define USE_SIGNAL
+# endif
+# endif
+# endif
+#endif
+
+/*
+ * some systems have a faulty sigaction() implementation!
+ * Allow us to bypass it.
+ * Or they may have installed sigact.h as signal.h which is why
+ * we have SA_NOCLDSTOP defined.
+ */
+#if !defined(SA_NOCLDSTOP) || defined(_SIGACT_H) || defined(USE_SIGNAL) || defined(USE_SIGSET) || defined(USE_SIGMASK)
+
+/*
+ * if we haven't been told,
+ * try and guess what we should implement with.
+ */
+#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL)
+# if defined(sigmask) || defined(BSD) || defined(_BSD) && !defined(BSD41)
+# define USE_SIGMASK
+# else
+# ifndef NO_SIGSET
+# define USE_SIGSET
+# else
+# define USE_SIGNAL
+# endif
+# endif
+#endif
+/*
+ * if we still don't know, we're in trouble
+ */
+#if !defined(USE_SIGSET) && !defined(USE_SIGMASK) && !defined(USE_SIGNAL)
+error must know what to implement with
+#endif
+
+#include "sigact.h"
+
+/*
+ * in case signal() has been mapped to our Signal().
+ */
+#undef signal
+
+
+int
+sigaction(int sig,
+ const struct sigaction *act,
+ struct sigaction *oact)
+{
+ SIG_HDLR(*oldh) ();
+
+ if (act) {
+#ifdef USE_SIGSET
+ oldh = sigset(sig, act->sa_handler);
+#else
+ oldh = signal(sig, act->sa_handler);
+#endif
+ } else {
+ if (oact) {
+#ifdef USE_SIGSET
+ oldh = sigset(sig, SIG_IGN);
+#else
+ oldh = signal(sig, SIG_IGN);
+#endif
+ if (oldh != SIG_IGN && oldh != SIG_ERR) {
+#ifdef USE_SIGSET
+ (void) sigset(sig, oldh);
+#else
+ (void) signal(sig, oldh);
+#endif
+ }
+ }
+ }
+ if (oact) {
+ oact->sa_handler = oldh;
+ }
+ return 0; /* hey we're faking it */
+}
+
+#ifndef HAVE_SIGADDSET
+int
+sigaddset(sigset_t *mask, int sig)
+{
+ *mask |= sigmask(sig);
+ return 0;
+}
+
+
+int
+sigdelset(sigset_t *mask, int sig)
+{
+ *mask &= ~(sigmask(sig));
+ return 0;
+}
+
+
+int
+sigemptyset(sigset_t *mask)
+{
+ *mask = 0;
+ return 0;
+}
+
+
+int
+sigfillset(sigset_t *mask)
+{
+ *mask = ~0;
+ return 0;
+}
+
+
+int
+sigismember(const sigset_t *mask, int sig)
+{
+ return ((*mask) & sigmask(sig));
+}
+#endif
+
+#ifndef HAVE_SIGPENDING
+int
+sigpending(sigset_t *set)
+{
+ return 0; /* faking it! */
+}
+#endif
+
+#ifndef HAVE_SIGPROCMASK
+int
+sigprocmask(int how, const sigset_t *set, sigset_t *oset)
+{
+#ifdef USE_SIGSET
+ int i;
+#endif
+ static sigset_t sm;
+ static int once = 0;
+
+ if (!once) {
+ /*
+ * initally we clear sm,
+ * there after, it represents the last
+ * thing we did.
+ */
+ once++;
+#ifdef USE_SIGMASK
+ sm = sigblock(0);
+#else
+ sm = 0;
+#endif
+ }
+ if (oset)
+ *oset = sm;
+ if (set) {
+ switch (how) {
+ case SIG_BLOCK:
+ sm |= *set;
+ break;
+ case SIG_UNBLOCK:
+ sm &= ~(*set);
+ break;
+ case SIG_SETMASK:
+ sm = *set;
+ break;
+ }
+#ifdef USE_SIGMASK
+ (void) sigsetmask(sm);
+#else
+#ifdef USE_SIGSET
+ for (i = 1; i < NSIG; i++) {
+ if (how == SIG_UNBLOCK) {
+ if (*set & sigmask(i))
+ sigrelse(i);
+ } else
+ if (sm & sigmask(i)) {
+ sighold(i);
+ }
+ }
+#endif
+#endif
+ }
+ return 0;
+}
+#endif
+
+#ifndef HAVE_SIGSUSPEND
+int
+sigsuspend(sigset_t *mask)
+{
+#ifdef USE_SIGMASK
+ sigpause(*mask);
+#else
+ int i;
+
+#ifdef USE_SIGSET
+
+ for (i = 1; i < NSIG; i++) {
+ if (*mask & sigmask(i)) {
+ /* not the same sigpause() as above! */
+ sigpause(i);
+ break;
+ }
+ }
+#else /* signal(2) only */
+ SIG_HDLR(*oldh) ();
+
+ /*
+ * make sure that signals in mask will not
+ * be ignored.
+ */
+ for (i = 1; i < NSIG; i++) {
+ if (*mask & sigmask(i)) {
+ if ((oldh = signal(i, SIG_DFL)) != SIG_ERR &&
+ oldh != SIG_IGN &&
+ oldh != SIG_DFL)
+ (void) signal(i, oldh); /* restore handler */
+ }
+ }
+ pause(); /* wait for a signal */
+#endif
+#endif
+ return 0;
+}
+#endif
+#endif /* ! SA_NOCLDSTOP */
+
+#if 0
+#if !defined(SIG_HDLR)
+#define SIG_HDLR void
+#endif
+#if !defined(SIG_ERR)
+#define SIG_ERR (SIG_HDLR (*)())-1
+#endif
+
+#if !defined(USE_SIGNAL) && !defined(USE_SIGMASK) && !defined(NO_SIGNAL)
+/*
+ * ensure we avoid signal mayhem
+ */
+
+extern void (*Signal (int sig, void (*handler) (int)))(int);
+
+SIG_HDLR(*signal(int sig, SIG_HDLR(*handler)(int))
+{
+ return (Signal(sig, handler));
+}
+#endif
+#endif
+
+/* This lot (for GNU-Emacs) goes at the end of the file. */
+/*
+ * Local Variables:
+ * version-control:t
+ * comment-column:40
+ * End:
+ */
diff --git a/contrib/bmake/str.c b/contrib/bmake/str.c
index b4baede4d417..11e7595dd867 100644
--- a/contrib/bmake/str.c
+++ b/contrib/bmake/str.c
@@ -1,4 +1,4 @@
-/* $NetBSD: str.c,v 1.85 2021/05/30 21:16:54 rillig Exp $ */
+/* $NetBSD: str.c,v 1.86 2021/06/21 16:59:18 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.85 2021/05/30 21:16:54 rillig Exp $");
+MAKE_RCSID("$NetBSD: str.c,v 1.86 2021/06/21 16:59:18 rillig Exp $");
/* Return the concatenation of s1 and s2, freshly allocated. */
char *
@@ -188,10 +188,9 @@ Substring_Words(const char *str, bool expand)
*word_end++ = '\0';
if (words_len == words_cap) {
- size_t new_size;
words_cap *= 2;
- new_size = (words_cap + 1) * sizeof(words[0]);
- words = bmake_realloc(words, new_size);
+ words = bmake_realloc(words,
+ (words_cap + 1) * sizeof(words[0]));
}
words[words_len++] =
Substring_Init(word_start, word_end - 1);
diff --git a/contrib/bmake/str.h b/contrib/bmake/str.h
index ce0bb5ad82bc..bd56a2981785 100644
--- a/contrib/bmake/str.h
+++ b/contrib/bmake/str.h
@@ -1,4 +1,4 @@
-/* $NetBSD: str.h,v 1.9 2021/05/30 21:16:54 rillig Exp $ */
+/* $NetBSD: str.h,v 1.12 2021/12/12 13:43:47 rillig Exp $ */
/*
Copyright (c) 2021 Roland Illig <rillig@NetBSD.org>
@@ -60,7 +60,6 @@ typedef struct LazyBuf {
size_t len;
size_t cap;
const char *expected;
- void *freeIt;
} LazyBuf;
/* The result of splitting a string into words. */
@@ -182,6 +181,14 @@ Substring_Equals(Substring sub, const char *str)
memcmp(sub.start, str, len) == 0;
}
+MAKE_INLINE bool
+Substring_Eq(Substring sub, Substring str)
+{
+ size_t len = Substring_Length(sub);
+ return len == Substring_Length(str) &&
+ memcmp(sub.start, str.start, len) == 0;
+}
+
MAKE_STATIC Substring
Substring_Sub(Substring sub, size_t start, size_t end)
{
@@ -266,13 +273,12 @@ LazyBuf_Init(LazyBuf *buf, const char *expected)
buf->len = 0;
buf->cap = 0;
buf->expected = expected;
- buf->freeIt = NULL;
}
MAKE_INLINE void
LazyBuf_Done(LazyBuf *buf)
{
- free(buf->freeIt);
+ free(buf->data);
}
MAKE_STATIC void
@@ -329,6 +335,11 @@ LazyBuf_Get(const LazyBuf *buf)
return Substring_Init(start, start + buf->len);
}
+/*
+ * Returns the content of the buffer as a newly allocated string.
+ *
+ * See LazyBuf_Get to avoid unnecessary memory allocations.
+ */
MAKE_STATIC FStr
LazyBuf_DoneGet(LazyBuf *buf)
{
@@ -353,6 +364,14 @@ Words_Free(Words w)
SubstringWords Substring_Words(const char *, bool);
MAKE_INLINE void
+SubstringWords_Init(SubstringWords *w)
+{
+ w->words = NULL;
+ w->len = 0;
+ w->freeIt = NULL;
+}
+
+MAKE_INLINE void
SubstringWords_Free(SubstringWords w)
{
free(w.words);
diff --git a/contrib/bmake/suff.c b/contrib/bmake/suff.c
index b2c926a2b8bc..ab7ac6184173 100644
--- a/contrib/bmake/suff.c
+++ b/contrib/bmake/suff.c
@@ -1,4 +1,4 @@
-/* $NetBSD: suff.c,v 1.350 2021/04/04 10:05:08 rillig Exp $ */
+/* $NetBSD: suff.c,v 1.357 2021/12/12 20:45:48 sjg 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.350 2021/04/04 10:05:08 rillig Exp $");
+MAKE_RCSID("$NetBSD: suff.c,v 1.357 2021/12/12 20:45:48 sjg Exp $");
typedef List SuffixList;
typedef ListNode SuffixListNode;
@@ -142,21 +142,27 @@ static GNodeList transforms = LST_INIT;
*/
static int sNum = 0;
-typedef enum SuffixFlags {
- SUFF_NONE = 0,
+typedef List SuffixListList;
+/*
+ * A suffix such as ".c" or ".o" that is used in suffix transformation rules
+ * such as ".c.o:".
+ */
+typedef struct Suffix {
+ /* The suffix itself, such as ".c" */
+ char *name;
+ /* Length of the name, to avoid strlen calls */
+ size_t nameLen;
/*
* This suffix marks include files. Their search path ends up in the
* undocumented special variable '.INCLUDES'.
*/
- SUFF_INCLUDE = 1 << 0,
-
+ bool include:1;
/*
* This suffix marks library files. Their search path ends up in the
* undocumented special variable '.LIBS'.
*/
- SUFF_LIBRARY = 1 << 1,
-
+ bool library:1;
/*
* The empty suffix.
*
@@ -166,42 +172,26 @@ typedef enum SuffixFlags {
* XXX: Why is SUFF_NULL needed at all? Wouldn't nameLen == 0 mean
* the same?
*/
- SUFF_NULL = 1 << 2
-
-} SuffixFlags;
-
-ENUM_FLAGS_RTTI_3(SuffixFlags,
- SUFF_INCLUDE, SUFF_LIBRARY, SUFF_NULL);
-
-typedef List SuffixListList;
-
-/*
- * A suffix such as ".c" or ".o" that is used in suffix transformation rules
- * such as ".c.o:".
- */
-typedef struct Suffix {
- /* The suffix itself, such as ".c" */
- char *name;
- /* Length of the name, to avoid strlen calls */
- size_t nameLen;
- /* Type of suffix */
- SuffixFlags flags;
+ bool isNull:1;
/* The path along which files of this suffix may be found */
SearchPath *searchPath;
+
/* The suffix number; TODO: document the purpose of this number */
int sNum;
/* Reference count of list membership and several other places */
int refCount;
+
/* Suffixes we have a transformation to */
SuffixList parents;
/* Suffixes we have a transformation from */
SuffixList children;
-
- /* Lists in which this suffix is referenced.
+ /*
+ * 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. */
+ * refCount though.
+ */
SuffixListList ref;
} Suffix;
@@ -475,7 +465,9 @@ Suffix_New(const char *name)
Lst_Init(&suff->parents);
Lst_Init(&suff->ref);
suff->sNum = sNum++;
- suff->flags = SUFF_NONE;
+ suff->include = false;
+ suff->library = false;
+ suff->isNull = false;
suff->refCount = 1; /* XXX: why 1? It's not assigned anywhere yet. */
return suff;
@@ -502,7 +494,9 @@ Suff_ClearSuffixes(void)
emptySuff = nullSuff = Suffix_New("");
SearchPath_AddAll(nullSuff->searchPath, &dirSearchPath);
- nullSuff->flags = SUFF_NULL;
+ nullSuff->include = false;
+ nullSuff->library = false;
+ nullSuff->isNull = true;
}
/*
@@ -619,6 +613,7 @@ Suff_AddTransform(const char *name)
/* TODO: Avoid the redundant parsing here. */
bool ok = ParseTransform(name, &srcSuff, &targSuff);
assert(ok);
+ /* LINTED 129 *//* expression has null effect */
(void)ok;
}
@@ -888,12 +883,12 @@ Suff_ExtendPaths(void)
Suffix *suff = ln->datum;
if (!Lst_IsEmpty(&suff->searchPath->dirs)) {
#ifdef INCLUDES
- if (suff->flags & SUFF_INCLUDE)
+ if (suff->include)
SearchPath_AddAll(includesPath,
suff->searchPath);
#endif
#ifdef LIBRARIES
- if (suff->flags & SUFF_LIBRARY)
+ if (suff->library)
SearchPath_AddAll(libsPath, suff->searchPath);
#endif
SearchPath_AddAll(suff->searchPath, &dirSearchPath);
@@ -926,7 +921,7 @@ Suff_AddInclude(const char *suffName)
{
Suffix *suff = FindSuffixByName(suffName);
if (suff != NULL)
- suff->flags |= SUFF_INCLUDE;
+ suff->include = true;
}
/*
@@ -940,7 +935,7 @@ Suff_AddLib(const char *suffName)
{
Suffix *suff = FindSuffixByName(suffName);
if (suff != NULL)
- suff->flags |= SUFF_LIBRARY;
+ suff->library = true;
}
/********** Implicit Source Search Functions *********/
@@ -1043,7 +1038,7 @@ CandidateList_AddCandidatesFor(CandidateList *list, Candidate *cand)
for (ln = cand->suff->children.first; ln != NULL; ln = ln->next) {
Suffix *suff = ln->datum;
- if ((suff->flags & SUFF_NULL) && suff->name[0] != '\0') {
+ if (suff->isNull && suff->name[0] != '\0') {
/*
* If the suffix has been marked as the NULL suffix,
* also create a candidate for a file with no suffix
@@ -1918,7 +1913,7 @@ FindDepsRegular(GNode *gn, CandidateSearcher *cs)
* If the suffix indicates that the target is a library, mark that in
* the node's type field.
*/
- if (targ->suff->flags & SUFF_LIBRARY)
+ if (targ->suff->library)
gn->type |= OP_LIB;
/*
@@ -2077,14 +2072,14 @@ Suff_SetNull(const char *name)
Suffix *suff = FindSuffixByName(name);
if (suff == NULL) {
Parse_Error(PARSE_WARNING,
- "Desired null suffix %s not defined.",
+ "Desired null suffix %s not defined",
name);
return;
}
if (nullSuff != NULL)
- nullSuff->flags &= ~(unsigned)SUFF_NULL;
- suff->flags |= SUFF_NULL;
+ nullSuff->isNull = false;
+ suff->isNull = true;
/* XXX: Here's where the transformation mangling would take place. */
nullSuff = suff;
}
@@ -2117,31 +2112,36 @@ Suff_End(void)
static void
-PrintSuffNames(const char *prefix, SuffixList *suffs)
+PrintSuffNames(const char *prefix, const SuffixList *suffs)
{
SuffixListNode *ln;
debug_printf("#\t%s: ", prefix);
for (ln = suffs->first; ln != NULL; ln = ln->next) {
- Suffix *suff = ln->datum;
+ const Suffix *suff = ln->datum;
debug_printf("%s ", suff->name);
}
debug_printf("\n");
}
static void
-Suffix_Print(Suffix *suff)
+Suffix_Print(const Suffix *suff)
{
+ Buffer buf;
+
+ Buf_InitSize(&buf, 16);
+ Buf_AddFlag(&buf, suff->include, "SUFF_INCLUDE");
+ Buf_AddFlag(&buf, suff->library, "SUFF_LIBRARY");
+ Buf_AddFlag(&buf, suff->isNull, "SUFF_NULL");
+
debug_printf("# \"%s\" (num %d, ref %d)",
suff->name, suff->sNum, suff->refCount);
- if (suff->flags != 0) {
- char flags_buf[SuffixFlags_ToStringSize];
-
- debug_printf(" (%s)",
- SuffixFlags_ToString(flags_buf, suff->flags));
- }
+ if (buf.len > 0)
+ debug_printf(" (%s)", buf.data);
debug_printf("\n");
+ Buf_Done(&buf);
+
PrintSuffNames("To", &suff->parents);
PrintSuffNames("From", &suff->children);
@@ -2177,3 +2177,20 @@ Suff_PrintAll(void)
PrintTransformation(ln->datum);
}
}
+
+const char *
+Suff_NamesStr(void)
+{
+ Buffer buf;
+ SuffixListNode *ln;
+ Suffix *suff;
+
+ Buf_InitSize(&buf, 16);
+ for (ln = sufflist.first; ln != NULL; ln = ln->next) {
+ suff = ln->datum;
+ if (ln != sufflist.first)
+ Buf_AddByte(&buf, ' ');
+ Buf_AddStr(&buf, suff->name);
+ }
+ return Buf_DoneData(&buf);
+}
diff --git a/contrib/bmake/targ.c b/contrib/bmake/targ.c
index 68573644ff35..beb038fa1a4b 100644
--- a/contrib/bmake/targ.c
+++ b/contrib/bmake/targ.c
@@ -1,4 +1,4 @@
-/* $NetBSD: targ.c,v 1.168 2021/04/03 12:01:00 rillig Exp $ */
+/* $NetBSD: targ.c,v 1.173 2021/11/28 19:51:06 rillig Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -113,7 +113,7 @@
#include "dir.h"
/* "@(#)targ.c 8.2 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: targ.c,v 1.168 2021/04/03 12:01:00 rillig Exp $");
+MAKE_RCSID("$NetBSD: targ.c,v 1.173 2021/11/28 19:51:06 rillig Exp $");
/*
* All target nodes that appeared on the left-hand side of one of the
@@ -187,7 +187,7 @@ GNode_New(const char *name)
gn->uname = NULL;
gn->path = NULL;
gn->type = name[0] == '-' && name[1] == 'l' ? OP_LIB : OP_NONE;
- gn->flags = GNF_NONE;
+ memset(&gn->flags, 0, sizeof(gn->flags));
gn->made = UNMADE;
gn->unmade = 0;
gn->mtime = 0;
@@ -309,7 +309,7 @@ Targ_NewInternalNode(const char *name)
Lst_Append(&allTargets, gn);
DEBUG1(TARG, "Adding \"%s\" to all targets.\n", gn->name);
if (doing_depend)
- gn->flags |= FROM_DEPEND;
+ gn->flags.fromDepend = true;
return gn;
}
@@ -416,36 +416,37 @@ Targ_FmtTime(time_t tm)
/* Print out a type field giving only those attributes the user can set. */
void
-Targ_PrintType(int type)
+Targ_PrintType(GNodeType type)
{
- int tbit;
-
- type &= ~OP_OPMASK;
-
- while (type != 0) {
- tbit = 1 << (ffs(type) - 1);
- type &= ~tbit;
-
- switch (tbit) {
-#define PRINTBIT(bit, attr) case bit: debug_printf(" " attr); break
-#define PRINTDBIT(bit, attr) case bit: DEBUG0(TARG, " " attr); break
- PRINTBIT(OP_OPTIONAL, ".OPTIONAL");
- PRINTBIT(OP_USE, ".USE");
- PRINTBIT(OP_EXEC, ".EXEC");
- PRINTBIT(OP_IGNORE, ".IGNORE");
- PRINTBIT(OP_PRECIOUS, ".PRECIOUS");
- PRINTBIT(OP_SILENT, ".SILENT");
- PRINTBIT(OP_MAKE, ".MAKE");
- PRINTBIT(OP_JOIN, ".JOIN");
- PRINTBIT(OP_INVISIBLE, ".INVISIBLE");
- PRINTBIT(OP_NOTMAIN, ".NOTMAIN");
- PRINTDBIT(OP_LIB, ".LIB");
- PRINTDBIT(OP_MEMBER, ".MEMBER");
- PRINTDBIT(OP_ARCHV, ".ARCHV");
- PRINTDBIT(OP_MADE, ".MADE");
- PRINTDBIT(OP_PHONY, ".PHONY");
-#undef PRINTBIT
-#undef PRINTDBIT
+ static const struct {
+ GNodeType bit;
+ bool internal;
+ const char name[10];
+ } names[] = {
+ { OP_MEMBER, true, "MEMBER" },
+ { OP_LIB, true, "LIB" },
+ { OP_ARCHV, true, "ARCHV" },
+ { OP_PHONY, true, "PHONY" },
+ { OP_NOTMAIN, false, "NOTMAIN" },
+ { OP_INVISIBLE, false, "INVISIBLE" },
+ { OP_MADE, true, "MADE" },
+ { OP_JOIN, false, "JOIN" },
+ { OP_MAKE, false, "MAKE" },
+ { OP_SILENT, false, "SILENT" },
+ { OP_PRECIOUS, false, "PRECIOUS" },
+ { OP_IGNORE, false, "IGNORE" },
+ { OP_EXEC, false, "EXEC" },
+ { OP_USE, false, "USE" },
+ { OP_OPTIONAL, false, "OPTIONAL" },
+ };
+ size_t i;
+
+ for (i = 0; i < sizeof(names) / sizeof(names[0]); i++) {
+ if (type & names[i].bit) {
+ if (names[i].internal)
+ DEBUG1(TARG, " .%s", names[i].name);
+ else
+ debug_printf(" .%s", names[i].name);
}
}
}
@@ -480,13 +481,27 @@ GNode_OpName(const GNode *gn)
return "";
}
+static bool
+GNodeFlags_IsNone(GNodeFlags flags)
+{
+ return !flags.remake
+ && !flags.childMade
+ && !flags.force
+ && !flags.doneWait
+ && !flags.doneOrder
+ && !flags.fromDepend
+ && !flags.doneAllsrc
+ && !flags.cycle
+ && !flags.doneCycle;
+}
+
/* Print the contents of a node. */
void
Targ_PrintNode(GNode *gn, int pass)
{
debug_printf("# %s%s", gn->name, gn->cohort_num);
GNode_FprintDetails(opts.debug_file, ", ", gn, "\n");
- if (gn->flags == 0)
+ if (GNodeFlags_IsNone(gn->flags))
return;
if (!GNode_IsTarget(gn))
diff --git a/contrib/bmake/trace.c b/contrib/bmake/trace.c
index 840c7995e5c1..8e2c507d14dc 100644
--- a/contrib/bmake/trace.c
+++ b/contrib/bmake/trace.c
@@ -1,4 +1,4 @@
-/* $NetBSD: trace.c,v 1.28 2021/02/05 05:15:12 rillig Exp $ */
+/* $NetBSD: trace.c,v 1.29 2021/09/21 23:06:18 rillig Exp $ */
/*
* Copyright (c) 2000 The NetBSD Foundation, Inc.
@@ -48,13 +48,13 @@
#include "job.h"
#include "trace.h"
-MAKE_RCSID("$NetBSD: trace.c,v 1.28 2021/02/05 05:15:12 rillig Exp $");
+MAKE_RCSID("$NetBSD: trace.c,v 1.29 2021/09/21 23:06:18 rillig Exp $");
static FILE *trfile;
static pid_t trpid;
const char *trwd;
-static const char *evname[] = {
+static const char evname[][4] = {
"BEG",
"END",
"ERR",
diff --git a/contrib/bmake/unit-tests/Makefile b/contrib/bmake/unit-tests/Makefile
index 784223a56652..98ed3907cd5a 100644
--- a/contrib/bmake/unit-tests/Makefile
+++ b/contrib/bmake/unit-tests/Makefile
@@ -1,6 +1,6 @@
-# $Id: Makefile,v 1.148 2021/06/16 19:18:56 sjg Exp $
+# $Id: Makefile,v 1.164 2021/12/12 22:50:00 sjg Exp $
#
-# $NetBSD: Makefile,v 1.279 2021/06/16 09:39:48 rillig Exp $
+# $NetBSD: Makefile,v 1.288 2021/12/12 22:16:48 rillig Exp $
#
# Unit tests for make(1)
#
@@ -167,6 +167,7 @@ TESTS+= directive-for
TESTS+= directive-for-errors
TESTS+= directive-for-escape
TESTS+= directive-for-generating-endif
+TESTS+= directive-for-if
TESTS+= directive-for-lines
TESTS+= directive-for-null
TESTS+= directive-hyphen-include
@@ -351,13 +352,16 @@ TESTS+= varmod-indirect
TESTS+= varmod-l-name-to-value
TESTS+= varmod-localtime
TESTS+= varmod-loop
+TESTS+= varmod-loop-delete
TESTS+= varmod-loop-varname
TESTS+= varmod-match
TESTS+= varmod-match-escape
TESTS+= varmod-no-match
TESTS+= varmod-order
+TESTS+= varmod-order-numeric
TESTS+= varmod-order-reverse
TESTS+= varmod-order-shuffle
+TESTS+= varmod-order-string
TESTS+= varmod-path
TESTS+= varmod-quote
TESTS+= varmod-quote-dollar
@@ -415,6 +419,7 @@ TESTS+= varname-dot-parsedir
TESTS+= varname-dot-parsefile
TESTS+= varname-dot-path
TESTS+= varname-dot-shell
+TESTS+= varname-dot-suffixes
TESTS+= varname-dot-targets
TESTS+= varname-empty
TESTS+= varname-make
@@ -430,12 +435,41 @@ TESTS+= varparse-mod
TESTS+= varparse-undef-partial
TESTS+= varquote
+# for now at least
+.if ${.SHELL:T} == "ksh"
+BROKEN_TESTS+= sh-flags
+.endif
+.if ${.MAKE.OS:NDarwin} == ""
+BROKEN_TESTS+= shell-ksh
+.endif
+.if ${.MAKE.OS} == "SCO_SV"
+BROKEN_TESTS+= \
+ opt-debug-graph[23] \
+ varmod-localtime \
+ varmod-to-separator \
+
+.if ${.SHELL:T} == "bash"
+BROKEN_TESTS+= job-output-null
+.else
+BROKEN_TESTS+= \
+ cmd-interrupt \
+ job-flags \
+
+.endif
+.endif
+
+# Some tests just do not work on some platforms or environments
+# so allow for some filtering.
+.if !empty(BROKEN_TESTS)
+.warning Skipping broken tests: ${BROKEN_TESTS:O:u}
+TESTS:= ${TESTS:${BROKEN_TESTS:S,^,N,:ts:}}
+.endif
+
# Ideas for more tests:
# char-0020-space.mk
# char-005C-backslash.mk
# escape-cond-str.mk
# escape-cond-func-arg.mk
-# escape-cond-func-arg.mk
# escape-varmod.mk
# escape-varmod-define.mk
# escape-varmod-match.mk
@@ -457,7 +491,7 @@ ENV.envfirst= FROM_ENV=value-from-env
ENV.varmisc= FROM_ENV=env
ENV.varmisc+= FROM_ENV_BEFORE=env
ENV.varmisc+= FROM_ENV_AFTER=env
-ENV.varmod-localtime+= TZ=Europe/Berlin
+ENV.varmod-localtime+= TZ=${UTC_1:UEurope/Berlin}
ENV.varname-vpath+= VPATH=varname-vpath.dir:varname-vpath.dir2
# Override make flags for some of the tests; default is -k.
@@ -490,7 +524,10 @@ SED_CMDS.job-output-long-lines= \
${:D marker should always be at the beginning of the line. } \
-e '/^aa*--- job-b ---$$/d' \
-e '/^bb*--- job-a ---$$/d'
-SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,'
+SED_CMDS.opt-chdir= -e 's,\(nonexistent\).[1-9][0-9]*,\1,' \
+ -e '/name/s,file,File,' \
+ -e 's,no such,No such,' \
+ -e 's,Filename,File name,'
SED_CMDS.opt-debug-graph1= ${STD_SED_CMDS.dg1}
SED_CMDS.opt-debug-graph2= ${STD_SED_CMDS.dg2}
SED_CMDS.opt-debug-graph3= ${STD_SED_CMDS.dg3}
@@ -511,11 +548,13 @@ SED_CMDS.sh-dots+= -e 's,^make: exec(\(.*\)) failed (.*)$$,<not found: \1>,'
SED_CMDS.sh-dots+= -e 's,^\(\*\*\* Error code \)[1-9][0-9]*,\1<nonzero>,'
SED_CMDS.sh-errctl= ${STD_SED_CMDS.dj}
SED_CMDS.sh-flags= ${STD_SED_CMDS.hide-from-output}
+SED_CMDS.shell-csh= ${STD_SED_CMDS.white-space}
SED_CMDS.suff-main+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-main-several+= ${STD_SED_CMDS.dg1}
SED_CMDS.suff-transform-debug+= ${STD_SED_CMDS.dg1}
SED_CMDS.var-op-shell+= ${STD_SED_CMDS.shell}
SED_CMDS.var-op-shell+= -e '/command/s,No such.*,not found,'
+SED_CMDS.var-op-shell+= ${STD_SED_CMDS.white-space}
SED_CMDS.vardebug+= -e 's,${.SHELL},</path/to/shell>,'
SED_CMDS.varmod-subst-regex+= ${STD_SED_CMDS.regex}
SED_CMDS.varname-dot-parsedir= -e '/in some cases/ s,^make: "[^"]*,make: "<normalized>,'
@@ -523,9 +562,7 @@ SED_CMDS.varname-dot-parsefile= -e '/in some cases/ s,^make: "[^"]*,make: "<norm
SED_CMDS.varname-dot-shell= -e 's, = /[^ ]*, = (details omitted),g'
SED_CMDS.varname-dot-shell+= -e 's,"/[^" ]*","(details omitted)",g'
SED_CMDS.varname-dot-shell+= -e 's,\[/[^] ]*\],[(details omitted)],g'
-SED_CMDS.varname-empty= -e 's,${.CURDIR},<curdir>,g'
-SED_CMDS.varname-empty+= -e '/\.PARSEDIR/d'
-SED_CMDS.varname-empty+= -e '/\.SHELL/d'
+SED_CMDS.varname-empty= ${.OBJDIR .PARSEDIR .PATH .SHELL:L:@v@-e '/\\$v/d'@}
# Some tests need an additional round of postprocessing.
POSTPROC.deptgt-suffixes= awk '/^\#\*\*\* Suffixes/,/^never-stop/'
@@ -541,7 +578,7 @@ unexport-env.rawout: export.mk
# Some standard sed commands, to be used in the SED_CMDS above.
# Omit details such as process IDs from the output of the -dg1 option.
-STD_SED_CMDS.dg1= -e 's,${.CURDIR}$$,<curdir>,'
+STD_SED_CMDS.dg1= -e '/\#.* \.$$/d'
STD_SED_CMDS.dg1+= -e '/\.MAKE.PATH_FILEMON/d'
STD_SED_CMDS.dg1+= -e '/^MAKE_VERSION/d;/^\#.*\/mk/d'
STD_SED_CMDS.dg1+= -e 's, ${DEFSYSPATH:U/usr/share/mk}$$, <defsyspath>,'
@@ -597,6 +634,8 @@ STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: line [0-9][0-9]*: ,,'
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: [0-9][0-9]*: ,,'
STD_SED_CMDS.shell+= -e 's,^${.SHELL:T}: ,,'
+STD_SED_CMDS.white-space= -e 's, *, ,g' -e 's, *$$,,'
+
# The actual error messages for a failed regcomp or regexec differ between the
# implementations.
STD_SED_CMDS.regex= \
@@ -661,7 +700,8 @@ TMPDIR:= /tmp/uid${.MAKE.UID}
x!= echo; mkdir -p ${TMPDIR}
.endif
-MAKE_TEST_ENV?= MALLOC_OPTIONS="JA" # for jemalloc
+MAKE_TEST_ENV= MALLOC_OPTIONS="JA" # for jemalloc 100
+MAKE_TEST_ENV+= MALLOC_CONF="junk:true" # for jemalloc 510
MAKE_TEST_ENV+= TMPDIR=${TMPDIR}
.if ${.MAKE.OS} == "NetBSD"
@@ -697,13 +737,22 @@ _SED_CMDS+= -e 's,^usage: ${TEST_MAKE:T:S,.,\\.,g} ,usage: make ,'
# replace anything after 'stopped in' with unit-tests
_SED_CMDS+= -e '/stopped/s, /.*, unit-tests,'
_SED_CMDS+= -e 's,${TMPDIR},TMPDIR,g'
-# strip ${.CURDIR}/ from the output
-_SED_CMDS+= -e 's,${.CURDIR:S,.,\\.,g}/,,g'
+# canonicalize ${.OBJDIR} and ${.CURDIR}
+.if ${.OBJDIR} != ${.CURDIR}
+# yes this is inaccurate but none of the tests expect <objdir> anywhere
+# which we get depending on how MAKEOBJDIR is set.
+_SED_CMDS+= -e 's,${.OBJDIR},<curdir>,g'
+.endif
+_SED_CMDS+= -e 's,${.CURDIR},<curdir>,g'
+_SED_CMDS+= -e 's,<curdir>/,,g'
_SED_CMDS+= -e 's,${UNIT_TESTS:S,.,\\.,g}/,,g'
-# on AT&T derrived systems; false exits 255 not 1
+# on AT&T derived systems: false exits 255 not 1
.if ${.MAKE.OS:N*BSD} != ""
_SED_CMDS+= -e 's,\(Error code\) 255,\1 1,'
.endif
+.if ${.SHELL:T} == "ksh"
+_SED_CMDS+= -e '/^set [+-]v/d'
+.endif
.rawout.out:
@${TOOL_SED} ${_SED_CMDS} ${SED_CMDS.${.PREFIX:T}} \
diff --git a/contrib/bmake/unit-tests/Makefile.config.in b/contrib/bmake/unit-tests/Makefile.config.in
index 0fe24f08d2f9..3139a0d4d0b5 100644
--- a/contrib/bmake/unit-tests/Makefile.config.in
+++ b/contrib/bmake/unit-tests/Makefile.config.in
@@ -1,4 +1,6 @@
-# $Id: Makefile.config.in,v 1.1 2018/12/30 17:14:24 sjg Exp $
+# $Id: Makefile.config.in,v 1.3 2021/10/22 07:48:57 sjg Exp $
srcdir= @srcdir@
+TOOL_DIFF?= @diff@
DIFF_FLAGS?= @diff_u@
+UTC_1= @UTC_1@
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.exp b/contrib/bmake/unit-tests/cond-cmp-numeric.exp
index 4a97b6879e7a..d10262aa8823 100644
--- a/contrib/bmake/unit-tests/cond-cmp-numeric.exp
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric.exp
@@ -6,6 +6,10 @@ CondParser_Eval: !(${:UNaN} == NaN)
lhs = "NaN", rhs = "NaN", op = ==
CondParser_Eval: 123 ! 123
make: "cond-cmp-numeric.mk" line 34: Malformed conditional (123 ! 123)
+CondParser_Eval: ${:U 123} < 124
+lhs = 123.000000, rhs = 124.000000, op = <
+CondParser_Eval: ${:U123 } < 124
+make: "cond-cmp-numeric.mk" line 50: String comparison operator must be either == or !=
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-cmp-numeric.mk b/contrib/bmake/unit-tests/cond-cmp-numeric.mk
index b1ec3e719d47..b34e5bfc0a06 100644
--- a/contrib/bmake/unit-tests/cond-cmp-numeric.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-numeric.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-numeric.mk,v 1.4 2020/11/08 22:56:16 rillig Exp $
+# $NetBSD: cond-cmp-numeric.mk,v 1.5 2021/07/29 06:31:18 rillig Exp $
#
# Tests for numeric comparisons in .if conditions.
@@ -37,5 +37,21 @@
. error
.endif
+# Leading spaces are allowed for numbers.
+# See EvalCompare and TryParseNumber.
+.if ${:U 123} < 124
+.else
+. error
+.endif
+
+# Trailing spaces are NOT allowed for numbers.
+# See EvalCompare and TryParseNumber.
+# expect+1: String comparison operator must be either == or !=
+.if ${:U123 } < 124
+. error
+.else
+. error
+.endif
+
all:
@:;
diff --git a/contrib/bmake/unit-tests/cond-cmp-string.mk b/contrib/bmake/unit-tests/cond-cmp-string.mk
index 9f3e731b2eb0..305a41099b98 100644
--- a/contrib/bmake/unit-tests/cond-cmp-string.mk
+++ b/contrib/bmake/unit-tests/cond-cmp-string.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-cmp-string.mk,v 1.14 2021/01/19 19:54:57 rillig Exp $
+# $NetBSD: cond-cmp-string.mk,v 1.15 2021/12/11 09:53:53 rillig Exp $
#
# Tests for string comparisons in .if conditions.
@@ -26,7 +26,7 @@
# starting point for variable expressions. Applying the :U modifier to such
# an undefined expression turns it into a defined expression.
#
-# See ApplyModifier_Defined and VEF_DEF.
+# See ApplyModifier_Defined and DEF_DEFINED.
.if ${:Ustr} != "str"
. error
.endif
diff --git a/contrib/bmake/unit-tests/cond-eof.exp b/contrib/bmake/unit-tests/cond-eof.exp
index 3b1e6eb1f056..3016a9b27805 100644
--- a/contrib/bmake/unit-tests/cond-eof.exp
+++ b/contrib/bmake/unit-tests/cond-eof.exp
@@ -1,8 +1,5 @@
-side effect
make: "cond-eof.mk" line 15: Malformed conditional (0 ${SIDE_EFFECT} ${SIDE_EFFECT2})
-side effect
make: "cond-eof.mk" line 17: Malformed conditional (1 ${SIDE_EFFECT} ${SIDE_EFFECT2})
-side effect
make: "cond-eof.mk" line 19: Malformed conditional ((0) ${SIDE_EFFECT} ${SIDE_EFFECT2})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/cond-eof.mk b/contrib/bmake/unit-tests/cond-eof.mk
index 08f432bc4593..ddf4a4cd20c8 100644
--- a/contrib/bmake/unit-tests/cond-eof.mk
+++ b/contrib/bmake/unit-tests/cond-eof.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-eof.mk,v 1.2 2020/12/14 20:28:09 rillig Exp $
+# $NetBSD: cond-eof.mk,v 1.3 2021/12/10 23:12:44 rillig Exp $
#
# Tests for parsing conditions, especially the end of such conditions, which
# are represented as the token TOK_EOF.
@@ -7,11 +7,11 @@ SIDE_EFFECT= ${:!echo 'side effect' 1>&2!}
SIDE_EFFECT2= ${:!echo 'side effect 2' 1>&2!}
# In the following conditions, ${SIDE_EFFECT} is the position of the first
-# parse error. It is always fully evaluated, even if it were not necessary
-# to expand the variable expression. This is because these syntax errors are
-# an edge case that does not occur during normal operation, therefore there
-# is no need to optimize for this case, and it would slow down the common
-# case as well.
+# 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.
+# 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.
.if 0 ${SIDE_EFFECT} ${SIDE_EFFECT2}
.endif
.if 1 ${SIDE_EFFECT} ${SIDE_EFFECT2}
diff --git a/contrib/bmake/unit-tests/cond-func-defined.mk b/contrib/bmake/unit-tests/cond-func-defined.mk
index 2aa49ccbf147..43db548a572b 100644
--- a/contrib/bmake/unit-tests/cond-func-defined.mk
+++ b/contrib/bmake/unit-tests/cond-func-defined.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-func-defined.mk,v 1.7 2020/11/15 14:07:53 rillig Exp $
+# $NetBSD: cond-func-defined.mk,v 1.8 2021/12/12 08:55:28 rillig Exp $
#
# Tests for the defined() function in .if conditions.
@@ -29,7 +29,7 @@ ${:UA B}= variable name with spaces
. error
.endif
-# Parse error: missing closing parenthesis; see ParseFuncArg.
+# Parse error: missing closing parenthesis; see ParseWord.
.if defined(DEF
. error
.else
diff --git a/contrib/bmake/unit-tests/cond-func-empty.exp b/contrib/bmake/unit-tests/cond-func-empty.exp
index 77a4edd47f49..d1dfda7c03ee 100644
--- a/contrib/bmake/unit-tests/cond-func-empty.exp
+++ b/contrib/bmake/unit-tests/cond-func-empty.exp
@@ -1,5 +1,5 @@
-make: "cond-func-empty.mk" line 152: Unclosed variable "WORD"
-make: "cond-func-empty.mk" line 152: Malformed conditional (empty(WORD)
+make: "cond-func-empty.mk" line 149: Unclosed variable "WORD"
+make: "cond-func-empty.mk" line 149: Malformed conditional (empty(WORD)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-func-empty.mk b/contrib/bmake/unit-tests/cond-func-empty.mk
index 11a990cbbce1..25c850d23d93 100644
--- a/contrib/bmake/unit-tests/cond-func-empty.mk
+++ b/contrib/bmake/unit-tests/cond-func-empty.mk
@@ -1,10 +1,10 @@
-# $NetBSD: cond-func-empty.mk,v 1.14 2021/04/11 13:35:56 rillig Exp $
+# $NetBSD: cond-func-empty.mk,v 1.16 2021/12/11 10:41:31 rillig Exp $
#
# Tests for the empty() function in .if conditions, which tests a variable
# expression for emptiness.
#
-# Note that the argument in the parentheses is indeed a variable name,
-# optionally followed by variable modifiers.
+# Note that the argument in the parentheses is a variable name, not a variable
+# expression, optionally followed by variable modifiers.
#
.undef UNDEF
@@ -25,14 +25,10 @@ WORD= word
.endif
# The :S modifier replaces the empty value with an actual word. The
-# expression is now no longer empty, but it is still possible to see whether
-# the expression was based on an undefined variable. The expression has the
-# flag VEF_UNDEF.
-#
-# The expression does not have the flag VEF_DEF though, therefore it is still
-# considered undefined. Yes, indeed, undefined but not empty. There are a
-# few variable modifiers that turn an undefined expression into a defined
-# expression, among them :U and :D, but not :S.
+# expression is now no longer empty, but it is still based on an undefined
+# variable (DEF_UNDEF). There are a few variable modifiers that turn an
+# undefined expression into a defined expression, among them :U and :D, but
+# not :S.
#
# XXX: This is hard to explain to someone who doesn't know these
# implementation details.
@@ -41,19 +37,19 @@ WORD= word
. error
.endif
-# The :U modifier modifies expressions based on undefined variables
-# (DEF_UNDEF) by adding the DEF_DEFINED flag, which marks the expression
-# as "being interesting enough to be further processed".
+# The :U modifier changes the state of a previously undefined expression from
+# DEF_UNDEF to DEF_DEFINED. This marks the expression as "being interesting
+# enough to be further processed".
#
.if empty(UNDEF:S,^$,value,W:Ufallback)
. error
.endif
# And now to the surprising part. Applying the following :S modifier to the
-# undefined expression makes it non-empty, but the marker VEF_UNDEF is
-# preserved nevertheless. The :U modifier that follows only looks at the
-# VEF_UNDEF flag to decide whether the variable is defined or not. This kind
-# of makes sense since the :U modifier tests the _variable_, not the
+# undefined expression makes it non-empty, but the expression is still in
+# state DEF_UNDEF. The :U modifier that follows only looks at the state
+# DEF_UNDEF to decide whether the variable is defined or not. This kind of
+# makes sense since the :U modifier tests the _variable_, not the
# _expression_.
#
# But since the variable was undefined to begin with, the fallback value from
@@ -78,12 +74,13 @@ WORD= word
. error
.endif
-# The empty variable named "" gets a fallback value of " ", which counts as
-# empty.
+# The following example constructs an expression with the variable name ""
+# and the value " ". This expression counts as empty since the value contains
+# only whitespace.
#
# Contrary to the other functions in conditionals, the trailing space is not
# stripped off, as can be seen in the -dv debug log. If the space had been
-# stripped, it wouldn't make a difference in this case.
+# stripped, it wouldn't make a difference in this case, but in other cases.
#
.if !empty(:U )
. error
@@ -92,8 +89,8 @@ WORD= word
# Now the variable named " " gets a non-empty value, which demonstrates that
# neither leading nor trailing spaces are trimmed in the argument of the
# function. If the spaces were trimmed, the variable name would be "" and
-# that variable is indeed undefined. Since ParseEmptyArg calls Var_Parse
-# without VARE_UNDEFERR, the value of the undefined variable is
+# that variable is indeed undefined. Since CondParser_FuncCallEmpty calls
+# Var_Parse without VARE_UNDEFERR, the value of the undefined variable is
# returned as an empty string.
${:U }= space
.if empty( )
@@ -129,8 +126,8 @@ ${:U }= space
#
# If everything goes well, the argument expands to "WORD", and that variable
# is defined at the beginning of this file. The surrounding 'W' and 'D'
-# ensure that the parser in ParseEmptyArg has the correct position, both
-# before and after the call to Var_Parse.
+# ensure that CondParser_FuncCallEmpty keeps track of the parsing position,
+# both before and after the call to Var_Parse.
.if empty(W${:UOR}D)
. error
.endif
@@ -155,17 +152,29 @@ ${:U WORD }= variable name with spaces
. error
.endif
-# Between 2020-06-28 and var.c 1.226 from 2020-07-02, this paragraph generated
-# a wrong error message "Variable VARNAME is recursive".
+# Since cond.c 1.76 from 2020-06-28 and before var.c 1.226 from 2020-07-02,
+# the following example generated a wrong error message "Variable VARNAME is
+# recursive".
+#
+# Since at least 1993, the manual page claimed that irrelevant parts of
+# conditions were not evaluated, but that was wrong for a long time. The
+# expressions in irrelevant parts of the condition were actually evaluated,
+# they just allowed undefined variables to be used in the conditions, and the
+# result of evaluating them was not used further. These unnecessary
+# evaluations were fixed in several commits, starting with var.c 1.226 from
+# 2020-07-02.
#
-# The bug was that the !empty() condition was evaluated, even though this was
-# not necessary since the defined() condition already evaluated to false.
+# In this example, the variable "VARNAME2" is not defined, so evaluation of
+# the condition should have stopped at this point, and the rest of the
+# condition should have been processed in parse-only mode. The right-hand
+# 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
-# this case the ${:U2}. Therefore, the variable name came out as simply
-# "VARNAME". Since this variable name should have been discarded quickly after
-# parsing it, this unrealistic variable name should have done no harm.
+# 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
# expansion was done without VARE_WANTRES (called VARF_WANTRES back
diff --git a/contrib/bmake/unit-tests/cond-func.exp b/contrib/bmake/unit-tests/cond-func.exp
index 855b9e5210fd..8dc0f821a255 100644
--- a/contrib/bmake/unit-tests/cond-func.exp
+++ b/contrib/bmake/unit-tests/cond-func.exp
@@ -6,7 +6,7 @@ make: "cond-func.mk" line 102: A plain function name is parsed as !empty(...).
make: "cond-func.mk" line 109: A plain function name is parsed as !empty(...).
make: "cond-func.mk" line 119: Symbols may start with a function name.
make: "cond-func.mk" line 124: Symbols may start with a function name.
-make: "cond-func.mk" line 130: Malformed conditional (defined()
+make: "cond-func.mk" line 130: Missing closing parenthesis for defined()
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op-and.exp b/contrib/bmake/unit-tests/cond-op-and.exp
index 173b6861a98b..cd6b03a8359e 100644
--- a/contrib/bmake/unit-tests/cond-op-and.exp
+++ b/contrib/bmake/unit-tests/cond-op-and.exp
@@ -1,4 +1,7 @@
-make: "cond-op-and.mk" line 43: Malformed conditional (0 &&& 0)
+make: "cond-op-and.mk" line 36: Malformed conditional (0 || (${DEF} && ${UNDEF}))
+make: "cond-op-and.mk" line 40: Malformed conditional (0 || (${UNDEF} && ${UNDEF}))
+make: "cond-op-and.mk" line 42: Malformed conditional (0 || (!${UNDEF} && ${UNDEF}))
+make: "cond-op-and.mk" line 71: Malformed conditional (0 &&& 0)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op-and.mk b/contrib/bmake/unit-tests/cond-op-and.mk
index 83c694f15723..83386ed77de4 100644
--- a/contrib/bmake/unit-tests/cond-op-and.mk
+++ b/contrib/bmake/unit-tests/cond-op-and.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op-and.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-op-and.mk,v 1.6 2021/12/10 19:14:35 rillig Exp $
#
# Tests for the && operator in .if conditions.
@@ -18,11 +18,39 @@
. error
.endif
+
# The right-hand side is not evaluated since the left-hand side is already
# false.
.if 0 && ${UNDEF}
.endif
+# When an outer condition makes the inner '&&' condition irrelevant, neither
+# of its operands must be evaluated.
+#
+.if 1 || (${UNDEF} && ${UNDEF})
+.endif
+
+# Test combinations of outer '||' with inner '&&', to ensure that the operands
+# of the inner '&&' are only evaluated if necessary.
+DEF= defined
+.if 0 || (${DEF} && ${UNDEF})
+.endif
+.if 0 || (!${DEF} && ${UNDEF})
+.endif
+.if 0 || (${UNDEF} && ${UNDEF})
+.endif
+.if 0 || (!${UNDEF} && ${UNDEF})
+.endif
+.if 1 || (${DEF} && ${UNDEF})
+.endif
+.if 1 || (!${DEF} && ${UNDEF})
+.endif
+.if 1 || (${UNDEF} && ${UNDEF})
+.endif
+.if 1 || (!${UNDEF} && ${UNDEF})
+.endif
+
+
# The && operator may be abbreviated as &. This is not widely known though
# and is also not documented in the manual page.
diff --git a/contrib/bmake/unit-tests/cond-op-or.exp b/contrib/bmake/unit-tests/cond-op-or.exp
index 7888a475e3e4..43b9a5438a31 100644
--- a/contrib/bmake/unit-tests/cond-op-or.exp
+++ b/contrib/bmake/unit-tests/cond-op-or.exp
@@ -1,4 +1,7 @@
-make: "cond-op-or.mk" line 43: Malformed conditional (0 ||| 0)
+make: "cond-op-or.mk" line 46: Malformed conditional (1 && (!${DEF} || ${UNDEF}))
+make: "cond-op-or.mk" line 48: Malformed conditional (1 && (${UNDEF} || ${UNDEF}))
+make: "cond-op-or.mk" line 50: Malformed conditional (1 && (!${UNDEF} || ${UNDEF}))
+make: "cond-op-or.mk" line 71: Malformed conditional (0 ||| 0)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op-or.mk b/contrib/bmake/unit-tests/cond-op-or.mk
index c6993e7c277e..0b7ac55e6c35 100644
--- a/contrib/bmake/unit-tests/cond-op-or.mk
+++ b/contrib/bmake/unit-tests/cond-op-or.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op-or.mk,v 1.6 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: cond-op-or.mk,v 1.8 2021/12/10 19:14:35 rillig Exp $
#
# Tests for the || operator in .if conditions.
@@ -18,11 +18,39 @@
. error
.endif
+
# The right-hand side is not evaluated since the left-hand side is already
# true.
.if 1 || ${UNDEF}
.endif
+# When an outer condition makes the inner '||' condition irrelevant, neither
+# of its operands must be evaluated. This had been wrong in cond.c 1.283 from
+# 2021-12-09 and was reverted in cond.c 1.284 an hour later.
+.if 0 && (!defined(UNDEF) || ${UNDEF})
+.endif
+
+# Test combinations of outer '&&' with inner '||', to ensure that the operands
+# of the inner '||' is only evaluated if necessary.
+DEF= defined
+.if 0 && (${DEF} || ${UNDEF})
+.endif
+.if 0 && (!${DEF} || ${UNDEF})
+.endif
+.if 0 && (${UNDEF} || ${UNDEF})
+.endif
+.if 0 && (!${UNDEF} || ${UNDEF})
+.endif
+.if 1 && (${DEF} || ${UNDEF})
+.endif
+.if 1 && (!${DEF} || ${UNDEF})
+.endif
+.if 1 && (${UNDEF} || ${UNDEF})
+.endif
+.if 1 && (!${UNDEF} || ${UNDEF})
+.endif
+
+
# The || operator may be abbreviated as |. This is not widely known though
# and is also not documented in the manual page.
diff --git a/contrib/bmake/unit-tests/cond-op.exp b/contrib/bmake/unit-tests/cond-op.exp
index 28e8d48e2697..b8f6a4301819 100644
--- a/contrib/bmake/unit-tests/cond-op.exp
+++ b/contrib/bmake/unit-tests/cond-op.exp
@@ -1,20 +1,22 @@
make: "cond-op.mk" line 50: Malformed conditional ("!word" == !word)
-make: "cond-op.mk" line 75: Malformed conditional (0 ${ERR::=evaluated})
-make: "cond-op.mk" line 79: After detecting a parse error, the rest is evaluated.
-make: "cond-op.mk" line 83: Parsing continues until here.
-make: "cond-op.mk" line 86: A B C => (A || B) && C A || B && C A || (B && C)
-make: "cond-op.mk" line 93: 0 0 0 => 0 0 0
-make: "cond-op.mk" line 93: 0 0 1 => 0 0 0
-make: "cond-op.mk" line 93: 0 1 0 => 0 0 0
-make: "cond-op.mk" line 93: 0 1 1 => 1 1 1
-make: "cond-op.mk" line 93: 1 0 0 => 0 1 1
-make: "cond-op.mk" line 93: 1 0 1 => 1 1 1
-make: "cond-op.mk" line 93: 1 1 0 => 0 1 1
-make: "cond-op.mk" line 93: 1 1 1 => 1 1 1
-make: "cond-op.mk" line 104: Malformed conditional (1 &&)
-make: "cond-op.mk" line 112: Malformed conditional (0 &&)
-make: "cond-op.mk" line 120: Malformed conditional (1 ||)
-make: "cond-op.mk" line 129: Malformed conditional (0 ||)
+make: "cond-op.mk" line 76: Malformed conditional (0 ${ERR::=evaluated})
+make: "cond-op.mk" line 80: A misplaced expression after 0 is not evaluated.
+make: "cond-op.mk" line 84: Malformed conditional (1 ${ERR::=evaluated})
+make: "cond-op.mk" line 88: A misplaced expression after 1 is not evaluated.
+make: "cond-op.mk" line 92: Parsing continues until here.
+make: "cond-op.mk" line 95: A B C => (A || B) && C A || B && C A || (B && C)
+make: "cond-op.mk" line 102: 0 0 0 => 0 0 0
+make: "cond-op.mk" line 102: 0 0 1 => 0 0 0
+make: "cond-op.mk" line 102: 0 1 0 => 0 0 0
+make: "cond-op.mk" line 102: 0 1 1 => 1 1 1
+make: "cond-op.mk" line 102: 1 0 0 => 0 1 1
+make: "cond-op.mk" line 102: 1 0 1 => 1 1 1
+make: "cond-op.mk" line 102: 1 1 0 => 0 1 1
+make: "cond-op.mk" line 102: 1 1 1 => 1 1 1
+make: "cond-op.mk" line 113: Malformed conditional (1 &&)
+make: "cond-op.mk" line 121: Malformed conditional (0 &&)
+make: "cond-op.mk" line 129: Malformed conditional (1 ||)
+make: "cond-op.mk" line 138: Malformed conditional (0 ||)
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-op.mk b/contrib/bmake/unit-tests/cond-op.mk
index 2ed451c90391..c3ab09f7709a 100644
--- a/contrib/bmake/unit-tests/cond-op.mk
+++ b/contrib/bmake/unit-tests/cond-op.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-op.mk,v 1.13 2021/01/19 18:20:30 rillig Exp $
+# $NetBSD: cond-op.mk,v 1.15 2021/12/10 23:12:44 rillig Exp $
#
# Tests for operators like &&, ||, ! in .if conditions.
#
@@ -58,25 +58,34 @@
. error
.endif
-# As soon as the parser sees the '$', it knows that the condition will
-# be malformed. Therefore there is no point in evaluating it.
+# In the following malformed conditions, as soon as the parser sees the '$'
+# after the '0' or the '1', it knows that the condition will be malformed.
+# Therefore there is no point in evaluating the misplaced expression.
#
-# As of 2021-01-20, that part of the condition is evaluated nevertheless,
-# since CondParser_Or just requests the next token, without restricting
-# the token to the expected tokens. If the parser were to restrict the
-# valid follow tokens for the token "0" to those that can actually produce
-# a correct condition (which in this case would be comparison operators,
-# TOK_AND, TOK_OR or TOK_RPAREN), the variable expression would not have
-# to be evaluated.
+# Before cond.c 1.286 from 2021-12-10, the extra expression was evaluated
+# nevertheless, since CondParser_Or and CondParser_And asked for the expanded
+# next token, even though in this position of the condition, only comparison
+# operators, TOK_AND, TOK_OR or TOK_RPAREN are allowed.
#
-# This would add a good deal of complexity to the code though, for almost
-# no benefit, especially since most expressions and conditions are side
-# effect free.
+#
+#
+#
+#
+#
+.undef ERR
.if 0 ${ERR::=evaluated}
. error
.endif
-.if ${ERR:Uundefined} == evaluated
-. info After detecting a parse error, the rest is evaluated.
+.if ${ERR:Uundefined} == undefined
+. info A misplaced expression after 0 is not evaluated.
+.endif
+
+.undef ERR
+.if 1 ${ERR::=evaluated}
+. error
+.endif
+.if ${ERR:Uundefined} == undefined
+. info A misplaced expression after 1 is not evaluated.
.endif
# Just in case that parsing should ever stop on the first error.
diff --git a/contrib/bmake/unit-tests/cond-short.mk b/contrib/bmake/unit-tests/cond-short.mk
index 113c3fd08fed..d41c38488fd6 100644
--- a/contrib/bmake/unit-tests/cond-short.mk
+++ b/contrib/bmake/unit-tests/cond-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: cond-short.mk,v 1.16 2021/03/14 11:49:37 rillig Exp $
+# $NetBSD: cond-short.mk,v 1.18 2021/12/12 09:49:09 rillig Exp $
#
# Demonstrates that in conditions, the right-hand side of an && or ||
# is only evaluated if it can actually influence the result.
@@ -12,7 +12,20 @@
# possible to skip evaluation of irrelevant variable expressions and only
# parse them. They were still evaluated though, the only difference to
# relevant variable expressions was that in the irrelevant variable
-# expressions, undefined variables were allowed.
+# 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 it still evaluated the
+# expression, even though the expression was irrelevant.
+#
+# Since the initial commit on 1993-03-21, the manual page has been saying that
+# make 'will only evaluate a conditional as far as is necessary to determine',
+# but that was wrong. The code in cond.c 1.1 from 1993-03-21 looks good since
+# it calls Var_Parse(condExpr, VAR_CMD, doEval,&varSpecLen,&doFree), but the
+# definition of Var_Parse does not call the third parameter 'doEval', as would
+# be expected, but instead 'err', accompanied by the comment 'TRUE if
+# undefined variables are an error'. This subtle difference between 'do not
+# evaluate at all' and 'allow undefined variables' led to the unexpected
+# evaluation.
#
# See also:
# var-eval-short.mk, for short-circuited variable modifiers
@@ -211,4 +224,56 @@ x!= echo '0 || $${iV2:U2} < $${V42}: $x' >&2; echo
. error
.endif
+
+# Ensure that irrelevant conditions do not influence the result of the whole
+# condition. As of cond.c 1.302 from 2021-12-11, an irrelevant function call
+# evaluates to true (see CondParser_FuncCall and CondParser_FuncCallEmpty), an
+# irrelevant comparison evaluates to false (see CondParser_Comparison).
+#
+# An irrelevant true bubbles up to the outermost CondParser_And, where it is
+# ignored. An irrelevant false bubbles up to the outermost CondParser_Or,
+# where it is ignored.
+#
+# If the condition parser should ever be restructured, the bubbling up of the
+# irrelevant evaluation results might show up accidentally. Prevent this.
+DEF= defined
+.undef UNDEF
+
+.if 0 && defined(DEF)
+. error
+.endif
+
+.if 1 && defined(DEF)
+.else
+. error
+.endif
+
+.if 0 && defined(UNDEF)
+. error
+.endif
+
+.if 1 && defined(UNDEF)
+. error
+.endif
+
+.if 0 || defined(DEF)
+.else
+. error
+.endif
+
+.if 1 || defined(DEF)
+.else
+. error
+.endif
+
+.if 0 || defined(UNDEF)
+. error
+.endif
+
+.if 1 || defined(UNDEF)
+.else
+. error
+.endif
+
+
all:
diff --git a/contrib/bmake/unit-tests/cond-token-plain.exp b/contrib/bmake/unit-tests/cond-token-plain.exp
index 24cfa6bcbc82..8afde2d41788 100644
--- a/contrib/bmake/unit-tests/cond-token-plain.exp
+++ b/contrib/bmake/unit-tests/cond-token-plain.exp
@@ -27,28 +27,35 @@ lhs = "var&&name", rhs = "var&&name", op = !=
CondParser_Eval: ${:Uvar}||name != "var||name"
lhs = "var||name", rhs = "var||name", op = !=
CondParser_Eval: bare
-make: "cond-token-plain.mk" line 102: A bare word is treated like defined(...), and the variable 'bare' is not defined.
+make: "cond-token-plain.mk" line 106: A bare word is treated like defined(...), and the variable 'bare' is not defined.
CondParser_Eval: VAR
-make: "cond-token-plain.mk" line 107: A bare word is treated like defined(...).
+make: "cond-token-plain.mk" line 111: A bare word is treated like defined(...).
CondParser_Eval: V${:UA}R
-make: "cond-token-plain.mk" line 114: ok
+make: "cond-token-plain.mk" line 118: ok
CondParser_Eval: V${UNDEF}AR
-make: "cond-token-plain.mk" line 122: Undefined variables in bare words expand to an empty string.
+make: "cond-token-plain.mk" line 126: Undefined variables in bare words expand to an empty string.
CondParser_Eval: 0${:Ux00}
-make: "cond-token-plain.mk" line 130: Numbers can be composed from literals and variable expressions.
-CondParser_Eval: 0${:Ux01}
make: "cond-token-plain.mk" line 134: Numbers can be composed from literals and variable expressions.
+CondParser_Eval: 0${:Ux01}
+make: "cond-token-plain.mk" line 138: Numbers can be composed from literals and variable expressions.
CondParser_Eval: "" ==
-make: "cond-token-plain.mk" line 140: Missing right-hand-side of operator '=='
+make: "cond-token-plain.mk" line 144: Missing right-hand side of operator '=='
CondParser_Eval: == ""
-make: "cond-token-plain.mk" line 148: Malformed conditional (== "")
+make: "cond-token-plain.mk" line 152: Malformed conditional (== "")
CondParser_Eval: \\
-make: "cond-token-plain.mk" line 163: The variable '\\' is not defined.
+make: "cond-token-plain.mk" line 167: The variable '\\' is not defined.
CondParser_Eval: \\
-make: "cond-token-plain.mk" line 168: Now the variable '\\' is defined.
+make: "cond-token-plain.mk" line 172: Now the variable '\\' is defined.
CondParser_Eval: "unquoted\"quoted" != unquoted"quoted
lhs = "unquoted"quoted", rhs = "unquoted"quoted", op = !=
CondParser_Eval: $$$$$$$$ != ""
+CondParser_Eval: left == right
+make: "cond-token-plain.mk" line 195: Malformed conditional (left == right)
+CondParser_Eval: ${0:?:} || left == right
+CondParser_Eval: 0
+make: "cond-token-plain.mk" line 201: Malformed conditional (${0:?:} || left == right)
+CondParser_Eval: left == right || ${0:?:}
+make: "cond-token-plain.mk" line 206: Malformed conditional (left == right || ${0:?:})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/cond-token-plain.mk b/contrib/bmake/unit-tests/cond-token-plain.mk
index 89f2247e077c..3e59f48bc3c7 100644
--- a/contrib/bmake/unit-tests/cond-token-plain.mk
+++ b/contrib/bmake/unit-tests/cond-token-plain.mk
@@ -1,17 +1,18 @@
-# $NetBSD: cond-token-plain.mk,v 1.10 2021/01/21 14:08:09 rillig Exp $
+# $NetBSD: cond-token-plain.mk,v 1.14 2021/12/12 09:36:00 rillig Exp $
#
# Tests for plain tokens (that is, string literals without quotes)
-# in .if conditions.
+# in .if conditions. These are also called bare words.
.MAKEFLAGS: -dc
+# The word 'value' after the '!=' is a bare word.
.if ${:Uvalue} != value
. error
.endif
-# Malformed condition since comment parsing is done in an early phase
-# and removes the '#' and everything behind it long before the condition
-# parser gets to see it.
+# Using a '#' in a string literal in a condition leads to a malformed
+# condition since comment parsing is done in an early phase and removes the
+# '#' and everything after it long before the condition parser gets to see it.
#
# XXX: The error message is missing for this malformed condition.
# The right-hand side of the comparison is just a '"', before unescaping.
@@ -32,7 +33,10 @@
# in a very early parsing phase.
#
# See https://gnats.netbsd.org/19596 for example makefiles demonstrating the
-# original problems. This workaround is probably not needed anymore.
+# original problems. At that time, the parser didn't recognize the comment in
+# the line '.else # comment3'. This workaround is not needed anymore since
+# comments are stripped in an earlier phase. See "case '#'" in
+# CondParser_Token.
#
# XXX: Missing error message for the malformed condition. The right-hand
# side before unescaping is double-quotes, backslash, backslash.
@@ -152,7 +156,7 @@ VAR= defined
.endif
# The '\\' is not a line continuation. Neither is it an unquoted string
-# literal. Instead, it is parsed as a function argument (ParseFuncArg),
+# literal. Instead, it is parsed as a bare word (ParseWord),
# and in that context, the backslash is just an ordinary character. The
# function argument thus stays '\\' (2 backslashes). This string is passed
# to FuncDefined, and since there is no variable named '\\', the condition
@@ -185,6 +189,23 @@ ${:U\\\\}= backslash
. error
.endif
+# In a condition in an .if directive, the left-hand side must not be an
+# unquoted string literal.
+# expect+1: Malformed conditional (left == right)
+.if left == right
+.endif
+# Before cond.c 1.276 from 2021-09-21, a variable 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)
+.if ${0:?:} || left == right
+.endif
+# This affected only the comparisons after the expression, so the following
+# was still a syntax error.
+# expect+1: Malformed conditional (left == right || ${0:?:})
+.if left == right || ${0:?:}
+.endif
+
# See cond-token-string.mk for similar tests where the condition is enclosed
# in "quotes".
diff --git a/contrib/bmake/unit-tests/deptgt-default.exp b/contrib/bmake/unit-tests/deptgt-default.exp
index 39a9383953dd..09fca899f063 100644
--- a/contrib/bmake/unit-tests/deptgt-default.exp
+++ b/contrib/bmake/unit-tests/deptgt-default.exp
@@ -1 +1,2 @@
+Default command is making 'not-a-target' from 'not-a-target'.
exit status 0
diff --git a/contrib/bmake/unit-tests/deptgt-default.mk b/contrib/bmake/unit-tests/deptgt-default.mk
index 814eaf72aed3..bf5f16536561 100644
--- a/contrib/bmake/unit-tests/deptgt-default.mk
+++ b/contrib/bmake/unit-tests/deptgt-default.mk
@@ -1,8 +1,17 @@
-# $NetBSD: deptgt-default.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: deptgt-default.mk,v 1.3 2021/12/01 23:56:29 rillig Exp $
#
-# Tests for the special target .DEFAULT in dependency declarations.
+# Tests for the special target .DEFAULT in dependency declarations, which
+# attaches its associated commands to all targets that don't specify any way
+# to create them.
-# TODO: Implementation
+all: test-default not-a-target
+
+test-default: .PHONY
+
+has-commands: .PHONY
+ @echo 'Making ${.TARGET} from ${.IMPSRC}.'
+
+.DEFAULT: dependency-is-ignored
+ @echo "Default command is making '${.TARGET}' from '${.IMPSRC}'."
all:
- @:;
diff --git a/contrib/bmake/unit-tests/deptgt-makeflags.mk b/contrib/bmake/unit-tests/deptgt-makeflags.mk
index 0a0f410e14c4..26f3f5794354 100644
--- a/contrib/bmake/unit-tests/deptgt-makeflags.mk
+++ b/contrib/bmake/unit-tests/deptgt-makeflags.mk
@@ -1,4 +1,4 @@
-# $NetBSD: deptgt-makeflags.mk,v 1.6 2020/11/15 20:20:58 rillig Exp $
+# $NetBSD: deptgt-makeflags.mk,v 1.7 2021/11/29 00:17:10 rillig Exp $
#
# Tests for the special target .MAKEFLAGS in dependency declarations,
# which adds command line options later, at parse time.
@@ -65,7 +65,7 @@
.endif
# Next try at defining another newline variable. Since whitespace around the
-# variable value is trimmed, two empty variable expressions surround the
+# variable value is trimmed, two empty variable expressions ${:U} surround the
# literal newline now. This prevents the newline from being skipped during
# parsing. The ':=' assignment operator expands the empty variable
# expressions, leaving only the newline as the variable value.
@@ -81,6 +81,31 @@
.endif
#.MAKEFLAGS: -d0
+# Now do the same for the other escape sequences; see Substring_Words.
+.MAKEFLAGS: CHAR_BS:="$${:U}\b$${:U}"
+.MAKEFLAGS: CHAR_FF:="$${:U}\f$${:U}"
+.MAKEFLAGS: CHAR_NL:="$${:U}\n$${:U}"
+.MAKEFLAGS: CHAR_CR:="$${:U}\r$${:U}"
+.MAKEFLAGS: CHAR_TAB:="$${:U}\t$${:U}"
+
+# Note: backspace is not whitespace, it is a control character.
+.if ${CHAR_BS:C,^[[:cntrl:]]$,found,W} != "found"
+. error
+.endif
+.if ${CHAR_FF:C,^[[:space:]]$,found,W} != "found"
+. error
+.endif
+.if ${CHAR_NL:C,^[[:space:]]$,found,W} != "found"
+. error
+.endif
+.if ${CHAR_CR:C,^[[:space:]]$,found,W} != "found"
+. error
+.endif
+.if ${CHAR_TAB:C,^[[:space:]]$,found,W} != "found"
+. error
+.endif
+
+
# Unbalanced quotes produce an error message. If they occur anywhere in the
# command line, the whole command line is skipped.
.MAKEFLAGS: VAR=previous
diff --git a/contrib/bmake/unit-tests/directive-else.exp b/contrib/bmake/unit-tests/directive-else.exp
index 138e893ffa88..17d5571ba74b 100644
--- a/contrib/bmake/unit-tests/directive-else.exp
+++ b/contrib/bmake/unit-tests/directive-else.exp
@@ -1,11 +1,11 @@
-make: "directive-else.mk" line 14: The .else directive does not take arguments.
+make: "directive-else.mk" line 14: The .else directive does not take arguments
make: "directive-else.mk" line 15: ok
make: "directive-else.mk" line 19: ok
-make: "directive-else.mk" line 21: The .else directive does not take arguments.
+make: "directive-else.mk" line 21: The .else directive does not take arguments
make: "directive-else.mk" line 26: if-less else
make: "directive-else.mk" line 32: ok
make: "directive-else.mk" line 33: warning: extra else
-make: "directive-else.mk" line 45: The .else directive does not take arguments.
+make: "directive-else.mk" line 45: The .else directive does not take arguments
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-endif.exp b/contrib/bmake/unit-tests/directive-endif.exp
index 286d85244eae..0de1ecf0bf25 100644
--- a/contrib/bmake/unit-tests/directive-endif.exp
+++ b/contrib/bmake/unit-tests/directive-endif.exp
@@ -1,7 +1,7 @@
-make: "directive-endif.mk" line 18: The .endif directive does not take arguments.
-make: "directive-endif.mk" line 23: The .endif directive does not take arguments.
-make: "directive-endif.mk" line 33: The .endif directive does not take arguments.
-make: "directive-endif.mk" line 39: The .endif directive does not take arguments.
+make: "directive-endif.mk" line 18: The .endif directive does not take arguments
+make: "directive-endif.mk" line 23: The .endif directive does not take arguments
+make: "directive-endif.mk" line 33: The .endif directive does not take arguments
+make: "directive-endif.mk" line 39: The .endif directive does not take arguments
make: "directive-endif.mk" line 45: Unknown directive "endifx"
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/directive-export-impl.exp b/contrib/bmake/unit-tests/directive-export-impl.exp
index 740daa605129..152fc0de4063 100644
--- a/contrib/bmake/unit-tests/directive-export-impl.exp
+++ b/contrib/bmake/unit-tests/directive-export-impl.exp
@@ -7,7 +7,7 @@ Var_Parse: ${UT_VAR:N*} (eval-defined)
Var_Parse: ${REF}> (eval-defined)
Evaluating modifier ${UT_VAR:N...} on value "<>"
Pattern for ':N' is "*"
-ModifyWords: split "<>" into 1 words
+ModifyWords: split "<>" into 1 word
Result of ${UT_VAR:N*} is ""
ParseDependency(: )
CondParser_Eval: ${:!echo "\$UT_VAR"!} != "<>"
@@ -28,7 +28,7 @@ Var_Parse: ${UT_VAR:N*} (eval-defined)
Var_Parse: ${REF}> (eval-defined)
Evaluating modifier ${UT_VAR:N...} on value "<>"
Pattern for ':N' is "*"
-ModifyWords: split "<>" into 1 words
+ModifyWords: split "<>" into 1 word
Result of ${UT_VAR:N*} is ""
ParseDependency(: )
ParseReadLine (54): 'REF= defined'
diff --git a/contrib/bmake/unit-tests/directive-for-escape.exp b/contrib/bmake/unit-tests/directive-for-escape.exp
index 59d4c2324f15..492f82e16d1f 100644
--- a/contrib/bmake/unit-tests/directive-for-escape.exp
+++ b/contrib/bmake/unit-tests/directive-for-escape.exp
@@ -11,45 +11,45 @@ make: "directive-for-escape.mk" line 29: !"\\
For: end for 1
For: loop body:
. info ${:U\$}
-make: "directive-for-escape.mk" line 41: $
+make: "directive-for-escape.mk" line 43: $
For: loop body:
. info ${:U${V}}
-make: "directive-for-escape.mk" line 41: value
+make: "directive-for-escape.mk" line 43: value
For: loop body:
. info ${:U${V:=-with-modifier}}
-make: "directive-for-escape.mk" line 41: value-with-modifier
+make: "directive-for-escape.mk" line 43: value-with-modifier
For: loop body:
. info ${:U$(V)}
-make: "directive-for-escape.mk" line 41: value
+make: "directive-for-escape.mk" line 43: value
For: loop body:
. info ${:U$(V:=-with-modifier)}
-make: "directive-for-escape.mk" line 41: value-with-modifier
+make: "directive-for-escape.mk" line 43: value-with-modifier
For: end for 1
For: loop body:
. info ${:U\${UNDEF\:U\\$\\$}
-make: "directive-for-escape.mk" line 55: ${UNDEF:U\$
+make: "directive-for-escape.mk" line 57: ${UNDEF:U\$
For: loop body:
. info ${:U{{\}\}}
-make: "directive-for-escape.mk" line 55: {{}}
+make: "directive-for-escape.mk" line 57: {{}}
For: loop body:
. info ${:Uend\}}
-make: "directive-for-escape.mk" line 55: end}
+make: "directive-for-escape.mk" line 57: end}
For: end for 1
For: loop body:
. info ${:Ubegin<${UNDEF:Ufallback:N{{{}}}}>end}
-make: "directive-for-escape.mk" line 67: begin<fallback>end
+make: "directive-for-escape.mk" line 69: begin<fallback>end
For: end for 1
For: loop body:
. info ${:U\$}
-make: "directive-for-escape.mk" line 75: $
+make: "directive-for-escape.mk" line 77: $
For: end for 1
For: loop body:
. info ${NUMBERS} ${:Ureplaced}
-make: "directive-for-escape.mk" line 83: one two three replaced
+make: "directive-for-escape.mk" line 85: one two three replaced
For: end for 1
For: loop body:
. info ${:Ureplaced}
-make: "directive-for-escape.mk" line 93: replaced
+make: "directive-for-escape.mk" line 95: replaced
For: end for 1
For: loop body:
. info . $$i: ${:Uinner}
@@ -62,14 +62,31 @@ For: loop body:
. info . $${i2}: ${i2}
. info . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
-make: "directive-for-escape.mk" line 101: . $i: inner
-make: "directive-for-escape.mk" line 102: . ${i}: inner
-make: "directive-for-escape.mk" line 103: . ${i:M*}: inner
-make: "directive-for-escape.mk" line 104: . $(i): inner
-make: "directive-for-escape.mk" line 105: . $(i:M*): inner
-make: "directive-for-escape.mk" line 106: . ${i${:U}}: outer
-make: "directive-for-escape.mk" line 107: . ${i\}}: inner}
-make: "directive-for-escape.mk" line 108: . ${i2}: two
-make: "directive-for-escape.mk" line 109: . ${i,}: comma
-make: "directive-for-escape.mk" line 110: . adjacent: innerinnerinnerinner
-exit status 0
+make: "directive-for-escape.mk" line 103: . $i: inner
+make: "directive-for-escape.mk" line 104: . ${i}: inner
+make: "directive-for-escape.mk" line 105: . ${i:M*}: inner
+make: "directive-for-escape.mk" line 106: . $(i): inner
+make: "directive-for-escape.mk" line 107: . $(i:M*): inner
+make: "directive-for-escape.mk" line 108: . ${i${:U}}: outer
+make: "directive-for-escape.mk" line 109: . ${i\}}: inner}
+make: "directive-for-escape.mk" line 110: . ${i2}: two
+make: "directive-for-escape.mk" line 111: . ${i,}: comma
+make: "directive-for-escape.mk" line 112: . adjacent: innerinnerinnerinner
+For: end for 1
+For: loop body:
+. info eight $$$$$$$$ and no cents.
+. info eight ${:Udollar}${:Udollar}${:Udollar}${:Udollar} and no cents.
+make: "directive-for-escape.mk" line 120: eight $$$$ and no cents.
+make: "directive-for-escape.mk" line 121: eight dollardollardollardollar and no cents.
+make: "directive-for-escape.mk" line 130: eight and no cents.
+For: end for 1
+make: "directive-for-escape.mk" line 137: newline in .for value
+make: "directive-for-escape.mk" line 137: newline in .for value
+For: loop body:
+. info short: ${:U" "}
+. info long: ${:U" "}
+make: "directive-for-escape.mk" line 138: short: " "
+make: "directive-for-escape.mk" line 139: long: " "
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-for-escape.mk b/contrib/bmake/unit-tests/directive-for-escape.mk
index babc4b8c6e88..725fa85d68c3 100644
--- a/contrib/bmake/unit-tests/directive-for-escape.mk
+++ b/contrib/bmake/unit-tests/directive-for-escape.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-escape.mk,v 1.7 2021/02/15 07:58:19 rillig Exp $
+# $NetBSD: directive-for-escape.mk,v 1.12 2021/12/05 11:40:03 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
@@ -13,8 +13,8 @@
ASCII= !"\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
# XXX: As of 2020-12-31, the '#' is not preserved in the expanded body of
-# the loop since it would not need only the escaping for the :U variable
-# modifier but also the escaping for the line-end comment.
+# the loop. Not only would it need the escaping for the variable modifier
+# ':U' but also the escaping for the line-end comment.
.for chars in ${ASCII}
. info ${chars}
.endfor
@@ -29,19 +29,21 @@ ASCII.2020-12-31= !"\\\#$$%&'()*+,-./0-9:;<=>?@A-Z[\]_^a-z{|}~
. info ${chars}
.endfor
-# Cover the code in for_var_len.
+# Cover the code in ExprLen.
#
# XXX: It is unexpected that the variable V gets expanded in the loop body.
-# The double '$$' should prevent exactly this. Probably nobody was
-# adventurous enough to use literal dollar signs in the values of a .for
-# loop.
+# The double '$$' should intuitively prevent exactly this. Probably nobody
+# was adventurous enough to use literal dollar signs in the values of a .for
+# loop, allowing this edge case to go unnoticed for years.
+#
+# See for.c, function ExprLen.
V= value
VALUES= $$ $${V} $${V:=-with-modifier} $$(V) $$(V:=-with-modifier)
.for i in ${VALUES}
. info $i
.endfor
-# Try to cover the code for nested '{}' in for_var_len, without success.
+# 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.
# Instead, it is meant to represent literal text, the only escaping mechanism
@@ -55,9 +57,9 @@ VALUES= $${UNDEF:U\$$\$$ {{}} end}
. info $i
.endfor
-# Second try to cover the code for nested '{}' in for_var_len.
+# Second try to cover the code for nested '{}' in ExprLen.
#
-# XXX: It is wrong that for_var_len requires the braces to be balanced.
+# XXX: It is wrong that ExprLen requires the braces to be balanced.
# Each variable modifier has its own inconsistent way of parsing nested
# variable expressions, braces and parentheses. (Compare ':M', ':S', and
# ':D' for details.) The only sensible thing to do is therefore to let
@@ -110,4 +112,31 @@ i,= comma
. info . adjacent: $i${i}${i:M*}$i
.endfor
+# The variable name can be a single '$' since there is no check on valid
+# variable names. ForLoop_SubstVarShort skips "stupid" variable names though,
+# but ForLoop_SubstVarLong naively parses the body of the loop, substituting
+# each '${$}' with an actual 'dollar'.
+.for $ in dollar
+. info eight $$$$$$$$ and no cents.
+. 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
+# 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.
+closing-brace= } # guard against an
+${closing-brace}= <closing-brace> # alternative interpretation
+.info eight ${$}${$}${$}${$} and no cents.
+
+# What happens if the values from the .for loop contain a literal newline?
+# Before for.c 1.144 from 2021-06-25, the newline was passed verbatim to the
+# body of the .for loop, where it was then interpreted as a literal newline,
+# leading to syntax errors such as "Unclosed variable expression" in the upper
+# line and "Invalid line type" in the lower line.
+.for i in "${.newline}"
+. info short: $i
+. info long: ${i}
+.endfor
+
all:
diff --git a/contrib/bmake/unit-tests/directive-for-if.exp b/contrib/bmake/unit-tests/directive-for-if.exp
new file mode 100644
index 000000000000..85bfc484856b
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-if.exp
@@ -0,0 +1,8 @@
+make: "directive-for-if.mk" line 48: if-less endif
+make: "directive-for-if.mk" line 48: if-less endif
+make: "directive-for-if.mk" line 48: if-less endif
+VAR1
+VAR3
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/directive-for-if.mk b/contrib/bmake/unit-tests/directive-for-if.mk
new file mode 100644
index 000000000000..8d73e8ae8c4d
--- /dev/null
+++ b/contrib/bmake/unit-tests/directive-for-if.mk
@@ -0,0 +1,86 @@
+# $NetBSD: directive-for-if.mk,v 1.1 2021/08/30 17:08:13 rillig Exp $
+#
+# Test for a .for directive that contains an .if directive.
+#
+# Before for.c 1.39 from 2008-12-21, when expanding the variables of a .for
+# loop, their values were placed verbatim in the expanded body. Since then,
+# each variable value expands to an expression of the form ${:Uvalue}.
+#
+# Before that change, the following adventurous code was possible:
+#
+# .for directive in if ifdef ifndef
+# . ${directive} "1" != "0"
+# . endif
+# .endfor
+#
+# A more practical usage of the .for loop that often led to surprises was the
+# following:
+#
+# .for var in VAR1 VAR2 VAR3
+# . if ${var} != "VAR2"
+# . endif
+# .endfor
+#
+# The .for loop body expanded to this string:
+#
+# . if VAR1 != "VAR2"
+# . endif
+#
+# Since bare words were not allowed at the left-hand side of a condition,
+# make complained about a "Malformed conditional", which was surprising since
+# the code before expanding the .for loop body looked quite well.
+#
+# In cond.c 1.48 from 2008-11-29, just a month before the expansion of .for
+# loops changed from plain textual value to using expressions of the form
+# ${:Uvalue}, this surprising behavior was documented in the code, and a
+# workaround was implemented that allowed bare words when they are followed
+# by either '!' or '=', as part of the operators '!=' or '=='.
+#
+# Since cond.c 1.68 from 2015-05-05, bare words are allowed on the left-hand
+# side of a condition, but that applies only to expression of the form
+# ${${cond} :? then : else}, it does not apply to conditions in ordinary .if
+# directives.
+
+# The following snippet worked in 2005, when the variables from the .for loop
+# expanded to their bare textual value.
+.for directive in if ifdef ifndef
+. ${directive} "1" != "0"
+. endif
+.endfor
+# In 2021, the above code does not generate an error message, even though the
+# code looks clearly malformed. This is due to the '!', which is interpreted
+# as a dependency operator, similar to ':' and '::'. The parser turns this
+# line into a dependency with the 3 targets '.', 'if', '"1"' and the 2 sources
+# '=' and '"0"'. Since that line is not interpreted as an '.if' directive,
+# the error message 'if-less endif' makes sense.
+
+# In 2005, make complained:
+#
+# .if line: Malformed conditional (VAR1 != "VAR2")
+# .endif line: if-less endif
+# .endif line: Need an operator
+#
+# 2008.11.30.22.37.55 does not complain about the left-hand side ${var}.
+.for var in VAR1 VAR2 VAR3
+. if ${var} != "VAR2"
+_!= echo "${var}" 1>&2; echo # In 2005, '.info' was not invented yet.
+. endif
+.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
+# 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.
+#
+# Surrounding ${var} with quotes disabled the check for typos though. For
+# ordinary variables, referring to an undefined variable on the left-hand side
+# of the comparison resulted in a "Malformed conditional". Since the .for
+# loop was usually close to the .if clause, this was not a problem in
+# practice.
+.for var in VAR1 VAR2 VAR3
+. if "${var}" != "VAR2"
+. endif
+.endfor
+
+all:
diff --git a/contrib/bmake/unit-tests/directive-for-null.exp b/contrib/bmake/unit-tests/directive-for-null.exp
index 37a7d68925ed..dee26de25e63 100644
--- a/contrib/bmake/unit-tests/directive-for-null.exp
+++ b/contrib/bmake/unit-tests/directive-for-null.exp
@@ -1,5 +1,5 @@
make: "(stdin)" line 2: Zero byte read from file
-make: "(stdin)" line 2: Unexpected end of file in for loop.
+make: "(stdin)" line 2: Unexpected end of file in .for loop
make: "(stdin)" line 3: Zero byte read from file
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/directive-include.exp b/contrib/bmake/unit-tests/directive-include.exp
index af56eefb2b88..b339f393e7e8 100755
--- a/contrib/bmake/unit-tests/directive-include.exp
+++ b/contrib/bmake/unit-tests/directive-include.exp
@@ -3,6 +3,9 @@ lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = !=
CondParser_Eval: ${.MAKE.MAKEFILES:T} != "${.PARSEFILE} null"
lhs = "directive-include.mk null", rhs = "directive-include.mk null", op = !=
make: "directive-include.mk" line 25: Could not find nonexistent.mk
+make: "directive-include.mk" line 47: Could not find "
+make: "directive-include.mk" line 52: Unknown modifier "Z"
+make: "directive-include.mk" line 52: Could not find nonexistent.mk
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/directive-include.mk b/contrib/bmake/unit-tests/directive-include.mk
index d36914b25a63..a6b300b3d273 100755
--- a/contrib/bmake/unit-tests/directive-include.mk
+++ b/contrib/bmake/unit-tests/directive-include.mk
@@ -1,4 +1,4 @@
-# $NetBSD: directive-include.mk,v 1.5 2020/11/21 14:59:11 rillig Exp $
+# $NetBSD: directive-include.mk,v 1.7 2021/12/03 22:48:07 rillig Exp $
#
# Tests for the .include directive, which includes another file.
@@ -30,5 +30,25 @@
# As of 2020-11-21, anything after the delimiter '"' is ignored.
.include "/dev/null" and ignore anything in the rest of the line.
+# The filename to be included can contain expressions.
+DEV= null
+.include "/dev/${DEV}"
+
+# Expressions in double quotes or angle quotes are first parsed naively, to
+# find the closing '"'. In a second step, the expressions are expanded. This
+# means that the expressions cannot include the characters '"' or '>'. This
+# restriction is not practically relevant since the expressions inside
+# '.include' directives are typically kept as simple as possible.
+#
+# If the whole line were expanded before parsing, the filename to be included
+# would be empty, and the closing '"' would be in the trailing part of the
+# line, which is ignored as of 2021-12-03.
+DQUOT= "
+.include "${DQUOT}"
+
+# When the expression in a filename cannot be evaluated, the failing
+# expression is skipped and the file is included nevertheless.
+# FIXME: Add proper error handling, no file must be included here.
+.include "nonexistent${:U123:Z}.mk"
+
all:
- @:;
diff --git a/contrib/bmake/unit-tests/export.mk b/contrib/bmake/unit-tests/export.mk
index 94e3a862dce1..bab08ee3ea23 100644
--- a/contrib/bmake/unit-tests/export.mk
+++ b/contrib/bmake/unit-tests/export.mk
@@ -1,4 +1,4 @@
-# $NetBSD: export.mk,v 1.10 2020/10/24 08:50:17 rillig Exp $
+# $NetBSD: export.mk,v 1.11 2021/12/05 14:57:36 rillig Exp $
UT_TEST= export
UT_FOO= foo${BAR}
@@ -40,7 +40,7 @@ BAR= bar is ${UT_FU}
.MAKE.EXPORTED+= UT_ZOO UT_TEST
-FILTER_CMD?= egrep -v '^(MAKEFLAGS|MALLOC_OPTIONS|PATH|PWD|SHLVL|_|&)='
+FILTER_CMD?= egrep -v '^(MAKEFLAGS|MALLOC_.*|PATH|PWD|SHLVL|_|&)='
all:
@env | ${FILTER_CMD} | sort
diff --git a/contrib/bmake/unit-tests/job-output-null.exp b/contrib/bmake/unit-tests/job-output-null.exp
index af9b4e64dba3..631d4862af44 100644
--- a/contrib/bmake/unit-tests/job-output-null.exp
+++ b/contrib/bmake/unit-tests/job-output-null.exp
@@ -1,4 +1,4 @@
-hello
-hello
-hello world without newline, hello world without newline, hello world without newline.
+1
+2a
+3a without newline, 3b without newline.
exit status 0
diff --git a/contrib/bmake/unit-tests/job-output-null.mk b/contrib/bmake/unit-tests/job-output-null.mk
index 7620bdf6a7ba..1efd9c667980 100644
--- a/contrib/bmake/unit-tests/job-output-null.mk
+++ b/contrib/bmake/unit-tests/job-output-null.mk
@@ -1,4 +1,4 @@
-# $NetBSD: job-output-null.mk,v 1.1 2021/04/15 19:02:29 rillig Exp $
+# $NetBSD: job-output-null.mk,v 1.3 2021/09/12 10:26:49 rillig Exp $
#
# Test how null bytes in the output of a command are handled. Make processes
# them using null-terminated strings, which may cut off some of the output.
@@ -7,20 +7,33 @@
# inconsistently. It's an edge case though since typically the child
# processes output text.
+# Note: The printf commands used in this test must only use a single format
+# string, without parameters. This is because it is implementation-dependent
+# how many times the command 'printf "fmt%s" "" "" ""' calls write(2).
+#
+# NetBSD /bin/sh 1 x write("fmtfmtfmt")
+# Dash 1 x write("fmtfmtfmt")
+# NetBSD /bin/ksh 3 x write("fmt") (via /bin/printf)
+# Bash 5 3 x write("fmt")
+#
+# In the latter case the output may arrive in parts, which in this test makes
+# a crucial difference since the outcome of the test depends on whether there
+# is a '\n' in each of the blocks from the output.
+
.MAKEFLAGS: -j1 # force jobs mode
all: .PHONY
# The null byte from the command output is kept as-is.
# See CollectOutput, which looks like it intended to replace these
# null bytes with simple spaces.
- @printf 'hello\0world%s\n' ''
+ @printf '1\0trailing\n'
# Give the parent process a chance to see the above output, but not
# yet the output from the next printf command.
@sleep 1
# All null bytes from the command output are kept as-is.
- @printf 'hello\0world%s\n' '' '' '' '' '' ''
+ @printf '2a\0trailing\n''2b\0trailing\n''2c\0trailing\n'
@sleep 1
@@ -29,4 +42,4 @@ all: .PHONY
#
# The three null bytes in a row test whether this output is
# compressed to a single space like in DebugFailedTarget. It isn't.
- @printf 'hello\0world\0without\0\0\0newline%s' ', ' ', ' '.'
+ @printf '3a\0without\0\0\0newline, 3b\0without\0\0\0newline.'
diff --git a/contrib/bmake/unit-tests/lint.exp b/contrib/bmake/unit-tests/lint.exp
index d7068b5e006a..db2290c040cd 100755
--- a/contrib/bmake/unit-tests/lint.exp
+++ b/contrib/bmake/unit-tests/lint.exp
@@ -1,4 +1,4 @@
-make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar.
+make: In the :@ modifier of "VAR", the variable name "${:Ubar:S,b,v,}" must not contain a dollar
y@:Q}
xvaluey
exit status 2
diff --git a/contrib/bmake/unit-tests/objdir-writable.exp b/contrib/bmake/unit-tests/objdir-writable.exp
index 9c507f647f8c..e7298a66d369 100644
--- a/contrib/bmake/unit-tests/objdir-writable.exp
+++ b/contrib/bmake/unit-tests/objdir-writable.exp
@@ -1,5 +1,5 @@
make warning: TMPDIR/roobj: Permission denied.
-/tmp
+TMPDIR
TMPDIR/roobj
TMPDIR/roobj
exit status 0
diff --git a/contrib/bmake/unit-tests/objdir-writable.mk b/contrib/bmake/unit-tests/objdir-writable.mk
index 9fc1c69afb56..b09baa3c32b2 100644
--- a/contrib/bmake/unit-tests/objdir-writable.mk
+++ b/contrib/bmake/unit-tests/objdir-writable.mk
@@ -1,8 +1,9 @@
-# $NetBSD: objdir-writable.mk,v 1.4 2020/11/14 07:36:00 sjg Exp $
+# $NetBSD: objdir-writable.mk,v 1.5 2021/07/04 01:28:54 sjg Exp $
# test checking for writable objdir
-RO_OBJDIR?= ${TMPDIR:U/tmp}/roobj
+TMPDIR?= /tmp
+RO_OBJDIR?= ${TMPDIR}/roobj
.if make(do-objdir)
# this should succeed
@@ -20,12 +21,12 @@ rm-objdir:
@rmdir ${RO_OBJDIR}
no-objdir:
- @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C /tmp -V .OBJDIR
+ @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C ${TMPDIR} -V .OBJDIR
ro-objdir:
- @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C /tmp -V .OBJDIR MAKE_OBJDIR_CHECK_WRITABLE=no
+ @MAKEOBJDIR=${RO_OBJDIR} ${.MAKE} -r -f /dev/null -C ${TMPDIR} -V .OBJDIR MAKE_OBJDIR_CHECK_WRITABLE=no
explicit-objdir:
- @MAKEOBJDIR=/tmp ${.MAKE} -r -f ${MAKEFILE:tA} -C /tmp do-objdir -V .OBJDIR
+ @MAKEOBJDIR=${TMPDIR} ${.MAKE} -r -f ${MAKEFILE:tA} -C ${TMPDIR} do-objdir -V .OBJDIR
.endif
diff --git a/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp b/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp
index 25eb2b470b72..c957c7736b32 100644
--- a/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp
+++ b/contrib/bmake/unit-tests/opt-debug-errors-jobs.exp
@@ -24,6 +24,8 @@ line2
*** Failed target: fail-newline
*** Failed commands:
echo 'line1${.newline}line2'; false
+ => echo 'line1
+line2'; false
*** [fail-newline] Error code 1
make: stopped in unit-tests
@@ -45,4 +47,12 @@ word1 word2
*** [fail-multiline-intention] Error code 1
make: stopped in unit-tests
+
+*** Failed target: fail-vars
+*** Failed commands:
+ @${COMPILE_C} ${COMPILE_C_FLAGS}
+ => @false c-compiler flag1 -macro="several words"
+*** [fail-vars] Error code 1
+
+make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk b/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk
index 83b50987a752..007a5f37e08a 100644
--- a/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk
+++ b/contrib/bmake/unit-tests/opt-debug-errors-jobs.mk
@@ -1,4 +1,4 @@
-# $NetBSD: opt-debug-errors-jobs.mk,v 1.1 2021/04/27 16:20:06 rillig Exp $
+# $NetBSD: opt-debug-errors-jobs.mk,v 1.2 2021/11/27 23:56:11 rillig Exp $
#
# Tests for the -de command line option, which adds debug logging for
# failed commands and targets; since 2021-04-27 also in jobs mode.
@@ -10,6 +10,7 @@ all: fail-escaped-space
all: fail-newline
all: fail-multiline
all: fail-multiline-intention
+all: fail-vars
fail-spaces:
echo '3 spaces'; false
@@ -34,3 +35,14 @@ fail-multiline:
fail-multiline-intention:
echo 'word1' \
'word2'; false
+
+# In makefiles that rely heavily on abstracted variables, it is not possible
+# to determine the actual command from the unexpanded command alone. To help
+# debugging these issues (for example in NetBSD's build.sh), output the
+# expanded command as well whenever it differs from the unexpanded command.
+# Since 2021-11-28.
+COMPILE_C= false c-compiler
+COMPILE_C_DEFS= macro="several words"
+COMPILE_C_FLAGS=flag1 ${COMPILE_C_DEFS:@def@-${def}@}
+fail-vars:
+ @${COMPILE_C} ${COMPILE_C_FLAGS}
diff --git a/contrib/bmake/unit-tests/opt-debug-graph1.exp b/contrib/bmake/unit-tests/opt-debug-graph1.exp
index 4d4aa0c3faea..4049900fee75 100644
--- a/contrib/bmake/unit-tests/opt-debug-graph1.exp
+++ b/contrib/bmake/unit-tests/opt-debug-graph1.exp
@@ -46,7 +46,6 @@ MFLAGS = -r -k -d g1
# Stats: 0 hits 2 misses 0 near misses 0 losers (0%)
# refs hits directory
# 1 0 <curdir>
-# 1 0 .
#*** Suffixes:
#*** Transformations:
diff --git a/contrib/bmake/unit-tests/opt-debug-graph2.exp b/contrib/bmake/unit-tests/opt-debug-graph2.exp
index 03f02719618e..675e5e8cac18 100644
--- a/contrib/bmake/unit-tests/opt-debug-graph2.exp
+++ b/contrib/bmake/unit-tests/opt-debug-graph2.exp
@@ -81,7 +81,6 @@ MFLAGS = -r -k -d g2
# Stats: 0 hits 4 misses 0 near misses 0 losers (0%)
# refs hits directory
# 1 0 <curdir>
-# 1 0 .
#*** Suffixes:
#*** Transformations:
diff --git a/contrib/bmake/unit-tests/opt-debug-graph3.exp b/contrib/bmake/unit-tests/opt-debug-graph3.exp
index f2966442eb26..78edb59e4e02 100644
--- a/contrib/bmake/unit-tests/opt-debug-graph3.exp
+++ b/contrib/bmake/unit-tests/opt-debug-graph3.exp
@@ -81,7 +81,6 @@ MFLAGS = -r -k -d g3
# Stats: 0 hits 4 misses 0 near misses 0 losers (0%)
# refs hits directory
# 1 0 <curdir>
-# 1 0 .
#*** Suffixes:
#*** Transformations:
diff --git a/contrib/bmake/unit-tests/opt-file.mk b/contrib/bmake/unit-tests/opt-file.mk
index b7a1c09e6d16..edeff4b9ab11 100644
--- a/contrib/bmake/unit-tests/opt-file.mk
+++ b/contrib/bmake/unit-tests/opt-file.mk
@@ -1,6 +1,7 @@
-# $NetBSD: opt-file.mk,v 1.12 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: opt-file.mk,v 1.14 2021/12/09 20:47:33 rillig Exp $
#
-# Tests for the -f command line option.
+# Tests for the -f command line option, which adds a makefile to the list of
+# files that are parsed.
# TODO: Implementation
@@ -10,7 +11,8 @@ all: file-ending-in-backslash-mmap
all: line-with-trailing-whitespace
all: file-containing-null-byte
-# Passing '-' as the filename reads from stdin. This is unusual but possible.
+# When the filename is '-', the input comes from stdin. This is unusual but
+# possible.
#
# In the unlikely case where a file ends in a backslash instead of a newline,
# that backslash is trimmed. See ParseGetLine.
@@ -19,7 +21,9 @@ all: file-containing-null-byte
# outside of the file buffer.
#
# printf '%s' 'VAR=value\' \
-# | MALLOC_OPTIONS=JA make-2014.01.01.00.00.00 -r -f - -V VAR -dA 2>&1 \
+# | MALLOC_OPTIONS="JA" \
+# MALLOC_CONF="junk:true" \
+# make-2014.01.01.00.00.00 -r -f - -V VAR -dA 2>&1 \
# | less
#
# The debug output shows how make happily uses freshly allocated memory (the
diff --git a/contrib/bmake/unit-tests/opt-tracefile.exp b/contrib/bmake/unit-tests/opt-tracefile.exp
index 39a9383953dd..0e815606d34f 100644
--- a/contrib/bmake/unit-tests/opt-tracefile.exp
+++ b/contrib/bmake/unit-tests/opt-tracefile.exp
@@ -1 +1,12 @@
+Making dependency1 from <nothing>.
+Making dependency2 from <nothing>.
+Making trace from dependency1 dependency2.
+0 BEG
+1 JOB
+1 DON
+1 JOB
+1 DON
+1 JOB
+1 DON
+0 END
exit status 0
diff --git a/contrib/bmake/unit-tests/opt-tracefile.mk b/contrib/bmake/unit-tests/opt-tracefile.mk
index b62392ca913c..291824680606 100644
--- a/contrib/bmake/unit-tests/opt-tracefile.mk
+++ b/contrib/bmake/unit-tests/opt-tracefile.mk
@@ -1,8 +1,16 @@
-# $NetBSD: opt-tracefile.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: opt-tracefile.mk,v 1.5 2021/12/06 22:35:20 rillig Exp $
#
-# Tests for the -T command line option.
+# Tests for the command line option '-T', which in jobs mode appends a trace
+# record to a trace log whenever a job is started or completed.
-# TODO: Implementation
+all: .PHONY
+ @rm -f opt-tracefile.log
+ @${MAKE} -f ${MAKEFILE} -j1 -Topt-tracefile.log trace
+ # Remove timestamps, process IDs and directory paths.
+ @awk '{ print $$2, $$3 }' opt-tracefile.log
+ @rm opt-tracefile.log
-all:
- @:;
+trace dependency1 dependency2: .PHONY
+ @echo 'Making ${.TARGET} from ${.ALLSRC:S,^$,<nothing>,W}.'
+
+trace: dependency1 dependency2
diff --git a/contrib/bmake/unit-tests/suff-main-several.exp b/contrib/bmake/unit-tests/suff-main-several.exp
index 09fa6d63bffa..b20f5ecf1143 100644
--- a/contrib/bmake/unit-tests/suff-main-several.exp
+++ b/contrib/bmake/unit-tests/suff-main-several.exp
@@ -111,7 +111,6 @@ MFLAGS = -r -k -d mps -d 0 -d g1
# Stats: 0 hits 2 misses 0 near misses 0 losers (0%)
# refs hits directory
# 1 0 <curdir>
-# 1 0 .
#*** Suffixes:
# ".4" (num 1, ref 1)
diff --git a/contrib/bmake/unit-tests/suff-transform-debug.exp b/contrib/bmake/unit-tests/suff-transform-debug.exp
index 0634ff616d0d..7fec51a1de9d 100644
--- a/contrib/bmake/unit-tests/suff-transform-debug.exp
+++ b/contrib/bmake/unit-tests/suff-transform-debug.exp
@@ -37,7 +37,6 @@ MFLAGS = -r -k -d g1
# Stats: 0 hits 2 misses 0 near misses 0 losers (0%)
# refs hits directory
# 1 0 <curdir>
-# 1 0 .
#*** Suffixes:
# ".a" (num 1, ref 2)
diff --git a/contrib/bmake/unit-tests/var-eval-short.exp b/contrib/bmake/unit-tests/var-eval-short.exp
index ae0aff7d7c2c..c476c93125e3 100644
--- a/contrib/bmake/unit-tests/var-eval-short.exp
+++ b/contrib/bmake/unit-tests/var-eval-short.exp
@@ -1,16 +1,16 @@
-make: "var-eval-short.mk" line 41: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar.
+make: "var-eval-short.mk" line 41: In the :@ modifier of "", the variable name "${FAIL}" must not contain a dollar
make: "var-eval-short.mk" line 41: Malformed conditional (0 && ${:Uword:@${FAIL}@expr@})
-make: "var-eval-short.mk" line 79: Invalid time value: ${FAIL}}
-make: "var-eval-short.mk" line 79: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}})
-make: "var-eval-short.mk" line 93: Invalid time value: ${FAIL}}
-make: "var-eval-short.mk" line 93: Malformed conditional (0 && ${:Uword:localtime=${FAIL}})
+make: "var-eval-short.mk" line 81: Invalid time value at "${FAIL}}"
+make: "var-eval-short.mk" line 81: Malformed conditional (0 && ${:Uword:gmtime=${FAIL}})
+make: "var-eval-short.mk" line 95: Invalid time value at "${FAIL}}"
+make: "var-eval-short.mk" line 95: Malformed conditional (0 && ${:Uword:localtime=${FAIL}})
CondParser_Eval: 0 && ${0:?${FAIL}then:${FAIL}else}
Var_Parse: ${0:?${FAIL}then:${FAIL}else} (parse-only)
Parsing modifier ${0:?...}
Modifier part: "${FAIL}then"
Modifier part: "${FAIL}else"
Result of ${0:?${FAIL}then:${FAIL}else} is "" (parse-only, defined)
-ParseReadLine (158): 'DEFINED= defined'
+ParseReadLine (160): 'DEFINED= defined'
Global: DEFINED = defined
CondParser_Eval: 0 && ${DEFINED:L:?${FAIL}then:${FAIL}else}
Var_Parse: ${DEFINED:L:?${FAIL}then:${FAIL}else} (parse-only)
@@ -20,7 +20,7 @@ Parsing modifier ${DEFINED:?...}
Modifier part: "${FAIL}then"
Modifier part: "${FAIL}else"
Result of ${DEFINED:?${FAIL}then:${FAIL}else} is "defined" (parse-only, regular)
-ParseReadLine (161): '.MAKEFLAGS: -d0'
+ParseReadLine (163): '.MAKEFLAGS: -d0'
ParseDependency(.MAKEFLAGS: -d0)
Global: .MAKEFLAGS = -r -k -d cpv -d
Global: .MAKEFLAGS = -r -k -d cpv -d 0
diff --git a/contrib/bmake/unit-tests/var-eval-short.mk b/contrib/bmake/unit-tests/var-eval-short.mk
index 41782f0d7823..1b9ccc714736 100644
--- a/contrib/bmake/unit-tests/var-eval-short.mk
+++ b/contrib/bmake/unit-tests/var-eval-short.mk
@@ -1,4 +1,4 @@
-# $NetBSD: var-eval-short.mk,v 1.5 2021/04/04 13:35:26 rillig Exp $
+# $NetBSD: var-eval-short.mk,v 1.7 2021/09/07 20:41:58 rillig Exp $
#
# Tests for each variable modifier to ensure that they only do the minimum
# necessary computations. If the result of the expression is not needed, they
@@ -44,13 +44,13 @@ FAIL= ${:!echo unexpected 1>&2!}
.if 0 && ${:Uword:@var@${FAIL}@}
.endif
-# Before var.c,v 1.877 from 2021-03-14, the modifier ':[...]' did not expand
+# Before var.c 1.877 from 2021-03-14, the modifier ':[...]' did not expand
# the nested expression ${FAIL} and then tried to parse the unexpanded text,
# which failed since '$' is not a valid range character.
.if 0 && ${:Uword:[${FAIL}]}
.endif
-# Before var.c,v 1.867 from 2021-03-14, the modifier ':_' defined the variable
+# Before var.c 1.867 from 2021-03-14, the modifier ':_' defined the variable
# even though the whole expression should have only been parsed, not
# evaluated.
.if 0 && ${:Uword:_=VAR}
@@ -58,11 +58,13 @@ FAIL= ${:!echo unexpected 1>&2!}
. error
.endif
-# Before var.c,v 1.856 from 2021-03-14, the modifier ':C' did not expand the
-# nested expression ${FAIL} and then tried to compile the unexpanded text as a
-# regular expression, which failed both because of the '{FAIL}', which is not
-# a valid repetition, and because of the '****', which are repeated
-# repetitions as well.
+# Before var.c 1.856 from 2021-03-14, the modifier ':C' did not expand the
+# nested expression ${FAIL}, which is correct, and then tried to compile the
+# unexpanded text as a regular expression, which is unnecessary since the
+# right-hand side of the '&&' cannot influence the outcome of the condition.
+# Compiling the regular expression then failed both because of the '{FAIL}',
+# which is not a valid repetition of the form '{1,5}', and because of the
+# '****', which are repeated repetitions as well.
# '${FAIL}'
.if 0 && ${:Uword:C,${FAIL}****,,}
.endif
diff --git a/contrib/bmake/unit-tests/var-op-expand.exp b/contrib/bmake/unit-tests/var-op-expand.exp
index 39a9383953dd..a4ba53942cf7 100644
--- a/contrib/bmake/unit-tests/var-op-expand.exp
+++ b/contrib/bmake/unit-tests/var-op-expand.exp
@@ -1 +1,7 @@
-exit status 0
+make: "var-op-expand.mk" line 265: Unknown modifier "s,value,replaced,"
+make: "var-op-expand.mk" line 268: warning: XXX Neither branch should be taken.
+make: "var-op-expand.mk" line 273: Unknown modifier "s,value,replaced,"
+make: "var-op-expand.mk" line 274: warning: XXX Neither branch should be taken.
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/var-op-expand.mk b/contrib/bmake/unit-tests/var-op-expand.mk
index ff62668a8ada..237f7baf1c62 100644
--- a/contrib/bmake/unit-tests/var-op-expand.mk
+++ b/contrib/bmake/unit-tests/var-op-expand.mk
@@ -1,8 +1,14 @@
-# $NetBSD: var-op-expand.mk,v 1.11 2021/01/01 23:07:48 sjg Exp $
+# $NetBSD: var-op-expand.mk,v 1.15 2021/11/30 23:52:19 rillig Exp $
#
# Tests for the := variable assignment operator, which expands its
# right-hand side.
+#
+# See also:
+# varname-dot-make-save_dollars.mk
+# Force the test results to be independent of the default value of this
+# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake
+# distribution and pkgsrc/devel/bmake.
.MAKE.SAVE_DOLLARS:= yes
# If the right-hand side does not contain a dollar sign, the ':=' assignment
@@ -174,5 +180,102 @@ VAR_SUBST_${UNDEF}:= assigned by ':='
. error
.endif
+
+# The following test case demonstrates that the variable 'LATER' is preserved
+# in the ':=' assignment since the variable 'LATER' is not yet defined.
+# After the assignment to 'LATER', evaluating the variable 'INDIRECT'
+# evaluates 'LATER' as well.
+#
+.undef LATER
+INDIRECT:= ${LATER:S,value,replaced,}
+.if ${INDIRECT} != ""
+. error
+.endif
+LATER= late-value
+.if ${INDIRECT} != "late-replaced"
+. error
+.endif
+
+
+# Same as the test case above, except for the additional modifier ':tl' when
+# evaluating the variable 'INDIRECT'. Nothing surprising here.
+.undef LATER
+.undef later
+INDIRECT:= ${LATER:S,value,replaced,}
+.if ${INDIRECT:tl} != ""
+. error
+.endif
+LATER= uppercase-value
+later= lowercase-value
+.if ${INDIRECT:tl} != "uppercase-replaced"
+. error
+.endif
+
+
+# Similar to the two test cases above, the situation gets a bit more involved
+# here, due to the double indirection. The variable 'indirect' is supposed to
+# be the lowercase version of the variable 'INDIRECT'.
+#
+# The assignment operator ':=' for the variable 'INDIRECT' could be a '=' as
+# well, it wouldn't make a difference in this case. The crucial detail is the
+# assignment operator ':=' for the variable 'indirect'. During this
+# assignment, the variable modifier ':S,value,replaced,' is converted to
+# lowercase, which turns 'S' into 's', thus producing an unknown modifier.
+# In this case, make issues a warning, but in cases where the modifier
+# includes a '=', the modifier would be interpreted as a SysV-style
+# substitution like '.c=.o', and make would not issue a warning, leading to
+# silent unexpected behavior.
+#
+# As of 2021-11-20, the actual behavior is unexpected. Fixing it is not
+# trivial. When the assignment to 'indirect' takes place, the expressions
+# from the nested expression could be preserved, like this:
+#
+# Start with:
+#
+# indirect:= ${INDIRECT:tl}
+#
+# Since INDIRECT is defined, expand it, remembering that the modifier
+# ':tl' must still be applied to the final result.
+#
+# indirect:= ${LATER:S,value,replaced,} \
+# OK \
+# ${LATER:value=sysv}
+#
+# The variable 'LATER' is not defined. An idea may be to append the
+# remaining modifier ':tl' to each expression that is starting with an
+# undefined variable, resulting in:
+#
+# indirect:= ${LATER:S,value,replaced,:tl} \
+# OK \
+# ${LATER:value=sysv:tl}
+#
+# This would work for the first expression. The second expression ends
+# with the SysV modifier ':from=to', and when this modifier is parsed,
+# it consumes all characters until the end of the expression, which in
+# this case would replace the suffix 'value' with the literal 'sysv:tl',
+# ignoring that the ':tl' was intended to be an additional modifier.
+#
+# Due to all of this, this surprising behavior is not easy to fix.
+#
+.undef LATER
+.undef later
+INDIRECT:= ${LATER:S,value,replaced,} OK ${LATER:value=sysv}
+indirect:= ${INDIRECT:tl}
+# expect+1: Unknown modifier "s,value,replaced,"
+.if ${indirect} != " ok "
+. error
+.else
+. warning XXX Neither branch should be taken.
+.endif
+LATER= uppercase-value
+later= lowercase-value
+# expect+1: Unknown modifier "s,value,replaced,"
+.if ${indirect} != "uppercase-replaced ok uppercase-sysv"
+. warning XXX Neither branch should be taken.
+.else
+. error
+.endif
+
+
all:
@:;
diff --git a/contrib/bmake/unit-tests/vardebug.exp b/contrib/bmake/unit-tests/vardebug.exp
index 6d00acc977af..3519bbd0ba1b 100644
--- a/contrib/bmake/unit-tests/vardebug.exp
+++ b/contrib/bmake/unit-tests/vardebug.exp
@@ -43,11 +43,11 @@ Result of ${:Uvalue} is "value" (eval-defined, defined)
Indirect modifier "M*e" from "${:UM*e}"
Evaluating modifier ${:M...} on value "value" (eval-defined, defined)
Pattern for ':M' is "*e"
-ModifyWords: split "value" into 1 words
+ModifyWords: split "value" into 1 word
Result of ${:M*e} is "value" (eval-defined, defined)
Evaluating modifier ${:M...} on value "value" (eval-defined, defined)
Pattern for ':M' is "valu[e]"
-ModifyWords: split "value" into 1 words
+ModifyWords: split "value" into 1 word
Result of ${:Mvalu[e]} is "value" (eval-defined, defined)
Global:delete VAR
Var_Parse: ${:Uvariable:unknown} (eval-defined)
diff --git a/contrib/bmake/unit-tests/varmisc.mk b/contrib/bmake/unit-tests/varmisc.mk
index e5ab375d8f39..81818f3fb8bb 100644
--- a/contrib/bmake/unit-tests/varmisc.mk
+++ b/contrib/bmake/unit-tests/varmisc.mk
@@ -1,5 +1,5 @@
-# $Id: varmisc.mk,v 1.23 2021/02/05 20:02:30 sjg Exp $
-# $NetBSD: varmisc.mk,v 1.30 2021/02/04 21:42:47 rillig Exp $
+# $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 $
#
# Miscellaneous variable tests.
@@ -66,7 +66,7 @@ cmpv:
@echo Literal=3.4.5 == ${3.4.5:L:${M_cmpv}}
@echo We have ${${.TARGET:T}.only}
-# catch misshandling of nested vars in .for loop
+# catch mishandling of nested variables in .for loop
MAN=
MAN1= make.1
.for s in 1 2
@@ -78,12 +78,15 @@ MAN+= ${MAN$s}
manok:
@echo MAN=${MAN}
+# Test parsing of boolean values.
# begin .MAKE.SAVE_DOLLARS; see Var_SetWithFlags and ParseBoolean.
SD_VALUES= 0 1 2 False True false true Yes No yes no On Off ON OFF on off
SD_4_DOLLARS= $$$$
.for val in ${SD_VALUES}
-.MAKE.SAVE_DOLLARS:= ${val} # Must be := since a simple = has no effect.
+# The assignment must be done using ':=' since a simple '=' would be
+# interpreted as 'yes', due to the leading '$'; see ParseBoolean.
+.MAKE.SAVE_DOLLARS:= ${val}
SD.${val}:= ${SD_4_DOLLARS}
.endfor
.MAKE.SAVE_DOLLARS:= yes
@@ -92,6 +95,7 @@ save-dollars:
.for val in ${SD_VALUES}
@printf '%s: %-8s = %s\n' $@ ${val} ${SD.${val}:Q}
.endfor
+# end .MAKE.SAVE_DOLLARS
# Appending to an undefined variable does not add a space in front.
.undef APPENDED
diff --git a/contrib/bmake/unit-tests/varmod-assign.exp b/contrib/bmake/unit-tests/varmod-assign.exp
index 1e43714d500b..1ad388418ab5 100644
--- a/contrib/bmake/unit-tests/varmod-assign.exp
+++ b/contrib/bmake/unit-tests/varmod-assign.exp
@@ -12,18 +12,6 @@ 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
-mod-assign: first=1.
-mod-assign: last=3.
-mod-assign: appended=1 2 3.
-1
-2
-3
-mod-assign: ran:3.
-mod-assign: global: 1, 3, 1 2 3, 3.
-mod-assign-nested: then1t1
-mod-assign-nested: else2e2
-mod-assign-nested: then3t3
-mod-assign-nested: else4e4
make: Bad modifier ":" for variable ""
mod-assign-empty: value}
make: Bad modifier ":" for variable ""
diff --git a/contrib/bmake/unit-tests/varmod-assign.mk b/contrib/bmake/unit-tests/varmod-assign.mk
index f50c654f5bcf..b8559025fbfd 100644
--- a/contrib/bmake/unit-tests/varmod-assign.mk
+++ b/contrib/bmake/unit-tests/varmod-assign.mk
@@ -1,70 +1,79 @@
-# $NetBSD: varmod-assign.mk,v 1.12 2021/03/15 18:56:38 rillig Exp $
+# $NetBSD: varmod-assign.mk,v 1.14 2021/12/05 10:13:44 rillig Exp $
#
# Tests for the obscure ::= variable modifiers, which perform variable
# assignments during evaluation, just like the = operator in C.
-all: mod-assign
-all: mod-assign-nested
all: mod-assign-empty
all: mod-assign-parse
all: mod-assign-shell-error
-mod-assign:
- # 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.
- @echo $@: ${1 2 3:L:@i@${FIRST::?=$i}@} first=${FIRST}.
-
- # The ::= modifier applies the = assignment operator 3 times.
- # The = operator overwrites the previous value, therefore the
- # variable LAST ends up with the value 3.
- @echo $@: ${1 2 3:L:@i@${LAST::=$i}@} last=${LAST}.
-
- # 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".
- @echo $@: ${1 2 3:L:@i@${APPENDED::+=$i}@} appended=${APPENDED}.
-
- # The ::!= modifier applies the != assignment operator 3 times.
- # The side effects of the shell commands are visible in the output.
- # Just as with the ::= modifier, the last value is stored in the
- # RAN variable.
- @echo $@: ${echo.1 echo.2 echo.3:L:@i@${RAN::!=${i:C,.*,&; & 1>\&2,:S,., ,g}}@} ran:${RAN}.
-
- # The assignments happen in the global scope and thus are
- # preserved even after the shell command has been run.
- @echo $@: global: ${FIRST:Q}, ${LAST:Q}, ${APPENDED:Q}, ${RAN:Q}.
-
-mod-assign-nested:
- # The condition "1" is true, therefore THEN1 gets assigned a value,
- # and IT1 as well. Nothing surprising here.
- @echo $@: ${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}}${THEN1}${ELSE1}${IT1}${IE1}
-
- # The condition "0" is false, therefore ELSE1 gets assigned a value,
- # and IE1 as well. Nothing surprising here as well.
- @echo $@: ${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}}${THEN2}${ELSE2}${IT2}${IE2}
-
- # The same effects happen when the variables are defined elsewhere.
- @echo $@: ${SINK3:Q}
- @echo $@: ${SINK4:Q}
-SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}}${THEN3}${ELSE3}${IT3}${IE3}
-SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}}${THEN4}${ELSE4}${IT4}${IE4}
+# 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
+# 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
+# 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
+# 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 happen in the global scope and thus are preserved even after
+# the shell command has been run and the condition has been evaluated.
+.if "${FIRST}, ${LAST}, ${APPENDED}, ${RAN}" != "1, 3, 1 2 3, <3>"
+. error
+.endif
+
+# Tests for nested assignments, which are hard to read and therefore seldom
+# used in practice.
+
+# The condition "1" is true, therefore THEN1 gets assigned a value,
+# and the inner IT1 as well. Nothing surprising here.
+.if "${1:?${THEN1::=then1${IT1::=t1}}:${ELSE1::=else1${IE1::=e1}}} ${THEN1}${ELSE1}${IT1}${IE1}" != " then1t1"
+. error
+.endif
+
+# The condition "0" is false, therefore ELSE2 gets assigned a value,
+# and the inner IE2 as well. Nothing surprising here as well.
+.if "${0:?${THEN2::=then2${IT2::=t2}}:${ELSE2::=else2${IE2::=e2}}} ${THEN2}${ELSE2}${IT2}${IE2}" != " else2e2"
+. error
+.endif
+
+# The same effects happen when the variables are defined elsewhere.
+SINK3:= ${1:?${THEN3::=then3${IT3::=t3}}:${ELSE3::=else3${IE3::=e3}}} ${THEN3}${ELSE3}${IT3}${IE3}
+SINK4:= ${0:?${THEN4::=then4${IT4::=t4}}:${ELSE4::=else4${IE4::=e4}}} ${THEN4}${ELSE4}${IT4}${IE4}
+.if ${SINK3} != " then3t3"
+. error
+.endif
+.if ${SINK4} != " else4e4"
+. error
+.endif
mod-assign-empty:
# Assigning to the empty variable would obviously not work since that
# variable is write-protected. Therefore it is rejected early with a
# "Bad modifier" message.
- #
- # XXX: The error message is hard to read since the variable name is
- # empty. This leads to a trailing space in the error message.
@echo $@: ${::=value}
# In this variant, it is not as obvious that the name of the
# expression is empty. Assigning to it is rejected as well, with the
# same "Bad modifier" message.
- #
- # XXX: The error message is hard to read since the variable name is
- # empty. This leads to a trailing space in the error message.
@echo $@: ${:Uvalue::=overwritten}
# The :L modifier sets the value of the expression to its variable
diff --git a/contrib/bmake/unit-tests/varmod-defined.exp b/contrib/bmake/unit-tests/varmod-defined.exp
index 2f7d4dbf4baa..e2ae8d29808c 100644
--- a/contrib/bmake/unit-tests/varmod-defined.exp
+++ b/contrib/bmake/unit-tests/varmod-defined.exp
@@ -11,7 +11,7 @@ Var_Parse: ${VAR:@var@${8_DOLLARS}@} (eval-keep-dollar-and-undefined)
Evaluating modifier ${VAR:@...} on value "$$$$$$$$" (eval-keep-dollar-and-undefined, regular)
Modifier part: "var"
Modifier part: "${8_DOLLARS}"
-ModifyWords: split "$$$$$$$$" into 1 words
+ModifyWords: split "$$$$$$$$" into 1 word
Global: var = $$$$$$$$
Var_Parse: ${8_DOLLARS} (eval-keep-undefined)
ModifyWord_Loop: in "$$$$$$$$", replace "var" with "${8_DOLLARS}" to "$$$$"
diff --git a/contrib/bmake/unit-tests/varmod-defined.mk b/contrib/bmake/unit-tests/varmod-defined.mk
index a44b9f993146..ab5d708cf73f 100644
--- a/contrib/bmake/unit-tests/varmod-defined.mk
+++ b/contrib/bmake/unit-tests/varmod-defined.mk
@@ -1,8 +1,11 @@
-# $NetBSD: varmod-defined.mk,v 1.11 2021/04/11 13:35:56 rillig Exp $
+# $NetBSD: varmod-defined.mk,v 1.12 2021/11/30 23:52:19 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.
+# Force the test results to be independent of the default value of this
+# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake
+# distribution and pkgsrc/devel/bmake.
.MAKE.SAVE_DOLLARS= yes
DEF= defined
diff --git a/contrib/bmake/unit-tests/varmod-gmtime.exp b/contrib/bmake/unit-tests/varmod-gmtime.exp
index 5d5806b92d26..fdc9a2170e2f 100644
--- a/contrib/bmake/unit-tests/varmod-gmtime.exp
+++ b/contrib/bmake/unit-tests/varmod-gmtime.exp
@@ -1,12 +1,12 @@
-make: "varmod-gmtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}"
+make: "varmod-gmtime.mk" line 57: Invalid time value at "${:U1593536400}} != "mtime=11593536400}""
make: "varmod-gmtime.mk" line 57: Malformed conditional (${%Y:L:gmtime=${:U1593536400}} != "mtime=11593536400}")
-make: "varmod-gmtime.mk" line 67: Invalid time value: -1} != ""
+make: "varmod-gmtime.mk" line 67: Invalid time value at "-1} != """
make: "varmod-gmtime.mk" line 67: Malformed conditional (${:L:gmtime=-1} != "")
-make: "varmod-gmtime.mk" line 76: Invalid time value: 1} != ""
+make: "varmod-gmtime.mk" line 76: Invalid time value at " 1} != """
make: "varmod-gmtime.mk" line 76: Malformed conditional (${:L:gmtime= 1} != "")
-make: "varmod-gmtime.mk" line 119: Invalid time value: 10000000000000000000000000000000} != ""
+make: "varmod-gmtime.mk" line 119: Invalid time value at "10000000000000000000000000000000} != """
make: "varmod-gmtime.mk" line 119: Malformed conditional (${:L:gmtime=10000000000000000000000000000000} != "")
-make: "varmod-gmtime.mk" line 130: Invalid time value: error} != ""
+make: "varmod-gmtime.mk" line 130: Invalid time value at "error} != """
make: "varmod-gmtime.mk" line 130: Malformed conditional (${:L:gmtime=error} != "")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/varmod-indirect.exp b/contrib/bmake/unit-tests/varmod-indirect.exp
index 63ed988d0c0e..9cff88eb760a 100644
--- a/contrib/bmake/unit-tests/varmod-indirect.exp
+++ b/contrib/bmake/unit-tests/varmod-indirect.exp
@@ -20,7 +20,7 @@ Indirect modifier "S,a,a," from "${:US,a,a,}"
Evaluating modifier ${UNDEF:S...} on value "" (eval-keep-dollar-and-undefined, undefined)
Modifier part: "a"
Modifier part: "a"
-ModifyWords: split "" into 1 words
+ModifyWords: split "" into 1 word
Result of ${UNDEF:S,a,a,} is "" (eval-keep-dollar-and-undefined, undefined)
Global: _ = before ${UNDEF:S,a,a,} after
ParseReadLine (179): '_:= before ${UNDEF:${:U}} after'
diff --git a/contrib/bmake/unit-tests/varmod-localtime.exp b/contrib/bmake/unit-tests/varmod-localtime.exp
index ed4d4f053c61..494f160b766e 100644
--- a/contrib/bmake/unit-tests/varmod-localtime.exp
+++ b/contrib/bmake/unit-tests/varmod-localtime.exp
@@ -1,12 +1,12 @@
-make: "varmod-localtime.mk" line 57: Invalid time value: ${:U1593536400}} != "mtime=11593536400}"
+make: "varmod-localtime.mk" line 57: Invalid time value at "${:U1593536400}} != "mtime=11593536400}""
make: "varmod-localtime.mk" line 57: Malformed conditional (${%Y:L:localtime=${:U1593536400}} != "mtime=11593536400}")
-make: "varmod-localtime.mk" line 67: Invalid time value: -1} != ""
+make: "varmod-localtime.mk" line 67: Invalid time value at "-1} != """
make: "varmod-localtime.mk" line 67: Malformed conditional (${:L:localtime=-1} != "")
-make: "varmod-localtime.mk" line 76: Invalid time value: 1} != ""
+make: "varmod-localtime.mk" line 76: Invalid time value at " 1} != """
make: "varmod-localtime.mk" line 76: Malformed conditional (${:L:localtime= 1} != "")
-make: "varmod-localtime.mk" line 119: Invalid time value: 10000000000000000000000000000000} != ""
+make: "varmod-localtime.mk" line 119: Invalid time value at "10000000000000000000000000000000} != """
make: "varmod-localtime.mk" line 119: Malformed conditional (${:L:localtime=10000000000000000000000000000000} != "")
-make: "varmod-localtime.mk" line 130: Invalid time value: error} != ""
+make: "varmod-localtime.mk" line 130: Invalid time value at "error} != """
make: "varmod-localtime.mk" line 130: Malformed conditional (${:L:localtime=error} != "")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
diff --git a/contrib/bmake/unit-tests/varmod-localtime.mk b/contrib/bmake/unit-tests/varmod-localtime.mk
index f2867b61f8e9..ffa09a0bc5fc 100644
--- a/contrib/bmake/unit-tests/varmod-localtime.mk
+++ b/contrib/bmake/unit-tests/varmod-localtime.mk
@@ -3,7 +3,7 @@
# Tests for the :localtime variable modifier, which formats a timestamp
# using strftime(3) in local time.
-.if ${TZ} != "Europe/Berlin" # see unit-tests/Makefile
+.if ${TZ:Uno:NEurope/Berlin:NUTC-1} != "" # see unit-tests/Makefile
. error
.endif
diff --git a/contrib/bmake/unit-tests/varmod-loop-delete.exp b/contrib/bmake/unit-tests/varmod-loop-delete.exp
new file mode 100644
index 000000000000..aac86ee39061
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-loop-delete.exp
@@ -0,0 +1,4 @@
+make: "varmod-loop-delete.mk" line 19: Cannot delete variable "VAR" while it is used
+make: Fatal errors encountered -- cannot continue
+make: stopped in unit-tests
+exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-loop-delete.mk b/contrib/bmake/unit-tests/varmod-loop-delete.mk
new file mode 100644
index 000000000000..ed145b59ba0f
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-loop-delete.mk
@@ -0,0 +1,33 @@
+# $NetBSD: varmod-loop-delete.mk,v 1.2 2021/12/05 15:51:33 rillig Exp $
+#
+# Tests for the variable modifier ':@', which as a side effect allows to
+# delete an arbitrary variable.
+
+# A side effect of the modifier ':@' is that the loop variable is created as
+# an actual variable in the current evaluation scope (Command/Global/target),
+# and at the end of the loop, this variable is deleted. Since var.c 1.204
+# from 2016-02-18 and before var.c 1.963 from 2021-12-05, a variable could be
+# deleted while it was in use, leading to a use-after-free bug.
+#
+# See Var_Parse, comment 'the value of the variable must not change'.
+
+# Set up the variable that deletes itself when it is evaluated.
+VAR= ${:U:@VAR@@} rest of the value
+
+# In an assignment, the scope is 'Global'. Since the variable 'VAR' is
+# defined in the global scope, it deletes itself.
+EVAL:= ${VAR}
+.if ${EVAL} != " rest of the value"
+. error
+.endif
+
+VAR= ${:U:@VAR@@} rest of the value
+all: .PHONY
+ # In the command that is associated with a target, the scope is the
+ # one from the target. That scope only contains a few variables like
+ # '.TARGET', '.ALLSRC', '.IMPSRC'. Make does not expect that these
+ # variables get modified from the outside.
+ #
+ # There is no variable named 'VAR' in the local scope, so nothing
+ # happens.
+ : $@: '${VAR}'
diff --git a/contrib/bmake/unit-tests/varmod-loop-varname.exp b/contrib/bmake/unit-tests/varmod-loop-varname.exp
index 9170307bd2a0..4f0379d5ea0a 100644
--- a/contrib/bmake/unit-tests/varmod-loop-varname.exp
+++ b/contrib/bmake/unit-tests/varmod-loop-varname.exp
@@ -1,11 +1,11 @@
-make: "varmod-loop-varname.mk" line 13: In the :@ modifier of "", the variable name "${:Ubar:S,b,v,}" must not contain a dollar.
-make: "varmod-loop-varname.mk" line 13: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+")
-make: "varmod-loop-varname.mk" line 80: In the :@ modifier of "1 2 3", the variable name "v$" must not contain a dollar.
-make: "varmod-loop-varname.mk" line 80: Malformed conditional (${1 2 3:L:@v$@($v)@} != "(1) (2) (3)")
-make: "varmod-loop-varname.mk" line 85: In the :@ modifier of "1 2 3", the variable name "v$$" must not contain a dollar.
-make: "varmod-loop-varname.mk" line 85: Malformed conditional (${1 2 3:L:@v$$@($v)@} != "() () ()")
-make: "varmod-loop-varname.mk" line 90: In the :@ modifier of "1 2 3", the variable name "v$$$" must not contain a dollar.
-make: "varmod-loop-varname.mk" line 90: Malformed conditional (${1 2 3:L:@v$$$@($v)@} != "() () ()")
+make: "varmod-loop-varname.mk" line 16: In the :@ modifier of "", the variable name "${:Ubar:S,b,v,}" must not contain a dollar
+make: "varmod-loop-varname.mk" line 16: Malformed conditional (${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+")
+make: "varmod-loop-varname.mk" line 85: In the :@ modifier of "1 2 3", the variable name "v$" must not contain a dollar
+make: "varmod-loop-varname.mk" line 85: Malformed conditional (${1 2 3:L:@v$@($v)@} != "(1) (2) (3)")
+make: "varmod-loop-varname.mk" line 90: In the :@ modifier of "1 2 3", the variable name "v$$" must not contain a dollar
+make: "varmod-loop-varname.mk" line 90: Malformed conditional (${1 2 3:L:@v$$@($v)@} != "() () ()")
+make: "varmod-loop-varname.mk" line 95: In the :@ modifier of "1 2 3", the variable name "v$$$" must not contain a dollar
+make: "varmod-loop-varname.mk" line 95: Malformed conditional (${1 2 3:L:@v$$$@($v)@} != "() () ()")
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-loop-varname.mk b/contrib/bmake/unit-tests/varmod-loop-varname.mk
index d51e2ba76a42..91f8a4876466 100644
--- a/contrib/bmake/unit-tests/varmod-loop-varname.mk
+++ b/contrib/bmake/unit-tests/varmod-loop-varname.mk
@@ -1,8 +1,11 @@
-# $NetBSD: varmod-loop-varname.mk,v 1.2 2021/04/04 13:35:26 rillig Exp $
+# $NetBSD: varmod-loop-varname.mk,v 1.4 2021/12/05 15:01:04 rillig Exp $
#
# Tests for the first part of the variable modifier ':@var@...@', which
# contains the variable name to use during the loop.
+# Force the test results to be independent of the default value of this
+# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake
+# distribution and pkgsrc/devel/bmake.
.MAKE.SAVE_DOLLARS= yes
@@ -12,6 +15,8 @@
# variable name.
.if ${:Uone two three:@${:Ubar:S,b,v,}@+${var}+@} != "+one+ +two+ +three+"
. error
+.else
+. error
.endif
diff --git a/contrib/bmake/unit-tests/varmod-loop.exp b/contrib/bmake/unit-tests/varmod-loop.exp
index a4704973f6e2..d05b870d5b5e 100644
--- a/contrib/bmake/unit-tests/varmod-loop.exp
+++ b/contrib/bmake/unit-tests/varmod-loop.exp
@@ -1,10 +1,10 @@
-ParseReadLine (75): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$'
+ParseReadLine (78): 'USE_8_DOLLARS= ${:U1:@var@${8_DOLLARS}@} ${8_DOLLARS} $$$$$$$$'
CondParser_Eval: ${USE_8_DOLLARS} != "\$\$\$\$ \$\$\$\$ \$\$\$\$"
lhs = "$$$$ $$$$ $$$$", rhs = "$$$$ $$$$ $$$$", op = !=
-ParseReadLine (80): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}'
+ParseReadLine (83): 'SUBST_CONTAINING_LOOP:= ${USE_8_DOLLARS}'
CondParser_Eval: ${SUBST_CONTAINING_LOOP} != "\$\$ \$\$\$\$ \$\$\$\$"
lhs = "$$ $$$$ $$$$", rhs = "$$ $$$$ $$$$", op = !=
-ParseReadLine (105): '.MAKEFLAGS: -d0'
+ParseReadLine (108): '.MAKEFLAGS: -d0'
ParseDependency(.MAKEFLAGS: -d0)
:varname-overwriting-target: :x1y x2y x3y: ::
mod-loop-dollar:1:
diff --git a/contrib/bmake/unit-tests/varmod-loop.mk b/contrib/bmake/unit-tests/varmod-loop.mk
index 4fdaa3ff4e61..82046ff95d79 100644
--- a/contrib/bmake/unit-tests/varmod-loop.mk
+++ b/contrib/bmake/unit-tests/varmod-loop.mk
@@ -1,7 +1,10 @@
-# $NetBSD: varmod-loop.mk,v 1.15 2021/04/11 13:35:56 rillig Exp $
+# $NetBSD: varmod-loop.mk,v 1.18 2021/12/05 15:20:13 rillig Exp $
#
# Tests for the :@var@...${var}...@ variable modifier.
+# Force the test results to be independent of the default value of this
+# setting, which is 'yes' for NetBSD's usr.bin/make but 'no' for the bmake
+# distribution and pkgsrc/devel/bmake.
.MAKE.SAVE_DOLLARS= yes
all: varname-overwriting-target
@@ -183,7 +186,4 @@ CMDLINE= global # needed for deleting the environment
. error # 'CMDLINE' is gone now from all scopes
.endif
-
-# TODO: Actually trigger the undefined behavior (use after free) that was
-# already suspected in Var_Parse, in the comment 'the value of the variable
-# must not change'.
+all: .PHONY
diff --git a/contrib/bmake/unit-tests/varmod-order-numeric.exp b/contrib/bmake/unit-tests/varmod-order-numeric.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-numeric.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-order-numeric.mk b/contrib/bmake/unit-tests/varmod-order-numeric.mk
new file mode 100644
index 000000000000..40a1d408e9ae
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-numeric.mk
@@ -0,0 +1,54 @@
+# $NetBSD: varmod-order-numeric.mk,v 1.5 2021/08/03 04:46:49 rillig Exp $
+#
+# Tests for the variable modifiers ':On', which returns the words, sorted in
+# ascending numeric order, and for ':Orn' and ':Onr', which additionally
+# reverse the order.
+#
+# The variable modifiers ':On', ':Onr' and ':Orn' were added in var.c 1.939
+# from 2021-07-30.
+
+# This list contains only 32-bit numbers since the make code needs to conform
+# to C90, which does not provide integer types larger than 32 bit. It uses
+# 'long long' by default, but that type is overridable if necessary to support
+# older environments.
+#
+# To get 53-bit integers even in C90, it would be possible to switch to
+# 'double' instead, but that would allow floating-point numbers as well, which
+# is out of scope for this variable modifier.
+NUMBERS= 3 5 7 1 42 -42 5K -3m 1M 1k -2G
+
+.if ${NUMBERS:On} != "-2G -3m -42 1 3 5 7 42 1k 5K 1M"
+. error ${NUMBERS:On}
+.endif
+
+.if ${NUMBERS:Orn} != "1M 5K 1k 42 7 5 3 1 -42 -3m -2G"
+. error ${NUMBERS:Orn}
+.endif
+
+# Both ':Onr' and ':Orn' have the same effect.
+.if ${NUMBERS:Onr} != "1M 5K 1k 42 7 5 3 1 -42 -3m -2G"
+. error ${NUMBERS:Onr}
+.endif
+
+# Duplicate numbers are preserved in the output. In this case the
+# equal-valued numbers are spelled the same, so they are indistinguishable in
+# the output.
+DUPLICATES= 3 1 2 2 1 1 # https://oeis.org/A034002
+.if ${DUPLICATES:On} != "1 1 1 2 2 3"
+. error ${DUPLICATES:On}
+.endif
+
+# If there are several numbers that have the same integer value, they are
+# returned in unspecified order.
+SAME_VALUE:= ${:U 79 80 0x0050 81 :On}
+.if ${SAME_VALUE} != "79 80 0x0050 81" && ${SAME_VALUE} != "79 0x0050 80 81"
+. error ${SAME_VALUE}
+.endif
+
+# Hexadecimal and octal numbers are supported as well.
+MIXED_BASE= 0 010 0x7 9
+.if ${MIXED_BASE:On} != "0 0x7 010 9"
+. error ${MIXED_BASE:On}
+.endif
+
+all:
diff --git a/contrib/bmake/unit-tests/varmod-order-reverse.mk b/contrib/bmake/unit-tests/varmod-order-reverse.mk
index 1a6d2d766f76..c3be8d0f7817 100644
--- a/contrib/bmake/unit-tests/varmod-order-reverse.mk
+++ b/contrib/bmake/unit-tests/varmod-order-reverse.mk
@@ -1,13 +1,12 @@
-# $NetBSD: varmod-order-reverse.mk,v 1.4 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-order-reverse.mk,v 1.5 2021/08/03 04:46:49 rillig Exp $
#
# Tests for the :Or variable modifier, which returns the words, sorted in
# descending order.
-NUMBERS= one two three four five six seven eight nine ten
+WORDS= one two three four five six seven eight nine ten
-.if ${NUMBERS:Or} != "two three ten six seven one nine four five eight"
-. error ${NUMBERS:Or}
+.if ${WORDS:Or} != "two three ten six seven one nine four five eight"
+. error ${WORDS:Or}
.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/varmod-order-shuffle.mk b/contrib/bmake/unit-tests/varmod-order-shuffle.mk
index 185141b6c4a5..16121d7e498f 100644
--- a/contrib/bmake/unit-tests/varmod-order-shuffle.mk
+++ b/contrib/bmake/unit-tests/varmod-order-shuffle.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-order-shuffle.mk,v 1.6 2020/11/09 20:16:33 rillig Exp $
+# $NetBSD: varmod-order-shuffle.mk,v 1.7 2021/08/03 04:46:49 rillig Exp $
#
# Tests for the :Ox variable modifier, which returns the words of the
# variable, shuffled.
@@ -11,7 +11,7 @@
#
# Tags: probabilistic
-NUMBERS= one two three four five six seven eight nine ten
+WORDS= one two three four five six seven eight nine ten
# Note that 1 in every 10! trials two independently generated
# randomized orderings will be the same. The test framework doesn't
@@ -20,24 +20,23 @@ NUMBERS= one two three four five six seven eight nine ten
# lets the whole test fail once in 1.209.600 runs, on average.
# Create two shuffles using the := assignment operator.
-shuffled1:= ${NUMBERS:Ox}
-shuffled2:= ${NUMBERS:Ox}
+shuffled1:= ${WORDS:Ox}
+shuffled2:= ${WORDS:Ox}
.if ${shuffled1} == ${shuffled2}
. error ${shuffled1} == ${shuffled2}
.endif
# Sorting the list before shuffling it has no effect.
-shuffled1:= ${NUMBERS:O:Ox}
-shuffled2:= ${NUMBERS:O:Ox}
+shuffled1:= ${WORDS:O:Ox}
+shuffled2:= ${WORDS:O:Ox}
.if ${shuffled1} == ${shuffled2}
. error ${shuffled1} == ${shuffled2}
.endif
# Sorting after shuffling must produce the original numbers.
-sorted:= ${NUMBERS:Ox:O}
-.if ${sorted} != ${NUMBERS:O}
-. error ${sorted} != ${NUMBERS:O}
+sorted:= ${WORDS:Ox:O}
+.if ${sorted} != ${WORDS:O}
+. error ${sorted} != ${WORDS:O}
.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/varmod-order-string.exp b/contrib/bmake/unit-tests/varmod-order-string.exp
new file mode 100644
index 000000000000..39a9383953dd
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-string.exp
@@ -0,0 +1 @@
+exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-order-string.mk b/contrib/bmake/unit-tests/varmod-order-string.mk
new file mode 100644
index 000000000000..bb0a145ba825
--- /dev/null
+++ b/contrib/bmake/unit-tests/varmod-order-string.mk
@@ -0,0 +1,28 @@
+# $NetBSD: varmod-order-string.mk,v 1.2 2021/08/03 04:46:49 rillig Exp $
+#
+# Tests for the :O variable modifier, which returns the words, sorted in
+# ascending order.
+
+# Simple words are sorted lexicographically.
+WORDS= one two three four five six seven eight nine ten
+.if ${WORDS:O} != "eight five four nine one seven six ten three two"
+. error ${WORDS:O}
+.endif
+
+# Double quotes and single quotes delimit words, while backticks are just
+# regular characters. Therefore '`in' is a separate word from 'backticks`',
+# and the additional spaces between them are removed.
+QUOTED_WORDS= none "double quoted" 'single quoted' `in backticks`
+.if ${QUOTED_WORDS:O} != "\"double quoted\" 'single quoted' `in backticks` none"
+. error ${QUOTED_WORDS:O}
+.endif
+
+# Numbers are sorted lexicographically as well.
+# To sort the words numerically, use ':On' instead; since var.c 1.939 from
+# 2021-07-30.
+NUMBERS= -100g -50m -7k -50 -13 0 000 13 50 5k1 7k 50m 100G
+.if ${NUMBERS:O} != "-100g -13 -50 -50m -7k 0 000 100G 13 50 50m 5k1 7k"
+. error ${NUMBERS:O}
+.endif
+
+all:
diff --git a/contrib/bmake/unit-tests/varmod-order.exp b/contrib/bmake/unit-tests/varmod-order.exp
index 94c3cb694886..46dc45e9f6d6 100644
--- a/contrib/bmake/unit-tests/varmod-order.exp
+++ b/contrib/bmake/unit-tests/varmod-order.exp
@@ -1,7 +1,24 @@
-make: Bad modifier ":OX" for variable "NUMBERS"
-make: "varmod-order.mk" line 13: Undefined variable "${NUMBERS:OX"
-make: Bad modifier ":OxXX" for variable "NUMBERS"
-make: "varmod-order.mk" line 16: Undefined variable "${NUMBERS:Ox"
+make: Bad modifier ":OX" for variable "WORDS"
+make: "varmod-order.mk" line 14: Undefined variable "${WORDS:OX"
+make: Bad modifier ":OxXX" for variable "WORDS"
+make: "varmod-order.mk" line 17: 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: Bad modifier ":Oxn" for variable "NUMBERS"
+make: "varmod-order.mk" line 29: Malformed conditional (${NUMBERS:Oxn})
+make: Bad modifier ":On_typo" for variable "NUMBERS"
+make: "varmod-order.mk" line 39: Malformed conditional (${NUMBERS:On_typo})
+make: Bad modifier ":Onr_typo" for variable "NUMBERS"
+make: "varmod-order.mk" line 48: Malformed conditional (${NUMBERS:Onr_typo})
+make: Bad modifier ":Orn_typo" for variable "NUMBERS"
+make: "varmod-order.mk" line 57: Malformed conditional (${NUMBERS:Orn_typo})
+make: Bad modifier ":Onn" for variable "NUMBERS"
+make: "varmod-order.mk" line 68: Malformed conditional (${NUMBERS:Onn})
+make: Bad modifier ":Onrr" for variable "NUMBERS"
+make: "varmod-order.mk" line 77: Malformed conditional (${NUMBERS:Onrr})
+make: Bad modifier ":Orrn" for variable "NUMBERS"
+make: "varmod-order.mk" line 86: Malformed conditional (${NUMBERS:Orrn})
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
diff --git a/contrib/bmake/unit-tests/varmod-order.mk b/contrib/bmake/unit-tests/varmod-order.mk
index 675b6efec5e7..7f3d485fe8e3 100644
--- a/contrib/bmake/unit-tests/varmod-order.mk
+++ b/contrib/bmake/unit-tests/varmod-order.mk
@@ -1,19 +1,92 @@
-# $NetBSD: varmod-order.mk,v 1.5 2020/10/24 08:46:08 rillig Exp $
+# $NetBSD: varmod-order.mk,v 1.7 2021/08/03 04:46:49 rillig Exp $
#
-# Tests for the :O variable modifier, which returns the words, sorted in
-# ascending order.
+# Tests for the :O variable modifier and its variants, which either sort the
+# words of the value or shuffle them.
-NUMBERS= one two three four five six seven eight nine ten
+WORDS= one two three four five six seven eight nine ten
+NUMBERS= 8 5 4 9 1 7 6 10 3 2 # in English alphabetical order
-.if ${NUMBERS:O} != "eight five four nine one seven six ten three two"
-. error ${NUMBERS:O}
+.if ${WORDS:O} != "eight five four nine one seven six ten three two"
+. error ${WORDS:O}
.endif
# Unknown modifier "OX"
-_:= ${NUMBERS:OX}
+_:= ${WORDS:OX}
# Unknown modifier "OxXX"
-_:= ${NUMBERS:OxXX}
+_:= ${WORDS:OxXX}
+
+# Missing closing brace, to cover the error handling code.
+_:= ${WORDS:O
+_:= ${NUMBERS:On
+_:= ${NUMBERS:Onr
+
+# Shuffling numerically doesn't make sense, so don't allow 'x' and 'n' to be
+# combined.
+#
+# expect-text: Bad modifier ":Oxn" for variable "NUMBERS"
+# expect+1: Malformed conditional (${NUMBERS:Oxn})
+.if ${NUMBERS:Oxn}
+. error
+.else
+. error
+.endif
+
+# Extra characters after ':On' are detected and diagnosed.
+# TODO: Add line number information to the "Bad modifier" diagnostic.
+#
+# expect-text: Bad modifier ":On_typo" for variable "NUMBERS"
+.if ${NUMBERS:On_typo}
+. error
+.else
+. error
+.endif
+
+# Extra characters after ':Onr' are detected and diagnosed.
+#
+# expect-text: Bad modifier ":Onr_typo" for variable "NUMBERS"
+.if ${NUMBERS:Onr_typo}
+. error
+.else
+. error
+.endif
+
+# Extra characters after ':Orn' are detected and diagnosed.
+#
+# expect+1: Bad modifier ":Orn_typo" for variable "NUMBERS"
+.if ${NUMBERS:Orn_typo}
+. error
+.else
+. error
+.endif
+
+# Repeating the 'n' is not supported. In the typical use cases, the sorting
+# criteria are fixed, not computed, therefore allowing this redundancy does
+# not make sense.
+#
+# expect-text: Bad modifier ":Onn" for variable "NUMBERS"
+.if ${NUMBERS:Onn}
+. error
+.else
+. error
+.endif
+
+# Repeating the 'r' is not supported as well, for the same reasons as above.
+#
+# expect-text: Bad modifier ":Onrr" for variable "NUMBERS"
+.if ${NUMBERS:Onrr}
+. error
+.else
+. error
+.endif
+
+# Repeating the 'r' is not supported as well, for the same reasons as above.
+#
+# expect-text: Bad modifier ":Orrn" for variable "NUMBERS"
+.if ${NUMBERS:Orrn}
+. error
+.else
+. error
+.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/varmod-root.exp b/contrib/bmake/unit-tests/varmod-root.exp
index 2c99cd3ef4c7..39a9383953dd 100644
--- a/contrib/bmake/unit-tests/varmod-root.exp
+++ b/contrib/bmake/unit-tests/varmod-root.exp
@@ -1,11 +1 @@
-root of 'a/b/c' is 'a/b/c'
-root of 'def' is 'def'
-root of 'a.b.c' is 'a.b'
-root of 'a.b/c' is 'a'
-root of 'a' is 'a'
-root of 'a.a' is 'a'
-root of '.gitignore' is ''
-root of 'a' is 'a'
-root of 'a.a' is 'a'
-root of 'trailing/' is 'trailing/'
exit status 0
diff --git a/contrib/bmake/unit-tests/varmod-root.mk b/contrib/bmake/unit-tests/varmod-root.mk
index 1e3159733df0..cf88491df799 100644
--- a/contrib/bmake/unit-tests/varmod-root.mk
+++ b/contrib/bmake/unit-tests/varmod-root.mk
@@ -1,9 +1,38 @@
-# $NetBSD: varmod-root.mk,v 1.4 2020/12/20 22:57:40 rillig Exp $
+# $NetBSD: varmod-root.mk,v 1.5 2021/12/05 22:31:58 rillig Exp $
#
# Tests for the :R variable modifier, which returns the filename root
# without the extension.
+.if ${a/b/c:L:R} != "a/b/c"
+. error
+.endif
+
+.if ${def:L:R} != "def"
+. error
+.endif
+
+.if ${a.b.c:L:R} != "a.b"
+. error
+.endif
+
+.if ${a.b/c:L:R} != "a"
+. error
+.endif
+
+.if ${a:L:R} != "a"
+. error
+.endif
+
+.if ${a.a:L:R} != "a"
+. error
+.endif
+
+.if ${.gitignore:L:R} != ""
+. error
+.endif
+
+.if ${trailing/:L:R} != "trailing/"
+. error
+.endif
+
all:
-.for path in a/b/c def a.b.c a.b/c a a.a .gitignore a a.a trailing/
- @echo "root of '"${path:Q}"' is '"${path:R:Q}"'"
-.endfor
diff --git a/contrib/bmake/unit-tests/varmod-select-words.mk b/contrib/bmake/unit-tests/varmod-select-words.mk
index a9df25f9ff32..ab094bf056b0 100644
--- a/contrib/bmake/unit-tests/varmod-select-words.mk
+++ b/contrib/bmake/unit-tests/varmod-select-words.mk
@@ -1,7 +1,10 @@
-# $NetBSD: varmod-select-words.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varmod-select-words.mk,v 1.3 2021/12/05 12:06:23 rillig Exp $
#
# Tests for the :[...] variable modifier, which selects a single word
# or a range of words from a variable.
+#
+# See also:
+# modword.mk (should be migrated here)
# TODO: Implementation
diff --git a/contrib/bmake/unit-tests/varmod-subst.mk b/contrib/bmake/unit-tests/varmod-subst.mk
index 85f41e499ab7..763535ff835a 100644
--- a/contrib/bmake/unit-tests/varmod-subst.mk
+++ b/contrib/bmake/unit-tests/varmod-subst.mk
@@ -1,4 +1,4 @@
-# $NetBSD: varmod-subst.mk,v 1.8 2021/05/14 19:37:16 rillig Exp $
+# $NetBSD: varmod-subst.mk,v 1.9 2021/09/06 21:18:55 rillig Exp $
#
# Tests for the :S,from,to, variable modifier.
@@ -86,6 +86,19 @@ WORDS= sequences of letters
. error The '.' seems to be interpreted as a wildcard of some kind.
.endif
+.if ${:Uvalue:S,^val,&,} != "value"
+. error
+.endif
+.if ${:Uvalue:S,ue$,&,} != "value"
+. error
+.endif
+.if ${:Uvalue:S,^val,&-&-&,} != "val-val-value"
+. error
+.endif
+.if ${:Uvalue:S,ue$,&-&-&,} != "value-ue-ue"
+. error
+.endif
+
mod-subst:
@echo $@:
@echo :${:Ua b b c:S,a b,,:Q}:
diff --git a/contrib/bmake/unit-tests/varmod-to-separator.exp b/contrib/bmake/unit-tests/varmod-to-separator.exp
index c6e8ce98a21a..3f8f1b2a11eb 100644
--- a/contrib/bmake/unit-tests/varmod-to-separator.exp
+++ b/contrib/bmake/unit-tests/varmod-to-separator.exp
@@ -1,6 +1,6 @@
-make: "varmod-to-separator.mk" line 107: Invalid character number: 400:tu}
+make: "varmod-to-separator.mk" line 107: Invalid character number at "400:tu}"
make: "varmod-to-separator.mk" line 107: Malformed conditional (${WORDS:[1..3]:ts\400:tu})
-make: "varmod-to-separator.mk" line 121: Invalid character number: 100:tu}
+make: "varmod-to-separator.mk" line 121: Invalid character number at "100:tu}"
make: "varmod-to-separator.mk" line 121: Malformed conditional (${WORDS:[1..3]:ts\x100:tu})
make: Bad modifier ":ts\-300" for variable "WORDS"
make: "varmod-to-separator.mk" line 128: Malformed conditional (${WORDS:[1..3]:ts\-300:tu})
diff --git a/contrib/bmake/unit-tests/varmod-unique.mk b/contrib/bmake/unit-tests/varmod-unique.mk
index 04d04a575af1..7fef35b69211 100644
--- a/contrib/bmake/unit-tests/varmod-unique.mk
+++ b/contrib/bmake/unit-tests/varmod-unique.mk
@@ -1,47 +1,46 @@
-# $NetBSD: varmod-unique.mk,v 1.5 2021/05/30 20:26:41 rillig Exp $
+# $NetBSD: varmod-unique.mk,v 1.6 2021/12/05 22:37:58 rillig Exp $
#
# Tests for the :u variable modifier, which discards adjacent duplicate
# words.
-.if ${:U1 2 1:u} != "1 2 1"
-. warning The :u modifier only merges _adjacent_ duplicate words.
+.if ${1 2 1:L:u} != "1 2 1"
+. warning The modifier ':u' only merges _adjacent_ duplicate words.
.endif
-.if ${:U1 2 2 3:u} != "1 2 3"
-. warning The :u modifier must merge adjacent duplicate words.
+.if ${1 2 2 3:L:u} != "1 2 3"
+. warning The modifier ':u' must merge adjacent duplicate words.
.endif
-.if ${:U:u} != ""
-. warning The :u modifier must do nothing with an empty word list.
+.if ${:L:u} != ""
+. warning The modifier ':u' must do nothing with an empty word list.
.endif
-.if ${:U :u} != ""
+.if ${ :L:u} != ""
. warning The modifier ':u' must normalize the whitespace.
.endif
-.if ${:Uword:u} != "word"
-. warning The :u modifier must do nothing with a single-element word list.
+.if ${word:L:u} != "word"
+. warning The modifier ':u' must do nothing with a single-element word list.
.endif
-.if ${:U word :u} != "word"
+.if ${ word :L:u} != "word"
. warning The modifier ':u' must normalize the whitespace.
.endif
-.if ${:U1 1 1 1 1 1 1 1:u} != "1"
-. warning The :u modifier must merge _all_ adjacent duplicate words.
+.if ${1 1 1 1 1 1 1 1:L:u} != "1"
+. warning The modifier ':u' must merge _all_ adjacent duplicate words.
.endif
-.if ${:U 1 2 1 1 :u} != "1 2 1"
-. warning The :u modifier must normalize whitespace between the words.
+.if ${ 1 2 1 1 :L:u} != "1 2 1"
+. warning The modifier ':u' must normalize whitespace between the words.
.endif
-.if ${:U1 1 1 1 2:u} != "1 2"
+.if ${1 1 1 1 2:L:u} != "1 2"
. warning Duplicate words at the beginning must be merged.
.endif
-.if ${:U1 2 2 2 2:u} != "1 2"
+.if ${1 2 2 2 2:L:u} != "1 2"
. warning Duplicate words at the end must be merged.
.endif
all:
- @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk b/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk
index 97f37a646d2d..31f228c220b2 100644
--- a/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk
+++ b/contrib/bmake/unit-tests/varname-dot-make-save_dollars.mk
@@ -1,8 +1,130 @@
-# $NetBSD: varname-dot-make-save_dollars.mk,v 1.2 2020/08/16 14:25:16 rillig Exp $
+# $NetBSD: varname-dot-make-save_dollars.mk,v 1.7 2021/12/03 18:43:52 rillig Exp $
#
-# Tests for the special .MAKE.SAVE_DOLLARS variable.
+# Tests for the special .MAKE.SAVE_DOLLARS variable, which controls whether
+# the assignment operator ':=' converts '$$' to a single '$' or keeps it
+# as-is.
+#
+# See also:
+# var-op-expand.mk for ':=' in general
+# varmisc.mk for parsing the boolean values
+
+# Initially, the variable .MAKE.SAVE_DOLLARS is undefined. At this point the
+# behavior of the assignment operator ':=' depends. NetBSD's usr.bin/make
+# preserves the '$$' as-is, while the bmake distribution replaces '$$' with
+# '$'.
+.if ${.MAKE.SAVE_DOLLARS:Uundefined} != "undefined"
+. error
+.endif
+
+
+# When dollars are preserved, this setting not only applies to literal
+# dollars, but also to those that come indirectly from other expressions.
+DOLLARS= $$$$$$$$
+.MAKE.SAVE_DOLLARS= yes
+VAR:= ${DOLLARS}
+# The reduction from 8 '$' to 4 '$' happens when ${VAR} is evaluated in the
+# condition; .MAKE.SAVE_DOLLARS only applies at the moment where the
+# assignment is performed using ':='.
+.if ${VAR} != "\$\$\$\$"
+. error
+.endif
+
+# When dollars are preserved, this setting not only applies to literal
+# dollars, but also to those that come indirectly from other expressions.
+DOLLARS= $$$$$$$$
+.MAKE.SAVE_DOLLARS= no
+VAR:= ${DOLLARS}
+.if ${VAR} != "\$\$"
+. error
+.endif
+
+# The 'yes' preserves the dollars from the literal.
+.MAKE.SAVE_DOLLARS= yes
+VAR:= $$$$$$$$
+.if ${VAR} != "\$\$\$\$"
+. error
+.endif
+
+# The 'no' converts each '$$' to '$'.
+.MAKE.SAVE_DOLLARS= no
+VAR:= $$$$$$$$
+.if ${VAR} != "\$\$"
+. error
+.endif
+
+# It's even possible to change the dollar interpretation in the middle of
+# evaluating an expression, but there is no practical need for it.
+.MAKE.SAVE_DOLLARS= no
+VAR:= $$$$-${.MAKE.SAVE_DOLLARS::=yes}-$$$$
+.if ${VAR} != "\$--\$\$"
+. error
+.endif
+
+# The '$' from the ':U' expressions do not appear as literal '$$' to the
+# parser (no matter whether directly or indirectly), they only appear as '$$'
+# in the value of an expression, therefore .MAKE.SAVE_DOLLARS doesn't apply
+# here.
+.MAKE.SAVE_DOLLARS= no
+VAR:= ${:U\$\$\$\$}-${.MAKE.SAVE_DOLLARS::=yes}-${:U\$\$\$\$}
+.if ${VAR} != "\$\$--\$\$"
+. error
+.endif
+
+# Undefining .MAKE.SAVE_DOLLARS does not have any effect, in particular it
+# does not restore the default behavior.
+.MAKE.SAVE_DOLLARS= no
+.undef .MAKE.SAVE_DOLLARS
+VAR:= $$$$$$$$
+.if ${VAR} != "\$\$"
+. error
+.endif
+
+# Undefining .MAKE.SAVE_DOLLARS does not have any effect, in particular it
+# does not restore the default behavior.
+.MAKE.SAVE_DOLLARS= yes
+.undef .MAKE.SAVE_DOLLARS
+VAR:= $$$$$$$$
+.if ${VAR} != "\$\$\$\$"
+. error
+.endif
+
+# The variable '.MAKE.SAVE_DOLLARS' not only affects literal '$$' on the
+# right-hand side of the assignment operator ':=', it also affects dollars
+# in indirect expressions.
+#
+# In this example, it affects the command in CMD itself, not the result of
+# running that command.
+.MAKE.SAVE_DOLLARS= no
+CMD= echo '$$$$$$$$'
+VAR:= ${CMD:sh}
+.if ${VAR} != "\$\$"
+. error
+.endif
+
+.MAKE.SAVE_DOLLARS= yes
+CMD= echo '$$$$$$$$'
+VAR:= ${CMD:sh}
+.if ${VAR} != "\$\$\$\$"
+. error
+.endif
+
+
+# In the modifier ':@var@body@', .MAKE.SAVE_DOLLARS does not affect the body.
+# In both cases, each '$$' is replaced with a single '$', no matter whether
+# directly or indirectly via another expression.
+.MAKE.SAVE_DOLLARS= no
+DOLLARS= $$$$$$$$
+VAR:= ${word:L:@word@$$$$$$$$-${DOLLARS}@}
+.if ${VAR} != "\$\$-\$\$"
+. error
+.endif
+
+.MAKE.SAVE_DOLLARS= yes
+DOLLARS= $$$$$$$$
+VAR:= ${word:L:@word@$$$$$$$$-${DOLLARS}@}
+.if ${VAR} != "\$\$-\$\$"
+. error
+.endif
-# TODO: Implementation
all:
- @:;
diff --git a/contrib/bmake/unit-tests/varname-dot-suffixes.exp b/contrib/bmake/unit-tests/varname-dot-suffixes.exp
new file mode 100644
index 000000000000..753ce20d9fa8
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-suffixes.exp
@@ -0,0 +1,39 @@
+Global:delete .SUFFIXES (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: _ =
+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)
+Result of ${.SUFFIXES::=assign} is "" (eval-keep-dollar-and-undefined, regular)
+Global: _ =
+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)
+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
+Global: .MAKEFLAGS = -r -k -d v -d 0 -d v -d 0
+Var_Parse: ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz" (eval-defined)
+Evaluating modifier ${1 2:L} on value "" (eval-defined, undefined)
+Result of ${1 2:L} is "1 2" (eval-defined, defined)
+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)
+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)
+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)
+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
+exit status 0
diff --git a/contrib/bmake/unit-tests/varname-dot-suffixes.mk b/contrib/bmake/unit-tests/varname-dot-suffixes.mk
new file mode 100644
index 000000000000..de8034a172cc
--- /dev/null
+++ b/contrib/bmake/unit-tests/varname-dot-suffixes.mk
@@ -0,0 +1,104 @@
+# $NetBSD: varname-dot-suffixes.mk,v 1.1 2021/12/12 22:16:48 rillig Exp $
+#
+# Tests for the special "variable" .SUFFIXES, which lists the suffixes that
+# have been registered for use in suffix transformation rules. Suffixes are
+# listed even if there is no actual transformation rule that uses them.
+#
+# The name '.SUFFIXES' does not refer to a real variable, instead it can be
+# used as a starting "variable name" for expressions like ${.SUFFIXES} or
+# ${.SUFFIXES:M*o}.
+
+# In the beginning, there are no suffix rules, the expression is thus empty.
+.if ${.SUFFIXES} != ""
+.endif
+
+# There is no actual variable named '.SUFFIXES', it is all made up.
+.if defined(.SUFFIXES)
+. error
+.endif
+
+# The suffixes list is still empty, and so is the "variable" '.SUFFIXES'.
+.if !empty(.SUFFIXES)
+. error
+.endif
+
+.SUFFIXES: .c .o .1 .err
+
+# The suffixes are listed in declaration order.
+.if ${.SUFFIXES} != ".c .o .1 .err"
+. error
+.endif
+
+# There is still no actual variable named '.SUFFIXES', it is all made up.
+.if defined(.SUFFIXES)
+. error
+.endif
+
+# Now the suffixes list is not empty anymore. It may seem strange that there
+# is no variable named '.SUFFIXES' but evaluating '${.SUFFIXES}' nevertheless
+# returns something. For all practical use cases, it's good enough though.
+.if empty(.SUFFIXES)
+. error
+.endif
+
+.SUFFIXES: .tar.gz
+
+# Changes to the suffixes list are reflected immediately.
+.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz"
+. error
+.endif
+
+# Deleting .SUFFIXES has no effect since there is no actual variable of that
+# name.
+.MAKEFLAGS: -dv
+# expect: Global:delete .SUFFIXES (not found)
+.undef .SUFFIXES
+.MAKEFLAGS: -d0
+.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz"
+. error
+.endif
+
+# 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)
+.SUFFIXES= set
+# expect: Global: .SUFFIXES = append ignored (read-only)
+.SUFFIXES+= append
+# expect: Global: .SUFFIXES = assign ignored (read-only)
+_:= ${.SUFFIXES::=assign}
+# expect: Command: .SUFFIXES = preserve ignored (read-only)
+_:= ${preserve:L:_=.SUFFIXES}
+.MAKEFLAGS: -d0
+
+# Using the name '.SUFFIXES' in a .for loop looks strange because these
+# variable names are typically in singular form, and .for loops do not use
+# real variables either, they are made up as well, see directive-for.mk. The
+# replacement mechanism for the iteration variables takes precedence.
+.for .SUFFIXES in .c .o
+. if ${.SUFFIXES} != ".c" && ${.SUFFIXES} != ".o"
+. error
+. endif
+.endfor
+
+# After the .for loop, the expression '${.SUFFIXES}' refers to the list of
+# suffixes again.
+.if ${.SUFFIXES} != ".c .o .1 .err .tar.gz"
+. error
+.endif
+
+# Using the name '.SUFFIXES' in the modifier ':@var@body@' does not create an
+# actual variable either. Like in the .for loop, choosing the name
+# '.SUFFIXES' for the iteration variable is unusual. In ODE Make, the
+# convention for these iteration variables is to have dots at both ends, so
+# 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)
+.if ${1 2:L:@.SUFFIXES@${.SUFFIXES}@} != ".c .o .1 .err .tar.gz .c .o .1 .err .tar.gz"
+. error
+.endif
+.MAKEFLAGS: -d0
+
+all:
diff --git a/contrib/bmake/unit-tests/varname-empty.exp b/contrib/bmake/unit-tests/varname-empty.exp
index ec225c6973c8..75a3f4151a8c 100644
--- a/contrib/bmake/unit-tests/varname-empty.exp
+++ b/contrib/bmake/unit-tests/varname-empty.exp
@@ -2,10 +2,6 @@ Var_SetExpand: variable name "${:U}" expands to empty string, with value "cmdlin
Var_SetExpand: variable name "" expands to empty string, with value "cmdline-plain" - ignored
Global: .CURDIR = <curdir>
Var_Parse: ${MAKE_OBJDIR_CHECK_WRITABLE} (eval)
-Global: .OBJDIR = <curdir>
-Global:delete .PATH (not found)
-Global: .PATH = .
-Global: .PATH = . <curdir>
Global: .TARGETS =
Internal: MAKEFILE = varname-empty.mk
Global: .MAKE.MAKEFILES = varname-empty.mk
diff --git a/contrib/bmake/util.c b/contrib/bmake/util.c
index 571d187c5fe1..dab18e2ece53 100644
--- a/contrib/bmake/util.c
+++ b/contrib/bmake/util.c
@@ -3,7 +3,7 @@
/*
* Missing stuff from OS's
*
- * $Id: util.c,v 1.46 2021/02/05 20:02:29 sjg Exp $
+ * $Id: util.c,v 1.49 2021/10/14 19:26:52 sjg Exp $
*/
#include <sys/param.h>
@@ -340,6 +340,10 @@ getcwd(path, sz)
}
#endif
+#if !defined(HAVE_SIGACTION)
+#include "sigact.h"
+#endif
+
/* force posix signals */
SignalProc
bmake_signal(int s, SignalProc a)
@@ -586,3 +590,79 @@ warnx(const char *fmt, ...)
va_end(ap);
}
#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#elif defined(HAVE_STDINT_H)
+#include <stdint.h>
+#endif
+#ifdef HAVE_LIMITS_H
+#include <limits.h>
+#endif
+
+#ifndef NUM_TYPE
+# ifdef HAVE_LONG_LONG_INT
+# define NUM_TYPE long long
+# elif defined(_INT64_T_DECLARED) || defined(int64_t)
+# define NUM_TYPE int64_t
+# endif
+#endif
+
+#ifdef NUM_TYPE
+#if !defined(HAVE_STRTOLL)
+#define BCS_ONLY
+#define _FUNCNAME strtoll
+#define __INT NUM_TYPE
+#undef __INT_MIN
+#undef __INT_MAX
+#ifdef LLONG_MAX
+# define __INT_MIN LLONG_MIN
+# define __INT_MAX LLONG_MAX
+#elif defined(INT64_MAX)
+# define __INT_MIN INT64_MIN
+# define __INT_MAX INT64_MAX
+#endif
+#ifndef _DIAGASSERT
+# define _DIAGASSERT(e)
+#endif
+#ifndef __UNCONST
+# define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+#include "_strtol.h"
+#endif
+
+#endif
+
+#if !defined(HAVE_STRTOL)
+#define BCS_ONLY
+#define _FUNCNAME strtol
+#define __INT long
+#undef __INT_MIN
+#undef __INT_MAX
+#define __INT_MIN LONG_MIN
+#define __INT_MAX LONG_MAX
+#ifndef _DIAGASSERT
+# define _DIAGASSERT(e)
+#endif
+#ifndef __UNCONST
+# define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+#include "_strtol.h"
+#endif
+
+#if !defined(HAVE_STRTOUL)
+#define BCS_ONLY
+#define _FUNCNAME strtoul
+#define __INT unsigned long
+#undef __INT_MIN
+#undef __INT_MAX
+#define __INT_MIN 0
+#define __INT_MAX ULONG_MAX
+#ifndef _DIAGASSERT
+# define _DIAGASSERT(e)
+#endif
+#ifndef __UNCONST
+# define __UNCONST(a) ((void *)(unsigned long)(const void *)(a))
+#endif
+#include "_strtol.h"
+#endif
diff --git a/contrib/bmake/var.c b/contrib/bmake/var.c
index 6e5148bba968..4542a2f9a2ed 100644
--- a/contrib/bmake/var.c
+++ b/contrib/bmake/var.c
@@ -1,4 +1,4 @@
-/* $NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $ */
+/* $NetBSD: var.c,v 1.973 2021/12/12 20:45:48 sjg Exp $ */
/*
* Copyright (c) 1988, 1989, 1990, 1993
@@ -148,7 +148,7 @@
#include "metachar.h"
/* "@(#)var.c 8.3 (Berkeley) 3/19/94" */
-MAKE_RCSID("$NetBSD: var.c,v 1.934 2021/06/21 08:40:44 rillig Exp $");
+MAKE_RCSID("$NetBSD: var.c,v 1.973 2021/12/12 20:45:48 sjg Exp $");
/*
* Variables are defined using one of the VAR=value assignments. Their
@@ -326,7 +326,7 @@ GNode *SCOPE_INTERNAL;
static VarExportedMode var_exportedVars = VAR_EXPORTED_NONE;
-static const char *VarEvalMode_Name[] = {
+static const char VarEvalMode_Name[][32] = {
"parse-only",
"eval",
"eval-defined",
@@ -504,6 +504,12 @@ Var_Delete(GNode *scope, const char *varname)
DEBUG2(VAR, "%s:delete %s\n", scope->name, varname);
v = he->value;
+ if (v->inUse) {
+ Parse_Error(PARSE_FATAL,
+ "Cannot delete variable \"%s\" while it is used",
+ v->name.str);
+ return;
+ }
if (v->exported)
unsetenv(v->name.str);
if (strcmp(v->name.str, MAKE_EXPORTED) == 0)
@@ -856,15 +862,17 @@ GetVarnamesToUnexport(bool isEnv, const char *arg,
}
static void
-UnexportVar(const char *varname, UnexportWhat what)
+UnexportVar(Substring varname, UnexportWhat what)
{
- Var *v = VarFind(varname, SCOPE_GLOBAL, false);
+ Var *v = VarFindSubstring(varname, SCOPE_GLOBAL, false);
if (v == NULL) {
- DEBUG1(VAR, "Not unexporting \"%s\" (not found)\n", varname);
+ DEBUG2(VAR, "Not unexporting \"%.*s\" (not found)\n",
+ (int)Substring_Length(varname), varname.start);
return;
}
- DEBUG1(VAR, "Unexporting \"%s\"\n", varname);
+ DEBUG2(VAR, "Unexporting \"%.*s\"\n",
+ (int)Substring_Length(varname), varname.start);
if (what != UNEXPORT_ENV && v->exported && !v->reexport)
unsetenv(v->name.str);
v->exported = false;
@@ -888,17 +896,15 @@ static void
UnexportVars(FStr *varnames, UnexportWhat what)
{
size_t i;
- Words words;
+ SubstringWords words;
if (what == UNEXPORT_ENV)
ClearEnv();
- words = Str_Words(varnames->str, false);
- for (i = 0; i < words.len; i++) {
- const char *varname = words.words[i];
- UnexportVar(varname, what);
- }
- Words_Free(words);
+ words = Substring_Words(varnames->str, false);
+ for (i = 0; i < words.len; i++)
+ UnexportVar(words.words[i], what);
+ SubstringWords_Free(words);
if (what != UNEXPORT_NAMED)
Global_Delete(MAKE_EXPORTED);
@@ -979,6 +985,12 @@ 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",
+ scope->name, name, val);
+ return;
+ }
v = VarAdd(name, val, scope, flags);
} else {
if (v->readOnly && !(flags & VAR_SET_READONLY)) {
@@ -1065,9 +1077,9 @@ Var_Set(GNode *scope, const char *name, const char *val)
* 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
- * scope scope in which to set it
*/
void
Var_SetExpand(GNode *scope, const char *name, const char *val)
@@ -1143,9 +1155,9 @@ Var_Append(GNode *scope, const char *name, const char *val)
* 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
- * scope scope in which this should occur
*
* Notes:
* Only if the variable is being sought in the global scope is the
@@ -1204,8 +1216,8 @@ Var_Exists(GNode *scope, const char *name)
* fallback scopes.
*
* Input:
- * name Variable to find, is expanded once
- * scope Scope in which to start search
+ * scope scope in which to start search
+ * name name of the variable to find, is expanded once
*/
bool
Var_ExistsExpand(GNode *scope, const char *name)
@@ -1230,8 +1242,8 @@ Var_ExistsExpand(GNode *scope, const char *name)
* or the usual scopes.
*
* Input:
- * name name to find, is not expanded any further
* scope scope in which to search for it
+ * name name to find, is not expanded any further
*
* Results:
* The value if the variable exists, NULL if it doesn't.
@@ -1593,6 +1605,54 @@ VarREError(int reerr, const regex_t *pat, const char *str)
free(errbuf);
}
+/*
+ * Replacement of regular expressions is not specified by POSIX, therefore
+ * re-implement it here.
+ */
+static void
+RegexReplace(const char *replace, SepBuf *buf, const char *wp,
+ const regmatch_t *m, size_t nsub)
+{
+ const char *rp;
+ unsigned int n;
+
+ for (rp = replace; *rp != '\0'; rp++) {
+ if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
+ SepBuf_AddBytes(buf, rp + 1, 1);
+ rp++;
+ continue;
+ }
+
+ if (*rp == '&') {
+ SepBuf_AddBytesBetween(buf,
+ wp + (size_t)m[0].rm_so,
+ wp + (size_t)m[0].rm_eo);
+ continue;
+ }
+
+ if (*rp != '\\' || !ch_isdigit(rp[1])) {
+ SepBuf_AddBytes(buf, rp, 1);
+ continue;
+ }
+
+ /* \0 to \9 backreference */
+ n = rp[1] - '0';
+ rp++;
+
+ if (n >= nsub) {
+ Error("No subexpression \\%u", n);
+ } else if (m[n].rm_so == -1) {
+ if (opts.strict) {
+ Error("No match for subexpression \\%u", n);
+ }
+ } else {
+ SepBuf_AddBytesBetween(buf,
+ wp + (size_t)m[n].rm_so,
+ wp + (size_t)m[n].rm_eo);
+ }
+ }
+}
+
struct ModifyWord_SubstRegexArgs {
regex_t re;
size_t nsub;
@@ -1611,87 +1671,42 @@ ModifyWord_SubstRegex(Substring word, SepBuf *buf, void *data)
struct ModifyWord_SubstRegexArgs *args = data;
int xrv;
const char *wp;
- const char *rp;
int flags = 0;
regmatch_t m[10];
assert(word.end[0] == '\0'); /* assume null-terminated word */
wp = word.start;
if (args->pflags.subOnce && args->matched)
- goto nosub;
+ goto no_match;
-tryagain:
+again:
xrv = regexec(&args->re, wp, args->nsub, m, flags);
+ if (xrv == 0)
+ goto ok;
+ if (xrv != REG_NOMATCH)
+ VarREError(xrv, &args->re, "Unexpected regex error");
+no_match:
+ SepBuf_AddStr(buf, wp);
+ return;
- switch (xrv) {
- case 0:
- args->matched = true;
- SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
-
- /*
- * Replacement of regular expressions is not specified by
- * POSIX, therefore implement it here.
- */
-
- for (rp = args->replace; *rp != '\0'; rp++) {
- if (*rp == '\\' && (rp[1] == '&' || rp[1] == '\\')) {
- SepBuf_AddBytes(buf, rp + 1, 1);
- rp++;
- continue;
- }
-
- if (*rp == '&') {
- SepBuf_AddBytesBetween(buf,
- wp + m[0].rm_so, wp + m[0].rm_eo);
- continue;
- }
-
- if (*rp != '\\' || !ch_isdigit(rp[1])) {
- SepBuf_AddBytes(buf, rp, 1);
- continue;
- }
+ok:
+ args->matched = true;
+ SepBuf_AddBytes(buf, wp, (size_t)m[0].rm_so);
- { /* \0 to \9 backreference */
- size_t n = (size_t)(rp[1] - '0');
- rp++;
-
- if (n >= args->nsub) {
- Error("No subexpression \\%u",
- (unsigned)n);
- } else if (m[n].rm_so == -1) {
- if (opts.strict) {
- Error(
- "No match for subexpression \\%u",
- (unsigned)n);
- }
- } else {
- SepBuf_AddBytesBetween(buf,
- wp + m[n].rm_so, wp + m[n].rm_eo);
- }
- }
- }
+ RegexReplace(args->replace, buf, wp, m, args->nsub);
- wp += m[0].rm_eo;
- if (args->pflags.subGlobal) {
- flags |= REG_NOTBOL;
- if (m[0].rm_so == 0 && m[0].rm_eo == 0) {
- SepBuf_AddBytes(buf, wp, 1);
- wp++;
- }
- if (*wp != '\0')
- goto tryagain;
+ 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) {
+ SepBuf_AddBytes(buf, wp, 1);
+ wp++;
}
if (*wp != '\0')
- SepBuf_AddStr(buf, wp);
- break;
- default:
- VarREError(xrv, &args->re, "Unexpected regex error");
- /* FALLTHROUGH */
- case REG_NOMATCH:
- nosub:
- SepBuf_AddStr(buf, wp);
- break;
+ goto again;
}
+ if (*wp != '\0')
+ SepBuf_AddStr(buf, wp);
}
#endif
@@ -1740,7 +1755,7 @@ static char *
VarSelectWords(const char *str, int first, int last,
char sep, bool oneBigWord)
{
- Words words;
+ SubstringWords words;
int len, start, end, step;
int i;
@@ -1748,15 +1763,13 @@ VarSelectWords(const char *str, int first, int last,
SepBuf_Init(&buf, sep);
if (oneBigWord) {
- /* fake what Str_Words() would do if there were only one word */
+ /* fake what Substring_Words() would do */
words.len = 1;
- words.words = bmake_malloc(
- (words.len + 1) * sizeof(words.words[0]));
- words.freeIt = bmake_strdup(str);
- words.words[0] = words.freeIt;
- words.words[1] = NULL;
+ words.words = bmake_malloc(sizeof(words.words[0]));
+ words.freeIt = NULL;
+ words.words[0] = Substring_InitStr(str); /* no need to copy */
} else {
- words = Str_Words(str, false);
+ words = Substring_Words(str, false);
}
/*
@@ -1782,11 +1795,11 @@ VarSelectWords(const char *str, int first, int last,
}
for (i = start; (step < 0) == (i >= end); i += step) {
- SepBuf_AddStr(&buf, words.words[i]);
+ SepBuf_AddSubstring(&buf, words.words[i]);
SepBuf_Sep(&buf);
}
- Words_Free(words);
+ SubstringWords_Free(words);
return SepBuf_DoneData(&buf);
}
@@ -1814,7 +1827,7 @@ ModifyWord_Realpath(Substring word, SepBuf *buf, void *data MAKE_ATTR_UNUSED)
static char *
-Words_JoinFree(Words words)
+SubstringWords_JoinFree(SubstringWords words)
{
Buffer buf;
size_t i;
@@ -1826,10 +1839,11 @@ Words_JoinFree(Words words)
/* XXX: Use ch->sep instead of ' ', for consistency. */
Buf_AddByte(&buf, ' ');
}
- Buf_AddStr(&buf, words.words[i]);
+ Buf_AddBytesBetween(&buf,
+ words.words[i].start, words.words[i].end);
}
- Words_Free(words);
+ SubstringWords_Free(words);
return Buf_DoneData(&buf);
}
@@ -1980,7 +1994,7 @@ VarStrftime(const char *fmt, bool zulu, time_t tim)
* After parsing, the modifier is evaluated. The side effects from evaluating
* nested variable 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 the update the value of the expression.
+ * 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
@@ -2015,7 +2029,7 @@ typedef enum ExprDefined {
DEF_DEFINED
} ExprDefined;
-static const char *const ExprDefined_Name[] = {
+static const char ExprDefined_Name[][10] = {
"regular",
"undefined",
"defined"
@@ -2027,7 +2041,7 @@ static const char *const ExprDefined_Name[] = {
#define const_member /* no const possible */
#endif
-/* A variable expression such as $@ or ${VAR:Mpattern:Q}. */
+/* An expression based on a variable, such as $@ or ${VAR:Mpattern:Q}. */
typedef struct Expr {
const char *name;
FStr value;
@@ -2052,7 +2066,7 @@ typedef struct Expr {
* Chain 2 ends at the ':' between ${IND1} and ${IND2}.
* Chain 3 starts with all modifiers from ${IND2}.
* Chain 3 ends at the ':' after ${IND2}.
- * Chain 1 continues with the the 2 modifiers ':O' and ':u'.
+ * Chain 1 continues with the 2 modifiers ':O' and ':u'.
* Chain 1 ends at the final '}' of the expression.
*
* After such a chain ends, its properties no longer have any effect.
@@ -2085,6 +2099,18 @@ Expr_Define(Expr *expr)
expr->defined = DEF_DEFINED;
}
+static const char *
+Expr_Str(const Expr *expr)
+{
+ return expr->value.str;
+}
+
+static SubstringWords
+Expr_Words(const Expr *expr)
+{
+ return Substring_Words(Expr_Str(expr), false);
+}
+
static void
Expr_SetValue(Expr *expr, FStr value)
{
@@ -2143,6 +2169,63 @@ IsEscapedModifierPart(const char *p, char delim,
return p[1] == '&' && subst != NULL;
}
+/*
+ * In a part of a modifier, parse a subexpression and evaluate it.
+ */
+static void
+ParseModifierPartExpr(const char **pp, LazyBuf *part, const ModChain *ch,
+ VarEvalMode emode)
+{
+ const char *p = *pp;
+ FStr nested_val;
+
+ (void)Var_Parse(&p, ch->expr->scope,
+ VarEvalMode_WithoutKeepDollar(emode), &nested_val);
+ /* TODO: handle errors */
+ LazyBuf_AddStr(part, nested_val.str);
+ FStr_Done(&nested_val);
+ *pp = p;
+}
+
+/*
+ * In a part of a modifier, parse a subexpression but don't evaluate it.
+ *
+ * XXX: This whole block is very similar to Var_Parse with VARE_PARSE_ONLY.
+ * There may be subtle edge cases though that are not yet covered in the unit
+ * tests and that are parsed differently, depending on whether they are
+ * evaluated or not.
+ *
+ * This subtle difference is not documented in the manual page, neither is
+ * the difference between parsing ':D' and ':M' documented. No code should
+ * ever depend on these details, but who knows.
+ */
+static void
+ParseModifierPartDollar(const char **pp, LazyBuf *part)
+{
+ const char *p = *pp;
+ const char *start = *pp;
+
+ if (p[1] == '(' || p[1] == '{') {
+ char startc = p[1];
+ int endc = startc == '(' ? ')' : '}';
+ int depth = 1;
+
+ for (p += 2; *p != '\0' && depth > 0; p++) {
+ if (p[-1] != '\\') {
+ if (*p == startc)
+ depth++;
+ if (*p == endc)
+ depth--;
+ }
+ }
+ LazyBuf_AddBytesBetween(part, start, p);
+ *pp = p;
+ } else {
+ LazyBuf_Add(part, *start);
+ *pp = p + 1;
+ }
+}
+
/* See ParseModifierPart for the documentation. */
static VarParseResult
ParseModifierPartSubst(
@@ -2164,88 +2247,26 @@ ParseModifierPartSubst(
p = *pp;
LazyBuf_Init(part, p);
- /*
- * Skim through until the matching delimiter is found; pick up
- * variable expressions on the way.
- */
while (*p != '\0' && *p != delim) {
- const char *varstart;
-
if (IsEscapedModifierPart(p, delim, subst)) {
LazyBuf_Add(part, p[1]);
p += 2;
- continue;
- }
-
- if (*p != '$') { /* Unescaped, simple text */
+ } else if (*p != '$') { /* Unescaped, simple text */
if (subst != NULL && *p == '&')
LazyBuf_AddSubstring(part, subst->lhs);
else
LazyBuf_Add(part, *p);
p++;
- continue;
- }
-
- if (p[1] == delim) { /* Unescaped $ at end of pattern */
+ } else if (p[1] == delim) { /* Unescaped '$' at end */
if (out_pflags != NULL)
out_pflags->anchorEnd = true;
else
LazyBuf_Add(part, *p);
p++;
- continue;
- }
-
- if (VarEvalMode_ShouldEval(emode)) {
- /* Nested variable, evaluated */
- const char *nested_p = p;
- FStr nested_val;
-
- (void)Var_Parse(&nested_p, ch->expr->scope,
- VarEvalMode_WithoutKeepDollar(emode), &nested_val);
- /* TODO: handle errors */
- LazyBuf_AddStr(part, nested_val.str);
- FStr_Done(&nested_val);
- p += nested_p - p;
- continue;
- }
-
- /*
- * XXX: This whole block is very similar to Var_Parse without
- * VARE_WANTRES. There may be subtle edge cases
- * though that are not yet covered in the unit tests and that
- * are parsed differently, depending on whether they are
- * evaluated or not.
- *
- * This subtle difference is not documented in the manual
- * page, neither is the difference between parsing :D and
- * :M documented. No code should ever depend on these
- * details, but who knows.
- */
-
- varstart = p; /* Nested variable, only parsed */
- if (p[1] == '(' || p[1] == '{') {
- /*
- * Find the end of this variable reference
- * and suck it in without further ado.
- * It will be interpreted later.
- */
- char startc = p[1];
- int endc = startc == '(' ? ')' : '}';
- int depth = 1;
-
- for (p += 2; *p != '\0' && depth > 0; p++) {
- if (p[-1] != '\\') {
- if (*p == startc)
- depth++;
- if (*p == endc)
- depth--;
- }
- }
- LazyBuf_AddBytesBetween(part, varstart, p);
- } else {
- LazyBuf_Add(part, *varstart);
- p++;
- }
+ } else if (VarEvalMode_ShouldEval(emode))
+ ParseModifierPartExpr(&p, part, ch, emode);
+ else
+ ParseModifierPartDollar(&p, part);
}
if (*p != delim) {
@@ -2388,7 +2409,7 @@ ModifyWords(ModChain *ch,
bool oneBigWord)
{
Expr *expr = ch->expr;
- const char *val = expr->value.str;
+ const char *val = Expr_Str(expr);
SepBuf result;
SubstringWords words;
size_t i;
@@ -2404,8 +2425,8 @@ ModifyWords(ModChain *ch,
words = Substring_Words(val, false);
- DEBUG2(VAR, "ModifyWords: split \"%s\" into %u words\n",
- val, (unsigned)words.len);
+ DEBUG3(VAR, "ModifyWords: split \"%s\" into %u %s\n",
+ val, (unsigned)words.len, words.len != 1 ? "words" : "word");
SepBuf_Init(&result, ch->sep);
for (i = 0; i < words.len; i++) {
@@ -2442,7 +2463,7 @@ ApplyModifier_Loop(const char **pp, ModChain *ch)
if (strchr(args.var, '$') != NULL) {
Parse_Error(PARSE_FATAL,
"In the :@ modifier of \"%s\", the variable name \"%s\" "
- "must not contain a dollar.",
+ "must not contain a dollar",
expr->name, args.var);
return AMR_CLEANUP;
}
@@ -2569,6 +2590,7 @@ TryParseTime(const char **pp, time_t *out_time)
static ApplyModifierResult
ApplyModifier_Gmtime(const char **pp, ModChain *ch)
{
+ Expr *expr;
time_t utc;
const char *mod = *pp;
@@ -2579,7 +2601,7 @@ ApplyModifier_Gmtime(const char **pp, ModChain *ch)
const char *p = mod + 7;
if (!TryParseTime(&p, &utc)) {
Parse_Error(PARSE_FATAL,
- "Invalid time value: %s", mod + 7);
+ "Invalid time value at \"%s\"", mod + 7);
return AMR_CLEANUP;
}
*pp = p;
@@ -2588,9 +2610,10 @@ ApplyModifier_Gmtime(const char **pp, ModChain *ch)
*pp = mod + 6;
}
- if (ModChain_ShouldEval(ch))
- Expr_SetValueOwn(ch->expr,
- VarStrftime(ch->expr->value.str, true, utc));
+ expr = ch->expr;
+ if (Expr_ShouldEval(expr))
+ Expr_SetValueOwn(expr,
+ VarStrftime(Expr_Str(expr), true, utc));
return AMR_OK;
}
@@ -2599,6 +2622,7 @@ ApplyModifier_Gmtime(const char **pp, ModChain *ch)
static ApplyModifierResult
ApplyModifier_Localtime(const char **pp, ModChain *ch)
{
+ Expr *expr;
time_t utc;
const char *mod = *pp;
@@ -2609,7 +2633,7 @@ ApplyModifier_Localtime(const char **pp, ModChain *ch)
const char *p = mod + 10;
if (!TryParseTime(&p, &utc)) {
Parse_Error(PARSE_FATAL,
- "Invalid time value: %s", mod + 10);
+ "Invalid time value at \"%s\"", mod + 10);
return AMR_CLEANUP;
}
*pp = p;
@@ -2618,9 +2642,10 @@ ApplyModifier_Localtime(const char **pp, ModChain *ch)
*pp = mod + 9;
}
- if (ModChain_ShouldEval(ch))
- Expr_SetValueOwn(ch->expr,
- VarStrftime(ch->expr->value.str, false, utc));
+ expr = ch->expr;
+ if (Expr_ShouldEval(expr))
+ Expr_SetValueOwn(expr,
+ VarStrftime(Expr_Str(expr), false, utc));
return AMR_OK;
}
@@ -2634,7 +2659,7 @@ ApplyModifier_Hash(const char **pp, ModChain *ch)
*pp += 4;
if (ModChain_ShouldEval(ch))
- Expr_SetValueOwn(ch->expr, VarHash(ch->expr->value.str));
+ Expr_SetValueOwn(ch->expr, VarHash(Expr_Str(ch->expr)));
return AMR_OK;
}
@@ -2649,7 +2674,7 @@ ApplyModifier_Path(const char **pp, ModChain *ch)
(*pp)++;
- if (!ModChain_ShouldEval(ch))
+ if (!Expr_ShouldEval(expr))
return AMR_OK;
Expr_Define(expr);
@@ -2733,9 +2758,9 @@ ApplyModifier_Range(const char **pp, ModChain *ch)
return AMR_OK;
if (n == 0) {
- Words words = Str_Words(ch->expr->value.str, false);
+ SubstringWords words = Expr_Words(ch->expr);
n = words.len;
- Words_Free(words);
+ SubstringWords_Free(words);
}
Buf_Init(&buf);
@@ -3003,7 +3028,7 @@ ApplyModifier_Quote(const char **pp, ModChain *ch)
if (!ModChain_ShouldEval(ch))
return AMR_OK;
- VarQuote(ch->expr->value.str, quoteDollar, &buf);
+ VarQuote(Expr_Str(ch->expr), quoteDollar, &buf);
if (buf.data != NULL)
Expr_SetValue(ch->expr, LazyBuf_DoneGet(&buf));
else
@@ -3081,7 +3106,7 @@ ApplyModifier_ToSep(const char **pp, ModChain *ch)
if (!TryParseChar(&p, base, &ch->sep)) {
Parse_Error(PARSE_FATAL,
- "Invalid character number: %s", p);
+ "Invalid character number at \"%s\"", p);
return AMR_CLEANUP;
}
if (!IsDelimiter(*p, ch)) {
@@ -3154,15 +3179,15 @@ ApplyModifier_To(const char **pp, ModChain *ch)
if (mod[1] == 'u') { /* :tu */
*pp = mod + 2;
- if (ModChain_ShouldEval(ch))
- Expr_SetValueOwn(expr, str_toupper(expr->value.str));
+ if (Expr_ShouldEval(expr))
+ Expr_SetValueOwn(expr, str_toupper(Expr_Str(expr)));
return AMR_OK;
}
if (mod[1] == 'l') { /* :tl */
*pp = mod + 2;
- if (ModChain_ShouldEval(ch))
- Expr_SetValueOwn(expr, str_tolower(expr->value.str));
+ if (Expr_ShouldEval(expr))
+ Expr_SetValueOwn(expr, str_tolower(Expr_Str(expr)));
return AMR_OK;
}
@@ -3211,9 +3236,9 @@ ApplyModifier_Words(const char **pp, ModChain *ch)
} else {
Buffer buf;
- Words words = Str_Words(expr->value.str, false);
+ SubstringWords words = Expr_Words(expr);
size_t ac = words.len;
- Words_Free(words);
+ SubstringWords_Free(words);
/* 3 digits + '\0' is usually enough */
Buf_InitSize(&buf, 4);
@@ -3267,7 +3292,7 @@ ApplyModifier_Words(const char **pp, ModChain *ch)
/* Normal case: select the words described by first and last. */
Expr_SetValueOwn(expr,
- VarSelectWords(expr->value.str, first, last,
+ VarSelectWords(Expr_Str(expr), first, last,
ch->sep, ch->oneBigWord));
ok:
@@ -3279,62 +3304,140 @@ bad_modifier:
return AMR_BAD;
}
+#ifndef NUM_TYPE
+# ifdef HAVE_LONG_LONG_INT
+# define NUM_TYPE long long
+# elif defined(_INT64_T_DECLARED) || defined(int64_t)
+# define NUM_TYPE int64_t
+# else
+# define NUM_TYPE long
+# define strtoll strtol
+# endif
+#endif
+
+static NUM_TYPE
+num_val(Substring s)
+{
+ NUM_TYPE val;
+ char *ep;
+
+ val = strtoll(s.start, &ep, 0);
+ if (ep != s.start) {
+ switch (*ep) {
+ case 'K':
+ case 'k':
+ val <<= 10;
+ break;
+ case 'M':
+ case 'm':
+ val <<= 20;
+ break;
+ case 'G':
+ case 'g':
+ val <<= 30;
+ break;
+ }
+ }
+ return val;
+}
+
static int
-str_cmp_asc(const void *a, const void *b)
+SubNumAsc(const void *sa, const void *sb)
{
- return strcmp(*(const char *const *)a, *(const char *const *)b);
+ NUM_TYPE a, b;
+
+ a = num_val(*((const Substring *)sa));
+ b = num_val(*((const Substring *)sb));
+ return (a > b) ? 1 : (b > a) ? -1 : 0;
}
static int
-str_cmp_desc(const void *a, const void *b)
+SubNumDesc(const void *sa, const void *sb)
{
- return strcmp(*(const char *const *)b, *(const char *const *)a);
+ return SubNumAsc(sb, sa);
+}
+
+static int
+SubStrAsc(const void *sa, const void *sb)
+{
+ return strcmp(
+ ((const Substring *)sa)->start, ((const Substring *)sb)->start);
+}
+
+static int
+SubStrDesc(const void *sa, const void *sb)
+{
+ return SubStrAsc(sb, sa);
}
static void
-ShuffleStrings(char **strs, size_t n)
+ShuffleSubstrings(Substring *strs, size_t n)
{
size_t i;
for (i = n - 1; i > 0; i--) {
size_t rndidx = (size_t)random() % (i + 1);
- char *t = strs[i];
+ Substring t = strs[i];
strs[i] = strs[rndidx];
strs[rndidx] = t;
}
}
-/* :O (order ascending) or :Or (order descending) or :Ox (shuffle) */
+/*
+ * :O order ascending
+ * :Or order descending
+ * :Ox shuffle
+ * :On numeric ascending
+ * :Onr, :Orn numeric descending
+ */
static ApplyModifierResult
ApplyModifier_Order(const char **pp, ModChain *ch)
{
- const char *mod = (*pp)++; /* skip past the 'O' in any case */
- Words words;
- enum SortMode {
- ASC, DESC, SHUFFLE
- } mode;
+ const char *mod = *pp;
+ SubstringWords words;
+ int (*cmp)(const void *, const void *);
- if (IsDelimiter(mod[1], ch)) {
- mode = ASC;
- } else if ((mod[1] == 'r' || mod[1] == 'x') &&
- IsDelimiter(mod[2], ch)) {
+ if (IsDelimiter(mod[1], ch) || mod[1] == '\0') {
+ cmp = SubStrAsc;
(*pp)++;
- mode = mod[1] == 'r' ? DESC : SHUFFLE;
- } else
- return AMR_BAD;
+ } else if (IsDelimiter(mod[2], ch) || mod[2] == '\0') {
+ if (mod[1] == 'n')
+ cmp = SubNumAsc;
+ else if (mod[1] == 'r')
+ cmp = SubStrDesc;
+ else if (mod[1] == 'x')
+ cmp = NULL;
+ else
+ goto bad;
+ *pp += 2;
+ } else if (IsDelimiter(mod[3], ch) || mod[3] == '\0') {
+ if ((mod[1] == 'n' && mod[2] == 'r') ||
+ (mod[1] == 'r' && mod[2] == 'n'))
+ cmp = SubNumDesc;
+ else
+ goto bad;
+ *pp += 3;
+ } else {
+ goto bad;
+ }
if (!ModChain_ShouldEval(ch))
return AMR_OK;
- words = Str_Words(ch->expr->value.str, false);
- if (mode == SHUFFLE)
- ShuffleStrings(words.words, words.len);
- else
- qsort(words.words, words.len, sizeof words.words[0],
- mode == ASC ? str_cmp_asc : str_cmp_desc);
- Expr_SetValueOwn(ch->expr, Words_JoinFree(words));
+ words = Expr_Words(ch->expr);
+ if (cmp == NULL)
+ ShuffleSubstrings(words.words, words.len);
+ else {
+ assert(words.words[0].end[0] == '\0');
+ qsort(words.words, words.len, sizeof(words.words[0]), cmp);
+ }
+ Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words));
return AMR_OK;
+
+bad:
+ (*pp)++;
+ return AMR_BAD;
}
/* :? then : else */
@@ -3350,7 +3453,7 @@ ApplyModifier_IfElse(const char **pp, ModChain *ch)
VarEvalMode then_emode = VARE_PARSE_ONLY;
VarEvalMode else_emode = VARE_PARSE_ONLY;
- int cond_rc = COND_PARSE; /* anything other than COND_INVALID */
+ CondEvalResult cond_rc = COND_PARSE; /* just not COND_INVALID */
if (Expr_ShouldEval(expr)) {
cond_rc = Cond_EvalCondition(expr->name, &value);
if (cond_rc != COND_INVALID && value)
@@ -3377,10 +3480,12 @@ ApplyModifier_IfElse(const char **pp, ModChain *ch)
if (cond_rc == COND_INVALID) {
Error("Bad conditional expression '%s' in '%s?%s:%s'",
expr->name, expr->name, then_expr.str, else_expr.str);
+ FStr_Done(&then_expr);
+ FStr_Done(&else_expr);
return AMR_CLEANUP;
}
- if (!ModChain_ShouldEval(ch)) {
+ if (!Expr_ShouldEval(expr)) {
FStr_Done(&then_expr);
FStr_Done(&else_expr);
} else if (value) {
@@ -3528,7 +3633,7 @@ ApplyModifier_Remember(const char **pp, ModChain *ch)
*pp = mod + 1;
if (Expr_ShouldEval(expr))
- Var_Set(expr->scope, name.str, expr->value.str);
+ Var_Set(expr->scope, name.str, Expr_Str(expr));
FStr_Done(&name);
return AMR_OK;
@@ -3556,7 +3661,7 @@ ApplyModifier_WordFunc(const char **pp, ModChain *ch,
static ApplyModifierResult
ApplyModifier_Unique(const char **pp, ModChain *ch)
{
- Words words;
+ SubstringWords words;
if (!IsDelimiter((*pp)[1], ch))
return AMR_UNKNOWN;
@@ -3565,14 +3670,14 @@ ApplyModifier_Unique(const char **pp, ModChain *ch)
if (!ModChain_ShouldEval(ch))
return AMR_OK;
- words = Str_Words(ch->expr->value.str, false);
+ words = Expr_Words(ch->expr);
if (words.len > 1) {
size_t si, di;
di = 0;
for (si = 1; si < words.len; si++) {
- if (strcmp(words.words[si], words.words[di]) != 0) {
+ if (!Substring_Eq(words.words[si], words.words[di])) {
di++;
if (di != si)
words.words[di] = words.words[si];
@@ -3581,7 +3686,7 @@ ApplyModifier_Unique(const char **pp, ModChain *ch)
words.len = di + 1;
}
- Expr_SetValueOwn(ch->expr, Words_JoinFree(words));
+ Expr_SetValueOwn(ch->expr, SubstringWords_JoinFree(words));
return AMR_OK;
}
@@ -3637,7 +3742,7 @@ ApplyModifier_SysV(const char **pp, ModChain *ch)
(*pp)--; /* Go back to the ch->endc. */
/* Do not turn an empty expression into non-empty. */
- if (lhsBuf.len == 0 && expr->value.str[0] == '\0')
+ if (lhsBuf.len == 0 && Expr_Str(expr)[0] == '\0')
goto done;
lhs = LazyBuf_Get(&lhsBuf);
@@ -3671,9 +3776,9 @@ ApplyModifier_SunShell(const char **pp, ModChain *ch)
if (Expr_ShouldEval(expr)) {
const char *errfmt;
- char *output = Cmd_Exec(expr->value.str, &errfmt);
+ char *output = Cmd_Exec(Expr_Str(expr), &errfmt);
if (errfmt != NULL)
- Error(errfmt, expr->value.str);
+ Error(errfmt, Expr_Str(expr));
Expr_SetValueOwn(expr, output);
}
@@ -3703,13 +3808,13 @@ LogBeforeApply(const ModChain *ch, const char *mod)
debug_printf(
"Evaluating modifier ${%s:%c%s} on value \"%s\"\n",
expr->name, mod[0], is_single_char ? "" : "...",
- expr->value.str);
+ Expr_Str(expr));
return;
}
debug_printf(
"Evaluating modifier ${%s:%c%s} on value \"%s\" (%s, %s)\n",
- expr->name, mod[0], is_single_char ? "" : "...", expr->value.str,
+ expr->name, mod[0], is_single_char ? "" : "...", Expr_Str(expr),
VarEvalMode_Name[expr->emode], ExprDefined_Name[expr->defined]);
}
@@ -3717,7 +3822,7 @@ static void
LogAfterApply(const ModChain *ch, const char *p, const char *mod)
{
const Expr *expr = ch->expr;
- const char *value = expr->value.str;
+ const char *value = Expr_Str(expr);
const char *quot = value == var_Error ? "" : "\"";
if ((expr->emode == VARE_WANTRES || expr->emode == VARE_UNDEFERR) &&
@@ -3757,6 +3862,7 @@ ApplyModifier(const char **pp, ModChain *ch)
return ApplyModifier_Regex(pp, ch);
#endif
case 'D':
+ case 'U':
return ApplyModifier_Defined(pp, ch);
case 'E':
return ApplyModifier_WordFunc(pp, ch, ModifyWord_Suffix);
@@ -3794,8 +3900,6 @@ ApplyModifier(const char **pp, ModChain *ch)
return ApplyModifier_WordFunc(pp, ch, ModifyWord_Tail);
case 't':
return ApplyModifier_To(pp, ch);
- case 'U':
- return ApplyModifier_Defined(pp, ch);
case 'u':
return ApplyModifier_Unique(pp, ch);
default:
@@ -3848,7 +3952,7 @@ ApplyModifiersIndirect(ModChain *ch, const char **pp)
if (mods.str[0] != '\0') {
const char *modsp = mods.str;
ApplyModifiers(expr, &modsp, '\0', '\0');
- if (expr->value.str == var_Error || *modsp != '\0') {
+ if (Expr_Str(expr) == var_Error || *modsp != '\0') {
FStr_Done(&mods);
*pp = p;
return AMIR_OUT; /* error already reported */
@@ -3916,7 +4020,7 @@ ApplySingleModifier(const char **pp, ModChain *ch)
"modifier \"%.*s\" of variable \"%s\" with value \"%s\"",
ch->endc,
(int)(p - mod), mod,
- ch->expr->name, ch->expr->value.str);
+ ch->expr->name, Expr_Str(ch->expr));
} else if (*p == ':') {
p++;
} else if (opts.strict && *p != '\0' && *p != ch->endc) {
@@ -3964,7 +4068,7 @@ ApplyModifiers(
assert(startc == '(' || startc == '{' || startc == '\0');
assert(endc == ')' || endc == '}' || endc == '\0');
- assert(expr->value.str != NULL);
+ assert(Expr_Str(expr) != NULL);
p = *pp;
@@ -4002,7 +4106,7 @@ ApplyModifiers(
}
*pp = p;
- assert(expr->value.str != NULL); /* Use var_Error or varUndefined. */
+ assert(Expr_Str(expr) != NULL); /* Use var_Error or varUndefined. */
return;
bad_modifier:
@@ -4284,6 +4388,7 @@ ParseVarnameLong(
)
{
LazyBuf varname;
+ Substring name;
Var *v;
bool haveModifier;
bool dynamic = false;
@@ -4294,13 +4399,13 @@ ParseVarnameLong(
p += 2; /* skip "${" or "$(" or "y(" */
ParseVarname(&p, startc, endc, scope, emode, &varname);
+ name = LazyBuf_Get(&varname);
if (*p == ':') {
haveModifier = true;
} else if (*p == endc) {
haveModifier = false;
} else {
- Substring name = LazyBuf_Get(&varname);
Parse_Error(PARSE_FATAL, "Unclosed variable \"%.*s\"",
(int)Substring_Length(name), name.start);
LazyBuf_Done(&varname);
@@ -4310,14 +4415,18 @@ ParseVarnameLong(
return false;
}
- v = VarFindSubstring(LazyBuf_Get(&varname), scope, true);
+ v = VarFindSubstring(name, scope, true);
/* At this point, p points just after the variable name,
* either at ':' or at endc. */
if (v == NULL) {
- v = FindLocalLegacyVar(LazyBuf_Get(&varname), scope,
- out_true_extraModifiers);
+ if (Substring_Equals(name, ".SUFFIXES"))
+ v = VarNew(Substring_Str(name),
+ Suff_NamesStr(), false, true);
+ else
+ v = FindLocalLegacyVar(name, scope,
+ out_true_extraModifiers);
}
if (v == NULL) {
@@ -4325,14 +4434,14 @@ ParseVarnameLong(
* Defer expansion of dynamic variables if they appear in
* non-local scope since they are not defined there.
*/
- dynamic = VarnameIsDynamic(LazyBuf_Get(&varname)) &&
+ dynamic = VarnameIsDynamic(name) &&
(scope == SCOPE_CMDLINE || scope == SCOPE_GLOBAL);
if (!haveModifier) {
p++; /* skip endc */
*out_false_pp = p;
*out_false_res = EvalUndefined(dynamic, start, p,
- LazyBuf_Get(&varname), emode, out_false_val);
+ name, emode, out_false_val);
return false;
}
@@ -4365,11 +4474,11 @@ ParseVarnameLong(
/* Free the environment variable now since we own it. */
static void
-FreeEnvVar(Var *v, FStr *inout_val)
+FreeEnvVar(Var *v, Expr *expr)
{
char *varValue = Buf_DoneData(&v->val);
- if (inout_val->str == varValue)
- inout_val->freeIt = varValue;
+ if (expr->value.str == varValue)
+ expr->value.freeIt = varValue;
else
free(varValue);
@@ -4434,9 +4543,9 @@ Var_Parse_FastLane(const char **pp, VarEvalMode emode, FStr *out_value)
*
* Input:
* *pp The string to parse.
- * When parsing a condition in ParseEmptyArg, it may also
- * point to the "y" of "empty(VARNAME:Modifiers)", which
- * is syntactically the same.
+ * In CondParser_FuncCallEmpty, it may also point to the
+ * "y" of "empty(VARNAME:Modifiers)", which is
+ * syntactically the same.
* scope The scope for finding variables
* emode Controls the exact details of parsing and evaluation
*
@@ -4535,14 +4644,14 @@ Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val)
* Before applying any modifiers, expand any nested expressions from
* the variable value.
*/
- if (strchr(expr.value.str, '$') != NULL &&
+ if (strchr(Expr_Str(&expr), '$') != NULL &&
VarEvalMode_ShouldEval(emode)) {
char *expanded;
VarEvalMode nested_emode = emode;
if (opts.strict)
nested_emode = VarEvalMode_UndefOk(nested_emode);
v->inUse = true;
- (void)Var_Subst(expr.value.str, scope, nested_emode,
+ (void)Var_Subst(Expr_Str(&expr), scope, nested_emode,
&expanded);
v->inUse = false;
/* TODO: handle errors */
@@ -4565,7 +4674,7 @@ Var_Parse(const char **pp, GNode *scope, VarEvalMode emode, FStr *out_val)
*pp = p;
if (v->fromEnv) {
- FreeEnvVar(v, &expr.value);
+ FreeEnvVar(v, &expr);
} else if (expr.defined != DEF_REGULAR) {
if (expr.defined == DEF_UNDEF) {
@@ -4732,6 +4841,14 @@ Var_Stats(void)
HashTable_DebugStats(&SCOPE_GLOBAL->vars, "Global variables");
}
+static int
+StrAsc(const void *sa, const void *sb)
+{
+ return strcmp(
+ *((const char *const *)sa), *((const char *const *)sb));
+}
+
+
/* Print all variables in a scope, sorted by name. */
void
Var_Dump(GNode *scope)
@@ -4748,7 +4865,7 @@ Var_Dump(GNode *scope)
*(const char **)Vector_Push(&vec) = hi.entry->key;
varnames = vec.items;
- qsort(varnames, vec.len, sizeof varnames[0], str_cmp_asc);
+ qsort(varnames, vec.len, sizeof varnames[0], StrAsc);
for (i = 0; i < vec.len; i++) {
const char *varname = varnames[i];